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 }