updated news file
[wmaker-crm.git] / wrlib / nxpm.c
blob33b8806c0c38bbb2fcd5b7460e3871011ba65ec4
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., 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];
184 r += 4; g += 4; b += 4; a += 4;
185 } else {
186 r += 3; g += 3; 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; g += 4; b += 4; a += 4;
207 } else {
208 r += 3; g += 3; b += 3;
212 line++;
215 #ifdef C_ALLOCA
216 alloca(0);
217 #endif
218 return image;
220 bad_format:
221 RErrorCode = RERR_BADIMAGEFILE;
222 #ifdef C_ALLOCA
223 alloca(0);
224 #endif
225 if (image)
226 RReleaseImage(image);
227 return NULL;
232 RImage*
233 RLoadXPM(RContext *context, char *file, int index)
235 RImage *image = NULL;
236 char line[LINEWIDTH+1];
237 char *buffer;
238 unsigned char *color_table[4];
239 unsigned short *symbol_table;
240 unsigned char *r, *g, *b, *a;
241 int i, j, k;
242 int transp;
243 unsigned short color;
244 int bsize;
245 int w, h, ccount, csize;
246 FILE *f;
248 f = fopen(file, "rb");
249 if (!f) {
250 RErrorCode = RERR_OPEN;
251 return NULL;
253 /* sig */
254 if (!fgets(line, LINEWIDTH, f))
255 goto bad_file;
256 /* declaration */
257 if (!fgets(line, LINEWIDTH, f))
258 goto bad_file;
260 /* data */
261 if (!fgets(line, LINEWIDTH, f))
262 goto bad_file;
264 if (line[0]=='/')
265 if (!fgets(line, LINEWIDTH, f))
266 goto bad_file;
268 if (sscanf(line, "\"%i %i %i %i\"", &w, &h, &ccount, &csize)!=4
269 || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
270 goto bad_file;
272 if (csize!=1 && csize!=2)
273 goto bad_format;
275 color_table[0] = alloca(ccount);
276 color_table[1] = alloca(ccount);
277 color_table[2] = alloca(ccount);
278 color_table[3] = alloca(ccount);
279 symbol_table = alloca(ccount * sizeof(unsigned short));
281 bsize = csize * w + 16;
282 buffer = alloca(bsize);
284 if (!color_table[0] || !color_table[1] || !color_table[2] ||
285 !color_table[3] || !symbol_table || !bsize) {
286 RErrorCode = RERR_NOMEMORY;
287 fclose(f);
288 alloca(0);
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') j++;
308 if (line[j]=='#') {
309 unsigned int red, green, blue;
311 k = 0;
312 j++;
313 while (line[j+k]!='"' && line[j+k]!=0) k++;
314 if (k==6) {
315 if (sscanf(&(line[j]), "%2x%2x%2x", &red, &green, &blue)!=3)
316 goto bad_format;
317 } else if (k==12) {
318 if (sscanf(&(line[j]), "%4x%4x%4x", &red, &green, &blue)!=3)
319 goto bad_format;
320 red >>= 8;
321 green >>= 8;
322 blue >>= 8;
323 } else
324 goto bad_format;
326 color_table[0][i] = red;
327 color_table[1][i] = green;
328 color_table[2][i] = blue;
329 color_table[3][i] = 255;
330 } else if (strncmp(&(line[j]), "None", 4)==0
331 || strncmp(&(line[j]), "none", 4)==0) {
332 color_table[3][i] = 0;
333 transp = 1;
334 } else {
335 goto bad_format;
339 image = RCreateImage(w, h, transp);
340 if (!image) {
341 fclose(f);
342 alloca(0);
343 return NULL;
346 r = image->data;
347 g = image->data+1;
348 b = image->data+2;
349 if (image->format == RRGBAFormat)
350 a = image->data+3;
351 else
352 a = NULL;
354 for (i=0; i<h; i++) {
355 if (!fgets(buffer, bsize, f))
356 goto bad_file;
357 if (buffer[0]=='/')
358 if (!fgets(buffer, bsize, f))
359 goto bad_file;
361 if (csize==1) {
362 for (j=1; j<=w; j++) {
363 color = buffer[j];
365 for (k=0; k<ccount; k++) {
366 if (symbol_table[k] == color)
367 break;
369 if (k==ccount)
370 k = 0;
372 *r = color_table[0][k];
373 *g = color_table[1][k];
374 *b = color_table[2][k];
375 if (a) {
376 *a = color_table[3][k];
377 r += 4; g += 4; b += 4; a += 4;
378 } else {
379 r += 3; g += 3; b += 3;
382 } else {
383 for (j=1; j<=w*2; j++) {
384 color = buffer[j++];
385 color |= buffer[j] << 8;
387 for (k=0; k<ccount; k++) {
388 if (symbol_table[k] == color)
389 break;
391 if (k==ccount) {
392 k = 0;
395 *r = color_table[0][k];
396 *g = color_table[1][k];
397 *b = color_table[2][k];
398 if (a) {
399 *a = color_table[3][k];
400 r += 4; g += 4; b += 4; a += 4;
401 } else {
402 r += 3; g += 3; b += 3;
408 fclose(f);
409 #ifdef C_ALLOCA
410 alloca(0);
411 #endif
412 return image;
414 bad_format:
415 RErrorCode = RERR_BADIMAGEFILE;
416 fclose(f);
417 #ifdef C_ALLOCA
418 alloca(0);
419 #endif
420 if (image)
421 RReleaseImage(image);
422 return NULL;
424 bad_file:
425 RErrorCode = RERR_BADIMAGEFILE;
426 fclose(f);
427 #ifdef C_ALLOCA
428 alloca(0);
429 #endif
430 if (image)
431 RReleaseImage(image);
432 return NULL;
435 #endif
438 typedef struct XPMColor {
439 unsigned char red;
440 unsigned char green;
441 unsigned char blue;
442 int index;
443 struct XPMColor *next;
444 } XPMColor;
448 #define I2CHAR(i) ((i)<12 ? (i)+'0' : ((i)<38 ? (i)+'A'-12 : (i)+'a'-38))
449 #define CINDEX(xpmc) (((unsigned)(xpmc)->red)<<16|((unsigned)(xpmc)->green)<<8|((unsigned)(xpmc)->blue))
453 static XPMColor*
454 lookfor(XPMColor *list, int index)
456 if (!list)
457 return NULL;
459 for (; list!=NULL; list=list->next) {
460 if (CINDEX(list) == index)
461 return list;
463 return NULL;
467 * Looks for the color in the colormap and inserts if it is not found.
469 * list is a binary search list. The unbalancing problem is just ignored.
471 * Returns False on error
473 static Bool
474 addcolor(XPMColor **list, unsigned r, unsigned g, unsigned b, int *colors)
476 XPMColor *tmpc;
477 XPMColor *newc;
478 int index;
480 index = r<<16|g<<8|b;
481 tmpc = *list;
483 tmpc = lookfor(*list, index);
485 if (tmpc)
486 return True;
488 newc = malloc(sizeof(XPMColor));
490 if (!newc) {
492 RErrorCode = RERR_NOMEMORY;
494 return False;
497 newc->red = r;
498 newc->green = g;
499 newc->blue = b;
500 newc->next = *list;
501 *list = newc;
503 (*colors)++;
505 return True;
509 static char*
510 index2str(char *buffer, int index, int charsPerPixel)
512 int i;
514 for (i=0; i<charsPerPixel; i++) {
515 buffer[i] = I2CHAR(index&63);
516 index >>= 6;
518 buffer[i] = 0;
520 return buffer;
524 static void
525 outputcolormap(FILE *file, XPMColor *colormap, int charsPerPixel)
527 int index;
528 char buf[128];
530 if (!colormap)
531 return;
533 for (index=0; colormap!=NULL; colormap=colormap->next,index++) {
534 colormap->index = index;
535 fprintf(file, "\"%s c #%02x%02x%02x\",\n",
536 index2str(buf, index, charsPerPixel), colormap->red,
537 colormap->green, colormap->blue);
542 static void
543 freecolormap(XPMColor *colormap)
545 XPMColor *tmp;
547 while (colormap) {
548 tmp = colormap->next;
549 free(colormap);
550 colormap = tmp;
556 /* save routine is common to internal support and library support */
557 Bool
558 RSaveXPM(RImage *image, char *filename)
560 FILE *file;
561 int x, y;
562 int colorCount=0;
563 int charsPerPixel;
564 XPMColor *colormap = NULL;
565 XPMColor *tmpc;
566 int i;
567 int ok = 0;
568 unsigned char *r, *g, *b, *a;
569 char transp[16];
570 char buf[128];
572 file = fopen(filename, "wb+");
573 if (!file) {
574 RErrorCode = RERR_OPEN;
575 return False;
578 fprintf(file, "/* XPM */\n");
580 fprintf(file, "static char *image[] = {\n");
582 r = image->data;
583 g = image->data+1;
584 b = image->data+2;
585 if (image->format == RRGBAFormat)
586 a = image->data+3;
587 else
588 a = NULL;
590 /* first pass: make colormap for the image */
591 if (a)
592 colorCount = 1;
593 for (y = 0; y < image->height; y++) {
594 for (x = 0; x < image->width; x++) {
595 if (!a || *a>127) {
596 if (!addcolor(&colormap, *r, *g, *b, &colorCount)) {
597 goto uhoh;
600 if (a) {
601 r += 4; g += 4; b += 4; a += 4;
602 } else {
603 r += 3; g += 3; b += 3;
608 charsPerPixel = 1;
609 while ((1 << charsPerPixel*6) < colorCount)
610 charsPerPixel++;
612 /* write header info */
613 fprintf(file, "\"%i %i %i %i\",\n", image->width, image->height,
614 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);
629 r = image->data;
630 g = image->data+1;
631 b = image->data+2;
632 if (image->format == RRGBAFormat)
633 a = image->data+3;
634 else
635 a = NULL;
637 /* write data */
638 for (y = 0; y < image->height; y++) {
640 fprintf(file, "\"");
642 for (x = 0; x < image->width; x++) {
644 if (!a || *a>127) {
645 tmpc = lookfor(colormap, (unsigned)*r<<16|(unsigned)*g<<8|(unsigned)*b);
647 fprintf(file, index2str(buf, tmpc->index, charsPerPixel));
648 } else {
649 fprintf(file, transp);
652 if (a) {
653 r += 4; g += 4; b += 4; a += 4;
654 } else {
655 r += 3; g += 3; b += 3;
659 if (y < image->height-1)
660 fprintf(file, "\",\n");
661 else
662 fprintf(file, "\"};\n");
665 ok = 1;
666 uhoh:
667 errno = 0;
668 fclose(file);
669 if (ok && errno==ENOSPC) {
670 RErrorCode = RERR_WRITE;
673 freecolormap(colormap);
675 return ok ? True : False;