freetype-commit
[Top][All Lists]
Advanced

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

[freetype2-demos] master 1d3d862 15/41: [ftinspect] Support color layers


From: Werner Lemberg
Subject: [freetype2-demos] master 1d3d862 15/41: [ftinspect] Support color layers.
Date: Mon, 3 Oct 2022 11:27:01 -0400 (EDT)

branch: master
commit 1d3d8628b85edb85bc8c6435f57248b96baac039
Author: Charlie Jiang <w@chariri.moe>
Commit: Werner Lemberg <wl@gnu.org>

    [ftinspect] Support color layers.
    
    * src/ftinspect/engine/rendering.cpp:
      Add implementation of `tryDirectRenderColorLayers`.
    
    * src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
      Implement `loadPaletteInfos` and `loadPalette`.
      Add `currentFontHasColorLayers` and a group of getters/setters supporting
      color layer rendering.
      Also reorder fields as the number of fields grows.
    
    * src/ftinspect/panels/settingpanel.cpp,
      src/ftinspect/panels/settingpanel.hpp:
      Implement `populatePalettes` and reorder functions.
      Fix `checkPalette` about combo box enabling state and reset the cache when
      the palette settings change.
      Uncomment functional code.
    
    * src/ftinspect/engine/paletteinfo.cpp,
      src/ftinspect/engine/paletteinfo.hpp: New files adding `PaletteInfo`.
      Retrieving palette names using SFNT name tables is not supported yet.
    
    * src/ftinspect/panels/singular.cpp:
      Call `Engine::loadPalette` when drawing.
    
    * src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
---
 src/ftinspect/CMakeLists.txt          |   1 +
 src/ftinspect/engine/engine.cpp       |  54 +++++++++++++++
 src/ftinspect/engine/engine.hpp       |  35 ++++++++--
 src/ftinspect/engine/paletteinfo.cpp  |  33 +++++++++
 src/ftinspect/engine/paletteinfo.hpp  |  24 +++++++
 src/ftinspect/engine/rendering.cpp    | 126 +++++++++++++++++++++++++++++++++-
 src/ftinspect/meson.build             |   1 +
 src/ftinspect/panels/settingpanel.cpp |  70 ++++++++++++++-----
 src/ftinspect/panels/settingpanel.hpp |   3 +-
 src/ftinspect/panels/singular.cpp     |   2 +-
 10 files changed, 324 insertions(+), 25 deletions(-)

diff --git a/src/ftinspect/CMakeLists.txt b/src/ftinspect/CMakeLists.txt
index 40e2903..a8eaea6 100644
--- a/src/ftinspect/CMakeLists.txt
+++ b/src/ftinspect/CMakeLists.txt
@@ -23,6 +23,7 @@ add_executable(ftinspect
   "engine/engine.cpp"
   "engine/fontfilemanager.cpp"
   "engine/rendering.cpp"
+  "engine/paletteinfo.cpp"
 
   "glyphcomponents/glyphbitmap.cpp"
   "glyphcomponents/glyphoutline.cpp"
diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index 85e3e0b..f46b075 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -268,6 +268,7 @@ Engine::loadFont(int fontIndex,
 {
   int numGlyphs = -1;
   fontType_ = FontType_Other;
+  palette_ = NULL;
 
   update();
 
@@ -326,6 +327,7 @@ Engine::loadFont(int fontIndex,
     ftSize_ = NULL;
     curFamilyName_ = QString();
     curStyleName_ = QString();
+    curPaletteInfos_.clear();
   }
   else
   {
@@ -341,6 +343,7 @@ Engine::loadFont(int fontIndex,
       fontType_ = FontType_TrueType;
     else
       fontType_ = FontType_Other;
+    loadPaletteInfos();
   }
 
   curNumGlyphs_ = numGlyphs;
@@ -352,6 +355,7 @@ void
 Engine::reloadFont()
 {
   update();
+  palette_ = NULL;
   if (!scaler_.face_id)
     return;
   imageType_.face_id = scaler_.face_id;
@@ -367,6 +371,25 @@ Engine::reloadFont()
 }
 
 
+void
+Engine::loadPalette()
+{
+  palette_ = NULL;
+  if (paletteData_.num_palettes == 0
+      || paletteIndex_ < 0
+      || paletteData_.num_palettes <= paletteIndex_)
+    return;
+
+  if (!ftSize_)
+    return;
+
+  FT_Palette_Select(ftSize_->face, 
+                    static_cast<FT_UShort>(paletteIndex_),
+                    &palette_);
+  // XXX error handling
+}
+
+
 void
 Engine::removeFont(int fontIndex, bool closeFile)
 {
@@ -413,6 +436,15 @@ Engine::currentFontHasEmbeddedBitmap()
 }
 
 
+bool
+Engine::currentFontHasColorLayers()
+{
+  if (!ftFallbackFace_)
+    return false;
+  return FT_HAS_COLOR(ftFallbackFace_);
+}
+
+
 std::vector<int>
 Engine::currentFontFixedSizes()
 {
@@ -676,6 +708,7 @@ Engine::resetCache()
   FTC_Manager_Reset(cacheManager_);
   ftFallbackFace_ = NULL;
   ftSize_ = NULL;
+  palette_ = NULL;
 }
 
 
@@ -778,4 +811,25 @@ Engine::queryEngine()
   }
 }
 
+
+void
+Engine::loadPaletteInfos()
+{
+  curPaletteInfos_.clear();
+
+  if (FT_Palette_Data_Get(ftFallbackFace_, &paletteData_))
+  {
+    // XXX Error handling
+    paletteData_.num_palettes = 0;
+    return;
+  }
+
+  // size never exceeds max val of ushort.
+  curPaletteInfos_.reserve(paletteData_.num_palettes);
+  for (int i = 0; i < paletteData_.num_palettes; ++i)
+    curPaletteInfos_.emplace_back(ftFallbackFace_, paletteData_, i, nullptr);
+    // no `NULL` here - we need `std::nullptr_t`
+}
+
+
 // end of engine.cpp
diff --git a/src/ftinspect/engine/engine.hpp b/src/ftinspect/engine/engine.hpp
index 6fe142a..333bfa0 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -7,6 +7,7 @@
 
 #include "fontfilemanager.hpp"
 
+#include "paletteinfo.hpp"
 #include "rendering.hpp"
 
 #include <memory>
@@ -19,6 +20,7 @@
 #include <freetype/ftoutln.h>
 #include <freetype/ftcache.h>
 #include <freetype/ftlcdfil.h>
+#include <freetype/ftcolor.h>
 
 
 // This structure maps the (font, face, instance) index triplet to abstract
@@ -94,6 +96,8 @@ public:
   //////// Getters
 
   FT_Library ftLibrary() const { return library_; }
+  FTC_Manager cacheManager() { return cacheManager_; }
+  FTC_ImageCache imageCacheManager() { return imageCache_; }
   FontFileManager& fontFileManager() { return fontFileManager_; }
   EngineDefaultValues& engineDefaults() { return engineDefaults_; }
   RenderingEngine* renderingEngine() { return renderingEngine_.get(); }
@@ -101,13 +105,20 @@ public:
   int numberOfOpenedFonts();
 
   // (for current fonts)
+  FT_Face currentFallbackFtFace() { return ftFallbackFace_; }
+  FT_Size currentFtSize() { return ftSize_; }
   bool renderReady(); // Can we render bitmaps? (implys `fontValid`)
   bool fontValid(); // Is the current font valid (valid font may be unavailable
                     // to render, such as non-scalable font with invalid sizes)
+
   int currentFontType() const { return fontType_; }
   const QString& currentFamilyName() { return curFamilyName_; }
   const QString& currentStyleName() { return curStyleName_; }
   int currentFontNumberOfGlyphs() { return curNumGlyphs_; }
+  
+  std::vector<PaletteInfo>& currentFontPalettes() { return curPaletteInfos_; }
+  FT_Color* currentPalette() { return palette_; }
+  FT_Palette_Data& currentFontPaletteData() { return paletteData_; }
 
   QString glyphName(int glyphIndex);
   long numberOfFaces(int fontIndex);
@@ -118,18 +129,23 @@ public:
   bool currentFontTricky();
   bool currentFontBitmapOnly();
   bool currentFontHasEmbeddedBitmap();
+  bool currentFontHasColorLayers();
   std::vector<int> currentFontFixedSizes();
 
   // (settings)
   int dpi() { return dpi_; }
+  FTC_ImageType imageType() { return &imageType_; }
   bool antiAliasingEnabled() { return antiAliasingEnabled_; }
   bool embeddedBitmapEnabled() { return embeddedBitmap_; }
+  bool useColorLayer() { return useColorLayer_; }
+  int paletteIndex() { return paletteIndex_; }
   FT_Render_Mode
   renderMode()
   {
     return static_cast<FT_Render_Mode>(renderMode_);
   }
 
+
   //////// Setters (direct or indirect)
 
   void setDPI(int d) { dpi_ = d; }
@@ -154,7 +170,8 @@ public:
   void setRenderMode(int mode) { renderMode_ = mode; }
   void setAntiAliasingEnabled(bool enabled) { antiAliasingEnabled_ = enabled; }
   void setEmbeddedBitmapEnabled(bool enabled) { embeddedBitmap_ = enabled; }
-
+  void setUseColorLayer(bool colorLayer) { useColorLayer_ = colorLayer; }
+  void setPaletteIndex(int index) { paletteIndex_ = index; }
   // (settings without backing fields)
 
   // Note: These 3 functions now takes actual mode/version from FreeType,
@@ -179,16 +196,22 @@ private:
 
   FontFileManager fontFileManager_;
 
+  // font info
   int curFontIndex_ = -1;
   QString curFamilyName_;
   QString curStyleName_;
   int curNumGlyphs_ = -1;
+  std::vector<PaletteInfo> curPaletteInfos_;
+  int fontType_;
 
+  // basic objects
   FT_Library library_;
   FTC_Manager cacheManager_;
   FTC_ImageCache imageCache_;
   FTC_SBitCache sbitsCache_;
+  EngineDefaultValues engineDefaults_;
 
+  // settings
   FTC_ScalerRec scaler_ = {};
   FTC_ImageTypeRec imageType_; // for `loadGlyphWithoutUpdate`
   // Sometimes the font may be valid (i.e. a face object can be retrieved), but
@@ -196,10 +219,8 @@ private:
   // Therefore, we use a fallback face for all non-rendering work.
   FT_Face ftFallbackFace_; // Never perform rendering or write to this!
   FT_Size ftSize_;
-
-  EngineDefaultValues engineDefaults_;
-
-  int fontType_;
+  FT_Palette_Data paletteData_ = {};
+  FT_Color* palette_ = NULL;
 
   bool antiAliasingEnabled_ = true;
   bool usingPixelSize_ = false;
@@ -214,6 +235,8 @@ private:
   bool doBlueZoneHinting_;
   bool showSegments_;
   bool embeddedBitmap_;
+  bool useColorLayer_;
+  int paletteIndex_ = -1;
   int antiAliasingTarget_;
   int renderMode_;
 
@@ -222,6 +245,8 @@ private:
   std::unique_ptr<RenderingEngine> renderingEngine_;
 
   void queryEngine();
+  void loadPaletteInfos();
+
   // Safe to put the impl to the cpp.
   template <class Func>
   void withFace(FaceID id, Func func);
diff --git a/src/ftinspect/engine/paletteinfo.cpp 
b/src/ftinspect/engine/paletteinfo.cpp
new file mode 100644
index 0000000..7abd2ae
--- /dev/null
+++ b/src/ftinspect/engine/paletteinfo.cpp
@@ -0,0 +1,33 @@
+// paletteinfo.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "paletteinfo.hpp"
+
+//#include "fontinfo.hpp"
+
+PaletteInfo::PaletteInfo(FT_Face face, 
+                         FT_Palette_Data& data, 
+                         int index,
+                         std::vector<SFNTName> const* sfntNames)
+: index(index)
+{
+  if (sfntNames && data.palette_name_ids)
+  {
+    auto id = data.palette_name_ids[index];
+    name = "(unsupported)";
+    //for (auto& obj : *sfntNames)
+    //{
+    //  if (obj.nameID == id && obj.strValid)
+    //  {
+    //    name = obj.str;
+    //    break;
+    //  }
+    //}
+  }
+  else
+    name = "(unnamed)";
+}
+
+
+// end of paletteinfo.cpp
diff --git a/src/ftinspect/engine/paletteinfo.hpp 
b/src/ftinspect/engine/paletteinfo.hpp
new file mode 100644
index 0000000..8d66df7
--- /dev/null
+++ b/src/ftinspect/engine/paletteinfo.hpp
@@ -0,0 +1,24 @@
+// paletteinfo.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <vector>
+#include <QString>
+
+#include <freetype/freetype.h>
+#include <freetype/ftcolor.h>
+
+struct SFNTName;
+struct PaletteInfo
+{
+  int index;
+  QString name;
+
+  PaletteInfo(FT_Face face, FT_Palette_Data& data, int index, 
+              std::vector<SFNTName> const* sfntNames);
+};
+
+
+// end of paletteinfo.hpp
diff --git a/src/ftinspect/engine/rendering.cpp 
b/src/ftinspect/engine/rendering.cpp
index 7c7e8c7..76f5eae 100644
--- a/src/ftinspect/engine/rendering.cpp
+++ b/src/ftinspect/engine/rendering.cpp
@@ -304,7 +304,131 @@ RenderingEngine::tryDirectRenderColorLayers(int 
glyphIndex,
                                    QRect* outRect,
                                    bool inverseRectY)
 {
-  return NULL; // TODO: impl
+  auto& paletteData = engine_->currentFontPaletteData();
+  auto paletteIndex = engine_->paletteIndex();
+  auto palette = engine_->currentPalette();
+  if (palette == NULL 
+      || !engine_->useColorLayer() 
+      || paletteIndex >= paletteData.num_palettes)
+    return NULL;
+
+  FT_LayerIterator iter = {};
+  
+  FT_UInt layerGlyphIdx = 0;
+  FT_UInt layerColorIdx = 0;
+
+  bool next = FT_Get_Color_Glyph_Layer(engine_->currentFtSize()->face,
+                                       glyphIndex,
+                                       &layerGlyphIdx,
+                                       &layerColorIdx,
+                                       &iter);
+  if (!next)
+    return NULL;
+
+  // temporarily change lf
+  auto imageType = engine_->imageType();
+  auto oldLoadFlags = imageType->flags;
+  auto loadFlags = oldLoadFlags;
+  loadFlags &= ~FT_LOAD_COLOR;
+  loadFlags |= FT_LOAD_RENDER;
+
+  loadFlags &= ~FT_LOAD_TARGET_(0xF);
+  loadFlags |= FT_LOAD_TARGET_NORMAL;
+  imageType->flags = loadFlags;
+
+  FT_Bitmap bitmap = {};
+  FT_Bitmap_Init(&bitmap);
+
+  FT_Vector bitmapOffset = {};
+  bool failed = false;
+
+  do
+  {
+    FT_Vector slotOffset;
+    FT_Glyph glyph;
+    if (FTC_ImageCache_Lookup(engine_->imageCacheManager(),
+                              imageType,
+                              layerGlyphIdx,
+                              &glyph,
+                              NULL))
+    {
+      // XXX Error handling
+      failed = true;
+      break;
+    }
+
+    if (glyph->format != FT_GLYPH_FORMAT_BITMAP)
+      continue;
+
+    auto bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
+    slotOffset.x = bitmapGlyph->left << 6;
+    slotOffset.y = bitmapGlyph->top << 6;
+
+    FT_Color color = {};
+
+    if (layerColorIdx == 0xFFFF)
+    {
+      // TODO: FT_Palette_Get_Foreground_Color: #1134
+      if (paletteData.palette_flags
+          && (paletteData.palette_flags[paletteIndex] 
+              & FT_PALETTE_FOR_DARK_BACKGROUND))
+      {
+        /* white opaque */
+        color.blue = 0xFF;
+        color.green = 0xFF;
+        color.red = 0xFF;
+        color.alpha = 0xFF;
+      }
+      else
+      {
+        /* black opaque */
+        color.blue = 0x00;
+        color.green = 0x00;
+        color.red = 0x00;
+        color.alpha = 0xFF;
+      }
+    }
+    else if (layerColorIdx < paletteData.num_palette_entries)
+      color = palette[layerColorIdx];
+    else
+      continue;
+
+    if (FT_Bitmap_Blend(engine_->ftLibrary(),
+                        &bitmapGlyph->bitmap, slotOffset,
+                        &bitmap, &bitmapOffset,
+                        color))
+    {
+      // XXX error
+      failed = true;
+      break;
+    }
+  } while (FT_Get_Color_Glyph_Layer(engine_->currentFtSize()->face,
+                                    glyphIndex,
+                                    &layerGlyphIdx,
+                                    &layerColorIdx,
+                                    &iter));
+
+  imageType->flags = oldLoadFlags;
+  if (failed)
+  {
+    FT_Bitmap_Done(engine_->ftLibrary(), &bitmap);
+    return NULL;
+  }
+
+  auto img = convertBitmapToQImage(&bitmap);
+  if (outRect)
+  {
+    outRect->moveLeft(static_cast<int>(bitmapOffset.x >> 6));
+    if (inverseRectY)
+      outRect->moveTop(static_cast<int>(-bitmapOffset.y >> 6));
+    else
+      outRect->moveTop(static_cast<int>(bitmapOffset.y >> 6));
+    outRect->setSize(img->size());
+  }
+
+  FT_Bitmap_Done(engine_->ftLibrary(), &bitmap);
+
+  return img;
 }
 
 
diff --git a/src/ftinspect/meson.build b/src/ftinspect/meson.build
index 02f1eed..05c5854 100644
--- a/src/ftinspect/meson.build
+++ b/src/ftinspect/meson.build
@@ -24,6 +24,7 @@ if qt5_dep.found()
     'engine/engine.cpp',
     'engine/fontfilemanager.cpp',
     'engine/rendering.cpp',
+    'engine/paletteinfo.cpp',
 
     'glyphcomponents/glyphbitmap.cpp',
     'glyphcomponents/glyphoutline.cpp',
diff --git a/src/ftinspect/panels/settingpanel.cpp 
b/src/ftinspect/panels/settingpanel.cpp
index 07e4572..f07691c 100644
--- a/src/ftinspect/panels/settingpanel.cpp
+++ b/src/ftinspect/panels/settingpanel.cpp
@@ -49,13 +49,6 @@ SettingPanel::lsbRsbDeltaEnabled()
 }
 
 
-void
-SettingPanel::populatePalettes()
-{
-  // TODO: Impl
-}
-
-
 void
 SettingPanel::checkAllSettings()
 {
@@ -193,8 +186,10 @@ SettingPanel::checkAntiAliasing()
 void
 SettingPanel::checkPalette()
 {
-  paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked());
-  emit repaintNeeded();
+  paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked()
+                               && paletteComboBox_->count() > 0);
+  engine_->resetCache();
+  emit fontReloadNeeded();
 }
 
 
@@ -267,6 +262,47 @@ SettingPanel::resetColorBlocks()
 }
 
 
+void
+SettingPanel::populatePalettes()
+{
+  auto needToReload = false;
+  auto& newPalettes = engine_->currentFontPalettes();
+  auto newSize = static_cast<int>(newPalettes.size()); // this never exceeds!
+  if (newSize != paletteComboBox_->count())
+    needToReload = true;
+  else
+    for (int i = 0; i < newSize; ++i)
+    {
+      auto oldNameVariant = paletteComboBox_->itemData(i);
+      if (!oldNameVariant.canConvert<QString>())
+      {
+        needToReload = true;
+        break;
+      }
+      if (oldNameVariant.toString() != newPalettes[i].name)
+      {
+        needToReload = true;
+        break;
+      }
+    }
+  if (!needToReload)
+    return;
+
+  {
+    QSignalBlocker blocker(paletteComboBox_);
+    paletteComboBox_->clear();
+    for (int i = 0; i < newSize; ++i)
+      paletteComboBox_->addItem(
+        QString("%1: %2")
+          .arg(i)
+          .arg(newPalettes[i].name),
+        newPalettes[i].name);
+  }
+
+  emit fontReloadNeeded();
+}
+
+
 void
 SettingPanel::onFontChanged()
 {
@@ -296,10 +332,12 @@ SettingPanel::onFontChanged()
   checkHinting();
 
   engine_->reloadFont();
-  //auto hasColor = engine_->currentFontHasColorLayers();
-  //colorLayerCheckBox_->setEnabled(hasColor);
-  //if (!hasColor)
-  //  colorLayerCheckBox_->setChecked(false);
+  auto hasColor = engine_->currentFontHasColorLayers();
+  colorLayerCheckBox_->setEnabled(hasColor);
+  if (!hasColor)
+    colorLayerCheckBox_->setChecked(false);
+  paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked()
+                               && paletteComboBox_->count() > 0);
   populatePalettes();
   //mmgxPanel_->reloadFont();
   blockSignals(blockState);
@@ -341,9 +379,9 @@ SettingPanel::applySettings()
   engine_->renderingEngine()->setGamma(gammaSlider_->value() / 10.0);
 
   engine_->setEmbeddedBitmapEnabled(embeddedBitmapCheckBox_->isChecked());
-  //engine_->setPaletteIndex(paletteComboBox_->currentIndex());
+  engine_->setPaletteIndex(paletteComboBox_->currentIndex());
 
-  //engine_->setUseColorLayer(colorLayerCheckBox_->isChecked());
+  engine_->setUseColorLayer(colorLayerCheckBox_->isChecked());
   //engine_->setLCDUsesBGR(aaSettings.isBGR);
   //engine_->setLCDSubPixelPositioning(
   //  antiAliasingComboBox_->currentIndex()
@@ -593,7 +631,7 @@ SettingPanel::createConnections()
           this, &SettingPanel::repaintNeeded);
   connect(paletteComboBox_,
           QOverload<int>::of(&QComboBox::currentIndexChanged), 
-          this, &SettingPanel::repaintNeeded);
+          this, &SettingPanel::checkPalette);
 
   connect(gammaSlider_, &QSlider::valueChanged,
           this, &SettingPanel::updateGamma);
diff --git a/src/ftinspect/panels/settingpanel.hpp 
b/src/ftinspect/panels/settingpanel.hpp
index 7f1d5e3..73c309a 100644
--- a/src/ftinspect/panels/settingpanel.hpp
+++ b/src/ftinspect/panels/settingpanel.hpp
@@ -111,8 +111,6 @@ private:
 
   //////// Other funcs
 
-  void populatePalettes();
-
   void checkAllSettings();
   void checkHinting();
   void checkHintingMode();
@@ -125,6 +123,7 @@ private:
   void openForegroundPicker();
   void updateGamma();
   void resetColorBlocks();
+  void populatePalettes();
 };
 
 
diff --git a/src/ftinspect/panels/singular.cpp 
b/src/ftinspect/panels/singular.cpp
index 60ffef6..14dbae5 100644
--- a/src/ftinspect/panels/singular.cpp
+++ b/src/ftinspect/panels/singular.cpp
@@ -90,8 +90,8 @@ SingularTab::drawGlyph()
   glyphView_->setBackgroundBrush(
     QColor(engine_->renderingEngine()->background()));
 
-
   applySettings();
+  engine_->loadPalette();
   FT_Glyph glyph = engine_->loadGlyph(currentGlyphIndex_);
   if (glyph)
   {



reply via email to

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