Tweak themes for more color consistency.
[ntk.git] / src / Fl_Image.cxx
blob86dc42e11adc207c85dd18fc6c57e10476610e2d
1 //
2 // "$Id: Fl_Image.cxx 8611 2011-04-20 14:01:04Z AlbrechtS $"
3 //
4 // Image 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.
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.
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.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
27 #include "config.h"
29 #include <FL/Fl.H>
30 #include <FL/fl_draw.H>
31 #include <FL/x.H>
32 #include <FL/Fl_Widget.H>
33 #include <FL/Fl_Menu_Item.H>
34 #include <FL/Fl_Image.H>
35 #include "flstring.h"
36 #include <FL/Fl_Cairo.H>
38 #ifdef WIN32
39 void fl_release_dc(HWND, HDC); // from Fl_win32.cxx
40 #endif
42 void fl_restore_clip(); // from fl_rect.cxx
45 // Base image class...
48 /**
49 The destructor is a virtual method that frees all memory used
50 by the image.
52 Fl_Image::~Fl_Image() {
55 /**
56 If the image has been cached for display, delete the cache
57 data. This allows you to change the data used for the image and
58 then redraw it without recreating an image object.
60 void Fl_Image::uncache() {
63 void Fl_Image::draw(int XP, int YP, int, int, int, int) {
64 draw_empty(XP, YP);
67 /**
68 The protected method draw_empty() draws a box with
69 an X in it. It can be used to draw any image that lacks image
70 data.
72 void Fl_Image::draw_empty(int X, int Y) {
73 if (w() > 0 && h() > 0) {
74 fl_color(FL_FOREGROUND_COLOR);
75 fl_rect(X, Y, w(), h());
76 fl_line(X, Y, X + w() - 1, Y + h() - 1);
77 fl_line(X, Y + h() - 1, X + w() - 1, Y);
81 /**
82 The copy() method creates a copy of the specified
83 image. If the width and height are provided, the image is
84 resized to the specified size. The image should be deleted (or in
85 the case of Fl_Shared_Image, released) when you are done
86 with it.
88 Fl_Image *Fl_Image::copy(int W, int H) {
89 return new Fl_Image(W, H, d());
92 /**
93 The color_average() method averages the colors in
94 the image with the FLTK color value c. The i
95 argument specifies the amount of the original image to combine
96 with the color, so a value of 1.0 results in no color blend, and
97 a value of 0.0 results in a constant image of the specified
98 color. <I>The original image data is not altered by this
99 method.</I>
101 void Fl_Image::color_average(Fl_Color, float) {
105 The desaturate() method converts an image to
106 grayscale. If the image contains an alpha channel (depth = 4),
107 the alpha channel is preserved. <I>This method does not alter
108 the original image data.</I>
110 void Fl_Image::desaturate() {
114 The label() methods are an obsolete way to set the
115 image attribute of a widget or menu item. Use the
116 image() or deimage() methods of the
117 Fl_Widget and Fl_Menu_Item classes
118 instead.
120 void Fl_Image::label(Fl_Widget* widget) {
121 widget->image(this);
125 The label() methods are an obsolete way to set the
126 image attribute of a widget or menu item. Use the
127 image() or deimage() methods of the
128 Fl_Widget and Fl_Menu_Item classes
129 instead.
131 void Fl_Image::label(Fl_Menu_Item* m) {
132 Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
133 m->label(_FL_IMAGE_LABEL, (const char*)this);
136 void
137 Fl_Image::labeltype(const Fl_Label *lo, // I - Label
138 int lx, // I - X position
139 int ly, // I - Y position
140 int lw, // I - Width of label
141 int lh, // I - Height of label
142 Fl_Align la) { // I - Alignment
143 Fl_Image *img; // Image pointer
144 int cx, cy; // Image position
146 img = (Fl_Image *)(lo->value);
148 if (la & FL_ALIGN_LEFT) cx = 0;
149 else if (la & FL_ALIGN_RIGHT) cx = img->w() - lw;
150 else cx = (img->w() - lw) / 2;
152 if (la & FL_ALIGN_TOP) cy = 0;
153 else if (la & FL_ALIGN_BOTTOM) cy = img->h() - lh;
154 else cy = (img->h() - lh) / 2;
156 fl_color((Fl_Color)lo->color);
158 img->draw(lx, ly, lw, lh, cx, cy);
161 void
162 Fl_Image::measure(const Fl_Label *lo, // I - Label
163 int &lw, // O - Width of image
164 int &lh) { // O - Height of image
165 Fl_Image *img; // Image pointer
167 img = (Fl_Image *)(lo->value);
169 lw = img->w();
170 lh = img->h();
175 // RGB image class...
177 /** The destructor free all memory and server resources that are used by the image. */
178 Fl_RGB_Image::~Fl_RGB_Image() {
179 uncache();
180 if (alloc_array) delete[] (uchar *)array;
183 void Fl_RGB_Image::uncache() {
184 #ifdef __APPLE_QUARTZ__
185 if (id_) {
186 CGImageRelease((CGImageRef)id_);
187 id_ = 0;
189 #else
190 if (id_) {
191 fl_delete_offscreen((Fl_Offscreen)id_);
192 id_ = 0;
195 if (mask_) {
196 fl_delete_bitmask((Fl_Bitmask)mask_);
197 mask_ = 0;
199 #endif
202 Fl_Image *Fl_RGB_Image::copy(int W, int H) {
203 Fl_RGB_Image *new_image; // New RGB image
204 uchar *new_array; // New array for image data
206 // Optimize the simple copy where the width and height are the same,
207 // or when we are copying an empty image...
208 if ((W == w() && H == h()) ||
209 !w() || !h() || !d() || !array) {
210 if (array) {
211 // Make a copy of the image data and return a new Fl_RGB_Image...
212 new_array = new uchar[w() * h() * d()];
213 if (ld() && ld()!=w()*d()) {
214 const uchar *src = array;
215 uchar *dst = new_array;
216 int dy, dh = h(), wd = w()*d(), wld = ld();
217 for (dy=0; dy<dh; dy++) {
218 memcpy(dst, src, wd);
219 src += wld;
220 dst += wd;
222 } else {
223 memcpy(new_array, array, w() * h() * d());
225 new_image = new Fl_RGB_Image(new_array, w(), h(), d());
226 new_image->alloc_array = 1;
228 return new_image;
229 } else return new Fl_RGB_Image(array, w(), h(), d(), ld());
231 if (W <= 0 || H <= 0) return 0;
235 new_array = new uchar [W * H * d()];
236 new_image = new Fl_RGB_Image(new_array, W, H, d());
237 new_image->alloc_array = 1;
240 cairo_format_t fmt;
242 switch (d() )
244 case 4:
245 fmt = CAIRO_FORMAT_ARGB32;
246 break;
247 case 3:
248 fmt = CAIRO_FORMAT_RGB24;
249 break;
250 case 1:
251 fmt = CAIRO_FORMAT_A8;
252 break;
255 cairo_surface_t *si = cairo_image_surface_create_for_data( (unsigned char *)array, fmt, w(), h( ),
256 cairo_format_stride_for_width( fmt, w() ) );
258 cairo_surface_t *di = cairo_image_surface_create_for_data( (unsigned char *)new_array, fmt, W, H,
259 cairo_format_stride_for_width( fmt, W ) );
261 cairo_t *cr = cairo_create( di );
263 cairo_scale( cr, (double)W / w(), (double)H / h() );
264 cairo_set_source_surface( cr, si, 0, 0 );
266 cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_GOOD);
267 cairo_set_operator( cr, CAIRO_OPERATOR_SOURCE );
269 cairo_paint( cr );
271 cairo_destroy( cr );
272 cairo_surface_destroy( si );
273 cairo_surface_destroy( di );
275 return new_image;
278 void Fl_RGB_Image::color_average(Fl_Color c, float i) {
279 // Don't average an empty image...
280 if (!w() || !h() || !d() || !array) return;
282 // Delete any existing pixmap/mask objects...
283 uncache();
285 // Allocate memory as needed...
286 uchar *new_array,
287 *new_ptr;
289 if (!alloc_array) new_array = new uchar[h() * w() * d()];
290 else new_array = (uchar *)array;
292 // Get the color to blend with...
293 uchar r, g, b;
294 unsigned ia, ir, ig, ib;
296 Fl::get_color(c, r, g, b);
297 if (i < 0.0f) i = 0.0f;
298 else if (i > 1.0f) i = 1.0f;
300 ia = (unsigned)(256 * i);
301 ir = r * (256 - ia);
302 ig = g * (256 - ia);
303 ib = b * (256 - ia);
305 // Update the image data to do the blend...
306 const uchar *old_ptr;
307 int x, y;
308 int line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
310 if (d() < 3) {
311 ig = (r * 31 + g * 61 + b * 8) / 100 * (256 - ia);
313 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
314 for (x = 0; x < w(); x ++) {
315 *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
316 if (d() > 1) *new_ptr++ = *old_ptr++;
318 } else {
319 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
320 for (x = 0; x < w(); x ++) {
321 *new_ptr++ = (*old_ptr++ * ia + ir) >> 8;
322 *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
323 *new_ptr++ = (*old_ptr++ * ia + ib) >> 8;
324 if (d() > 3) *new_ptr++ = *old_ptr++;
328 // Set the new pointers/values as needed...
329 if (!alloc_array) {
330 array = new_array;
331 alloc_array = 1;
333 ld(0);
337 void Fl_RGB_Image::desaturate() {
338 // Don't desaturate an empty image...
339 if (!w() || !h() || !d() || !array) return;
341 // Can only desaturate color images...
342 if (d() < 3) return;
344 // Delete any existing pixmap/mask objects...
345 uncache();
347 // Allocate memory for a grayscale image...
348 uchar *new_array,
349 *new_ptr;
350 int new_d;
352 new_d = d() - 2;
353 new_array = new uchar[h() * w() * new_d];
355 // Copy the image data, converting to grayscale...
356 const uchar *old_ptr;
357 int x, y;
358 int line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
360 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
361 for (x = 0; x < w(); x ++, old_ptr += d()) {
362 *new_ptr++ = (uchar)((31 * old_ptr[0] + 61 * old_ptr[1] + 8 * old_ptr[2]) / 100);
363 if (d() > 3) *new_ptr++ = old_ptr[3];
366 // Free the old array as needed, and then set the new pointers/values...
367 if (alloc_array) delete[] (uchar *)array;
369 array = new_array;
370 alloc_array = 1;
372 ld(0);
373 d(new_d);
376 #if !defined(WIN32) && !defined(__APPLE_QUARTZ__)
377 // Composite an image with alpha on systems that don't have accelerated
378 // alpha compositing...
379 static void alpha_blend(Fl_RGB_Image *img, int X, int Y, int W, int H, int cx, int cy) {
380 int ld = img->ld();
381 if (ld == 0) ld = img->w() * img->d();
382 uchar *srcptr = (uchar*)img->array + cy * ld + cx * img->d();
383 int srcskip = ld - img->d() * W;
385 uchar *dst = new uchar[W * H * 3];
386 uchar *dstptr = dst;
388 fl_read_image(dst, X, Y, W, H, 0);
390 uchar srcr, srcg, srcb, srca;
391 uchar dstr, dstg, dstb, dsta;
393 if (img->d() == 2) {
394 // Composite grayscale + alpha over RGB...
395 for (int y = H; y > 0; y--, srcptr+=srcskip)
396 for (int x = W; x > 0; x--) {
397 srcg = *srcptr++;
398 srca = *srcptr++;
400 dstr = dstptr[0];
401 dstg = dstptr[1];
402 dstb = dstptr[2];
403 dsta = 255 - srca;
405 *dstptr++ = (srcg * srca + dstr * dsta) >> 8;
406 *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
407 *dstptr++ = (srcg * srca + dstb * dsta) >> 8;
409 } else {
410 // Composite RGBA over RGB...
411 for (int y = H; y > 0; y--, srcptr+=srcskip)
412 for (int x = W; x > 0; x--) {
413 srcr = *srcptr++;
414 srcg = *srcptr++;
415 srcb = *srcptr++;
416 srca = *srcptr++;
418 dstr = dstptr[0];
419 dstg = dstptr[1];
420 dstb = dstptr[2];
421 dsta = 255 - srca;
423 *dstptr++ = (srcr * srca + dstr * dsta) >> 8;
424 *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
425 *dstptr++ = (srcb * srca + dstb * dsta) >> 8;
429 fl_draw_image(dst, X, Y, W, H, 3, 0);
431 delete[] dst;
433 #endif // !WIN32 && !__APPLE_QUARTZ__
435 void Fl_RGB_Image::draw(int XP, int YP, int WP, int HP, int cx, int cy) {
436 fl_graphics_driver->draw(this, XP, YP, WP, HP, cx, cy);
439 static int start(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int w, int h, int &cx, int &cy,
440 int &X, int &Y, int &W, int &H)
442 // account for current clip region (faster on Irix):
443 fl_clip_box(XP,YP,WP,HP,X,Y,W,H);
444 cx += X-XP; cy += Y-YP;
445 // clip the box down to the size of image, quit if empty:
446 if (cx < 0) {W += cx; X -= cx; cx = 0;}
447 if (cx+W > w) W = w-cx;
448 if (W <= 0) return 1;
449 if (cy < 0) {H += cy; Y -= cy; cy = 0;}
450 if (cy+H > h) H = h-cy;
451 if (H <= 0) return 1;
452 return 0;
455 #ifdef __APPLE__
456 void Fl_Quartz_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
457 int X, Y, W, H;
458 // Don't draw an empty image...
459 if (!img->d() || !img->array) {
460 img->draw_empty(XP, YP);
461 return;
463 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
464 return;
466 if (!img->id_) {
467 CGColorSpaceRef lut = 0;
468 if (img->d()<=2)
469 lut = CGColorSpaceCreateDeviceGray();
470 else
471 lut = CGColorSpaceCreateDeviceRGB();
472 CGDataProviderRef src = CGDataProviderCreateWithData( 0L, img->array, img->w()*img->h()*img->d(), 0L);
473 img->id_ = CGImageCreate( img->w(), img->h(), 8, img->d()*8, img->ld()?img->ld():img->w()*img->d(),
474 lut, (img->d()&1)?kCGImageAlphaNone:kCGImageAlphaLast,
475 src, 0L, false, kCGRenderingIntentDefault);
476 CGColorSpaceRelease(lut);
477 CGDataProviderRelease(src);
479 if (img->id_ && fl_gc) {
480 CGRect rect = { { X, Y }, { W, H } };
481 Fl_X::q_begin_image(rect, cx, cy, img->w(), img->h());
482 CGContextDrawImage(fl_gc, rect, (CGImageRef)img->id_);
483 Fl_X::q_end_image();
487 #elif defined(WIN32)
488 void Fl_GDI_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
489 int X, Y, W, H;
490 // Don't draw an empty image...
491 if (!img->d() || !img->array) {
492 img->draw_empty(XP, YP);
493 return;
495 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
496 return;
498 if (!img->id_) {
499 img->id_ = fl_create_offscreen(img->w(), img->h());
500 if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
501 fl_begin_offscreen((Fl_Offscreen)img->id_);
502 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
503 fl_end_offscreen();
504 } else {
505 fl_begin_offscreen((Fl_Offscreen)img->id_);
506 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
507 fl_end_offscreen();
508 if (img->d() == 2 || img->d() == 4) {
509 img->mask_ = fl_create_alphamask(img->w(), img->h(), img->d(), img->ld(), img->array);
513 if (img->mask_) {
514 HDC new_gc = CreateCompatibleDC(fl_gc);
515 int save = SaveDC(new_gc);
516 SelectObject(new_gc, (void*)img->mask_);
517 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCAND);
518 SelectObject(new_gc, (void*)img->id_);
519 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCPAINT);
520 RestoreDC(new_gc,save);
521 DeleteDC(new_gc);
522 } else if (img->d()==2 || img->d()==4) {
523 fl_copy_offscreen_with_alpha(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
524 } else {
525 fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
529 #else
530 void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
531 int X, Y, W, H;
532 // Don't draw an empty image...
533 if (!img->d() || !img->array) {
534 img->draw_empty(XP, YP);
535 return;
537 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
538 return;
540 if (!img->id_) {
541 if (img->d() == 1 || img->d() == 3) {
542 img->id_ = fl_create_offscreen(img->w(), img->h());
543 fl_begin_offscreen((Fl_Offscreen)img->id_);
544 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
545 fl_end_offscreen();
548 if (img->id_) {
549 if (img->mask_) {
550 // I can't figure out how to combine a mask with existing region,
551 // so cut the image down to a clipped rectangle:
552 int nx, ny; fl_clip_box(X,Y,W,H,nx,ny,W,H);
553 cx += nx-X; X = nx;
554 cy += ny-Y; Y = ny;
555 // make X use the bitmap as a mask:
556 XSetClipMask(fl_display, fl_gc, img->mask_);
557 int ox = X-cx; if (ox < 0) ox += img->w();
558 int oy = Y-cy; if (oy < 0) oy += img->h();
559 XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy);
562 fl_copy_offscreen(X, Y, W, H, img->id_, cx, cy);
564 if (img->mask_) {
565 // put the old clip region back
566 XSetClipOrigin(fl_display, fl_gc, 0, 0);
567 fl_restore_clip();
569 } else {
570 // Composite image with alpha manually each time...
571 alpha_blend(img, X, Y, W, H, cx, cy);
575 #endif
577 void Fl_RGB_Image::label(Fl_Widget* widget) {
578 widget->image(this);
581 void Fl_RGB_Image::label(Fl_Menu_Item* m) {
582 Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
583 m->label(_FL_IMAGE_LABEL, (const char*)this);
588 // End of "$Id: Fl_Image.cxx 8611 2011-04-20 14:01:04Z AlbrechtS $".