Panda3D
|
00001 // Filename: pnmTextGlyph.cxx 00002 // Created by: drose (03Apr02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pnmTextGlyph.h" 00016 #include "indent.h" 00017 00018 //////////////////////////////////////////////////////////////////// 00019 // Function: PNMTextGlyph::Constructor 00020 // Access: Public 00021 // Description: 00022 //////////////////////////////////////////////////////////////////// 00023 PNMTextGlyph:: 00024 PNMTextGlyph(double advance) : 00025 _advance(advance) 00026 { 00027 _left = 0; 00028 _top = 0; 00029 _int_advance = (int)floor(_advance + 0.5); 00030 } 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: PNMTextGlyph::Destructor 00034 // Access: Public 00035 // Description: 00036 //////////////////////////////////////////////////////////////////// 00037 PNMTextGlyph:: 00038 ~PNMTextGlyph() { 00039 } 00040 00041 //////////////////////////////////////////////////////////////////// 00042 // Function: PNMTextGlyph::place 00043 // Access: Public 00044 // Description: Copies the glyph to the indicated destination image 00045 // at the indicated origin. It colors the glyph pixels 00046 // the indicated foreground color, blends antialiased 00047 // pixels with the appropriate amount of the foreground 00048 // color and the existing background color, and leaves 00049 // other pixels alone. 00050 //////////////////////////////////////////////////////////////////// 00051 void PNMTextGlyph:: 00052 place(PNMImage &dest_image, int xp, int yp, const LColor &fg) { 00053 if (!_image.is_valid()) { 00054 // If we have no image, do nothing. 00055 return; 00056 } 00057 LRGBColord fg_rgb(fg[0], fg[1], fg[2]); 00058 double fg_alpha = fg[3]; 00059 00060 int left = xp + _left; 00061 int top = yp - _top; 00062 int right = left + _image.get_x_size(); 00063 int bottom = top + _image.get_y_size(); 00064 00065 // Clip the glyph to the destination image. 00066 int cleft = max(left, 0); 00067 int ctop = max(top, 0); 00068 int cright = min(right, dest_image.get_x_size()); 00069 int cbottom = min(bottom, dest_image.get_y_size()); 00070 00071 for (int y = ctop; y < cbottom; y++) { 00072 for (int x = cleft; x < cright; x++) { 00073 double gval = get_value(x - left, y - top); 00074 if (gval == 1.0) { 00075 dest_image.set_xel(x, y, fg_rgb); 00076 if (dest_image.has_alpha()) { 00077 dest_image.set_alpha(x, y, fg_alpha); 00078 } 00079 00080 } else if (gval > 0.0) { 00081 LRGBColord bg_rgb = dest_image.get_xel(x, y); 00082 dest_image.set_xel(x, y, fg_rgb * gval + bg_rgb * (1.0 - gval)); 00083 if (dest_image.has_alpha()) { 00084 double bg_alpha = dest_image.get_alpha(x, y); 00085 dest_image.set_alpha(x, y, fg_alpha * gval + bg_alpha * (1.0 - gval)); 00086 } 00087 } 00088 } 00089 } 00090 } 00091 00092 //////////////////////////////////////////////////////////////////// 00093 // Function: PNMTextGlyph::place 00094 // Access: Public 00095 // Description: This flavor of place() also fills in the interior 00096 // color. This requires that determine_interior was 00097 // called earlier. 00098 //////////////////////////////////////////////////////////////////// 00099 void PNMTextGlyph:: 00100 place(PNMImage &dest_image, int xp, int yp, const LColor &fg, 00101 const LColor &interior) { 00102 if (!_image.is_valid()) { 00103 // If we have no image, do nothing. 00104 return; 00105 } 00106 LRGBColord fg_rgb(fg[0], fg[1], fg[2]); 00107 double fg_alpha = fg[3]; 00108 LRGBColord interior_rgb(interior[0], interior[1], interior[2]); 00109 double interior_alpha = interior[3]; 00110 00111 int left = xp + _left; 00112 int top = yp - _top; 00113 int right = left + _image.get_x_size(); 00114 int bottom = top + _image.get_y_size(); 00115 00116 // Clip the glyph to the destination image. 00117 int cleft = max(left, 0); 00118 int ctop = max(top, 0); 00119 int cright = min(right, dest_image.get_x_size()); 00120 int cbottom = min(bottom, dest_image.get_y_size()); 00121 00122 for (int y = ctop; y < cbottom; y++) { 00123 for (int x = cleft; x < cright; x++) { 00124 double gval = get_value(x - left, y - top); 00125 if (gval == 1.0) { 00126 dest_image.set_xel(x, y, fg_rgb); 00127 if (dest_image.has_alpha()) { 00128 dest_image.set_alpha(x, y, fg_alpha); 00129 } 00130 00131 } else if (gval > 0.0) { 00132 bool is_interior = get_interior_flag(x - left, y - top); 00133 LRGBColord bg_rgb; 00134 if (is_interior) { 00135 bg_rgb = interior_rgb; 00136 } else { 00137 bg_rgb = dest_image.get_xel(x, y); 00138 } 00139 00140 dest_image.set_xel(x, y, fg_rgb * gval + bg_rgb * (1.0 - gval)); 00141 if (dest_image.has_alpha()) { 00142 double bg_alpha; 00143 00144 if (is_interior) { 00145 bg_alpha = interior_alpha; 00146 } else { 00147 bg_alpha = dest_image.get_alpha(x, y); 00148 } 00149 dest_image.set_alpha(x, y, fg_alpha * gval + bg_alpha * (1.0 - gval)); 00150 } 00151 } else { // gval == 0.0 00152 bool is_interior = get_interior_flag(x - left, y - top); 00153 if (is_interior) { 00154 dest_image.set_xel(x, y, interior_rgb); 00155 if (dest_image.has_alpha()) { 00156 dest_image.set_alpha(x, y, interior_alpha); 00157 } 00158 } 00159 } 00160 } 00161 } 00162 } 00163 00164 //////////////////////////////////////////////////////////////////// 00165 // Function: PNMTextGlyph::determine_interior 00166 // Access: Private 00167 // Description: Once the glyph has been generated, but before it has 00168 // been scaled down by _scale_factor, walk through the 00169 // glyph and try to determine which parts represent the 00170 // interior portions of a hollow font, and mark them so 00171 // they may be properly colored. 00172 //////////////////////////////////////////////////////////////////// 00173 void PNMTextGlyph:: 00174 determine_interior() { 00175 // We will use the red component as a working buffer. First, we 00176 // fill the whole thing to maxval. 00177 int x_size = _image.get_x_size(); 00178 int y_size = _image.get_y_size(); 00179 xelval maxval = _image.get_maxval(); 00180 for (int yi = 0; yi < y_size; yi++) { 00181 for (int xi = 0; xi < x_size; xi++) { 00182 _image.set_red_val(xi, yi, maxval); 00183 } 00184 } 00185 00186 // Now we recursively analyze the image to determine the number of 00187 // walls between each pixel and any edge. All outer edge pixels 00188 // have a value of 0; all dark pixels adjacent to those pixels have 00189 // a value of 1, and light pixels adjacent to those have a value of 00190 // 2, and so on. 00191 _scan_interior_points.clear(); 00192 for (int yi = 0; yi < y_size; yi++) { 00193 scan_interior(0, yi, 0, false, 0); 00194 scan_interior(x_size - 1, yi, 0, false, 0); 00195 } 00196 for (int xi = 0; xi < x_size; xi++) { 00197 scan_interior(xi, 0, 0, false, 0); 00198 scan_interior(xi, y_size - 1, 0, false, 0); 00199 } 00200 00201 // Pick up any points that we couldn't visit recursively because of 00202 // the lame stack limit on Windows. 00203 while (!_scan_interior_points.empty()) { 00204 int index = _scan_interior_points.back(); 00205 _scan_interior_points.pop_back(); 00206 int y = index / _image.get_x_size(); 00207 int x = index % _image.get_x_size(); 00208 xelval new_code = _image.get_red_val(x, y); 00209 bool this_dark = (_image.get_blue_val(x, y) > 0); 00210 00211 scan_interior(x - 1, y, new_code, this_dark, 0); 00212 scan_interior(x, y - 1, new_code, this_dark, 0); 00213 scan_interior(x + 1, y, new_code, this_dark, 0); 00214 scan_interior(x, y + 1, new_code, this_dark, 0); 00215 } 00216 _scan_interior_points.clear(); 00217 00218 // Finally, go back and set any pixel whose red value is two more 00219 // than a multiple of 4 to dark. This indicates the interior part 00220 // of a hollow font. 00221 for (int yi = 0; yi < y_size; yi++) { 00222 for (int xi = 0; xi < x_size; xi++) { 00223 xelval code = _image.get_red_val(xi, yi); 00224 if (((code + 2) & 0x3) == 0) { 00225 _image.set_red_val(xi, yi, maxval); 00226 } else { 00227 _image.set_red_val(xi, yi, 0); 00228 } 00229 } 00230 } 00231 } 00232 00233 //////////////////////////////////////////////////////////////////// 00234 // Function: PNMTextGlyph::scan_interior 00235 // Access: Private 00236 // Description: Recursively scans the image for interior pixels. On 00237 // completion, the image's red channel will be filled 00238 // with 0, 1, 2, etc., representing the number of edges 00239 // between each pixel and the border. 00240 //////////////////////////////////////////////////////////////////// 00241 void PNMTextGlyph:: 00242 scan_interior(int x, int y, xelval new_code, bool neighbor_dark, 00243 int recurse_level) { 00244 if (x < 0 || y < 0 || x >= _image.get_x_size() || y >= _image.get_y_size()) { 00245 return; 00246 } 00247 bool this_dark = (_image.get_blue_val(x, y) > 0); 00248 if (this_dark != neighbor_dark) { 00249 // If we just crossed an edge, we have to increment the code. 00250 if (new_code < _image.get_maxval()) { 00251 new_code++; 00252 } 00253 nassertv(new_code > 0); 00254 } 00255 00256 if (new_code < _image.get_red_val(x, y)) { 00257 _image.set_red_val(x, y, new_code); 00258 recurse_level++; 00259 if (recurse_level > 1024) { 00260 // To cobble around a lame Windows limitation on the length of 00261 // the stack, we must prevent the recursion from going too deep. 00262 // But we still need to remember this pixel so we can come back 00263 // to it later. 00264 int index = y * _image.get_x_size() + x; 00265 _scan_interior_points.push_back(index); 00266 00267 } else { 00268 scan_interior(x - 1, y, new_code, this_dark, recurse_level); 00269 scan_interior(x, y - 1, new_code, this_dark, recurse_level); 00270 scan_interior(x + 1, y, new_code, this_dark, recurse_level); 00271 scan_interior(x, y + 1, new_code, this_dark, recurse_level); 00272 } 00273 } 00274 } 00275 00276 //////////////////////////////////////////////////////////////////// 00277 // Function: PNMTextGlyph::rescale 00278 // Access: Private 00279 // Description: After the image has been rendered large by FreeType, 00280 // scales it small again for placing. 00281 //////////////////////////////////////////////////////////////////// 00282 void PNMTextGlyph:: 00283 rescale(double scale_factor) { 00284 if (scale_factor == 1.0) { 00285 return; 00286 } 00287 nassertv(scale_factor != 0.0); 00288 _advance /= scale_factor; 00289 _int_advance = (int)floor(_advance + 0.5); 00290 00291 if (_image.is_valid()) { 00292 int orig_x_size = _image.get_x_size(); 00293 int orig_y_size = _image.get_y_size(); 00294 int orig_left = _left; 00295 int orig_top = _top; 00296 00297 // Pad the image by a few pixels all around to allow for 00298 // antialiasing at the edges. 00299 int extra_pad = (int)ceil(scale_factor); 00300 orig_x_size += 2*extra_pad; 00301 orig_y_size += 2*extra_pad; 00302 orig_left -= extra_pad; 00303 orig_top += extra_pad; 00304 00305 // Now compute the reduced size. 00306 int new_x_size = (int)ceil(orig_x_size / scale_factor); 00307 int new_y_size = (int)ceil(orig_y_size / scale_factor); 00308 int new_left = (int)floor(orig_left / scale_factor); 00309 int new_top = (int)ceil(orig_top / scale_factor); 00310 00311 // And scale those back up so we can determine the amount of 00312 // additional padding we need to make the pixels remain in the 00313 // right place after the integer reduction. 00314 int old_x_size = (int)(new_x_size * scale_factor + 0.5); 00315 int old_y_size = (int)(new_y_size * scale_factor + 0.5); 00316 int old_left = (int)(new_left * scale_factor + 0.5); 00317 int old_top = (int)(new_top * scale_factor + 0.5); 00318 00319 int pad_left = orig_left - old_left; 00320 int pad_top = old_top - orig_top; 00321 00322 // These shouldn't go negative. 00323 nassertv(extra_pad + pad_left >= 0 && extra_pad + pad_top >= 0); 00324 00325 PNMImage enlarged(old_x_size, old_y_size, _image.get_num_channels()); 00326 enlarged.copy_sub_image(_image, pad_left + extra_pad, pad_top + extra_pad); 00327 00328 _image.clear(new_x_size, new_y_size, _image.get_num_channels()); 00329 _image.quick_filter_from(enlarged); 00330 00331 _left = new_left; 00332 _top = new_top; 00333 } 00334 }