Chromium Code Reviews| Index: src/gpu/GrStencilAndCoverTextContext.cpp |
| diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp |
| index 637c85a997d8942da5c725777b8bef544fcf0227..5f18723e0d2096b1e78fc20edc0e638623bbcf5a 100644 |
| --- a/src/gpu/GrStencilAndCoverTextContext.cpp |
| +++ b/src/gpu/GrStencilAndCoverTextContext.cpp |
| @@ -7,11 +7,9 @@ |
| #include "GrStencilAndCoverTextContext.h" |
| #include "GrDrawTarget.h" |
| -#include "GrFontScaler.h" |
| #include "GrGpu.h" |
| #include "GrPath.h" |
| -#include "GrTextStrike.h" |
| -#include "GrTextStrike_impl.h" |
| +#include "GrPathRange.h" |
| #include "SkAutoKern.h" |
| #include "SkDraw.h" |
| #include "SkDrawProcs.h" |
| @@ -20,12 +18,99 @@ |
| #include "SkPath.h" |
| #include "SkTextMapStateProc.h" |
| -static const int kMaxReservedGlyphs = 64; |
| +class GrStencilAndCoverTextContext::GlyphPathRange : public GrCacheable { |
| + static const int kMaxGlyphCount = 1 << 16; // Glyph IDs are uint16_t's |
| + static const int kGlyphGroupSize = 16; // Glyphs get tracked in groups of 16 |
| + static const bool kIsWrapped = false; |
| + |
| +public: |
| + static GlyphPathRange* Create(GrContext* context, |
| + SkGlyphCache* cache, |
| + const SkStrokeRec& stroke) { |
| + static const GrResourceKey::ResourceType gGlyphPathRangeType = |
| + GrResourceKey::GenerateResourceType(); |
|
bsalomon
2014/07/17 17:27:28
I think the type should come from GrPathRange itse
Chris Dalton
2014/07/17 18:15:19
Acknowledged.
|
| + static const GrCacheID::Domain gGlyphPathRangeDomain = GrCacheID::GenerateDomain(); |
| + |
| + GrCacheID::Key key; |
| + key.fData32[0] = cache->getDescriptor().getChecksum(); |
| + key.fData32[1] = cache->getScalerContext()->getTypeface()->uniqueID(); |
| + key.fData64[1] = GrPath::ComputeStrokeKey(stroke); |
| + |
| + GrResourceKey resourceKey(GrCacheID(gGlyphPathRangeDomain, key), gGlyphPathRangeType, 0); |
| + SkAutoTUnref<GlyphPathRange> glyphs( |
| + static_cast<GlyphPathRange*>(context->findAndRefCachedResource(resourceKey))); |
| + |
| + if (NULL == glyphs || |
| + !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/)) { |
| + GrGpu* const gpu = context->getGpu(); |
| + // We reserve a range of kMaxGlyphCount paths because of fallbacks fonts. We |
| + // can't know exactly how many glyphs we might need without preloading every |
| + // fallback, which we don't want to do at this point. |
| + SkAutoTUnref<GrPathRange> pathRange(gpu->createPathRange(kMaxGlyphCount, stroke)); |
| + glyphs.reset(SkNEW_ARGS(GlyphPathRange, (cache->getDescriptor(), pathRange))); |
| + context->addResourceToCache(resourceKey, glyphs); |
| + } |
| + |
| + return glyphs.detach(); |
| + } |
| + |
| + const GrPathRange* pathRange() const { return fPathRange.get(); } |
| + |
| + void preloadGlyph(uint16_t glyphID, SkGlyphCache* cache) { |
| + const uint16_t groupIndex = glyphID / kGlyphGroupSize; |
| + const uint16_t groupByte = groupIndex >> 3; |
| + const uint8_t groupBit = 1 << (groupIndex & 7); |
| + |
| + const bool hasGlyph = fLoadedGlyphs[groupByte] & groupBit; |
| + if (hasGlyph) { |
| + return; |
| + } |
| + |
| + // We track which glyphs are loaded in groups of kGlyphGroupSize. To |
| + // mark a glyph loaded we need to load the entire group. |
| + const uint16_t groupFirstID = groupIndex * kGlyphGroupSize; |
| + const uint16_t groupLastID = groupFirstID + kGlyphGroupSize - 1; |
| + SkPath skPath; |
| + for (int id = groupFirstID; id <= groupLastID; ++id) { |
| + const SkGlyph& skGlyph = cache->getGlyphIDMetrics(id); |
| + if (const SkPath* skPath = cache->findPath(skGlyph)) { |
| + fPathRange->initAt(id, *skPath); |
| + } // GrGpu::drawPaths will silently ignore undefined paths. |
| + } |
| + |
| + fLoadedGlyphs[groupByte] |= groupBit; |
| + this->didChangeGpuMemorySize(); |
| + } |
| + |
| + // GrCacheable overrides |
| + virtual size_t gpuMemorySize() const SK_OVERRIDE { return fPathRange->gpuMemorySize(); } |
| + virtual bool isValidOnGpu() const SK_OVERRIDE { return fPathRange->isValidOnGpu(); } |
| + |
| +private: |
| + GlyphPathRange(const SkDescriptor& desc, GrPathRange* pathRange) |
| + : fDesc(desc.copy()) |
| + , fPathRange(pathRange) { |
| + memset(fLoadedGlyphs, 0, sizeof(fLoadedGlyphs)); |
| + } |
| + |
| + ~GlyphPathRange() { |
| + SkDescriptor::Free(fDesc); |
| + } |
| + |
| + static const int kMaxGroupCount = (kMaxGlyphCount + (kGlyphGroupSize - 1)) / kGlyphGroupSize; |
| + SkDescriptor* const fDesc; |
| + uint8_t fLoadedGlyphs[(kMaxGroupCount + 7) >> 3]; // One bit per glyph group |
| + SkRefPtr<GrPathRange> fPathRange; |
| + |
| + typedef GrCacheable INHERITED; |
| +}; |
| + |
| GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( |
| GrContext* context, const SkDeviceProperties& properties) |
| : GrTextContext(context, properties) |
| - , fStroke(SkStrokeRec::kFill_InitStyle) { |
| + , fStroke(SkStrokeRec::kFill_InitStyle) |
| + , fPendingGlyphCount(0) { |
| } |
| GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { |
| @@ -73,10 +158,8 @@ void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, |
| SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
| SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform); |
| - SkGlyphCache* cache = autoCache.getCache(); |
| - GrFontScaler* scaler = GetGrFontScaler(cache); |
| - GrTextStrike* strike = |
| - fContext->getFontCache()->getStrike(scaler, true); |
| + fGlyphCache = autoCache.getCache(); |
| + fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); |
| const char* stop = text + byteLength; |
| @@ -89,7 +172,7 @@ void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, |
| while (textPtr < stop) { |
| // We don't need x, y here, since all subpixel variants will have the |
| // same advance. |
| - const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); |
| + const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0); |
| stopX += glyph.fAdvanceX; |
| stopY += glyph.fAdvanceY; |
| @@ -115,17 +198,10 @@ void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, |
| SkFixed fx = SkScalarToFixed(x); |
| SkFixed fy = SkScalarToFixed(y); |
| while (text < stop) { |
| - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
| + const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
| fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); |
| if (glyph.fWidth) { |
| - this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
| - glyph.getSubXFixed(), |
| - glyph.getSubYFixed()), |
| - SkPoint::Make( |
| - SkFixedToScalar(fx), |
| - SkFixedToScalar(fy)), |
| - strike, |
| - scaler); |
| + this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy)); |
| } |
| fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); |
| @@ -164,10 +240,8 @@ void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, |
| SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
| SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); |
| - SkGlyphCache* cache = autoCache.getCache(); |
| - GrFontScaler* scaler = GetGrFontScaler(cache); |
| - GrTextStrike* strike = |
| - fContext->getFontCache()->getStrike(scaler, true); |
| + fGlyphCache = autoCache.getCache(); |
| + fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); |
| const char* stop = text + byteLength; |
| SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); |
| @@ -177,33 +251,22 @@ void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, |
| while (text < stop) { |
| SkPoint loc; |
| tmsProc(pos, &loc); |
| - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
| + const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
| if (glyph.fWidth) { |
| - this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
| - glyph.getSubXFixed(), |
| - glyph.getSubYFixed()), |
| - loc, |
| - strike, |
| - scaler); |
| + this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); |
| } |
| pos += scalarsPerPosition; |
| } |
| } else { |
| while (text < stop) { |
| - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
| + const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
| if (glyph.fWidth) { |
| SkPoint tmsLoc; |
| tmsProc(pos, &tmsLoc); |
| SkPoint loc; |
| alignProc(tmsLoc, glyph, &loc); |
| - this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
| - glyph.getSubXFixed(), |
| - glyph.getSubYFixed()), |
| - loc, |
| - strike, |
| - scaler); |
| - |
| + this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); |
| } |
| pos += scalarsPerPosition; |
| } |
| @@ -305,62 +368,46 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| *fDrawTarget->drawState()->stencil() = kStencilPass; |
| - size_t reserveAmount; |
| - switch (skPaint.getTextEncoding()) { |
| - default: |
| - SkASSERT(false); |
| - case SkPaint::kUTF8_TextEncoding: |
| - reserveAmount = textByteLength; |
| - break; |
| - case SkPaint::kUTF16_TextEncoding: |
| - reserveAmount = textByteLength / 2; |
| - break; |
| - case SkPaint::kUTF32_TextEncoding: |
| - case SkPaint::kGlyphID_TextEncoding: |
| - reserveAmount = textByteLength / 4; |
| - break; |
| + SkASSERT(0 == fPendingGlyphCount); |
| +} |
| + |
| +inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) { |
| + if (fPendingGlyphCount >= kGlyphBufferSize) { |
| + this->flush(); |
| + SkASSERT(0 == fPendingGlyphCount); |
| } |
| - fPaths.setReserve(reserveAmount); |
| - fTransforms.setReserve(reserveAmount); |
| + |
| + fGlyphs->preloadGlyph(glyphID, fGlyphCache); |
| + |
| + fIndexBuffer[fPendingGlyphCount] = glyphID; |
| + fTransformBuffer[6 * fPendingGlyphCount + 0] = fTextRatio; |
| + fTransformBuffer[6 * fPendingGlyphCount + 1] = 0; |
| + fTransformBuffer[6 * fPendingGlyphCount + 2] = x; |
| + fTransformBuffer[6 * fPendingGlyphCount + 3] = 0; |
| + fTransformBuffer[6 * fPendingGlyphCount + 4] = fTextRatio; |
| + fTransformBuffer[6 * fPendingGlyphCount + 5] = y; |
| + |
| + ++fPendingGlyphCount; |
| } |
| -inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID, |
| - const SkPoint& pos, |
| - GrTextStrike* strike, |
| - GrFontScaler* scaler) { |
| - GrGlyph* glyph = strike->getGlyph(glyphID, scaler); |
| - if (NULL == glyph || glyph->fBounds.isEmpty()) { |
| +void GrStencilAndCoverTextContext::flush() { |
| + if (0 == fPendingGlyphCount) { |
| return; |
| } |
| - if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { |
| - if (!fTmpPath.isEmpty()) { |
| - *fPaths.append() = fContext->createPath(fTmpPath, fStroke); |
| - SkMatrix* t = fTransforms.append(); |
| - t->setTranslate(pos.fX, pos.fY); |
| - t->preScale(fTextRatio, fTextRatio); |
| - } |
| - } |
| + fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCount, |
| + fTransformBuffer, kAffine_GrTransformFormat, |
| + SkPath::kWinding_FillType); |
| + |
| + fPendingGlyphCount = 0; |
| } |
| void GrStencilAndCoverTextContext::finish() { |
| - if (fPaths.count() > 0) { |
| - fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()), |
| - fPaths.begin(), fTransforms.begin(), |
| - SkPath::kWinding_FillType, fStroke.getStyle()); |
| + this->flush(); |
| - for (int i = 0; i < fPaths.count(); ++i) { |
| - fPaths[i]->unref(); |
| - } |
| - if (fPaths.count() > kMaxReservedGlyphs) { |
| - fPaths.reset(); |
| - fTransforms.reset(); |
| - } else { |
| - fPaths.rewind(); |
| - fTransforms.rewind(); |
| - } |
| - } |
| - fTmpPath.reset(); |
| + SkSafeUnref(fGlyphs); |
| + fGlyphs = NULL; |
| + fGlyphCache = NULL; |
| fDrawTarget->drawState()->stencil()->setDisabled(); |
| fStateRestore.set(NULL); |