DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: fl_draw_pixmap.cxx 8362 2011-02-02 18:39:34Z manolo $" |
| 3 | // |
| 4 | // Pixmap drawing code for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2010 by Bill Spitzak and others. |
| 7 | // |
| 8 | // This library is free software; you can redistribute it and/or |
| 9 | // modify it under the terms of the GNU Library General Public |
| 10 | // License as published by the Free Software Foundation; either |
| 11 | // version 2 of the License, or (at your option) any later version. |
| 12 | // |
| 13 | // This library is distributed in the hope that it will be useful, |
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | // Library General Public License for more details. |
| 17 | // |
| 18 | // You should have received a copy of the GNU Library General Public |
| 19 | // License along with this library; if not, write to the Free Software |
| 20 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| 21 | // USA. |
| 22 | // |
| 23 | // Please report all bugs and problems on the following page: |
| 24 | // |
| 25 | // http://www.fltk.org/str.php |
| 26 | // |
| 27 | |
| 28 | // Implemented without using the xpm library (which I can't use because |
| 29 | // it interferes with the color cube used by fl_draw_image). |
| 30 | // Current implementation is cheap and slow, and works best on a full-color |
| 31 | // display. Transparency is not handled, and colors are dithered to |
| 32 | // the color cube. Color index is achieved by adding the id |
| 33 | // characters together! Also mallocs a lot of temporary memory! |
| 34 | // Notice that there is no pixmap file interface. This is on purpose, |
| 35 | // as I want to discourage programs that require support files to work. |
| 36 | // All data needed by a program ui should be compiled in!!! |
| 37 | |
| 38 | #include <FL/Fl.H> |
| 39 | #include <FL/fl_draw.H> |
| 40 | #include <FL/x.H> |
| 41 | #include <stdio.h> |
| 42 | #include "flstring.h" |
| 43 | |
| 44 | static int ncolors, chars_per_pixel; |
| 45 | |
| 46 | /** |
| 47 | Get the dimensions of a pixmap. |
| 48 | An XPM image contains the dimensions in its data. This function |
| 49 | returns te width and height. |
| 50 | \param[in] data pointer to XPM image data. |
| 51 | \param[out] w,h width and height of image |
| 52 | \returns non-zero if the dimensions were parsed OK |
| 53 | \returns 0 if there were any problems |
| 54 | */ |
| 55 | int fl_measure_pixmap(/*const*/ char* const* data, int &w, int &h) { |
| 56 | return fl_measure_pixmap((const char*const*)data,w,h); |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | Get the dimensions of a pixmap. |
| 61 | \see fl_measure_pixmap(char* const* data, int &w, int &h) |
| 62 | */ |
| 63 | int fl_measure_pixmap(const char * const *cdata, int &w, int &h) { |
| 64 | int i = sscanf(cdata[0],"%d%d%d%d",&w,&h,&ncolors,&chars_per_pixel); |
| 65 | if (i<4 || w<=0 || h<=0 || |
| 66 | (chars_per_pixel!=1 && chars_per_pixel!=2) ) return w=0; |
| 67 | return 1; |
| 68 | } |
| 69 | |
| 70 | #ifdef U64 |
| 71 | |
| 72 | // The callback from fl_draw_image to get a row of data passes this: |
| 73 | struct pixmap_data { |
| 74 | int w, h; |
| 75 | const uchar*const* data; |
| 76 | union { |
| 77 | U64 colors[256]; |
| 78 | U64* byte1[256]; |
| 79 | }; |
| 80 | }; |
| 81 | |
| 82 | // callback for 1 byte per pixel: |
| 83 | static void cb1(void*v, int x, int y, int w, uchar* buf) { |
| 84 | pixmap_data& d = *(pixmap_data*)v; |
| 85 | const uchar* p = d.data[y]+x; |
| 86 | U64* q = (U64*)buf; |
| 87 | for (int X=w; X>0; X-=2, p += 2) { |
| 88 | if (X>1) { |
| 89 | # if WORDS_BIGENDIAN |
| 90 | *q++ = (d.colors[p[0]]<<32) | d.colors[p[1]]; |
| 91 | # else |
| 92 | *q++ = (d.colors[p[1]]<<32) | d.colors[p[0]]; |
| 93 | # endif |
| 94 | } else { |
| 95 | # if WORDS_BIGENDIAN |
| 96 | *q++ = d.colors[p[0]]<<32; |
| 97 | # else |
| 98 | *q++ = d.colors[p[0]]; |
| 99 | # endif |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | // callback for 2 bytes per pixel: |
| 105 | static void cb2(void*v, int x, int y, int w, uchar* buf) { |
| 106 | pixmap_data& d = *(pixmap_data*)v; |
| 107 | const uchar* p = d.data[y]+2*x; |
| 108 | U64* q = (U64*)buf; |
| 109 | for (int X=w; X>0; X-=2) { |
| 110 | U64* colors = d.byte1[*p++]; |
| 111 | int index = *p++; |
| 112 | if (X>1) { |
| 113 | U64* colors1 = d.byte1[*p++]; |
| 114 | int index1 = *p++; |
| 115 | # if WORDS_BIGENDIAN |
| 116 | *q++ = (colors[index]<<32) | colors1[index1]; |
| 117 | # else |
| 118 | *q++ = (colors1[index1]<<32) | colors[index]; |
| 119 | # endif |
| 120 | } else { |
| 121 | # if WORDS_BIGENDIAN |
| 122 | *q++ = colors[index]<<32; |
| 123 | # else |
| 124 | *q++ = colors[index]; |
| 125 | # endif |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | #else // U32 |
| 131 | |
| 132 | // The callback from fl_draw_image to get a row of data passes this: |
| 133 | struct pixmap_data { |
| 134 | int w, h; |
| 135 | const uchar*const* data; |
| 136 | union { |
| 137 | U32 colors[256]; |
| 138 | U32* byte1[256]; |
| 139 | }; |
| 140 | }; |
| 141 | |
| 142 | // callback for 1 byte per pixel: |
| 143 | static void cb1(void*v, int x, int y, int w, uchar* buf) { |
| 144 | pixmap_data& d = *(pixmap_data*)v; |
| 145 | const uchar* p = d.data[y]+x; |
| 146 | U32* q = (U32*)buf; |
| 147 | for (int X=w; X--;) *q++ = d.colors[*p++]; |
| 148 | } |
| 149 | |
| 150 | // callback for 2 bytes per pixel: |
| 151 | static void cb2(void*v, int x, int y, int w, uchar* buf) { |
| 152 | pixmap_data& d = *(pixmap_data*)v; |
| 153 | const uchar* p = d.data[y]+2*x; |
| 154 | U32* q = (U32*)buf; |
| 155 | for (int X=w; X--;) { |
| 156 | U32* colors = d.byte1[*p++]; |
| 157 | *q++ = colors[*p++]; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | #endif // U64 else U32 |
| 162 | |
| 163 | uchar **fl_mask_bitmap; // if non-zero, create bitmap and store pointer here |
| 164 | |
| 165 | /** |
| 166 | Draw XPM image data, with the top-left corner at the given position. |
| 167 | The image is dithered on 8-bit displays so you won't lose color |
| 168 | space for programs displaying both images and pixmaps. |
| 169 | \param[in] data pointer to XPM image data |
| 170 | \param[in] x,y position of top-left corner |
| 171 | \param[in] bg background color |
| 172 | \returns 0 if there was any error decoding the XPM data. |
| 173 | */ |
| 174 | int fl_draw_pixmap(/*const*/ char* const* data, int x,int y,Fl_Color bg) { |
| 175 | return fl_draw_pixmap((const char*const*)data,x,y,bg); |
| 176 | } |
| 177 | |
| 178 | #ifdef WIN32 |
| 179 | // to compute an unused color to be used for the pixmap background |
| 180 | FL_EXPORT UINT win_pixmap_bg_color; // the RGB() of the pixmap background color |
| 181 | static int color_count; // # of non-transparent colors used in pixmap |
| 182 | static uchar *used_colors; // used_colors[3*i+j] j=0,1,2 are the RGB values of the ith used color |
| 183 | |
| 184 | static void make_unused_color(uchar &r, uchar &g, uchar &b) |
| 185 | // makes an RGB triplet different from all the colors used in the pixmap |
| 186 | // and compute win_pixmap_bg_color from this triplet |
| 187 | { |
| 188 | int i; |
| 189 | r = 2; g = 3; b = 4; |
| 190 | while (1) { |
| 191 | for ( i = 0; i < color_count; i++) { |
| 192 | if(used_colors[3*i] == r && used_colors[3*i+1] == g && used_colors[3*i+2] == b) break; |
| 193 | } |
| 194 | if (i >= color_count) { |
| 195 | free(used_colors); |
| 196 | win_pixmap_bg_color = RGB(r, g, b); |
| 197 | return; |
| 198 | } |
| 199 | if (r < 255) r++; |
| 200 | else { |
| 201 | r = 0; |
| 202 | if (g < 255) g++; |
| 203 | else { |
| 204 | g = 0; |
| 205 | b++; |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | #endif |
| 211 | |
| 212 | /** |
| 213 | Draw XPM image data, with the top-left corner at the given position. |
| 214 | \see fl_draw_pixmap(char* const* data, int x, int y, Fl_Color bg) |
| 215 | */ |
| 216 | int fl_draw_pixmap(const char*const* cdata, int x, int y, Fl_Color bg) { |
| 217 | pixmap_data d; |
| 218 | if (!fl_measure_pixmap(cdata, d.w, d.h)) return 0; |
| 219 | const uchar*const* data = (const uchar*const*)(cdata+1); |
| 220 | int transparent_index = -1; |
| 221 | uchar *transparent_c = (uchar *)0; // such that transparent_c[0,1,2] are the RGB of the transparent color |
| 222 | #ifdef WIN32 |
| 223 | color_count = 0; |
| 224 | used_colors = (uchar *)malloc(abs(ncolors)*3*sizeof(uchar)); |
| 225 | #endif |
| 226 | |
| 227 | if (ncolors < 0) { // FLTK (non standard) compressed colormap |
| 228 | ncolors = -ncolors; |
| 229 | const uchar *p = *data++; |
| 230 | // if first color is ' ' it is transparent (put it later to make |
| 231 | // it not be transparent): |
| 232 | if (*p == ' ') { |
| 233 | uchar* c = (uchar*)&d.colors[(int)' ']; |
| 234 | #ifdef U64 |
| 235 | *(U64*)c = 0; |
| 236 | # if WORDS_BIGENDIAN |
| 237 | c += 4; |
| 238 | # endif |
| 239 | #endif |
| 240 | transparent_index = ' '; |
| 241 | Fl::get_color(bg, c[0], c[1], c[2]); c[3] = 0; |
| 242 | transparent_c = c; |
| 243 | p += 4; |
| 244 | ncolors--; |
| 245 | } |
| 246 | // read all the rest of the colors: |
| 247 | for (int i=0; i < ncolors; i++) { |
| 248 | uchar* c = (uchar*)&d.colors[*p++]; |
| 249 | #ifdef U64 |
| 250 | *(U64*)c = 0; |
| 251 | # if WORDS_BIGENDIAN |
| 252 | c += 4; |
| 253 | # endif |
| 254 | #endif |
| 255 | #ifdef WIN32 |
| 256 | used_colors[3*color_count] = *p; |
| 257 | used_colors[3*color_count+1] = *(p+1); |
| 258 | used_colors[3*color_count+2] = *(p+2); |
| 259 | color_count++; |
| 260 | #endif |
| 261 | *c++ = *p++; |
| 262 | *c++ = *p++; |
| 263 | *c++ = *p++; |
| 264 | #ifdef __APPLE_QUARTZ__ |
| 265 | *c = 255; |
| 266 | #else |
| 267 | *c = 0; |
| 268 | #endif |
| 269 | } |
| 270 | } else { // normal XPM colormap with names |
| 271 | if (chars_per_pixel>1) memset(d.byte1, 0, sizeof(d.byte1)); |
| 272 | for (int i=0; i<ncolors; i++) { |
| 273 | const uchar *p = *data++; |
| 274 | // the first 1 or 2 characters are the color index: |
| 275 | int ind = *p++; |
| 276 | uchar* c; |
| 277 | if (chars_per_pixel>1) { |
| 278 | #ifdef U64 |
| 279 | U64* colors = d.byte1[ind]; |
| 280 | if (!colors) colors = d.byte1[ind] = new U64[256]; |
| 281 | #else |
| 282 | U32* colors = d.byte1[ind]; |
| 283 | if (!colors) colors = d.byte1[ind] = new U32[256]; |
| 284 | #endif |
| 285 | c = (uchar*)&colors[*p]; |
| 286 | ind = (ind<<8)|*p++; |
| 287 | } else { |
| 288 | c = (uchar *)&d.colors[ind]; |
| 289 | } |
| 290 | // look for "c word", or last word if none: |
| 291 | const uchar *previous_word = p; |
| 292 | for (;;) { |
| 293 | while (*p && isspace(*p)) p++; |
| 294 | uchar what = *p++; |
| 295 | while (*p && !isspace(*p)) p++; |
| 296 | while (*p && isspace(*p)) p++; |
| 297 | if (!*p) {p = previous_word; break;} |
| 298 | if (what == 'c') break; |
| 299 | previous_word = p; |
| 300 | while (*p && !isspace(*p)) p++; |
| 301 | } |
| 302 | #ifdef U64 |
| 303 | *(U64*)c = 0; |
| 304 | # if WORDS_BIGENDIAN |
| 305 | c += 4; |
| 306 | # endif |
| 307 | #endif |
| 308 | #ifdef __APPLE_QUARTZ__ |
| 309 | c[3] = 255; |
| 310 | #endif |
| 311 | int parse = fl_parse_color((const char*)p, c[0], c[1], c[2]); |
| 312 | if (parse) { |
| 313 | #ifdef WIN32 |
| 314 | used_colors[3*color_count] = c[0]; |
| 315 | used_colors[3*color_count+1] = c[1]; |
| 316 | used_colors[3*color_count+2] = c[2]; |
| 317 | color_count++; |
| 318 | #endif |
| 319 | } |
| 320 | else { |
| 321 | // assume "None" or "#transparent" for any errors |
| 322 | // "bg" should be transparent... |
| 323 | Fl::get_color(bg, c[0], c[1], c[2]); |
| 324 | #ifdef __APPLE_QUARTZ__ |
| 325 | c[3] = 0; |
| 326 | #endif |
| 327 | transparent_index = ind; |
| 328 | transparent_c = c; |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | d.data = data; |
| 333 | #ifdef WIN32 |
| 334 | if (transparent_c) { |
| 335 | make_unused_color(transparent_c[0], transparent_c[1], transparent_c[2]); |
| 336 | } |
| 337 | else { |
| 338 | uchar r, g, b; |
| 339 | make_unused_color(r, g, b); |
| 340 | } |
| 341 | #endif |
| 342 | |
| 343 | #ifdef __APPLE_QUARTZ__ |
| 344 | if (fl_graphics_driver->class_name() == Fl_Quartz_Graphics_Driver::class_id ) { |
| 345 | bool transparent = (transparent_index>=0); |
| 346 | transparent = true; |
| 347 | U32 *array = new U32[d.w * d.h], *q = array; |
| 348 | for (int Y = 0; Y < d.h; Y++) { |
| 349 | const uchar* p = data[Y]; |
| 350 | if (chars_per_pixel <= 1) { |
| 351 | for (int X = 0; X < d.w; X++) { |
| 352 | *q++ = d.colors[*p++]; |
| 353 | } |
| 354 | } else { |
| 355 | for (int X = 0; X < d.w; X++) { |
| 356 | U32* colors = (U32*)d.byte1[*p++]; |
| 357 | *q++ = colors[*p++]; |
| 358 | } |
| 359 | } |
| 360 | } |
| 361 | CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); |
| 362 | CGDataProviderRef src = CGDataProviderCreateWithData( 0L, array, d.w * d.h * 4, 0L); |
| 363 | CGImageRef img = CGImageCreate(d.w, d.h, 8, 4*8, 4*d.w, |
| 364 | lut, transparent?kCGImageAlphaLast:kCGImageAlphaNoneSkipLast, |
| 365 | src, 0L, false, kCGRenderingIntentDefault); |
| 366 | CGColorSpaceRelease(lut); |
| 367 | CGDataProviderRelease(src); |
| 368 | CGRect rect = { { x, y} , { d.w, d.h } }; |
| 369 | Fl_X::q_begin_image(rect, 0, 0, d.w, d.h); |
| 370 | CGContextDrawImage(fl_gc, rect, img); |
| 371 | Fl_X::q_end_image(); |
| 372 | CGImageRelease(img); |
| 373 | delete[] array; |
| 374 | } |
| 375 | else { |
| 376 | #endif // __APPLE_QUARTZ__ |
| 377 | |
| 378 | // build the mask bitmap used by Fl_Pixmap: |
| 379 | if (fl_mask_bitmap && transparent_index >= 0) { |
| 380 | int W = (d.w+7)/8; |
| 381 | uchar* bitmap = new uchar[W * d.h]; |
| 382 | *fl_mask_bitmap = bitmap; |
| 383 | for (int Y = 0; Y < d.h; Y++) { |
| 384 | const uchar* p = data[Y]; |
| 385 | if (chars_per_pixel <= 1) { |
| 386 | int dw = d.w; |
| 387 | for (int X = 0; X < W; X++) { |
| 388 | uchar b = (dw-->0 && *p++ != transparent_index); |
| 389 | if (dw-->0 && *p++ != transparent_index) b |= 2; |
| 390 | if (dw-->0 && *p++ != transparent_index) b |= 4; |
| 391 | if (dw-->0 && *p++ != transparent_index) b |= 8; |
| 392 | if (dw-->0 && *p++ != transparent_index) b |= 16; |
| 393 | if (dw-->0 && *p++ != transparent_index) b |= 32; |
| 394 | if (dw-->0 && *p++ != transparent_index) b |= 64; |
| 395 | if (dw-->0 && *p++ != transparent_index) b |= 128; |
| 396 | *bitmap++ = b; |
| 397 | } |
| 398 | } else { |
| 399 | uchar b = 0, bit = 1; |
| 400 | for (int X = 0; X < d.w; X++) { |
| 401 | int ind = *p++; |
| 402 | ind = (ind<<8) | (*p++); |
| 403 | if (ind != transparent_index) b |= bit; |
| 404 | |
| 405 | if (bit < 128) bit <<= 1; |
| 406 | else { |
| 407 | *bitmap++ = b; |
| 408 | b = 0; |
| 409 | bit = 1; |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | if (bit > 1) *bitmap++ = b; |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | fl_draw_image(chars_per_pixel==1 ? cb1 : cb2, &d, x, y, d.w, d.h, 4); |
| 419 | #ifdef __APPLE_QUARTZ__ |
| 420 | } |
| 421 | #endif |
| 422 | |
| 423 | if (chars_per_pixel > 1) for (int i = 0; i < 256; i++) delete[] d.byte1[i]; |
| 424 | return 1; |
| 425 | } |
| 426 | |
| 427 | // |
| 428 | // End of "$Id: fl_draw_pixmap.cxx 8362 2011-02-02 18:39:34Z manolo $". |
| 429 | // |