Panda3D
Loading...
Searching...
No Matches
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,...
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.