FreeType to draw COLR glyphs with drawables.

Bug: skia:12121
Change-Id: I616e1357e9209642b6e323e631f475e83e5c4b79
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/507337
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
Ben Wagner 2022-02-10 17:54:57 -05:00 committed by SkCQ
parent b4f87c81e0
commit 8fd41401f1
8 changed files with 149 additions and 70 deletions

View File

@ -93,7 +93,8 @@ public:
kGlyphMetricsFallback = 4,
kGlyphPathFallback = 5,
kLast = kGlyphPath
kGlyphDrawable = 6,
kLast = kGlyphDrawable
};
// An interface to delete handles that may be pinned by the remote server.

View File

@ -59,6 +59,17 @@ bool SkScalerContextProxy::generatePath(const SkGlyph& glyph, SkPath* path) {
return false;
}
sk_sp<SkDrawable> SkScalerContextProxy::generateDrawable(const SkGlyph&) {
TRACE_EVENT1("skia", "generateDrawable", "rec", TRACE_STR_COPY(this->getRec().dump().c_str()));
if (this->getProxyTypeface()->isLogging()) {
SkDebugf("GlyphCacheMiss generateDrawable: %s\n", this->getRec().dump().c_str());
}
fDiscardableManager->notifyCacheMiss(
SkStrikeClient::CacheMissType::kGlyphDrawable, fRec.fTextSize);
return nullptr;
}
void SkScalerContextProxy::generateFontMetrics(SkFontMetrics* metrics) {
TRACE_EVENT1(
"skia", "generateFontMetrics", "rec", TRACE_STR_COPY(this->getRec().dump().c_str()));

View File

@ -32,6 +32,7 @@ protected:
void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
void generateImage(const SkGlyph& glyph) override;
bool generatePath(const SkGlyph& glyphID, SkPath* path) override;
sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
void generateFontMetrics(SkFontMetrics* metrics) override;
SkTypefaceProxy* getProxyTypeface() const;

View File

@ -8,6 +8,7 @@
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkPath.h"
#include "include/core/SkStream.h"
@ -388,6 +389,7 @@ protected:
void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override;
void generateImage(const SkGlyph& glyph) override;
bool generatePath(const SkGlyph& glyph, SkPath* path) override;
sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
void generateFontMetrics(SkFontMetrics*) override;
private:
@ -1336,6 +1338,32 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
generateGlyphImage(fFace, glyph, *bitmapMatrix);
}
sk_sp<SkDrawable> SkScalerContext_FreeType::generateDrawable(const SkGlyph& glyph) {
// Because FreeType's FT_Face is stateful (not thread safe) and the current design of this
// SkTypeface and SkScalerContext does not work around this, it is necessary lock at least the
// FT_Face when using it (this implementation currently locks the whole FT_Library).
// It should be possible to draw the drawable straight out of the FT_Face. However, this would
// mean locking each time any such drawable is drawn. To avoid locking, this implementation
// creates drawables backed as pictures so that they can be played back later without locking.
SkAutoMutexExclusive ac(f_t_mutex());
if (this->setupSize()) {
sk_bzero(glyph.fImage, glyph.imageSize());
return nullptr;
}
FT_Error err = FT_Load_Glyph(fFace, glyph.getGlyphID(), fLoadGlyphFlags);
if (err != 0) {
SK_TRACEFTR(err, "SkScalerContext_FreeType::generateDrawable: FT_Load_Glyph(glyph:%d "
"width:%d height:%d rb:%zu flags:%d) failed.",
glyph.getGlyphID(), glyph.width(), glyph.height(), glyph.rowBytes(),
fLoadGlyphFlags);
return nullptr;
}
emboldenIfNeeded(fFace, fFace->glyph, glyph.getGlyphID());
return generateGlyphDrawable(fFace, glyph);
}
bool SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, SkPath* path) {
SkASSERT(path);

View File

@ -9,7 +9,9 @@
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkPath.h"
#include "include/core/SkPictureRecorder.h"
#include "include/effects/SkGradientShader.h"
#include "include/private/SkColorData.h"
#include "include/private/SkTo.h"
@ -1234,6 +1236,76 @@ bool colrv1_start_glyph_bounds(SkMatrix *ctm,
} // namespace
#ifdef FT_COLOR_H
bool SkScalerContext_FreeType_Base::drawColorGlyph(SkCanvas* canvas,
FT_Face face,
const SkGlyph& glyph) {
SkPaint paint;
paint.setAntiAlias(true);
FT_Palette_Data palette_data;
FT_Error err = FT_Palette_Data_Get(face, &palette_data);
if (err) {
SK_TRACEFTR(err, "Could not get palette data from %s fontFace.",
face->family_name);
return false;
}
SkAutoTArray<SkColor> originalPalette;
FT_Color* ftPalette;
err = FT_Palette_Select(face, 0, &ftPalette);
if (err) {
SK_TRACEFTR(err, "Could not get palette colors from %s fontFace.",
face->family_name);
return false;
}
originalPalette.reset(palette_data.num_palette_entries);
for (int i = 0; i < palette_data.num_palette_entries; ++i) {
originalPalette[i] = SkColorSetARGB(ftPalette[i].alpha,
ftPalette[i].red,
ftPalette[i].green,
ftPalette[i].blue);
}
SkSpan<SkColor> paletteSpan(originalPalette.data(),
palette_data.num_palette_entries);
// Only attempt to draw a COLRv1 glyph if FreeType is new enough to have the COLRv1 support.
#ifdef TT_SUPPORT_COLRV1
VisitedSet visited_set;
if (colrv1_start_glyph(canvas, paletteSpan,
fRec.fForegroundColor,
face, glyph.getGlyphID(),
FT_COLOR_INCLUDE_ROOT_TRANSFORM,
&visited_set))
{
return true;
}
#endif // TT_SUPPORT_COLRV1
// If we didn't have colr v1 layers, try v0 layers.
bool haveLayers = false;
FT_LayerIterator layerIterator;
layerIterator.p = nullptr;
FT_UInt layerGlyphIndex = 0;
FT_UInt layerColorIndex = 0;
while (FT_Get_Color_Glyph_Layer(face, glyph.getGlyphID(), &layerGlyphIndex,
&layerColorIndex, &layerIterator)) {
haveLayers = true;
if (layerColorIndex == 0xFFFF) {
paint.setColor(fRec.fForegroundColor);
} else {
paint.setColor(paletteSpan[layerColorIndex]);
}
SkPath path;
if (this->generateFacePath(face, layerGlyphIndex, &path)) {
canvas->drawPath(path, paint);
}
}
return haveLayers;
}
#endif // FT_COLOR_H
void SkScalerContext_FreeType_Base::generateGlyphImage(
FT_Face face,
const SkGlyph& glyph,
@ -1280,76 +1352,11 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(
SkFixedToScalar(glyph.getSubYFixed()));
}
SkPaint paint;
paint.setAntiAlias(true);
FT_Palette_Data palette_data;
FT_Error err = FT_Palette_Data_Get(face, &palette_data);
if (err) {
SK_TRACEFTR(err, "Could not get palette data from %s fontFace.",
face->family_name);
return;
}
SkAutoTArray<SkColor> originalPalette;
FT_Color* ftPalette;
err = FT_Palette_Select(face, 0, &ftPalette);
if (err) {
SK_TRACEFTR(err, "Could not get palette colors from %s fontFace.",
face->family_name);
return;
}
originalPalette.reset(palette_data.num_palette_entries);
for (int i = 0; i < palette_data.num_palette_entries; ++i) {
originalPalette[i] = SkColorSetARGB(ftPalette[i].alpha,
ftPalette[i].red,
ftPalette[i].green,
ftPalette[i].blue);
}
SkSpan<SkColor> paletteSpan(originalPalette.data(),
palette_data.num_palette_entries);
FT_Bool haveLayers = false;
#ifdef TT_SUPPORT_COLRV1
// Only attempt to draw COLRv1 glyph is FreeType is new enough
// to have the COLRv1 additions, as indicated by the
// TT_SUPPORT_COLRV1 flag defined by the FreeType headers in
// that case.
VisitedSet visited_set;
haveLayers = colrv1_start_glyph(&canvas, paletteSpan,
fRec.fForegroundColor,
face, glyph.getGlyphID(),
FT_COLOR_INCLUDE_ROOT_TRANSFORM,
&visited_set);
#else
haveLayers = false;
#endif
if (!haveLayers) {
// If we didn't have colr v1 layers, try v0 layers.
FT_LayerIterator layerIterator;
layerIterator.p = nullptr;
FT_UInt layerGlyphIndex = 0;
FT_UInt layerColorIndex = 0;
while (FT_Get_Color_Glyph_Layer(face, glyph.getGlyphID(), &layerGlyphIndex,
&layerColorIndex, &layerIterator)) {
haveLayers = true;
if (layerColorIndex == 0xFFFF) {
paint.setColor(fRec.fForegroundColor);
} else {
paint.setColor(paletteSpan[layerColorIndex]);
}
SkPath path;
if (this->generateFacePath(face, layerGlyphIndex, &path)) {
canvas.drawPath(path, paint);
}
}
}
bool haveLayers = this->drawColorGlyph(&canvas, face, glyph);
if (!haveLayers) {
SK_TRACEFTR(err, "Could not get layers (neither v0, nor v1) from %s fontFace.",
face->family_name);
SkDebugf("Could not get layers (neither v0, nor v1) from %s fontFace.",
face->family_name);
return;
}
} else
@ -1780,3 +1787,18 @@ bool SkScalerContext_FreeType_Base::computeColrV1GlyphBoundingBox(FT_Face face,
return false;
#endif
}
sk_sp<SkDrawable> SkScalerContext_FreeType_Base::generateGlyphDrawable(FT_Face face,
const SkGlyph& glyph) {
#ifdef FT_COLOR_H
if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE && glyph.isColor()) {
SkPictureRecorder recorder;
SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::Make(glyph.mask().fBounds));
if (!this->drawColorGlyph(recordingCanvas, face, glyph)) {
return nullptr;
}
return recorder.finishRecordingAsDrawable();
}
#endif // FT_COLOR_H
return nullptr;
}

View File

@ -47,9 +47,11 @@ protected:
: INHERITED(std::move(typeface), effects, desc)
{}
bool drawColorGlyph(SkCanvas*, FT_Face, const SkGlyph&);
void generateGlyphImage(FT_Face face, const SkGlyph& glyph, const SkMatrix& bitmapTransform);
bool generateGlyphPath(FT_Face face, SkPath* path);
bool generateFacePath(FT_Face face, SkGlyphID glyphID, SkPath* path);
sk_sp<SkDrawable> generateGlyphDrawable(FT_Face face, const SkGlyph&);
// Computes a bounding box for a COLRv1 glyph id in FT_BBox 26.6 format and FreeType's y-up
// coordinate space.

View File

@ -7,6 +7,7 @@
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkPath.h"
#include "src/core/SkAdvancedTypefaceMetrics.h"
#include "src/core/SkGlyph.h"
@ -27,6 +28,7 @@ protected:
void generateMetrics(SkGlyph*, SkArenaAlloc*) override;
void generateImage(const SkGlyph&) override;
bool generatePath(const SkGlyph&, SkPath*) override;
sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override;
void generateFontMetrics(SkFontMetrics*) override;
private:
@ -138,6 +140,14 @@ bool RandomScalerContext::generatePath(const SkGlyph& glyph, SkPath* path) {
return fProxy->generatePath(glyph, path);
}
sk_sp<SkDrawable> RandomScalerContext::generateDrawable(const SkGlyph& glyph) {
SkGlyph* shadowProxyGlyph = fProxyGlyphs.find(glyph.getPackedID());
if (shadowProxyGlyph && shadowProxyGlyph->path()) {
return nullptr;
}
return fProxy->generateDrawable(glyph);
}
void RandomScalerContext::generateFontMetrics(SkFontMetrics* metrics) {
fProxy->getFontMetrics(metrics);
}
@ -213,6 +223,10 @@ void SkRandomTypeface::getPostScriptGlyphNames(SkString* names) const {
return fProxy->getPostScriptGlyphNames(names);
}
bool SkRandomTypeface::onGlyphMaskNeedsCurrentColor() const {
return fProxy->glyphMaskNeedsCurrentColor();
}
int SkRandomTypeface::onGetVariationDesignPosition(
SkFontArguments::VariationPosition::Coordinate coordinates[],
int coordinateCount) const {

View File

@ -43,7 +43,7 @@ protected:
void getPostScriptGlyphNames(SkString*) const override;
bool onGlyphMaskNeedsCurrentColor() const override { return false; }
bool onGlyphMaskNeedsCurrentColor() const override;
int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
int coordinateCount) const override;
int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],