Panda3D
pnmBrush.cxx
1 // Filename: pnmBrush.cxx
2 // Created by: drose (01Feb07)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnmBrush.h"
16 #include "pnmImage.h"
17 #include "config_pnmimage.h"
18 #include "cmath.h"
19 
20 // A PNMTransparentBrush doesn't draw or fill anything.
21 class EXPCL_PANDA_PNMIMAGE PNMTransparentBrush : public PNMBrush {
22 public:
24  PNMBrush(0.0, 0.0) { }
25 
26  virtual void draw(PNMImage &, int, int, float) {
27  }
28 
29  virtual void fill(PNMImage &, int, int, int, int, int) {
30  }
31 };
32 
33 // A PNMPixelBrush is a family of brushes that draw one pixel at a time.
34 class EXPCL_PANDA_PNMIMAGE PNMPixelBrush : public PNMBrush {
35 protected:
36  PNMPixelBrush(const LColorf &color) :
37  PNMBrush(0.5, 0.5), _color(color) { }
38 
39  LColorf _color;
40 };
41 
42 // Arbitrarily sets the pixel to a particular color, with no antialiasing.
43 class EXPCL_PANDA_PNMIMAGE PNMSetPixelBrush : public PNMPixelBrush {
44 public:
45  PNMSetPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
46 
47  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
48  if (x >= 0 && x < image.get_x_size() &&
49  y >= 0 && y < image.get_y_size() &&
50  pixel_scale >= 0.5) {
51  image.set_xel_a(x, y, _color);
52  }
53  }
54 
55  virtual void fill(PNMImage &image, int xfrom, int xto, int y,
56  int xo, int yo) {
57  if (y >= 0 && y < image.get_y_size()) {
58  xfrom = max(xfrom, 0);
59  xto = min(xto, image.get_x_size() - 1);
60  for (int x = xfrom; x <= xto; ++x) {
61  image.set_xel_a(x, y, _color);
62  }
63  }
64  }
65 };
66 
67 // Blends the pixel in to the existing background.
68 class EXPCL_PANDA_PNMIMAGE PNMBlendPixelBrush : public PNMPixelBrush {
69 public:
70  PNMBlendPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
71 
72  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
73  if (x >= 0 && x < image.get_x_size() &&
74  y >= 0 && y < image.get_y_size()) {
75  image.blend(x, y, _color[0], _color[1], _color[2], _color[3] * pixel_scale);
76  }
77  }
78 
79  virtual void fill(PNMImage &image, int xfrom, int xto, int y,
80  int xo, int yo) {
81  if (y >= 0 && y < image.get_y_size()) {
82  xfrom = max(xfrom, 0);
83  xto = min(xto, image.get_x_size() - 1);
84  for (int x = xfrom; x <= xto; ++x) {
85  image.blend(x, y, _color[0], _color[1], _color[2], _color[3]);
86  }
87  }
88  }
89 };
90 
91 // Darkens the pixel in the existing background.
92 class EXPCL_PANDA_PNMIMAGE PNMDarkenPixelBrush : public PNMPixelBrush {
93 public:
94  PNMDarkenPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
95 
96  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
97  if (x >= 0 && x < image.get_x_size() &&
98  y >= 0 && y < image.get_y_size()) {
99 
100  LColorf p = (_color - 1.0f) * pixel_scale + 1.0f;
101  image.set_xel_a(x, y, p.fmin(image.get_xel_a(x, y)));
102  }
103  }
104 
105  virtual void fill(PNMImage &image, int xfrom, int xto, int y,
106  int xo, int yo) {
107  if (y >= 0 && y < image.get_y_size()) {
108  xfrom = max(xfrom, 0);
109  xto = min(xto, image.get_x_size() - 1);
110  for (int x = xfrom; x <= xto; ++x) {
111  image.set_xel_a(x, y, _color.fmin(image.get_xel_a(x, y)));
112  }
113  }
114  }
115 };
116 
117 // Lightens the pixel in the existing background.
118 class EXPCL_PANDA_PNMIMAGE PNMLightenPixelBrush : public PNMPixelBrush {
119 public:
120  PNMLightenPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
121 
122  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
123  if (x >= 0 && x < image.get_x_size() &&
124  y >= 0 && y < image.get_y_size()) {
125  image.set_xel_a(x, y,
126  image.get_xel_a(x, y).fmax(_color * pixel_scale));
127  }
128  }
129 
130  virtual void fill(PNMImage &image, int xfrom, int xto, int y,
131  int xo, int yo) {
132  if (y >= 0 && y < image.get_y_size()) {
133  xfrom = max(xfrom, 0);
134  xto = max(xto, image.get_x_size() - 1);
135  for (int x = xfrom; x <= xto; ++x) {
136  image.set_xel_a(x, y,
137  image.get_xel_a(x, y).fmax(_color));
138  }
139  }
140  }
141 };
142 
143 // A PNMImageBrush is a family of brushes that draw an image at a time.
144 class EXPCL_PANDA_PNMIMAGE PNMImageBrush : public PNMBrush {
145 protected:
146  PNMImageBrush(const PNMImage &image, float xc, float yc) :
147  PNMBrush(xc, yc),
148  _image(image)
149  {
150  }
151 
152  virtual void fill(PNMImage &image, int xfrom, int xto, int y,
153  int xo, int yo) {
154  if (y >= 0 && y < image.get_y_size()) {
155  xfrom = max(xfrom, 0);
156  xto = min(xto, image.get_x_size() - 1);
157 
158  int x_pat = (xfrom + xo) % _image.get_x_size();
159  int y_pat = (y + yo) % _image.get_y_size();
160 
161  // Copy the first (right half) of the image scanline.
162  int x = xfrom;
163  do_scanline(image, x, y, x_pat, y_pat, xto - x + 1, 1);
164  // Now repeatedly make more copies of the image scanline.
165  x += _image.get_x_size() - x_pat;
166  while (x <= xto) {
167  do_scanline(image, x, y, 0, y_pat, xto - x + 1, 1);
168  x += _image.get_x_size();
169  }
170  }
171  }
172 
173  virtual void do_scanline(PNMImage &image, int xto, int yto,
174  int xfrom, int yfrom, int x_size, int y_size)=0;
175 
176  PNMImage _image;
177 };
178 
179 // Sets the pixels from the rectangular image, with no antialiasing.
180 class EXPCL_PANDA_PNMIMAGE PNMSetImageBrush : public PNMImageBrush {
181 public:
182  PNMSetImageBrush(const PNMImage &image, float xc, float yc) :
183  PNMImageBrush(image, xc, yc) { }
184 
185  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
186  if (pixel_scale >= 0.5) {
187  image.copy_sub_image(_image, x, y);
188  }
189  }
190 
191  virtual void do_scanline(PNMImage &image, int xto, int yto,
192  int xfrom, int yfrom, int x_size, int y_size) {
193  image.copy_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
194  }
195 };
196 
197 // Blends the pixels in using alpha.
198 class EXPCL_PANDA_PNMIMAGE PNMBlendImageBrush : public PNMImageBrush {
199 public:
200  PNMBlendImageBrush(const PNMImage &image, float xc, float yc) :
201  PNMImageBrush(image, xc, yc) { }
202 
203  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
204  image.blend_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
205  }
206 
207  virtual void do_scanline(PNMImage &image, int xto, int yto,
208  int xfrom, int yfrom, int x_size, int y_size) {
209  image.blend_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
210  }
211 };
212 
213 // Darkens the pixels
214 class EXPCL_PANDA_PNMIMAGE PNMDarkenImageBrush : public PNMImageBrush {
215 public:
216  PNMDarkenImageBrush(const PNMImage &image, float xc, float yc) :
217  PNMImageBrush(image, xc, yc) { }
218 
219  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
220  image.darken_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
221  }
222 
223  virtual void do_scanline(PNMImage &image, int xto, int yto,
224  int xfrom, int yfrom, int x_size, int y_size) {
225  image.darken_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
226  }
227 };
228 
229 // Lightens the pixels
230 class EXPCL_PANDA_PNMIMAGE PNMLightenImageBrush : public PNMImageBrush {
231 public:
232  PNMLightenImageBrush(const PNMImage &image, float xc, float yc) :
233  PNMImageBrush(image, xc, yc) { }
234 
235  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
236  image.lighten_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
237  }
238 
239  virtual void do_scanline(PNMImage &image, int xto, int yto,
240  int xfrom, int yfrom, int x_size, int y_size) {
241  image.lighten_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
242  }
243 };
244 
245 ////////////////////////////////////////////////////////////////////
246 // Function: PNMBrush::Destructor
247 // Access: Published, Virtual
248 // Description:
249 ////////////////////////////////////////////////////////////////////
250 PNMBrush::
251 ~PNMBrush() {
252 }
253 
254 ////////////////////////////////////////////////////////////////////
255 // Function: PNMBrush::make_transparent
256 // Access: Published, Static
257 // Description: Returns a new brush that does not paint anything.
258 // Can be used as either a pen or a fill brush to make
259 // borderless or unfilled shapes, respectively.
260 ////////////////////////////////////////////////////////////////////
261 PT(PNMBrush) PNMBrush::
262 make_transparent() {
263  return new PNMTransparentBrush();
264 }
265 
266 ////////////////////////////////////////////////////////////////////
267 // Function: PNMBrush::make_pixel
268 // Access: Published, Static
269 // Description: Returns a new brush that paints a single pixel of the
270 // indicated color on a border, or paints a solid color
271 // in an interior.
272 ////////////////////////////////////////////////////////////////////
273 PT(PNMBrush) PNMBrush::
274 make_pixel(const LColorf &color, PNMBrush::BrushEffect effect) {
275  switch (effect) {
276  case BE_set:
277  return new PNMSetPixelBrush(color);
278 
279  case BE_blend:
280  return new PNMBlendPixelBrush(color);
281 
282  case BE_darken:
283  return new PNMDarkenPixelBrush(color);
284 
285  case BE_lighten:
286  return new PNMLightenPixelBrush(color);
287  }
288 
289  pnmimage_cat.error()
290  << "**Invalid BrushEffect (" << (int)effect << ")**\n";
291  return new PNMSetPixelBrush(color);
292 }
293 
294 ////////////////////////////////////////////////////////////////////
295 // Function: PNMBrush::make_spot
296 // Access: Published, Static
297 // Description: Returns a new brush that paints a spot of the
298 // indicated color and radius. If fuzzy is true, the
299 // spot is fuzzy; otherwise, it is hard-edged.
300 ////////////////////////////////////////////////////////////////////
301 PT(PNMBrush) PNMBrush::
302 make_spot(const LColorf &color, float radius, bool fuzzy,
303  BrushEffect effect) {
304  LColorf bg;
305 
306  switch (effect) {
307  case BE_set:
308  bg.set(0, 0, 0, 0);
309  break;
310 
311  case BE_blend:
312  bg.set(color[0], color[1], color[2], 0.0f);
313  break;
314 
315  case BE_darken:
316  bg.set(1, 1, 1, 1);
317  break;
318 
319  case BE_lighten:
320  bg.set(0, 0, 0, 0);
321  break;
322 
323  default:
324  pnmimage_cat.error()
325  << "**Invalid BrushEffect (" << (int)effect << ")**\n";
326  }
327 
328  int size = (int)cceil(radius * 2.0f);
329  float half_size = (float)size * 0.5f;
330  PNMImage spot(size, size, 4);
331  float r = half_size / radius;
332 
333  if (fuzzy) {
334  spot.render_spot(color, bg, 0.0f, r);
335  } else {
336  spot.render_spot(color, bg, r, r);
337  }
338  return make_image(spot, half_size, half_size, effect);
339 }
340 
341 ////////////////////////////////////////////////////////////////////
342 // Function: PNMBrush::make_image
343 // Access: Published, Static
344 // Description: Returns a new brush that paints with the indicated
345 // image. xc and yc indicate the pixel in the center of
346 // the brush.
347 //
348 // The brush makes a copy of the image; it is safe to
349 // deallocate or modify the image after making this
350 // call.
351 ////////////////////////////////////////////////////////////////////
352 PT(PNMBrush) PNMBrush::
353 make_image(const PNMImage &image, float xc, float yc,
354  PNMBrush::BrushEffect effect) {
355  switch (effect) {
356  case BE_set:
357  return new PNMSetImageBrush(image, xc, yc);
358 
359  case BE_blend:
360  return new PNMBlendImageBrush(image, xc, yc);
361 
362  case BE_darken:
363  return new PNMDarkenImageBrush(image, xc, yc);
364 
365  case BE_lighten:
366  return new PNMLightenImageBrush(image, xc, yc);
367  }
368 
369  pnmimage_cat.error()
370  << "**Invalid BrushEffect (" << (int)effect << ")**\n";
371  return new PNMSetImageBrush(image, xc, yc);
372 }
void copy_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1)
Copies a rectangular area of another image into a rectangular area of this image. ...
Definition: pnmImage.cxx:1119
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
void blend(int x, int y, const LRGBColorf &val, float alpha)
Smoothly blends the indicated pixel value in with whatever was already in the image, based on the given alpha value.
Definition: pnmImage.I:1060
void blend_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), except the alpha channel of the copy is used to blend the copy into th...
Definition: pnmImage.cxx:1178
int get_y_size() const
Returns the number of pixels in the Y direction.
int get_x_size() const
Returns the number of pixels in the X direction.
LColorf get_xel_a(int x, int y) const
Returns the RGBA color at the indicated pixel.
Definition: pnmImage.I:720
void lighten_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), but the resulting color will be the lighter of the source and destinat...
Definition: pnmImage.cxx:1370
void set_xel_a(int x, int y, const LColorf &value)
Changes the RGBA color at the indicated pixel.
Definition: pnmImage.I:789
void darken_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1, float pixel_scale=1.0)
Behaves like copy_sub_image(), but the resulting color will be the darker of the source and destinati...
Definition: pnmImage.cxx:1299
This class is used to control the shape and color of the drawing operations performed by a PNMPainter...
Definition: pnmBrush.h:41
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void render_spot(const LColorf &fg, const LColorf &bg, float min_radius, float max_radius)
Renders a solid-color circle, with a fuzzy edge, into the center of the PNMImage. ...
Definition: pnmImage.cxx:1846