2 // "$Id: Fl_Image.cxx 8611 2011-04-20 14:01:04Z AlbrechtS $"
4 // Image drawing code for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
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
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
30 #include <FL/fl_draw.H>
32 #include <FL/Fl_Widget.H>
33 #include <FL/Fl_Menu_Item.H>
34 #include <FL/Fl_Image.H>
36 #include <FL/Fl_Cairo.H>
39 void fl_release_dc(HWND
, HDC
); // from Fl_win32.cxx
42 void fl_restore_clip(); // from fl_rect.cxx
45 // Base image class...
49 The destructor is a virtual method that frees all memory used
52 Fl_Image::~Fl_Image() {
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) {
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
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
);
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
88 Fl_Image
*Fl_Image::copy(int W
, int H
) {
89 return new Fl_Image(W
, H
, d());
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
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
120 void Fl_Image::label(Fl_Widget
* widget
) {
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
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);
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
);
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
);
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() {
180 if (alloc_array
) delete[] (uchar
*)array
;
183 void Fl_RGB_Image::uncache() {
184 #ifdef __APPLE_QUARTZ__
186 CGImageRelease((CGImageRef
)id_
);
191 fl_delete_offscreen((Fl_Offscreen
)id_
);
196 fl_delete_bitmask((Fl_Bitmask
)mask_
);
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
) {
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
);
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;
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;
245 fmt
= CAIRO_FORMAT_ARGB32
;
248 fmt
= CAIRO_FORMAT_RGB24
;
251 fmt
= CAIRO_FORMAT_A8
;
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
);
272 cairo_surface_destroy( si
);
273 cairo_surface_destroy( di
);
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...
285 // Allocate memory as needed...
289 if (!alloc_array
) new_array
= new uchar
[h() * w() * d()];
290 else new_array
= (uchar
*)array
;
292 // Get the color to blend with...
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
);
305 // Update the image data to do the blend...
306 const uchar
*old_ptr
;
308 int line_i
= ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
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
++;
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...
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...
344 // Delete any existing pixmap/mask objects...
347 // Allocate memory for a grayscale image...
353 new_array
= new uchar
[h() * w() * new_d
];
355 // Copy the image data, converting to grayscale...
356 const uchar
*old_ptr
;
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
;
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
) {
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];
388 fl_read_image(dst
, X
, Y
, W
, H
, 0);
390 uchar srcr
, srcg
, srcb
, srca
;
391 uchar dstr
, dstg
, dstb
, dsta
;
394 // Composite grayscale + alpha over RGB...
395 for (int y
= H
; y
> 0; y
--, srcptr
+=srcskip
)
396 for (int x
= W
; x
> 0; x
--) {
405 *dstptr
++ = (srcg
* srca
+ dstr
* dsta
) >> 8;
406 *dstptr
++ = (srcg
* srca
+ dstg
* dsta
) >> 8;
407 *dstptr
++ = (srcg
* srca
+ dstb
* dsta
) >> 8;
410 // Composite RGBA over RGB...
411 for (int y
= H
; y
> 0; y
--, srcptr
+=srcskip
)
412 for (int x
= W
; x
> 0; x
--) {
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);
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;
456 void Fl_Quartz_Graphics_Driver::draw(Fl_RGB_Image
*img
, int XP
, int YP
, int WP
, int HP
, int cx
, int cy
) {
458 // Don't draw an empty image...
459 if (!img
->d() || !img
->array
) {
460 img
->draw_empty(XP
, YP
);
463 if (start(img
, XP
, YP
, WP
, HP
, img
->w(), img
->h(), cx
, cy
, X
, Y
, W
, H
)) {
467 CGColorSpaceRef lut
= 0;
469 lut
= CGColorSpaceCreateDeviceGray();
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_
);
488 void Fl_GDI_Graphics_Driver::draw(Fl_RGB_Image
*img
, int XP
, int YP
, int WP
, int HP
, int cx
, int cy
) {
490 // Don't draw an empty image...
491 if (!img
->d() || !img
->array
) {
492 img
->draw_empty(XP
, YP
);
495 if (start(img
, XP
, YP
, WP
, HP
, img
->w(), img
->h(), cx
, cy
, X
, Y
, W
, H
)) {
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());
505 fl_begin_offscreen((Fl_Offscreen
)img
->id_
);
506 fl_draw_image(img
->array
, 0, 0, img
->w(), img
->h(), img
->d(), img
->ld());
508 if (img
->d() == 2 || img
->d() == 4) {
509 img
->mask_
= fl_create_alphamask(img
->w(), img
->h(), img
->d(), img
->ld(), img
->array
);
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
);
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
);
525 fl_copy_offscreen(X
, Y
, W
, H
, (Fl_Offscreen
)img
->id_
, cx
, cy
);
530 void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image
*img
, int XP
, int YP
, int WP
, int HP
, int cx
, int cy
) {
532 // Don't draw an empty image...
533 if (!img
->d() || !img
->array
) {
534 img
->draw_empty(XP
, YP
);
537 if (start(img
, XP
, YP
, WP
, HP
, img
->w(), img
->h(), cx
, cy
, X
, Y
, W
, H
)) {
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());
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
);
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
);
565 // put the old clip region back
566 XSetClipOrigin(fl_display
, fl_gc
, 0, 0);
570 // Composite image with alpha manually each time...
571 alpha_blend(img
, X
, Y
, W
, H
, cx
, cy
);
577 void Fl_RGB_Image::label(Fl_Widget
* widget
) {
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 $".