// ========================================================== // XBM Loader // // Design and implementation by // - Hervé Drolon // part of the code adapted from the netPBM package (xbmtopbm.c) // // This file is part of FreeImage 3 // // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER // THIS DISCLAIMER. // // Use at your own risk! // // // ========================================================== #include "FreeImage.h" #include "Utilities.h" // ========================================================== // Internal functions // ========================================================== #define MAX_LINE 512 static const char *ERR_XBM_SYNTAX = "Syntax error"; static const char *ERR_XBM_LINE = "Line too long"; 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)"; static const char *ERR_XBM_EOFREAD = "EOF / read error"; static const char *ERR_XBM_WIDTH = "Invalid width"; static const char *ERR_XBM_HEIGHT = "Invalid height"; static const char *ERR_XBM_MEMORY = "Out of memory"; /** Get a string from a stream. Read the string from the current stream to the first newline character. The result stored in str is appended with a null character. @param str Storage location for data @param n Maximum number of characters to read @param io Pointer to the FreeImageIO structure @param handle Handle to the stream @return Returns str. NULL is returned to indicate an error or an end-of-file condition. */ static char* readLine(char *str, int n, FreeImageIO *io, fi_handle handle) { char c; int count, i = 0; do { count = io->read_proc(&c, 1, 1, handle); str[i++] = c; } while((c != '\n') && (i < n)); if(count <= 0) return NULL; str[i] = '\0'; return str; } /** Get a char from the stream @param io Pointer to the FreeImageIO structure @param handle Handle to the stream @return Returns the next character in the stream */ static int readChar(FreeImageIO *io, fi_handle handle) { BYTE c; io->read_proc(&c, 1, 1, handle); return c; } /** Read an XBM file into a buffer @param io Pointer to the FreeImageIO structure @param handle Handle to the stream @param widthP (return value) Pointer to the bitmap width @param heightP (return value) Pointer to the bitmap height @param dataP (return value) Pointer to the bitmap buffer @return Returns NULL if OK, returns an error message otherwise */ static const char* readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, char **dataP) { char line[MAX_LINE], name_and_type[MAX_LINE]; char* ptr; char* t; int version = 0; int raster_length, v; int bytes, bytes_per_line, padding; int c1, c2, value1, value2; int hex_table[256]; BOOL found_declaration; /* in scanning through the bitmap file, we have found the first line of the C declaration of the array (the "static char ..." or whatever line) */ BOOL eof; // we've encountered end of file while searching file *widthP = *heightP = -1; found_declaration = FALSE; // haven't found it yet; haven't even looked eof = FALSE; // haven't encountered end of file yet while(!found_declaration && !eof) { if( readLine(line, MAX_LINE, io, handle) == NULL) { eof = TRUE; } else { if( strlen( line ) == MAX_LINE - 1 ) return( ERR_XBM_LINE ); if( sscanf(line, "#define %s %d", name_and_type, &v) == 2 ) { if( ( t = strrchr( name_and_type, '_' ) ) == NULL ) t = name_and_type; else t++; if ( ! strcmp( "width", t ) ) *widthP = v; else if ( ! strcmp( "height", t ) ) *heightP = v; continue; } if( sscanf( line, "static short %s = {", name_and_type ) == 1 ) { version = 10; found_declaration = TRUE; } else if( sscanf( line, "static char %s = {", name_and_type ) == 1 ) { version = 11; found_declaration = TRUE; } else if(sscanf(line, "static unsigned char %s = {", name_and_type ) == 1 ) { version = 11; found_declaration = TRUE; } } } if(!found_declaration) return( ERR_XBM_DECL ); if(*widthP == -1 ) return( ERR_XBM_WIDTH ); if( *heightP == -1 ) return( ERR_XBM_HEIGHT ); padding = 0; if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10) ) padding = 1; bytes_per_line = (*widthP + 7) / 8 + padding; raster_length = bytes_per_line * *heightP; *dataP = (char*) malloc( raster_length ); if ( *dataP == (char*) 0 ) return( ERR_XBM_MEMORY ); // initialize hex_table for ( c1 = 0; c1 < 256; c1++ ) { hex_table[c1] = 256; } hex_table['0'] = 0; hex_table['1'] = 1; hex_table['2'] = 2; hex_table['3'] = 3; hex_table['4'] = 4; hex_table['5'] = 5; hex_table['6'] = 6; hex_table['7'] = 7; hex_table['8'] = 8; hex_table['9'] = 9; hex_table['A'] = 10; hex_table['B'] = 11; hex_table['C'] = 12; hex_table['D'] = 13; hex_table['E'] = 14; hex_table['F'] = 15; hex_table['a'] = 10; hex_table['b'] = 11; hex_table['c'] = 12; hex_table['d'] = 13; hex_table['e'] = 14; hex_table['f'] = 15; if(version == 10) { for( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) { while( ( c1 = readChar(io, handle) ) != 'x' ) { if ( c1 == EOF ) return( ERR_XBM_EOFREAD ); } c1 = readChar(io, handle); c2 = readChar(io, handle); if( c1 == EOF || c2 == EOF ) return( ERR_XBM_EOFREAD ); value1 = ( hex_table[c1] << 4 ) + hex_table[c2]; if ( value1 >= 256 ) return( ERR_XBM_SYNTAX ); c1 = readChar(io, handle); c2 = readChar(io, handle); if( c1 == EOF || c2 == EOF ) return( ERR_XBM_EOFREAD ); value2 = ( hex_table[c1] << 4 ) + hex_table[c2]; if ( value2 >= 256 ) return( ERR_XBM_SYNTAX ); *ptr++ = (char)value2; if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) ) *ptr++ = (char)value1; } } else { for(bytes = 0, ptr = *dataP; bytes < raster_length; bytes++ ) { /* ** skip until digit is found */ for( ; ; ) { c1 = readChar(io, handle); if ( c1 == EOF ) return( ERR_XBM_EOFREAD ); value1 = hex_table[c1]; if ( value1 != 256 ) break; } /* ** loop on digits */ for( ; ; ) { c2 = readChar(io, handle); if ( c2 == EOF ) return( ERR_XBM_EOFREAD ); value2 = hex_table[c2]; if ( value2 != 256 ) { value1 = (value1 << 4) | value2; if ( value1 >= 256 ) return( ERR_XBM_SYNTAX ); } else if ( c2 == 'x' || c2 == 'X' ) { if ( value1 == 0 ) continue; else return( ERR_XBM_SYNTAX ); } else break; } *ptr++ = (char)value1; } } return NULL; } // ========================================================== // Plugin Interface // ========================================================== static int s_format_id; // ========================================================== // Plugin Implementation // ========================================================== static const char * DLL_CALLCONV Format() { return "XBM"; } static const char * DLL_CALLCONV Description() { return "X11 Bitmap Format"; } static const char * DLL_CALLCONV Extension() { return "xbm"; } static const char * DLL_CALLCONV RegExpr() { return NULL; } static const char * DLL_CALLCONV MimeType() { return "image/x-xbitmap"; } static BOOL DLL_CALLCONV Validate(FreeImageIO *io, fi_handle handle) { char magic[8]; if(readLine(magic, 7, io, handle)) { if(strcmp(magic, "#define") == 0) return TRUE; } return FALSE; } static BOOL DLL_CALLCONV SupportsExportDepth(int depth) { return FALSE; } static BOOL DLL_CALLCONV SupportsExportType(FREE_IMAGE_TYPE type) { return FALSE; } // ---------------------------------------------------------- static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { char *buffer = NULL; int width, height; FIBITMAP *dib = NULL; try { // load the bitmap data const char* error = readXBMFile(io, handle, &width, &height, &buffer); // Microsoft doesn't implement throw between functions :( if(error) throw (char*)error; // allocate a new dib dib = FreeImage_Allocate(width, height, 1); if(!dib) throw (char*)ERR_XBM_MEMORY; // write the palette data RGBQUAD *pal = FreeImage_GetPalette(dib); pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; // copy the bitmap BYTE *bP = (BYTE*)buffer; for(int y = 0; y < height; y++) { BYTE count = 0; BYTE mask = 1; BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y); for(int x = 0; x < width; x++) { if(count >= 8) { bP++; count = 0; mask = 1; } if(*bP & mask) { // Set bit(x, y) to 0 bits[x >> 3] &= (0xFF7F >> (x & 0x7)); } else { // Set bit(x, y) to 1 bits[x >> 3] |= (0x80 >> (x & 0x7)); } count++; mask <<= 1; } bP++; } free(buffer); return dib; } catch(const char *text) { if(buffer) free(buffer); if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } // ========================================================== // Init // ========================================================== void DLL_CALLCONV InitXBM(Plugin *plugin, int format_id) { s_format_id = format_id; plugin->format_proc = Format; plugin->description_proc = Description; plugin->extension_proc = Extension; plugin->regexpr_proc = RegExpr; plugin->open_proc = NULL; plugin->close_proc = NULL; plugin->pagecount_proc = NULL; plugin->pagecapability_proc = NULL; plugin->load_proc = Load; plugin->save_proc = NULL; plugin->validate_proc = Validate; plugin->mime_proc = MimeType; plugin->supports_export_bpp_proc = SupportsExportDepth; plugin->supports_export_type_proc = SupportsExportType; plugin->supports_icc_profiles_proc = NULL; }