Change to the linux kernel coding style
[wmaker-crm.git] / wrlib / nxpm.c
1 /* nxpm.c - load "normalized" XPM image
2  *
3  * Raster graphics library
4  *
5  * Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include <config.h>
23
24 /* AIX requires this to be the first thing in the file.  */
25 #ifdef __GNUC__
26 # define alloca __builtin_alloca
27 #else
28 # if HAVE_ALLOCA_H
29 #  include <alloca.h>
30 # else
31 #  ifdef _AIX
32 #   pragma alloca
33 #  else
34 #   ifndef alloca               /* predefined by HP cc +Olibcalls */
35 char *alloca();
36 #   endif
37 #  endif
38 # endif
39 #endif
40
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <assert.h>
45 #include <errno.h>
46
47 #include "wraster.h"
48
49 /*
50  * Restricted support for XPM images.
51  *
52  * The images must be in the following "normalized" format:
53  *
54  *
55  * line         content
56  * 1            signature comment
57  * 2            ignored ( normally "static char *xpm[] = {" )
58  * 3            "width height color_count chars" where chars is 1 or 2
59  * 4            color definitions. Only c values with #rrggbb or #rrrrggggbbb
60  *                      format OR None
61  * n            data
62  *
63  * - no comments or blank lines are allowed, except for the signature
64  * - all lines must have at most 256 characters
65  * - no white spaces allowed at left of each line
66  */
67
68 #define LINEWIDTH 64
69
70 #ifndef USE_XPM
71
72 RImage *RGetImageFromXPMData(RContext * context, char **data)
73 {
74         RImage *image = NULL;
75         unsigned char *color_table[4];
76         unsigned short *symbol_table;
77         unsigned char *r, *g, *b, *a;
78         int i, j, k, line = 0;
79         int transp;
80         unsigned short color;
81         int bsize;
82         int w, h, ccount, csize;
83
84         if (sscanf(data[line++], "%i %i %i %i", &w, &h, &ccount, &csize) != 4
85             || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
86                 goto bad_format;
87
88         if (csize != 1 && csize != 2)
89                 goto bad_format;
90
91         color_table[0] = alloca(ccount);
92         color_table[1] = alloca(ccount);
93         color_table[2] = alloca(ccount);
94         color_table[3] = alloca(ccount);
95         symbol_table = alloca(ccount * sizeof(unsigned short));
96
97         bsize = csize * w + 16;
98
99         if (!color_table[0] || !color_table[1] || !color_table[2] || !color_table[3] || !symbol_table || !bsize) {
100                 RErrorCode = RERR_NOMEMORY;
101                 alloca(0);
102                 return NULL;
103         }
104
105         transp = 0;
106         /* get color table */
107         for (i = 0; i < ccount; i++) {
108                 symbol_table[i] = data[line][0];
109                 if (csize == 2)
110                         symbol_table[i] |= data[line][1] << 8;
111
112                 j = csize;
113                 while (data[line][j] != '#' && data[line][j] != 0 && data[line][j] != 'N')
114                         j++;
115
116                 if (data[line][j] == '#') {
117                         unsigned int red, green, blue;
118
119                         k = 0;
120                         j++;
121                         while (data[line][j + k] != 0)
122                                 k++;
123                         if (k == 6) {
124                                 if (sscanf(&(data[line][j]), "%2x%2x%2x", &red, &green, &blue) != 3)
125                                         goto bad_format;
126                         } else if (k == 12) {
127                                 if (sscanf(&(data[line][j]), "%4x%4x%4x", &red, &green, &blue) != 3)
128                                         goto bad_format;
129                                 red >>= 8;
130                                 green >>= 8;
131                                 blue >>= 8;
132                         } else
133                                 goto bad_format;
134
135                         color_table[0][i] = red;
136                         color_table[1][i] = green;
137                         color_table[2][i] = blue;
138                         color_table[3][i] = 255;
139                 } else if (strncmp(&(data[line][j]), "None", 4) == 0 || strncmp(&(data[line][j]), "none", 4) == 0) {
140                         color_table[3][i] = 0;
141                         transp = 1;
142                 } else {
143                         goto bad_format;
144                 }
145                 line++;
146         }
147
148         image = RCreateImage(w, h, transp);
149         if (!image) {
150                 alloca(0);
151                 return NULL;
152         }
153
154         r = image->data;
155         g = image->data + 1;
156         b = image->data + 2;
157         if (image->format == RRGBAFormat)
158                 a = image->data + 3;
159         else
160                 a = NULL;
161
162         for (i = 0; i < h; i++) {
163                 if (csize == 1) {
164                         for (j = 0; j < w; j++) {
165                                 color = data[line][j];
166
167                                 for (k = 0; k < ccount; k++) {
168                                         if (symbol_table[k] == color)
169                                                 break;
170                                 }
171                                 if (k == ccount)
172                                         k = 0;
173
174                                 *r = color_table[0][k];
175                                 *g = color_table[1][k];
176                                 *b = color_table[2][k];
177                                 if (a) {
178                                         *a = color_table[3][k];
179                                         r += 4;
180                                         g += 4;
181                                         b += 4;
182                                         a += 4;
183                                 } else {
184                                         r += 3;
185                                         g += 3;
186                                         b += 3;
187                                 }
188                         }
189                 } else {
190                         for (j = 0; j < w * 2; j++) {
191                                 color = data[line][j++];
192                                 color |= data[line][j];
193
194                                 for (k = 0; k < ccount; k++) {
195                                         if (symbol_table[k] == color)
196                                                 break;
197                                 }
198                                 if (k == ccount)
199                                         k = 0;
200
201                                 *r = color_table[0][k];
202                                 *g = color_table[1][k];
203                                 *b = color_table[2][k];
204                                 if (a) {
205                                         *a = color_table[3][k];
206                                         r += 4;
207                                         g += 4;
208                                         b += 4;
209                                         a += 4;
210                                 } else {
211                                         r += 3;
212                                         g += 3;
213                                         b += 3;
214                                 }
215                         }
216                 }
217                 line++;
218         }
219
220 #ifdef C_ALLOCA
221         alloca(0);
222 #endif
223         return image;
224
225  bad_format:
226         RErrorCode = RERR_BADIMAGEFILE;
227 #ifdef C_ALLOCA
228         alloca(0);
229 #endif
230         if (image)
231                 RReleaseImage(image);
232         return NULL;
233 }
234
235 RImage *RLoadXPM(RContext * context, char *file, int index)
236 {
237         RImage *image = NULL;
238         char line[LINEWIDTH + 1];
239         char *buffer;
240         unsigned char *color_table[4];
241         unsigned short *symbol_table;
242         unsigned char *r, *g, *b, *a;
243         int i, j, k;
244         int transp;
245         unsigned short color;
246         int bsize;
247         int w, h, ccount, csize;
248         FILE *f;
249
250         f = fopen(file, "rb");
251         if (!f) {
252                 RErrorCode = RERR_OPEN;
253                 return NULL;
254         }
255         /* sig */
256         if (!fgets(line, LINEWIDTH, f))
257                 goto bad_file;
258         /* declaration */
259         if (!fgets(line, LINEWIDTH, f))
260                 goto bad_file;
261
262         /* data */
263         if (!fgets(line, LINEWIDTH, f))
264                 goto bad_file;
265
266         if (line[0] == '/')
267                 if (!fgets(line, LINEWIDTH, f))
268                         goto bad_file;
269
270         if (sscanf(line, "\"%i %i %i %i\"", &w, &h, &ccount, &csize) != 4
271             || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
272                 goto bad_file;
273
274         if (csize != 1 && csize != 2)
275                 goto bad_format;
276
277         color_table[0] = alloca(ccount);
278         color_table[1] = alloca(ccount);
279         color_table[2] = alloca(ccount);
280         color_table[3] = alloca(ccount);
281         symbol_table = alloca(ccount * sizeof(unsigned short));
282
283         bsize = csize * w + 16;
284         buffer = alloca(bsize);
285
286         if (!color_table[0] || !color_table[1] || !color_table[2] || !color_table[3] || !symbol_table || !bsize) {
287                 RErrorCode = RERR_NOMEMORY;
288                 fclose(f);
289                 alloca(0);
290                 return NULL;
291         }
292
293         transp = 0;
294         /* get color table */
295         for (i = 0; i < ccount; i++) {
296                 if (!fgets(line, LINEWIDTH, f))
297                         goto bad_file;
298                 if (line[0] == '/')
299                         if (!fgets(line, LINEWIDTH, f))
300                                 goto bad_file;
301
302                 symbol_table[i] = line[1];
303                 if (csize == 2)
304                         symbol_table[i] |= line[2] << 8;
305
306                 j = csize + 1;
307                 while (line[j] != '#' && line[j] != '"' && line[j] != 0 && line[j] != 'N')
308                         j++;
309
310                 if (line[j] == '#') {
311                         unsigned int red, green, blue;
312
313                         k = 0;
314                         j++;
315                         while (line[j + k] != '"' && line[j + k] != 0)
316                                 k++;
317                         if (k == 6) {
318                                 if (sscanf(&(line[j]), "%2x%2x%2x", &red, &green, &blue) != 3)
319                                         goto bad_format;
320                         } else if (k == 12) {
321                                 if (sscanf(&(line[j]), "%4x%4x%4x", &red, &green, &blue) != 3)
322                                         goto bad_format;
323                                 red >>= 8;
324                                 green >>= 8;
325                                 blue >>= 8;
326                         } else
327                                 goto bad_format;
328
329                         color_table[0][i] = red;
330                         color_table[1][i] = green;
331                         color_table[2][i] = blue;
332                         color_table[3][i] = 255;
333                 } else if (strncmp(&(line[j]), "None", 4) == 0 || strncmp(&(line[j]), "none", 4) == 0) {
334                         color_table[3][i] = 0;
335                         transp = 1;
336                 } else {
337                         goto bad_format;
338                 }
339         }
340
341         image = RCreateImage(w, h, transp);
342         if (!image) {
343                 fclose(f);
344                 alloca(0);
345                 return NULL;
346         }
347
348         r = image->data;
349         g = image->data + 1;
350         b = image->data + 2;
351         if (image->format == RRGBAFormat)
352                 a = image->data + 3;
353         else
354                 a = NULL;
355
356         for (i = 0; i < h; i++) {
357                 if (!fgets(buffer, bsize, f))
358                         goto bad_file;
359                 if (buffer[0] == '/')
360                         if (!fgets(buffer, bsize, f))
361                                 goto bad_file;
362
363                 if (csize == 1) {
364                         for (j = 1; j <= w; j++) {
365                                 color = buffer[j];
366
367                                 for (k = 0; k < ccount; k++) {
368                                         if (symbol_table[k] == color)
369                                                 break;
370                                 }
371                                 if (k == ccount)
372                                         k = 0;
373
374                                 *r = color_table[0][k];
375                                 *g = color_table[1][k];
376                                 *b = color_table[2][k];
377                                 if (a) {
378                                         *a = color_table[3][k];
379                                         r += 4;
380                                         g += 4;
381                                         b += 4;
382                                         a += 4;
383                                 } else {
384                                         r += 3;
385                                         g += 3;
386                                         b += 3;
387                                 }
388                         }
389                 } else {
390                         for (j = 1; j <= w * 2; j++) {
391                                 color = buffer[j++];
392                                 color |= buffer[j] << 8;
393
394                                 for (k = 0; k < ccount; k++) {
395                                         if (symbol_table[k] == color)
396                                                 break;
397                                 }
398                                 if (k == ccount) {
399                                         k = 0;
400                                 }
401
402                                 *r = color_table[0][k];
403                                 *g = color_table[1][k];
404                                 *b = color_table[2][k];
405                                 if (a) {
406                                         *a = color_table[3][k];
407                                         r += 4;
408                                         g += 4;
409                                         b += 4;
410                                         a += 4;
411                                 } else {
412                                         r += 3;
413                                         g += 3;
414                                         b += 3;
415                                 }
416                         }
417                 }
418         }
419
420         fclose(f);
421 #ifdef C_ALLOCA
422         alloca(0);
423 #endif
424         return image;
425
426  bad_format:
427         RErrorCode = RERR_BADIMAGEFILE;
428         fclose(f);
429 #ifdef C_ALLOCA
430         alloca(0);
431 #endif
432         if (image)
433                 RReleaseImage(image);
434         return NULL;
435
436  bad_file:
437         RErrorCode = RERR_BADIMAGEFILE;
438         fclose(f);
439 #ifdef C_ALLOCA
440         alloca(0);
441 #endif
442         if (image)
443                 RReleaseImage(image);
444         return NULL;
445 }
446
447 #endif
448
449 typedef struct XPMColor {
450         unsigned char red;
451         unsigned char green;
452         unsigned char blue;
453         int index;
454         struct XPMColor *next;
455 } XPMColor;
456
457 #define I2CHAR(i)       ((i)<12 ? (i)+'0' : ((i)<38 ? (i)+'A'-12 : (i)+'a'-38))
458 #define CINDEX(xpmc)    (((unsigned)(xpmc)->red)<<16|((unsigned)(xpmc)->green)<<8|((unsigned)(xpmc)->blue))
459
460 static XPMColor *lookfor(XPMColor * list, int index)
461 {
462         if (!list)
463                 return NULL;
464
465         for (; list != NULL; list = list->next) {
466                 if (CINDEX(list) == index)
467                         return list;
468         }
469         return NULL;
470 }
471
472 /*
473  * Looks for the color in the colormap and inserts if it is not found.
474  *
475  * list is a binary search list. The unbalancing problem is just ignored.
476  *
477  * Returns False on error
478  */
479 static Bool addcolor(XPMColor ** list, unsigned r, unsigned g, unsigned b, int *colors)
480 {
481         XPMColor *tmpc;
482         XPMColor *newc;
483         int index;
484
485         index = r << 16 | g << 8 | b;
486         tmpc = *list;
487
488         tmpc = lookfor(*list, index);
489
490         if (tmpc)
491                 return True;
492
493         newc = malloc(sizeof(XPMColor));
494
495         if (!newc) {
496
497                 RErrorCode = RERR_NOMEMORY;
498
499                 return False;
500         }
501
502         newc->red = r;
503         newc->green = g;
504         newc->blue = b;
505         newc->next = *list;
506         *list = newc;
507
508         (*colors)++;
509
510         return True;
511 }
512
513 static char *index2str(char *buffer, int index, int charsPerPixel)
514 {
515         int i;
516
517         for (i = 0; i < charsPerPixel; i++) {
518                 buffer[i] = I2CHAR(index & 63);
519                 index >>= 6;
520         }
521         buffer[i] = 0;
522
523         return buffer;
524 }
525
526 static void outputcolormap(FILE * file, XPMColor * colormap, int charsPerPixel)
527 {
528         int index;
529         char buf[128];
530
531         if (!colormap)
532                 return;
533
534         for (index = 0; colormap != NULL; colormap = colormap->next, index++) {
535                 colormap->index = index;
536                 fprintf(file, "\"%s c #%02x%02x%02x\",\n",
537                         index2str(buf, index, charsPerPixel), colormap->red, colormap->green, colormap->blue);
538         }
539 }
540
541 static void freecolormap(XPMColor * colormap)
542 {
543         XPMColor *tmp;
544
545         while (colormap) {
546                 tmp = colormap->next;
547                 free(colormap);
548                 colormap = tmp;
549         }
550 }
551
552 /* save routine is common to internal support and library support */
553 Bool RSaveXPM(RImage * image, char *filename)
554 {
555         FILE *file;
556         int x, y;
557         int colorCount = 0;
558         int charsPerPixel;
559         XPMColor *colormap = NULL;
560         XPMColor *tmpc;
561         int i;
562         int ok = 0;
563         unsigned char *r, *g, *b, *a;
564         char transp[16];
565         char buf[128];
566
567         file = fopen(filename, "wb+");
568         if (!file) {
569                 RErrorCode = RERR_OPEN;
570                 return False;
571         }
572
573         fprintf(file, "/* XPM */\n");
574
575         fprintf(file, "static char *image[] = {\n");
576
577         r = image->data;
578         g = image->data + 1;
579         b = image->data + 2;
580         if (image->format == RRGBAFormat)
581                 a = image->data + 3;
582         else
583                 a = NULL;
584
585         /* first pass: make colormap for the image */
586         if (a)
587                 colorCount = 1;
588         for (y = 0; y < image->height; y++) {
589                 for (x = 0; x < image->width; x++) {
590                         if (!a || *a > 127) {
591                                 if (!addcolor(&colormap, *r, *g, *b, &colorCount)) {
592                                         goto uhoh;
593                                 }
594                         }
595                         if (a) {
596                                 r += 4;
597                                 g += 4;
598                                 b += 4;
599                                 a += 4;
600                         } else {
601                                 r += 3;
602                                 g += 3;
603                                 b += 3;
604                         }
605                 }
606         }
607
608         charsPerPixel = 1;
609         while ((1 << charsPerPixel * 6) < colorCount)
610                 charsPerPixel++;
611
612         /* write header info */
613         fprintf(file, "\"%i %i %i %i\",\n", image->width, image->height, colorCount, charsPerPixel);
614
615         /* write colormap data */
616         if (a) {
617                 for (i = 0; i < charsPerPixel; i++)
618                         transp[i] = ' ';
619                 transp[i] = 0;
620
621                 fprintf(file, "\"%s c None\",\n", transp);
622         }
623
624         i = 0;
625         outputcolormap(file, colormap, charsPerPixel);
626
627         r = image->data;
628         g = image->data + 1;
629         b = image->data + 2;
630         if (image->format == RRGBAFormat)
631                 a = image->data + 3;
632         else
633                 a = NULL;
634
635         /* write data */
636         for (y = 0; y < image->height; y++) {
637
638                 fprintf(file, "\"");
639
640                 for (x = 0; x < image->width; x++) {
641
642                         if (!a || *a > 127) {
643                                 tmpc = lookfor(colormap, (unsigned)*r << 16 | (unsigned)*g << 8 | (unsigned)*b);
644
645                                 fprintf(file, index2str(buf, tmpc->index, charsPerPixel));
646                         } else {
647                                 fprintf(file, transp);
648                         }
649
650                         if (a) {
651                                 r += 4;
652                                 g += 4;
653                                 b += 4;
654                                 a += 4;
655                         } else {
656                                 r += 3;
657                                 g += 3;
658                                 b += 3;
659                         }
660                 }
661
662                 if (y < image->height - 1)
663                         fprintf(file, "\",\n");
664                 else
665                         fprintf(file, "\"};\n");
666         }
667
668         ok = 1;
669  uhoh:
670         errno = 0;
671         fclose(file);
672         if (ok && errno == ENOSPC) {
673                 RErrorCode = RERR_WRITE;
674         }
675
676         freecolormap(colormap);
677
678         return ok ? True : False;
679 }