Panda3D
|
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 }