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