• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrAtlasTextOp.h"
9 
10 #include "GrContext.h"
11 #include "GrOpFlushState.h"
12 #include "GrResourceProvider.h"
13 
14 #include "SkGlyphCache.h"
15 #include "SkMathPriv.h"
16 
17 #include "effects/GrBitmapTextGeoProc.h"
18 #include "effects/GrDistanceFieldGeoProc.h"
19 #include "text/GrAtlasGlyphCache.h"
20 
21 ///////////////////////////////////////////////////////////////////////////////////////////////////
22 
skcolor_to_grcolor_nopremultiply(SkColor c)23 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
24     unsigned r = SkColorGetR(c);
25     unsigned g = SkColorGetG(c);
26     unsigned b = SkColorGetB(c);
27     return GrColorPackRGBA(r, g, b, 0xff);
28 }
29 
30 static const int kDistanceAdjustLumShift = 5;
31 
dumpInfo() const32 SkString GrAtlasTextOp::dumpInfo() const {
33     SkString str;
34 
35     for (int i = 0; i < fGeoCount; ++i) {
36         str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
37                     i,
38                     fGeoData[i].fColor,
39                     fGeoData[i].fX,
40                     fGeoData[i].fY,
41                     fGeoData[i].fBlob->runCount());
42     }
43 
44     str.append(DumpPipelineInfo(*this->pipeline()));
45     str.append(INHERITED::dumpInfo());
46     return str;
47 }
48 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const49 void GrAtlasTextOp::getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
50                                                        GrPipelineAnalysisCoverage* coverage) const {
51     if (kColorBitmapMask_MaskType == fMaskType) {
52         color->setToUnknown();
53     } else {
54         color->setToConstant(fColor);
55     }
56     switch (fMaskType) {
57         case kGrayscaleDistanceField_MaskType:
58         case kGrayscaleCoverageMask_MaskType:
59             *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
60             break;
61         case kLCDCoverageMask_MaskType:
62         case kLCDDistanceField_MaskType:
63             *coverage = GrPipelineAnalysisCoverage::kLCD;
64             break;
65         case kColorBitmapMask_MaskType:
66             *coverage = GrPipelineAnalysisCoverage::kNone;
67             break;
68     }
69 }
70 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)71 void GrAtlasTextOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
72     optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
73 
74     fColor = fGeoData[0].fColor;
75     fUsesLocalCoords = optimizations.readsLocalCoords();
76 }
77 
onPrepareDraws(Target * target) const78 void GrAtlasTextOp::onPrepareDraws(Target* target) const {
79     // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
80     // TODO actually only invert if we don't have RGBA
81     SkMatrix localMatrix;
82     if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
83         SkDebugf("Cannot invert viewmatrix\n");
84         return;
85     }
86 
87     sk_sp<GrTextureProxy> proxy = fFontCache->getProxy(this->maskFormat());
88     if (!proxy) {
89         SkDebugf("Could not allocate backing texture for atlas\n");
90         return;
91     }
92 
93     GrMaskFormat maskFormat = this->maskFormat();
94 
95     FlushInfo flushInfo;
96     if (this->usesDistanceFields()) {
97         flushInfo.fGeometryProcessor =
98                 this->setupDfProcessor(fFontCache->context()->resourceProvider(),
99                                        this->viewMatrix(),
100                                        fFilteredColor, this->color(), std::move(proxy));
101     } else {
102         GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
103         flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
104                 fFontCache->context()->resourceProvider(),
105                 this->color(), std::move(proxy), params,
106                 maskFormat, localMatrix, this->usesLocalCoords());
107     }
108 
109     flushInfo.fGlyphsToFlush = 0;
110     size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
111     SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat));
112 
113     int glyphCount = this->numGlyphs();
114     const GrBuffer* vertexBuffer;
115 
116     void* vertices = target->makeVertexSpace(
117             vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset);
118     flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
119     flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
120     if (!vertices || !flushInfo.fVertexBuffer) {
121         SkDebugf("Could not allocate vertices\n");
122         return;
123     }
124 
125     unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
126 
127     GrBlobRegenHelper helper(this, target, &flushInfo);
128     SkAutoGlyphCache glyphCache;
129     for (int i = 0; i < fGeoCount; i++) {
130         const Geometry& args = fGeoData[i];
131         Blob* blob = args.fBlob;
132         size_t byteCount;
133         void* blobVertices;
134         int subRunGlyphCount;
135         blob->regenInOp(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache,
136                         vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor,
137                         &blobVertices, &byteCount, &subRunGlyphCount);
138 
139         // now copy all vertices
140         memcpy(currVertex, blobVertices, byteCount);
141 
142         currVertex += byteCount;
143     }
144 
145     this->flush(target, &flushInfo);
146 }
147 
flush(GrMeshDrawOp::Target * target,FlushInfo * flushInfo) const148 void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
149     GrMesh mesh;
150     int maxGlyphsPerDraw =
151             static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
152     mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
153                        flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerGlyph,
154                        kIndicesPerGlyph, flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
155     target->draw(flushInfo->fGeometryProcessor.get(), mesh);
156     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
157     flushInfo->fGlyphsToFlush = 0;
158 }
159 
onCombineIfPossible(GrOp * t,const GrCaps & caps)160 bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
161     GrAtlasTextOp* that = t->cast<GrAtlasTextOp>();
162     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
163                                 that->bounds(), caps)) {
164         return false;
165     }
166 
167     if (fMaskType != that->fMaskType) {
168         return false;
169     }
170 
171     if (!this->usesDistanceFields()) {
172         if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
173             return false;
174         }
175         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
176             return false;
177         }
178     } else {
179         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
180             return false;
181         }
182 
183         if (fFilteredColor != that->fFilteredColor) {
184             return false;
185         }
186 
187         if (fUseBGR != that->fUseBGR) {
188             return false;
189         }
190     }
191 
192     fNumGlyphs += that->numGlyphs();
193 
194     // Reallocate space for geo data if necessary and then import that's geo data.
195     int newGeoCount = that->fGeoCount + fGeoCount;
196     // We assume (and here enforce) that the allocation size is the smallest power of two that
197     // is greater than or equal to the number of geometries (and at least
198     // kMinGeometryAllocated).
199     int newAllocSize = GrNextPow2(newGeoCount);
200     int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
201 
202     if (newGeoCount > currAllocSize) {
203         fGeoData.realloc(newAllocSize);
204     }
205 
206     // We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that
207     // it doesn't try to unref them.
208     memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
209 #ifdef SK_DEBUG
210     for (int i = 0; i < that->fGeoCount; ++i) {
211         that->fGeoData.get()[i].fBlob = (Blob*)0x1;
212     }
213 #endif
214     that->fGeoCount = 0;
215     fGeoCount = newGeoCount;
216 
217     this->joinBounds(*that);
218     return true;
219 }
220 
221 // TODO just use class params
222 // TODO trying to figure out why lcd is so whack
setupDfProcessor(GrResourceProvider * resourceProvider,const SkMatrix & viewMatrix,SkColor filteredColor,GrColor color,sk_sp<GrTextureProxy> proxy) const223 sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(GrResourceProvider* resourceProvider,
224                                                            const SkMatrix& viewMatrix,
225                                                            SkColor filteredColor,
226                                                            GrColor color,
227                                                            sk_sp<GrTextureProxy> proxy) const {
228     GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
229     bool isLCD = this->isLCD();
230     // set up any flags
231     uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
232     flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
233     flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
234 
235     // see if we need to create a new effect
236     if (isLCD) {
237         flags |= kUseLCD_DistanceFieldEffectFlag;
238         flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
239 
240         GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
241 
242         float redCorrection = fDistanceAdjustTable->getAdjustment(
243                 GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift,
244                 fUseGammaCorrectDistanceTable);
245         float greenCorrection = fDistanceAdjustTable->getAdjustment(
246                 GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift,
247                 fUseGammaCorrectDistanceTable);
248         float blueCorrection = fDistanceAdjustTable->getAdjustment(
249                 GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift,
250                 fUseGammaCorrectDistanceTable);
251         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
252                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
253                         redCorrection, greenCorrection, blueCorrection);
254 
255         return GrDistanceFieldLCDTextGeoProc::Make(resourceProvider,
256                                                    color, viewMatrix, std::move(proxy),
257                                                    params, widthAdjust, flags,
258                                                    this->usesLocalCoords());
259     } else {
260 #ifdef SK_GAMMA_APPLY_TO_A8
261         U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
262         float correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
263                                                                fUseGammaCorrectDistanceTable);
264         return GrDistanceFieldA8TextGeoProc::Make(resourceProvider, color,
265                                                   viewMatrix, std::move(proxy),
266                                                   params, correction, flags,
267                                                   this->usesLocalCoords());
268 #else
269         return GrDistanceFieldA8TextGeoProc::Make(resourceProvider, color,
270                                                   viewMatrix, std::move(proxy),
271                                                   params, flags, this->usesLocalCoords());
272 #endif
273     }
274 }
275 
flush()276 void GrBlobRegenHelper::flush() { fOp->flush(fTarget, fFlushInfo); }
277