Chromium Code Reviews| Index: src/gpu/GrAtlasTextContext.cpp |
| diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp |
| index d6bb3e079712ecb27c9996786cf741e228a197dc..3a963fcfe8ed67e79b83e8a96abf7db4fd85ae4b 100644 |
| --- a/src/gpu/GrAtlasTextContext.cpp |
| +++ b/src/gpu/GrAtlasTextContext.cpp |
| @@ -91,12 +91,54 @@ bool GrAtlasTextContext::canDraw(const GrRenderTarget*, |
| return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); |
| } |
|
robertphillips
2015/04/09 13:15:32
kMinDiscernableTranslation ?
Can this be moved int
joshualitt
2015/04/09 14:59:00
Acknowledged.
|
| -bool GrAtlasTextContext::MustRegenerateBlob(const BitmapTextBlob& blob, const SkPaint& paint, |
| +static const SkScalar kThreshold = 0.0625; |
| + |
| +bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* transX, SkScalar* transY, |
| + const BitmapTextBlob& blob, const SkPaint& paint, |
| const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { |
| - // We always regenerate blobs with patheffects or mask filters we could cache these |
| - // TODO find some way to cache the maskfilter / patheffects on the textblob |
| - return !blob.fViewMatrix.cheapEqualTo(viewMatrix) || blob.fX != x || blob.fY != y || |
| - paint.getMaskFilter() || paint.getPathEffect() || paint.getStyle() != blob.fStyle; |
| + // Color can affect the mask |
| + // TODO we can adjust the color within specific gamma slots |
| + if (blob.fColor != paint.getColor()) { |
| + return true; |
| + } |
| + |
| + if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { |
| + return true; |
| + } |
| + |
| + if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) { |
| + return true; |
| + } |
| + |
| + if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() || |
| + blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() || |
| + blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() || |
| + blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) { |
| + return true; |
| + } |
| + |
| + // We can update the positions in the cachedtextblobs without regenerating the whole blob, but |
| + // only for integer translations. |
|
robertphillips
2015/04/09 13:15:32
That's an ominous todo
Shouldn't this be something
joshualitt
2015/04/09 14:59:00
I took another stab at it. I *think* I can allow
|
| + // TODO I think this is wrong |
| + if (SkScalarFraction(blob.fViewMatrix.getTranslateX() - |
| + viewMatrix.getTranslateX()) > kThreshold || |
| + SkScalarFraction(x - blob.fX) > kThreshold || |
| + SkScalarFraction(blob.fViewMatrix.getTranslateX() - |
| + viewMatrix.getTranslateX()) > kThreshold || |
| + SkScalarFraction(y - blob.fY) > kThreshold) { |
| + return true; |
| + } |
| + |
|
robertphillips
2015/04/09 13:15:32
// This cool math does the following ...
?
joshualitt
2015/04/09 14:59:00
Acknowledged.
|
| + (*transX) = viewMatrix.getTranslateX() + |
| + viewMatrix.getScaleX() * (x - blob.fX) + |
| + viewMatrix.getSkewX() * (y - blob.fY) - |
| + blob.fViewMatrix.getTranslateX(); |
| + |
| + (*transY) = viewMatrix.getTranslateY() + |
| + viewMatrix.getSkewY() * (x - blob.fX) + |
| + viewMatrix.getScaleY() * (y - blob.fY) - |
| + blob.fViewMatrix.getTranslateY(); |
| + return false; |
| } |
| @@ -113,24 +155,47 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, |
| const SkTextBlob* blob, SkScalar x, SkScalar y, |
| SkDrawFilter* drawFilter, const SkIRect& clipBounds) { |
| uint32_t uniqueID = blob->uniqueID(); |
| - BitmapTextBlob* cacheBlob = fCache->find(uniqueID); |
| + SkAutoTUnref<BitmapTextBlob> cacheBlob; |
| + // TODO start caching these, mix bits into the key |
|
robertphillips
2015/04/09 13:15:32
Would this be more natural as "canCache" ?
joshualitt
2015/04/09 14:59:00
Acknowledged.
|
| + bool mustNotCache = skPaint.getPathEffect() || |
| + skPaint.getMaskFilter() || |
| + skPaint.getColorFilter() || |
| + skPaint.getStyle() != SkPaint::kFill_Style || |
| + drawFilter; |
| + |
| + if (!mustNotCache) { |
| + cacheBlob.reset(SkSafeRef(fCache->find(uniqueID))); |
| + } |
| + |
| SkIRect clipRect; |
| clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); |
| + SkScalar transX = 0.f; |
| + SkScalar transY = 0.f; |
| + |
| if (cacheBlob) { |
| - if (MustRegenerateBlob(*cacheBlob, skPaint, viewMatrix, x, y)) { |
| + if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, viewMatrix, x, y)) { |
| // We have to remake the blob because changes may invalidate our masks. |
| // TODO we could probably get away reuse most of the time if the pointer is unique, |
| // but we'd have to clear the subrun information |
| fCache->remove(cacheBlob); |
| - cacheBlob = fCache->createCachedBlob(blob, kGrayTextVASize); |
| + cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, kGrayTextVASize))); |
| this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter, |
| clipRect); |
| } else { |
| + // If we can reuse the blob, then make sure we update the blob's viewmatrix and x/y |
| + // offsets to reflect the results of any translations we may apply in generateGeometry |
| + cacheBlob->fViewMatrix = viewMatrix; |
| + cacheBlob->fX = x; |
| + cacheBlob->fY = y; |
| fCache->makeMRU(cacheBlob); |
| } |
| } else { |
| - cacheBlob = fCache->createCachedBlob(blob, kGrayTextVASize); |
| + if (mustNotCache) { |
| + cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize)); |
| + } else { |
| + cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, kGrayTextVASize))); |
| + } |
| this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter, clipRect); |
| } |
| @@ -140,7 +205,7 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, |
| SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint); |
| this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter, |
| - clip, viewMatrix, clipBounds, x, y); |
| + clip, viewMatrix, clipBounds, x, y, transX, transY); |
| } |
| void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob, |
| @@ -150,6 +215,7 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob, |
| cacheBlob->fViewMatrix = viewMatrix; |
| cacheBlob->fX = x; |
| cacheBlob->fY = y; |
| + cacheBlob->fColor = skPaint.getColor(); |
| cacheBlob->fStyle = skPaint.getStyle(); |
| // Regenerate textblob |
| @@ -185,7 +251,7 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob, |
| newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex; |
| } |
| - if (SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix)) { |
| + if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) { |
| cacheBlob->fRuns[run].fDrawAsPaths = true; |
| continue; |
| } |
| @@ -536,11 +602,6 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph |
| int width = glyph->fBounds.width(); |
| int height = glyph->fBounds.height(); |
| - // check if we clipped out |
| - if (clipRect.quickReject(x, y, x + width, y + height)) { |
| - return; |
| - } |
| - |
| // If the glyph is too large we fall back to paths |
| if (fCurrStrike->glyphTooLargeForAtlas(glyph)) { |
| if (NULL == glyph->fPath) { |
| @@ -598,7 +659,6 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph |
| *colorPtr = color; |
| } |
| vertex += vertexStride; |
| - |
| // V1 |
| position = reinterpret_cast<SkPoint*>(vertex); |
| position->set(r.fLeft, r.fBottom); |
| @@ -640,11 +700,15 @@ public: |
| : fBlob(SkRef(geometry.fBlob.get())) |
| , fRun(geometry.fRun) |
| , fSubRun(geometry.fSubRun) |
| - , fColor(geometry.fColor) {} |
| + , fColor(geometry.fColor) |
| + , fTransX(geometry.fTransX) |
| + , fTransY(geometry.fTransY) {} |
| SkAutoTUnref<Blob> fBlob; |
| int fRun; |
| int fSubRun; |
| GrColor fColor; |
| + SkScalar fTransX; |
| + SkScalar fTransY; |
| }; |
| static GrBatch* Create(const Geometry& geometry, GrMaskFormat maskFormat, |
| @@ -758,6 +822,7 @@ public: |
| uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat); |
| bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen; |
| bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor; |
| + bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f; |
| int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; |
| // We regenerate both texture coords and colors in the blob itself, and update the |
| @@ -768,7 +833,7 @@ public: |
| // or coords as needed. One final note, if we have to break a run for an atlas eviction |
| // then we can't really trust the atlas has all of the correct data. Atlas evictions |
| // should be pretty rare, so we just always regenerate in those cases |
| - if (regenerateTextureCoords || regenerateColors) { |
| + if (regenerateTextureCoords || regenerateColors || regeneratePositions) { |
| // first regenerate texture coordinates / colors if need be |
| const SkDescriptor* desc = NULL; |
| SkGlyphCache* cache = NULL; |
| @@ -822,9 +887,19 @@ public: |
| this->regenerateColors(vertex, vertexStride, args.fColor); |
| } |
| + if (regeneratePositions) { |
| + intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices); |
| + vertex += info.fVertexStartIndex; |
| + vertex += vertexStride * glyphIdx * kVerticesPerGlyph; |
| + SkScalar transX = args.fTransX; |
| + SkScalar transY = args.fTransY; |
| + this->regeneratePositions(vertex, vertexStride, transX, transY); |
| + } |
| instancesToFlush++; |
| } |
| + // We my have changed the color so update it here |
| + run.fColor = args.fColor; |
| if (regenerateTextureCoords) { |
| SkGlyphCache::AttachCache(cache); |
| info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration : |
| @@ -904,6 +979,16 @@ private: |
| } |
| } |
| + void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX, |
| + SkScalar transY) { |
| + for (int i = 0; i < kVerticesPerGlyph; i++) { |
| + SkPoint* point = reinterpret_cast<SkPoint*>(vertex); |
| + point->fX += transX; |
| + point->fY += transY; |
| + vertex += vertexStride; |
| + } |
| + } |
| + |
| void initDraw(GrBatchTarget* batchTarget, |
| const GrGeometryProcessor* gp, |
| const GrPipeline* pipeline) { |
| @@ -1010,7 +1095,7 @@ void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, cons |
| inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, |
| BitmapTextBlob* cacheBlob, int run, GrColor color, |
| - uint8_t paintAlpha) { |
| + uint8_t paintAlpha, SkScalar transX, SkScalar transY) { |
| for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) { |
| const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun]; |
| int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; |
| @@ -1028,6 +1113,8 @@ inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder |
| geometry.fRun = run; |
| geometry.fSubRun = subRun; |
| geometry.fColor = subRunColor; |
| + geometry.fTransX = transX; |
| + geometry.fTransY = transY; |
| SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, format, glyphCount, |
| fContext->getBatchFontCache())); |
| @@ -1036,11 +1123,15 @@ inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder |
| } |
| inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt, |
| - const GrPaint& grPaint, const GrClip& clip) { |
| + const GrPaint& grPaint, const GrClip& clip, |
| + SkScalar transX, SkScalar transY) { |
| for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) { |
| - const BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i]; |
| + BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i]; |
| + bigGlyph.fVx += transX; |
| + bigGlyph.fVy += transY; |
| SkMatrix translate; |
| - translate.setTranslate(SkIntToScalar(bigGlyph.fVx), SkIntToScalar(bigGlyph.fVy)); |
| + translate.setTranslate(SkIntToScalar(bigGlyph.fVx), |
| + SkIntToScalar(bigGlyph.fVy)); |
| SkPath tmpPath(bigGlyph.fPath); |
| tmpPath.transform(translate); |
| GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); |
| @@ -1058,8 +1149,8 @@ void GrAtlasTextContext::flush(GrDrawTarget* target, |
| const GrClip& clip, |
| const SkMatrix& viewMatrix, |
| const SkIRect& clipBounds, |
| - SkScalar x, |
| - SkScalar y) { |
| + SkScalar x, SkScalar y, |
| + SkScalar transX, SkScalar transY) { |
| // We loop through the runs of the blob, flushing each. If any run is too large, then we flush |
| // it as paths |
| GrPipelineBuilder pipelineBuilder; |
| @@ -1074,11 +1165,12 @@ void GrAtlasTextContext::flush(GrDrawTarget* target, |
| this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y); |
| continue; |
| } |
| - this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha); |
| + cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY); |
| + this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, transX, transY); |
| } |
| // Now flush big glyphs |
| - this->flushBigGlyphs(cacheBlob, rt, grPaint, clip); |
| + this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, transX, transY); |
| } |
| void GrAtlasTextContext::flush(GrDrawTarget* target, |
| @@ -1094,9 +1186,9 @@ void GrAtlasTextContext::flush(GrDrawTarget* target, |
| GrColor color = grPaint.getColor(); |
| uint8_t paintAlpha = skPaint.getAlpha(); |
| for (int run = 0; run < cacheBlob->fRunCount; run++) { |
| - this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha); |
| + this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, 0, 0); |
| } |
| // Now flush big glyphs |
| - this->flushBigGlyphs(cacheBlob, rt, grPaint, clip); |
| + this->flushBigGlyphs(cacheBlob, rt, grPaint, clip, 0, 0); |
| } |