/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginXBM.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #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* |
53 | 29.3k | readLine(char *str, int n, FreeImageIO *io, fi_handle handle) { |
54 | 29.3k | char c; |
55 | 29.3k | int count, i = 0; |
56 | 204k | do { |
57 | 204k | count = io->read_proc(&c, 1, 1, handle); |
58 | 204k | str[i++] = c; |
59 | 204k | } while((c != '\n') && (i < n)); |
60 | 29.3k | if(count <= 0) |
61 | 0 | return NULL; |
62 | 29.3k | str[i] = '\0'; |
63 | 29.3k | return str; |
64 | 29.3k | } |
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 |
73 | 0 | readChar(FreeImageIO *io, fi_handle handle) { |
74 | 0 | BYTE c; |
75 | 0 | io->read_proc(&c, 1, 1, handle); |
76 | 0 | return c; |
77 | 0 | } |
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* |
89 | 0 | readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, char **dataP) { |
90 | 0 | char line[MAX_LINE], name_and_type[MAX_LINE]; |
91 | 0 | char* ptr; |
92 | 0 | char* t; |
93 | 0 | int version = 0; |
94 | 0 | int raster_length, v; |
95 | 0 | int bytes, bytes_per_line, padding; |
96 | 0 | int c1, c2, value1, value2; |
97 | 0 | int hex_table[256]; |
98 | 0 | 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 | 0 | BOOL eof; // we've encountered end of file while searching file |
104 | |
|
105 | 0 | *widthP = *heightP = -1; |
106 | |
|
107 | 0 | found_declaration = FALSE; // haven't found it yet; haven't even looked |
108 | 0 | eof = FALSE; // haven't encountered end of file yet |
109 | | |
110 | 0 | while(!found_declaration && !eof) { |
111 | |
|
112 | 0 | if( readLine(line, MAX_LINE, io, handle) == NULL) { |
113 | 0 | eof = TRUE; |
114 | 0 | } |
115 | 0 | else { |
116 | 0 | if( strlen( line ) == MAX_LINE - 1 ) |
117 | 0 | return( ERR_XBM_LINE ); |
118 | 0 | if( sscanf(line, "#define %s %d", name_and_type, &v) == 2 ) { |
119 | 0 | if( ( t = strrchr( name_and_type, '_' ) ) == NULL ) |
120 | 0 | t = name_and_type; |
121 | 0 | else |
122 | 0 | t++; |
123 | 0 | if ( ! strcmp( "width", t ) ) |
124 | 0 | *widthP = v; |
125 | 0 | else if ( ! strcmp( "height", t ) ) |
126 | 0 | *heightP = v; |
127 | 0 | continue; |
128 | 0 | } |
129 | | |
130 | 0 | if( sscanf( line, "static short %s = {", name_and_type ) == 1 ) { |
131 | 0 | version = 10; |
132 | 0 | found_declaration = TRUE; |
133 | 0 | } |
134 | 0 | else if( sscanf( line, "static char %s = {", name_and_type ) == 1 ) { |
135 | 0 | version = 11; |
136 | 0 | found_declaration = TRUE; |
137 | 0 | } |
138 | 0 | else if(sscanf(line, "static unsigned char %s = {", name_and_type ) == 1 ) { |
139 | 0 | version = 11; |
140 | 0 | found_declaration = TRUE; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | if(!found_declaration) |
146 | 0 | return( ERR_XBM_DECL ); |
147 | | |
148 | 0 | if(*widthP == -1 ) |
149 | 0 | return( ERR_XBM_WIDTH ); |
150 | 0 | if( *heightP == -1 ) |
151 | 0 | return( ERR_XBM_HEIGHT ); |
152 | | |
153 | 0 | padding = 0; |
154 | 0 | if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10) ) |
155 | 0 | padding = 1; |
156 | |
|
157 | 0 | bytes_per_line = (*widthP + 7) / 8 + padding; |
158 | |
|
159 | 0 | raster_length = bytes_per_line * *heightP; |
160 | 0 | *dataP = (char*) malloc( raster_length ); |
161 | 0 | if ( *dataP == (char*) 0 ) |
162 | 0 | return( ERR_XBM_MEMORY ); |
163 | | |
164 | | // initialize hex_table |
165 | 0 | for ( c1 = 0; c1 < 256; c1++ ) { |
166 | 0 | hex_table[c1] = 256; |
167 | 0 | } |
168 | 0 | hex_table['0'] = 0; |
169 | 0 | hex_table['1'] = 1; |
170 | 0 | hex_table['2'] = 2; |
171 | 0 | hex_table['3'] = 3; |
172 | 0 | hex_table['4'] = 4; |
173 | 0 | hex_table['5'] = 5; |
174 | 0 | hex_table['6'] = 6; |
175 | 0 | hex_table['7'] = 7; |
176 | 0 | hex_table['8'] = 8; |
177 | 0 | hex_table['9'] = 9; |
178 | 0 | hex_table['A'] = 10; |
179 | 0 | hex_table['B'] = 11; |
180 | 0 | hex_table['C'] = 12; |
181 | 0 | hex_table['D'] = 13; |
182 | 0 | hex_table['E'] = 14; |
183 | 0 | hex_table['F'] = 15; |
184 | 0 | hex_table['a'] = 10; |
185 | 0 | hex_table['b'] = 11; |
186 | 0 | hex_table['c'] = 12; |
187 | 0 | hex_table['d'] = 13; |
188 | 0 | hex_table['e'] = 14; |
189 | 0 | hex_table['f'] = 15; |
190 | |
|
191 | 0 | if(version == 10) { |
192 | 0 | for( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) { |
193 | 0 | while( ( c1 = readChar(io, handle) ) != 'x' ) { |
194 | 0 | if ( c1 == EOF ) |
195 | 0 | return( ERR_XBM_EOFREAD ); |
196 | 0 | } |
197 | | |
198 | 0 | c1 = readChar(io, handle); |
199 | 0 | c2 = readChar(io, handle); |
200 | 0 | if( c1 == EOF || c2 == EOF ) |
201 | 0 | return( ERR_XBM_EOFREAD ); |
202 | 0 | value1 = ( hex_table[c1] << 4 ) + hex_table[c2]; |
203 | 0 | if ( value1 >= 256 ) |
204 | 0 | return( ERR_XBM_SYNTAX ); |
205 | 0 | c1 = readChar(io, handle); |
206 | 0 | c2 = readChar(io, handle); |
207 | 0 | if( c1 == EOF || c2 == EOF ) |
208 | 0 | return( ERR_XBM_EOFREAD ); |
209 | 0 | value2 = ( hex_table[c1] << 4 ) + hex_table[c2]; |
210 | 0 | if ( value2 >= 256 ) |
211 | 0 | return( ERR_XBM_SYNTAX ); |
212 | 0 | *ptr++ = (char)value2; |
213 | 0 | if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) ) |
214 | 0 | *ptr++ = (char)value1; |
215 | 0 | } |
216 | 0 | } |
217 | 0 | else { |
218 | 0 | for(bytes = 0, ptr = *dataP; bytes < raster_length; bytes++ ) { |
219 | | /* |
220 | | ** skip until digit is found |
221 | | */ |
222 | 0 | for( ; ; ) { |
223 | 0 | c1 = readChar(io, handle); |
224 | 0 | if ( c1 == EOF ) |
225 | 0 | return( ERR_XBM_EOFREAD ); |
226 | 0 | value1 = hex_table[c1]; |
227 | 0 | if ( value1 != 256 ) |
228 | 0 | break; |
229 | 0 | } |
230 | | /* |
231 | | ** loop on digits |
232 | | */ |
233 | 0 | for( ; ; ) { |
234 | 0 | c2 = readChar(io, handle); |
235 | 0 | if ( c2 == EOF ) |
236 | 0 | return( ERR_XBM_EOFREAD ); |
237 | 0 | value2 = hex_table[c2]; |
238 | 0 | if ( value2 != 256 ) { |
239 | 0 | value1 = (value1 << 4) | value2; |
240 | 0 | if ( value1 >= 256 ) |
241 | 0 | return( ERR_XBM_SYNTAX ); |
242 | 0 | } |
243 | 0 | else if ( c2 == 'x' || c2 == 'X' ) { |
244 | 0 | if ( value1 == 0 ) |
245 | 0 | continue; |
246 | 0 | else return( ERR_XBM_SYNTAX ); |
247 | 0 | } |
248 | 0 | else break; |
249 | 0 | } |
250 | 0 | *ptr++ = (char)value1; |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | 0 | return NULL; |
255 | 0 | } |
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 |
268 | 2 | Format() { |
269 | 2 | return "XBM"; |
270 | 2 | } |
271 | | |
272 | | static const char * DLL_CALLCONV |
273 | 0 | Description() { |
274 | 0 | return "X11 Bitmap Format"; |
275 | 0 | } |
276 | | |
277 | | static const char * DLL_CALLCONV |
278 | 0 | Extension() { |
279 | 0 | return "xbm"; |
280 | 0 | } |
281 | | |
282 | | static const char * DLL_CALLCONV |
283 | 0 | RegExpr() { |
284 | 0 | return NULL; |
285 | 0 | } |
286 | | |
287 | | static const char * DLL_CALLCONV |
288 | 0 | MimeType() { |
289 | 0 | return "image/x-xbitmap"; |
290 | 0 | } |
291 | | |
292 | | static BOOL DLL_CALLCONV |
293 | 29.3k | Validate(FreeImageIO *io, fi_handle handle) { |
294 | 29.3k | char magic[8]; |
295 | 29.3k | if(readLine(magic, 7, io, handle)) { |
296 | 29.3k | if(strcmp(magic, "#define") == 0) |
297 | 0 | return TRUE; |
298 | 29.3k | } |
299 | 29.3k | return FALSE; |
300 | 29.3k | } |
301 | | |
302 | | static BOOL DLL_CALLCONV |
303 | 0 | SupportsExportDepth(int depth) { |
304 | 0 | return FALSE; |
305 | 0 | } |
306 | | |
307 | | static BOOL DLL_CALLCONV |
308 | 0 | SupportsExportType(FREE_IMAGE_TYPE type) { |
309 | 0 | return FALSE; |
310 | 0 | } |
311 | | |
312 | | // ---------------------------------------------------------- |
313 | | |
314 | | static FIBITMAP * DLL_CALLCONV |
315 | 0 | Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { |
316 | 0 | char *buffer = NULL; |
317 | 0 | int width, height; |
318 | 0 | FIBITMAP *dib = NULL; |
319 | |
|
320 | 0 | try { |
321 | | |
322 | | // load the bitmap data |
323 | 0 | const char* error = readXBMFile(io, handle, &width, &height, &buffer); |
324 | | // Microsoft doesn't implement throw between functions :( |
325 | 0 | if(error) throw (char*)error; |
326 | | |
327 | | |
328 | | // allocate a new dib |
329 | 0 | dib = FreeImage_Allocate(width, height, 1); |
330 | 0 | if(!dib) throw (char*)ERR_XBM_MEMORY; |
331 | | |
332 | | // write the palette data |
333 | 0 | RGBQUAD *pal = FreeImage_GetPalette(dib); |
334 | 0 | pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; |
335 | 0 | pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; |
336 | | |
337 | | // copy the bitmap |
338 | 0 | BYTE *bP = (BYTE*)buffer; |
339 | 0 | for(int y = 0; y < height; y++) { |
340 | 0 | BYTE count = 0; |
341 | 0 | BYTE mask = 1; |
342 | 0 | BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y); |
343 | |
|
344 | 0 | for(int x = 0; x < width; x++) { |
345 | 0 | if(count >= 8) { |
346 | 0 | bP++; |
347 | 0 | count = 0; |
348 | 0 | mask = 1; |
349 | 0 | } |
350 | 0 | if(*bP & mask) { |
351 | | // Set bit(x, y) to 0 |
352 | 0 | bits[x >> 3] &= (0xFF7F >> (x & 0x7)); |
353 | 0 | } else { |
354 | | // Set bit(x, y) to 1 |
355 | 0 | bits[x >> 3] |= (0x80 >> (x & 0x7)); |
356 | 0 | } |
357 | 0 | count++; |
358 | 0 | mask <<= 1; |
359 | 0 | } |
360 | 0 | bP++; |
361 | 0 | } |
362 | |
|
363 | 0 | free(buffer); |
364 | 0 | return dib; |
365 | |
|
366 | 0 | } catch(const char *text) { |
367 | 0 | if(buffer) free(buffer); |
368 | 0 | if(dib) FreeImage_Unload(dib); |
369 | 0 | FreeImage_OutputMessageProc(s_format_id, text); |
370 | 0 | return NULL; |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | | |
375 | | // ========================================================== |
376 | | // Init |
377 | | // ========================================================== |
378 | | |
379 | | void DLL_CALLCONV |
380 | 2 | InitXBM(Plugin *plugin, int format_id) { |
381 | 2 | s_format_id = format_id; |
382 | | |
383 | 2 | plugin->format_proc = Format; |
384 | 2 | plugin->description_proc = Description; |
385 | 2 | plugin->extension_proc = Extension; |
386 | 2 | plugin->regexpr_proc = RegExpr; |
387 | 2 | plugin->open_proc = NULL; |
388 | 2 | plugin->close_proc = NULL; |
389 | 2 | plugin->pagecount_proc = NULL; |
390 | 2 | plugin->pagecapability_proc = NULL; |
391 | 2 | plugin->load_proc = Load; |
392 | 2 | plugin->save_proc = NULL; |
393 | 2 | plugin->validate_proc = Validate; |
394 | 2 | plugin->mime_proc = MimeType; |
395 | 2 | plugin->supports_export_bpp_proc = SupportsExportDepth; |
396 | 2 | plugin->supports_export_type_proc = SupportsExportType; |
397 | 2 | plugin->supports_icc_profiles_proc = NULL; |
398 | 2 | } |
399 | | |