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