freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[freetype2-demos] gsoc-2022-chariri-3 cd132d4 15/36: [ftinspect] Let con


From: Werner Lemberg
Subject: [freetype2-demos] gsoc-2022-chariri-3 cd132d4 15/36: [ftinspect] Let continuous view support non-outline glyphs.
Date: Wed, 27 Jul 2022 06:32:45 -0400 (EDT)

branch: gsoc-2022-chariri-3
commit cd132d4b5a8b3cd55459640cae70d0a47baec45f
Author: Charlie Jiang <w@chariri.moe>
Commit: Charlie Jiang <w@chariri.moe>

    [ftinspect] Let continuous view support non-outline glyphs.
    
    Integrate the rendering infrastructure we built previously into the
    `GlyphContinuous`.
    
    TODO: Optimize: Cache `QImage`s because they're expensive to create.
    
    * src/ftinspect/rendering/glyphcontinuous.cpp,
      src/ftinspect/rendering/glyphcontinuous.hpp:
      As described, integrate rendering infrastructures from `Engine`.
      Add better documentation for FreeType objects stored in the class.
      Removed `cloneOutline` since you should never clone an outline alone.
      Renamed some functions.
    
    * src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
      Split `convertBitmapToQImage` into `convertGlyphToQImage` and
      `convertBitmapToQImage`, add `computeGlyphOffset` and rename
      `glyphToBitmap` to follow the style `convertXXX`.
    
    * src/ftinspect/rendering/glyphbitmap.cpp: Update renamed function ref.
    
    * src/ftinspect/rendering/renderutils.cpp,
      src/ftinspect/rendering/renderutils.hpp: Add `cloneBitmap`.
---
 src/ftinspect/engine/engine.cpp             |  74 ++++++++---
 src/ftinspect/engine/engine.hpp             |   6 +-
 src/ftinspect/rendering/glyphbitmap.cpp     |   2 +-
 src/ftinspect/rendering/glyphcontinuous.cpp | 187 +++++++++++++++-------------
 src/ftinspect/rendering/glyphcontinuous.hpp |  25 ++--
 src/ftinspect/rendering/renderutils.cpp     |  13 ++
 src/ftinspect/rendering/renderutils.hpp     |   2 +
 7 files changed, 193 insertions(+), 116 deletions(-)

diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index f51af03..9dc33fd 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -453,7 +453,7 @@ Engine::loadGlyphWithoutUpdate(int glyphIndex)
 
 
 bool
-Engine::glyphToBitmap(FT_Glyph src,
+Engine::convertGlyphToBitmapGlyph(FT_Glyph src,
                       FT_Glyph* out)
 {
   if (src->format == FT_GLYPH_FORMAT_BITMAP)
@@ -739,16 +739,11 @@ convertLCDVToARGB(FT_Bitmap& bitmap,
 
 
 QImage*
-Engine::convertBitmapToQImage(FT_Glyph src,
-                              QRect* outRect)
+Engine::convertBitmapToQImage(FT_Bitmap* src)
 {
   QImage* result = NULL;
-  FT_BitmapGlyph bitmapGlyph;
-  bool ownBitmapGlyph
-    = glyphToBitmap(src, reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
-  if (!bitmapGlyph)
-    return result;
-  auto& bmap = bitmapGlyph->bitmap;
+  
+  auto& bmap = *src;
   bool ownBitmap = false;
 
   int width = bmap.width;
@@ -769,14 +764,6 @@ Engine::convertBitmapToQImage(FT_Glyph src,
   else if (bmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
     height /= 3;
 
-  if (outRect)
-  {
-    outRect->setLeft(bitmapGlyph->left);
-    outRect->setTop(-bitmapGlyph->top);
-    outRect->setWidth(width);
-    outRect->setHeight(height);
-  }
-
   switch (bmap.pixel_mode)
   {
   case FT_PIXEL_MODE_MONO:
@@ -827,8 +814,6 @@ Engine::convertBitmapToQImage(FT_Glyph src,
   }
 
 cleanup:
-  if (ownBitmapGlyph)
-    FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
   if (ownBitmap)
     FT_Bitmap_Done(library_, &bmap);
 
@@ -836,6 +821,57 @@ cleanup:
 }
 
 
+QImage*
+Engine::convertGlyphToQImage(FT_Glyph src, QRect* outRect)
+{
+  FT_BitmapGlyph bitmapGlyph;
+  bool ownBitmapGlyph
+    = convertGlyphToBitmapGlyph(src, 
reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
+  if (!bitmapGlyph)
+    return NULL;
+
+  auto result = convertBitmapToQImage(&bitmapGlyph->bitmap);
+
+  if (result && outRect)
+  {
+    outRect->setLeft(bitmapGlyph->left);
+    outRect->setTop(-bitmapGlyph->top);
+    outRect->setWidth(bitmapGlyph->bitmap.width);
+    outRect->setHeight(bitmapGlyph->bitmap.rows);
+  }
+
+  if (ownBitmapGlyph)
+    FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
+
+  return result;
+}
+
+
+QPoint
+Engine::computeGlyphOffset(FT_Glyph glyph)
+{
+  if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    FT_BBox cbox;
+    FT_Outline_Get_CBox(&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline, 
+                        &cbox);
+    cbox.xMin &= ~63;
+    cbox.yMin &= ~63;
+    cbox.xMax = (cbox.xMax + 63) & ~63;
+    cbox.yMax = (cbox.yMax + 63) & ~63;
+    return { static_cast<int>(cbox.xMin) / 64,
+               static_cast<int>(-cbox.yMax / 64) };
+  }
+  if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
+  {
+    auto bg = reinterpret_cast<FT_BitmapGlyph>(glyph);
+    return { bg->left, -bg->top };
+  }
+
+  return {};
+}
+
+
 QHash<FT_Glyph_Format, QString> glyphFormatNamesCache;
 QHash<FT_Glyph_Format, QString>&
 glyphFormatNames()
diff --git a/src/ftinspect/engine/engine.hpp b/src/ftinspect/engine/engine.hpp
index 757655e..9c2d6e5 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -105,9 +105,11 @@ public:
 
   // Return `true` if you need to free `out`
   // `out` will be set to NULL in cases of error
-  bool glyphToBitmap(FT_Glyph src, FT_Glyph* out);
+  bool convertGlyphToBitmapGlyph(FT_Glyph src, FT_Glyph* out);
   FT_Bitmap convertBitmapTo8Bpp(FT_Bitmap* bitmap);
-  QImage* convertBitmapToQImage(FT_Glyph src, QRect* outRect);
+  QImage* convertBitmapToQImage(FT_Bitmap* src);
+  QImage* convertGlyphToQImage(FT_Glyph src, QRect* outRect);
+  QPoint computeGlyphOffset(FT_Glyph glyph);
 
   // reload current triplet, but with updated settings, useful for updating
   // `ftSize_` only
diff --git a/src/ftinspect/rendering/glyphbitmap.cpp 
b/src/ftinspect/rendering/glyphbitmap.cpp
index 5ccc4e7..aab7776 100644
--- a/src/ftinspect/rendering/glyphbitmap.cpp
+++ b/src/ftinspect/rendering/glyphbitmap.cpp
@@ -18,7 +18,7 @@ GlyphBitmap::GlyphBitmap(FT_Glyph glyph,
                          Engine* engine)
 {
   QRect bRect;
-  image_ = engine->convertBitmapToQImage(glyph, &bRect);
+  image_ = engine->convertGlyphToQImage(glyph, &bRect);
   boundingRect_ = bRect; // QRectF to QRect
 }
 
diff --git a/src/ftinspect/rendering/glyphcontinuous.cpp 
b/src/ftinspect/rendering/glyphcontinuous.cpp
index a0e236b..cb2a196 100644
--- a/src/ftinspect/rendering/glyphcontinuous.cpp
+++ b/src/ftinspect/rendering/glyphcontinuous.cpp
@@ -11,13 +11,14 @@
 #include <QPainter>
 #include <QWheelEvent>
 
+#include <freetype/ftbitmap.h>
+
 
 GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
 : QWidget(parent), engine_(engine)
 {
   setAcceptDrops(false);
   setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-  graphicsDefault_ = GraphicsDefault::deafultInstance();
 
   FT_Stroker_New(engine_->ftLibrary(), &stroker_);
 }
@@ -99,10 +100,10 @@ GlyphContinuous::paintAG(QPainter* painter)
     switch (mode_)
     {
     case M_Fancy:
-      transformGlyphAGFancy();
+      transformGlyphFancy();
       break;
     case M_Stroked:
-      transformGlyphAGStroked();
+      transformGlyphStroked();
       break;
     default:;
     }
@@ -118,7 +119,7 @@ GlyphContinuous::paintAG(QPainter* painter)
 
 
 void
-GlyphContinuous::transformGlyphAGFancy()
+GlyphContinuous::transformGlyphFancy()
 {
   // adopted from ftview.c:289
   /***************************************************************/
@@ -146,41 +147,52 @@ GlyphContinuous::transformGlyphAGFancy()
   xstr = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
   ystr = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
 
-  if (!isGlyphCloned_)
-    cloneGlyph();
-
-  if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
-    return; // TODO suuport non-outline: code below all depend on `outline_`!
-
-  FT_Outline_Transform(&outline_, &shear);
-  FT_Outline_EmboldenXY(&outline_, xstr, ystr);
+  if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    if (!isGlyphCloned_)
+      cloneGlyph();
+    FT_Outline_Transform(&outline_, &shear);
+    if (FT_Outline_EmboldenXY(&outline_, xstr, ystr))
+    {
+      // XXX error handling?
+      return;
+    }
 
-  if (glyph_->advance.x)
-    glyph_->advance.x += xstr;
+    if (glyph_->advance.x)
+      glyph_->advance.x += xstr;
 
-  if (glyph_->advance.y)
-    glyph_->advance.y += ystr;
-  
-  //glyph_->metrics.width += xstr;
-  //glyph_->metrics.height += ystr;
-  //glyph_->metrics.horiAdvance += xstr;
-  //glyph_->metrics.vertAdvance += ystr;
+    if (glyph_->advance.y)
+      glyph_->advance.y += ystr;
+  }
+  else if (glyph_->format == FT_GLYPH_FORMAT_BITMAP)
+  {
+    if (!isBitmapCloned_)
+      cloneBitmap();
+    xstr &= ~63;
+    ystr &= ~63;
+
+    // No shearing support for bitmap
+    FT_Bitmap_Embolden(engine_->ftLibrary(), &bitmap_, 
+                       xstr, ystr);
+  }
+  else
+    return; // XXX no support for SVG
 }
 
 
 void
-GlyphContinuous::transformGlyphAGStroked()
+GlyphContinuous::transformGlyphStroked()
 {
-  //if (!isGlyphCloned_)
-    //cloneGlyph();
-  // Well, now here only outline glyph is supported.
+  // Well, here only outline glyph is supported.
   if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
     return;
+  auto oldGlyph = glyph_;
   auto error = FT_Glyph_Stroke(&glyph_, stroker_, 0);
   if (!error)
   {
+    if (isGlyphCloned_)
+      FT_Done_Glyph(oldGlyph);
     isGlyphCloned_ = true;
-    isOutlineCloned_ = false;
     outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
   }
 }
@@ -228,61 +240,19 @@ GlyphContinuous::paintChar(QPainter* painter)
   // XXX: this is different from what's being done in
   // `ftcommon.c`:FTDemo_Draw_Slot: is this correct??
 
-  // First translate the outline
-
-  if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
-    return true; // XXX only outline is supported - need to impl others later
-
-  FT_BBox cbox;
-  // Don't forget to free this when returning
-  if (!isOutlineCloned_ && !isGlyphCloned_)
-    cloneOutline();
+  QImage* image;
   
-  transformOutlineToOrigin(&outline_, &cbox);
-  
-  auto outlineWidth = (cbox.xMax - cbox.xMin) / 64;
-  auto outlineHeight = (cbox.yMax - cbox.yMin) / 64;
-
-  // Then convert to bitmap
-  FT_Bitmap bitmap;
-  QImage::Format format = QImage::Format_Indexed8;
-  auto aaEnabled = engine_->antiAliasingEnabled();
-
-  // TODO cover LCD and color
-  if (!aaEnabled)
-    format = QImage::Format_Mono;
-
-  // TODO optimization: reuse QImage?
-  QImage image(QSize(outlineWidth, outlineHeight), format);
-
-  if (!aaEnabled)
-    image.setColorTable(graphicsDefault_->monoColorTable);
+  if (bitmap_.buffer) // Always prefer `bitmap_` since it can be manipulated
+    image = engine_->convertBitmapToQImage(&bitmap_);
   else
-    image.setColorTable(graphicsDefault_->grayColorTable);
+    image = engine_->convertGlyphToQImage(glyph_, NULL);
+  auto offset = engine_->computeGlyphOffset(glyph_);
 
-  image.fill(0);
-
-  bitmap.rows = static_cast<unsigned int>(outlineHeight);
-  bitmap.width = static_cast<unsigned int>(outlineWidth);
-  bitmap.buffer = image.bits();
-  bitmap.pitch = image.bytesPerLine();
-  bitmap.pixel_mode = aaEnabled ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO;
-
-  FT_Error error = FT_Outline_Get_Bitmap(engine_->ftLibrary(),
-                                         &outline_,
-                                         &bitmap);
-  if (error)
-  {
-    // XXX error handling
-    return true;
-  }
-
-  painter->drawImage(
-      QPoint(x_ + cbox.xMin / 64, y_ + (-cbox.yMax / 64)),
-      image.convertToFormat(QImage::Format_ARGB32_Premultiplied));
+  painter->drawImage(offset + QPoint(x_, y_),
+                     *image);
+  delete image;
 
   x_ += width;
-  
   return true;
 }
 
@@ -290,15 +260,13 @@ GlyphContinuous::paintChar(QPainter* painter)
 bool
 GlyphContinuous::loadGlyph(int index)
 {
+  if (isGlyphCloned_)
+    FT_Done_Glyph(glyph_);
   glyph_ = engine_->loadGlyphWithoutUpdate(index);
   isGlyphCloned_ = false;
   if (!glyph_)
     return false;
-  if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
-  {
-    isOutlineCloned_ = false;
-    outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
-  }
+  refreshOutlineOrBitmapFromGlyph();
   return true;
 }
 
@@ -306,16 +274,57 @@ GlyphContinuous::loadGlyph(int index)
 void
 GlyphContinuous::cloneGlyph()
 {
+  if (isGlyphCloned_)
+    return;
   glyph_ = ::cloneGlyph(glyph_);
+  refreshOutlineOrBitmapFromGlyph();
   isGlyphCloned_ = true;
 }
 
 
 void
-GlyphContinuous::cloneOutline()
+GlyphContinuous::cloneBitmap()
 {
-  outline_ = ::cloneOutline(engine_->ftLibrary(), &outline_);
-  isOutlineCloned_ = true;
+  if (isBitmapCloned_)
+    return;
+  bitmap_ = ::cloneBitmap(engine_->ftLibrary(), &bitmap_);
+  isBitmapCloned_ = true;
+}
+
+
+void
+GlyphContinuous::refreshOutlineOrBitmapFromGlyph()
+{
+  if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
+
+    // Make `bitmap_` invalid
+    if (isBitmapCloned_)
+      FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
+    isBitmapCloned_ = false;
+    bitmap_.buffer = NULL;
+  }
+  else if (glyph_->format == FT_GLYPH_FORMAT_BITMAP)
+  {
+    // Initialize `bitmap_`
+    if (isBitmapCloned_)
+      FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
+    isBitmapCloned_ = false;
+    bitmap_ = reinterpret_cast<FT_BitmapGlyph>(glyph_)->bitmap;
+
+    outline_.points = NULL;
+  }
+  else
+  {
+    // Both invalid.
+    outline_.points = NULL;
+
+    if (isBitmapCloned_)
+      FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
+    isBitmapCloned_ = false;
+    bitmap_.buffer = NULL;
+  }
 }
 
 
@@ -324,13 +333,17 @@ GlyphContinuous::cleanCloned()
 {
   if (isGlyphCloned_)
   {
+    if (glyph_->format == FT_GLYPH_FORMAT_BITMAP && !isBitmapCloned_)
+      bitmap_.buffer = NULL;
+
     FT_Done_Glyph(glyph_);
     isGlyphCloned_ = false;
   }
-  if (isOutlineCloned_)
+  if (isBitmapCloned_)
   {
-    FT_Outline_Done(engine_->ftLibrary(), &outline_);
-    isOutlineCloned_ = false;
+    FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
+    bitmap_.buffer = NULL;
+    isBitmapCloned_ = false;
   }
 }
 
diff --git a/src/ftinspect/rendering/glyphcontinuous.hpp 
b/src/ftinspect/rendering/glyphcontinuous.hpp
index cd4855f..773d96e 100644
--- a/src/ftinspect/rendering/glyphcontinuous.hpp
+++ b/src/ftinspect/rendering/glyphcontinuous.hpp
@@ -70,7 +70,6 @@ protected:
 
 private:
   Engine* engine_;
-  GraphicsDefault* graphicsDefault_;
 
   Source source_ = SRC_AllGlyphs;
   Mode mode_ = M_Normal;
@@ -88,23 +87,35 @@ private:
   FT_Size_Metrics metrics_;
   int x_ = 0, y_ = 0;
   int stepY_ = 0;
+
+  // Pay especially attention to life cycles & ownerships of those objects:
+  // Note that outline and bitmap can be either invalid, owned by glyph or
+  // owned by `this`.
+  // If owned by `this`, then it's safe to do manipulation, and need to cleanup
+  // If owned by glyph, then must clone to do manipulation, and no cleanup
+  // In `loadGraph`, these 3 values will all be updated.
+  // Note that `glyph_` is a pointer, while `outline_` and `bitmap_` are 
structs
   FT_Glyph glyph_;
-  FT_Outline outline_;
+  FT_Outline outline_; // Using outline_->points == NULL to determine validity
+  FT_Bitmap bitmap_; // Using bitmap_->buffer == NULL to determine validity
   // when glyph is cloned, outline is factually also cloned
-  // but `isOutlineCloned` won't be set!
-  bool isGlyphCloned_ = false, isOutlineCloned_ = false;
+  // never manually clone your outline or you can't easily render it!
+  bool isGlyphCloned_ = false;
+  bool isBitmapCloned_ = false;
 
   FT_Stroker stroker_;
 
   void paintAG(QPainter* painter);
-  void transformGlyphAGFancy();
-  void transformGlyphAGStroked();
+  void transformGlyphFancy();
+  void transformGlyphStroked();
   void prePaint();
   // return if there's enough space to paint the current char
   bool paintChar(QPainter* painter);
   bool loadGlyph(int index);
+
   void cloneGlyph();
-  void cloneOutline();
+  void cloneBitmap();
+  void refreshOutlineOrBitmapFromGlyph();
   void cleanCloned();
 
   bool checkFitX(int x);
diff --git a/src/ftinspect/rendering/renderutils.cpp 
b/src/ftinspect/rendering/renderutils.cpp
index e8316cd..d07eb3f 100644
--- a/src/ftinspect/rendering/renderutils.cpp
+++ b/src/ftinspect/rendering/renderutils.cpp
@@ -25,6 +25,19 @@ cloneGlyph(FT_Glyph src)
 }
 
 
+FT_Bitmap
+cloneBitmap(FT_Library library,
+            FT_Bitmap* src)
+{
+  FT_Bitmap target = *src;
+  target.buffer = NULL;
+  target.palette = NULL;
+  FT_Bitmap_Init(&target);
+  FT_Bitmap_Copy(library, src, &target);
+  return target;
+}
+
+
 void
 transformOutlineToOrigin(FT_Outline* outline,
                          FT_BBox* outControlBox)
diff --git a/src/ftinspect/rendering/renderutils.hpp 
b/src/ftinspect/rendering/renderutils.hpp
index 3d284b9..e611a0e 100644
--- a/src/ftinspect/rendering/renderutils.hpp
+++ b/src/ftinspect/rendering/renderutils.hpp
@@ -5,11 +5,13 @@
 #pragma once
 
 #include <freetype/ftglyph.h>
+#include <freetype/ftbitmap.h>
 #include <freetype/ftoutln.h>
 
 // The constructed `outline` must be freed by the caller
 FT_Outline cloneOutline(FT_Library library, FT_Outline* src);
 FT_Glyph cloneGlyph(FT_Glyph src);
+FT_Bitmap cloneBitmap(FT_Library library, FT_Bitmap* src);
 
 void transformOutlineToOrigin(FT_Outline* outline,
                               FT_BBox* outControlBox);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]