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:
parent
b4f87c81e0
commit
8fd41401f1
@ -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.
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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[],
|
||||
|
Loading…
Reference in New Issue
Block a user