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