Implement support for rendering color emoji on Windows
This change adds support to the DirectWrite scaler context for detection of color fonts. If it detects a color font and the glyph is a color glyph, it will use DirectWrite's TranslateColorGlyphRun API and generate an image of the glyph that can then be rendered. Chromium tests: https://codereview.chromium.org/2003853002 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1984943002 Review-Url: https://codereview.chromium.org/1984943002
This commit is contained in:
parent
740cc88ee3
commit
c4b091543b
@ -10,6 +10,7 @@
|
||||
|
||||
#undef GetGlyphIndices
|
||||
|
||||
#include "SkDraw.h"
|
||||
#include "SkDWrite.h"
|
||||
#include "SkDWriteGeometrySink.h"
|
||||
#include "SkEndian.h"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "SkOTTable_gasp.h"
|
||||
#include "SkOTTable_maxp.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkRasterClip.h"
|
||||
#include "SkScalerContext.h"
|
||||
#include "SkScalerContext_win_dw.h"
|
||||
#include "SkSharedMutex.h"
|
||||
@ -210,6 +212,14 @@ SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
|
||||
, fTypeface(SkRef(typeface))
|
||||
, fGlyphCount(-1) {
|
||||
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
fTypeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2);
|
||||
|
||||
SkTScopedComPtr<IDWriteFontFace2> fontFace2;
|
||||
fTypeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2);
|
||||
fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont();
|
||||
#endif
|
||||
|
||||
// In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
|
||||
// except when bi-level rendering is requested or there are embedded
|
||||
// bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
|
||||
@ -458,6 +468,47 @@ static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkScalerContext_DW::isColorGlyph(const SkGlyph& glyph) {
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayer;
|
||||
if (getColorGlyphRun(glyph, &colorLayer)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
bool SkScalerContext_DW::getColorGlyphRun(const SkGlyph& glyph,
|
||||
IDWriteColorGlyphRunEnumerator** colorGlyph)
|
||||
{
|
||||
FLOAT advance = 0;
|
||||
UINT16 glyphId = glyph.getGlyphID();
|
||||
|
||||
DWRITE_GLYPH_OFFSET offset;
|
||||
offset.advanceOffset = 0.0f;
|
||||
offset.ascenderOffset = 0.0f;
|
||||
|
||||
DWRITE_GLYPH_RUN run;
|
||||
run.glyphCount = 1;
|
||||
run.glyphAdvances = &advance;
|
||||
run.fontFace = fTypeface->fDWriteFontFace.get();
|
||||
run.fontEmSize = SkScalarToFloat(fTextSizeRender);
|
||||
run.bidiLevel = 0;
|
||||
run.glyphIndices = &glyphId;
|
||||
run.isSideways = FALSE;
|
||||
run.glyphOffsets = &offset;
|
||||
|
||||
HRESULT hr = fFactory2->TranslateColorGlyphRun(
|
||||
0, 0, &run, nullptr, fMeasuringMode, &fXform, 0, colorGlyph);
|
||||
if (hr == DWRITE_E_NOCOLOR) {
|
||||
return false;
|
||||
}
|
||||
HRBM(hr, "Failed to translate color glyph run");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
|
||||
glyph->fWidth = 0;
|
||||
glyph->fHeight = 0;
|
||||
@ -466,6 +517,12 @@ void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
|
||||
|
||||
this->generateAdvance(glyph);
|
||||
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
if (fIsColorFont && isColorGlyph(*glyph)) {
|
||||
glyph->fMaskFormat = SkMask::kARGB32_Format;
|
||||
}
|
||||
#endif
|
||||
|
||||
RECT bbox;
|
||||
HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox),
|
||||
"Requested bounding box could not be determined.");
|
||||
@ -710,6 +767,77 @@ const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
|
||||
return fBits.begin();
|
||||
}
|
||||
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
void SkScalerContext_DW::generateColorGlyphImage(const SkGlyph& glyph) {
|
||||
SkASSERT(isColorGlyph(glyph));
|
||||
SkASSERT(glyph.fMaskFormat == SkMask::Format::kARGB32_Format);
|
||||
|
||||
memset(glyph.fImage, 0, glyph.computeImageSize());
|
||||
|
||||
SkTScopedComPtr<IDWriteColorGlyphRunEnumerator> colorLayers;
|
||||
getColorGlyphRun(glyph, &colorLayers);
|
||||
SkASSERT(colorLayers.get());
|
||||
|
||||
SkMatrix matrix = fSkXform;
|
||||
matrix.postTranslate(-SkIntToScalar(glyph.fLeft), -SkIntToScalar(glyph.fTop));
|
||||
SkRasterClip rc(SkIRect::MakeWH(glyph.fWidth, glyph.fHeight));
|
||||
SkDraw draw;
|
||||
draw.fDst = SkPixmap(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType),
|
||||
glyph.fImage,
|
||||
glyph.ComputeRowBytes(glyph.fWidth, SkMask::Format::kARGB32_Format));
|
||||
draw.fMatrix = &matrix;
|
||||
draw.fRC = &rc;
|
||||
|
||||
SkPaint paint;
|
||||
if (fRenderingMode != DWRITE_RENDERING_MODE_ALIASED) {
|
||||
paint.setFlags(SkPaint::Flags::kAntiAlias_Flag);
|
||||
}
|
||||
|
||||
BOOL hasNextRun = FALSE;
|
||||
while (SUCCEEDED(colorLayers->MoveNext(&hasNextRun)) && hasNextRun) {
|
||||
const DWRITE_COLOR_GLYPH_RUN* colorGlyph;
|
||||
HRVM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run");
|
||||
|
||||
SkColor color;
|
||||
if (colorGlyph->paletteIndex != 0xffff) {
|
||||
color = SkColorSetARGB(SkFloatToIntRound(colorGlyph->runColor.a * 255),
|
||||
SkFloatToIntRound(colorGlyph->runColor.r * 255),
|
||||
SkFloatToIntRound(colorGlyph->runColor.g * 255),
|
||||
SkFloatToIntRound(colorGlyph->runColor.b * 255));
|
||||
} else {
|
||||
// If all components of runColor are 0 or (equivalently) paletteIndex is 0xFFFF then
|
||||
// the 'current brush' is used. fRec.getLuminanceColor() is kinda sorta what is wanted
|
||||
// here, but not really, it will often be the wrong value because it wan't designed for
|
||||
// this.
|
||||
// In practice, I've not encountered a color glyph that uses the current brush color.
|
||||
// If this assert ever fires, we should verify that the color is rendered properly.
|
||||
SkASSERT(false);
|
||||
color = fRec.getLuminanceColor();
|
||||
}
|
||||
paint.setColor(color);
|
||||
|
||||
SkPath path;
|
||||
SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
|
||||
HRVM(SkDWriteGeometrySink::Create(&path, &geometryToPath),
|
||||
"Could not create geometry to path converter.");
|
||||
{
|
||||
Exclusive l(DWriteFactoryMutex);
|
||||
HRVM(colorGlyph->glyphRun.fontFace->GetGlyphRunOutline(
|
||||
colorGlyph->glyphRun.fontEmSize,
|
||||
colorGlyph->glyphRun.glyphIndices,
|
||||
colorGlyph->glyphRun.glyphAdvances,
|
||||
colorGlyph->glyphRun.glyphOffsets,
|
||||
colorGlyph->glyphRun.glyphCount,
|
||||
colorGlyph->glyphRun.isSideways,
|
||||
colorGlyph->glyphRun.bidiLevel % 2, //rtl
|
||||
geometryToPath.get()),
|
||||
"Could not create glyph outline.");
|
||||
}
|
||||
draw.drawPath(path, paint, nullptr, true /* pathIsMutable */);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
|
||||
//Create the mask.
|
||||
DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
|
||||
@ -718,6 +846,14 @@ void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
|
||||
renderingMode = DWRITE_RENDERING_MODE_ALIASED;
|
||||
textureType = DWRITE_TEXTURE_ALIASED_1x1;
|
||||
}
|
||||
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
|
||||
generateColorGlyphImage(glyph);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
|
||||
if (!bits) {
|
||||
sk_bzero(glyph.fImage, glyph.computeImageSize());
|
||||
|
@ -14,6 +14,9 @@
|
||||
#include "SkTypes.h"
|
||||
|
||||
#include <dwrite.h>
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
#include <dwrite_2.h>
|
||||
#endif
|
||||
|
||||
class SkGlyph;
|
||||
class SkDescriptor;
|
||||
@ -42,6 +45,14 @@ private:
|
||||
DWRITE_TEXTURE_TYPE textureType,
|
||||
RECT* bbox);
|
||||
|
||||
bool isColorGlyph(const SkGlyph& glyph);
|
||||
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
bool getColorGlyphRun(const SkGlyph& glyph, IDWriteColorGlyphRunEnumerator** colorGlyph);
|
||||
|
||||
void generateColorGlyphImage(const SkGlyph& glyph);
|
||||
#endif
|
||||
|
||||
SkTDArray<uint8_t> fBits;
|
||||
/** The total matrix without the text height scale. */
|
||||
SkMatrix fSkXform;
|
||||
@ -64,6 +75,10 @@ private:
|
||||
DWRITE_RENDERING_MODE fRenderingMode;
|
||||
DWRITE_TEXTURE_TYPE fTextureType;
|
||||
DWRITE_MEASURING_MODE fMeasuringMode;
|
||||
#if SK_HAS_DWRITE_2_H
|
||||
SkTScopedComPtr<IDWriteFactory2> fFactory2;
|
||||
bool fIsColorFont;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkCommonFlags.h"
|
||||
#include "SkFontMgr.h"
|
||||
#include "SkFontStyle.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkTestScalerContext.h"
|
||||
@ -72,6 +74,9 @@ const char* platform_os_emoji() {
|
||||
if (!strncmp(osName, "Mac", 3)) {
|
||||
return "SBIX";
|
||||
}
|
||||
if (!strncmp(osName, "Win", 3)) {
|
||||
return "COLR";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -82,6 +87,23 @@ sk_sp<SkTypeface> emoji_typeface() {
|
||||
if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX")) {
|
||||
return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle());
|
||||
}
|
||||
if (!strcmp(sk_tool_utils::platform_os_emoji(), "COLR")) {
|
||||
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
|
||||
const char *colorEmojiFontName = "Segoe UI Emoji";
|
||||
sk_sp<SkTypeface> typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle()));
|
||||
if (typeface) {
|
||||
return typeface;
|
||||
}
|
||||
sk_sp<SkTypeface> fallback(fm->matchFamilyStyleCharacter(
|
||||
colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */,
|
||||
0x1f4b0 /* character: 💰 */));
|
||||
if (fallback) {
|
||||
return fallback;
|
||||
}
|
||||
// If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol.
|
||||
// Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji.
|
||||
return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -89,7 +111,9 @@ const char* emoji_sample_text() {
|
||||
if (!strcmp(sk_tool_utils::platform_os_emoji(), "CBDT")) {
|
||||
return "Hamburgefons";
|
||||
}
|
||||
if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX")) {
|
||||
if (!strcmp(sk_tool_utils::platform_os_emoji(), "SBIX") ||
|
||||
!strcmp(sk_tool_utils::platform_os_emoji(), "COLR"))
|
||||
{
|
||||
return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // 💰🏡🎅
|
||||
"\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80" // 🍪🍕🚀
|
||||
"\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // 🚻💩📷
|
||||
|
Loading…
Reference in New Issue
Block a user