wmaker: Moved function prototype to the appropriate header
[wmaker-crm.git] / wrlib / nxpm.c
blob37a48e8a7f6b731466e09fbb30ec2878494ba8ee
1 /* nxpm.c - load "normalized" XPM image
3 * Raster graphics library
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
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.
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.
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., 51 Franklin St, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
23 #include <config.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
31 #include "wraster.h"
32 #include "imgformat.h"
35 * Restricted support for XPM images.
37 * The images must be in the following "normalized" format:
40 * line content
41 * 1 signature comment
42 * 2 ignored ( normally "static char *xpm[] = {" )
43 * 3 "width height color_count chars" where chars is 1 or 2
44 * 4 color definitions. Only c values with #rrggbb or #rrrrggggbbb
45 * format OR None
46 * n data
48 * - no comments or blank lines are allowed, except for the signature
49 * - all lines must have at most 256 characters
50 * - no white spaces allowed at left of each line
53 #define LINEWIDTH 64
55 #ifndef USE_XPM
57 static void free_color_symbol_table(unsigned char *color_table[],
58 unsigned short *symbol_table)
60 if (color_table[0])
61 free(color_table[0]);
62 if (color_table[1])
63 free(color_table[1]);
64 if (color_table[2])
65 free(color_table[2]);
66 if (color_table[3])
67 free(color_table[3]);
68 if (symbol_table)
69 free(symbol_table);
72 RImage *RGetImageFromXPMData(RContext * context, char **data)
74 RImage *image = NULL;
75 unsigned char *color_table[4] = { NULL, NULL, NULL, NULL };
76 unsigned short *symbol_table = NULL;
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;
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;
88 if (csize != 1 && csize != 2)
89 goto bad_format;
91 color_table[0] = malloc(ccount);
92 color_table[1] = malloc(ccount);
93 color_table[2] = malloc(ccount);
94 color_table[3] = malloc(ccount);
95 symbol_table = malloc(ccount * sizeof(unsigned short));
97 bsize = csize * w + 16;
99 if (!color_table[0] || !color_table[1] || !color_table[2] || !color_table[3] || !symbol_table || !bsize) {
100 RErrorCode = RERR_NOMEMORY;
101 free_color_symbol_table(color_table, symbol_table);
102 return NULL;
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;
112 j = csize;
113 while (data[line][j] != '#' && data[line][j] != 0 && data[line][j] != 'N')
114 j++;
116 if (data[line][j] == '#') {
117 unsigned int red, green, blue;
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;
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;
145 line++;
148 image = RCreateImage(w, h, transp);
149 if (!image) {
150 free_color_symbol_table(color_table, symbol_table);
151 return NULL;
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;
162 for (i = 0; i < h; i++) {
163 if (csize == 1) {
164 for (j = 0; j < w; j++) {
165 color = data[line][j];
167 for (k = 0; k < ccount; k++) {
168 if (symbol_table[k] == color)
169 break;
171 if (k == ccount)
172 k = 0;
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;
189 } else {
190 for (j = 0; j < w * 2; j++) {
191 color = data[line][j++];
192 color |= data[line][j];
194 for (k = 0; k < ccount; k++) {
195 if (symbol_table[k] == color)
196 break;
198 if (k == ccount)
199 k = 0;
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;
217 line++;
220 free_color_symbol_table(color_table, symbol_table);
221 return image;
223 bad_format:
224 RErrorCode = RERR_BADIMAGEFILE;
225 free_color_symbol_table(color_table, symbol_table);
226 if (image)
227 RReleaseImage(image);
228 return NULL;
231 RImage *RLoadXPM(RContext * context, const char *file)
233 RImage *image = NULL;
234 char line[LINEWIDTH + 1];
235 char *buffer = NULL;
236 unsigned char *color_table[4] = { NULL, NULL, NULL, NULL };
237 unsigned short *symbol_table = NULL;
238 unsigned char *r, *g, *b, *a;
239 int i, j, k;
240 int transp;
241 unsigned short color;
242 int bsize;
243 int w, h, ccount, csize;
244 FILE *f;
246 f = fopen(file, "rb");
247 if (!f) {
248 RErrorCode = RERR_OPEN;
249 return NULL;
251 /* sig */
252 if (!fgets(line, LINEWIDTH, f))
253 goto bad_file;
254 /* declaration */
255 if (!fgets(line, LINEWIDTH, f))
256 goto bad_file;
258 /* data */
259 if (!fgets(line, LINEWIDTH, f))
260 goto bad_file;
262 if (line[0] == '/')
263 if (!fgets(line, LINEWIDTH, f))
264 goto bad_file;
266 if (sscanf(line, "\"%i %i %i %i\"", &w, &h, &ccount, &csize) != 4
267 || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
268 goto bad_file;
270 if (csize != 1 && csize != 2)
271 goto bad_format;
273 color_table[0] = malloc(ccount);
274 color_table[1] = malloc(ccount);
275 color_table[2] = malloc(ccount);
276 color_table[3] = malloc(ccount);
277 symbol_table = malloc(ccount * sizeof(unsigned short));
279 bsize = csize * w + 16;
280 buffer = malloc(bsize);
282 if (!color_table[0] || !color_table[1] || !color_table[2] ||
283 !color_table[3] || !symbol_table || !bsize || !buffer) {
284 RErrorCode = RERR_NOMEMORY;
285 fclose(f);
286 free_color_symbol_table(color_table, symbol_table);
287 if (buffer)
288 free(buffer);
289 return NULL;
292 transp = 0;
293 /* get color table */
294 for (i = 0; i < ccount; i++) {
295 if (!fgets(line, LINEWIDTH, f))
296 goto bad_file;
297 if (line[0] == '/')
298 if (!fgets(line, LINEWIDTH, f))
299 goto bad_file;
301 symbol_table[i] = line[1];
302 if (csize == 2)
303 symbol_table[i] |= line[2] << 8;
305 j = csize + 1;
306 while (line[j] != '#' && line[j] != '"' && line[j] != 0 && line[j] != 'N')
307 j++;
309 if (line[j] == '#') {
310 unsigned int red, green, blue;
312 k = 0;
313 j++;
314 while (line[j + k] != '"' && line[j + k] != 0)
315 k++;
316 if (k == 6) {
317 if (sscanf(&(line[j]), "%2x%2x%2x", &red, &green, &blue) != 3)
318 goto bad_format;
319 } else if (k == 12) {
320 if (sscanf(&(line[j]), "%4x%4x%4x", &red, &green, &blue) != 3)
321 goto bad_format;
322 red >>= 8;
323 green >>= 8;
324 blue >>= 8;
325 } else
326 goto bad_format;
328 color_table[0][i] = red;
329 color_table[1][i] = green;
330 color_table[2][i] = blue;
331 color_table[3][i] = 255;
332 } else if (strncmp(&(line[j]), "None", 4) == 0 || strncmp(&(line[j]), "none", 4) == 0) {
333 color_table[3][i] = 0;
334 transp = 1;
335 } else {
336 goto bad_format;
340 image = RCreateImage(w, h, transp);
341 if (!image) {
342 fclose(f);
343 free_color_symbol_table(color_table, symbol_table);
344 if (buffer)
345 free(buffer);
346 return NULL;
349 r = image->data;
350 g = image->data + 1;
351 b = image->data + 2;
352 if (image->format == RRGBAFormat)
353 a = image->data + 3;
354 else
355 a = NULL;
357 for (i = 0; i < h; i++) {
358 if (!fgets(buffer, bsize, f))
359 goto bad_file;
360 if (buffer[0] == '/')
361 if (!fgets(buffer, bsize, f))
362 goto bad_file;
364 if (csize == 1) {
365 for (j = 1; j <= w; j++) {
366 color = buffer[j];
368 for (k = 0; k < ccount; k++) {
369 if (symbol_table[k] == color)
370 break;
372 if (k == ccount)
373 k = 0;
375 *r = color_table[0][k];
376 *g = color_table[1][k];
377 *b = color_table[2][k];
378 if (a) {
379 *a = color_table[3][k];
380 r += 4;
381 g += 4;
382 b += 4;
383 a += 4;
384 } else {
385 r += 3;
386 g += 3;
387 b += 3;
390 } else {
391 for (j = 1; j <= w * 2; j++) {
392 color = buffer[j++];
393 color |= buffer[j] << 8;
395 for (k = 0; k < ccount; k++) {
396 if (symbol_table[k] == color)
397 break;
399 if (k == ccount) {
400 k = 0;
403 *r = color_table[0][k];
404 *g = color_table[1][k];
405 *b = color_table[2][k];
406 if (a) {
407 *a = color_table[3][k];
408 r += 4;
409 g += 4;
410 b += 4;
411 a += 4;
412 } else {
413 r += 3;
414 g += 3;
415 b += 3;
421 fclose(f);
422 free_color_symbol_table(color_table, symbol_table);
423 if (buffer)
424 free(buffer);
425 return image;
427 bad_format:
428 RErrorCode = RERR_BADIMAGEFILE;
429 fclose(f);
430 free_color_symbol_table(color_table, symbol_table);
431 if (buffer)
432 free(buffer);
433 if (image)
434 RReleaseImage(image);
435 return NULL;
437 bad_file:
438 RErrorCode = RERR_BADIMAGEFILE;
439 fclose(f);
440 free_color_symbol_table(color_table, symbol_table);
441 if (buffer)
442 free(buffer);
443 if (image)
444 RReleaseImage(image);
445 return NULL;
448 #endif
450 typedef struct XPMColor {
451 unsigned char red;
452 unsigned char green;
453 unsigned char blue;
454 int index;
455 struct XPMColor *next;
456 } XPMColor;
458 #define I2CHAR(i) ((i)<12 ? (i)+'0' : ((i)<38 ? (i)+'A'-12 : (i)+'a'-38))
459 #define CINDEX(xpmc) (((unsigned)(xpmc)->red)<<16|((unsigned)(xpmc)->green)<<8|((unsigned)(xpmc)->blue))
461 static XPMColor *lookfor(XPMColor * list, int index)
463 if (!list)
464 return NULL;
466 for (; list != NULL; list = list->next) {
467 if (CINDEX(list) == index)
468 return list;
470 return NULL;
474 * Looks for the color in the colormap and inserts if it is not found.
476 * list is a binary search list. The unbalancing problem is just ignored.
478 * Returns False on error
480 static Bool addcolor(XPMColor ** list, unsigned r, unsigned g, unsigned b, int *colors)
482 XPMColor *tmpc;
483 XPMColor *newc;
484 int index;
486 index = r << 16 | g << 8 | b;
487 tmpc = *list;
489 tmpc = lookfor(*list, index);
491 if (tmpc)
492 return True;
494 newc = malloc(sizeof(XPMColor));
496 if (!newc) {
498 RErrorCode = RERR_NOMEMORY;
500 return False;
503 newc->red = r;
504 newc->green = g;
505 newc->blue = b;
506 newc->next = *list;
507 *list = newc;
509 (*colors)++;
511 return True;
514 static char *index2str(char *buffer, int index, int charsPerPixel)
516 int i;
518 for (i = 0; i < charsPerPixel; i++) {
519 buffer[i] = I2CHAR(index & 63);
520 index >>= 6;
522 buffer[i] = 0;
524 return buffer;
527 static void outputcolormap(FILE * file, XPMColor * colormap, int charsPerPixel)
529 int index;
530 char buf[128];
532 if (!colormap)
533 return;
535 for (index = 0; colormap != NULL; colormap = colormap->next, index++) {
536 colormap->index = index;
537 fprintf(file, "\"%s c #%02x%02x%02x\",\n",
538 index2str(buf, index, charsPerPixel), colormap->red, colormap->green, colormap->blue);
542 static void freecolormap(XPMColor * colormap)
544 XPMColor *tmp;
546 while (colormap) {
547 tmp = colormap->next;
548 free(colormap);
549 colormap = tmp;
553 /* save routine is common to internal support and library support */
554 Bool RSaveXPM(RImage * image, const char *filename)
556 FILE *file;
557 int x, y;
558 int colorCount = 0;
559 int charsPerPixel;
560 XPMColor *colormap = NULL;
561 XPMColor *tmpc;
562 int i;
563 int ok = 0;
564 unsigned char *r, *g, *b, *a;
565 char transp[16];
566 char buf[128];
568 file = fopen(filename, "wb+");
569 if (!file) {
570 RErrorCode = RERR_OPEN;
571 return False;
574 fprintf(file, "/* XPM */\n");
576 fprintf(file, "static char *image[] = {\n");
578 r = image->data;
579 g = image->data + 1;
580 b = image->data + 2;
581 if (image->format == RRGBAFormat)
582 a = image->data + 3;
583 else
584 a = NULL;
586 /* first pass: make colormap for the image */
587 if (a)
588 colorCount = 1;
589 for (y = 0; y < image->height; y++) {
590 for (x = 0; x < image->width; x++) {
591 if (!a || *a > 127) {
592 if (!addcolor(&colormap, *r, *g, *b, &colorCount)) {
593 goto uhoh;
596 if (a) {
597 r += 4;
598 g += 4;
599 b += 4;
600 a += 4;
601 } else {
602 r += 3;
603 g += 3;
604 b += 3;
609 charsPerPixel = 1;
610 while ((1 << charsPerPixel * 6) < colorCount)
611 charsPerPixel++;
613 /* write header info */
614 fprintf(file, "\"%i %i %i %i\",\n", image->width, image->height, colorCount, charsPerPixel);
616 /* write colormap data */
617 if (a) {
618 for (i = 0; i < charsPerPixel; i++)
619 transp[i] = ' ';
620 transp[i] = 0;
622 fprintf(file, "\"%s c None\",\n", transp);
625 i = 0;
626 outputcolormap(file, colormap, charsPerPixel);
628 r = image->data;
629 g = image->data + 1;
630 b = image->data + 2;
631 if (image->format == RRGBAFormat)
632 a = image->data + 3;
633 else
634 a = NULL;
636 /* write data */
637 for (y = 0; y < image->height; y++) {
639 fprintf(file, "\"");
641 for (x = 0; x < image->width; x++) {
643 if (!a || *a > 127) {
644 tmpc = lookfor(colormap, (unsigned)*r << 16 | (unsigned)*g << 8 | (unsigned)*b);
646 fprintf(file, "%s", index2str(buf, tmpc->index, charsPerPixel));
647 } else {
648 fprintf(file, "%s", transp);
651 if (a) {
652 r += 4;
653 g += 4;
654 b += 4;
655 a += 4;
656 } else {
657 r += 3;
658 g += 3;
659 b += 3;
663 if (y < image->height - 1)
664 fprintf(file, "\",\n");
665 else
666 fprintf(file, "\"};\n");
669 ok = 1;
670 uhoh:
671 errno = 0;
672 fclose(file);
673 if (ok && errno == ENOSPC) {
674 RErrorCode = RERR_WRITE;
677 freecolormap(colormap);
679 return ok ? True : False;