| Index: src/gpu/GrStencilAndCoverTextContext.cpp
|
| diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
|
| index 637c85a997d8942da5c725777b8bef544fcf0227..65c26423d1391edd69193620234af1e344e5303f 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,95 @@
|
| #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
|
| +
|
| +public:
|
| + static GlyphPathRange* Create(GrContext* context,
|
| + SkGlyphCache* cache,
|
| + const SkStrokeRec& stroke) {
|
| + 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),
|
| + GrPathRange::resourceType(), 0);
|
| + SkAutoTUnref<GlyphPathRange> glyphs(
|
| + static_cast<GlyphPathRange*>(context->findAndRefCachedResource(resourceKey)));
|
| +
|
| + if (NULL == glyphs ||
|
| + !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/)) {
|
| + glyphs.reset(SkNEW_ARGS(GlyphPathRange, (context, cache->getDescriptor(), stroke)));
|
| + 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 = 0 != (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(GrContext* context, const SkDescriptor& desc, const SkStrokeRec& stroke)
|
| + : fDesc(desc.copy())
|
| + // 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.
|
| + , fPathRange(context->getGpu()->createPathRange(kMaxGlyphCount, stroke)) {
|
| + 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
|
| + SkAutoTUnref<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 +154,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 +168,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 +194,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 +236,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 +247,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 +364,45 @@ 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();
|
| }
|
| - 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, GrDrawTarget::kAffine_PathTransformType,
|
| + 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);
|
|
|