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