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

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