freetype-commit
[Top][All Lists]
Advanced

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

[freetype2-demos] gsoc-2022-chariri-3 7532d1a 33/36: [ftinspect] Support


From: Werner Lemberg
Subject: [freetype2-demos] gsoc-2022-chariri-3 7532d1a 33/36: [ftinspect] Support color layer font.
Date: Wed, 27 Jul 2022 06:32:46 -0400 (EDT)

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

    [ftinspect] Support color layer font.
    
    TODO: After implemented SFNT, allow showing of the palette names.
    
    * src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
      Add fields to hold palettes info and functions to retrieve palette.
      Add options about color layer rendering.
      Add `tryDirectRenderColorLayers` to perform color layer rendering.
    
    * src/ftinspect/engine/stringrenderer.cpp,
      src/ftinspect/engine/stringrenderer.hpp: Support color layered rendering.
    
    * src/ftinspect/rendering/glyphcontinuous.cpp,
      src/ftinspect/rendering/glyphcontinuous.hpp: Add a callback to draw
      pre-converted images.
    
    * src/ftinspect/engine/paletteinfo.cpp,
      src/ftinspect/engine/paletteinfo.hpp: New files. `PaletteInfo` holds
      metadata about palettes.
    
    * src/ftinspect/panels/continuous.cpp: Bugfix: Sync settings before calling
      renderer to reload all glyphs.
    
    * src/ftinspect/panels/settingpanel.cpp,
      src/ftinspect/panels/settingpanel.hpp:
      Add "Enable Color Layer" and "Palette" options. Add code populating
      palettes from the engine, so rename `checkHinting` to a more general
      `onFontChanged`. Minor lifecycle bugfix.
    
    * src/ftinspect/maingui.cpp: Rename `SettingPanel::checkHinting` to
      `onFontChanged`.
    
    * src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
---
 src/ftinspect/CMakeLists.txt                |   1 +
 src/ftinspect/engine/engine.cpp             | 164 ++++++++++++++++++++++++++++
 src/ftinspect/engine/engine.hpp             |  28 ++++-
 src/ftinspect/engine/paletteinfo.cpp        |  27 +++++
 src/ftinspect/engine/paletteinfo.hpp        |  22 ++++
 src/ftinspect/engine/stringrenderer.cpp     | 104 ++++++++++--------
 src/ftinspect/engine/stringrenderer.hpp     |  12 ++
 src/ftinspect/maingui.cpp                   |   2 +-
 src/ftinspect/meson.build                   |   1 +
 src/ftinspect/panels/continuous.cpp         |   1 +
 src/ftinspect/panels/settingpanel.cpp       |  79 +++++++++++++-
 src/ftinspect/panels/settingpanel.hpp       |   9 +-
 src/ftinspect/rendering/glyphcontinuous.cpp |  18 +++
 src/ftinspect/rendering/glyphcontinuous.hpp |   3 +
 14 files changed, 418 insertions(+), 53 deletions(-)

diff --git a/src/ftinspect/CMakeLists.txt b/src/ftinspect/CMakeLists.txt
index e544f7e..aba5c6b 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/charmap.cpp"
+  "engine/paletteinfo.cpp"
   "engine/stringrenderer.cpp"
 
   "rendering/glyphbitmap.cpp"
diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index 4ee99d0..2dd882e 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -343,6 +343,9 @@ Engine::loadFont(int fontIndex,
     ftSize_ = NULL;
     curFamilyName_ = QString();
     curStyleName_ = QString();
+
+    curCharMaps_.clear();
+    curPaletteInfos_.clear();
   }
   else
   {
@@ -362,6 +365,8 @@ Engine::loadFont(int fontIndex,
     curCharMaps_.reserve(face->num_charmaps);
     for (int i = 0; i < face->num_charmaps; i++)
       curCharMaps_.emplace_back(i, face->charmaps[i]);
+
+    loadPaletteInfos();
   }
 
   curNumGlyphs_ = numGlyphs;
@@ -380,6 +385,22 @@ Engine::reloadFont()
 }
 
 
+void
+Engine::loadPalette()
+{
+  palette_ = NULL;
+  if (paletteData_.num_palettes == 0
+      || paletteIndex_ < 0
+      || paletteData_.num_palettes <= paletteIndex_)
+    return;
+
+  FT_Palette_Select(ftSize_->face, 
+                    static_cast<FT_UShort>(paletteIndex_),
+                    &palette_);
+  // XXX error handling
+}
+
+
 void
 Engine::removeFont(int fontIndex, bool closeFile)
 {
@@ -817,6 +838,24 @@ Engine::queryEngine()
 }
 
 
+void
+Engine::loadPaletteInfos()
+{
+  curPaletteInfos_.clear();
+
+  if (FT_Palette_Data_Get(ftSize_->face, &paletteData_))
+  {
+    // XXX Error handling
+    paletteData_.num_palettes = 0;
+    return;
+  }
+
+  curPaletteInfos_.reserve(paletteData_.num_palettes);
+  for (int i = 0; i < paletteData_.num_palettes; ++i)
+    curPaletteInfos_.emplace_back(ftSize_->face, paletteData_, i);
+}
+
+
 void
 convertLCDToARGB(FT_Bitmap& bitmap,
                  QImage& image,
@@ -978,6 +1017,131 @@ Engine::computeGlyphOffset(FT_Glyph glyph, bool inverseY)
 }
 
 
+QImage*
+Engine::tryDirectRenderColorLayers(int glyphIndex,
+                                   QRect* outRect)
+{
+  if (palette_ == NULL 
+      || !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(ftSize_->face,
+                                       glyphIndex,
+                                       &layerGlyphIdx,
+                                       &layerColorIdx,
+                                       &iter);
+  if (!next)
+    return NULL;
+
+  // temporarily change lf
+  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(imageCache_,
+                              &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(library_,
+                        &bitmapGlyph->bitmap, slotOffset,
+                        &bitmap, &bitmapOffset,
+                        color))
+    {
+      // XXX error
+      failed = true;
+      break;
+    }
+  } while (FT_Get_Color_Glyph_Layer(ftSize_->face,
+                                    glyphIndex,
+                                    &layerGlyphIdx,
+                                    &layerColorIdx,
+                                    &iter));
+
+  imageType_.flags = oldLoadFlags;
+  if (failed)
+  {
+    FT_Bitmap_Done(library_, &bitmap);
+    return NULL;
+  }
+
+  auto img = convertBitmapToQImage(&bitmap);
+  if (outRect)
+  {
+    outRect->setSize(img->size());
+    outRect->setLeft(bitmapOffset.x >> 6);
+    outRect->setTop(bitmapOffset.y >> 6);
+  }
+
+  FT_Bitmap_Done(library_, &bitmap);
+
+  return img;
+}
+
+
 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 f4bff2d..a4cf996 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -7,6 +7,7 @@
 
 #include "fontfilemanager.hpp"
 #include "charmap.hpp"
+#include "paletteinfo.hpp"
 
 #include <vector>
 #include <QString>
@@ -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,9 +96,22 @@ public:
                                bool inverseRectY);
   QPoint computeGlyphOffset(FT_Glyph glyph, bool inverseY);
 
+  /*
+   * Directly render the glyph at the specified index
+   * to a `QImage`. If you want to perform color-layer
+   * rendering, call this before trying to load the
+   * glyph and do normal rendering, If the returning
+   * value is non-NULL, then there's no need to
+   * load the glyph the normal way, just draw the `QImage`.
+   * Will return NULL if not enabled or color layers not available.
+   */
+  QImage* tryDirectRenderColorLayers(int glyphIndex,
+                                     QRect* outRect);
+
   // reload current triplet, but with updated settings, useful for updating
   // `ftSize_` only
-  void reloadFont(); 
+  void reloadFont();
+  void loadPalette();
 
   void openFonts(QStringList fontFileNames);
   void removeFont(int fontIndex, bool closeFile = true);
@@ -132,6 +147,7 @@ public:
   FT_Vector currentFontKerning(int glyphIndex, int prevIndex);
   
   std::vector<CharMapInfo>& currentFontCharMaps() { return curCharMaps_; }
+  std::vector<PaletteInfo>& currentFontPalettes() { return curPaletteInfos_; }
   FontFileManager& fontFileManager() { return fontFileManager_; }
   EngineDefaultValues& engineDefaults() { return engineDefaults_; }
   bool antiAliasingEnabled() { return antiAliasingEnabled_; }
@@ -164,6 +180,8 @@ public:
   void setRenderMode(int mode) { renderMode_ = mode; }
   void setAntiAliasingEnabled(bool enabled) { antiAliasingEnabled_ = enabled; }
   void setEmbeddedBitmap(bool force) { embeddedBitmap_ = force; }
+  void setUseColorLayer(bool colorLayer) { useColorLayer_ = colorLayer; }
+  void setPaletteIndex(int index) { paletteIndex_ = index; }
   void setLCDUsesBGR(bool isBGR) { lcdUsesBGR_ = isBGR; }
   void setLCDSubPixelPositioning(bool sp) { lcdSubPixelPositioning_ = sp; }
 
@@ -191,6 +209,7 @@ private:
   QString curStyleName_;
   int curNumGlyphs_ = -1;
   std::vector<CharMapInfo> curCharMaps_;
+  std::vector<PaletteInfo> curPaletteInfos_;
 
   FT_Library library_;
   FTC_Manager cacheManager_;
@@ -199,8 +218,10 @@ private:
   FTC_CMapCache cmapCache_;
 
   FTC_ScalerRec scaler_ = {};
-  FT_Size ftSize_;
   FTC_ImageTypeRec imageType_;
+  FT_Size ftSize_;
+  FT_Palette_Data paletteData_ = {};
+  FT_Color* palette_ = NULL;
 
   EngineDefaultValues engineDefaults_;
 
@@ -219,6 +240,8 @@ private:
   bool doBlueZoneHinting_;
   bool showSegments_;
   bool embeddedBitmap_;
+  bool useColorLayer_;
+  int paletteIndex_ = -1;
   int antiAliasingTarget_;
   bool lcdUsesBGR_;
   bool lcdSubPixelPositioning_;
@@ -228,6 +251,7 @@ private:
   unsigned long loadFlags_;
 
   void queryEngine();
+  void loadPaletteInfos();
 
 public:
 
diff --git a/src/ftinspect/engine/paletteinfo.cpp 
b/src/ftinspect/engine/paletteinfo.cpp
new file mode 100644
index 0000000..a89c894
--- /dev/null
+++ b/src/ftinspect/engine/paletteinfo.cpp
@@ -0,0 +1,27 @@
+// paletteinfo.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "paletteinfo.hpp"
+
+PaletteInfo::PaletteInfo(FT_Face face, 
+                         FT_Palette_Data& data, 
+                         int index)
+: index(index)
+{
+  if (data.palette_name_ids)
+  {
+    auto id = data.palette_name_ids[index];
+    FT_SfntName sname;
+    FT_Get_Sfnt_Name(face, id, &sname);
+    name = "(SFNT no impl)";
+    // TODO: Get SFNT Name: After implemented SFNT names functionality.
+  }
+  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..980263e
--- /dev/null
+++ b/src/ftinspect/engine/paletteinfo.hpp
@@ -0,0 +1,22 @@
+// paletteinfo.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <QString>
+
+#include <freetype/freetype.h>
+#include <freetype/ftcolor.h>
+#include <freetype/ftsnames.h>
+
+struct PaletteInfo
+{
+  int index;
+  QString name;
+
+  PaletteInfo(FT_Face face, FT_Palette_Data& data, int index);
+};
+
+
+// end of paletteinfo.hpp
diff --git a/src/ftinspect/engine/stringrenderer.cpp 
b/src/ftinspect/engine/stringrenderer.cpp
index c0b8500..8d8adbd 100644
--- a/src/ftinspect/engine/stringrenderer.cpp
+++ b/src/ftinspect/engine/stringrenderer.cpp
@@ -150,6 +150,7 @@ void
 StringRenderer::prepareRendering()
 {
   engine_->reloadFont();
+  engine_->loadPalette();
   if (kerningDegree_ != KD_None)
     trackingKerning_ = engine_->currentFontTrackingKerning(kerningDegree_);
   else
@@ -499,70 +500,83 @@ StringRenderer::renderLine(int x,
     if (!ctx.glyph)
       continue;
 
-    // copy the glyph because we're doing manipulation
-    auto error = FT_Glyph_Copy(ctx.glyph, &image);
-    if (error)
-      continue;
+    advance = vertical_ ? ctx.vadvance : ctx.hadvance;
 
-    glyphPreprocessCallback_(&image);
+    QRect rect;
+    QImage* colorLayerImage
+        = engine_->tryDirectRenderColorLayers(ctx.glyphIndex, &rect);
 
-    if (image->format != FT_GLYPH_FORMAT_BITMAP)
+    if (colorLayerImage)
     {
-      if (vertical_)
-        error = FT_Glyph_Transform(image, NULL, &ctx.vvector);
-
-      if (!error)
-      {
-        if (matrixEnabled_)
-          error = FT_Glyph_Transform(image, &matrix_, &pen);
-        else
-          error = FT_Glyph_Transform(image, NULL, &pen);
-      }
-
-      if (error)
-      {
-        FT_Done_Glyph(image);
-        continue;
-      }
+      rect.setX(rect.x() + (pen.x >> 6));
+      rect.setY(height - rect.y() - (pen.y >> 6));
+      renderImageCallback_(colorLayerImage, rect);
     }
     else
     {
-      auto bitmap = reinterpret_cast<FT_BitmapGlyph>(image);
+      // copy the glyph because we're doing manipulation
+      auto error = FT_Glyph_Copy(ctx.glyph, &image);
+      if (error)
+        continue;
+
+      glyphPreprocessCallback_(&image);
 
-      if (vertical_)
+      if (image->format != FT_GLYPH_FORMAT_BITMAP)
       {
-        bitmap->left += (ctx.vvector.x + pen.x) >> 6;
-        bitmap->top += (ctx.vvector.y + pen.y) >> 6;
+        if (vertical_)
+          error = FT_Glyph_Transform(image, NULL, &ctx.vvector);
+
+        if (!error)
+        {
+          if (matrixEnabled_)
+            error = FT_Glyph_Transform(image, &matrix_, &pen);
+          else
+            error = FT_Glyph_Transform(image, NULL, &pen);
+        }
+
+        if (error)
+        {
+          FT_Done_Glyph(image);
+          continue;
+        }
       }
       else
       {
-        bitmap->left += pen.x >> 6;
-        bitmap->top += pen.y >> 6;
+        auto bitmap = reinterpret_cast<FT_BitmapGlyph>(image);
+
+        if (vertical_)
+        {
+          bitmap->left += (ctx.vvector.x + pen.x) >> 6;
+          bitmap->top += (ctx.vvector.y + pen.y) >> 6;
+        }
+        else
+        {
+          bitmap->left += pen.x >> 6;
+          bitmap->top += pen.y >> 6;
+        }
       }
-    }
 
-    advance = vertical_ ? ctx.vadvance : ctx.hadvance;
+      if (matrixEnabled_)
+        FT_Vector_Transform(&advance, &matrix_);
 
-    if (matrixEnabled_)
-      FT_Vector_Transform(&advance, &matrix_);
+      FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_PIXELS, &bbox);
 
-    FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_PIXELS, &bbox);
+      // check bounding box; if it is completely outside the
+      // display surface, we don't need to render it
+      if (bbox.xMax >= 0 
+          && bbox.yMax >= 0
+          && bbox.xMin <= width
+          && bbox.yMin <= height)
+      {
+        FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
+        renderCallback_(image, penPos);
+      }
 
-    // check bounding box; if it is completely outside the
-    // display surface, we don't need to render it
-    if (bbox.xMax >= 0 
-        && bbox.yMax >= 0
-        && bbox.xMin <= width
-        && bbox.yMin <= height)
-    {
-      FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
-      renderCallback_(image, penPos);
+      FT_Done_Glyph(image);
     }
-
+    
     pen.x += advance.x;
     pen.y += advance.y;
-
-    FT_Done_Glyph(image);
   }
 
   return offset + totalCount;
diff --git a/src/ftinspect/engine/stringrenderer.hpp 
b/src/ftinspect/engine/stringrenderer.hpp
index 1e9585b..610b341 100644
--- a/src/ftinspect/engine/stringrenderer.hpp
+++ b/src/ftinspect/engine/stringrenderer.hpp
@@ -62,6 +62,12 @@ public:
    * position.
    */
   using RenderCallback = std::function<void(FT_Glyph, FT_Vector)>;
+  /*
+   * For color layered fonts, this will direct render the QImage for you.
+   * TODO: Remove `RenderCallback` and do QImage creation in this class?
+   * The receiver is responsible for deleteing the QImage.
+   */
+  using RenderImageCallback = std::function<void(QImage*, QRect)>;
   /*
    * The glyph pointer may be replaced. In that case, ownership is transfered
    * to the renderer, and the new glyph will be eventually freed by
@@ -90,6 +96,11 @@ public:
     renderCallback_ = std::move(cb);
   }
   void
+  setImageCallback(RenderImageCallback cb)
+  {
+    renderImageCallback_ = std::move(cb);
+  }
+  void
   setPreprocessCallback(PreprocessCallback cb)
   {
     glyphPreprocessCallback_ = std::move(cb);
@@ -173,6 +184,7 @@ private:
   bool matrixEnabled_ = false;
 
   RenderCallback renderCallback_;
+  RenderImageCallback renderImageCallback_;
   PreprocessCallback glyphPreprocessCallback_;
   LineBeginCallback lineBeginCallback_;
 
diff --git a/src/ftinspect/maingui.cpp b/src/ftinspect/maingui.cpp
index add58e4..93acecf 100644
--- a/src/ftinspect/maingui.cpp
+++ b/src/ftinspect/maingui.cpp
@@ -148,7 +148,7 @@ void
 MainGUI::onTripletChanged()
 {
   auto state = settingPanel_->blockSignals(true);
-  settingPanel_->checkHinting();
+  settingPanel_->onFontChanged();
   settingPanel_->blockSignals(state);
   reloadCurrentTabFont();
 }
diff --git a/src/ftinspect/meson.build b/src/ftinspect/meson.build
index ed9a328..cb6efbc 100644
--- a/src/ftinspect/meson.build
+++ b/src/ftinspect/meson.build
@@ -23,6 +23,7 @@ if qt5_dep.found()
     'engine/engine.cpp',
     'engine/fontfilemanager.cpp',
     'engine/charmap.cpp',
+    'engine/paletteinfo.cpp',
     'engine/stringrenderer.cpp',
 
     'rendering/glyphbitmap.cpp',
diff --git a/src/ftinspect/panels/continuous.cpp 
b/src/ftinspect/panels/continuous.cpp
index 0ccaf7f..be80c57 100644
--- a/src/ftinspect/panels/continuous.cpp
+++ b/src/ftinspect/panels/continuous.cpp
@@ -230,6 +230,7 @@ ContinuousTab::charMapChanged()
   }
   updateLimitIndex();
 
+  syncSettings();
   canvas_->stringRenderer().reloadAll();
   repaintGlyph();
   lastCharMapIndex_ = newIndex;
diff --git a/src/ftinspect/panels/settingpanel.cpp 
b/src/ftinspect/panels/settingpanel.cpp
index 6eeec70..499bd58 100644
--- a/src/ftinspect/panels/settingpanel.cpp
+++ b/src/ftinspect/panels/settingpanel.cpp
@@ -25,14 +25,14 @@ SettingPanel::antiAliasingModeIndex()
 void
 SettingPanel::checkAllSettings()
 {
-  checkHinting();
+  onFontChanged();
   checkAutoHinting();
   checkAntiAliasing();
 }
 
 
 void
-SettingPanel::checkHinting()
+SettingPanel::onFontChanged()
 {
   if (hintingCheckBox_->isChecked())
   {
@@ -76,6 +76,48 @@ SettingPanel::checkHinting()
 
     emit repaintNeeded();
   }
+
+  populatePalettes();
+}
+
+
+void
+SettingPanel::populatePalettes()
+{
+  auto needToReload = false;
+  auto& newPalettes = engine_->currentFontPalettes();
+  if (newPalettes.size() != paletteComboBox_->count())
+    needToReload = true;
+  else
+    for (int i = 0; i < newPalettes.size(); ++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 < newPalettes.size(); ++i)
+      paletteComboBox_->addItem(
+        QString("%1: %2")
+          .arg(i)
+          .arg(newPalettes[i].name),
+        newPalettes[i].name);
+  }
+
+  emit fontReloadNeeded();
 }
 
 
@@ -166,6 +208,14 @@ SettingPanel::checkAntiAliasing()
 }
 
 
+void
+SettingPanel::checkPalette()
+{
+  paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked());
+  emit repaintNeeded();
+}
+
+
 void
 SettingPanel::syncSettings()
 {
@@ -190,6 +240,9 @@ SettingPanel::syncSettings()
   engine_->setGamma(gammaSlider_->value());
 
   engine_->setEmbeddedBitmap(embeddedBitmapCheckBox_->isChecked());
+  engine_->setPaletteIndex(paletteComboBox_->currentIndex());
+
+  engine_->setUseColorLayer(colorLayerCheckBox_->isChecked());
   engine_->setLCDUsesBGR(aaSettings.isBGR);
   engine_->setLCDSubPixelPositioning(
     antiAliasingComboBox_->currentIndex()
@@ -210,12 +263,15 @@ SettingPanel::createConnections()
   connect(lcdFilterComboBox_, 
           QOverload<int>::of(&QComboBox::currentIndexChanged),
           this, &SettingPanel::repaintNeeded);
+  connect(paletteComboBox_,
+          QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+          &SettingPanel::repaintNeeded);
 
   connect(gammaSlider_, &QSlider::valueChanged,
           this, &SettingPanel::repaintNeeded);
   
   connect(hintingCheckBox_, &QCheckBox::clicked,
-          this, &SettingPanel::checkHinting);
+          this, &SettingPanel::onFontChanged);
 
   connect(horizontalHintingCheckBox_, &QCheckBox::clicked,
           this, &SettingPanel::repaintNeeded);
@@ -230,6 +286,8 @@ SettingPanel::createConnections()
           this, &SettingPanel::checkAutoHinting);
   connect(embeddedBitmapCheckBox_, &QCheckBox::clicked,
           this, &SettingPanel::fontReloadNeeded);
+  connect(colorLayerCheckBox_, &QCheckBox::clicked,
+          this, &SettingPanel::checkPalette);
 }
 
 
@@ -252,6 +310,7 @@ SettingPanel::createLayout()
   blueZoneHintingCheckBox_ = new QCheckBox(tr("Blue-Zone Hinting"), this);
   segmentDrawingCheckBox_ = new QCheckBox(tr("Segment Drawing"), this);
   embeddedBitmapCheckBox_ = new QCheckBox(tr("Enable Embedded Bitmap"), this);
+  colorLayerCheckBox_ = new QCheckBox(tr("Enable Color Layer"), this);
 
   antiAliasingLabel_ = new QLabel(tr("Anti-Aliasing"), this);
   antiAliasingLabel_->setAlignment(Qt::AlignRight);
@@ -261,7 +320,7 @@ SettingPanel::createLayout()
   antiAliasingComboBox_->setModel(antiAliasingComboBoxModel_);
   antiAliasingLabel_->setBuddy(antiAliasingComboBox_);
 
-  lcdFilterLabel_ = new QLabel(tr("LCD Filter"));
+  lcdFilterLabel_ = new QLabel(tr("LCD Filter"), this);
   lcdFilterLabel_->setAlignment(Qt::AlignRight);
 
   lcdFilterComboboxModel_ = new LCDFilterComboBoxModel(this);
@@ -269,6 +328,11 @@ SettingPanel::createLayout()
   lcdFilterComboBox_->setModel(lcdFilterComboboxModel_);
   lcdFilterLabel_->setBuddy(lcdFilterComboBox_);
 
+  paletteLabel_ = new QLabel(tr("Palette: "), this);
+
+  paletteComboBox_ = new QComboBox(this);
+  paletteLabel_->setBuddy(paletteComboBox_);
+
   int width;
   // make all labels have the same width
   width = hintingModeLabel_->minimumSizeHint().width();
@@ -327,6 +391,10 @@ SettingPanel::createLayout()
   gammaLayout_->addWidget(gammaLabel_);
   gammaLayout_->addWidget(gammaSlider_);
 
+  paletteLayout_ = new QHBoxLayout;
+  paletteLayout_->addWidget(paletteLabel_);
+  paletteLayout_->addWidget(paletteComboBox_);
+
   generalTabLayout_ = new QVBoxLayout;
   generalTabLayout_->addWidget(hintingCheckBox_);
   generalTabLayout_->addLayout(hintingModeLayout_);
@@ -343,6 +411,8 @@ SettingPanel::createLayout()
   generalTabLayout_->addStretch(1);
   generalTabLayout_->addLayout(gammaLayout_);
   generalTabLayout_->addWidget(embeddedBitmapCheckBox_);
+  generalTabLayout_->addWidget(colorLayerCheckBox_);
+  generalTabLayout_->addLayout(paletteLayout_);
   generalTabLayout_->addSpacing(20); // XXX px
   generalTabLayout_->addStretch(1);
 
@@ -393,6 +463,7 @@ SettingPanel::setDefaults()
   verticalHintingCheckBox_->setChecked(true);
   blueZoneHintingCheckBox_->setChecked(true);
   embeddedBitmapCheckBox_->setChecked(false);
+  colorLayerCheckBox_->setChecked(true);
 
   gammaSlider_->setValue(18); // 1.8
 }
diff --git a/src/ftinspect/panels/settingpanel.hpp 
b/src/ftinspect/panels/settingpanel.hpp
index 9724bde..17a7122 100644
--- a/src/ftinspect/panels/settingpanel.hpp
+++ b/src/ftinspect/panels/settingpanel.hpp
@@ -36,10 +36,11 @@ signals:
 
 public slots:
   void checkAllSettings();
-  void checkHinting();
+  void onFontChanged();
   void checkHintingMode();
   void checkAutoHinting();
   void checkAntiAliasing();
+  void checkPalette();
 
 private:
   Engine* engine_;
@@ -56,6 +57,7 @@ private:
   QLabel* antiAliasingLabel_;
   QLabel* hintingModeLabel_;
   QLabel* lcdFilterLabel_;
+  QLabel* paletteLabel_;
 
   QCheckBox* hintingCheckBox_;
   QCheckBox* horizontalHintingCheckBox_;
@@ -64,6 +66,7 @@ private:
   QCheckBox* segmentDrawingCheckBox_;
   QCheckBox* autoHintingCheckBox_;
   QCheckBox* embeddedBitmapCheckBox_;
+  QCheckBox* colorLayerCheckBox_;
 
   AntiAliasingComboBoxModel* antiAliasingComboBoxModel_;
   HintingModeComboBoxModel* hintingModeComboBoxModel_;
@@ -72,6 +75,7 @@ private:
   QComboBox* hintingModeComboBox_;
   QComboBox* antiAliasingComboBox_;
   QComboBox* lcdFilterComboBox_;
+  QComboBox* paletteComboBox_;
 
   QSlider* gammaSlider_;
 
@@ -84,6 +88,7 @@ private:
   QHBoxLayout* antiAliasingLayout_;
   QHBoxLayout* lcdFilterLayout_;
   QHBoxLayout* gammaLayout_;
+  QHBoxLayout* paletteLayout_;
 
   QVBoxLayout* generalTabLayout_;
 
@@ -92,6 +97,8 @@ private:
   void createConnections();
   void createLayout();
   void setDefaults();
+
+  void populatePalettes();
 };
 
 
diff --git a/src/ftinspect/rendering/glyphcontinuous.cpp 
b/src/ftinspect/rendering/glyphcontinuous.cpp
index 0d7f63b..9497bec 100644
--- a/src/ftinspect/rendering/glyphcontinuous.cpp
+++ b/src/ftinspect/rendering/glyphcontinuous.cpp
@@ -101,6 +101,11 @@ GlyphContinuous::paintByRenderer(QPainter* painter)
     {
       drawSingleGlyph(painter, glyph, penPos);
     });
+  stringRenderer_.setImageCallback(
+    [&](QImage* image, QRect pos)
+    {
+      drawSingleGlyphImage(painter, image, pos);
+    });
   stringRenderer_.setPreprocessCallback(
     [&](FT_Glyph* ptr)
     {
@@ -285,4 +290,17 @@ GlyphContinuous::drawSingleGlyph(QPainter* painter,
 }
 
 
+void
+GlyphContinuous::drawSingleGlyphImage(QPainter* painter,
+                                      QImage* image,
+                                      QRect pos)
+{
+  // TODO red square?
+
+  pos.setLeft(pos.left() + sizeIndicatorOffset_);
+  painter->drawImage(pos, *image);
+  delete image;
+}
+
+
 // end of glyphcontinuous.cpp
diff --git a/src/ftinspect/rendering/glyphcontinuous.hpp 
b/src/ftinspect/rendering/glyphcontinuous.hpp
index 9260b50..109d022 100644
--- a/src/ftinspect/rendering/glyphcontinuous.hpp
+++ b/src/ftinspect/rendering/glyphcontinuous.hpp
@@ -103,6 +103,9 @@ private:
   void drawSingleGlyph(QPainter* painter,
                        FT_Glyph glyph,
                        FT_Vector penPos);
+  void drawSingleGlyphImage(QPainter* painter,
+                            QImage* image,
+                            QRect pos);
 };
 
 



reply via email to

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