Panda3D
pnmTextGlyph.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 pnmTextGlyph.cxx
10 * @author drose
11 * @date 2002-04-03
12 */
13
14#include "pnmTextGlyph.h"
15#include "indent.h"
16
17using std::max;
18using std::min;
19
20/**
21 *
22 */
23PNMTextGlyph::
24PNMTextGlyph(double advance) :
25 _advance(advance)
26{
27 _left = 0;
28 _top = 0;
29 _int_advance = (int)floor(_advance + 0.5);
30}
31
32/**
33 *
34 */
35PNMTextGlyph::
36~PNMTextGlyph() {
37}
38
39/**
40 * Copies the glyph to the indicated destination image at the indicated
41 * origin. It colors the glyph pixels the indicated foreground color, blends
42 * antialiased pixels with the appropriate amount of the foreground color and
43 * the existing background color, and leaves other pixels alone.
44 */
46place(PNMImage &dest_image, int xp, int yp, const LColor &fg) {
47 if (!_image.is_valid()) {
48 // If we have no image, do nothing.
49 return;
50 }
51
52 LColorf fgf = LCAST(float, fg);
53
54 int left = xp + _left;
55 int top = yp - _top;
56 int right = left + _image.get_x_size();
57 int bottom = top + _image.get_y_size();
58
59 // Clip the glyph to the destination image.
60 int cleft = max(left, 0);
61 int ctop = max(top, 0);
62 int cright = min(right, dest_image.get_x_size());
63 int cbottom = min(bottom, dest_image.get_y_size());
64
65 for (int y = ctop; y < cbottom; y++) {
66 for (int x = cleft; x < cright; x++) {
67 double gval = get_value(x - left, y - top);
68 if (gval == 1.0) {
69 dest_image.set_xel_a(x, y, fgf);
70
71 } else if (gval > 0.0) {
72 LColorf bg = dest_image.get_xel_a(x, y);
73 dest_image.set_xel_a(x, y, fgf * gval + bg * (1.0 - gval));
74 }
75 }
76 }
77}
78
79/**
80 * This flavor of place() also fills in the interior color. This requires
81 * that determine_interior was called earlier.
82 */
84place(PNMImage &dest_image, int xp, int yp, const LColor &fg,
85 const LColor &interior) {
86 if (!_image.is_valid()) {
87 // If we have no image, do nothing.
88 return;
89 }
90
91 LColorf fgf = LCAST(float, fg);
92 LColorf interiorf = LCAST(float, interior);
93
94 int left = xp + _left;
95 int top = yp - _top;
96 int right = left + _image.get_x_size();
97 int bottom = top + _image.get_y_size();
98
99 // Clip the glyph to the destination image.
100 int cleft = max(left, 0);
101 int ctop = max(top, 0);
102 int cright = min(right, dest_image.get_x_size());
103 int cbottom = min(bottom, dest_image.get_y_size());
104
105 for (int y = ctop; y < cbottom; y++) {
106 for (int x = cleft; x < cright; x++) {
107 double gval = get_value(x - left, y - top);
108 if (gval == 1.0) {
109 dest_image.set_xel_a(x, y, fgf);
110
111 } else if (gval > 0.0) {
112 bool is_interior = get_interior_flag(x - left, y - top);
113 LColorf bg;
114 if (is_interior) {
115 bg = interiorf;
116 } else {
117 bg = dest_image.get_xel_a(x, y);
118 }
119
120 dest_image.set_xel_a(x, y, fgf * gval + bg * (1.0 - gval));
121 } else { // gval == 0.0
122 bool is_interior = get_interior_flag(x - left, y - top);
123 if (is_interior) {
124 dest_image.set_xel_a(x, y, interiorf);
125 }
126 }
127 }
128 }
129}
130
131/**
132 * Once the glyph has been generated, but before it has been scaled down by
133 * _scale_factor, walk through the glyph and try to determine which parts
134 * represent the interior portions of a hollow font, and mark them so they may
135 * be properly colored.
136 */
137void PNMTextGlyph::
138determine_interior() {
139 // We will use the red component as a working buffer. First, we fill the
140 // whole thing to maxval.
141 int x_size = _image.get_x_size();
142 int y_size = _image.get_y_size();
143 xelval maxval = _image.get_maxval();
144 for (int yi = 0; yi < y_size; yi++) {
145 for (int xi = 0; xi < x_size; xi++) {
146 _image.set_red_val(xi, yi, maxval);
147 }
148 }
149
150 // Now we recursively analyze the image to determine the number of walls
151 // between each pixel and any edge. All outer edge pixels have a value of
152 // 0; all dark pixels adjacent to those pixels have a value of 1, and light
153 // pixels adjacent to those have a value of 2, and so on.
154 _scan_interior_points.clear();
155 for (int yi = 0; yi < y_size; yi++) {
156 scan_interior(0, yi, 0, false, 0);
157 scan_interior(x_size - 1, yi, 0, false, 0);
158 }
159 for (int xi = 0; xi < x_size; xi++) {
160 scan_interior(xi, 0, 0, false, 0);
161 scan_interior(xi, y_size - 1, 0, false, 0);
162 }
163
164 // Pick up any points that we couldn't visit recursively because of the lame
165 // stack limit on Windows.
166 while (!_scan_interior_points.empty()) {
167 int index = _scan_interior_points.back();
168 _scan_interior_points.pop_back();
169 int y = index / _image.get_x_size();
170 int x = index % _image.get_x_size();
171 xelval new_code = _image.get_red_val(x, y);
172 bool this_dark = (_image.get_blue_val(x, y) > 0);
173
174 scan_interior(x - 1, y, new_code, this_dark, 0);
175 scan_interior(x, y - 1, new_code, this_dark, 0);
176 scan_interior(x + 1, y, new_code, this_dark, 0);
177 scan_interior(x, y + 1, new_code, this_dark, 0);
178 }
179 _scan_interior_points.clear();
180
181 // Finally, go back and set any pixel whose red value is two more than a
182 // multiple of 4 to dark. This indicates the interior part of a hollow
183 // font.
184 for (int yi = 0; yi < y_size; yi++) {
185 for (int xi = 0; xi < x_size; xi++) {
186 xelval code = _image.get_red_val(xi, yi);
187 if (((code + 2) & 0x3) == 0) {
188 _image.set_red_val(xi, yi, maxval);
189 } else {
190 _image.set_red_val(xi, yi, 0);
191 }
192 }
193 }
194}
195
196/**
197 * Recursively scans the image for interior pixels. On completion, the
198 * image's red channel will be filled with 0, 1, 2, etc., representing the
199 * number of edges between each pixel and the border.
200 */
201void PNMTextGlyph::
202scan_interior(int x, int y, xelval new_code, bool neighbor_dark,
203 int recurse_level) {
204 if (x < 0 || y < 0 || x >= _image.get_x_size() || y >= _image.get_y_size()) {
205 return;
206 }
207 bool this_dark = (_image.get_blue_val(x, y) > 0);
208 if (this_dark != neighbor_dark) {
209 // If we just crossed an edge, we have to increment the code.
210 if (new_code < _image.get_maxval()) {
211 new_code++;
212 }
213 nassertv(new_code > 0);
214 }
215
216 if (new_code < _image.get_red_val(x, y)) {
217 _image.set_red_val(x, y, new_code);
218 recurse_level++;
219 if (recurse_level > 1024) {
220 // To cobble around a lame Windows limitation on the length of the
221 // stack, we must prevent the recursion from going too deep. But we
222 // still need to remember this pixel so we can come back to it later.
223 int index = y * _image.get_x_size() + x;
224 _scan_interior_points.push_back(index);
225
226 } else {
227 scan_interior(x - 1, y, new_code, this_dark, recurse_level);
228 scan_interior(x, y - 1, new_code, this_dark, recurse_level);
229 scan_interior(x + 1, y, new_code, this_dark, recurse_level);
230 scan_interior(x, y + 1, new_code, this_dark, recurse_level);
231 }
232 }
233}
234
235/**
236 * After the image has been rendered large by FreeType, scales it small again
237 * for placing.
238 */
239void PNMTextGlyph::
240rescale(double scale_factor) {
241 if (scale_factor == 1.0) {
242 return;
243 }
244 nassertv(scale_factor != 0.0);
245 _advance /= scale_factor;
246 _int_advance = (int)floor(_advance + 0.5);
247
248 if (_image.is_valid()) {
249 int orig_x_size = _image.get_x_size();
250 int orig_y_size = _image.get_y_size();
251 int orig_left = _left;
252 int orig_top = _top;
253
254 // Pad the image by a few pixels all around to allow for antialiasing at
255 // the edges.
256 int extra_pad = (int)ceil(scale_factor);
257 orig_x_size += 2*extra_pad;
258 orig_y_size += 2*extra_pad;
259 orig_left -= extra_pad;
260 orig_top += extra_pad;
261
262 // Now compute the reduced size.
263 int new_x_size = (int)ceil(orig_x_size / scale_factor);
264 int new_y_size = (int)ceil(orig_y_size / scale_factor);
265 int new_left = (int)floor(orig_left / scale_factor);
266 int new_top = (int)ceil(orig_top / scale_factor);
267
268 // And scale those back up so we can determine the amount of additional
269 // padding we need to make the pixels remain in the right place after the
270 // integer reduction.
271 int old_x_size = (int)(new_x_size * scale_factor + 0.5);
272 int old_y_size = (int)(new_y_size * scale_factor + 0.5);
273 int old_left = (int)(new_left * scale_factor + 0.5);
274 int old_top = (int)(new_top * scale_factor + 0.5);
275
276 int pad_left = orig_left - old_left;
277 int pad_top = old_top - orig_top;
278
279 // These shouldn't go negative.
280 nassertv(extra_pad + pad_left >= 0 && extra_pad + pad_top >= 0);
281
282 PNMImage enlarged(old_x_size, old_y_size, _image.get_num_channels());
283 enlarged.copy_sub_image(_image, pad_left + extra_pad, pad_top + extra_pad);
284
285 _image.clear(new_x_size, new_y_size, _image.get_num_channels());
286 _image.quick_filter_from(enlarged);
287
288 _left = new_left;
289 _top = new_top;
290 }
291}
get_maxval
Returns the maximum channel value allowable for any pixel in this image; for instance,...
int get_x_size() const
Returns the number of pixels in the X direction.
get_num_channels
Returns the number of channels in the image.
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 clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition: pnmImage.cxx:48
void set_xel_a(int x, int y, const LColorf &value)
Changes the RGBA color at the indicated pixel.
Definition: pnmImage.I:675
void quick_filter_from(const PNMImage &copy, int xborder=0, int yborder=0)
Resizes from the given image, with a fixed radius of 0.5.
LColorf get_xel_a(int x, int y) const
Returns the RGBA color at the indicated pixel.
Definition: pnmImage.I:608
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width.
Definition: pnmImage.I:342
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:472
xelval get_red_val(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:452
void set_red_val(int x, int y, xelval r)
Sets the red component color only at the indicated pixel.
Definition: pnmImage.I:506
bool get_interior_flag(int x, int y) const
Returns true if the indicated pixel represents a pixel in the interior of a hollow font,...
Definition: pnmTextGlyph.I:89
void place(PNMImage &dest_image, int xp, int yp, const LColor &fg)
Copies the glyph to the indicated destination image at the indicated origin.
double get_value(int x, int y) const
Returns the value of the indicated pixel of the glyph.
Definition: pnmTextGlyph.I:77
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.