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
19using std::max;
20using std::min;
21
22// A PNMTransparentBrush doesn't draw or fill anything.
23class EXPCL_PANDA_PNMIMAGE PNMTransparentBrush : public PNMBrush {
24public:
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.
36class EXPCL_PANDA_PNMIMAGE PNMPixelBrush : public PNMBrush {
37protected:
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.
45class EXPCL_PANDA_PNMIMAGE PNMSetPixelBrush : public PNMPixelBrush {
46public:
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.
70class EXPCL_PANDA_PNMIMAGE PNMBlendPixelBrush : public PNMPixelBrush {
71public:
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.
94class EXPCL_PANDA_PNMIMAGE PNMDarkenPixelBrush : public PNMPixelBrush {
95public:
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.
120class EXPCL_PANDA_PNMIMAGE PNMLightenPixelBrush : public PNMPixelBrush {
121public:
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.
146class EXPCL_PANDA_PNMIMAGE PNMImageBrush : public PNMBrush {
147protected:
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.
182class EXPCL_PANDA_PNMIMAGE PNMSetImageBrush : public PNMImageBrush {
183public:
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.
200class EXPCL_PANDA_PNMIMAGE PNMBlendImageBrush : public PNMImageBrush {
201public:
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
216class EXPCL_PANDA_PNMIMAGE PNMDarkenImageBrush : public PNMImageBrush {
217public:
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
232class EXPCL_PANDA_PNMIMAGE PNMLightenImageBrush : public PNMImageBrush {
233public:
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 */
250PNMBrush::
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 */
258PT(PNMBrush) PNMBrush::
259make_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 */
267PT(PNMBrush) PNMBrush::
268make_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 */
292PT(PNMBrush) PNMBrush::
293make_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 */
339PT(PNMBrush) PNMBrush::
340make_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}
This class is used to control the shape and color of the drawing operations performed by a PNMPainter...
Definition: pnmBrush.h:36
int get_x_size() const
Returns the number of pixels in the X direction.
int get_y_size() const
Returns the number of pixels in the Y direction.
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
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
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
void set_xel_a(int x, int y, const LColorf &value)
Changes the RGBA color at the indicated pixel.
Definition: pnmImage.I:675
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
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:900
LColorf get_xel_a(int x, int y) const
Returns the RGBA color at the indicated pixel.
Definition: pnmImage.I:608
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(PNMBrush) PNMBrush
Returns a new brush that does not paint anything.
Definition: pnmBrush.cxx:258
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.