PageRenderTime 42ms CodeModel.GetById 7ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/PluginXPM.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 487 lines | 352 code | 64 blank | 71 comment | 96 complexity | 8cc5b92bcd62de1adf6a200e4cf64bc8 MD5 | raw file
  1// ==========================================================
  2// XPM Loader and Writer
  3//
  4// Design and implementation by
  5// - Ryan Rubley (ryan@lostreality.org)
  6//
  7// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
  8// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
  9// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
 10// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
 11// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
 12// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
 13// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
 14// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 15// THIS DISCLAIMER.
 16//
 17// Use at your own risk!
 18// ==========================================================
 19
 20#ifdef _MSC_VER 
 21#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
 22#endif
 23
 24// IMPLEMENTATION NOTES:
 25// ------------------------
 26// Initial design and implementation by
 27// - Karl-Heinz Bussian (khbussian@moss.de)
 28// - Hervé Drolon (drolon@infonie.fr)
 29// Completely rewritten from scratch by Ryan Rubley (ryan@lostreality.org)
 30// in order to address the following major fixes:
 31// * Supports any number of chars per pixel (not just 1 or 2)
 32// * Files with 2 chars per pixel but <= 256colors are loaded as 256 color (not 24bit)
 33// * Loads much faster, uses much less memory
 34// * supports #rgb #rrrgggbbb and #rrrrggggbbbb colors (not just #rrggbb)
 35// * supports symbolic color names
 36// ==========================================================
 37
 38#include "FreeImage.h"
 39#include "Utilities.h"
 40
 41// ==========================================================
 42// Plugin Interface
 43// ==========================================================
 44static int s_format_id;
 45
 46// ==========================================================
 47// Internal Functions
 48// ==========================================================
 49
 50// read in and skip all junk until we find a certain char
 51static BOOL
 52FindChar(FreeImageIO *io, fi_handle handle, BYTE look_for) {
 53	BYTE c;
 54	io->read_proc(&c, sizeof(BYTE), 1, handle);
 55	while(c != look_for) {
 56		if( io->read_proc(&c, sizeof(BYTE), 1, handle) != 1 )
 57			return FALSE;
 58	}
 59	return TRUE;
 60}
 61
 62// find start of string, read data until ending quote found, allocate memory and return a string
 63static char *
 64ReadString(FreeImageIO *io, fi_handle handle) {
 65	if( !FindChar(io, handle,'"') )
 66		return NULL;
 67	BYTE c;
 68	std::string s;
 69	io->read_proc(&c, sizeof(BYTE), 1, handle);
 70	while(c != '"') {
 71		s += c;
 72		if( io->read_proc(&c, sizeof(BYTE), 1, handle) != 1 )
 73			return NULL;
 74	}
 75	char *cstr = (char *)malloc(s.length()+1);
 76	strcpy(cstr,s.c_str());
 77	return cstr;
 78}
 79
 80static char *
 81Base92(unsigned int num) {
 82	static char b92[16]; //enough for more then 64 bits
 83	static char digit[] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
 84	b92[15] = '\0';
 85	int i = 14;
 86	do {
 87		b92[i--] = digit[num % 92];
 88		num /= 92;
 89	} while( num && i >= 0 );
 90	return b92+i+1;
 91}
 92
 93// ==========================================================
 94// Plugin Implementation
 95// ==========================================================
 96
 97static const char * DLL_CALLCONV
 98Format() {
 99	return "XPM";
100}
101
102static const char * DLL_CALLCONV
103Description() {
104	return "X11 Pixmap Format";
105}
106
107static const char * DLL_CALLCONV
108Extension() {
109	return "xpm";
110}
111
112static const char * DLL_CALLCONV
113RegExpr() {
114	return "^[ \\t]*/\\* XPM \\*/[ \\t]$";
115}
116
117static const char * DLL_CALLCONV
118MimeType() {
119	return "image/x-xpixmap";
120}
121
122static BOOL DLL_CALLCONV
123Validate(FreeImageIO *io, fi_handle handle) {
124	char buffer[256];
125
126	// checks the first 256 characters for the magic string
127	int count = io->read_proc(buffer, 1, 256, handle);
128	if(count <= 9) return FALSE;
129	for(int i = 0; i < (count - 9); i++) {
130		if(strncmp(&buffer[i], "/* XPM */", 9) == 0)
131			return TRUE;
132	}
133	return FALSE;
134}
135
136static BOOL DLL_CALLCONV
137SupportsExportDepth(int depth) {
138	return (
139			(depth == 8) ||
140			(depth == 24)
141		);
142}
143
144static BOOL DLL_CALLCONV
145SupportsExportType(FREE_IMAGE_TYPE type) {
146	return (type == FIT_BITMAP) ? TRUE : FALSE;
147}
148
149static BOOL DLL_CALLCONV
150SupportsNoPixels() {
151	return TRUE;
152}
153
154// ----------------------------------------------------------
155
156static FIBITMAP * DLL_CALLCONV
157Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
158	char msg[256];
159    FIBITMAP *dib = NULL;
160
161    if (!handle) return NULL;
162
163    try {
164		char *str;
165		
166		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
167		
168		//find the starting brace
169		if( !FindChar(io, handle,'{') )
170			throw "Could not find starting brace";
171
172		//read info string
173		str = ReadString(io, handle);
174		if(!str)
175			throw "Error reading info string";
176
177		int width, height, colors, cpp;
178		if( sscanf(str, "%d %d %d %d", &width, &height, &colors, &cpp) != 4 ) {
179			free(str);
180			throw "Improperly formed info string";
181		}
182		free(str);
183
184        if (colors > 256) {
185			dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
186		} else {
187			dib = FreeImage_AllocateHeader(header_only, width, height, 8);
188		}
189
190		//build a map of color chars to rgb values
191		std::map<std::string,FILE_RGBA> rawpal; //will store index in Alpha if 8bpp
192		for(int i = 0; i < colors; i++ ) {
193			FILE_RGBA rgba;
194
195			str = ReadString(io, handle);
196			if(!str)
197				throw "Error reading color strings";
198
199			std::string chrs(str,cpp); //create a string for the color chars using the first cpp chars
200			char *keys = str + cpp; //the color keys for these chars start after the first cpp chars
201
202			//translate all the tabs to spaces
203			char *tmp = keys;
204			while( strchr(tmp,'\t') ) {
205				tmp = strchr(tmp,'\t');
206				*tmp++ = ' ';
207			}
208
209			//prefer the color visual
210			if( strstr(keys," c ") ) {
211				char *clr = strstr(keys," c ") + 3;
212				while( *clr == ' ' ) clr++; //find the start of the hex rgb value
213				if( *clr == '#' ) {
214					int red = 0, green = 0, blue = 0, n;
215					clr++;
216					//end string at first space, if any found
217					if( strchr(clr,' ') )
218						*(strchr(clr,' ')) = '\0';
219					//parse hex color, it can be #rgb #rrggbb #rrrgggbbb or #rrrrggggbbbb
220					switch( strlen(clr) ) {
221						case 3:	n = sscanf(clr,"%01x%01x%01x",&red,&green,&blue);
222							red |= (red << 4);
223							green |= (green << 4);
224							blue |= (blue << 4);
225							break;
226						case 6:	n = sscanf(clr,"%02x%02x%02x",&red,&green,&blue);
227							break;
228						case 9:	n = sscanf(clr,"%03x%03x%03x",&red,&green,&blue);
229							red >>= 4;
230							green >>= 4;
231							blue >>= 4;
232							break;
233						case 12: n = sscanf(clr,"%04x%04x%04x",&red,&green,&blue);
234							red >>= 8;
235							green >>= 8;
236							blue >>= 8;
237							break;
238						default:
239							n = 0;
240							break;
241					}
242					if( n != 3 ) {
243						free(str);
244						throw "Improperly formed hex color value";
245					}
246					rgba.r = (BYTE)red;
247					rgba.g = (BYTE)green;
248					rgba.b = (BYTE)blue;
249				} else if( !strncmp(clr,"None",4) || !strncmp(clr,"none",4) ) {
250					rgba.r = rgba.g = rgba.b = 0xFF;
251				} else {
252					char *tmp = clr;
253
254					//scan forward for each space, if its " x " or " xx " end the string there
255					//this means its probably some other visual data beyond that point and not
256					//part of the color name.  How many named color end with a 1 or 2 character
257					//word? Probably none in our list at least.
258					while( (tmp = strchr(tmp,' ')) != NULL ) {
259						if( tmp[1] != ' ' ) {
260							if( (tmp[2] == ' ') || (tmp[2] != ' ' && tmp[3] == ' ') ) {
261								tmp[0] = '\0';
262								break;
263							}
264						}
265						tmp++;
266					}
267
268					//remove any trailing spaces
269					tmp = clr+strlen(clr)-1;
270					while( *tmp == ' ' ) {
271						*tmp = '\0';
272						tmp--;
273					}
274
275					if (!FreeImage_LookupX11Color(clr,  &rgba.r, &rgba.g, &rgba.b)) {
276						sprintf(msg, "Unknown color name '%s'", str);
277						free(str);
278						throw msg;
279					}
280				}
281			} else {
282				free(str);
283				throw "Only color visuals are supported";
284			}
285
286			//add color to map
287			rgba.a = (BYTE)((colors > 256) ? 0 : i);
288			rawpal[chrs] = rgba;
289
290			//build palette if needed
291			if( colors <= 256 ) {
292				RGBQUAD *pal = FreeImage_GetPalette(dib);
293				pal[i].rgbBlue = rgba.b;
294				pal[i].rgbGreen = rgba.g;
295				pal[i].rgbRed = rgba.r;
296			}
297
298			free(str);
299		}
300		//done parsing color map
301
302		if(header_only) {
303			// header only mode
304			return dib;
305		}
306
307		//read in pixel data
308		for(int y = 0; y < height; y++ ) {
309			BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
310			str = ReadString(io, handle);
311			if(!str)
312				throw "Error reading pixel strings";
313			char *pixel_ptr = str;
314
315			for(int x = 0; x < width; x++ ) {
316				//locate the chars in the color map
317				std::string chrs(pixel_ptr,cpp);
318				FILE_RGBA rgba = rawpal[chrs];
319
320				if( colors > 256 ) {
321					line[FI_RGBA_BLUE] = rgba.b;
322					line[FI_RGBA_GREEN] = rgba.g;
323					line[FI_RGBA_RED] = rgba.r;
324					line += 3;
325				} else {
326					*line = rgba.a;
327					line++;
328				}
329
330				pixel_ptr += cpp;
331			}
332
333			free(str);
334		}
335		//done reading pixel data
336
337		return dib;
338	} catch(const char *text) {
339       FreeImage_OutputMessageProc(s_format_id, text);
340
341       if( dib != NULL )
342           FreeImage_Unload(dib);
343
344       return NULL;
345    }
346}
347
348static BOOL DLL_CALLCONV
349Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
350	if ((dib != NULL) && (handle != NULL)) {
351		char header[] = "/* XPM */\nstatic char *freeimage[] = {\n/* width height num_colors chars_per_pixel */\n\"",
352		start_colors[] = "\",\n/* colors */\n\"",
353		start_pixels[] = "\",\n/* pixels */\n\"",
354		new_line[] = "\",\n\"",
355		footer[] = "\"\n};\n",
356		buf[256]; //256 is more then enough to sprintf 4 ints into, or the base-92 chars and #rrggbb line
357
358		if( io->write_proc(header, (unsigned int)strlen(header), 1, handle) != 1 )
359			return FALSE;
360
361		int width = FreeImage_GetWidth(dib), height = FreeImage_GetHeight(dib), bpp = FreeImage_GetBPP(dib);
362		RGBQUAD *pal = FreeImage_GetPalette(dib);
363		int x,y;
364
365		//map base92 chrs to the rgb value to create the palette
366		std::map<DWORD,FILE_RGB> chrs2color;
367		//map 8bpp index or 24bpp rgb value to the base92 chrs to create pixel data
368		typedef union {
369			DWORD index;
370			FILE_RGBA rgba;
371		} DWORDRGBA;
372		std::map<DWORD,std::string> color2chrs;
373
374		//loop thru entire dib, if new color, inc num_colors and add to both maps
375		int num_colors = 0;
376		for(y = 0; y < height; y++ ) {
377			BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
378			for(x = 0; x < width; x++ ) {
379				FILE_RGB rgb;
380				DWORDRGBA u;
381				if( bpp > 8 ) {
382					u.rgba.b = rgb.b = line[FI_RGBA_BLUE];
383					u.rgba.g = rgb.g = line[FI_RGBA_GREEN];
384					u.rgba.r = rgb.r = line[FI_RGBA_RED];
385					u.rgba.a = 0;
386					line += 3;
387				} else {
388					u.index = *line;
389					rgb.b = pal[u.index].rgbBlue;
390					rgb.g = pal[u.index].rgbGreen;
391					rgb.r = pal[u.index].rgbRed;
392					line++;
393				}
394				if( color2chrs.find(u.index) == color2chrs.end() ) { //new color
395					std::string chrs(Base92(num_colors));
396					color2chrs[u.index] = chrs;
397					chrs2color[num_colors] = rgb;
398					num_colors++;
399				}
400			}
401		}
402
403		int cpp = (int)(log((double)num_colors)/log(92.0)) + 1;
404
405		sprintf(buf, "%d %d %d %d", FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), num_colors, cpp );
406		if( io->write_proc(buf, (unsigned int)strlen(buf), 1, handle) != 1 )
407			return FALSE;
408
409		if( io->write_proc(start_colors, (unsigned int)strlen(start_colors), 1, handle) != 1 )
410			return FALSE;
411
412		//write colors, using map of chrs->rgb
413		for(x = 0; x < num_colors; x++ ) {
414			sprintf(buf, "%*s c #%02x%02x%02x", cpp, Base92(x), chrs2color[x].r, chrs2color[x].g, chrs2color[x].b );
415			if( io->write_proc(buf, (unsigned int)strlen(buf), 1, handle) != 1 )
416				return FALSE;
417			if( x == num_colors - 1 ) {
418				if( io->write_proc(start_pixels, (unsigned int)strlen(start_pixels), 1, handle) != 1 )
419					return FALSE;
420			} else {
421				if( io->write_proc(new_line, (unsigned int)strlen(new_line), 1, handle) != 1 )
422					return FALSE;
423			}
424		}
425
426
427		//write pixels, using map of rgb(if 24bpp) or index(if 8bpp)->chrs
428		for(y = 0; y < height; y++ ) {
429			BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
430			for(x = 0; x < width; x++ ) {
431				DWORDRGBA u;
432				if( bpp > 8 ) {
433					u.rgba.b = line[FI_RGBA_BLUE];
434					u.rgba.g = line[FI_RGBA_GREEN];
435					u.rgba.r = line[FI_RGBA_RED];
436					u.rgba.a = 0;
437					line += 3;
438				} else {
439					u.index = *line;
440					line++;
441				}
442				sprintf(buf, "%*s", cpp, (char *)color2chrs[u.index].c_str());
443				if( io->write_proc(buf, cpp, 1, handle) != 1 )
444					return FALSE;
445			}
446			if( y == height - 1 ) {
447				if( io->write_proc(footer, (unsigned int)strlen(footer), 1, handle) != 1 )
448					return FALSE;
449			} else {
450				if( io->write_proc(new_line, (unsigned int)strlen(new_line), 1, handle) != 1 )
451					return FALSE;
452			}
453		}
454
455		return TRUE;
456	} else {
457		return FALSE;
458	}
459}
460
461// ==========================================================
462//   Init
463// ==========================================================
464
465void DLL_CALLCONV
466InitXPM(Plugin *plugin, int format_id)
467{
468    s_format_id = format_id;
469
470	plugin->format_proc = Format;
471	plugin->description_proc = Description;
472	plugin->extension_proc = Extension;
473	plugin->regexpr_proc = RegExpr;
474	plugin->open_proc = NULL;
475	plugin->close_proc = NULL;
476	plugin->pagecount_proc = NULL;
477	plugin->pagecapability_proc = NULL;
478	plugin->load_proc = Load;
479	plugin->save_proc = Save;
480	plugin->validate_proc = Validate;
481	plugin->mime_proc = MimeType;
482	plugin->supports_export_bpp_proc = SupportsExportDepth;
483	plugin->supports_export_type_proc = SupportsExportType;
484	plugin->supports_icc_profiles_proc = NULL;
485	plugin->supports_no_pixels_proc = SupportsNoPixels;
486}
487