Panda3D
 All Classes Functions Variables Enumerations
pnmTextGlyph.cxx
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 }
 All Classes Functions Variables Enumerations