Panda3D

pnmBrush.cxx

00001 // Filename: pnmBrush.cxx
00002 // Created by:  drose (01Feb07)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pnmBrush.h"
00016 #include "config_pnmimage.h"
00017 #include "cmath.h"
00018 
00019 // A PNMTransparentBrush doesn't draw or fill anything.
00020 class EXPCL_PANDA_PNMIMAGE PNMTransparentBrush : public PNMBrush {
00021 public:
00022   PNMTransparentBrush() : 
00023     PNMBrush(0.0, 0.0) { }
00024 
00025   virtual void draw(PNMImage &, int, int, double) {
00026   }
00027 
00028   virtual void fill(PNMImage &, int, int, int, int, int) {
00029   }
00030 };
00031 
00032 // A PNMPixelBrush is a family of brushes that draw one pixel at a time.
00033 class EXPCL_PANDA_PNMIMAGE PNMPixelBrush : public PNMBrush {
00034 protected:
00035   PNMPixelBrush(const LColord &color) : 
00036     PNMBrush(0.5, 0.5), _rgb(color[0], color[1], color[2]), _a(color[3]) { }
00037 
00038   LRGBColord _rgb;
00039   double _a;
00040 };
00041 
00042 // Arbitrarily sets the pixel to a particular color, with no antialiasing.
00043 class EXPCL_PANDA_PNMIMAGE PNMSetPixelBrush : public PNMPixelBrush {
00044 public:
00045   PNMSetPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
00046 
00047   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00048     if (x >= 0 && x < image.get_x_size() && 
00049         y >= 0 && y < image.get_y_size() &&
00050         pixel_scale >= 0.5) {
00051       image.set_xel(x, y, _rgb);
00052       if (image.has_alpha()) {
00053         image.set_alpha(x, y, _a);
00054       }
00055     }
00056   }
00057 
00058   virtual void fill(PNMImage &image, int xfrom, int xto, int y,
00059                     int xo, int yo) {
00060     if (y >= 0 && y < image.get_y_size()) {
00061       xfrom = max(xfrom, 0);
00062       xto = min(xto, image.get_x_size() - 1);
00063       for (int x = xfrom; x <= xto; ++x) {
00064         image.set_xel(x, y, _rgb);
00065       }
00066       if (image.has_alpha()) {
00067         for (int x = xfrom; x <= xto; ++x) {
00068           image.set_alpha(x, y, _a);
00069         }
00070       }
00071     }
00072   }
00073 };
00074 
00075 // Blends the pixel in to the existing background.
00076 class EXPCL_PANDA_PNMIMAGE PNMBlendPixelBrush : public PNMPixelBrush {
00077 public:
00078   PNMBlendPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
00079 
00080   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00081     if (x >= 0 && x < image.get_x_size() && 
00082         y >= 0 && y < image.get_y_size()) {
00083       image.blend(x, y, _rgb, _a * pixel_scale);
00084     }
00085   }
00086 
00087   virtual void fill(PNMImage &image, int xfrom, int xto, int y,
00088                     int xo, int yo) {
00089     if (y >= 0 && y < image.get_y_size()) {
00090       xfrom = max(xfrom, 0);
00091       xto = min(xto, image.get_x_size() - 1);
00092       for (int x = xfrom; x <= xto; ++x) {
00093         image.blend(x, y, _rgb, _a);
00094       }
00095     }
00096   }
00097 };
00098 
00099 // Darkens the pixel in the existing background.
00100 class EXPCL_PANDA_PNMIMAGE PNMDarkenPixelBrush : public PNMPixelBrush {
00101 public:
00102   PNMDarkenPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
00103 
00104   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00105     if (x >= 0 && x < image.get_x_size() && 
00106         y >= 0 && y < image.get_y_size()) {
00107       LRGBColord rgb = image.get_xel(x, y);
00108       LRGBColord p;
00109       p.set(min(1.0 - (1.0 - _rgb[0]) * pixel_scale, rgb[0]), 
00110             min(1.0 - (1.0 - _rgb[1]) * pixel_scale, rgb[1]), 
00111             min(1.0 - (1.0 - _rgb[2]) * pixel_scale, rgb[2]));
00112       image.set_xel(x, y, p);
00113 
00114       if (image.has_alpha()) {
00115         double a = image.get_alpha(x, y);
00116         image.set_alpha(x, y, min(1.0 - (1.0 - _a) * pixel_scale, a));
00117       }
00118     }
00119   }
00120 
00121   virtual void fill(PNMImage &image, int xfrom, int xto, int y,
00122                     int xo, int yo) {
00123     if (y >= 0 && y < image.get_y_size()) {
00124       xfrom = max(xfrom, 0);
00125       xto = min(xto, image.get_x_size() - 1);
00126       for (int x = xfrom; x <= xto; ++x) {
00127         LRGBColord rgb = image.get_xel(x, y);
00128         LRGBColord p;
00129         p.set(min(_rgb[0], rgb[0]), 
00130               min(_rgb[1], rgb[1]), 
00131               min(_rgb[2], rgb[2]));
00132         image.set_xel(x, y, p);
00133       }
00134       if (image.has_alpha()) {
00135         for (int x = xfrom; x <= xto; ++x) {
00136           double a = image.get_alpha(x, y);
00137           image.set_alpha(x, y, min(_a, a));
00138         }
00139       }
00140     }
00141   }
00142 };
00143 
00144 // Lightens the pixel in the existing background.
00145 class EXPCL_PANDA_PNMIMAGE PNMLightenPixelBrush : public PNMPixelBrush {
00146 public:
00147   PNMLightenPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
00148 
00149   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00150     if (x >= 0 && x < image.get_x_size() && 
00151         y >= 0 && y < image.get_y_size()) {
00152       LRGBColord rgb = image.get_xel(x, y);
00153       LRGBColord p;
00154       p.set(max(_rgb[0] * pixel_scale, rgb[0]), 
00155             max(_rgb[1] * pixel_scale, rgb[1]), 
00156             max(_rgb[2] * pixel_scale, rgb[2]));
00157       image.set_xel(x, y, p);
00158 
00159       if (image.has_alpha()) {
00160         double a = image.get_alpha(x, y);
00161         image.set_alpha(x, y, max(_a * pixel_scale, a));
00162       }
00163     }
00164   }
00165 
00166   virtual void fill(PNMImage &image, int xfrom, int xto, int y,
00167                     int xo, int yo) {
00168     if (y >= 0 && y < image.get_y_size()) {
00169       xfrom = max(xfrom, 0);
00170       xto = max(xto, image.get_x_size() - 1);
00171       for (int x = xfrom; x <= xto; ++x) {
00172         LRGBColord rgb = image.get_xel(x, y);
00173         LRGBColord p;
00174         p.set(max(_rgb[0], rgb[0]), 
00175               max(_rgb[1], rgb[1]), 
00176               max(_rgb[2], rgb[2]));
00177         image.set_xel(x, y, p);
00178       }
00179       if (image.has_alpha()) {
00180         for (int x = xfrom; x <= xto; ++x) {
00181           double a = image.get_alpha(x, y);
00182           image.set_alpha(x, y, max(_a, a));
00183         }
00184       }
00185     }
00186   }
00187 };
00188 
00189 // A PNMImageBrush is a family of brushes that draw an image at a time.
00190 class EXPCL_PANDA_PNMIMAGE PNMImageBrush : public PNMBrush {
00191 protected:
00192   PNMImageBrush(const PNMImage &image, double xc, double yc) : 
00193     PNMBrush(xc, yc),
00194     _image(image) 
00195   {
00196   }
00197 
00198   virtual void fill(PNMImage &image, int xfrom, int xto, int y,
00199                     int xo, int yo) {
00200     if (y >= 0 && y < image.get_y_size()) {
00201       xfrom = max(xfrom, 0);
00202       xto = min(xto, image.get_x_size() - 1);
00203 
00204       int x_pat = (xfrom + xo) % _image.get_x_size();
00205       int y_pat = (y + yo) % _image.get_y_size();
00206 
00207       // Copy the first (right half) of the image scanline.
00208       int x = xfrom;
00209       do_scanline(image, x, y, x_pat, y_pat, xto - x + 1, 1);
00210       // Now repeatedly make more copies of the image scanline.
00211       x += _image.get_x_size() - x_pat;
00212       while (x <= xto) {
00213         do_scanline(image, x, y, 0, y_pat, xto - x + 1, 1);
00214         x += _image.get_x_size();
00215       }
00216     }
00217   }
00218 
00219   virtual void do_scanline(PNMImage &image, int xto, int yto,
00220                            int xfrom, int yfrom, int x_size, int y_size)=0;
00221 
00222   PNMImage _image;
00223 };
00224 
00225 // Sets the pixels from the rectangular image, with no antialiasing.
00226 class EXPCL_PANDA_PNMIMAGE PNMSetImageBrush : public PNMImageBrush {
00227 public:
00228   PNMSetImageBrush(const PNMImage &image, double xc, double yc) : 
00229     PNMImageBrush(image, xc, yc) { }
00230 
00231   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00232     if (pixel_scale >= 0.5) {
00233       image.copy_sub_image(_image, x, y);
00234     }
00235   }
00236 
00237   virtual void do_scanline(PNMImage &image, int xto, int yto, 
00238                            int xfrom, int yfrom, int x_size, int y_size) {
00239     image.copy_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
00240   }
00241 };
00242 
00243 // Blends the pixels in using alpha.
00244 class EXPCL_PANDA_PNMIMAGE PNMBlendImageBrush : public PNMImageBrush {
00245 public:
00246   PNMBlendImageBrush(const PNMImage &image, double xc, double yc) : 
00247     PNMImageBrush(image, xc, yc) { }
00248 
00249   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00250     image.blend_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
00251   }
00252 
00253   virtual void do_scanline(PNMImage &image, int xto, int yto, 
00254                            int xfrom, int yfrom, int x_size, int y_size) {
00255     image.blend_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
00256   }
00257 };
00258 
00259 // Darkens the pixels
00260 class EXPCL_PANDA_PNMIMAGE PNMDarkenImageBrush : public PNMImageBrush {
00261 public:
00262   PNMDarkenImageBrush(const PNMImage &image, double xc, double yc) : 
00263     PNMImageBrush(image, xc, yc) { }
00264 
00265   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00266     image.darken_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
00267   }
00268 
00269   virtual void do_scanline(PNMImage &image, int xto, int yto, 
00270                            int xfrom, int yfrom, int x_size, int y_size) {
00271     image.darken_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
00272   }
00273 };
00274 
00275 // Lightens the pixels
00276 class EXPCL_PANDA_PNMIMAGE PNMLightenImageBrush : public PNMImageBrush {
00277 public:
00278   PNMLightenImageBrush(const PNMImage &image, double xc, double yc) : 
00279     PNMImageBrush(image, xc, yc) { }
00280 
00281   virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
00282     image.lighten_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
00283   }
00284 
00285   virtual void do_scanline(PNMImage &image, int xto, int yto, 
00286                            int xfrom, int yfrom, int x_size, int y_size) {
00287     image.lighten_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
00288   }
00289 };
00290 
00291 ////////////////////////////////////////////////////////////////////
00292 //     Function: PNMBrush::Destructor
00293 //       Access: Published, Virtual
00294 //  Description: 
00295 ////////////////////////////////////////////////////////////////////
00296 PNMBrush::
00297 ~PNMBrush() {
00298 }
00299 
00300 ////////////////////////////////////////////////////////////////////
00301 //     Function: PNMBrush::make_transparent
00302 //       Access: Published, Static
00303 //  Description: Returns a new brush that does not paint anything.
00304 //               Can be used as either a pen or a fill brush to make
00305 //               borderless or unfilled shapes, respectively.
00306 ////////////////////////////////////////////////////////////////////
00307 PT(PNMBrush) PNMBrush::
00308 make_transparent() {
00309   return new PNMTransparentBrush();
00310 }
00311   
00312 ////////////////////////////////////////////////////////////////////
00313 //     Function: PNMBrush::make_pixel
00314 //       Access: Published, Static
00315 //  Description: Returns a new brush that paints a single pixel of the
00316 //               indicated color on a border, or paints a solid color
00317 //               in an interior.
00318 ////////////////////////////////////////////////////////////////////
00319 PT(PNMBrush) PNMBrush::
00320 make_pixel(const LColord &color, PNMBrush::BrushEffect effect) {
00321   switch (effect) {
00322   case BE_set:
00323     return new PNMSetPixelBrush(color);
00324 
00325   case BE_blend:
00326     return new PNMBlendPixelBrush(color);
00327 
00328   case BE_darken:
00329     return new PNMDarkenPixelBrush(color);
00330 
00331   case BE_lighten:
00332     return new PNMLightenPixelBrush(color);
00333   }
00334 
00335   pnmimage_cat.error()
00336     << "**Invalid BrushEffect (" << (int)effect << ")**\n";
00337   return new PNMSetPixelBrush(color);
00338 }
00339 
00340 ////////////////////////////////////////////////////////////////////
00341 //     Function: PNMBrush::make_spot
00342 //       Access: Published, Static
00343 //  Description: Returns a new brush that paints a spot of the
00344 //               indicated color and radius.  If fuzzy is true, the
00345 //               spot is fuzzy; otherwise, it is hard-edged.
00346 ////////////////////////////////////////////////////////////////////
00347 PT(PNMBrush) PNMBrush::
00348 make_spot(const LColord &color, double radius, bool fuzzy,
00349           BrushEffect effect) {
00350   LColord bg;
00351 
00352   switch (effect) {
00353   case BE_set:
00354     bg.set(0, 0, 0, 0);
00355     break;
00356 
00357   case BE_blend:
00358     bg.set(color[0], color[1], color[2], 0.0);
00359     break;
00360 
00361   case BE_darken:
00362     bg.set(1, 1, 1, 1);
00363     break;
00364 
00365   case BE_lighten:
00366     bg.set(0, 0, 0, 0);
00367     break;
00368 
00369   default:
00370     pnmimage_cat.error()
00371       << "**Invalid BrushEffect (" << (int)effect << ")**\n";
00372   }
00373 
00374   int size = (int)cceil(radius * 2.0);
00375   double half_size = (double)size * 0.5;
00376   PNMImage spot(size, size, 4);
00377   double r = half_size / radius;
00378 
00379   if (fuzzy) {
00380     spot.render_spot(color, bg, 0.0, r);
00381   } else {
00382     spot.render_spot(color, bg, r, r);
00383   }
00384   return make_image(spot, half_size, half_size, effect);
00385 }
00386   
00387 ////////////////////////////////////////////////////////////////////
00388 //     Function: PNMBrush::make_image
00389 //       Access: Published, Static
00390 //  Description: Returns a new brush that paints with the indicated
00391 //               image.  xc and yc indicate the pixel in the center of
00392 //               the brush.
00393 //
00394 //               The brush makes a copy of the image; it is safe to
00395 //               deallocate or modify the image after making this
00396 //               call.
00397 ////////////////////////////////////////////////////////////////////
00398 PT(PNMBrush) PNMBrush::
00399 make_image(const PNMImage &image, double xc, double yc,
00400            PNMBrush::BrushEffect effect) {
00401   switch (effect) {
00402   case BE_set:
00403     return new PNMSetImageBrush(image, xc, yc);
00404 
00405   case BE_blend:
00406     return new PNMBlendImageBrush(image, xc, yc);
00407 
00408   case BE_darken:
00409     return new PNMDarkenImageBrush(image, xc, yc);
00410 
00411   case BE_lighten:
00412     return new PNMLightenImageBrush(image, xc, yc);
00413   }
00414 
00415   pnmimage_cat.error()
00416     << "**Invalid BrushEffect (" << (int)effect << ")**\n";
00417   return new PNMSetImageBrush(image, xc, yc);
00418 }
 All Classes Functions Variables Enumerations