.
[wmaker-crm.git] / wrlib / nxpm.c
blob560350cd174b9e19b03ace8cb3b895360d73182d
1 /* nxpm.c - load "normalized" XPM image
2 *
3 * Raster graphics library
4 *
5 * Copyright (c) 1997 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., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <config.h>
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
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <assert.h>
46 #include <errno.h>
48 #include "wraster.h"
52 * Restricted support for XPM images.
54 * The images must be in the following "normalized" format:
57 * line content
58 * 1 signature comment
59 * 2 ignored ( normally "static char *xpm[] = {" )
60 * 3 "width height color_count chars" where chars is 1 or 2
61 * 4 color definitions. Only c values with #rrggbb or #rrrrggggbbb
62 * format OR None
63 * n data
65 * - no comments or blank lines are allowed, except for the signature
66 * - all lines must have at most 256 characters
67 * - no white spaces allowed at left of each line
70 #define LINEWIDTH 64
72 #ifndef USE_XPM
75 RImage*
76 RGetImageFromXPMData(RContext *context, char **data)
78 RImage *image = NULL;
79 unsigned char *color_table[4];
80 unsigned short *symbol_table;
81 unsigned char *r, *g, *b, *a;
82 int i, j, k, line = 0;
83 int transp;
84 unsigned short color;
85 int bsize;
86 int w, h, ccount, csize;
88 if (sscanf(data[line++], "%i %i %i %i", &w, &h, &ccount, &csize)!=4
89 || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
90 goto bad_format;
92 if (csize!=1 && csize!=2)
93 goto bad_format;
95 color_table[0] = alloca(ccount);
96 color_table[1] = alloca(ccount);
97 color_table[2] = alloca(ccount);
98 color_table[3] = alloca(ccount);
99 symbol_table = alloca(ccount * sizeof(unsigned short));
101 bsize = csize * w + 16;
103 if (!color_table[0] || !color_table[1] || !color_table[2] ||
104 !color_table[3] || !symbol_table || !bsize) {
105 RErrorCode = RERR_NOMEMORY;
106 alloca(0);
107 return NULL;
110 transp = 0;
111 /* get color table */
112 for (i=0; i<ccount; i++) {
113 symbol_table[i] = data[line][0];
114 if (csize==2)
115 symbol_table[i] |= data[line][1]<<8;
117 j = csize;
118 while (data[line][j]!='#' && data[line][j]!=0
119 && data[line][j]!='N') j++;
121 if (data[line][j]=='#') {
122 unsigned int red, green, blue;
124 k = 0;
125 j++;
126 while (data[line][j+k]!=0) k++;
127 if (k==6) {
128 if (sscanf(&(data[line][j]), "%2x%2x%2x", &red, &green, &blue)!=3)
129 goto bad_format;
130 } else if (k==12) {
131 if (sscanf(&(data[line][j]), "%4x%4x%4x", &red, &green, &blue)!=3)
132 goto bad_format;
133 red >>= 8;
134 green >>= 8;
135 blue >>= 8;
136 } else
137 goto bad_format;
139 color_table[0][i] = red;
140 color_table[1][i] = green;
141 color_table[2][i] = blue;
142 color_table[3][i] = 255;
143 } else if (strncmp(&(data[line][j]), "None", 4)==0
144 || strncmp(&(data[line][j]), "none", 4)==0) {
145 color_table[3][i] = 0;
146 transp = 1;
147 } else {
148 goto bad_format;
150 line++;
153 image = RCreateImage(w, h, transp);
154 if (!image) {
155 alloca(0);
156 return NULL;
159 r = image->data;
160 g = image->data+1;
161 b = image->data+2;
162 if (image->format == RRGBAFormat)
163 a = image->data+3;
164 else
165 a = NULL;
167 for (i=0; i<h; i++) {
168 if (csize==1) {
169 for (j=0; j<w; j++) {
170 color = data[line][j];
172 for (k=0; k<ccount; k++) {
173 if (symbol_table[k] == color)
174 break;
176 if (k==ccount)
177 k = 0;
179 *(r++) = color_table[0][k];
180 *(g++) = color_table[1][k];
181 *(b++) = color_table[2][k];
182 if (a)
183 *(a++) = color_table[3][k];
185 } else {
186 for (j=0; j<w*2; j++) {
187 color = data[line][j++];
188 color |= data[line][j];
190 for (k=0; k<ccount; k++) {
191 if (symbol_table[k] == color)
192 break;
194 if (k==ccount)
195 k = 0;
197 *(r++) = color_table[0][k];
198 *(g++) = color_table[1][k];
199 *(b++) = color_table[2][k];
200 if (a)
201 *(a++) = color_table[3][k];
204 line++;
207 #ifdef C_ALLOCA
208 alloca(0);
209 #endif
210 return image;
212 bad_format:
213 RErrorCode = RERR_BADIMAGEFILE;
214 #ifdef C_ALLOCA
215 alloca(0);
216 #endif
217 if (image)
218 RDestroyImage(image);
219 return NULL;
224 RImage*
225 RLoadXPM(RContext *context, char *file, int index)
227 RImage *image = NULL;
228 char line[LINEWIDTH+1];
229 char *buffer;
230 unsigned char *color_table[4];
231 unsigned short *symbol_table;
232 unsigned char *r, *g, *b, *a;
233 int i, j, k;
234 int transp;
235 unsigned short color;
236 int bsize;
237 int w, h, ccount, csize;
238 FILE *f;
240 f = fopen(file, "r");
241 if (!f) {
242 RErrorCode = RERR_OPEN;
243 return NULL;
245 /* sig */
246 if (!fgets(line, LINEWIDTH, f))
247 goto bad_file;
248 /* declaration */
249 if (!fgets(line, LINEWIDTH, f))
250 goto bad_file;
252 /* data */
253 if (!fgets(line, LINEWIDTH, f))
254 goto bad_file;
256 if (line[0]=='/')
257 if (!fgets(line, LINEWIDTH, f))
258 goto bad_file;
260 if (sscanf(line, "\"%i %i %i %i\"", &w, &h, &ccount, &csize)!=4
261 || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
262 goto bad_file;
264 if (csize!=1 && csize!=2)
265 goto bad_format;
267 color_table[0] = alloca(ccount);
268 color_table[1] = alloca(ccount);
269 color_table[2] = alloca(ccount);
270 color_table[3] = alloca(ccount);
271 symbol_table = alloca(ccount * sizeof(unsigned short));
273 bsize = csize * w + 16;
274 buffer = alloca(bsize);
276 if (!color_table[0] || !color_table[1] || !color_table[2] ||
277 !color_table[3] || !symbol_table || !bsize) {
278 RErrorCode = RERR_NOMEMORY;
279 fclose(f);
280 alloca(0);
281 return NULL;
284 transp = 0;
285 /* get color table */
286 for (i=0; i<ccount; i++) {
287 if (!fgets(line, LINEWIDTH, f))
288 goto bad_file;
289 if (line[0]=='/')
290 if (!fgets(line, LINEWIDTH, f))
291 goto bad_file;
293 symbol_table[i] = line[1];
294 if (csize==2)
295 symbol_table[i] |= line[2]<<8;
297 j = csize+1;
298 while (line[j]!='#' && line[j]!='"' && line[j]!=0 && line[j]!='N') j++;
300 if (line[j]=='#') {
301 unsigned int red, green, blue;
303 k = 0;
304 j++;
305 while (line[j+k]!='"' && line[j+k]!=0) k++;
306 if (k==6) {
307 if (sscanf(&(line[j]), "%2x%2x%2x", &red, &green, &blue)!=3)
308 goto bad_format;
309 } else if (k==12) {
310 if (sscanf(&(line[j]), "%4x%4x%4x", &red, &green, &blue)!=3)
311 goto bad_format;
312 red >>= 8;
313 green >>= 8;
314 blue >>= 8;
315 } else
316 goto bad_format;
318 color_table[0][i] = red;
319 color_table[1][i] = green;
320 color_table[2][i] = blue;
321 color_table[3][i] = 255;
322 } else if (strncmp(&(line[j]), "None", 4)==0
323 || strncmp(&(line[j]), "none", 4)==0) {
324 color_table[3][i] = 0;
325 transp = 1;
326 } else {
327 goto bad_format;
331 image = RCreateImage(w, h, transp);
332 if (!image) {
333 fclose(f);
334 alloca(0);
335 return NULL;
338 r = image->data;
339 g = image->data+1;
340 b = image->data+2;
341 if (image->format == RRGBAFormat)
342 a = image->data+3;
343 else
344 a = NULL;
346 for (i=0; i<h; i++) {
347 if (!fgets(buffer, bsize, f))
348 goto bad_file;
349 if (buffer[0]=='/')
350 if (!fgets(buffer, bsize, f))
351 goto bad_file;
353 if (csize==1) {
354 for (j=1; j<=w; j++) {
355 color = buffer[j];
357 for (k=0; k<ccount; k++) {
358 if (symbol_table[k] == color)
359 break;
361 if (k==ccount)
362 k = 0;
364 *(r++) = color_table[0][k];
365 *(g++) = color_table[1][k];
366 *(b++) = color_table[2][k];
367 if (a)
368 *(a++) = color_table[3][k];
370 } else {
371 for (j=1; j<=w*2; j++) {
372 color = buffer[j++];
373 color |= buffer[j] << 8;
375 for (k=0; k<ccount; k++) {
376 if (symbol_table[k] == color)
377 break;
379 if (k==ccount) {
380 k = 0;
383 *(r++) = color_table[0][k];
384 *(g++) = color_table[1][k];
385 *(b++) = color_table[2][k];
386 if (a)
387 *(a++) = color_table[3][k];
392 fclose(f);
393 #ifdef C_ALLOCA
394 alloca(0);
395 #endif
396 return image;
398 bad_format:
399 RErrorCode = RERR_BADIMAGEFILE;
400 fclose(f);
401 #ifdef C_ALLOCA
402 alloca(0);
403 #endif
404 if (image)
405 RDestroyImage(image);
406 return NULL;
408 bad_file:
409 RErrorCode = RERR_BADIMAGEFILE;
410 fclose(f);
411 #ifdef C_ALLOCA
412 alloca(0);
413 #endif
414 if (image)
415 RDestroyImage(image);
416 return NULL;
419 #endif
422 typedef struct XPMColor {
423 unsigned char red;
424 unsigned char green;
425 unsigned char blue;
426 int index;
427 struct XPMColor *next;
428 } XPMColor;
432 #define I2CHAR(i) ((i)<12 ? (i)+'0' : ((i)<38 ? (i)+'A'-12 : (i)+'a'-38))
433 #define CINDEX(xpmc) (((unsigned)(xpmc)->red)<<16|((unsigned)(xpmc)->green)<<8|((unsigned)(xpmc)->blue))
437 static XPMColor*
438 lookfor(XPMColor *list, int index)
440 if (!list)
441 return NULL;
443 for (; list!=NULL; list=list->next) {
444 if (CINDEX(list) == index)
445 return list;
447 return NULL;
451 * Looks for the color in the colormap and inserts if it is not found.
453 * list is a binary search list. The unbalancing problem is just ignored.
455 * Returns False on error
457 static Bool
458 addcolor(XPMColor **list, unsigned r, unsigned g, unsigned b, int *colors)
460 XPMColor *tmpc;
461 XPMColor *newc;
462 int index;
464 index = r<<16|g<<8|b;
465 tmpc = *list;
467 tmpc = lookfor(*list, index);
469 if (tmpc)
470 return True;
472 newc = malloc(sizeof(XPMColor));
474 if (!newc) {
476 RErrorCode = RERR_NOMEMORY;
478 return False;
481 newc->red = r;
482 newc->green = g;
483 newc->blue = b;
484 newc->next = *list;
485 *list = newc;
487 (*colors)++;
489 return True;
493 static char*
494 index2str(char *buffer, int index, int charsPerPixel)
496 int i;
498 for (i=0; i<charsPerPixel; i++) {
499 buffer[i] = I2CHAR(index&63);
500 index >>= 6;
502 buffer[i] = 0;
504 return buffer;
508 static void
509 outputcolormap(FILE *file, XPMColor *colormap, int charsPerPixel)
511 int index;
512 char buf[128];
514 if (!colormap)
515 return;
517 for (index=0; colormap!=NULL; colormap=colormap->next,index++) {
518 colormap->index = index;
519 fprintf(file, "\"%s c #%02x%02x%02x\",\n",
520 index2str(buf, index, charsPerPixel), colormap->red,
521 colormap->green, colormap->blue);
526 static void
527 freecolormap(XPMColor *colormap)
529 XPMColor *tmp;
531 while (colormap) {
532 tmp = colormap->next;
533 free(colormap);
534 colormap = tmp;
540 /* save routine is common to internal support and library support */
541 Bool
542 RSaveXPM(RImage *image, char *filename)
544 FILE *file;
545 int x, y;
546 int colorCount=0;
547 int charsPerPixel;
548 XPMColor *colormap = NULL;
549 XPMColor *tmpc;
550 int i;
551 int ok = 0;
552 unsigned char *r, *g, *b, *a;
553 char transp[16];
554 char buf[128];
556 file = fopen(filename, "w+");
557 if (!file) {
558 RErrorCode = RERR_OPEN;
559 return False;
562 fprintf(file, "/* XPM */\n");
564 fprintf(file, "static char *image[] = {\n");
566 r = image->data;
567 g = image->data+1;
568 b = image->data+2;
569 if (image->format == RRGBAFormat)
570 a = image->data+3;
571 else
572 a = NULL;
574 /* first pass: make colormap for the image */
575 if (a)
576 colorCount = 1;
577 for (y = 0; y < image->height; y++) {
578 for (x = 0; x < image->width; x++) {
579 if (!a || *a++>127)
580 if (!addcolor(&colormap, *r, *g, *b, &colorCount)) {
581 goto uhoh;
583 r++; g++; b++;
587 charsPerPixel = 1;
588 while ((1 << charsPerPixel*6) < colorCount)
589 charsPerPixel++;
591 /* write header info */
592 fprintf(file, "\"%i %i %i %i\",\n", image->width, image->height,
593 colorCount, charsPerPixel);
595 /* write colormap data */
596 if (image->data[3]) {
597 for (i=0; i<charsPerPixel; i++)
598 transp[i] = ' ';
599 transp[i] = 0;
601 fprintf(file, "\"%s c None\",\n", transp);
604 i = 0;
605 outputcolormap(file, colormap, charsPerPixel);
608 r = image->data;
609 g = image->data+1;
610 b = image->data+2;
611 if (image->format == RRGBAFormat)
612 a = image->data+3;
613 else
614 a = NULL;
616 /* write data */
617 for (y = 0; y < image->height; y++) {
619 fprintf(file, "\"");
621 for (x = 0; x < image->width; x++) {
623 if (!a || *a++>127) {
624 tmpc = lookfor(colormap, (unsigned)*r<<16|(unsigned)*g<<8|(unsigned)*b);
626 fprintf(file, index2str(buf, tmpc->index, charsPerPixel));
627 } else {
628 fprintf(file, transp);
631 r++; g++; b++;
634 if (y < image->height-1)
635 fprintf(file, "\",\n");
636 else
637 fprintf(file, "\"};\n");
640 ok = 1;
641 uhoh:
642 errno = 0;
643 fclose(file);
644 if (ok && errno==ENOSPC) {
645 RErrorCode = RERR_WRITE;
648 freecolormap(colormap);
650 return ok ? True : False;