浏览代码

support non-scalable fonts

David Rose 24 年之前
父节点
当前提交
d70a1f25cb
共有 1 个文件被更改,包括 89 次插入26 次删除
  1. 89 26
      panda/src/text/dynamicTextFont.cxx

+ 89 - 26
panda/src/text/dynamicTextFont.cxx

@@ -98,6 +98,7 @@ DynamicTextFont(const Filename &font_filename, int face_index) {
       }
       set_name(name);
 
+      /*
       if (!FT_IS_SCALABLE(_face)) {
         text_cat.error()
           << "Unable to read font " << get_name()
@@ -106,7 +107,7 @@ DynamicTextFont(const Filename &font_filename, int face_index) {
         // haven't bothered to write the few lines of glue code that
         // would do it.
 
-      } else {
+        } else */ {
         text_cat.info()
           << "Loaded font " << get_name() << "\n";
         _is_valid = true;
@@ -298,6 +299,37 @@ reset_scale() {
   int error = FT_Set_Char_Size(_face,
                                (int)(_point_size * 64), (int)(_point_size * 64),
                                dpi, dpi);
+  if (error) {
+    // If we were unable to set a particular char size, perhaps we
+    // have a non-scalable font.  Try to figure out the closest
+    // available size.
+    int desired_height = (int)(_pixels_per_unit * _point_size / points_per_unit + 0.5f);
+    int best_size = -1;
+    if (_face->num_fixed_sizes > 0) {
+      best_size = 0;
+      int best_diff = abs(desired_height - _face->available_sizes[0].height);
+      for (int i = 1; i < _face->num_fixed_sizes; i++) {
+        int diff = abs(desired_height - _face->available_sizes[i].height);
+        if (diff < best_diff) {
+          best_size = i;
+          best_diff = diff;
+        }
+      }
+    }
+    if (best_size >= 0) {
+      int pixel_height = _face->available_sizes[best_size].height;
+      int pixel_width = _face->available_sizes[best_size].width;
+      error = FT_Set_Pixel_Sizes(_face, pixel_width, pixel_height);
+      if (!error) {
+        text_cat.info()
+          << "Using " << pixel_height << "-pixel font for "
+          << get_name() << "\n";
+
+        _pixels_per_unit = pixel_height * points_per_unit / _point_size;
+      }
+    }
+  }
+
   if (error) {
     text_cat.warning()
       << "Unable to set " << get_name() 
@@ -306,11 +338,7 @@ reset_scale() {
     return false;
   }
 
-  // The face's height is only relevant for scalable fonts,
-  // according to FreeType.  How should we determine whether we
-  // have a scalable font or otherwise?
-  float pixel_size = _point_size * (_pixels_per_unit / points_per_unit);
-  _line_height = (float)_face->height * pixel_size / ((float)_face->units_per_EM * 64.0f);
+  _line_height = _face->size->metrics.height / (_pixels_per_unit * 64.0f);
 
   // Determine the correct width for a space.
   error = FT_Load_Char(_face, ' ', FT_LOAD_DEFAULT);
@@ -354,29 +382,64 @@ make_glyph(int glyph_index) {
     return glyph;
 
   } else {
-    if (bitmap.pixel_mode != ft_pixel_mode_grays) {
+    DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows);
+
+    if (bitmap.pixel_mode == ft_pixel_mode_grays && bitmap.num_grays == 256) {
+      // This is the easy case: we can memcpy the rendered glyph
+      // directly into our texture image, one row at a time.
+      unsigned char *buffer_row = bitmap.buffer;
+      for (int yi = 0; yi < bitmap.rows; yi++) {
+        unsigned char *texture_row = glyph->get_row(yi);
+        nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL);
+        memcpy(texture_row, buffer_row, bitmap.width);
+        buffer_row += bitmap.pitch;
+      }
+
+    } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
+      // This is a little bit more work: we have to expand the
+      // one-bit-per-pixel bitmap into a one-byte-per-pixel texture.
+      unsigned char *buffer_row = bitmap.buffer;
+      for (int yi = 0; yi < bitmap.rows; yi++) {
+        unsigned char *texture_row = glyph->get_row(yi);
+        nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL);
+
+        int bit = 0x80;
+        unsigned char *b = buffer_row;
+        for (int xi = 0; xi < bitmap.width; xi++) {
+          if (*b & bit) {
+            texture_row[xi] = 0xff;
+          } else {
+            texture_row[xi] = 0x00;
+          }
+          bit >>= 1;
+          if (bit == 0) {
+            ++b;
+            bit = 0x80;
+          }
+        }
+
+        buffer_row += bitmap.pitch;
+      }
+      
+
+    } else if (bitmap.pixel_mode == ft_pixel_mode_grays) {
+      // Here we must expand a grayscale pixmap with n levels of gray
+      // into our 256-level texture.
+      unsigned char *buffer_row = bitmap.buffer;
+      for (int yi = 0; yi < bitmap.rows; yi++) {
+        unsigned char *texture_row = glyph->get_row(yi);
+        nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL);
+        for (int xi = 0; xi < bitmap.width; xi++) {
+          texture_row[xi] = (int)(buffer_row[xi] * 255) / (bitmap.num_grays - 1);
+        }
+        buffer_row += bitmap.pitch;
+      }
+
+    } else {
       text_cat.error()
         << "Unexpected pixel mode in bitmap: " << (int)bitmap.pixel_mode << "\n";
-      return (DynamicTextGlyph *)NULL;
-    }
-    
-    if (bitmap.num_grays != 256) {
-      // We expect 256 levels of grayscale to come back from FreeType,
-      // since that's what we asked for.
-      text_cat.warning()
-        << "Expected 256 levels of gray, got " << bitmap.num_grays << "\n";
-    }
-    
-    DynamicTextGlyph *glyph = slot_glyph(bitmap.width, bitmap.rows);
-    
-    // Now copy the rendered glyph into the texture.
-    unsigned char *buffer_row = bitmap.buffer;
-    for (int yi = 0; yi < bitmap.rows; yi++) {
-      unsigned char *texture_row = glyph->get_row(yi);
-      nassertr(texture_row != (unsigned char *)NULL, (DynamicTextGlyph *)NULL);
-      memcpy(texture_row, buffer_row, bitmap.width);
-      buffer_row += bitmap.pitch;
     }
+
     glyph->_page->mark_dirty(Texture::DF_image);
     
     glyph->make_geom(slot->bitmap_top, slot->bitmap_left, advance,