blob: 4117ba77bfd6de238e6026b6f030cab94de288d3 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
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.
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#include <FL/Fl.H>
29#include <FL/fl_draw.H>
30#include <FL/x.H>
31#include <FL/Fl_Widget.H>
32#include <FL/Fl_Menu_Item.H>
33#include <FL/Fl_Image.H>
34#include "flstring.h"
35
36#ifdef WIN32
37void fl_release_dc(HWND, HDC); // from Fl_win32.cxx
38#endif
39
40void fl_restore_clip(); // from fl_rect.cxx
41
42//
43// Base image class...
44//
45
46/**
47 The destructor is a virtual method that frees all memory used
48 by the image.
49*/
50Fl_Image::~Fl_Image() {
51}
52
53/**
54 If the image has been cached for display, delete the cache
55 data. This allows you to change the data used for the image and
56 then redraw it without recreating an image object.
57*/
58void Fl_Image::uncache() {
59}
60
61void Fl_Image::draw(int XP, int YP, int, int, int, int) {
62 draw_empty(XP, YP);
63}
64
65/**
66 The protected method draw_empty() draws a box with
67 an X in it. It can be used to draw any image that lacks image
68 data.
69*/
70void Fl_Image::draw_empty(int X, int Y) {
71 if (w() > 0 && h() > 0) {
72 fl_color(FL_FOREGROUND_COLOR);
73 fl_rect(X, Y, w(), h());
74 fl_line(X, Y, X + w() - 1, Y + h() - 1);
75 fl_line(X, Y + h() - 1, X + w() - 1, Y);
76 }
77}
78
79/**
80 The copy() method creates a copy of the specified
81 image. If the width and height are provided, the image is
82 resized to the specified size. The image should be deleted (or in
83 the case of Fl_Shared_Image, released) when you are done
84 with it.
85*/
86Fl_Image *Fl_Image::copy(int W, int H) {
87 return new Fl_Image(W, H, d());
88}
89
90/**
91 The color_average() method averages the colors in
92 the image with the FLTK color value c. The i
93 argument specifies the amount of the original image to combine
94 with the color, so a value of 1.0 results in no color blend, and
95 a value of 0.0 results in a constant image of the specified
96 color. <I>The original image data is not altered by this
97 method.</I>
98*/
99void Fl_Image::color_average(Fl_Color, float) {
100}
101
102/**
103 The desaturate() method converts an image to
104 grayscale. If the image contains an alpha channel (depth = 4),
105 the alpha channel is preserved. <I>This method does not alter
106 the original image data.</I>
107*/
108void Fl_Image::desaturate() {
109}
110
111/**
112 The label() methods are an obsolete way to set the
113 image attribute of a widget or menu item. Use the
114 image() or deimage() methods of the
115 Fl_Widget and Fl_Menu_Item classes
116 instead.
117*/
118void Fl_Image::label(Fl_Widget* widget) {
119 widget->image(this);
120}
121
122/**
123 The label() methods are an obsolete way to set the
124 image attribute of a widget or menu item. Use the
125 image() or deimage() methods of the
126 Fl_Widget and Fl_Menu_Item classes
127 instead.
128*/
129void Fl_Image::label(Fl_Menu_Item* m) {
130 Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
131 m->label(_FL_IMAGE_LABEL, (const char*)this);
132}
133
134void
135Fl_Image::labeltype(const Fl_Label *lo, // I - Label
136 int lx, // I - X position
137 int ly, // I - Y position
138 int lw, // I - Width of label
139 int lh, // I - Height of label
140 Fl_Align la) { // I - Alignment
141 Fl_Image *img; // Image pointer
142 int cx, cy; // Image position
143
144 img = (Fl_Image *)(lo->value);
145
146 if (la & FL_ALIGN_LEFT) cx = 0;
147 else if (la & FL_ALIGN_RIGHT) cx = img->w() - lw;
148 else cx = (img->w() - lw) / 2;
149
150 if (la & FL_ALIGN_TOP) cy = 0;
151 else if (la & FL_ALIGN_BOTTOM) cy = img->h() - lh;
152 else cy = (img->h() - lh) / 2;
153
154 fl_color((Fl_Color)lo->color);
155
156 img->draw(lx, ly, lw, lh, cx, cy);
157}
158
159void
160Fl_Image::measure(const Fl_Label *lo, // I - Label
161 int &lw, // O - Width of image
162 int &lh) { // O - Height of image
163 Fl_Image *img; // Image pointer
164
165 img = (Fl_Image *)(lo->value);
166
167 lw = img->w();
168 lh = img->h();
169}
170
171
172//
173// RGB image class...
174//
175/** The destructor free all memory and server resources that are used by the image. */
176Fl_RGB_Image::~Fl_RGB_Image() {
177 uncache();
178 if (alloc_array) delete[] (uchar *)array;
179}
180
181void Fl_RGB_Image::uncache() {
182#ifdef __APPLE_QUARTZ__
183 if (id_) {
184 CGImageRelease((CGImageRef)id_);
185 id_ = 0;
186 }
187#else
188 if (id_) {
189 fl_delete_offscreen((Fl_Offscreen)id_);
190 id_ = 0;
191 }
192
193 if (mask_) {
194 fl_delete_bitmask((Fl_Bitmask)mask_);
195 mask_ = 0;
196 }
197#endif
198}
199
200Fl_Image *Fl_RGB_Image::copy(int W, int H) {
201 Fl_RGB_Image *new_image; // New RGB image
202 uchar *new_array; // New array for image data
203
204 // Optimize the simple copy where the width and height are the same,
205 // or when we are copying an empty image...
206 if ((W == w() && H == h()) ||
207 !w() || !h() || !d() || !array) {
208 if (array) {
209 // Make a copy of the image data and return a new Fl_RGB_Image...
210 new_array = new uchar[w() * h() * d()];
211 if (ld() && ld()!=w()*d()) {
212 const uchar *src = array;
213 uchar *dst = new_array;
214 int dy, dh = h(), wd = w()*d(), wld = ld();
215 for (dy=0; dy<dh; dy++) {
216 memcpy(dst, src, wd);
217 src += wld;
218 dst += wd;
219 }
220 } else {
221 memcpy(new_array, array, w() * h() * d());
222 }
223 new_image = new Fl_RGB_Image(new_array, w(), h(), d());
224 new_image->alloc_array = 1;
225
226 return new_image;
227 } else return new Fl_RGB_Image(array, w(), h(), d(), ld());
228 }
229 if (W <= 0 || H <= 0) return 0;
230
231 // OK, need to resize the image data; allocate memory and
232 uchar *new_ptr; // Pointer into new array
233 const uchar *old_ptr; // Pointer into old array
234 int c, // Channel number
235 sy, // Source coordinate
236 dx, dy, // Destination coordinates
237 xerr, yerr, // X & Y errors
238 xmod, ymod, // X & Y moduli
239 xstep, ystep, // X & Y step increments
240 line_d; // stride from line to line
241
242
243 // Figure out Bresenheim step/modulus values...
244 xmod = w() % W;
245 xstep = (w() / W) * d();
246 ymod = h() % H;
247 ystep = h() / H;
248 line_d = ld() ? ld() : w() * d();
249
250 // Allocate memory for the new image...
251 new_array = new uchar [W * H * d()];
252 new_image = new Fl_RGB_Image(new_array, W, H, d());
253 new_image->alloc_array = 1;
254
255 // Scale the image using a nearest-neighbor algorithm...
256 for (dy = H, sy = 0, yerr = H, new_ptr = new_array; dy > 0; dy --) {
257 for (dx = W, xerr = W, old_ptr = array + sy * line_d; dx > 0; dx --) {
258 for (c = 0; c < d(); c ++) *new_ptr++ = old_ptr[c];
259
260 old_ptr += xstep;
261 xerr -= xmod;
262
263 if (xerr <= 0) {
264 xerr += W;
265 old_ptr += d();
266 }
267 }
268
269 sy += ystep;
270 yerr -= ymod;
271 if (yerr <= 0) {
272 yerr += H;
273 sy ++;
274 }
275 }
276
277 return new_image;
278}
279
280void Fl_RGB_Image::color_average(Fl_Color c, float i) {
281 // Don't average an empty image...
282 if (!w() || !h() || !d() || !array) return;
283
284 // Delete any existing pixmap/mask objects...
285 uncache();
286
287 // Allocate memory as needed...
288 uchar *new_array,
289 *new_ptr;
290
291 if (!alloc_array) new_array = new uchar[h() * w() * d()];
292 else new_array = (uchar *)array;
293
294 // Get the color to blend with...
295 uchar r, g, b;
296 unsigned ia, ir, ig, ib;
297
298 Fl::get_color(c, r, g, b);
299 if (i < 0.0f) i = 0.0f;
300 else if (i > 1.0f) i = 1.0f;
301
302 ia = (unsigned)(256 * i);
303 ir = r * (256 - ia);
304 ig = g * (256 - ia);
305 ib = b * (256 - ia);
306
307 // Update the image data to do the blend...
308 const uchar *old_ptr;
309 int x, y;
310 int line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
311
312 if (d() < 3) {
313 ig = (r * 31 + g * 61 + b * 8) / 100 * (256 - ia);
314
315 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
316 for (x = 0; x < w(); x ++) {
317 *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
318 if (d() > 1) *new_ptr++ = *old_ptr++;
319 }
320 } else {
321 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
322 for (x = 0; x < w(); x ++) {
323 *new_ptr++ = (*old_ptr++ * ia + ir) >> 8;
324 *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
325 *new_ptr++ = (*old_ptr++ * ia + ib) >> 8;
326 if (d() > 3) *new_ptr++ = *old_ptr++;
327 }
328 }
329
330 // Set the new pointers/values as needed...
331 if (!alloc_array) {
332 array = new_array;
333 alloc_array = 1;
334
335 ld(0);
336 }
337}
338
339void Fl_RGB_Image::desaturate() {
340 // Don't desaturate an empty image...
341 if (!w() || !h() || !d() || !array) return;
342
343 // Can only desaturate color images...
344 if (d() < 3) return;
345
346 // Delete any existing pixmap/mask objects...
347 uncache();
348
349 // Allocate memory for a grayscale image...
350 uchar *new_array,
351 *new_ptr;
352 int new_d;
353
354 new_d = d() - 2;
355 new_array = new uchar[h() * w() * new_d];
356
357 // Copy the image data, converting to grayscale...
358 const uchar *old_ptr;
359 int x, y;
360 int line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
361
362 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
363 for (x = 0; x < w(); x ++, old_ptr += d()) {
364 *new_ptr++ = (uchar)((31 * old_ptr[0] + 61 * old_ptr[1] + 8 * old_ptr[2]) / 100);
365 if (d() > 3) *new_ptr++ = old_ptr[3];
366 }
367
368 // Free the old array as needed, and then set the new pointers/values...
369 if (alloc_array) delete[] (uchar *)array;
370
371 array = new_array;
372 alloc_array = 1;
373
374 ld(0);
375 d(new_d);
376}
377
378#if !defined(WIN32) && !defined(__APPLE_QUARTZ__)
379// Composite an image with alpha on systems that don't have accelerated
380// alpha compositing...
381static void alpha_blend(Fl_RGB_Image *img, int X, int Y, int W, int H, int cx, int cy) {
382 int ld = img->ld();
383 if (ld == 0) ld = img->w() * img->d();
384 uchar *srcptr = (uchar*)img->array + cy * ld + cx * img->d();
385 int srcskip = ld - img->d() * W;
386
387 uchar *dst = new uchar[W * H * 3];
388 uchar *dstptr = dst;
389
390 fl_read_image(dst, X, Y, W, H, 0);
391
392 uchar srcr, srcg, srcb, srca;
393 uchar dstr, dstg, dstb, dsta;
394
395 if (img->d() == 2) {
396 // Composite grayscale + alpha over RGB...
397 for (int y = H; y > 0; y--, srcptr+=srcskip)
398 for (int x = W; x > 0; x--) {
399 srcg = *srcptr++;
400 srca = *srcptr++;
401
402 dstr = dstptr[0];
403 dstg = dstptr[1];
404 dstb = dstptr[2];
405 dsta = 255 - srca;
406
407 *dstptr++ = (srcg * srca + dstr * dsta) >> 8;
408 *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
409 *dstptr++ = (srcg * srca + dstb * dsta) >> 8;
410 }
411 } else {
412 // Composite RGBA over RGB...
413 for (int y = H; y > 0; y--, srcptr+=srcskip)
414 for (int x = W; x > 0; x--) {
415 srcr = *srcptr++;
416 srcg = *srcptr++;
417 srcb = *srcptr++;
418 srca = *srcptr++;
419
420 dstr = dstptr[0];
421 dstg = dstptr[1];
422 dstb = dstptr[2];
423 dsta = 255 - srca;
424
425 *dstptr++ = (srcr * srca + dstr * dsta) >> 8;
426 *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
427 *dstptr++ = (srcb * srca + dstb * dsta) >> 8;
428 }
429 }
430
431 fl_draw_image(dst, X, Y, W, H, 3, 0);
432
433 delete[] dst;
434}
435#endif // !WIN32 && !__APPLE_QUARTZ__
436
437void Fl_RGB_Image::draw(int XP, int YP, int WP, int HP, int cx, int cy) {
438 fl_graphics_driver->draw(this, XP, YP, WP, HP, cx, cy);
439}
440
441static int start(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int w, int h, int &cx, int &cy,
442 int &X, int &Y, int &W, int &H)
443{
444 // account for current clip region (faster on Irix):
445 fl_clip_box(XP,YP,WP,HP,X,Y,W,H);
446 cx += X-XP; cy += Y-YP;
447 // clip the box down to the size of image, quit if empty:
448 if (cx < 0) {W += cx; X -= cx; cx = 0;}
449 if (cx+W > w) W = w-cx;
450 if (W <= 0) return 1;
451 if (cy < 0) {H += cy; Y -= cy; cy = 0;}
452 if (cy+H > h) H = h-cy;
453 if (H <= 0) return 1;
454 return 0;
455}
456
457#ifdef __APPLE__
458void Fl_Quartz_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
459 int X, Y, W, H;
460 // Don't draw an empty image...
461 if (!img->d() || !img->array) {
462 img->draw_empty(XP, YP);
463 return;
464 }
465 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
466 return;
467 }
468 if (!img->id_) {
469 CGColorSpaceRef lut = 0;
470 if (img->d()<=2)
471 lut = CGColorSpaceCreateDeviceGray();
472 else
473 lut = CGColorSpaceCreateDeviceRGB();
474 CGDataProviderRef src = CGDataProviderCreateWithData( 0L, img->array, img->w()*img->h()*img->d(), 0L);
475 img->id_ = CGImageCreate( img->w(), img->h(), 8, img->d()*8, img->ld()?img->ld():img->w()*img->d(),
476 lut, (img->d()&1)?kCGImageAlphaNone:kCGImageAlphaLast,
477 src, 0L, false, kCGRenderingIntentDefault);
478 CGColorSpaceRelease(lut);
479 CGDataProviderRelease(src);
480 }
481 if (img->id_ && fl_gc) {
482 CGRect rect = { { X, Y }, { W, H } };
483 Fl_X::q_begin_image(rect, cx, cy, img->w(), img->h());
484 CGContextDrawImage(fl_gc, rect, (CGImageRef)img->id_);
485 Fl_X::q_end_image();
486 }
487}
488
489#elif defined(WIN32)
490void Fl_GDI_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
491 int X, Y, W, H;
492 // Don't draw an empty image...
493 if (!img->d() || !img->array) {
494 img->draw_empty(XP, YP);
495 return;
496 }
497 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
498 return;
499 }
500 if (!img->id_) {
501 img->id_ = fl_create_offscreen(img->w(), img->h());
502 if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
503 fl_begin_offscreen((Fl_Offscreen)img->id_);
504 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
505 fl_end_offscreen();
506 } else {
507 fl_begin_offscreen((Fl_Offscreen)img->id_);
508 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
509 fl_end_offscreen();
510 if (img->d() == 2 || img->d() == 4) {
511 img->mask_ = fl_create_alphamask(img->w(), img->h(), img->d(), img->ld(), img->array);
512 }
513 }
514 }
515 if (img->mask_) {
516 HDC new_gc = CreateCompatibleDC(fl_gc);
517 int save = SaveDC(new_gc);
518 SelectObject(new_gc, (void*)img->mask_);
519 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCAND);
520 SelectObject(new_gc, (void*)img->id_);
521 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCPAINT);
522 RestoreDC(new_gc,save);
523 DeleteDC(new_gc);
524 } else if (img->d()==2 || img->d()==4) {
525 fl_copy_offscreen_with_alpha(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
526 } else {
527 fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
528 }
529}
530
531#else
532void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
533 int X, Y, W, H;
534 // Don't draw an empty image...
535 if (!img->d() || !img->array) {
536 img->draw_empty(XP, YP);
537 return;
538 }
539 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
540 return;
541 }
542 if (!img->id_) {
543 if (img->d() == 1 || img->d() == 3) {
544 img->id_ = fl_create_offscreen(img->w(), img->h());
545 fl_begin_offscreen((Fl_Offscreen)img->id_);
546 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
547 fl_end_offscreen();
548 }
549 }
550 if (img->id_) {
551 if (img->mask_) {
552 // I can't figure out how to combine a mask with existing region,
553 // so cut the image down to a clipped rectangle:
554 int nx, ny; fl_clip_box(X,Y,W,H,nx,ny,W,H);
555 cx += nx-X; X = nx;
556 cy += ny-Y; Y = ny;
557 // make X use the bitmap as a mask:
558 XSetClipMask(fl_display, fl_gc, img->mask_);
559 int ox = X-cx; if (ox < 0) ox += img->w();
560 int oy = Y-cy; if (oy < 0) oy += img->h();
561 XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy);
562 }
563
564 fl_copy_offscreen(X, Y, W, H, img->id_, cx, cy);
565
566 if (img->mask_) {
567 // put the old clip region back
568 XSetClipOrigin(fl_display, fl_gc, 0, 0);
569 fl_restore_clip();
570 }
571 } else {
572 // Composite image with alpha manually each time...
573 alpha_blend(img, X, Y, W, H, cx, cy);
574 }
575}
576
577#endif
578
579void Fl_RGB_Image::label(Fl_Widget* widget) {
580 widget->image(this);
581}
582
583void Fl_RGB_Image::label(Fl_Menu_Item* m) {
584 Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
585 m->label(_FL_IMAGE_LABEL, (const char*)this);
586}
587
588
589//
590// End of "$Id: Fl_Image.cxx 8611 2011-04-20 14:01:04Z AlbrechtS $".
591//