From 294c32612d712eb56361ac5439271a91ae96862e Mon Sep 17 00:00:00 2001 From: jvanverth Date: Fri, 10 Oct 2014 11:36:12 -0700 Subject: [PATCH] Fix color emoji. Removes the GrMaskFormat and single atlas in GrTextStrike. Replaces it by storing the GrMaskFormat in each GrGlyph, and doing a lookup for the correct atlas based on that. Disables color glyph rendering in GrDistanceFieldTextContext for now. BUG=skia:2887 Committed: https://skia.googlesource.com/skia/+/bc92163ddfe957ad6ffbb02ac40e0ba75ff82216 Review URL: https://codereview.chromium.org/636183005 --- expectations/gm/ignored-tests.txt | 4 ++ include/gpu/GrFontScaler.h | 3 +- include/gpu/GrGlyph.h | 18 ++++--- src/gpu/GrAtlas.cpp | 3 +- src/gpu/GrBitmapTextContext.cpp | 14 +++--- src/gpu/GrBitmapTextContext.h | 7 +-- src/gpu/GrDistanceFieldTextContext.cpp | 9 +++- src/gpu/GrFontScaler.cpp | 24 +++++++++- src/gpu/GrTextStrike.cpp | 65 ++++++++++++-------------- src/gpu/GrTextStrike.h | 17 +++---- 10 files changed, 101 insertions(+), 63 deletions(-) diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 5793ba217b..27182c904a 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -33,6 +33,10 @@ ## epoger will rebaseline by 25 Dec 2013 #gradtext +# jvanverth +# color emoji fix +coloremoji + # rileya - https://codereview.chromium.org/516463005/ will rebaseline after bots cycle yuv_to_rgb_effect diff --git a/include/gpu/GrFontScaler.h b/include/gpu/GrFontScaler.h index 0376038135..54d1e3f9cc 100644 --- a/include/gpu/GrFontScaler.h +++ b/include/gpu/GrFontScaler.h @@ -66,7 +66,8 @@ public: virtual ~GrFontScaler(); const GrFontDescKey* getKey(); - GrMaskFormat getMaskFormat(); + GrMaskFormat getMaskFormat() const; + GrMaskFormat getPackedGlyphMaskFormat(GrGlyph::PackedID) const; bool getPackedGlyphBounds(GrGlyph::PackedID, SkIRect* bounds); bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height, int rowBytes, void* image); diff --git a/include/gpu/GrGlyph.h b/include/gpu/GrGlyph.h index a379144a42..0e534d694a 100644 --- a/include/gpu/GrGlyph.h +++ b/include/gpu/GrGlyph.h @@ -9,8 +9,10 @@ #define GrGlyph_DEFINED #include "GrRect.h" -#include "SkPath.h" +#include "GrTypes.h" + #include "SkChecksum.h" +#include "SkPath.h" class GrPlot; @@ -23,17 +25,19 @@ class GrPlot; struct GrGlyph { typedef uint32_t PackedID; - GrPlot* fPlot; - SkPath* fPath; - PackedID fPackedID; - GrIRect16 fBounds; - SkIPoint16 fAtlasLocation; + GrPlot* fPlot; + SkPath* fPath; + PackedID fPackedID; + GrMaskFormat fMaskFormat; + GrIRect16 fBounds; + SkIPoint16 fAtlasLocation; - void init(GrGlyph::PackedID packed, const SkIRect& bounds) { + void init(GrGlyph::PackedID packed, const SkIRect& bounds, GrMaskFormat format) { fPlot = NULL; fPath = NULL; fPackedID = packed; fBounds.set(bounds); + fMaskFormat = format; fAtlasLocation.set(0, 0); } diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp index e75f859fec..0491eeda02 100644 --- a/src/gpu/GrAtlas.cpp +++ b/src/gpu/GrAtlas.cpp @@ -216,7 +216,8 @@ GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage, // last one was most recently added and probably most empty for (int i = usage->fPlots.count()-1; i >= 0; --i) { GrPlot* plot = usage->fPlots[i]; - if (plot->addSubImage(width, height, image, loc)) { + // client may have plots from more than one atlas, must check for ours before adding + if (this == plot->fAtlas && plot->addSubImage(width, height, image, loc)) { this->makeMRU(plot); return plot; } diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp index 9478d0436c..7cfe91771f 100755 --- a/src/gpu/GrBitmapTextContext.cpp +++ b/src/gpu/GrBitmapTextContext.cpp @@ -370,7 +370,7 @@ void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, } // try to clear out an unused plot before we flush - if (fContext->getFontCache()->freeUnusedPlot(fStrike) && + if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } @@ -386,7 +386,7 @@ void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, fContext->flush(); // we should have an unused plot now - if (fContext->getFontCache()->freeUnusedPlot(fStrike) && + if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } @@ -422,6 +422,7 @@ HAS_ATLAS: width = SkIntToFixed(width); height = SkIntToFixed(height); + // the current texture/maskformat must match what the glyph needs GrTexture* texture = glyph->fPlot->texture(); SkASSERT(texture); @@ -429,9 +430,10 @@ HAS_ATLAS: this->flush(); fCurrTexture = texture; fCurrTexture->ref(); + fCurrMaskFormat = glyph->fMaskFormat; } - bool useColorVerts = kA8_GrMaskFormat == fStrike->getMaskFormat(); + bool useColorVerts = kA8_GrMaskFormat == fCurrMaskFormat; if (NULL == fVertices) { // If we need to reserve vertices allow the draw target to suggest @@ -549,12 +551,12 @@ void GrBitmapTextContext::flush() { // This effect could be stored with one of the cache objects (atlas?) drawState->setGeometryProcessor(fCachedGeometryProcessor.get()); SkASSERT(fStrike); - switch (fStrike->getMaskFormat()) { + switch (fCurrMaskFormat) { // Color bitmap text case kARGB_GrMaskFormat: SkASSERT(!drawState->hasColorVertexAttribute()); drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); - drawState->setColor(0xffffffff); + drawState->setAlpha(fSkPaint.getAlpha()); break; // LCD text case kA888_GrMaskFormat: @@ -585,7 +587,7 @@ void GrBitmapTextContext::flush() { SkASSERT(drawState->hasColorVertexAttribute()); break; default: - SkFAIL("Unexepected mask format."); + SkFAIL("Unexpected mask format."); } int nGlyphs = fCurrVertex / 4; fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); diff --git a/src/gpu/GrBitmapTextContext.h b/src/gpu/GrBitmapTextContext.h index 4bff399ade..a9805cb0fb 100644 --- a/src/gpu/GrBitmapTextContext.h +++ b/src/gpu/GrBitmapTextContext.h @@ -40,13 +40,14 @@ private: GrTextStrike* fStrike; void* fVertices; - int32_t fMaxVertices; + int fCurrVertex; + int fMaxVertices; + SkRect fVertexBounds; GrTexture* fCurrTexture; + GrMaskFormat fCurrMaskFormat; SkAutoTUnref fCachedGeometryProcessor; // Used to check whether fCachedEffect is still valid. uint32_t fEffectTextureUniqueID; - int fCurrVertex; - SkRect fVertexBounds; void init(const GrPaint&, const SkPaint&); void appendGlyph(GrGlyph::PackedID, SkFixed left, SkFixed top, GrFontScaler*); diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp index baba10d570..a1f5ce6660 100755 --- a/src/gpu/GrDistanceFieldTextContext.cpp +++ b/src/gpu/GrDistanceFieldTextContext.cpp @@ -412,6 +412,11 @@ void GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, return; } + // TODO: support color glyphs + if (kA8_GrMaskFormat != glyph->fMaskFormat) { + return; + } + SkScalar sx = SkFixedToScalar(vx); SkScalar sy = SkFixedToScalar(vy); /* @@ -440,7 +445,7 @@ void GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, } // try to clear out an unused plot before we flush - if (fContext->getFontCache()->freeUnusedPlot(fStrike) && + if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } @@ -456,7 +461,7 @@ void GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, fContext->flush(); // we should have an unused plot now - if (fContext->getFontCache()->freeUnusedPlot(fStrike) && + if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } diff --git a/src/gpu/GrFontScaler.cpp b/src/gpu/GrFontScaler.cpp index 164768f3b0..b16b498ac1 100644 --- a/src/gpu/GrFontScaler.cpp +++ b/src/gpu/GrFontScaler.cpp @@ -59,7 +59,7 @@ GrFontScaler::~GrFontScaler() { SkSafeUnref(fKey); } -GrMaskFormat GrFontScaler::getMaskFormat() { +GrMaskFormat GrFontScaler::getMaskFormat() const { SkMask::Format format = fStrike->getMaskFormat(); switch (format) { case SkMask::kBW_Format: @@ -85,6 +85,28 @@ const GrFontDescKey* GrFontScaler::getKey() { return fKey; } +GrMaskFormat GrFontScaler::getPackedGlyphMaskFormat(GrGlyph::PackedID packed) const { + const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), + GrGlyph::UnpackFixedX(packed), + GrGlyph::UnpackFixedY(packed)); + SkMask::Format format = static_cast(glyph.fMaskFormat); + switch (format) { + case SkMask::kBW_Format: + // fall through to kA8 -- we store BW glyphs in our 8-bit cache + case SkMask::kA8_Format: + return kA8_GrMaskFormat; + case SkMask::kLCD16_Format: + return kA565_GrMaskFormat; + case SkMask::kLCD32_Format: + return kA888_GrMaskFormat; + case SkMask::kARGB32_Format: + return kARGB_GrMaskFormat; + default: + SkDEBUGFAIL("unsupported SkMask::Format"); + return kA8_GrMaskFormat; + } +} + bool GrFontScaler::getPackedGlyphBounds(GrGlyph::PackedID packed, SkIRect* bounds) { const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), GrGlyph::UnpackFixedX(packed), diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp index 8ae10cc8dd..81be3418f7 100644 --- a/src/gpu/GrTextStrike.cpp +++ b/src/gpu/GrTextStrike.cpp @@ -80,20 +80,7 @@ static int mask_format_to_atlas_index(GrMaskFormat format) { } GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) { - GrMaskFormat format = scaler->getMaskFormat(); - GrPixelConfig config = mask_format_to_pixel_config(format); - int atlasIndex = mask_format_to_atlas_index(format); - if (NULL == fAtlases[atlasIndex]) { - SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, - GR_ATLAS_TEXTURE_HEIGHT); - fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags, - textureSize, - GR_NUM_PLOTS_X, - GR_NUM_PLOTS_Y, - true)); - } - GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, - (this, scaler->getKey(), format, fAtlases[atlasIndex])); + GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey())); fCache.add(strike); if (fHead) { @@ -130,10 +117,30 @@ void GrFontCache::purgeStrike(GrTextStrike* strike) { delete strike; } -bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) { + +GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage, + int width, int height, const void* image, + SkIPoint16* loc) { + GrPixelConfig config = mask_format_to_pixel_config(format); + int atlasIndex = mask_format_to_atlas_index(format); + if (NULL == fAtlases[atlasIndex]) { + SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, + GR_ATLAS_TEXTURE_HEIGHT); + fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags, + textureSize, + GR_NUM_PLOTS_X, + GR_NUM_PLOTS_Y, + true)); + } + return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc); +} + + +bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) { SkASSERT(preserveStrike); - GrAtlas* atlas = preserveStrike->fAtlas; + int index = mask_format_to_atlas_index(glyph->fMaskFormat); + GrAtlas* atlas = fAtlases[index]; GrPlot* plot = atlas->getUnusedPlot(); if (NULL == plot) { return false; @@ -141,13 +148,7 @@ bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) { plot->resetRects(); GrTextStrike* strike = fHead; - GrMaskFormat maskFormat = preserveStrike->fMaskFormat; while (strike) { - if (maskFormat != strike->fMaskFormat) { - strike = strike->fNext; - continue; - } - GrTextStrike* strikeToPurge = strike; strike = strikeToPurge->fNext; strikeToPurge->removePlot(plot); @@ -228,16 +229,11 @@ void GrFontCache::dump() const { atlas and a position within that texture. */ -GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key, - GrMaskFormat format, - GrAtlas* atlas) : fPool(64) { +GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key) : fPool(64) { fFontScalerKey = key; fFontScalerKey->ref(); fFontCache = cache; // no need to ref, it won't go away before we do - fAtlas = atlas; // no need to ref, it won't go away before we do - - fMaskFormat = format; #ifdef SK_DEBUG // GrPrintf(" GrTextStrike %p %d\n", this, gCounter); @@ -271,9 +267,10 @@ GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, return NULL; } } - + GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed); + GrGlyph* glyph = fPool.alloc(); - glyph->init(packed, bounds); + glyph->init(packed, bounds, format); fCache.add(glyph); return glyph; } @@ -317,7 +314,7 @@ bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { SkAutoUnref ar(SkSafeRef(scaler)); - int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); + int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat); size_t size = glyph->fBounds.area() * bytesPerPixel; GrAutoMalloc<1024> storage(size); @@ -337,9 +334,9 @@ bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { } } - GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, glyph->width(), - glyph->height(), storage.get(), - &glyph->fAtlasLocation); + GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage, + glyph->width(), glyph->height(), + storage.get(), &glyph->fAtlasLocation); if (NULL == plot) { return false; diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h index 401bd73259..779d676e43 100644 --- a/src/gpu/GrTextStrike.h +++ b/src/gpu/GrTextStrike.h @@ -23,18 +23,16 @@ class GrGpu; class GrFontPurgeListener; /** - * The textcache maps a hostfontscaler instance to a dictionary of + * The textstrike maps a hostfontscaler instance to a dictionary of * glyphid->strike */ class GrTextStrike { public: - GrTextStrike(GrFontCache*, const GrFontDescKey* fontScalerKey, GrMaskFormat, GrAtlas*); + GrTextStrike(GrFontCache*, const GrFontDescKey* fontScalerKey); ~GrTextStrike(); const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; } GrFontCache* getFontCache() const { return fFontCache; } - GrMaskFormat getMaskFormat() const { return fMaskFormat; } - GrTexture* getTexture() const { return fAtlas->getTexture(); } inline GrGlyph* getGlyph(GrGlyph::PackedID, GrFontScaler*); // returns true if glyph (or glyph+padding for distance field) @@ -67,8 +65,6 @@ private: GrTAllocPool fPool; GrFontCache* fFontCache; - GrAtlas* fAtlas; - GrMaskFormat fMaskFormat; bool fUseDistanceField; GrAtlas::ClientPlotUsage fPlotUsage; @@ -85,10 +81,15 @@ public: inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField); + // add to texture atlas that matches this format + GrPlot* addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage, + int width, int height, const void* image, + SkIPoint16* loc); + void freeAll(); - // make an unused plot available - bool freeUnusedPlot(GrTextStrike* preserveStrike); + // make an unused plot available for this glyph + bool freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph); // testing int countStrikes() const { return fCache.count(); } -- 2.34.1