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