1 // ==========================================================
2 // XBM Loader
3 //
4 // Design and implementation by
5 // - Herv� Drolon <drolon@infonie.fr>
6 // part of the code adapted from the netPBM package (xbmtopbm.c)
7 //
8 // This file is part of FreeImage 3
9 //
10 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
11 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
12 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
13 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
14 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
15 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
16 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
17 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
18 // THIS DISCLAIMER.
19 //
20 // Use at your own risk!
21 //
22 //
23 // ==========================================================
24 
25 #include "FreeImage.h"
26 #include "Utilities.h"
27 
28 // ==========================================================
29 // Internal functions
30 // ==========================================================
31 
32 #define MAX_LINE	512
33 
34 static const char *ERR_XBM_SYNTAX	= "Syntax error";
35 static const char *ERR_XBM_LINE		= "Line too long";
36 static const char *ERR_XBM_DECL		= "Unable to find a line in the file containing the start of C array declaration (\"static char\" or whatever)";
37 static const char *ERR_XBM_EOFREAD	= "EOF / read error";
38 static const char *ERR_XBM_WIDTH	= "Invalid width";
39 static const char *ERR_XBM_HEIGHT	= "Invalid height";
40 static const char *ERR_XBM_MEMORY	= "Out of memory";
41 
42 /**
43 Get a string from a stream.
44 Read the string from the current stream to the first newline character.
45 The result stored in str is appended with a null character.
46 @param str Storage location for data
47 @param n Maximum number of characters to read
48 @param io Pointer to the FreeImageIO structure
49 @param handle Handle to the stream
50 @return Returns str. NULL is returned to indicate an error or an end-of-file condition.
51 */
52 static char*
readLine(char * str,int n,FreeImageIO * io,fi_handle handle)53 readLine(char *str, int n, FreeImageIO *io, fi_handle handle) {
54 	char c;
55 	int count, i = 0;
56 	do {
57 		count = io->read_proc(&c, 1, 1, handle);
58 		str[i++] = c;
59 	} while((c != '\n') && (i < n));
60 	if(count <= 0)
61 		return NULL;
62 	str[i] = '\0';
63 	return str;
64 }
65 
66 /**
67 Get a char from the stream
68 @param io Pointer to the FreeImageIO structure
69 @param handle Handle to the stream
70 @return Returns the next character in the stream
71 */
72 static int
readChar(FreeImageIO * io,fi_handle handle)73 readChar(FreeImageIO *io, fi_handle handle) {
74 	BYTE c;
75 	io->read_proc(&c, 1, 1, handle);
76 	return c;
77 }
78 
79 /**
80 Read an XBM file into a buffer
81 @param io Pointer to the FreeImageIO structure
82 @param handle Handle to the stream
83 @param widthP (return value) Pointer to the bitmap width
84 @param heightP (return value) Pointer to the bitmap height
85 @param dataP (return value) Pointer to the bitmap buffer
86 @return Returns NULL if OK, returns an error message otherwise
87 */
88 static const char*
readXBMFile(FreeImageIO * io,fi_handle handle,int * widthP,int * heightP,char ** dataP)89 readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, char **dataP) {
90 	char line[MAX_LINE], name_and_type[MAX_LINE];
91 	char* ptr;
92 	char* t;
93 	int version = 0;
94 	int raster_length, v;
95 	int bytes, bytes_per_line, padding;
96 	int c1, c2, value1, value2;
97 	int hex_table[256];
98 	BOOL found_declaration;
99 	/* in scanning through the bitmap file, we have found the first
100 	 line of the C declaration of the array (the "static char ..."
101 	 or whatever line)
102 	 */
103 	BOOL eof;	// we've encountered end of file while searching file
104 
105 	*widthP = *heightP = -1;
106 
107 	found_declaration = FALSE;    // haven't found it yet; haven't even looked
108 	eof = FALSE;                  // haven't encountered end of file yet
109 
110 	while(!found_declaration && !eof) {
111 
112 		if( readLine(line, MAX_LINE, io, handle) == NULL) {
113 			eof = TRUE;
114 		}
115 		else {
116 			if( strlen( line ) == MAX_LINE - 1 )
117 				return( ERR_XBM_LINE );
118 			if( sscanf(line, "#define %s %d", name_and_type, &v) == 2 ) {
119 				if( ( t = strrchr( name_and_type, '_' ) ) == NULL )
120 					t = name_and_type;
121 				else
122 					t++;
123 				if ( ! strcmp( "width", t ) )
124 					*widthP = v;
125 				else if ( ! strcmp( "height", t ) )
126 					*heightP = v;
127 				continue;
128 			}
129 
130 			if( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
131 				version = 10;
132 				found_declaration = TRUE;
133 			}
134 			else if( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
135 				version = 11;
136 				found_declaration = TRUE;
137 			}
138 			else if(sscanf(line, "static unsigned char %s = {", name_and_type ) == 1 ) {
139 				version = 11;
140 				found_declaration = TRUE;
141 			}
142 		}
143 	}
144 
145 	if(!found_declaration)
146 		return( ERR_XBM_DECL );
147 
148 	if(*widthP == -1 )
149 		return( ERR_XBM_WIDTH );
150 	if( *heightP == -1 )
151 		return( ERR_XBM_HEIGHT );
152 
153 	padding = 0;
154 	if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10) )
155 		padding = 1;
156 
157 	bytes_per_line = (*widthP + 7) / 8 + padding;
158 
159 	raster_length =  bytes_per_line * *heightP;
160 	*dataP = (char*) malloc( raster_length );
161 	if ( *dataP == (char*) 0 )
162 		return( ERR_XBM_MEMORY );
163 
164 	// initialize hex_table
165 	for ( c1 = 0; c1 < 256; c1++ ) {
166 		hex_table[c1] = 256;
167 	}
168 	hex_table['0'] = 0;
169 	hex_table['1'] = 1;
170 	hex_table['2'] = 2;
171 	hex_table['3'] = 3;
172 	hex_table['4'] = 4;
173 	hex_table['5'] = 5;
174 	hex_table['6'] = 6;
175 	hex_table['7'] = 7;
176 	hex_table['8'] = 8;
177 	hex_table['9'] = 9;
178 	hex_table['A'] = 10;
179 	hex_table['B'] = 11;
180 	hex_table['C'] = 12;
181 	hex_table['D'] = 13;
182 	hex_table['E'] = 14;
183 	hex_table['F'] = 15;
184 	hex_table['a'] = 10;
185 	hex_table['b'] = 11;
186 	hex_table['c'] = 12;
187 	hex_table['d'] = 13;
188 	hex_table['e'] = 14;
189 	hex_table['f'] = 15;
190 
191 	if(version == 10) {
192 		for( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
193 			while( ( c1 = readChar(io, handle) ) != 'x' ) {
194 				if ( c1 == EOF )
195 					return( ERR_XBM_EOFREAD );
196 			}
197 
198 			c1 = readChar(io, handle);
199 			c2 = readChar(io, handle);
200 			if( c1 == EOF || c2 == EOF )
201 				return( ERR_XBM_EOFREAD );
202 			value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
203 			if ( value1 >= 256 )
204 				return( ERR_XBM_SYNTAX );
205 			c1 = readChar(io, handle);
206 			c2 = readChar(io, handle);
207 			if( c1 == EOF || c2 == EOF )
208 				return( ERR_XBM_EOFREAD );
209 			value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
210 			if ( value2 >= 256 )
211 				return( ERR_XBM_SYNTAX );
212 			*ptr++ = (char)value2;
213 			if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
214 				*ptr++ = (char)value1;
215 		}
216 	}
217 	else {
218 		for(bytes = 0, ptr = *dataP; bytes < raster_length; bytes++ ) {
219 			/*
220 			** skip until digit is found
221 			*/
222 			for( ; ; ) {
223 				c1 = readChar(io, handle);
224 				if ( c1 == EOF )
225 					return( ERR_XBM_EOFREAD );
226 				value1 = hex_table[c1];
227 				if ( value1 != 256 )
228 					break;
229 			}
230 			/*
231 			** loop on digits
232 			*/
233 			for( ; ; ) {
234 				c2 = readChar(io, handle);
235 				if ( c2 == EOF )
236 					return( ERR_XBM_EOFREAD );
237 				value2 = hex_table[c2];
238 				if ( value2 != 256 ) {
239 					value1 = (value1 << 4) | value2;
240 					if ( value1 >= 256 )
241 						return( ERR_XBM_SYNTAX );
242 				}
243 				else if ( c2 == 'x' || c2 == 'X' ) {
244 					if ( value1 == 0 )
245 						continue;
246 					else return( ERR_XBM_SYNTAX );
247 				}
248 				else break;
249 			}
250 			*ptr++ = (char)value1;
251 		}
252 	}
253 
254 	return NULL;
255 }
256 
257 // ==========================================================
258 // Plugin Interface
259 // ==========================================================
260 
261 static int s_format_id;
262 
263 // ==========================================================
264 // Plugin Implementation
265 // ==========================================================
266 
267 static const char * DLL_CALLCONV
Format()268 Format() {
269 	return "XBM";
270 }
271 
272 static const char * DLL_CALLCONV
Description()273 Description() {
274 	return "X11 Bitmap Format";
275 }
276 
277 static const char * DLL_CALLCONV
Extension()278 Extension() {
279 	return "xbm";
280 }
281 
282 static const char * DLL_CALLCONV
RegExpr()283 RegExpr() {
284 	return NULL;
285 }
286 
287 static const char * DLL_CALLCONV
MimeType()288 MimeType() {
289 	return "image/x-xbitmap";
290 }
291 
292 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)293 Validate(FreeImageIO *io, fi_handle handle) {
294 	char magic[8];
295 	if(readLine(magic, 7, io, handle)) {
296 		if(strcmp(magic, "#define") == 0)
297 			return TRUE;
298 	}
299 	return FALSE;
300 }
301 
302 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)303 SupportsExportDepth(int depth) {
304 	return FALSE;
305 }
306 
307 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)308 SupportsExportType(FREE_IMAGE_TYPE type) {
309 	return FALSE;
310 }
311 
312 // ----------------------------------------------------------
313 
314 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)315 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
316 	char *buffer = NULL;
317 	int width, height;
318 	FIBITMAP *dib = NULL;
319 
320 	try {
321 
322 		// load the bitmap data
323 		const char* error = readXBMFile(io, handle, &width, &height, &buffer);
324 		// Microsoft doesn't implement throw between functions :(
325 		if(error) throw (char*)error;
326 
327 
328 		// allocate a new dib
329 		dib = FreeImage_Allocate(width, height, 1);
330 		if(!dib) throw (char*)ERR_XBM_MEMORY;
331 
332 		// write the palette data
333 		RGBQUAD *pal = FreeImage_GetPalette(dib);
334 		pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
335 		pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
336 
337 		// copy the bitmap
338 		BYTE *bP = (BYTE*)buffer;
339 		for(int y = 0; y < height; y++) {
340 			BYTE count = 0;
341 			BYTE mask = 1;
342 			BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
343 
344 			for(int x = 0; x < width; x++) {
345 				if(count >= 8) {
346 					bP++;
347 					count = 0;
348 					mask = 1;
349 				}
350 				if(*bP & mask) {
351 					// Set bit(x, y) to 0
352 					bits[x >> 3] &= (0xFF7F >> (x & 0x7));
353 				} else {
354 					// Set bit(x, y) to 1
355 					bits[x >> 3] |= (0x80 >> (x & 0x7));
356 				}
357 				count++;
358 				mask <<= 1;
359 			}
360 			bP++;
361 		}
362 
363 		free(buffer);
364 		return dib;
365 
366 	} catch(const char *text) {
367 		if(buffer)	free(buffer);
368 		if(dib)		FreeImage_Unload(dib);
369 		FreeImage_OutputMessageProc(s_format_id, text);
370 		return NULL;
371 	}
372 }
373 
374 
375 // ==========================================================
376 //   Init
377 // ==========================================================
378 
379 void DLL_CALLCONV
InitXBM(Plugin * plugin,int format_id)380 InitXBM(Plugin *plugin, int format_id) {
381     s_format_id = format_id;
382 
383 	plugin->format_proc = Format;
384 	plugin->description_proc = Description;
385 	plugin->extension_proc = Extension;
386 	plugin->regexpr_proc = RegExpr;
387 	plugin->open_proc = NULL;
388 	plugin->close_proc = NULL;
389 	plugin->pagecount_proc = NULL;
390 	plugin->pagecapability_proc = NULL;
391 	plugin->load_proc = Load;
392 	plugin->save_proc = NULL;
393 	plugin->validate_proc = Validate;
394 	plugin->mime_proc = MimeType;
395 	plugin->supports_export_bpp_proc = SupportsExportDepth;
396 	plugin->supports_export_type_proc = SupportsExportType;
397 	plugin->supports_icc_profiles_proc = NULL;
398 }
399 
400