blob: 0aac48ca27103905460642fd299f8e2e8fd15db4 [file] [log] [blame]
// ==========================================================
// XBM Loader
//
// Design and implementation by
// - Hervé Drolon <drolon@infonie.fr>
// 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;
}