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., 51 Franklin St, Fifth Floor, Boston,
20  *  MA 02110-1301, USA.
21  */
22 
23 #include <config.h>
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
30 
31 #include "wraster.h"
32 #include "imgformat.h"
33 
34 /*
35  * Restricted support for XPM images.
36  *
37  * The images must be in the following "normalized" format:
38  *
39  *
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
47  *
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
51  */
52 
53 typedef struct XPMColor {
54 	unsigned char red;
55 	unsigned char green;
56 	unsigned char blue;
57 	int index;
58 	struct XPMColor *next;
59 } XPMColor;
60 
61 #define I2CHAR(i)	((i)<12 ? (i)+'0' : ((i)<38 ? (i)+'A'-12 : (i)+'a'-38))
62 #define CINDEX(xpmc)	(((unsigned)(xpmc)->red)<<16|((unsigned)(xpmc)->green)<<8|((unsigned)(xpmc)->blue))
63 
lookfor(XPMColor * list,int index)64 static XPMColor *lookfor(XPMColor * list, int index)
65 {
66 	if (!list)
67 		return NULL;
68 
69 	for (; list != NULL; list = list->next) {
70 		if (CINDEX(list) == index)
71 			return list;
72 	}
73 	return NULL;
74 }
75 
76 /*
77  * Looks for the color in the colormap and inserts if it is not found.
78  *
79  * list is a binary search list. The unbalancing problem is just ignored.
80  *
81  * Returns False on error
82  */
addcolor(XPMColor ** list,unsigned r,unsigned g,unsigned b,int * colors)83 static Bool addcolor(XPMColor ** list, unsigned r, unsigned g, unsigned b, int *colors)
84 {
85 	XPMColor *tmpc;
86 	XPMColor *newc;
87 	int index;
88 
89 	index = r << 16 | g << 8 | b;
90 
91 	tmpc = lookfor(*list, index);
92 
93 	if (tmpc)
94 		return True;
95 
96 	newc = malloc(sizeof(XPMColor));
97 
98 	if (!newc) {
99 
100 		RErrorCode = RERR_NOMEMORY;
101 
102 		return False;
103 	}
104 
105 	newc->red = r;
106 	newc->green = g;
107 	newc->blue = b;
108 	newc->next = *list;
109 	*list = newc;
110 
111 	(*colors)++;
112 
113 	return True;
114 }
115 
index2str(char * buffer,int index,int charsPerPixel)116 static char *index2str(char *buffer, int index, int charsPerPixel)
117 {
118 	int i;
119 
120 	for (i = 0; i < charsPerPixel; i++) {
121 		buffer[i] = I2CHAR(index & 63);
122 		index >>= 6;
123 	}
124 	buffer[i] = 0;
125 
126 	return buffer;
127 }
128 
outputcolormap(FILE * file,XPMColor * colormap,int charsPerPixel)129 static void outputcolormap(FILE * file, XPMColor * colormap, int charsPerPixel)
130 {
131 	int index;
132 	char buf[128];
133 
134 	if (!colormap)
135 		return;
136 
137 	for (index = 0; colormap != NULL; colormap = colormap->next, index++) {
138 		colormap->index = index;
139 		fprintf(file, "\"%s c #%02x%02x%02x\",\n",
140 			index2str(buf, index, charsPerPixel), colormap->red, colormap->green, colormap->blue);
141 	}
142 }
143 
freecolormap(XPMColor * colormap)144 static void freecolormap(XPMColor * colormap)
145 {
146 	XPMColor *tmp;
147 
148 	while (colormap) {
149 		tmp = colormap->next;
150 		free(colormap);
151 		colormap = tmp;
152 	}
153 }
154 
155 /* save routine is common to internal support and library support */
RSaveXPM(RImage * image,const char * filename)156 Bool RSaveXPM(RImage * image, const char *filename)
157 {
158 	FILE *file;
159 	int x, y;
160 	int colorCount = 0;
161 	int charsPerPixel;
162 	XPMColor *colormap = NULL;
163 	XPMColor *tmpc;
164 	int i;
165 	int ok = 0;
166 	unsigned char *r, *g, *b, *a;
167 	char transp[16];
168 	char buf[128];
169 
170 	file = fopen(filename, "wb+");
171 	if (!file) {
172 		RErrorCode = RERR_OPEN;
173 		return False;
174 	}
175 
176 	fprintf(file, "/* XPM */\n");
177 
178 	fprintf(file, "static char *image[] = {\n");
179 
180 	r = image->data;
181 	g = image->data + 1;
182 	b = image->data + 2;
183 	if (image->format == RRGBAFormat)
184 		a = image->data + 3;
185 	else
186 		a = NULL;
187 
188 	/* first pass: make colormap for the image */
189 	if (a)
190 		colorCount = 1;
191 	for (y = 0; y < image->height; y++) {
192 		for (x = 0; x < image->width; x++) {
193 			if (!a || *a > 127) {
194 				if (!addcolor(&colormap, *r, *g, *b, &colorCount)) {
195 					goto uhoh;
196 				}
197 			}
198 			if (a) {
199 				r += 4;
200 				g += 4;
201 				b += 4;
202 				a += 4;
203 			} else {
204 				r += 3;
205 				g += 3;
206 				b += 3;
207 			}
208 		}
209 	}
210 
211 	charsPerPixel = 1;
212 	while ((1 << charsPerPixel * 6) < colorCount)
213 		charsPerPixel++;
214 
215 	/* write header info */
216 	fprintf(file, "\"%i %i %i %i\",\n", image->width, image->height, colorCount, charsPerPixel);
217 
218 	/* write colormap data */
219 	if (a) {
220 		for (i = 0; i < charsPerPixel; i++)
221 			transp[i] = ' ';
222 		transp[i] = 0;
223 
224 		fprintf(file, "\"%s c None\",\n", transp);
225 	}
226 
227 	outputcolormap(file, colormap, charsPerPixel);
228 
229 	r = image->data;
230 	g = image->data + 1;
231 	b = image->data + 2;
232 	if (image->format == RRGBAFormat)
233 		a = image->data + 3;
234 	else
235 		a = NULL;
236 
237 	/* write data */
238 	for (y = 0; y < image->height; y++) {
239 
240 		fprintf(file, "\"");
241 
242 		for (x = 0; x < image->width; x++) {
243 
244 			if (!a || *a > 127) {
245 				tmpc = lookfor(colormap, (unsigned)*r << 16 | (unsigned)*g << 8 | (unsigned)*b);
246 
247 				fprintf(file, "%s", index2str(buf, tmpc->index, charsPerPixel));
248 			} else {
249 				fprintf(file, "%s", transp);
250 			}
251 
252 			if (a) {
253 				r += 4;
254 				g += 4;
255 				b += 4;
256 				a += 4;
257 			} else {
258 				r += 3;
259 				g += 3;
260 				b += 3;
261 			}
262 		}
263 
264 		if (y < image->height - 1)
265 			fprintf(file, "\",\n");
266 		else
267 			fprintf(file, "\"};\n");
268 	}
269 
270 	ok = 1;
271  uhoh:
272 	errno = 0;
273 	fclose(file);
274 	if (ok && errno == ENOSPC) {
275 		RErrorCode = RERR_WRITE;
276 	}
277 
278 	freecolormap(colormap);
279 
280 	return ok ? True : False;
281 }
282