more cursor motion and text entry fixes, esp. for graphic blocks...
[wmaker-crm.git] / wrlib / nxpm.c
blob7af7444168595d6a0cc219d12a13c2a4d554f584
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];
184 r += 4;
185 g += 4;
186 b += 4;
187 a += 4;
188 } else {
189 r += 3;
190 g += 3;
191 b += 3;
194 } else {
195 for (j=0; j<w*2; j++) {
196 color = data[line][j++];
197 color |= data[line][j];
199 for (k=0; k<ccount; k++) {
200 if (symbol_table[k] == color)
201 break;
203 if (k==ccount)
204 k = 0;
206 *r = color_table[0][k];
207 *g = color_table[1][k];
208 *b = color_table[2][k];
209 if (a) {
210 *a = color_table[3][k];
211 r += 4;
212 g += 4;
213 b += 4;
214 a += 4;
215 } else {
216 r += 3;
217 g += 3;
218 b += 3;
222 line++;
225 #ifdef C_ALLOCA
226 alloca(0);
227 #endif
228 return image;
230 bad_format:
231 RErrorCode = RERR_BADIMAGEFILE;
232 #ifdef C_ALLOCA
233 alloca(0);
234 #endif
235 if (image)
236 RDestroyImage(image);
237 return NULL;
242 RImage*
243 RLoadXPM(RContext *context, char *file, int index)
245 RImage *image = NULL;
246 char line[LINEWIDTH+1];
247 char *buffer;
248 unsigned char *color_table[4];
249 unsigned short *symbol_table;
250 unsigned char *r, *g, *b, *a;
251 int i, j, k;
252 int transp;
253 unsigned short color;
254 int bsize;
255 int w, h, ccount, csize;
256 FILE *f;
258 f = fopen(file, "r");
259 if (!f) {
260 RErrorCode = RERR_OPEN;
261 return NULL;
263 /* sig */
264 if (!fgets(line, LINEWIDTH, f))
265 goto bad_file;
266 /* declaration */
267 if (!fgets(line, LINEWIDTH, f))
268 goto bad_file;
270 /* data */
271 if (!fgets(line, LINEWIDTH, f))
272 goto bad_file;
274 if (line[0]=='/')
275 if (!fgets(line, LINEWIDTH, f))
276 goto bad_file;
278 if (sscanf(line, "\"%i %i %i %i\"", &w, &h, &ccount, &csize)!=4
279 || w <= 0 || h <= 0 || ccount <= 0 || csize <= 0)
280 goto bad_file;
282 if (csize!=1 && csize!=2)
283 goto bad_format;
285 color_table[0] = alloca(ccount);
286 color_table[1] = alloca(ccount);
287 color_table[2] = alloca(ccount);
288 color_table[3] = alloca(ccount);
289 symbol_table = alloca(ccount * sizeof(unsigned short));
291 bsize = csize * w + 16;
292 buffer = alloca(bsize);
294 if (!color_table[0] || !color_table[1] || !color_table[2] ||
295 !color_table[3] || !symbol_table || !bsize) {
296 RErrorCode = RERR_NOMEMORY;
297 fclose(f);
298 alloca(0);
299 return NULL;
302 transp = 0;
303 /* get color table */
304 for (i=0; i<ccount; i++) {
305 if (!fgets(line, LINEWIDTH, f))
306 goto bad_file;
307 if (line[0]=='/')
308 if (!fgets(line, LINEWIDTH, f))
309 goto bad_file;
311 symbol_table[i] = line[1];
312 if (csize==2)
313 symbol_table[i] |= line[2]<<8;
315 j = csize+1;
316 while (line[j]!='#' && line[j]!='"' && line[j]!=0 && line[j]!='N') j++;
318 if (line[j]=='#') {
319 unsigned int red, green, blue;
321 k = 0;
322 j++;
323 while (line[j+k]!='"' && line[j+k]!=0) k++;
324 if (k==6) {
325 if (sscanf(&(line[j]), "%2x%2x%2x", &red, &green, &blue)!=3)
326 goto bad_format;
327 } else if (k==12) {
328 if (sscanf(&(line[j]), "%4x%4x%4x", &red, &green, &blue)!=3)
329 goto bad_format;
330 red >>= 8;
331 green >>= 8;
332 blue >>= 8;
333 } else
334 goto bad_format;
336 color_table[0][i] = red;
337 color_table[1][i] = green;
338 color_table[2][i] = blue;
339 color_table[3][i] = 255;
340 } else if (strncmp(&(line[j]), "None", 4)==0
341 || strncmp(&(line[j]), "none", 4)==0) {
342 color_table[3][i] = 0;
343 transp = 1;
344 } else {
345 goto bad_format;
349 image = RCreateImage(w, h, transp);
350 if (!image) {
351 fclose(f);
352 alloca(0);
353 return NULL;
356 r = image->data;
357 g = image->data+1;
358 b = image->data+2;
359 if (image->format == RRGBAFormat)
360 a = image->data+3;
361 else
362 a = NULL;
364 for (i=0; i<h; i++) {
365 if (!fgets(buffer, bsize, f))
366 goto bad_file;
367 if (buffer[0]=='/')
368 if (!fgets(buffer, bsize, f))
369 goto bad_file;
371 if (csize==1) {
372 for (j=1; j<=w; j++) {
373 color = buffer[j];
375 for (k=0; k<ccount; k++) {
376 if (symbol_table[k] == color)
377 break;
379 if (k==ccount)
380 k = 0;
382 *(r++) = color_table[0][k];
383 *(g++) = color_table[1][k];
384 *(b++) = color_table[2][k];
385 if (a)
386 *(a++) = color_table[3][k];
388 } else {
389 for (j=1; j<=w*2; j++) {
390 color = buffer[j++];
391 color |= buffer[j] << 8;
393 for (k=0; k<ccount; k++) {
394 if (symbol_table[k] == color)
395 break;
397 if (k==ccount) {
398 k = 0;
401 *(r++) = color_table[0][k];
402 *(g++) = color_table[1][k];
403 *(b++) = color_table[2][k];
404 if (a)
405 *(a++) = color_table[3][k];
410 fclose(f);
411 #ifdef C_ALLOCA
412 alloca(0);
413 #endif
414 return image;
416 bad_format:
417 RErrorCode = RERR_BADIMAGEFILE;
418 fclose(f);
419 #ifdef C_ALLOCA
420 alloca(0);
421 #endif
422 if (image)
423 RDestroyImage(image);
424 return NULL;
426 bad_file:
427 RErrorCode = RERR_BADIMAGEFILE;
428 fclose(f);
429 #ifdef C_ALLOCA
430 alloca(0);
431 #endif
432 if (image)
433 RDestroyImage(image);
434 return NULL;
437 #endif
440 typedef struct XPMColor {
441 unsigned char red;
442 unsigned char green;
443 unsigned char blue;
444 int index;
445 struct XPMColor *next;
446 } XPMColor;
450 #define I2CHAR(i) ((i)<12 ? (i)+'0' : ((i)<38 ? (i)+'A'-12 : (i)+'a'-38))
451 #define CINDEX(xpmc) (((unsigned)(xpmc)->red)<<16|((unsigned)(xpmc)->green)<<8|((unsigned)(xpmc)->blue))
455 static XPMColor*
456 lookfor(XPMColor *list, int index)
458 if (!list)
459 return NULL;
461 for (; list!=NULL; list=list->next) {
462 if (CINDEX(list) == index)
463 return list;
465 return NULL;
469 * Looks for the color in the colormap and inserts if it is not found.
471 * list is a binary search list. The unbalancing problem is just ignored.
473 * Returns False on error
475 static Bool
476 addcolor(XPMColor **list, unsigned r, unsigned g, unsigned b, int *colors)
478 XPMColor *tmpc;
479 XPMColor *newc;
480 int index;
482 index = r<<16|g<<8|b;
483 tmpc = *list;
485 tmpc = lookfor(*list, index);
487 if (tmpc)
488 return True;
490 newc = malloc(sizeof(XPMColor));
492 if (!newc) {
494 RErrorCode = RERR_NOMEMORY;
496 return False;
499 newc->red = r;
500 newc->green = g;
501 newc->blue = b;
502 newc->next = *list;
503 *list = newc;
505 (*colors)++;
507 return True;
511 static char*
512 index2str(char *buffer, int index, int charsPerPixel)
514 int i;
516 for (i=0; i<charsPerPixel; i++) {
517 buffer[i] = I2CHAR(index&63);
518 index >>= 6;
520 buffer[i] = 0;
522 return buffer;
526 static void
527 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,
539 colormap->green, colormap->blue);
544 static void
545 freecolormap(XPMColor *colormap)
547 XPMColor *tmp;
549 while (colormap) {
550 tmp = colormap->next;
551 free(colormap);
552 colormap = tmp;
558 /* save routine is common to internal support and library support */
559 Bool
560 RSaveXPM(RImage *image, char *filename)
562 FILE *file;
563 int x, y;
564 int colorCount=0;
565 int charsPerPixel;
566 XPMColor *colormap = NULL;
567 XPMColor *tmpc;
568 int i;
569 int ok = 0;
570 unsigned char *r, *g, *b, *a;
571 char transp[16];
572 char buf[128];
574 file = fopen(filename, "w+");
575 if (!file) {
576 RErrorCode = RERR_OPEN;
577 return False;
580 fprintf(file, "/* XPM */\n");
582 fprintf(file, "static char *image[] = {\n");
584 r = image->data;
585 g = image->data+1;
586 b = image->data+2;
587 if (image->format == RRGBAFormat)
588 a = image->data+3;
589 else
590 a = NULL;
592 /* first pass: make colormap for the image */
593 if (a)
594 colorCount = 1;
595 for (y = 0; y < image->height; y++) {
596 for (x = 0; x < image->width; x++) {
597 if (!a || *a++>127)
598 if (!addcolor(&colormap, *r, *g, *b, &colorCount)) {
599 goto uhoh;
601 r++; g++; b++;
605 charsPerPixel = 1;
606 while ((1 << charsPerPixel*6) < colorCount)
607 charsPerPixel++;
609 /* write header info */
610 fprintf(file, "\"%i %i %i %i\",\n", image->width, image->height,
611 colorCount, charsPerPixel);
613 /* write colormap data */
614 if (image->data[3]) {
615 for (i=0; i<charsPerPixel; i++)
616 transp[i] = ' ';
617 transp[i] = 0;
619 fprintf(file, "\"%s c None\",\n", transp);
622 i = 0;
623 outputcolormap(file, colormap, charsPerPixel);
626 r = image->data;
627 g = image->data+1;
628 b = image->data+2;
629 if (image->format == RRGBAFormat)
630 a = image->data+3;
631 else
632 a = NULL;
634 /* write data */
635 for (y = 0; y < image->height; y++) {
637 fprintf(file, "\"");
639 for (x = 0; x < image->width; x++) {
641 if (!a || *a++>127) {
642 tmpc = lookfor(colormap, (unsigned)*r<<16|(unsigned)*g<<8|(unsigned)*b);
644 fprintf(file, index2str(buf, tmpc->index, charsPerPixel));
645 } else {
646 fprintf(file, transp);
649 r++; g++; b++;
652 if (y < image->height-1)
653 fprintf(file, "\",\n");
654 else
655 fprintf(file, "\"};\n");
658 ok = 1;
659 uhoh:
660 errno = 0;
661 fclose(file);
662 if (ok && errno==ENOSPC) {
663 RErrorCode = RERR_WRITE;
666 freecolormap(colormap);
668 return ok ? True : False;