1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/xpmdecod.cpp
3 // Purpose:     wxXPMDecoder
4 // Author:      John Cristy, Vaclav Slavik
5 // RCS-ID:      $Id: xpmdecod.cpp 54948 2008-08-03 10:54:33Z VZ $
6 // Copyright:   (c) John Cristy, Vaclav Slavik
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 /*
11 
12 This file is partially based on source code of ImageMagick by John Cristy. Its
13 license is as follows:
14 
15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16 %                                                                             %
17 %                                                                             %
18 %                                                                             %
19 %                            X   X  PPPP   M   M                              %
20 %                             X X   P   P  MM MM                              %
21 %                              X    PPPP   M M M                              %
22 %                             X X   P      M   M                              %
23 %                            X   X  P      M   M                              %
24 %                                                                             %
25 %                                                                             %
26 %                    Read/Write ImageMagick Image Format.                     %
27 %                                                                             %
28 %                                                                             %
29 %                              Software Design                                %
30 %                                John Cristy                                  %
31 %                                 July 1992                                   %
32 %                                                                             %
33 %                                                                             %
34 %  Copyright (C) 2001 ImageMagick Studio, a non-profit organization dedicated %
35 %  to making software imaging solutions freely available.                     %
36 %                                                                             %
37 %  Permission is hereby granted, free of charge, to any person obtaining a    %
38 %  copy of this software and associated documentation files ("ImageMagick"),  %
39 %  to deal in ImageMagick without restriction, including without limitation   %
40 %  the rights to use, copy, modify, merge, publish, distribute, sublicense,   %
41 %  and/or sell copies of ImageMagick, and to permit persons to whom the       %
42 %  ImageMagick is furnished to do so, subject to the following conditions:    %
43 %                                                                             %
44 %  The above copyright notice and this permission notice shall be included in %
45 %  all copies or substantial portions of ImageMagick.                         %
46 %                                                                             %
47 %  The software is provided "as is", without warranty of any kind, express or %
48 %  implied, including but not limited to the warranties of merchantability,   %
49 %  fitness for a particular purpose and noninfringement.  In no event shall   %
50 %  ImageMagick Studio be liable for any claim, damages or other liability,    %
51 %  whether in an action of contract, tort or otherwise, arising from, out of  %
52 %  or in connection with ImageMagick or the use or other dealings in          %
53 %  ImageMagick.                                                               %
54 %                                                                             %
55 %  Except as contained in this notice, the name of the ImageMagick Studio     %
56 %  shall not be used in advertising or otherwise to promote the sale, use or  %
57 %  other dealings in ImageMagick without prior written authorization from the %
58 %  ImageMagick Studio.                                                        %
59 %                                                                             %
60 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 %
62 %
63 */
64 
65 /*
66  * Also contains some pieces from libxpm and its modification for win32 by
67  * HeDu <hedu@cul-ipn.uni-kiel.de>:
68  *
69  * Copyright (C) 1989-95 GROUPE BULL
70  *
71  * Permission is hereby granted, free of charge, to any person obtaining a copy
72  * of this software and associated documentation files (the "Software"), to
73  * deal in the Software without restriction, including without limitation the
74  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
75  * sell copies of the Software, and to permit persons to whom the Software is
76  * furnished to do so, subject to the following conditions:
77  *
78  * The above copyright notice and this permission notice shall be included in
79  * all copies or substantial portions of the Software.
80  *
81  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
82  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
84  * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
85  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
86  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
87  *
88  * Except as contained in this notice, the name of GROUPE BULL shall not be
89  * used in advertising or otherwise to promote the sale, use or other dealings
90  * in this Software without prior written authorization from GROUPE BULL.
91  */
92 
93 // For compilers that support precompilation, includes "wx.h".
94 #include "wx/wxprec.h"
95 
96 #ifdef __BORLANDC__
97     #pragma hdrstop
98 #endif
99 
100 #if wxUSE_IMAGE && wxUSE_XPM
101 
102 #include "wx/xpmdecod.h"
103 
104 #ifndef WX_PRECOMP
105     #include "wx/intl.h"
106     #include "wx/log.h"
107     #include "wx/utils.h"
108     #include "wx/hashmap.h"
109     #include "wx/stream.h"
110     #include "wx/image.h"
111     #include "wx/palette.h"
112 #endif
113 
114 #include <string.h>
115 #include <ctype.h>
116 
117 #if wxUSE_STREAMS
CanRead(wxInputStream & stream)118 bool wxXPMDecoder::CanRead(wxInputStream& stream)
119 {
120     unsigned char buf[9];
121 
122     if ( !stream.Read(buf, WXSIZEOF(buf)) )
123         return false;
124 
125     stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);
126 
127     return memcmp(buf, "/* XPM */", WXSIZEOF(buf)) == 0;
128 }
129 
ReadFile(wxInputStream & stream)130 wxImage wxXPMDecoder::ReadFile(wxInputStream& stream)
131 {
132     size_t length = stream.GetSize();
133     wxCHECK_MSG( length != 0, wxNullImage,
134                  wxT("Cannot read XPM from stream of unknown size") );
135 
136     // use a smart buffer to be sure to free memory even when we return on
137     // error
138     wxCharBuffer buffer(length);
139 
140     char *xpm_buffer = (char *)buffer.data();
141     if ( stream.Read(xpm_buffer, length).GetLastError() == wxSTREAM_READ_ERROR )
142         return wxNullImage;
143     xpm_buffer[length] = '\0';
144 
145     /*
146      *  Remove comments from the file:
147      */
148     char *p, *q;
149     for (p = xpm_buffer; *p != '\0'; p++)
150     {
151         if ( (*p == '"') || (*p == '\'') )
152         {
153             if (*p == '"')
154             {
155               for (p++; *p != '\0'; p++)
156                 if ( (*p == '"') && (*(p - 1) != '\\') )
157                     break;
158             }
159             else // *p == '\''
160             {
161                 for (p++; *p != '\0'; p++)
162                     if ( (*p == '\'') && (*(p - 1) != '\\') )
163                         break;
164             }
165             if (*p == '\0')
166                 break;
167             continue;
168         }
169         if ( (*p != '/') || (*(p + 1) != '*') )
170             continue;
171         for (q = p + 2; *q != '\0'; q++)
172         {
173             if ( (*q == '*') && (*(q + 1) == '/') )
174                 break;
175         }
176 
177         // memmove allows overlaps (unlike strcpy):
178         size_t cpylen = strlen(q + 2) + 1;
179         memmove(p, q + 2, cpylen);
180     }
181 
182     /*
183      *  Remove unquoted characters:
184      */
185     size_t i = 0;
186     for (p = xpm_buffer; *p != '\0'; p++)
187     {
188         if ( *p != '"' )
189             continue;
190         for (q = p + 1; *q != '\0'; q++)
191             if (*q == '"')
192                 break;
193         strncpy(xpm_buffer + i, p + 1, q - p - 1);
194         i += q - p - 1;
195         xpm_buffer[i++] = '\n';
196         p = q + 1;
197     }
198     xpm_buffer[i] = '\0';
199 
200     /*
201      *  Create array of lines and convert \n's to \0's:
202      */
203     const char **xpm_lines;
204     size_t lines_cnt = 0;
205     size_t line;
206 
207     for (p = xpm_buffer; *p != '\0'; p++)
208     {
209         if ( *p == '\n' )
210             lines_cnt++;
211     }
212 
213     if ( !lines_cnt )
214     {
215         // this doesn't really look an XPM image
216         return wxNullImage;
217     }
218 
219     xpm_lines = new const char*[lines_cnt + 1];
220     xpm_lines[0] = xpm_buffer;
221     line = 1;
222     for (p = xpm_buffer; (*p != '\0') && (line < lines_cnt); p++)
223     {
224         if ( *p == '\n' )
225         {
226             xpm_lines[line] = p + 1;
227             *p = '\0';
228             line++;
229         }
230     }
231 
232     xpm_lines[lines_cnt] = NULL;
233 
234     /*
235      *  Read the image:
236      */
237     wxImage img = ReadData(xpm_lines);
238 
239     delete[] xpm_lines;
240 
241     return img;
242 }
243 #endif // wxUSE_STREAMS
244 
245 
246 /*****************************************************************************\
247 * rgbtab.h                                                                    *
248 *                                                                             *
249 * A hard coded rgb.txt. To keep it short I removed all colornames with        *
250 * trailing numbers, Blue3 etc, except the GrayXX. Sorry Grey-lovers I prefer  *
251 * Gray ;-). But Grey is recognized on lookups, only on save Gray will be      *
252 * used, maybe you want to do some substitue there too.                        *
253 *                                                                             *
254 * To save memory the RGBs are coded in one long value, as done by the RGB     *
255 * macro.                                                                      *
256 *                                                                             *
257 * Developed by HeDu 3/94 (hedu@cul-ipn.uni-kiel.de)                           *
258 \*****************************************************************************/
259 
260 
261 typedef struct
262 {
263     const char *name;
264     wxUint32 rgb;
265 } rgbRecord;
266 
267 #define myRGB(r,g,b)   ((wxUint32)r<<16|(wxUint32)g<<8|(wxUint32)b)
268 
269 static rgbRecord theRGBRecords[] =
270 {
271     {"aliceblue", myRGB(240, 248, 255)},
272     {"antiquewhite", myRGB(250, 235, 215)},
273     {"aquamarine", myRGB(50, 191, 193)},
274     {"azure", myRGB(240, 255, 255)},
275     {"beige", myRGB(245, 245, 220)},
276     {"bisque", myRGB(255, 228, 196)},
277     {"black", myRGB(0, 0, 0)},
278     {"blanchedalmond", myRGB(255, 235, 205)},
279     {"blue", myRGB(0, 0, 255)},
280     {"blueviolet", myRGB(138, 43, 226)},
281     {"brown", myRGB(165, 42, 42)},
282     {"burlywood", myRGB(222, 184, 135)},
283     {"cadetblue", myRGB(95, 146, 158)},
284     {"chartreuse", myRGB(127, 255, 0)},
285     {"chocolate", myRGB(210, 105, 30)},
286     {"coral", myRGB(255, 114, 86)},
287     {"cornflowerblue", myRGB(34, 34, 152)},
288     {"cornsilk", myRGB(255, 248, 220)},
289     {"cyan", myRGB(0, 255, 255)},
290     {"darkgoldenrod", myRGB(184, 134, 11)},
291     {"darkgreen", myRGB(0, 86, 45)},
292     {"darkkhaki", myRGB(189, 183, 107)},
293     {"darkolivegreen", myRGB(85, 86, 47)},
294     {"darkorange", myRGB(255, 140, 0)},
295     {"darkorchid", myRGB(139, 32, 139)},
296     {"darksalmon", myRGB(233, 150, 122)},
297     {"darkseagreen", myRGB(143, 188, 143)},
298     {"darkslateblue", myRGB(56, 75, 102)},
299     {"darkslategray", myRGB(47, 79, 79)},
300     {"darkturquoise", myRGB(0, 166, 166)},
301     {"darkviolet", myRGB(148, 0, 211)},
302     {"deeppink", myRGB(255, 20, 147)},
303     {"deepskyblue", myRGB(0, 191, 255)},
304     {"dimgray", myRGB(84, 84, 84)},
305     {"dodgerblue", myRGB(30, 144, 255)},
306     {"firebrick", myRGB(142, 35, 35)},
307     {"floralwhite", myRGB(255, 250, 240)},
308     {"forestgreen", myRGB(80, 159, 105)},
309     {"gainsboro", myRGB(220, 220, 220)},
310     {"ghostwhite", myRGB(248, 248, 255)},
311     {"gold", myRGB(218, 170, 0)},
312     {"goldenrod", myRGB(239, 223, 132)},
313     {"gray", myRGB(126, 126, 126)},
314     {"gray0", myRGB(0, 0, 0)},
315     {"gray1", myRGB(3, 3, 3)},
316     {"gray10", myRGB(26, 26, 26)},
317     {"gray100", myRGB(255, 255, 255)},
318     {"gray11", myRGB(28, 28, 28)},
319     {"gray12", myRGB(31, 31, 31)},
320     {"gray13", myRGB(33, 33, 33)},
321     {"gray14", myRGB(36, 36, 36)},
322     {"gray15", myRGB(38, 38, 38)},
323     {"gray16", myRGB(41, 41, 41)},
324     {"gray17", myRGB(43, 43, 43)},
325     {"gray18", myRGB(46, 46, 46)},
326     {"gray19", myRGB(48, 48, 48)},
327     {"gray2", myRGB(5, 5, 5)},
328     {"gray20", myRGB(51, 51, 51)},
329     {"gray21", myRGB(54, 54, 54)},
330     {"gray22", myRGB(56, 56, 56)},
331     {"gray23", myRGB(59, 59, 59)},
332     {"gray24", myRGB(61, 61, 61)},
333     {"gray25", myRGB(64, 64, 64)},
334     {"gray26", myRGB(66, 66, 66)},
335     {"gray27", myRGB(69, 69, 69)},
336     {"gray28", myRGB(71, 71, 71)},
337     {"gray29", myRGB(74, 74, 74)},
338     {"gray3", myRGB(8, 8, 8)},
339     {"gray30", myRGB(77, 77, 77)},
340     {"gray31", myRGB(79, 79, 79)},
341     {"gray32", myRGB(82, 82, 82)},
342     {"gray33", myRGB(84, 84, 84)},
343     {"gray34", myRGB(87, 87, 87)},
344     {"gray35", myRGB(89, 89, 89)},
345     {"gray36", myRGB(92, 92, 92)},
346     {"gray37", myRGB(94, 94, 94)},
347     {"gray38", myRGB(97, 97, 97)},
348     {"gray39", myRGB(99, 99, 99)},
349     {"gray4", myRGB(10, 10, 10)},
350     {"gray40", myRGB(102, 102, 102)},
351     {"gray41", myRGB(105, 105, 105)},
352     {"gray42", myRGB(107, 107, 107)},
353     {"gray43", myRGB(110, 110, 110)},
354     {"gray44", myRGB(112, 112, 112)},
355     {"gray45", myRGB(115, 115, 115)},
356     {"gray46", myRGB(117, 117, 117)},
357     {"gray47", myRGB(120, 120, 120)},
358     {"gray48", myRGB(122, 122, 122)},
359     {"gray49", myRGB(125, 125, 125)},
360     {"gray5", myRGB(13, 13, 13)},
361     {"gray50", myRGB(127, 127, 127)},
362     {"gray51", myRGB(130, 130, 130)},
363     {"gray52", myRGB(133, 133, 133)},
364     {"gray53", myRGB(135, 135, 135)},
365     {"gray54", myRGB(138, 138, 138)},
366     {"gray55", myRGB(140, 140, 140)},
367     {"gray56", myRGB(143, 143, 143)},
368     {"gray57", myRGB(145, 145, 145)},
369     {"gray58", myRGB(148, 148, 148)},
370     {"gray59", myRGB(150, 150, 150)},
371     {"gray6", myRGB(15, 15, 15)},
372     {"gray60", myRGB(153, 153, 153)},
373     {"gray61", myRGB(156, 156, 156)},
374     {"gray62", myRGB(158, 158, 158)},
375     {"gray63", myRGB(161, 161, 161)},
376     {"gray64", myRGB(163, 163, 163)},
377     {"gray65", myRGB(166, 166, 166)},
378     {"gray66", myRGB(168, 168, 168)},
379     {"gray67", myRGB(171, 171, 171)},
380     {"gray68", myRGB(173, 173, 173)},
381     {"gray69", myRGB(176, 176, 176)},
382     {"gray7", myRGB(18, 18, 18)},
383     {"gray70", myRGB(179, 179, 179)},
384     {"gray71", myRGB(181, 181, 181)},
385     {"gray72", myRGB(184, 184, 184)},
386     {"gray73", myRGB(186, 186, 186)},
387     {"gray74", myRGB(189, 189, 189)},
388     {"gray75", myRGB(191, 191, 191)},
389     {"gray76", myRGB(194, 194, 194)},
390     {"gray77", myRGB(196, 196, 196)},
391     {"gray78", myRGB(199, 199, 199)},
392     {"gray79", myRGB(201, 201, 201)},
393     {"gray8", myRGB(20, 20, 20)},
394     {"gray80", myRGB(204, 204, 204)},
395     {"gray81", myRGB(207, 207, 207)},
396     {"gray82", myRGB(209, 209, 209)},
397     {"gray83", myRGB(212, 212, 212)},
398     {"gray84", myRGB(214, 214, 214)},
399     {"gray85", myRGB(217, 217, 217)},
400     {"gray86", myRGB(219, 219, 219)},
401     {"gray87", myRGB(222, 222, 222)},
402     {"gray88", myRGB(224, 224, 224)},
403     {"gray89", myRGB(227, 227, 227)},
404     {"gray9", myRGB(23, 23, 23)},
405     {"gray90", myRGB(229, 229, 229)},
406     {"gray91", myRGB(232, 232, 232)},
407     {"gray92", myRGB(235, 235, 235)},
408     {"gray93", myRGB(237, 237, 237)},
409     {"gray94", myRGB(240, 240, 240)},
410     {"gray95", myRGB(242, 242, 242)},
411     {"gray96", myRGB(245, 245, 245)},
412     {"gray97", myRGB(247, 247, 247)},
413     {"gray98", myRGB(250, 250, 250)},
414     {"gray99", myRGB(252, 252, 252)},
415     {"green", myRGB(0, 255, 0)},
416     {"greenyellow", myRGB(173, 255, 47)},
417     {"honeydew", myRGB(240, 255, 240)},
418     {"hotpink", myRGB(255, 105, 180)},
419     {"indianred", myRGB(107, 57, 57)},
420     {"ivory", myRGB(255, 255, 240)},
421     {"khaki", myRGB(179, 179, 126)},
422     {"lavender", myRGB(230, 230, 250)},
423     {"lavenderblush", myRGB(255, 240, 245)},
424     {"lawngreen", myRGB(124, 252, 0)},
425     {"lemonchiffon", myRGB(255, 250, 205)},
426     {"lightblue", myRGB(176, 226, 255)},
427     {"lightcoral", myRGB(240, 128, 128)},
428     {"lightcyan", myRGB(224, 255, 255)},
429     {"lightgoldenrod", myRGB(238, 221, 130)},
430     {"lightgoldenrodyellow", myRGB(250, 250, 210)},
431     {"lightgray", myRGB(168, 168, 168)},
432     {"lightpink", myRGB(255, 182, 193)},
433     {"lightsalmon", myRGB(255, 160, 122)},
434     {"lightseagreen", myRGB(32, 178, 170)},
435     {"lightskyblue", myRGB(135, 206, 250)},
436     {"lightslateblue", myRGB(132, 112, 255)},
437     {"lightslategray", myRGB(119, 136, 153)},
438     {"lightsteelblue", myRGB(124, 152, 211)},
439     {"lightyellow", myRGB(255, 255, 224)},
440     {"limegreen", myRGB(0, 175, 20)},
441     {"linen", myRGB(250, 240, 230)},
442     {"magenta", myRGB(255, 0, 255)},
443     {"maroon", myRGB(143, 0, 82)},
444     {"mediumaquamarine", myRGB(0, 147, 143)},
445     {"mediumblue", myRGB(50, 50, 204)},
446     {"mediumforestgreen", myRGB(50, 129, 75)},
447     {"mediumgoldenrod", myRGB(209, 193, 102)},
448     {"mediumorchid", myRGB(189, 82, 189)},
449     {"mediumpurple", myRGB(147, 112, 219)},
450     {"mediumseagreen", myRGB(52, 119, 102)},
451     {"mediumslateblue", myRGB(106, 106, 141)},
452     {"mediumspringgreen", myRGB(35, 142, 35)},
453     {"mediumturquoise", myRGB(0, 210, 210)},
454     {"mediumvioletred", myRGB(213, 32, 121)},
455     {"midnightblue", myRGB(47, 47, 100)},
456     {"mintcream", myRGB(245, 255, 250)},
457     {"mistyrose", myRGB(255, 228, 225)},
458     {"moccasin", myRGB(255, 228, 181)},
459     {"navajowhite", myRGB(255, 222, 173)},
460     {"navy", myRGB(35, 35, 117)},
461     {"navyblue", myRGB(35, 35, 117)},
462     {"oldlace", myRGB(253, 245, 230)},
463     {"olivedrab", myRGB(107, 142, 35)},
464     {"orange", myRGB(255, 135, 0)},
465     {"orangered", myRGB(255, 69, 0)},
466     {"orchid", myRGB(239, 132, 239)},
467     {"palegoldenrod", myRGB(238, 232, 170)},
468     {"palegreen", myRGB(115, 222, 120)},
469     {"paleturquoise", myRGB(175, 238, 238)},
470     {"palevioletred", myRGB(219, 112, 147)},
471     {"papayawhip", myRGB(255, 239, 213)},
472     {"peachpuff", myRGB(255, 218, 185)},
473     {"peru", myRGB(205, 133, 63)},
474     {"pink", myRGB(255, 181, 197)},
475     {"plum", myRGB(197, 72, 155)},
476     {"powderblue", myRGB(176, 224, 230)},
477     {"purple", myRGB(160, 32, 240)},
478     {"red", myRGB(255, 0, 0)},
479     {"rosybrown", myRGB(188, 143, 143)},
480     {"royalblue", myRGB(65, 105, 225)},
481     {"saddlebrown", myRGB(139, 69, 19)},
482     {"salmon", myRGB(233, 150, 122)},
483     {"sandybrown", myRGB(244, 164, 96)},
484     {"seagreen", myRGB(82, 149, 132)},
485     {"seashell", myRGB(255, 245, 238)},
486     {"sienna", myRGB(150, 82, 45)},
487     {"silver", myRGB(192, 192, 192)},
488     {"skyblue", myRGB(114, 159, 255)},
489     {"slateblue", myRGB(126, 136, 171)},
490     {"slategray", myRGB(112, 128, 144)},
491     {"snow", myRGB(255, 250, 250)},
492     {"springgreen", myRGB(65, 172, 65)},
493     {"steelblue", myRGB(84, 112, 170)},
494     {"tan", myRGB(222, 184, 135)},
495     {"thistle", myRGB(216, 191, 216)},
496     {"tomato", myRGB(255, 99, 71)},
497     {"transparent", myRGB(0, 0, 1)},
498     {"turquoise", myRGB(25, 204, 223)},
499     {"violet", myRGB(156, 62, 206)},
500     {"violetred", myRGB(243, 62, 150)},
501     {"wheat", myRGB(245, 222, 179)},
502     {"white", myRGB(255, 255, 255)},
503     {"whitesmoke", myRGB(245, 245, 245)},
504     {"yellow", myRGB(255, 255, 0)},
505     {"yellowgreen", myRGB(50, 216, 56)},
506     {NULL, myRGB(0, 0, 0)}
507 };
508 static int numTheRGBRecords = 235;
509 
ParseHexadecimal(char digit1,char digit2)510 static unsigned char ParseHexadecimal(char digit1, char digit2)
511 {
512     unsigned char i1, i2;
513 
514     if (digit1 >= 'a')
515         i1 = (unsigned char)(digit1 - 'a' + 0x0A);
516     else if (digit1 >= 'A')
517         i1 = (unsigned char)(digit1 - 'A' + 0x0A);
518     else
519         i1 = (unsigned char)(digit1 - '0');
520     if (digit2 >= 'a')
521         i2 = (unsigned char)(digit2 - 'a' + 0x0A);
522     else if (digit2 >= 'A')
523         i2 = (unsigned char)(digit2 - 'A' + 0x0A);
524     else
525         i2 = (unsigned char)(digit2 - '0');
526     return (unsigned char)(0x10 * i1 + i2);
527 }
528 
GetRGBFromName(const char * inname,bool * isNone,unsigned char * r,unsigned char * g,unsigned char * b)529 static bool GetRGBFromName(const char *inname, bool *isNone,
530                            unsigned char *r, unsigned char*g, unsigned char *b)
531 {
532     int left, right, middle;
533     int cmp;
534     wxUint32 rgbVal;
535     char *name;
536     char *grey, *p;
537 
538     // Neither #rrggbb nor #rrrrggggbbbb are in database, we parse them directly
539     size_t inname_len = strlen(inname);
540     if ( *inname == '#' && (inname_len == 7 || inname_len == 13))
541     {
542         size_t ofs = (inname_len == 7) ? 2 : 4;
543         *r = ParseHexadecimal(inname[1], inname[2]);
544         *g = ParseHexadecimal(inname[1*ofs+1], inname[1*ofs+2]);
545         *b = ParseHexadecimal(inname[2*ofs+1], inname[2*ofs+2]);
546         *isNone = false;
547         return true;
548     }
549 
550     name = wxStrdupA(inname);
551 
552     // theRGBRecords[] has no names with spaces, and no grey, but a
553     // lot of gray...
554 
555     // so first extract ' '
556     while ((p = strchr(name, ' ')) != NULL)
557     {
558         while (*(p))            // till eof of string
559         {
560             *p = *(p + 1);      // copy to the left
561             p++;
562         }
563     }
564     // fold to lower case
565     p = name;
566     while (*p)
567     {
568         *p = (char)tolower(*p);
569         p++;
570     }
571 
572     // substitute Grey with Gray, else rgbtab.h would have more than 100
573     // 'duplicate' entries
574     if ( (grey = strstr(name, "grey")) != NULL )
575         grey[2] = 'a';
576 
577     // check for special 'none' colour:
578     bool found;
579     if ( strcmp(name, "none") == 0 )
580     {
581         *isNone = true;
582         found = true;
583     }
584     else // not "None"
585     {
586         found = false;
587 
588         // binary search:
589         left = 0;
590         right = numTheRGBRecords - 1;
591         do
592         {
593             middle = (left + right) / 2;
594             cmp = strcmp(name, theRGBRecords[middle].name);
595             if ( cmp == 0 )
596             {
597                 rgbVal = theRGBRecords[middle].rgb;
598                 *r = (unsigned char)((rgbVal >> 16) & 0xFF);
599                 *g = (unsigned char)((rgbVal >> 8) & 0xFF);
600                 *b = (unsigned char)((rgbVal) & 0xFF);
601                 *isNone = false;
602                 found = true;
603                 break;
604             }
605             else if ( cmp < 0 )
606             {
607                 right = middle - 1;
608             }
609             else // cmp > 0
610             {
611                 left = middle + 1;
612             }
613         } while (left <= right);
614     }
615 
616     free(name);
617 
618     return found;
619 }
620 
ParseColor(const char * data)621 static const char *ParseColor(const char *data)
622 {
623     static const char *targets[] =
624                         {"c ", "g ", "g4 ", "m ", "b ", "s ", NULL};
625 
626     const char *p, *r;
627     const char *q;
628     int i;
629 
630     for (i = 0; targets[i] != NULL; i++)
631     {
632         r = data;
633         for (q = targets[i]; *r != '\0'; r++)
634         {
635             if ( *r != *q )
636                 continue;
637             if ( !isspace((int) (*(r - 1))) )
638                 continue;
639             p = r;
640             for (;;)
641             {
642                 if ( *q == '\0' )
643                     return p;
644                 if ( *p++ != *q++ )
645                     break;
646             }
647             q = targets[i];
648         }
649     }
650     return NULL;
651 }
652 
653 struct wxXPMColourMapData
654 {
wxXPMColourMapDatawxXPMColourMapData655     wxXPMColourMapData() { R = G = B = 0; }
656     unsigned char R,G,B;
657 };
658 WX_DECLARE_STRING_HASH_MAP(wxXPMColourMapData, wxXPMColourMap);
659 
ReadData(const char * const * xpm_data)660 wxImage wxXPMDecoder::ReadData(const char* const* xpm_data)
661 {
662     wxCHECK_MSG(xpm_data, wxNullImage, wxT("NULL XPM data") );
663 
664     wxImage img;
665     int count;
666     unsigned width, height, colors_cnt, chars_per_pixel;
667     size_t i, j, i_key;
668     wxChar key[64];
669     const char *clr_def;
670     bool hasMask;
671     wxXPMColourMap clr_tbl;
672     wxXPMColourMap::iterator it;
673     wxString maskKey;
674 
675     /*
676      *  Read hints and initialize structures:
677      */
678 
679     count = sscanf(xpm_data[0], "%u %u %u %u",
680                    &width, &height, &colors_cnt, &chars_per_pixel);
681     if ( count != 4 || width * height * colors_cnt == 0 )
682     {
683         wxLogError(_("XPM: incorrect header format!"));
684         return wxNullImage;
685     }
686 
687     // VS: XPM color map this large would be insane, since XPMs are encoded with
688     //     92 possible values on each position, 92^64 is *way* larger space than
689     //     8bit RGB...
690     wxCHECK_MSG(chars_per_pixel < 64, wxNullImage, wxT("XPM colormaps this large not supported."));
691 
692     if ( !img.Create(width, height) )
693         return wxNullImage;
694 
695     img.SetMask(false);
696     key[chars_per_pixel] = wxT('\0');
697     hasMask = false;
698 
699     /*
700      *  Create colour map:
701      */
702     wxXPMColourMapData clr_data;
703     for (i = 0; i < colors_cnt; i++)
704     {
705         const char *xmpColLine = xpm_data[1 + i];
706 
707         // we must have at least " x y" after the colour index, hence +5
708         if ( !xmpColLine || strlen(xmpColLine) < chars_per_pixel + 5 )
709         {
710             wxLogError(_("XPM: incorrect colour description in line %d"),
711                        (int)(1 + i));
712             return wxNullImage;
713         }
714 
715         for (i_key = 0; i_key < chars_per_pixel; i_key++)
716             key[i_key] = (wxChar)xmpColLine[i_key];
717         clr_def = ParseColor(xmpColLine + chars_per_pixel);
718 
719         if ( clr_def == NULL )
720         {
721             wxLogError(_("XPM: malformed colour definition '%s' at line %d!"),
722                        xmpColLine, (int)(1 + i));
723             return wxNullImage;
724         }
725 
726         bool isNone = false;
727         if ( !GetRGBFromName(clr_def, &isNone,
728                              &clr_data.R, &clr_data.G, &clr_data.B) )
729         {
730             wxLogError(_("XPM: malformed colour definition '%s' at line %d!"),
731                        xmpColLine, (int)(1 + i));
732             return wxNullImage;
733         }
734 
735         if ( isNone )
736         {
737             img.SetMask(true);
738             img.SetMaskColour(255, 0, 255);
739             clr_data.R =
740             clr_data.B = 255;
741             clr_data.G = 0;
742             hasMask = true;
743             maskKey = key;
744         }
745 
746         clr_tbl[key] = clr_data;
747     }
748 
749     /*
750      *  Modify colour entries with RGB = (255,0,255) to (255,0,254) if
751      *  mask colour is present (so that existing pixels with (255,0,255)
752      *  magenta colour are not incorrectly made transparent):
753      */
754     if (hasMask)
755     {
756         for (it = clr_tbl.begin(); it != clr_tbl.end(); ++it)
757         {
758             if (it->second.R == 255 && it->second.G == 0 &&
759                 it->second.B == 255 &&
760                 it->first != maskKey)
761             {
762                 it->second.B = 254;
763             }
764         }
765     }
766 
767     /*
768      *  Parse image data:
769      */
770 
771     unsigned char *img_data = img.GetData();
772     wxXPMColourMap::iterator entry;
773     wxXPMColourMap::iterator end = clr_tbl.end();
774 
775     for (j = 0; j < height; j++)
776     {
777         for (i = 0; i < width; i++, img_data += 3)
778         {
779             const char *xpmImgLine = xpm_data[1 + colors_cnt + j];
780             if ( !xpmImgLine || strlen(xpmImgLine) < width*chars_per_pixel )
781             {
782                 wxLogError(_("XPM: truncated image data at line %d!"),
783                            (int)(1 + colors_cnt + j));
784                 return wxNullImage;
785             }
786 
787             for (i_key = 0; i_key < chars_per_pixel; i_key++)
788             {
789                 key[i_key] = (wxChar)xpmImgLine[chars_per_pixel * i + i_key];
790             }
791 
792             entry = clr_tbl.find(key);
793             if ( entry == end )
794             {
795                 wxLogError(_("XPM: Malformed pixel data!"));
796 
797                 // better return right now as otherwise we risk to flood the
798                 // user with error messages as something seems to be seriously
799                 // wrong with the file and so we could give this message for
800                 // each remaining pixel if we don't bail out
801                 return wxNullImage;
802             }
803             else
804             {
805                 img_data[0] = entry->second.R;
806                 img_data[1] = entry->second.G;
807                 img_data[2] = entry->second.B;
808             }
809         }
810     }
811 
812 #if wxUSE_PALETTE
813     unsigned char* r = new unsigned char[colors_cnt];
814     unsigned char* g = new unsigned char[colors_cnt];
815     unsigned char* b = new unsigned char[colors_cnt];
816 
817     for (it = clr_tbl.begin(), i = 0; it != clr_tbl.end(); it++, i++)
818     {
819         r[i] = it->second.R;
820         g[i] = it->second.G;
821         b[i] = it->second.B;
822     }
823     wxASSERT(i == colors_cnt);
824     img.SetPalette(wxPalette(colors_cnt, r, g, b));
825     delete[] r;
826     delete[] g;
827     delete[] b;
828 #endif // wxUSE_PALETTE
829 
830     return img;
831 }
832 
833 #endif // wxUSE_IMAGE && wxUSE_XPM
834