"$_src/gpu/batches/GrAAStrokeRectOp.h",
"$_src/gpu/batches/GrAnalyticRectOp.cpp",
"$_src/gpu/batches/GrAnalyticRectOp.h",
- "$_src/gpu/batches/GrAtlasTextBatch.cpp",
- "$_src/gpu/batches/GrAtlasTextBatch.h",
+ "$_src/gpu/batches/GrAtlasTextOp.cpp",
+ "$_src/gpu/batches/GrAtlasTextOp.h",
"$_src/gpu/batches/GrClearOp.h",
"$_src/gpu/batches/GrClearStencilClipOp.h",
"$_src/gpu/batches/GrCopySurfaceOp.cpp",
+++ /dev/null
-/*
- * 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 "GrAtlasTextBatch.h"
-
-#include "GrOpFlushState.h"
-#include "GrResourceProvider.h"
-
-#include "SkGlyphCache.h"
-#include "SkMathPriv.h"
-
-#include "effects/GrBitmapTextGeoProc.h"
-#include "effects/GrDistanceFieldGeoProc.h"
-#include "text/GrBatchFontCache.h"
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
- unsigned r = SkColorGetR(c);
- unsigned g = SkColorGetG(c);
- unsigned b = SkColorGetB(c);
- return GrColorPackRGBA(r, g, b, 0xff);
-}
-
-static const int kDistanceAdjustLumShift = 5;
-
-SkString GrAtlasTextBatch::dumpInfo() const {
- SkString str;
-
- for (int i = 0; i < fGeoCount; ++i) {
- str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
- i,
- fGeoData[i].fColor,
- fGeoData[i].fX,
- fGeoData[i].fY,
- fGeoData[i].fBlob->runCount());
- }
-
- str.append(DumpPipelineInfo(*this->pipeline()));
- str.append(INHERITED::dumpInfo());
- return str;
-}
-
-void GrAtlasTextBatch::computePipelineOptimizations(GrInitInvariantOutput* color,
- GrInitInvariantOutput* coverage,
- GrBatchToXPOverrides* overrides) const {
- if (kColorBitmapMask_MaskType == fMaskType) {
- color->setUnknownFourComponents();
- } else {
- color->setKnownFourComponents(fBatch.fColor);
- }
- switch (fMaskType) {
- case kGrayscaleDistanceField_MaskType:
- case kGrayscaleCoverageMask_MaskType:
- coverage->setUnknownSingleComponent();
- break;
- case kLCDCoverageMask_MaskType:
- case kLCDDistanceField_MaskType:
- coverage->setUnknownOpaqueFourComponents();
- coverage->setUsingLCDCoverage();
- break;
- case kColorBitmapMask_MaskType:
- coverage->setKnownSingleComponent(0xff);
- }
-}
-
-void GrAtlasTextBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
- // Handle any color overrides
- if (!overrides.readsColor()) {
- fGeoData[0].fColor = GrColor_ILLEGAL;
- }
- overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
-
- // setup batch properties
- fBatch.fColorIgnored = !overrides.readsColor();
- fBatch.fColor = fGeoData[0].fColor;
- fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
- fBatch.fCoverageIgnored = !overrides.readsCoverage();
-}
-
-void GrAtlasTextBatch::onPrepareDraws(Target* target) const {
- // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
- // TODO actually only invert if we don't have RGBA
- SkMatrix localMatrix;
- if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
- SkDebugf("Cannot invert viewmatrix\n");
- return;
- }
-
- GrTexture* texture = fFontCache->getTexture(this->maskFormat());
- if (!texture) {
- SkDebugf("Could not allocate backing texture for atlas\n");
- return;
- }
-
- GrMaskFormat maskFormat = this->maskFormat();
-
- FlushInfo flushInfo;
- if (this->usesDistanceFields()) {
- flushInfo.fGeometryProcessor =
- this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture);
- } else {
- GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
- flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(this->color(),
- texture,
- params,
- maskFormat,
- localMatrix,
- this->usesLocalCoords());
- }
-
- flushInfo.fGlyphsToFlush = 0;
- size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
- SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat));
-
- int glyphCount = this->numGlyphs();
- const GrBuffer* vertexBuffer;
-
- void* vertices = target->makeVertexSpace(vertexStride,
- glyphCount * kVerticesPerGlyph,
- &vertexBuffer,
- &flushInfo.fVertexOffset);
- flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
- flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
- if (!vertices || !flushInfo.fVertexBuffer) {
- SkDebugf("Could not allocate vertices\n");
- return;
- }
-
- unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
-
- GrBlobRegenHelper helper(this, target, &flushInfo);
- SkAutoGlyphCache glyphCache;
- for (int i = 0; i < fGeoCount; i++) {
- const Geometry& args = fGeoData[i];
- Blob* blob = args.fBlob;
- size_t byteCount;
- void* blobVertices;
- int subRunGlyphCount;
- blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache,
- vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor,
- &blobVertices, &byteCount, &subRunGlyphCount);
-
- // now copy all vertices
- memcpy(currVertex, blobVertices, byteCount);
-
-#ifdef SK_DEBUG
- // bounds sanity check
- SkRect rect;
- rect.setLargestInverted();
- SkPoint* vertex = (SkPoint*) ((char*)blobVertices);
- rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount);
-
- if (this->usesDistanceFields()) {
- args.fViewMatrix.mapRect(&rect);
- }
- // Allow for small numerical error in the bounds.
- SkRect bounds = this->bounds();
- bounds.outset(0.001f, 0.001f);
- SkASSERT(bounds.contains(rect));
-#endif
-
- currVertex += byteCount;
- }
-
- this->flush(target, &flushInfo);
-}
-
-void GrAtlasTextBatch::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
- GrMesh mesh;
- int maxGlyphsPerDraw =
- static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
- mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
- flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset,
- kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
- maxGlyphsPerDraw);
- target->draw(flushInfo->fGeometryProcessor.get(), mesh);
- flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
- flushInfo->fGlyphsToFlush = 0;
-}
-
-bool GrAtlasTextBatch::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
- GrAtlasTextBatch* that = t->cast<GrAtlasTextBatch>();
- if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
- that->bounds(), caps)) {
- return false;
- }
-
- if (fMaskType != that->fMaskType) {
- return false;
- }
-
- if (!this->usesDistanceFields()) {
- if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
- return false;
- }
- if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
- return false;
- }
- } else {
- if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
- return false;
- }
-
- if (fFilteredColor != that->fFilteredColor) {
- return false;
- }
-
- if (fUseBGR != that->fUseBGR) {
- return false;
- }
- }
-
- fBatch.fNumGlyphs += that->numGlyphs();
-
- // Reallocate space for geo data if necessary and then import that's geo data.
- int newGeoCount = that->fGeoCount + fGeoCount;
- // We assume (and here enforce) that the allocation size is the smallest power of two that
- // is greater than or equal to the number of geometries (and at least
- // kMinGeometryAllocated).
- int newAllocSize = GrNextPow2(newGeoCount);
- int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
-
- if (newGeoCount > currAllocSize) {
- fGeoData.realloc(newAllocSize);
- }
-
- memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
- // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
- // it doesn't try to unref them.
-#ifdef SK_DEBUG
- for (int i = 0; i < that->fGeoCount; ++i) {
- that->fGeoData.get()[i].fBlob = (Blob*)0x1;
- }
-#endif
- that->fGeoCount = 0;
- fGeoCount = newGeoCount;
-
- this->joinBounds(*that);
- return true;
-}
-
-// TODO just use class params
-// TODO trying to figure out why lcd is so whack
-sk_sp<GrGeometryProcessor> GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatrix,
- SkColor filteredColor,
- GrColor color,
- GrTexture* texture) const {
- GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
- bool isLCD = this->isLCD();
- // set up any flags
- uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
- flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
- flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
-
- // see if we need to create a new effect
- if (isLCD) {
- flags |= kUseLCD_DistanceFieldEffectFlag;
- flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
-
- GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
-
- float redCorrection = fDistanceAdjustTable->getAdjustment(
- GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift,
- fUseGammaCorrectDistanceTable);
- float greenCorrection = fDistanceAdjustTable->getAdjustment(
- GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift,
- fUseGammaCorrectDistanceTable);
- float blueCorrection = fDistanceAdjustTable->getAdjustment(
- GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift,
- fUseGammaCorrectDistanceTable);
- GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
- GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
- greenCorrection,
- blueCorrection);
-
- return GrDistanceFieldLCDTextGeoProc::Make(color,
- viewMatrix,
- texture,
- params,
- widthAdjust,
- flags,
- this->usesLocalCoords());
- } else {
-#ifdef SK_GAMMA_APPLY_TO_A8
- U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
- float correction = fDistanceAdjustTable->getAdjustment(
- lum >> kDistanceAdjustLumShift, fUseGammaCorrectDistanceTable);
- return GrDistanceFieldA8TextGeoProc::Make(color,
- viewMatrix,
- texture,
- params,
- correction,
- flags,
- this->usesLocalCoords());
-#else
- return GrDistanceFieldA8TextGeoProc::Make(color,
- viewMatrix,
- texture,
- params,
- flags,
- this->usesLocalCoords());
-#endif
- }
-
-}
-
-void GrBlobRegenHelper::flush() {
- fBatch->flush(fTarget, fFlushInfo);
-}
+++ /dev/null
-/*
- * 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 GrAtlasTextBatch_DEFINED
-#define GrAtlasTextBatch_DEFINED
-
-#include "batches/GrMeshDrawOp.h"
-
-#include "text/GrAtlasTextContext.h"
-#include "text/GrDistanceFieldAdjustTable.h"
-
-class GrAtlasTextBatch final : public GrMeshDrawOp {
-public:
- DEFINE_OP_CLASS_ID
-
- static const int kVerticesPerGlyph = GrAtlasTextBlob::kVerticesPerGlyph;
- static const int kIndicesPerGlyph = 6;
-
- typedef GrAtlasTextBlob Blob;
- struct Geometry {
- SkMatrix fViewMatrix;
- Blob* fBlob;
- SkScalar fX;
- SkScalar fY;
- int fRun;
- int fSubRun;
- GrColor fColor;
- };
-
- static GrAtlasTextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
- GrBatchFontCache* fontCache) {
- GrAtlasTextBatch* batch = new GrAtlasTextBatch;
-
- batch->fFontCache = fontCache;
- switch (maskFormat) {
- case kA8_GrMaskFormat:
- batch->fMaskType = kGrayscaleCoverageMask_MaskType;
- break;
- case kA565_GrMaskFormat:
- batch->fMaskType = kLCDCoverageMask_MaskType;
- break;
- case kARGB_GrMaskFormat:
- batch->fMaskType = kColorBitmapMask_MaskType;
- break;
- }
- batch->fBatch.fNumGlyphs = glyphCount;
- batch->fGeoCount = 1;
- batch->fFilteredColor = 0;
- batch->fFontCache = fontCache;
- batch->fUseBGR = false;
- return batch;
- }
-
- static GrAtlasTextBatch* CreateDistanceField(
- int glyphCount, GrBatchFontCache* fontCache,
- const GrDistanceFieldAdjustTable* distanceAdjustTable,
- bool useGammaCorrectDistanceTable,
- SkColor filteredColor, bool isLCD,
- bool useBGR) {
- GrAtlasTextBatch* batch = new GrAtlasTextBatch;
-
- batch->fFontCache = fontCache;
- batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
- batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
- batch->fUseGammaCorrectDistanceTable = useGammaCorrectDistanceTable;
- batch->fFilteredColor = filteredColor;
- batch->fUseBGR = useBGR;
- batch->fBatch.fNumGlyphs = glyphCount;
- batch->fGeoCount = 1;
- return batch;
- }
-
- // to avoid even the initial copy of the struct, we have a getter for the first item which
- // is used to seed the batch with its initial geometry. After seeding, the client should call
- // init() so the Batch can initialize itself
- Geometry& geometry() { return fGeoData[0]; }
-
- void init() {
- const Geometry& geo = fGeoData[0];
- fBatch.fColor = geo.fColor;
- SkRect bounds;
- geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX,
- geo.fY);
- // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
- // we treat this as a set of non-AA rects rendered with a texture.
- this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
- }
-
- const char* name() const override { return "TextBatch"; }
-
- SkString dumpInfo() const override;
-
-protected:
- void computePipelineOptimizations(GrInitInvariantOutput* color,
- GrInitInvariantOutput* coverage,
- GrBatchToXPOverrides* overrides) const override;
-
-
-private:
- void initBatchTracker(const GrXPOverridesForBatch& overrides) override;
-
- struct FlushInfo {
- sk_sp<const GrBuffer> fVertexBuffer;
- sk_sp<const GrBuffer> fIndexBuffer;
- sk_sp<GrGeometryProcessor> fGeometryProcessor;
- int fGlyphsToFlush;
- int fVertexOffset;
- };
-
- void onPrepareDraws(Target* target) const override;
-
- GrAtlasTextBatch() : INHERITED(ClassID()) {} // initialized in factory functions.
-
- ~GrAtlasTextBatch() {
- for (int i = 0; i < fGeoCount; i++) {
- fGeoData[i].fBlob->unref();
- }
- }
-
- GrMaskFormat maskFormat() const {
- switch (fMaskType) {
- case kLCDCoverageMask_MaskType:
- return kA565_GrMaskFormat;
- case kColorBitmapMask_MaskType:
- return kARGB_GrMaskFormat;
- case kGrayscaleCoverageMask_MaskType:
- case kGrayscaleDistanceField_MaskType:
- case kLCDDistanceField_MaskType:
- return kA8_GrMaskFormat;
- }
- return kA8_GrMaskFormat; // suppress warning
- }
-
- bool usesDistanceFields() const {
- return kGrayscaleDistanceField_MaskType == fMaskType ||
- kLCDDistanceField_MaskType == fMaskType;
- }
-
- bool isLCD() const {
- return kLCDCoverageMask_MaskType == fMaskType ||
- kLCDDistanceField_MaskType == fMaskType;
- }
-
- inline void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const;
-
- GrColor color() const { return fBatch.fColor; }
- const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
- bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
- int numGlyphs() const { return fBatch.fNumGlyphs; }
-
- bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override;
-
- // TODO just use class params
- // TODO trying to figure out why lcd is so whack
- sk_sp<GrGeometryProcessor> setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
- GrColor color, GrTexture* texture) const;
-
- struct BatchTracker {
- GrColor fColor;
- bool fUsesLocalCoords;
- bool fColorIgnored;
- bool fCoverageIgnored;
- int fNumGlyphs;
- };
-
- BatchTracker fBatch;
- // The minimum number of Geometry we will try to allocate.
- enum { kMinGeometryAllocated = 4 };
- SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
- int fGeoCount;
-
- enum MaskType {
- kGrayscaleCoverageMask_MaskType,
- kLCDCoverageMask_MaskType,
- kColorBitmapMask_MaskType,
- kGrayscaleDistanceField_MaskType,
- kLCDDistanceField_MaskType,
- } fMaskType;
- bool fUseBGR; // fold this into the enum?
-
- GrBatchFontCache* fFontCache;
-
- // Distance field properties
- sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
- SkColor fFilteredColor;
- bool fUseGammaCorrectDistanceTable;
-
- friend class GrBlobRegenHelper; // Needs to trigger flushes
-
- typedef GrMeshDrawOp INHERITED;
-};
-
-/*
- * A simple helper class to abstract the interface GrAtlasTextBlob needs to regenerate itself.
- * It'd be nicer if this was nested, but we need to forward declare it in GrAtlasTextBlob.h
- */
-class GrBlobRegenHelper {
-public:
- GrBlobRegenHelper(const GrAtlasTextBatch* batch, GrMeshDrawOp::Target* target,
- GrAtlasTextBatch::FlushInfo* flushInfo)
- : fBatch(batch), fTarget(target), fFlushInfo(flushInfo) {}
-
- void flush();
-
- void incGlyphCount(int glyphCount = 1) {
- fFlushInfo->fGlyphsToFlush += glyphCount;
- }
-
-private:
- const GrAtlasTextBatch* fBatch;
- GrMeshDrawOp::Target* fTarget;
- GrAtlasTextBatch::FlushInfo* fFlushInfo;
-};
-
-#endif
--- /dev/null
+/*
+ * 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 "GrAtlasTextOp.h"
+
+#include "GrOpFlushState.h"
+#include "GrResourceProvider.h"
+
+#include "SkGlyphCache.h"
+#include "SkMathPriv.h"
+
+#include "effects/GrBitmapTextGeoProc.h"
+#include "effects/GrDistanceFieldGeoProc.h"
+#include "text/GrBatchFontCache.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
+ unsigned r = SkColorGetR(c);
+ unsigned g = SkColorGetG(c);
+ unsigned b = SkColorGetB(c);
+ return GrColorPackRGBA(r, g, b, 0xff);
+}
+
+static const int kDistanceAdjustLumShift = 5;
+
+SkString GrAtlasTextBatch::dumpInfo() const {
+ SkString str;
+
+ for (int i = 0; i < fGeoCount; ++i) {
+ str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
+ i,
+ fGeoData[i].fColor,
+ fGeoData[i].fX,
+ fGeoData[i].fY,
+ fGeoData[i].fBlob->runCount());
+ }
+
+ str.append(DumpPipelineInfo(*this->pipeline()));
+ str.append(INHERITED::dumpInfo());
+ return str;
+}
+
+void GrAtlasTextBatch::computePipelineOptimizations(GrInitInvariantOutput* color,
+ GrInitInvariantOutput* coverage,
+ GrBatchToXPOverrides* overrides) const {
+ if (kColorBitmapMask_MaskType == fMaskType) {
+ color->setUnknownFourComponents();
+ } else {
+ color->setKnownFourComponents(fBatch.fColor);
+ }
+ switch (fMaskType) {
+ case kGrayscaleDistanceField_MaskType:
+ case kGrayscaleCoverageMask_MaskType:
+ coverage->setUnknownSingleComponent();
+ break;
+ case kLCDCoverageMask_MaskType:
+ case kLCDDistanceField_MaskType:
+ coverage->setUnknownOpaqueFourComponents();
+ coverage->setUsingLCDCoverage();
+ break;
+ case kColorBitmapMask_MaskType:
+ coverage->setKnownSingleComponent(0xff);
+ }
+}
+
+void GrAtlasTextBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
+ // Handle any color overrides
+ if (!overrides.readsColor()) {
+ fGeoData[0].fColor = GrColor_ILLEGAL;
+ }
+ overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
+
+ // setup batch properties
+ fBatch.fColorIgnored = !overrides.readsColor();
+ fBatch.fColor = fGeoData[0].fColor;
+ fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
+ fBatch.fCoverageIgnored = !overrides.readsCoverage();
+}
+
+void GrAtlasTextBatch::onPrepareDraws(Target* target) const {
+ // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
+ // TODO actually only invert if we don't have RGBA
+ SkMatrix localMatrix;
+ if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
+ SkDebugf("Cannot invert viewmatrix\n");
+ return;
+ }
+
+ GrTexture* texture = fFontCache->getTexture(this->maskFormat());
+ if (!texture) {
+ SkDebugf("Could not allocate backing texture for atlas\n");
+ return;
+ }
+
+ GrMaskFormat maskFormat = this->maskFormat();
+
+ FlushInfo flushInfo;
+ if (this->usesDistanceFields()) {
+ flushInfo.fGeometryProcessor =
+ this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture);
+ } else {
+ GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
+ flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(this->color(),
+ texture,
+ params,
+ maskFormat,
+ localMatrix,
+ this->usesLocalCoords());
+ }
+
+ flushInfo.fGlyphsToFlush = 0;
+ size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
+ SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat));
+
+ int glyphCount = this->numGlyphs();
+ const GrBuffer* vertexBuffer;
+
+ void* vertices = target->makeVertexSpace(vertexStride,
+ glyphCount * kVerticesPerGlyph,
+ &vertexBuffer,
+ &flushInfo.fVertexOffset);
+ flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
+ flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
+ if (!vertices || !flushInfo.fVertexBuffer) {
+ SkDebugf("Could not allocate vertices\n");
+ return;
+ }
+
+ unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
+
+ GrBlobRegenHelper helper(this, target, &flushInfo);
+ SkAutoGlyphCache glyphCache;
+ for (int i = 0; i < fGeoCount; i++) {
+ const Geometry& args = fGeoData[i];
+ Blob* blob = args.fBlob;
+ size_t byteCount;
+ void* blobVertices;
+ int subRunGlyphCount;
+ blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache,
+ vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor,
+ &blobVertices, &byteCount, &subRunGlyphCount);
+
+ // now copy all vertices
+ memcpy(currVertex, blobVertices, byteCount);
+
+#ifdef SK_DEBUG
+ // bounds sanity check
+ SkRect rect;
+ rect.setLargestInverted();
+ SkPoint* vertex = (SkPoint*) ((char*)blobVertices);
+ rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount);
+
+ if (this->usesDistanceFields()) {
+ args.fViewMatrix.mapRect(&rect);
+ }
+ // Allow for small numerical error in the bounds.
+ SkRect bounds = this->bounds();
+ bounds.outset(0.001f, 0.001f);
+ SkASSERT(bounds.contains(rect));
+#endif
+
+ currVertex += byteCount;
+ }
+
+ this->flush(target, &flushInfo);
+}
+
+void GrAtlasTextBatch::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
+ GrMesh mesh;
+ int maxGlyphsPerDraw =
+ static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
+ mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
+ flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset,
+ kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
+ maxGlyphsPerDraw);
+ target->draw(flushInfo->fGeometryProcessor.get(), mesh);
+ flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
+ flushInfo->fGlyphsToFlush = 0;
+}
+
+bool GrAtlasTextBatch::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
+ GrAtlasTextBatch* that = t->cast<GrAtlasTextBatch>();
+ if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
+ that->bounds(), caps)) {
+ return false;
+ }
+
+ if (fMaskType != that->fMaskType) {
+ return false;
+ }
+
+ if (!this->usesDistanceFields()) {
+ if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
+ return false;
+ }
+ if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ return false;
+ }
+ } else {
+ if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ return false;
+ }
+
+ if (fFilteredColor != that->fFilteredColor) {
+ return false;
+ }
+
+ if (fUseBGR != that->fUseBGR) {
+ return false;
+ }
+ }
+
+ fBatch.fNumGlyphs += that->numGlyphs();
+
+ // Reallocate space for geo data if necessary and then import that's geo data.
+ int newGeoCount = that->fGeoCount + fGeoCount;
+ // We assume (and here enforce) that the allocation size is the smallest power of two that
+ // is greater than or equal to the number of geometries (and at least
+ // kMinGeometryAllocated).
+ int newAllocSize = GrNextPow2(newGeoCount);
+ int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
+
+ if (newGeoCount > currAllocSize) {
+ fGeoData.realloc(newAllocSize);
+ }
+
+ memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
+ // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
+ // it doesn't try to unref them.
+#ifdef SK_DEBUG
+ for (int i = 0; i < that->fGeoCount; ++i) {
+ that->fGeoData.get()[i].fBlob = (Blob*)0x1;
+ }
+#endif
+ that->fGeoCount = 0;
+ fGeoCount = newGeoCount;
+
+ this->joinBounds(*that);
+ return true;
+}
+
+// TODO just use class params
+// TODO trying to figure out why lcd is so whack
+sk_sp<GrGeometryProcessor> GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatrix,
+ SkColor filteredColor,
+ GrColor color,
+ GrTexture* texture) const {
+ GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
+ bool isLCD = this->isLCD();
+ // set up any flags
+ uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+ flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
+ flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
+
+ // see if we need to create a new effect
+ if (isLCD) {
+ flags |= kUseLCD_DistanceFieldEffectFlag;
+ flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
+
+ GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
+
+ float redCorrection = fDistanceAdjustTable->getAdjustment(
+ GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift,
+ fUseGammaCorrectDistanceTable);
+ float greenCorrection = fDistanceAdjustTable->getAdjustment(
+ GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift,
+ fUseGammaCorrectDistanceTable);
+ float blueCorrection = fDistanceAdjustTable->getAdjustment(
+ GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift,
+ fUseGammaCorrectDistanceTable);
+ GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
+ GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
+ greenCorrection,
+ blueCorrection);
+
+ return GrDistanceFieldLCDTextGeoProc::Make(color,
+ viewMatrix,
+ texture,
+ params,
+ widthAdjust,
+ flags,
+ this->usesLocalCoords());
+ } else {
+#ifdef SK_GAMMA_APPLY_TO_A8
+ U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
+ float correction = fDistanceAdjustTable->getAdjustment(
+ lum >> kDistanceAdjustLumShift, fUseGammaCorrectDistanceTable);
+ return GrDistanceFieldA8TextGeoProc::Make(color,
+ viewMatrix,
+ texture,
+ params,
+ correction,
+ flags,
+ this->usesLocalCoords());
+#else
+ return GrDistanceFieldA8TextGeoProc::Make(color,
+ viewMatrix,
+ texture,
+ params,
+ flags,
+ this->usesLocalCoords());
+#endif
+ }
+
+}
+
+void GrBlobRegenHelper::flush() {
+ fBatch->flush(fTarget, fFlushInfo);
+}
--- /dev/null
+/*
+ * 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 GrAtlasTextBatch_DEFINED
+#define GrAtlasTextBatch_DEFINED
+
+#include "batches/GrMeshDrawOp.h"
+
+#include "text/GrAtlasTextContext.h"
+#include "text/GrDistanceFieldAdjustTable.h"
+
+class GrAtlasTextBatch final : public GrMeshDrawOp {
+public:
+ DEFINE_OP_CLASS_ID
+
+ static const int kVerticesPerGlyph = GrAtlasTextBlob::kVerticesPerGlyph;
+ static const int kIndicesPerGlyph = 6;
+
+ typedef GrAtlasTextBlob Blob;
+ struct Geometry {
+ SkMatrix fViewMatrix;
+ Blob* fBlob;
+ SkScalar fX;
+ SkScalar fY;
+ int fRun;
+ int fSubRun;
+ GrColor fColor;
+ };
+
+ static GrAtlasTextBatch* CreateBitmap(GrMaskFormat maskFormat, int glyphCount,
+ GrBatchFontCache* fontCache) {
+ GrAtlasTextBatch* batch = new GrAtlasTextBatch;
+
+ batch->fFontCache = fontCache;
+ switch (maskFormat) {
+ case kA8_GrMaskFormat:
+ batch->fMaskType = kGrayscaleCoverageMask_MaskType;
+ break;
+ case kA565_GrMaskFormat:
+ batch->fMaskType = kLCDCoverageMask_MaskType;
+ break;
+ case kARGB_GrMaskFormat:
+ batch->fMaskType = kColorBitmapMask_MaskType;
+ break;
+ }
+ batch->fBatch.fNumGlyphs = glyphCount;
+ batch->fGeoCount = 1;
+ batch->fFilteredColor = 0;
+ batch->fFontCache = fontCache;
+ batch->fUseBGR = false;
+ return batch;
+ }
+
+ static GrAtlasTextBatch* CreateDistanceField(
+ int glyphCount, GrBatchFontCache* fontCache,
+ const GrDistanceFieldAdjustTable* distanceAdjustTable,
+ bool useGammaCorrectDistanceTable,
+ SkColor filteredColor, bool isLCD,
+ bool useBGR) {
+ GrAtlasTextBatch* batch = new GrAtlasTextBatch;
+
+ batch->fFontCache = fontCache;
+ batch->fMaskType = isLCD ? kLCDDistanceField_MaskType : kGrayscaleDistanceField_MaskType;
+ batch->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
+ batch->fUseGammaCorrectDistanceTable = useGammaCorrectDistanceTable;
+ batch->fFilteredColor = filteredColor;
+ batch->fUseBGR = useBGR;
+ batch->fBatch.fNumGlyphs = glyphCount;
+ batch->fGeoCount = 1;
+ return batch;
+ }
+
+ // to avoid even the initial copy of the struct, we have a getter for the first item which
+ // is used to seed the batch with its initial geometry. After seeding, the client should call
+ // init() so the Batch can initialize itself
+ Geometry& geometry() { return fGeoData[0]; }
+
+ void init() {
+ const Geometry& geo = fGeoData[0];
+ fBatch.fColor = geo.fColor;
+ SkRect bounds;
+ geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX,
+ geo.fY);
+ // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
+ // we treat this as a set of non-AA rects rendered with a texture.
+ this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
+ }
+
+ const char* name() const override { return "TextBatch"; }
+
+ SkString dumpInfo() const override;
+
+protected:
+ void computePipelineOptimizations(GrInitInvariantOutput* color,
+ GrInitInvariantOutput* coverage,
+ GrBatchToXPOverrides* overrides) const override;
+
+
+private:
+ void initBatchTracker(const GrXPOverridesForBatch& overrides) override;
+
+ struct FlushInfo {
+ sk_sp<const GrBuffer> fVertexBuffer;
+ sk_sp<const GrBuffer> fIndexBuffer;
+ sk_sp<GrGeometryProcessor> fGeometryProcessor;
+ int fGlyphsToFlush;
+ int fVertexOffset;
+ };
+
+ void onPrepareDraws(Target* target) const override;
+
+ GrAtlasTextBatch() : INHERITED(ClassID()) {} // initialized in factory functions.
+
+ ~GrAtlasTextBatch() {
+ for (int i = 0; i < fGeoCount; i++) {
+ fGeoData[i].fBlob->unref();
+ }
+ }
+
+ GrMaskFormat maskFormat() const {
+ switch (fMaskType) {
+ case kLCDCoverageMask_MaskType:
+ return kA565_GrMaskFormat;
+ case kColorBitmapMask_MaskType:
+ return kARGB_GrMaskFormat;
+ case kGrayscaleCoverageMask_MaskType:
+ case kGrayscaleDistanceField_MaskType:
+ case kLCDDistanceField_MaskType:
+ return kA8_GrMaskFormat;
+ }
+ return kA8_GrMaskFormat; // suppress warning
+ }
+
+ bool usesDistanceFields() const {
+ return kGrayscaleDistanceField_MaskType == fMaskType ||
+ kLCDDistanceField_MaskType == fMaskType;
+ }
+
+ bool isLCD() const {
+ return kLCDCoverageMask_MaskType == fMaskType ||
+ kLCDDistanceField_MaskType == fMaskType;
+ }
+
+ inline void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const;
+
+ GrColor color() const { return fBatch.fColor; }
+ const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+ int numGlyphs() const { return fBatch.fNumGlyphs; }
+
+ bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override;
+
+ // TODO just use class params
+ // TODO trying to figure out why lcd is so whack
+ sk_sp<GrGeometryProcessor> setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
+ GrColor color, GrTexture* texture) const;
+
+ struct BatchTracker {
+ GrColor fColor;
+ bool fUsesLocalCoords;
+ bool fColorIgnored;
+ bool fCoverageIgnored;
+ int fNumGlyphs;
+ };
+
+ BatchTracker fBatch;
+ // The minimum number of Geometry we will try to allocate.
+ enum { kMinGeometryAllocated = 4 };
+ SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
+ int fGeoCount;
+
+ enum MaskType {
+ kGrayscaleCoverageMask_MaskType,
+ kLCDCoverageMask_MaskType,
+ kColorBitmapMask_MaskType,
+ kGrayscaleDistanceField_MaskType,
+ kLCDDistanceField_MaskType,
+ } fMaskType;
+ bool fUseBGR; // fold this into the enum?
+
+ GrBatchFontCache* fFontCache;
+
+ // Distance field properties
+ sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
+ SkColor fFilteredColor;
+ bool fUseGammaCorrectDistanceTable;
+
+ friend class GrBlobRegenHelper; // Needs to trigger flushes
+
+ typedef GrMeshDrawOp INHERITED;
+};
+
+/*
+ * A simple helper class to abstract the interface GrAtlasTextBlob needs to regenerate itself.
+ * It'd be nicer if this was nested, but we need to forward declare it in GrAtlasTextBlob.h
+ */
+class GrBlobRegenHelper {
+public:
+ GrBlobRegenHelper(const GrAtlasTextBatch* batch, GrMeshDrawOp::Target* target,
+ GrAtlasTextBatch::FlushInfo* flushInfo)
+ : fBatch(batch), fTarget(target), fFlushInfo(flushInfo) {}
+
+ void flush();
+
+ void incGlyphCount(int glyphCount = 1) {
+ fFlushInfo->fGlyphsToFlush += glyphCount;
+ }
+
+private:
+ const GrAtlasTextBatch* fBatch;
+ GrMeshDrawOp::Target* fTarget;
+ GrAtlasTextBatch::FlushInfo* fFlushInfo;
+};
+
+#endif
#include "SkDrawFilter.h"
#include "SkGlyphCache.h"
#include "SkTextBlobRunIterator.h"
-#include "batches/GrAtlasTextBatch.h"
+#include "batches/GrAtlasTextOp.h"
GrAtlasTextBlob* GrAtlasTextBlob::Create(GrMemoryPool* pool, int glyphCount, int runCount) {
// We allocate size for the GrAtlasTextBlob itself, plus size for the vertices array,
#include "SkDistanceFieldGen.h"
#include "SkGlyphCache.h"
-#include "batches/GrAtlasTextBatch.h"
+#include "batches/GrAtlasTextOp.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// A large template to handle regenerating the vertices of a textblob with as few branches as