fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_Image.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_Image.cxx 7903 2010-11-28 21:06:39Z matt $"
00003 //
00004 // Image drawing code for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 #include <FL/Fl.H>
00029 #include <FL/fl_draw.H>
00030 #include <FL/x.H>
00031 #include <FL/Fl_Widget.H>
00032 #include <FL/Fl_Menu_Item.H>
00033 #include <FL/Fl_Image.H>
00034 #include "flstring.h"
00035 
00036 #ifdef WIN32
00037 void fl_release_dc(HWND, HDC); // from Fl_win32.cxx
00038 #endif
00039 
00040 void fl_restore_clip(); // from fl_rect.cxx
00041 
00042 //
00043 // Base image class...
00044 //
00045 
00050 Fl_Image::~Fl_Image() {
00051 }
00052 
00058 void Fl_Image::uncache() {
00059 }
00060 
00061 void Fl_Image::draw(int XP, int YP, int, int, int, int) {
00062   draw_empty(XP, YP);
00063 }
00064 
00070 void Fl_Image::draw_empty(int X, int Y) {
00071   if (w() > 0 && h() > 0) {
00072     fl_color(FL_FOREGROUND_COLOR);
00073     fl_rect(X, Y, w(), h());
00074     fl_line(X, Y, X + w() - 1, Y + h() - 1);
00075     fl_line(X, Y + h() - 1, X + w() - 1, Y);
00076   }
00077 }
00078 
00086 Fl_Image *Fl_Image::copy(int W, int H) {
00087   return new Fl_Image(W, H, d());
00088 }
00089 
00099 void Fl_Image::color_average(Fl_Color, float) {
00100 }
00101 
00108 void Fl_Image::desaturate() {
00109 }
00110 
00118 void Fl_Image::label(Fl_Widget* widget) {
00119   widget->image(this);
00120 }
00121 
00129 void Fl_Image::label(Fl_Menu_Item* m) {
00130   Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
00131   m->label(_FL_IMAGE_LABEL, (const char*)this);
00132 }
00133 
00134 void
00135 Fl_Image::labeltype(const Fl_Label *lo,         // I - Label
00136                     int            lx,          // I - X position
00137                     int            ly,          // I - Y position
00138                     int            lw,          // I - Width of label
00139                     int            lh,          // I - Height of label
00140                     Fl_Align       la) {        // I - Alignment
00141   Fl_Image      *img;                           // Image pointer
00142   int           cx, cy;                         // Image position
00143 
00144   img = (Fl_Image *)(lo->value);
00145 
00146   if (la & FL_ALIGN_LEFT) cx = 0;
00147   else if (la & FL_ALIGN_RIGHT) cx = img->w() - lw;
00148   else cx = (img->w() - lw) / 2;
00149 
00150   if (la & FL_ALIGN_TOP) cy = 0;
00151   else if (la & FL_ALIGN_BOTTOM) cy = img->h() - lh;
00152   else cy = (img->h() - lh) / 2;
00153 
00154   fl_color((Fl_Color)lo->color);
00155 
00156   img->draw(lx, ly, lw, lh, cx, cy);
00157 }
00158 
00159 void
00160 Fl_Image::measure(const Fl_Label *lo,           // I - Label
00161                   int            &lw,           // O - Width of image
00162                   int            &lh) {         // O - Height of image
00163   Fl_Image *img;                                // Image pointer
00164 
00165   img = (Fl_Image *)(lo->value);
00166 
00167   lw = img->w();
00168   lh = img->h();
00169 }
00170 
00171 
00172 //
00173 // RGB image class...
00174 //
00176 Fl_RGB_Image::~Fl_RGB_Image() {
00177   uncache();
00178   if (alloc_array) delete[] (uchar *)array;
00179 }
00180 
00181 void Fl_RGB_Image::uncache() {
00182 #ifdef __APPLE_QUARTZ__
00183   if (id_) {
00184     CGImageRelease((CGImageRef)id_);
00185     id_ = 0;
00186   }
00187 #else
00188   if (id_) {
00189     fl_delete_offscreen((Fl_Offscreen)id_);
00190     id_ = 0;
00191   }
00192 
00193   if (mask_) {
00194     fl_delete_bitmask((Fl_Bitmask)mask_);
00195     mask_ = 0;
00196   }
00197 #endif
00198 }
00199 
00200 Fl_Image *Fl_RGB_Image::copy(int W, int H) {
00201   Fl_RGB_Image  *new_image;     // New RGB image
00202   uchar         *new_array;     // New array for image data
00203 
00204   // Optimize the simple copy where the width and height are the same,
00205   // or when we are copying an empty image...
00206   if ((W == w() && H == h()) ||
00207       !w() || !h() || !d() || !array) {
00208     if (array) {
00209       // Make a copy of the image data and return a new Fl_RGB_Image...
00210       new_array = new uchar[w() * h() * d()];
00211       if (ld() && ld()!=w()*d()) {
00212         const uchar *src = array;
00213         uchar *dst = new_array;
00214         int dy, dh = h(), wd = w()*d(), wld = ld();
00215         for (dy=0; dy<dh; dy++) {
00216           memcpy(dst, src, wd);
00217           src += wld;
00218           dst += wd;
00219         }
00220       } else {
00221         memcpy(new_array, array, w() * h() * d());
00222       }
00223       new_image = new Fl_RGB_Image(new_array, w(), h(), d());
00224       new_image->alloc_array = 1;
00225 
00226       return new_image;
00227     } else return new Fl_RGB_Image(array, w(), h(), d(), ld());
00228   }
00229   if (W <= 0 || H <= 0) return 0;
00230 
00231   // OK, need to resize the image data; allocate memory and 
00232   uchar         *new_ptr;       // Pointer into new array
00233   const uchar   *old_ptr;       // Pointer into old array
00234   int           c,              // Channel number
00235                 sy,             // Source coordinate
00236                 dx, dy,         // Destination coordinates
00237                 xerr, yerr,     // X & Y errors
00238                 xmod, ymod,     // X & Y moduli
00239                 xstep, ystep,   // X & Y step increments
00240     line_d; // stride from line to line
00241 
00242 
00243   // Figure out Bresenheim step/modulus values...
00244   xmod   = w() % W;
00245   xstep  = (w() / W) * d();
00246   ymod   = h() % H;
00247   ystep  = h() / H;
00248   line_d = ld() ? ld() : w() * d();
00249 
00250   // Allocate memory for the new image...
00251   new_array = new uchar [W * H * d()];
00252   new_image = new Fl_RGB_Image(new_array, W, H, d());
00253   new_image->alloc_array = 1;
00254 
00255   // Scale the image using a nearest-neighbor algorithm...
00256   for (dy = H, sy = 0, yerr = H, new_ptr = new_array; dy > 0; dy --) {
00257     for (dx = W, xerr = W, old_ptr = array + sy * line_d; dx > 0; dx --) {
00258       for (c = 0; c < d(); c ++) *new_ptr++ = old_ptr[c];
00259 
00260       old_ptr += xstep;
00261       xerr    -= xmod;
00262 
00263       if (xerr <= 0) {
00264         xerr    += W;
00265         old_ptr += d();
00266       }
00267     }
00268 
00269     sy   += ystep;
00270     yerr -= ymod;
00271     if (yerr <= 0) {
00272       yerr += H;
00273       sy ++;
00274     }
00275   }
00276 
00277   return new_image;
00278 }
00279 
00280 void Fl_RGB_Image::color_average(Fl_Color c, float i) {
00281   // Don't average an empty image...
00282   if (!w() || !h() || !d() || !array) return;
00283 
00284   // Delete any existing pixmap/mask objects...
00285   uncache();
00286 
00287   // Allocate memory as needed...
00288   uchar         *new_array,
00289                 *new_ptr;
00290 
00291   if (!alloc_array) new_array = new uchar[h() * w() * d()];
00292   else new_array = (uchar *)array;
00293 
00294   // Get the color to blend with...
00295   uchar         r, g, b;
00296   unsigned      ia, ir, ig, ib;
00297 
00298   Fl::get_color(c, r, g, b);
00299   if (i < 0.0f) i = 0.0f;
00300   else if (i > 1.0f) i = 1.0f;
00301 
00302   ia = (unsigned)(256 * i);
00303   ir = r * (256 - ia);
00304   ig = g * (256 - ia);
00305   ib = b * (256 - ia);
00306 
00307   // Update the image data to do the blend...
00308   const uchar   *old_ptr;
00309   int           x, y;
00310   int   line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
00311 
00312   if (d() < 3) {
00313     ig = (r * 31 + g * 61 + b * 8) / 100 * (256 - ia);
00314 
00315     for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
00316       for (x = 0; x < w(); x ++) {
00317         *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
00318         if (d() > 1) *new_ptr++ = *old_ptr++;
00319       }
00320   } else {
00321     for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
00322       for (x = 0; x < w(); x ++) {
00323         *new_ptr++ = (*old_ptr++ * ia + ir) >> 8;
00324         *new_ptr++ = (*old_ptr++ * ia + ig) >> 8;
00325         *new_ptr++ = (*old_ptr++ * ia + ib) >> 8;
00326         if (d() > 3) *new_ptr++ = *old_ptr++;
00327       }
00328   }
00329 
00330   // Set the new pointers/values as needed...
00331   if (!alloc_array) {
00332     array       = new_array;
00333     alloc_array = 1;
00334 
00335     ld(0);
00336   }
00337 }
00338 
00339 void Fl_RGB_Image::desaturate() {
00340   // Don't desaturate an empty image...
00341   if (!w() || !h() || !d() || !array) return;
00342 
00343   // Can only desaturate color images...
00344   if (d() < 3) return;
00345 
00346   // Delete any existing pixmap/mask objects...
00347   uncache();
00348 
00349   // Allocate memory for a grayscale image...
00350   uchar         *new_array,
00351                 *new_ptr;
00352   int           new_d;
00353 
00354   new_d     = d() - 2;
00355   new_array = new uchar[h() * w() * new_d];
00356 
00357   // Copy the image data, converting to grayscale...
00358   const uchar   *old_ptr;
00359   int           x, y;
00360   int   line_i = ld() ? ld() - (w()*d()) : 0; // increment from line end to beginning of next line
00361 
00362   for (new_ptr = new_array, old_ptr = array, y = 0; y < h(); y ++, old_ptr += line_i)
00363     for (x = 0; x < w(); x ++, old_ptr += d()) {
00364       *new_ptr++ = (uchar)((31 * old_ptr[0] + 61 * old_ptr[1] + 8 * old_ptr[2]) / 100);
00365       if (d() > 3) *new_ptr++ = old_ptr[3];
00366     }
00367 
00368   // Free the old array as needed, and then set the new pointers/values...
00369   if (alloc_array) delete[] (uchar *)array;
00370 
00371   array       = new_array;
00372   alloc_array = 1;
00373 
00374   ld(0);
00375   d(new_d);
00376 }
00377 
00378 #if !defined(WIN32) && !defined(__APPLE_QUARTZ__)
00379 // Composite an image with alpha on systems that don't have accelerated
00380 // alpha compositing...
00381 static void alpha_blend(Fl_RGB_Image *img, int X, int Y, int W, int H, int cx, int cy) {
00382   uchar *srcptr = (uchar*)img->array + img->d() * (img->w() * cy + cx);
00383   int srcskip = img->d() * (img->w() - W);
00384 
00385   uchar *dst = new uchar[W * H * 3];
00386   uchar *dstptr = dst;
00387 
00388   fl_read_image(dst, X, Y, W, H, 0);
00389 
00390   uchar srcr, srcg, srcb, srca;
00391   uchar dstr, dstg, dstb, dsta;
00392 
00393   if (img->d() == 2) {
00394     // Composite grayscale + alpha over RGB...
00395     // Composite RGBA over RGB...
00396     for (int y = H; y > 0; y--, srcptr+=srcskip)
00397       for (int x = W; x > 0; x--) {
00398         srcg = *srcptr++;
00399         srca = *srcptr++;
00400 
00401         dstr = dstptr[0];
00402         dstg = dstptr[1];
00403         dstb = dstptr[2];
00404         dsta = 255 - srca;
00405 
00406         *dstptr++ = (srcg * srca + dstr * dsta) >> 8;
00407         *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
00408         *dstptr++ = (srcg * srca + dstb * dsta) >> 8;
00409       }
00410   } else {
00411     // Composite RGBA over RGB...
00412     for (int y = H; y > 0; y--, srcptr+=srcskip)
00413       for (int x = W; x > 0; x--) {
00414         srcr = *srcptr++;
00415         srcg = *srcptr++;
00416         srcb = *srcptr++;
00417         srca = *srcptr++;
00418 
00419         dstr = dstptr[0];
00420         dstg = dstptr[1];
00421         dstb = dstptr[2];
00422         dsta = 255 - srca;
00423 
00424         *dstptr++ = (srcr * srca + dstr * dsta) >> 8;
00425         *dstptr++ = (srcg * srca + dstg * dsta) >> 8;
00426         *dstptr++ = (srcb * srca + dstb * dsta) >> 8;
00427       }
00428   }
00429 
00430   fl_draw_image(dst, X, Y, W, H, 3, 0);
00431 
00432   delete[] dst;
00433 }
00434 #endif // !WIN32 && !__APPLE_QUARTZ__
00435 
00436 void Fl_RGB_Image::draw(int XP, int YP, int WP, int HP, int cx, int cy) {
00437   fl_graphics_driver->draw(this, XP, YP, WP, HP, cx, cy);
00438 }
00439 
00440 static int start(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int w, int h, int &cx, int &cy, 
00441                  int &X, int &Y, int &W, int &H)
00442 {
00443   // account for current clip region (faster on Irix):
00444   fl_clip_box(XP,YP,WP,HP,X,Y,W,H);
00445   cx += X-XP; cy += Y-YP;
00446   // clip the box down to the size of image, quit if empty:
00447   if (cx < 0) {W += cx; X -= cx; cx = 0;}
00448   if (cx+W > w) W = w-cx;
00449   if (W <= 0) return 1;
00450   if (cy < 0) {H += cy; Y -= cy; cy = 0;}
00451   if (cy+H > h) H = h-cy;
00452   if (H <= 0) return 1;
00453   return 0;
00454 }
00455 
00456 #ifdef __APPLE__
00457 void Fl_Quartz_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
00458   int X, Y, W, H;
00459   // Don't draw an empty image...
00460   if (!img->d() || !img->array) {
00461     img->draw_empty(XP, YP);
00462     return;
00463   }
00464   if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
00465     return;
00466   }
00467   if (!img->id_) {
00468     CGColorSpaceRef lut = 0;
00469     if (img->d()<=2)
00470       lut = CGColorSpaceCreateDeviceGray();
00471     else
00472       lut = CGColorSpaceCreateDeviceRGB();
00473     CGDataProviderRef src = CGDataProviderCreateWithData( 0L, img->array, img->w()*img->h()*img->d(), 0L);
00474     img->id_ = CGImageCreate( img->w(), img->h(), 8, img->d()*8, img->ld()?img->ld():img->w()*img->d(),
00475                         lut, (img->d()&1)?kCGImageAlphaNone:kCGImageAlphaLast,
00476                         src, 0L, false, kCGRenderingIntentDefault);
00477     CGColorSpaceRelease(lut);
00478     CGDataProviderRelease(src);
00479   }
00480   if (img->id_ && fl_gc) {
00481     CGRect rect = { { X, Y }, { W, H } };
00482     Fl_X::q_begin_image(rect, cx, cy, img->w(), img->h());
00483     CGContextDrawImage(fl_gc, rect, (CGImageRef)img->id_);
00484     Fl_X::q_end_image();
00485   }
00486 }
00487 
00488 #elif defined(WIN32)
00489 void Fl_GDI_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
00490   int X, Y, W, H;
00491   // Don't draw an empty image...
00492   if (!img->d() || !img->array) {
00493     img->draw_empty(XP, YP);
00494     return;
00495   }
00496   if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
00497     return;
00498   }
00499   if (!img->id_) {
00500     img->id_ = fl_create_offscreen(img->w(), img->h());
00501     if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
00502       fl_begin_offscreen((Fl_Offscreen)img->id_);
00503       fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
00504       fl_end_offscreen();
00505     } else {
00506       fl_begin_offscreen((Fl_Offscreen)img->id_);
00507       fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
00508       fl_end_offscreen();
00509       if (img->d() == 2 || img->d() == 4) {
00510         img->mask_ = fl_create_alphamask(img->w(), img->h(), img->d(), img->ld(), img->array);
00511       }
00512     }
00513   }
00514   if (img->mask_) {
00515     HDC new_gc = CreateCompatibleDC(fl_gc);
00516     int save = SaveDC(new_gc);
00517     SelectObject(new_gc, (void*)img->mask_);
00518     BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCAND);
00519     SelectObject(new_gc, (void*)img->id_);
00520     BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCPAINT);
00521     RestoreDC(new_gc,save);
00522     DeleteDC(new_gc);
00523   } else if (img->d()==2 || img->d()==4) {
00524     fl_copy_offscreen_with_alpha(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
00525   } else {
00526     fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)img->id_, cx, cy);
00527   }
00528 }
00529 
00530 #else
00531 void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
00532   int X, Y, W, H;
00533   // Don't draw an empty image...
00534   if (!img->d() || !img->array) {
00535     img->draw_empty(XP, YP);
00536     return;
00537   }
00538   if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
00539     return;
00540   }
00541   if (!img->id_) {
00542     if (img->d() == 1 || img->d() == 3) {
00543       img->id_ = fl_create_offscreen(img->w(), img->h());
00544       fl_begin_offscreen((Fl_Offscreen)img->id_);
00545       fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
00546       fl_end_offscreen();
00547     }
00548   }
00549   if (img->id_) {
00550     if (img->mask_) {
00551       // I can't figure out how to combine a mask with existing region,
00552       // so cut the image down to a clipped rectangle:
00553       int nx, ny; fl_clip_box(X,Y,W,H,nx,ny,W,H);
00554       cx += nx-X; X = nx;
00555       cy += ny-Y; Y = ny;
00556       // make X use the bitmap as a mask:
00557       XSetClipMask(fl_display, fl_gc, img->mask_);
00558       int ox = X-cx; if (ox < 0) ox += img->w();
00559       int oy = Y-cy; if (oy < 0) oy += img->h();
00560       XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy);
00561     }
00562     
00563     fl_copy_offscreen(X, Y, W, H, img->id_, cx, cy);
00564     
00565     if (img->mask_) {
00566       // put the old clip region back
00567       XSetClipOrigin(fl_display, fl_gc, 0, 0);
00568       fl_restore_clip();
00569     }
00570   } else {
00571     // Composite image with alpha manually each time...
00572     alpha_blend(img, X, Y, W, H, cx, cy);
00573   }
00574 }
00575 
00576 #endif
00577 
00578 void Fl_RGB_Image::label(Fl_Widget* widget) {
00579   widget->image(this);
00580 }
00581 
00582 void Fl_RGB_Image::label(Fl_Menu_Item* m) {
00583   Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, measure);
00584   m->label(_FL_IMAGE_LABEL, (const char*)this);
00585 }
00586 
00587 
00588 //
00589 // End of "$Id: Fl_Image.cxx 7903 2010-11-28 21:06:39Z matt $".
00590 //