[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);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freetype2-demos] gsoc-2022-chariri-3 cd132d4 15/36: [ftinspect] Let continuous view support non-outline glyphs.,
Werner Lemberg <=