blob: 58157be43a5246734af93ee7cc31d20d4e69cd05 [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//
DRC685f17e2011-07-28 09:23:00 +0000175
176int fl_convert_pixmap(const char*const* cdata, uchar* out, Fl_Color bg);
177
178/** The constructor creates a new RGBA image from the specified Fl_Pixmap. */
179Fl_RGB_Image::Fl_RGB_Image(const Fl_Pixmap *pxm, Fl_Color bg):
180 Fl_Image(pxm->w(), pxm->h(), 4), id_(0), mask_(0)
181{
182 array = new uchar[w() * h() * d()];
183 alloc_array = 1;
184 fl_convert_pixmap(pxm->data(), (uchar*)array, bg);
185 data((const char **)&array, 1);
186}
187
DRC2ff39b82011-07-28 08:38:59 +0000188/** The destructor free all memory and server resources that are used by the image. */
189Fl_RGB_Image::~Fl_RGB_Image() {
190 uncache();
191 if (alloc_array) delete[] (uchar *)array;
192}
193
194void Fl_RGB_Image::uncache() {
195#ifdef __APPLE_QUARTZ__
196 if (id_) {
197 CGImageRelease((CGImageRef)id_);
198 id_ = 0;
199 }
200#else
201 if (id_) {
202 fl_delete_offscreen((Fl_Offscreen)id_);
203 id_ = 0;
204 }
205
206 if (mask_) {
207 fl_delete_bitmask((Fl_Bitmask)mask_);
208 mask_ = 0;
209 }
210#endif
211}
212
213Fl_Image *Fl_RGB_Image::copy(int W, int H) {
214 Fl_RGB_Image *new_image; // New RGB image
215 uchar *new_array; // New array for image data
216
217 // Optimize the simple copy where the width and height are the same,
218 // or when we are copying an empty image...
219 if ((W == w() && H == h()) ||
220 !w() || !h() || !d() || !array) {
221 if (array) {
222 // Make a copy of the image data and return a new Fl_RGB_Image...
223 new_array = new uchar[w() * h() * d()];
224 if (ld() && ld()!=w()*d()) {
225 const uchar *src = array;
226 uchar *dst = new_array;
227 int dy, dh = h(), wd = w()*d(), wld = ld();
228 for (dy=0; dy<dh; dy++) {
229 memcpy(dst, src, wd);
230 src += wld;
231 dst += wd;
232 }
233 } else {
234 memcpy(new_array, array, w() * h() * d());
235 }
236 new_image = new Fl_RGB_Image(new_array, w(), h(), d());
237 new_image->alloc_array = 1;
238
239 return new_image;
240 } else return new Fl_RGB_Image(array, w(), h(), d(), ld());
241 }
242 if (W <= 0 || H <= 0) return 0;
243
244 // OK, need to resize the image data; allocate memory and
245 uchar *new_ptr; // Pointer into new array
246 const uchar *old_ptr; // Pointer into old array
247 int c, // Channel number
248 sy, // Source coordinate
249 dx, dy, // Destination coordinates
250 xerr, yerr, // X & Y errors
251 xmod, ymod, // X & Y moduli
252 xstep, ystep, // X & Y step increments
253 line_d; // stride from line to line
254
255
256 // Figure out Bresenheim step/modulus values...
257 xmod = w() % W;
258 xstep = (w() / W) * d();
259 ymod = h() % H;
260 ystep = h() / H;
261 line_d = ld() ? ld() : w() * d();
262
263 // Allocate memory for the new image...
264 new_array = new uchar [W * H * d()];
265 new_image = new Fl_RGB_Image(new_array, W, H, d());
266 new_image->alloc_array = 1;
267
268 // Scale the image using a nearest-neighbor algorithm...
269 for (dy = H, sy = 0, yerr = H, new_ptr = new_array; dy > 0; dy --) {
270 for (dx = W, xerr = W, old_ptr = array + sy * line_d; dx > 0; dx --) {
271 for (c = 0; c < d(); c ++) *new_ptr++ = old_ptr[c];
272
273 old_ptr += xstep;
274 xerr -= xmod;
275
276 if (xerr <= 0) {
277 xerr += W;
278 old_ptr += d();
279 }
280 }
281
282 sy += ystep;
283 yerr -= ymod;
284 if (yerr <= 0) {
285 yerr += H;
286 sy ++;
287 }
288 }
289
290 return new_image;
291}
292
293void Fl_RGB_Image::color_average(Fl_Color c, float i) {
294 // Don't average an empty image...
295 if (!w() || !h() || !d() || !array) return;
296
297 // Delete any existing pixmap/mask objects...
298 uncache();
299
300 // Allocate memory as needed...
301 uchar *new_array,
302 *new_ptr;
303
304 if (!alloc_array) new_array = new uchar[h() * w() * d()];
305 else new_array = (uchar *)array;
306
307 // Get the color to blend with...
308 uchar r, g, b;
309 unsigned ia, ir, ig, ib;
310
311 Fl::get_color(c, r, g, b);
312 if (i < 0.0f) i = 0.0f;
313 else if (i > 1.0f) i = 1.0f;
314
315 ia = (unsigned)(256 * i);
316 ir = r * (256 - ia);
317 ig = g * (256 - ia);
318 ib = b * (256 - ia);
319
320 // Update the image data to do the blend...
321 const uchar *old_ptr;
322 int x, y;
323 int line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
324
325 if (d() < 3) {
326 ig = (r * 31 + g * 61 + b * 8) / 100 * (256 - ia);
327
328 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
329 for (x = 0; x < w(); x ++) {
330 *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
331 if (d() > 1) *new_ptr++ = *old_ptr++;
332 }
333 } else {
334 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
335 for (x = 0; x < w(); x ++) {
336 *new_ptr++ = (*old_ptr++ * ia + ir) >> 8;
337 *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
338 *new_ptr++ = (*old_ptr++ * ia + ib) >> 8;
339 if (d() > 3) *new_ptr++ = *old_ptr++;
340 }
341 }
342
343 // Set the new pointers/values as needed...
344 if (!alloc_array) {
345 array = new_array;
346 alloc_array = 1;
347
348 ld(0);
349 }
350}
351
352void Fl_RGB_Image::desaturate() {
353 // Don't desaturate an empty image...
354 if (!w() || !h() || !d() || !array) return;
355
356 // Can only desaturate color images...
357 if (d() < 3) return;
358
359 // Delete any existing pixmap/mask objects...
360 uncache();
361
362 // Allocate memory for a grayscale image...
363 uchar *new_array,
364 *new_ptr;
365 int new_d;
366
367 new_d = d() - 2;
368 new_array = new uchar[h() * w() * new_d];
369
370 // Copy the image data, converting to grayscale...
371 const uchar *old_ptr;
372 int x, y;
373 int line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
374
375 for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
376 for (x = 0; x < w(); x ++, old_ptr += d()) {
377 *new_ptr++ = (uchar)((31 * old_ptr[0] + 61 * old_ptr[1] + 8 * old_ptr[2]) / 100);
378 if (d() > 3) *new_ptr++ = old_ptr[3];
379 }
380
381 // Free the old array as needed, and then set the new pointers/values...
382 if (alloc_array) delete[] (uchar *)array;
383
384 array = new_array;
385 alloc_array = 1;
386
387 ld(0);
388 d(new_d);
389}
390
391#if !defined(WIN32) && !defined(__APPLE_QUARTZ__)
392// Composite an image with alpha on systems that don't have accelerated
393// alpha compositing...
394static void alpha_blend(Fl_RGB_Image *img, int X, int Y, int W, int H, int cx, int cy) {
395 int ld = img->ld();
396 if (ld == 0) ld = img->w() * img->d();
397 uchar *srcptr = (uchar*)img->array + cy * ld + cx * img->d();
398 int srcskip = ld - img->d() * W;
399
400 uchar *dst = new uchar[W * H * 3];
401 uchar *dstptr = dst;
402
403 fl_read_image(dst, X, Y, W, H, 0);
404
405 uchar srcr, srcg, srcb, srca;
406 uchar dstr, dstg, dstb, dsta;
407
408 if (img->d() == 2) {
409 // Composite grayscale + alpha over RGB...
410 for (int y = H; y > 0; y--, srcptr+=srcskip)
411 for (int x = W; x > 0; x--) {
412 srcg = *srcptr++;
413 srca = *srcptr++;
414
415 dstr = dstptr[0];
416 dstg = dstptr[1];
417 dstb = dstptr[2];
418 dsta = 255 - srca;
419
420 *dstptr++ = (srcg * srca + dstr * dsta) >> 8;
421 *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
422 *dstptr++ = (srcg * srca + dstb * dsta) >> 8;
423 }
424 } else {
425 // Composite RGBA over RGB...
426 for (int y = H; y > 0; y--, srcptr+=srcskip)
427 for (int x = W; x > 0; x--) {
428 srcr = *srcptr++;
429 srcg = *srcptr++;
430 srcb = *srcptr++;
431 srca = *srcptr++;
432
433 dstr = dstptr[0];
434 dstg = dstptr[1];
435 dstb = dstptr[2];
436 dsta = 255 - srca;
437
438 *dstptr++ = (srcr * srca + dstr * dsta) >> 8;
439 *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
440 *dstptr++ = (srcb * srca + dstb * dsta) >> 8;
441 }
442 }
443
444 fl_draw_image(dst, X, Y, W, H, 3, 0);
445
446 delete[] dst;
447}
448#endif // !WIN32 && !__APPLE_QUARTZ__
449
450void Fl_RGB_Image::draw(int XP, int YP, int WP, int HP, int cx, int cy) {
451 fl_graphics_driver->draw(this, XP, YP, WP, HP, cx, cy);
452}
453
454static int start(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int w, int h, int &cx, int &cy,
455 int &X, int &Y, int &W, int &H)
456{
457 // account for current clip region (faster on Irix):
458 fl_clip_box(XP,YP,WP,HP,X,Y,W,H);
459 cx += X-XP; cy += Y-YP;
460 // clip the box down to the size of image, quit if empty:
461 if (cx < 0) {W += cx; X -= cx; cx = 0;}
462 if (cx+W > w) W = w-cx;
463 if (W <= 0) return 1;
464 if (cy < 0) {H += cy; Y -= cy; cy = 0;}
465 if (cy+H > h) H = h-cy;
466 if (H <= 0) return 1;
467 return 0;
468}
469
470#ifdef __APPLE__
471void Fl_Quartz_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
472 int X, Y, W, H;
473 // Don't draw an empty image...
474 if (!img->d() || !img->array) {
475 img->draw_empty(XP, YP);
476 return;
477 }
478 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
479 return;
480 }
481 if (!img->id_) {
482 CGColorSpaceRef lut = 0;
483 if (img->d()<=2)
484 lut = CGColorSpaceCreateDeviceGray();
485 else
486 lut = CGColorSpaceCreateDeviceRGB();
487 CGDataProviderRef src = CGDataProviderCreateWithData( 0L, img->array, img->w()*img->h()*img->d(), 0L);
488 img->id_ = CGImageCreate( img->w(), img->h(), 8, img->d()*8, img->ld()?img->ld():img->w()*img->d(),
489 lut, (img->d()&1)?kCGImageAlphaNone:kCGImageAlphaLast,
490 src, 0L, false, kCGRenderingIntentDefault);
491 CGColorSpaceRelease(lut);
492 CGDataProviderRelease(src);
493 }
494 if (img->id_ && fl_gc) {
495 CGRect rect = { { X, Y }, { W, H } };
496 Fl_X::q_begin_image(rect, cx, cy, img->w(), img->h());
497 CGContextDrawImage(fl_gc, rect, (CGImageRef)img->id_);
498 Fl_X::q_end_image();
499 }
500}
501
502#elif defined(WIN32)
503void Fl_GDI_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
504 int X, Y, W, H;
505 // Don't draw an empty image...
506 if (!img->d() || !img->array) {
507 img->draw_empty(XP, YP);
508 return;
509 }
510 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
511 return;
512 }
513 if (!img->id_) {
514 img->id_ = fl_create_offscreen(img->w(), img->h());
515 if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
516 fl_begin_offscreen((Fl_Offscreen)img->id_);
517 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
518 fl_end_offscreen();
519 } else {
520 fl_begin_offscreen((Fl_Offscreen)img->id_);
521 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
522 fl_end_offscreen();
523 if (img->d() == 2 || img->d() == 4) {
524 img->mask_ = fl_create_alphamask(img->w(), img->h(), img->d(), img->ld(), img->array);
525 }
526 }
527 }
528 if (img->mask_) {
529 HDC new_gc = CreateCompatibleDC(fl_gc);
530 int save = SaveDC(new_gc);
531 SelectObject(new_gc, (void*)img->mask_);
532 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCAND);
533 SelectObject(new_gc, (void*)img->id_);
534 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCPAINT);
535 RestoreDC(new_gc,save);
536 DeleteDC(new_gc);
537 } else if (img->d()==2 || img->d()==4) {
538 fl_copy_offscreen_with_alpha(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
539 } else {
540 fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
541 }
542}
543
544#else
545void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
546 int X, Y, W, H;
547 // Don't draw an empty image...
548 if (!img->d() || !img->array) {
549 img->draw_empty(XP, YP);
550 return;
551 }
552 if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
553 return;
554 }
555 if (!img->id_) {
556 if (img->d() == 1 || img->d() == 3) {
557 img->id_ = fl_create_offscreen(img->w(), img->h());
558 fl_begin_offscreen((Fl_Offscreen)img->id_);
559 fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
560 fl_end_offscreen();
561 }
562 }
563 if (img->id_) {
564 if (img->mask_) {
565 // I can't figure out how to combine a mask with existing region,
566 // so cut the image down to a clipped rectangle:
567 int nx, ny; fl_clip_box(X,Y,W,H,nx,ny,W,H);
568 cx += nx-X; X = nx;
569 cy += ny-Y; Y = ny;
570 // make X use the bitmap as a mask:
571 XSetClipMask(fl_display, fl_gc, img->mask_);
572 int ox = X-cx; if (ox < 0) ox += img->w();
573 int oy = Y-cy; if (oy < 0) oy += img->h();
574 XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy);
575 }
576
577 fl_copy_offscreen(X, Y, W, H, img->id_, cx, cy);
578
579 if (img->mask_) {
580 // put the old clip region back
581 XSetClipOrigin(fl_display, fl_gc, 0, 0);
582 fl_restore_clip();
583 }
584 } else {
585 // Composite image with alpha manually each time...
586 alpha_blend(img, X, Y, W, H, cx, cy);
587 }
588}
589
590#endif
591
592void Fl_RGB_Image::label(Fl_Widget* widget) {
593 widget->image(this);
594}
595
596void Fl_RGB_Image::label(Fl_Menu_Item* m) {
597 Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
598 m->label(_FL_IMAGE_LABEL, (const char*)this);
599}
600
601
602//
603// End of "$Id: Fl_Image.cxx 8611 2011-04-20 14:01:04Z AlbrechtS $".
604//