Fix for 510931, merge to m44 BUG=510931 NOTREECHECKS=true NOTRY=true NOPRESUBMIT=true Review URL: https://codereview.chromium.org/1275393003
diff --git a/gm/textblobrandomfont.cpp b/gm/textblobrandomfont.cpp new file mode 100644 index 0000000..a95966e --- /dev/null +++ b/gm/textblobrandomfont.cpp
@@ -0,0 +1,139 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" + +#include "Resources.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkStream.h" +#include "SkSurface.h" +#include "SkTextBlob.h" +#include "SkTypeface.h" +#include "../src/fonts/SkRandomScalerContext.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" + +namespace skiagm { +class TextBlobRandomFont : public GM { +public: + // This gm tests that textblobs can be translated and scaled with a font that returns random + // but deterministic masks + TextBlobRandomFont() { } + +protected: + void onOnceBeforeDraw() override { + SkTextBlobBuilder builder; + + const char* text = "The quick brown fox jumps over the lazy dog."; + + // make textbloben + SkPaint paint; + paint.setTextSize(32); + paint.setLCDRenderText(true); + + // Setup our random scaler context + SkAutoTUnref<SkTypeface> orig(sk_tool_utils::create_portable_typeface("sans-serif", + SkTypeface::kBold)); + if (NULL == orig) { + orig.reset(SkTypeface::RefDefault()); + } + SkAutoTUnref<SkTypeface> random(SkNEW_ARGS(SkRandomTypeface, (orig, paint, false))); + paint.setTypeface(random); + + SkRect bounds; + paint.measureText(text, strlen(text), &bounds); + sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, 0); + + // A8 + const char* bigtext1 = "The quick brown fox"; + const char* bigtext2 = "jumps over the lazy dog."; + paint.setTextSize(160); + paint.setSubpixelText(false); + paint.setLCDRenderText(false); + paint.measureText(bigtext1, strlen(bigtext1), &bounds); + SkScalar offset = bounds.height(); + sk_tool_utils::add_to_text_blob(&builder, bigtext1, paint, 0, offset); + + paint.measureText(bigtext2, strlen(bigtext2), &bounds); + offset += bounds.height(); + sk_tool_utils::add_to_text_blob(&builder, bigtext2, paint, 0, offset); + + // build + fBlob.reset(builder.build()); + } + + SkString onShortName() override { + return SkString("textblobrandomfont"); + } + + SkISize onISize() override { + return SkISize::Make(kWidth, kHeight); + } + + void onDraw(SkCanvas* canvas) override { + // This GM exists to test a specific feature of the GPU backend. + if (NULL == canvas->getGrContext()) { + this->drawGpuOnlyMessage(canvas); + return; + } + + canvas->drawColor(sk_tool_utils::color_to_565(SK_ColorWHITE)); + + SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + SkAutoTUnref<SkSurface> surface(canvas->newSurface(info, &props)); + if (surface) { + SkPaint paint; + paint.setAntiAlias(true); + + SkCanvas* c = surface->getCanvas(); + + int stride = SkScalarCeilToInt(fBlob->bounds().height()); + int yOffset = stride / 8; + for (int i = 0; i < 1; i++) { + // fiddle the canvas to force regen of textblobs + canvas->rotate(i % 2 ? 0.0f : -0.05f); + canvas->drawTextBlob(fBlob, 10.0f, SkIntToScalar(yOffset), paint); + yOffset += stride; + + // This will draw as black boxes + c->drawTextBlob(fBlob, 10, SkIntToScalar(yOffset), paint); + surface->draw(canvas, 0, 0, nullptr); + + // free gpu resources and verify + yOffset += stride; + canvas->getGrContext()->freeGpuResources(); + canvas->drawTextBlob(fBlob, 10, SkIntToScalar(yOffset), paint); + + yOffset += stride; + } + + } else { + const char* text = "This test requires a surface"; + size_t len = strlen(text); + SkPaint paint; + canvas->drawText(text, len, 10, 100, paint); + } + } + +private: + SkAutoTUnref<const SkTextBlob> fBlob; + + static const int kWidth = 2000; + static const int kHeight = 1600; + + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM( return SkNEW(TextBlobRandomFont); ) +} +#endif
diff --git a/gyp/utils.gypi b/gyp/utils.gypi index f382e10..50a2a1a 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi
@@ -133,6 +133,8 @@ #testing '<(skia_src_path)/fonts/SkGScalerContext.cpp', '<(skia_src_path)/fonts/SkGScalerContext.h', + '<(skia_src_path)/fonts/SkRandomScalerContext.cpp', + '<(skia_src_path)/fonts/SkRandomScalerContext.cpp', '<(skia_src_path)/fonts/SkTestScalerContext.cpp', '<(skia_src_path)/fonts/SkTestScalerContext.h', ],
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index 7d8b892..873c882 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h
@@ -367,6 +367,7 @@ friend class SkPDFCIDFont; friend class GrPathRendering; friend class GrGLPathRendering; + friend class SkRandomTypeface; // For debugging /** Retrieve detailed typeface metrics. Used by the PDF backend. @param perGlyphInfo Indicate what glyph specific information (advances,
diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h index 02b3a76..fe5235a 100644 --- a/src/core/SkScalerContext.h +++ b/src/core/SkScalerContext.h
@@ -299,6 +299,8 @@ void forceGenerateImageFromPath() { fGenerateImageFromPath = true; } + void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; } + private: // never null SkAutoTUnref<SkTypeface> fTypeface; @@ -328,6 +330,8 @@ // When there is a filter, previous steps must create a linear mask // and the pre-blend applied as a final step. const SkMaskGamma::PreBlend fPreBlendForFilter; + + friend class SkRandomScalerContext; // For debugging }; #define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c')
diff --git a/src/fonts/SkRandomScalerContext.cpp b/src/fonts/SkRandomScalerContext.cpp new file mode 100644 index 0000000..799e51d --- /dev/null +++ b/src/fonts/SkRandomScalerContext.cpp
@@ -0,0 +1,258 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkRandomScalerContext.h" +#include "SkGlyph.h" +#include "SkPath.h" +#include "SkCanvas.h" +#include "SkRasterizer.h" + +class SkRandomScalerContext : public SkScalerContext { +public: + SkRandomScalerContext(SkRandomTypeface*, const SkDescriptor*, bool fFakeIt); + virtual ~SkRandomScalerContext(); + +protected: + unsigned generateGlyphCount() override; + uint16_t generateCharToGlyph(SkUnichar) override; + void generateAdvance(SkGlyph*) override; + void generateMetrics(SkGlyph*) override; + void generateImage(const SkGlyph&) override; + void generatePath(const SkGlyph&, SkPath*) override; + void generateFontMetrics(SkPaint::FontMetrics*) override; + +private: + SkRandomTypeface* fFace; + SkScalerContext* fProxy; + bool fFakeIt; +}; + +#define STD_SIZE 1 + +#include "SkDescriptor.h" + +SkRandomScalerContext::SkRandomScalerContext(SkRandomTypeface* face, const SkDescriptor* desc, + bool fakeIt) + : SkScalerContext(face, desc) + , fFace(face) + , fFakeIt(fakeIt) { + fProxy = face->proxy()->createScalerContext(desc); +} + +SkRandomScalerContext::~SkRandomScalerContext() { + SkDELETE(fProxy); +} + +unsigned SkRandomScalerContext::generateGlyphCount() { + return fProxy->getGlyphCount(); +} + +uint16_t SkRandomScalerContext::generateCharToGlyph(SkUnichar uni) { + return fProxy->charToGlyphID(uni); +} + +void SkRandomScalerContext::generateAdvance(SkGlyph* glyph) { + fProxy->getAdvance(glyph); +} + +void SkRandomScalerContext::generateMetrics(SkGlyph* glyph) { + // Here we will change the mask format of the glyph + // NOTE this is being overridden by the base class + SkMask::Format format; + switch (glyph->getGlyphID() % 4) { + case 0: + format = SkMask::kLCD16_Format; + break; + case 1: + format = SkMask::kA8_Format; + break; + case 2: + format = SkMask::kARGB32_Format; + break; + case 3: + format = SkMask::kBW_Format; + break; + } + + fProxy->getMetrics(glyph); + + glyph->fMaskFormat = format; + if (fFakeIt) { + return; + } + if (SkMask::kARGB32_Format == format) { + SkPath path; + fProxy->getPath(*glyph, &path); + + SkRect storage; + const SkPaint& paint = fFace->paint(); + const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), + &storage, + SkPaint::kFill_Style); + SkIRect ibounds; + newBounds.roundOut(&ibounds); + glyph->fLeft = ibounds.fLeft; + glyph->fTop = ibounds.fTop; + glyph->fWidth = ibounds.width(); + glyph->fHeight = ibounds.height(); + } else { + SkPath devPath, fillPath; + SkMatrix fillToDevMatrix; + + this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); + + // just use devPath + const SkIRect ir = devPath.getBounds().roundOut(); + + if (ir.isEmpty() || !ir.is16Bit()) { + glyph->fLeft = 0; + glyph->fTop = 0; + glyph->fWidth = 0; + glyph->fHeight = 0; + return; + } + glyph->fLeft = ir.fLeft; + glyph->fTop = ir.fTop; + glyph->fWidth = SkToU16(ir.width()); + glyph->fHeight = SkToU16(ir.height()); + + if (glyph->fWidth > 0) { + switch (glyph->fMaskFormat) { + case SkMask::kLCD16_Format: + glyph->fWidth += 2; + glyph->fLeft -= 1; + break; + default: + break; + } + } + } +} + +void SkRandomScalerContext::generateImage(const SkGlyph& glyph) { + SkMask::Format format = (SkMask::Format)glyph.fMaskFormat; + switch (glyph.getGlyphID() % 4) { + case 0: + format = SkMask::kLCD16_Format; + break; + case 1: + format = SkMask::kA8_Format; + break; + case 2: + format = SkMask::kARGB32_Format; + break; + case 3: + format = SkMask::kBW_Format; + break; + } + const_cast<SkGlyph&>(glyph).fMaskFormat = format; + + // if the format is ARGB, we just draw the glyph from path ourselves. Otherwise, we force + // our proxy context to generate the image from paths. + if (!fFakeIt) { + if (SkMask::kARGB32_Format == glyph.fMaskFormat) { + SkPath path; + fProxy->getPath(glyph, &path); + + SkBitmap bm; + bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight), + glyph.fImage, glyph.rowBytes()); + bm.eraseColor(0); + + SkCanvas canvas(bm); + canvas.translate(-SkIntToScalar(glyph.fLeft), + -SkIntToScalar(glyph.fTop)); + canvas.drawPath(path, fFace->paint()); + } else { + fProxy->forceGenerateImageFromPath(); + fProxy->getImage(glyph); + fProxy->forceOffGenerateImageFromPath(); + } + } else { + sk_bzero(glyph.fImage, glyph.computeImageSize()); + } +} + +void SkRandomScalerContext::generatePath(const SkGlyph& glyph, SkPath* path) { + fProxy->getPath(glyph, path); +} + +void SkRandomScalerContext::generateFontMetrics(SkPaint::FontMetrics* metrics) { + fProxy->getFontMetrics(metrics); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTypefaceCache.h" + +SkRandomTypeface::SkRandomTypeface(SkTypeface* proxy, const SkPaint& paint, bool fakeIt) + : SkTypeface(proxy->fontStyle(), SkTypefaceCache::NewFontID(), false) + , fProxy(SkRef(proxy)) + , fPaint(paint) + , fFakeIt(fakeIt) {} + +SkRandomTypeface::~SkRandomTypeface() { + fProxy->unref(); +} + +SkScalerContext* SkRandomTypeface::onCreateScalerContext( + const SkDescriptor* desc) const { + return SkNEW_ARGS(SkRandomScalerContext, (const_cast<SkRandomTypeface*>(this), desc, fFakeIt)); +} + +void SkRandomTypeface::onFilterRec(SkScalerContextRec* rec) const { + fProxy->filterRec(rec); + rec->setHinting(SkPaint::kNo_Hinting); + rec->fMaskFormat = SkMask::kARGB32_Format; +} + +SkAdvancedTypefaceMetrics* SkRandomTypeface::onGetAdvancedTypefaceMetrics( + PerGlyphInfo info, + const uint32_t* glyphIDs, + uint32_t glyphIDsCount) const { + return fProxy->getAdvancedTypefaceMetrics(info, glyphIDs, glyphIDsCount); +} + +SkStreamAsset* SkRandomTypeface::onOpenStream(int* ttcIndex) const { + return fProxy->openStream(ttcIndex); +} + +void SkRandomTypeface::onGetFontDescriptor(SkFontDescriptor* desc, + bool* isLocal) const { + fProxy->getFontDescriptor(desc, isLocal); +} + +int SkRandomTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount); +} + +int SkRandomTypeface::onCountGlyphs() const { + return fProxy->countGlyphs(); +} + +int SkRandomTypeface::onGetUPEM() const { + return fProxy->getUnitsPerEm(); +} + +void SkRandomTypeface::onGetFamilyName(SkString* familyName) const { + fProxy->getFamilyName(familyName); +} + +SkTypeface::LocalizedStrings* SkRandomTypeface::onCreateFamilyNameIterator() const { + return fProxy->createFamilyNameIterator(); +} + +int SkRandomTypeface::onGetTableTags(SkFontTableTag tags[]) const { + return fProxy->getTableTags(tags); +} + +size_t SkRandomTypeface::onGetTableData(SkFontTableTag tag, size_t offset, + size_t length, void* data) const { + return fProxy->getTableData(tag, offset, length, data); +} +
diff --git a/src/fonts/SkRandomScalerContext.h b/src/fonts/SkRandomScalerContext.h new file mode 100644 index 0000000..24b203f --- /dev/null +++ b/src/fonts/SkRandomScalerContext.h
@@ -0,0 +1,55 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRandomScalerContext_DEFINED +#define SkRandomScalerContext_DEFINED + +#include "SkScalerContext.h" +#include "SkTypeface.h" + +/* + * This scaler context is for debug only purposes. It will 'randomly' but deterministically return + * LCD / A8 / BW / RBGA masks based off of the Glyph ID + */ + +class SkRandomTypeface : public SkTypeface { +public: + SkRandomTypeface(SkTypeface* proxy, const SkPaint&, bool fakeit); + virtual ~SkRandomTypeface(); + + SkTypeface* proxy() const { return fProxy; } + const SkPaint& paint() const { return fPaint; } + +protected: + SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override; + void onFilterRec(SkScalerContextRec*) const override; + SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics( + PerGlyphInfo, + const uint32_t* glyphIDs, + uint32_t glyphIDsCount) const override; + SkStreamAsset* onOpenStream(int* ttcIndex) const override; + void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override; + + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; + int onCountGlyphs() const override; + int onGetUPEM() const override; + + void onGetFamilyName(SkString* familyName) const override; + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + + int onGetTableTags(SkFontTableTag tags[]) const override; + size_t onGetTableData(SkFontTableTag, size_t offset, + size_t length, void* data) const override; + +private: + SkTypeface* fProxy; + SkPaint fPaint; + bool fFakeIt; +}; + +#endif
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp index b562014..bc015ea 100644 --- a/src/gpu/GrAtlasTextContext.cpp +++ b/src/gpu/GrAtlasTextContext.cpp
@@ -875,10 +875,7 @@ if (glyph.fWidth) { this->bmpAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kCoverage_MaskStyle), + glyph, Sk48Dot16FloorToInt(fx), Sk48Dot16FloorToInt(fy), color, @@ -945,10 +942,7 @@ if (glyph.fWidth) { this->bmpAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kCoverage_MaskStyle), + glyph, Sk48Dot16FloorToInt(fx), Sk48Dot16FloorToInt(fy), color, @@ -983,10 +977,7 @@ this->bmpAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kCoverage_MaskStyle), + glyph, Sk48Dot16FloorToInt(fx), Sk48Dot16FloorToInt(fy), color, @@ -1011,10 +1002,7 @@ Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY; this->bmpAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kCoverage_MaskStyle), + glyph, Sk48Dot16FloorToInt(fx), Sk48Dot16FloorToInt(fy), color, @@ -1039,10 +1027,7 @@ Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY; this->bmpAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kCoverage_MaskStyle), + glyph, Sk48Dot16FloorToInt(fx), Sk48Dot16FloorToInt(fy), color, @@ -1168,10 +1153,7 @@ if (!this->dfAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kDistance_MaskStyle), + glyph, x, y, color, fontScaler, clipRect, textRatio, viewMatrix)) { // couldn't append, send to fallback @@ -1201,10 +1183,7 @@ if (!this->dfAppendGlyph(blob, runIndex, - GrGlyph::Pack(glyph.getGlyphID(), - glyph.getSubXFixed(), - glyph.getSubYFixed(), - GrGlyph::kDistance_MaskStyle), + glyph, x - advanceX, y - advanceY, color, fontScaler, clipRect, @@ -1224,16 +1203,19 @@ } void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex, - GrGlyph::PackedID packed, + const SkGlyph& skGlyph, int vx, int vy, GrColor color, GrFontScaler* scaler, const SkIRect& clipRect) { Run& run = blob->fRuns[runIndex]; if (!fCurrStrike) { fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); - run.fStrike.reset(SkRef(fCurrStrike)); } - GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler); + GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), + skGlyph.getSubXFixed(), + skGlyph.getSubYFixed(), + GrGlyph::kCoverage_MaskStyle); + GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler); if (!glyph) { return; } @@ -1259,7 +1241,7 @@ // If the glyph is too large we fall back to paths if (glyph->fTooLargeForAtlas) { - this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy)); + this->appendGlyphPath(blob, glyph, scaler, skGlyph, SkIntToScalar(vx), SkIntToScalar(vy)); return; } @@ -1267,7 +1249,10 @@ PerSubRunInfo* subRun = &run.fSubRunInfo.back(); if (run.fInitialized && subRun->fMaskFormat != format) { - subRun = &run.fSubRunInfo.push_back(); + subRun = &run.push_back(); + subRun->fStrike.reset(SkRef(fCurrStrike)); + } else if (!run.fInitialized) { + subRun->fStrike.reset(SkRef(fCurrStrike)); } run.fInitialized = true; @@ -1285,7 +1270,7 @@ } bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex, - GrGlyph::PackedID packed, + const SkGlyph& skGlyph, SkScalar sx, SkScalar sy, GrColor color, GrFontScaler* scaler, const SkIRect& clipRect, @@ -1293,10 +1278,13 @@ Run& run = blob->fRuns[runIndex]; if (!fCurrStrike) { fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); - run.fStrike.reset(SkRef(fCurrStrike)); } - GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler); + GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), + skGlyph.getSubXFixed(), + skGlyph.getSubYFixed(), + GrGlyph::kDistance_MaskStyle); + GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler); if (!glyph) { return true; } @@ -1335,11 +1323,15 @@ // TODO combine with the above // If the glyph is too large we fall back to paths if (glyph->fTooLargeForAtlas) { - this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy); + this->appendGlyphPath(blob, glyph, scaler, skGlyph, sx - dx, sy - dy); return true; } PerSubRunInfo* subRun = &run.fSubRunInfo.back(); + if (!run.fInitialized) { + subRun->fStrike.reset(SkRef(fCurrStrike)); + } + run.fInitialized = true; SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat); subRun->fMaskFormat = kA8_GrMaskFormat; @@ -1352,17 +1344,16 @@ } inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph, - GrFontScaler* scaler, SkScalar x, SkScalar y) { + GrFontScaler* scaler, const SkGlyph& skGlyph, + SkScalar x, SkScalar y) { if (NULL == glyph->fPath) { - SkPath* path = SkNEW(SkPath); - if (!scaler->getGlyphPath(glyph->glyphID(), path)) { - // flag the glyph as being dead? - SkDELETE(path); + const SkPath* glyphPath = scaler->getGlyphPath(skGlyph); + if (!glyphPath) { return; } - glyph->fPath = path; + + glyph->fPath = SkNEW_ARGS(SkPath, (*glyphPath)); } - SkASSERT(glyph->fPath); blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y)); } @@ -1585,7 +1576,7 @@ uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat); bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen || - run.fStrike->isAbandoned(); + info.fStrike->isAbandoned(); bool regenerateColors; if (fUseDistanceFields) { regenerateColors = !fUseLCDText && run.fColor != args.fColor; @@ -1633,41 +1624,46 @@ desc = newDesc; cache = SkGlyphCache::DetachCache(run.fTypeface, desc); scaler = GrTextContext::GetGrFontScaler(cache); - strike = run.fStrike; + strike = info.fStrike; typeface = run.fTypeface; } - if (run.fStrike->isAbandoned()) { + if (info.fStrike->isAbandoned()) { regenerateGlyphs = true; strike = fFontCache->getStrike(scaler); } else { - strike = run.fStrike; + strike = info.fStrike; } } for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { if (regenerateTextureCoords) { size_t glyphOffset = glyphIdx + info.fGlyphStartIndex; - GrGlyph* glyph; + + GrGlyph* glyph = blob->fGlyphs[glyphOffset]; + GrGlyph::PackedID id = glyph->fPackedID; + const SkGlyph& skGlyph = scaler->grToSkGlyph(id); if (regenerateGlyphs) { // Get the id from the old glyph, and use the new strike to lookup // the glyph. - glyph = blob->fGlyphs[glyphOffset]; - blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID, + blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, id, fMaskFormat, scaler); } glyph = blob->fGlyphs[glyphOffset]; SkASSERT(glyph); if (!fFontCache->hasGlyph(glyph) && - !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) { + !strike->addGlyphToAtlas(batchTarget, glyph, scaler, skGlyph, + fMaskFormat)) { this->flush(batchTarget, &flushInfo); this->initDraw(batchTarget, gp, pipeline); brokenRun = glyphIdx > 0; SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget, glyph, - scaler); + scaler, + skGlyph, + fMaskFormat); SkASSERT(success); } fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph, @@ -1705,7 +1701,7 @@ run.fColor = args.fColor; if (regenerateTextureCoords) { if (regenerateGlyphs) { - run.fStrike.reset(SkRef(strike)); + info.fStrike.reset(SkRef(strike)); } info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration : fFontCache->atlasGeneration(fMaskFormat);
diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h index eb7ba47..83ffa0e 100644 --- a/src/gpu/GrAtlasTextContext.h +++ b/src/gpu/GrAtlasTextContext.h
@@ -109,6 +109,19 @@ , fGlyphStartIndex(0) , fGlyphEndIndex(0) , fDrawAsDistanceFields(false) {} + SubRunInfo(const SubRunInfo& that) + : fBulkUseToken(that.fBulkUseToken) + , fStrike(SkSafeRef(that.fStrike.get())) + , fAtlasGeneration(that.fAtlasGeneration) + , fVertexStartIndex(that.fVertexStartIndex) + , fVertexEndIndex(that.fVertexEndIndex) + , fGlyphStartIndex(that.fGlyphStartIndex) + , fGlyphEndIndex(that.fGlyphEndIndex) + , fTextRatio(that.fTextRatio) + , fMaskFormat(that.fMaskFormat) + , fDrawAsDistanceFields(that.fDrawAsDistanceFields) + , fUseLCDText(that.fUseLCDText) { + } // Distance field text cannot draw coloremoji, and so has to fall back. However, // though the distance field text and the coloremoji may share the same run, they // will have different descriptors. If fOverrideDescriptor is non-NULL, then it @@ -117,6 +130,7 @@ // significantly, and then the subrun could just have a refed pointer to the // correct descriptor. GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken; + SkAutoTUnref<GrBatchTextStrike> fStrike; uint64_t fAtlasGeneration; size_t fVertexStartIndex; size_t fVertexEndIndex; @@ -130,8 +144,9 @@ SubRunInfo& push_back() { // Forward glyph / vertex information to seed the new sub run - SubRunInfo& prevSubRun = fSubRunInfo.back(); SubRunInfo& newSubRun = fSubRunInfo.push_back(); + SubRunInfo& prevSubRun = fSubRunInfo.fromBack(1); + newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex; newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex; @@ -140,7 +155,6 @@ return newSubRun; } static const int kMinSubRuns = 1; - SkAutoTUnref<GrBatchTextStrike> fStrike; SkAutoTUnref<SkTypeface> fTypeface; SkRect fVertexBounds; SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo; @@ -259,13 +273,13 @@ BitmapTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint, const SkMatrix& viewMatrix, SkGlyphCache** cache, SkPaint* dfPaint, SkScalar* textRatio); - void bmpAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top, + void bmpAppendGlyph(BitmapTextBlob*, int runIndex, const SkGlyph&, int left, int top, GrColor color, GrFontScaler*, const SkIRect& clipRect); - bool dfAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, SkScalar sx, SkScalar sy, + bool dfAppendGlyph(BitmapTextBlob*, int runIndex, const SkGlyph&, SkScalar sx, SkScalar sy, GrColor color, GrFontScaler*, const SkIRect& clipRect, SkScalar textRatio, const SkMatrix& viewMatrix); - inline void appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph, - GrFontScaler* scaler, SkScalar x, SkScalar y); + inline void appendGlyphPath(BitmapTextBlob*, GrGlyph*, GrFontScaler*, const SkGlyph&, + SkScalar x, SkScalar y); inline void appendGlyphCommon(BitmapTextBlob*, Run*, Run::SubRunInfo*, const SkRect& positions, GrColor color, size_t vertexStride, bool useVertexColor,
diff --git a/src/gpu/GrBatchAtlas.cpp b/src/gpu/GrBatchAtlas.cpp index 20e73e4..7d84757 100644 --- a/src/gpu/GrBatchAtlas.cpp +++ b/src/gpu/GrBatchAtlas.cpp
@@ -17,9 +17,9 @@ loc->fY += offset.fY; } -static GrBatchAtlas::AtlasID create_id(int index, int generation) { - // Generation ID can roll over because we only check for equality +static GrBatchAtlas::AtlasID create_id(uint32_t index, uint64_t generation) { SkASSERT(index < (1 << 16)); + SkASSERT(generation < ((uint64_t)1 << 48)); return generation << 16 | index; } @@ -40,8 +40,8 @@ // monotonically incrementing number which is bumped every time the cpu backing store is // wiped, or when the plot itself is evicted from the atlas(ie, there is continuity in genID() // across atlas spills) - int index() const { return fIndex; } - int genID() const { return fGenID; } + uint32_t index() const { return fIndex; } + uint64_t genID() const { return fGenID; } GrBatchAtlas::AtlasID id() { return fID; } GrTexture* texture() const { return fTexture; } @@ -121,8 +121,8 @@ SkDEBUGCODE(fDirty = false;) } - int x() const { return fX; } - int y() const { return fY; } + uint32_t x() const { return fX; } + uint32_t y() const { return fY; } private: BatchPlot() @@ -153,7 +153,7 @@ delete fRects; } - void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint32_t generation, + void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint64_t generation, int offX, int offY, int width, int height, size_t bpp) { fIndex = index; fGenID = generation; @@ -176,13 +176,13 @@ BatchToken fLastUse; uint32_t fIndex; - uint32_t fGenID; + uint64_t fGenID; GrBatchAtlas::AtlasID fID; unsigned char* fData; - int fWidth; - int fHeight; - int fX; - int fY; + uint32_t fWidth; + uint32_t fHeight; + uint32_t fX; + uint32_t fY; GrTexture* fTexture; GrRectanizer* fRects; GrBatchAtlas* fAtlas; @@ -226,8 +226,8 @@ , fPlotHeight(texture->height() / numPlotsY) , fAtlasGeneration(kInvalidAtlasGeneration + 1) { SkASSERT(fNumPlotsX * fNumPlotsY <= BulkUseTokenUpdater::kMaxPlots); - SkASSERT(fPlotWidth * fNumPlotsX == texture->width()); - SkASSERT(fPlotHeight * fNumPlotsY == texture->height()); + SkASSERT(fPlotWidth * fNumPlotsX == static_cast<uint32_t>(texture->width())); + SkASSERT(fPlotHeight * fNumPlotsY == static_cast<uint32_t>(texture->height())); // We currently do not support compressed atlases... SkASSERT(!GrPixelConfigIsCompressed(texture->desc().fConfig)); @@ -239,7 +239,7 @@ SkAutoTUnref<BatchPlot>* currPlot = fPlotArray; for (int y = fNumPlotsY - 1, r = 0; y >= 0; --y, ++r) { for (int x = fNumPlotsX - 1, c = 0; x >= 0; --x, ++c) { - int id = r * fNumPlotsX + c; + uint32_t id = r * fNumPlotsX + c; currPlot->reset(SkNEW(BatchPlot)); (*currPlot)->init(this, texture, id, 1, x, y, fPlotWidth, fPlotHeight, fBPP); @@ -287,7 +287,9 @@ bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget, int width, int height, const void* image, SkIPoint16* loc) { // We should already have a texture, TODO clean this up - SkASSERT(fTexture && width <= fPlotWidth && height <= fPlotHeight); + SkASSERT(fTexture && + static_cast<uint32_t>(width) <= fPlotWidth && + static_cast<uint32_t>(height) <= fPlotHeight); // now look through all allocated plots for one we can share, in Most Recently Refed order GrBatchPlotList::Iter plotIter; @@ -331,7 +333,7 @@ int index = plot->index(); int x = plot->x(); int y = plot->y(); - int generation = plot->genID(); + uint64_t generation = plot->genID(); this->processEviction(plot->id()); fPlotList.remove(plot); @@ -352,14 +354,14 @@ } bool GrBatchAtlas::hasID(AtlasID id) { - int index = GetIndexFromID(id); + uint32_t index = GetIndexFromID(id); SkASSERT(index < fNumPlotsX * fNumPlotsY); return fPlotArray[index]->genID() == GetGenerationFromID(id); } void GrBatchAtlas::setLastUseToken(AtlasID id, BatchToken batchToken) { SkASSERT(this->hasID(id)); - int index = GetIndexFromID(id); + uint32_t index = GetIndexFromID(id); SkASSERT(index < fNumPlotsX * fNumPlotsY); this->makeMRU(fPlotArray[index]); fPlotArray[index]->setLastUseToken(batchToken);
diff --git a/src/gpu/GrBatchAtlas.h b/src/gpu/GrBatchAtlas.h index 83ef5ec..4d21394 100644 --- a/src/gpu/GrBatchAtlas.h +++ b/src/gpu/GrBatchAtlas.h
@@ -24,7 +24,7 @@ typedef uint64_t BatchToken; // An AtlasID is an opaque handle which callers can use to determine if the atlas contains // a specific piece of data - typedef uint32_t AtlasID; + typedef uint64_t AtlasID; static const uint32_t kInvalidAtlasID = 0; static const uint64_t kInvalidAtlasGeneration = 0; @@ -67,6 +67,11 @@ class BulkUseTokenUpdater { public: BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {} + BulkUseTokenUpdater(const BulkUseTokenUpdater& that) + : fPlotsToUpdate(that.fPlotsToUpdate) + , fPlotAlreadyUpdated(that.fPlotAlreadyUpdated) { + } + void add(AtlasID id) { int index = GrBatchAtlas::GetIndexFromID(id); if (!this->find(index)) { @@ -107,12 +112,13 @@ } private: - static int GetIndexFromID(AtlasID id) { + static uint32_t GetIndexFromID(AtlasID id) { return id & 0xffff; } - static int GetGenerationFromID(AtlasID id) { - return (id >> 16) & 0xffff; + // top 48 bits are reserved for the generation ID + static uint64_t GetGenerationFromID(AtlasID id) { + return (id >> 16) & 0xffffffffffff; } inline void updatePlot(GrBatchTarget*, AtlasID*, BatchPlot*); @@ -122,10 +128,10 @@ inline void processEviction(AtlasID); GrTexture* fTexture; - int fNumPlotsX; - int fNumPlotsY; - int fPlotWidth; - int fPlotHeight; + uint32_t fNumPlotsX; + uint32_t fNumPlotsY; + uint32_t fPlotWidth; + uint32_t fPlotHeight; size_t fBPP; uint64_t fAtlasGeneration;
diff --git a/src/gpu/GrBatchFontCache.cpp b/src/gpu/GrBatchFontCache.cpp index 2d6213c..007f18a 100644 --- a/src/gpu/GrBatchFontCache.cpp +++ b/src/gpu/GrBatchFontCache.cpp
@@ -173,20 +173,20 @@ } } -GrGlyph* GrBatchTextStrike::generateGlyph(GrGlyph::PackedID packed, +GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed, GrFontScaler* scaler) { SkIRect bounds; if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) { - if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) { + if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) { return NULL; } } else { - if (!scaler->getPackedGlyphBounds(packed, &bounds)) { + if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) { return NULL; } } - GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed); - + GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph); + GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW); glyph->init(packed, bounds, format); fCache.add(glyph); @@ -206,7 +206,8 @@ } bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph, - GrFontScaler* scaler) { + GrFontScaler* scaler, const SkGlyph& skGlyph, + GrMaskFormat expectedMaskFormat) { SkASSERT(glyph); SkASSERT(scaler); SkASSERT(fCache.find(glyph->fPackedID)); @@ -214,27 +215,25 @@ SkAutoUnref ar(SkSafeRef(scaler)); - int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat); + int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat); size_t size = glyph->fBounds.area() * bytesPerPixel; GrAutoMalloc<1024> storage(size); if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) { - if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(), - glyph->height(), + if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(), storage.get())) { return false; } } else { - if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), - glyph->height(), - glyph->width() * bytesPerPixel, + if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(), + glyph->width() * bytesPerPixel, expectedMaskFormat, storage.get())) { return false; } } - bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, glyph->fMaskFormat, + bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, expectedMaskFormat, glyph->width(), glyph->height(), storage.get(), &glyph->fAtlasLocation); if (success) {
diff --git a/src/gpu/GrBatchFontCache.h b/src/gpu/GrBatchFontCache.h index f1c56ac..f6617d0 100644 --- a/src/gpu/GrBatchFontCache.h +++ b/src/gpu/GrBatchFontCache.h
@@ -12,6 +12,7 @@ #include "GrDrawTarget.h" #include "GrFontScaler.h" #include "GrGlyph.h" +#include "SkGlyph.h" #include "SkTDynamicHash.h" #include "SkVarAlloc.h" @@ -32,16 +33,36 @@ const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; } GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; } - inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) { + inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed, + GrFontScaler* scaler) { GrGlyph* glyph = fCache.find(packed); if (NULL == glyph) { - glyph = this->generateGlyph(packed, scaler); + glyph = this->generateGlyph(skGlyph, packed, scaler); } return glyph; } - // returns true if glyph successfully added to texture atlas, false otherwise - bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*); + // This variant of the above function is called by TextBatch. At this point, it is possible + // that the maskformat of the glyph differs from what we expect. In these cases we will just + // draw a clear square. + // skbug:4143 crbug:510931 + inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed, + GrMaskFormat expectedMaskFormat, GrFontScaler* scaler) { + GrGlyph* glyph = fCache.find(packed); + if (NULL == glyph) { + glyph = this->generateGlyph(skGlyph, packed, scaler); + glyph->fMaskFormat = expectedMaskFormat; + } + return glyph; + } + + // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's + // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never + // happen. + // TODO we can handle some of these cases if we really want to, but the long term solution is to + // get the actual glyph image itself when we get the glyph metrics. + bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*, const SkGlyph&, + GrMaskFormat expectedMaskFormat); // testing int countGlyphs() const { return fCache.count(); } @@ -68,7 +89,7 @@ int fAtlasedGlyphs; bool fIsAbandoned; - GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler); + GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, GrFontScaler*); friend class GrBatchFontCache; };
diff --git a/src/gpu/GrFontScaler.cpp b/src/gpu/GrFontScaler.cpp index ed1970e..1d228a4 100644 --- a/src/gpu/GrFontScaler.cpp +++ b/src/gpu/GrFontScaler.cpp
@@ -83,10 +83,7 @@ return fKey; } -GrMaskFormat GrFontScaler::getPackedGlyphMaskFormat(GrGlyph::PackedID packed) const { - const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), - GrGlyph::UnpackFixedX(packed), - GrGlyph::UnpackFixedY(packed)); +GrMaskFormat GrFontScaler::getPackedGlyphMaskFormat(const SkGlyph& glyph) const { SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); switch (format) { case SkMask::kBW_Format: @@ -103,19 +100,23 @@ } } -bool GrFontScaler::getPackedGlyphBounds(GrGlyph::PackedID packed, SkIRect* bounds) { - const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), - GrGlyph::UnpackFixedX(packed), - GrGlyph::UnpackFixedY(packed)); +bool GrFontScaler::getPackedGlyphBounds(const SkGlyph& glyph, SkIRect* bounds) { +#if 1 + // crbug:510931 + // Retrieving the image from the cache can actually change the mask format. + fStrike->findImage(glyph); +#endif bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight); return true; } -bool GrFontScaler::getPackedGlyphDFBounds(GrGlyph::PackedID packed, SkIRect* bounds) { - const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), - GrGlyph::UnpackFixedX(packed), - GrGlyph::UnpackFixedY(packed)); +bool GrFontScaler::getPackedGlyphDFBounds(const SkGlyph& glyph, SkIRect* bounds) { +#if 1 + // crbug:510931 + // Retrieving the image from the cache can actually change the mask format. + fStrike->findImage(glyph); +#endif bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight); bounds->outset(SK_DistanceFieldPad, SK_DistanceFieldPad); @@ -148,12 +149,8 @@ } } -bool GrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed, - int width, int height, - int dstRB, void* dst) { - const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), - GrGlyph::UnpackFixedX(packed), - GrGlyph::UnpackFixedY(packed)); +bool GrFontScaler::getPackedGlyphImage(const SkGlyph& glyph, int width, int height, int dstRB, + GrMaskFormat expectedMaskFormat, void* dst) { SkASSERT(glyph.fWidth == width); SkASSERT(glyph.fHeight == height); const void* src = fStrike->findImage(glyph); @@ -161,6 +158,18 @@ return false; } + // crbug:510931 + // Retrieving the image from the cache can actually change the mask format. This case is very + // uncommon so for now we just draw a clear box for these glyphs. + if (getPackedGlyphMaskFormat(glyph) != expectedMaskFormat) { + const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat); + for (int y = 0; y < height; y++) { + sk_bzero(dst, width * bpp); + dst = (char*)dst + dstRB; + } + return true; + } + int srcRB = glyph.rowBytes(); // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to // check the glyph's format, not the strike's format, and to be able to convert to any of the @@ -168,7 +177,7 @@ if (SkMask::kBW_Format == glyph.fMaskFormat) { // expand bits to our mask type const uint8_t* bits = reinterpret_cast<const uint8_t*>(src); - switch (this->getMaskFormat()) { + switch (expectedMaskFormat) { case kA8_GrMaskFormat:{ uint8_t* bytes = reinterpret_cast<uint8_t*>(dst); expand_bits(bytes, bits, width, height, dstRB, srcRB); @@ -185,7 +194,7 @@ } else if (srcRB == dstRB) { memcpy(dst, src, dstRB * height); } else { - const int bbp = GrMaskFormatBytesPerPixel(this->getMaskFormat()); + const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat); for (int y = 0; y < height; y++) { memcpy(dst, src, width * bbp); src = (const char*)src + srcRB; @@ -195,12 +204,7 @@ return true; } -bool GrFontScaler::getPackedGlyphDFImage(GrGlyph::PackedID packed, - int width, int height, - void* dst) { - const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed), - GrGlyph::UnpackFixedX(packed), - GrGlyph::UnpackFixedY(packed)); +bool GrFontScaler::getPackedGlyphDFImage(const SkGlyph& glyph, int width, int height, void* dst) { SkASSERT(glyph.fWidth + 2*SK_DistanceFieldPad == width); SkASSERT(glyph.fHeight + 2*SK_DistanceFieldPad == height); const void* image = fStrike->findImage(glyph); @@ -229,14 +233,12 @@ return true; } -// we should just return const SkPath* (NULL means false) -bool GrFontScaler::getGlyphPath(uint16_t glyphID, SkPath* path) { +const SkPath* GrFontScaler::getGlyphPath(const SkGlyph& glyph) { + return fStrike->findPath(glyph); +} - const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID); - const SkPath* skPath = fStrike->findPath(glyph); - if (skPath) { - *path = *skPath; - return true; - } - return false; +const SkGlyph& GrFontScaler::grToSkGlyph(GrGlyph::PackedID id) { + return fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(id), + GrGlyph::UnpackFixedX(id), + GrGlyph::UnpackFixedY(id)); }
diff --git a/src/gpu/GrFontScaler.h b/src/gpu/GrFontScaler.h index 54d1e3f..eb93d91 100644 --- a/src/gpu/GrFontScaler.h +++ b/src/gpu/GrFontScaler.h
@@ -13,6 +13,7 @@ #include "SkDescriptor.h" +class SkGlyph; class SkPath; /* @@ -67,14 +68,14 @@ const GrFontDescKey* getKey(); 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); - bool getPackedGlyphDFBounds(GrGlyph::PackedID, SkIRect* bounds); - bool getPackedGlyphDFImage(GrGlyph::PackedID, int width, int height, - void* image); - bool getGlyphPath(uint16_t glyphID, SkPath*); + GrMaskFormat getPackedGlyphMaskFormat(const SkGlyph&) const; + bool getPackedGlyphBounds(const SkGlyph&, SkIRect* bounds); + bool getPackedGlyphImage(const SkGlyph&, int width, int height, int rowBytes, + GrMaskFormat expectedMaskFormat, void* image); + bool getPackedGlyphDFBounds(const SkGlyph&, SkIRect* bounds); + bool getPackedGlyphDFImage(const SkGlyph&, int width, int height, void* image); + const SkPath* getGlyphPath(const SkGlyph&); + const SkGlyph& grToSkGlyph(GrGlyph::PackedID); private: SkGlyphCache* fStrike;
diff --git a/tests/TextBlobCacheTest.cpp b/tests/TextBlobCacheTest.cpp new file mode 100644 index 0000000..c063040 --- /dev/null +++ b/tests/TextBlobCacheTest.cpp
@@ -0,0 +1,164 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "sk_tool_utils.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkPoint.h" +#include "SkTextBlob.h" +#include "SkFontMgr.h" +#include "SkGraphics.h" +#include "SkSurface.h" +#include "SkTypeface.h" +#include "../src/fonts/SkRandomScalerContext.h" + +#ifdef SK_BUILD_FOR_WIN + #include "SkTypeface_win.h" +#endif + +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" + +struct TextBlobWrapper { + // This class assumes it 'owns' the textblob it wraps, and thus does not need to take a ref + explicit TextBlobWrapper(const SkTextBlob* blob) : fBlob(blob) {} + TextBlobWrapper(const TextBlobWrapper& blob) : fBlob(SkRef(blob.fBlob.get())) {} + + SkAutoTUnref<const SkTextBlob> fBlob; +}; + +static void draw(SkCanvas* canvas, int redraw, const SkTArray<TextBlobWrapper>& blobs) { + int yOffset = 0; + for (int r = 0; r < redraw; r++) { + for (int i = 0; i < blobs.count(); i++) { + const SkTextBlob* blob = blobs[i].fBlob.get(); + const SkRect& bounds = blob->bounds(); + yOffset += SkScalarCeilToInt(bounds.height()); + SkPaint paint; + canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint); + } + } +} + +static const int kWidth = 1024; +static const int kHeight = 768; + +// This test hammers the GPU textblobcache and font atlas +static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContextFactory* factory, + int maxTotalText, int maxGlyphID, int maxFamilies, bool normal) { + // setup surface + uint32_t flags = 0; + SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); + + // We don't typically actually draw with this unittest + GrContext* ctx = factory->get(GrContextFactory::kNull_GLContextType); + SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); + SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info, + 0, &props)); + REPORTER_ASSERT(reporter, surface); + if (!surface) { + return; + } + + SkCanvas* canvas = surface->getCanvas(); + + SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); + + int count = SkMin32(fm->countFamilies(), maxFamilies); + + // make a ton of text + SkAutoTArray<uint16_t> text(maxTotalText); + for (int i = 0; i < maxTotalText; i++) { + text[i] = i % maxGlyphID; + } + + // generate textblobs + SkTArray<TextBlobWrapper> blobs; + for (int i = 0; i < count; i++) { + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setTextSize(48); // draw big glyphs to really stress the atlas + + SkString familyName; + fm->getFamilyName(i, &familyName); + SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i)); + for (int j = 0; j < set->count(); ++j) { + SkFontStyle fs; + set->getStyle(j, &fs, NULL); + + // We use a typeface which randomy returns unexpected mask formats to fuzz + SkAutoTUnref<SkTypeface> orig(set->createTypeface(j)); + if (normal) { + paint.setTypeface(orig); + } else { + SkAutoTUnref<SkTypeface> typeface(SkNEW_ARGS(SkRandomTypeface, (orig, paint, true))); + paint.setTypeface(typeface); + } + + SkTextBlobBuilder builder; + for (int aa = 0; aa < 2; aa++) { + for (int subpixel = 0; subpixel < 2; subpixel++) { + for (int lcd = 0; lcd < 2; lcd++) { + paint.setAntiAlias(SkToBool(aa)); + paint.setSubpixelText(SkToBool(subpixel)); + paint.setLCDRenderText(SkToBool(lcd)); + if (!SkToBool(lcd)) { + paint.setTextSize(160); + } + const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, + maxTotalText, + 0, 0, + NULL); + memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); + } + } + } + SkNEW_APPEND_TO_TARRAY(&blobs, TextBlobWrapper, (builder.build())); + } + } + + // create surface where LCD is impossible + info = SkImageInfo::MakeN32Premul(kWidth, kHeight); + SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); + SkAutoTUnref<SkSurface> surfaceNoLCD(canvas->newSurface(info, &propsNoLCD)); + REPORTER_ASSERT(reporter, surface); + if (!surface) { + return; + } + + SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); + + // test redraw + draw(canvas, 2, blobs); + draw(canvasNoLCD, 2, blobs); + + // test draw after free + ctx->freeGpuResources(); + draw(canvas, 1, blobs); + + ctx->freeGpuResources(); + draw(canvasNoLCD, 1, blobs); + + // test draw after abandon + ctx->abandonContext(); + draw(canvas, 1, blobs); +} + +DEF_GPUTEST(TextBlobCache, reporter, factory) { + text_blob_cache_inner(reporter, factory, 4096, 256, 30, true); +} + +DEF_GPUTEST(TextBlobAbnormal, reporter, factory) { +#ifdef SK_BUILD_FOR_ANDROID + text_blob_cache_inner(reporter, factory, 256, 256, 30, false); +#else + text_blob_cache_inner(reporter, factory, 512, 256, 30, false); +#endif +} +#endif