• 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 "GrAtlasTextBatch.h"
9 
10 #include "GrBatchFlushState.h"
11 #include "GrResourceProvider.h"
12 
13 #include "SkGlyphCache.h"
14 
15 #include "effects/GrBitmapTextGeoProc.h"
16 #include "effects/GrDistanceFieldGeoProc.h"
17 #include "text/GrBatchFontCache.h"
18 
19 ///////////////////////////////////////////////////////////////////////////////////////////////////
20 
skcolor_to_grcolor_nopremultiply(SkColor c)21 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
22     unsigned r = SkColorGetR(c);
23     unsigned g = SkColorGetG(c);
24     unsigned b = SkColorGetB(c);
25     return GrColorPackRGBA(r, g, b, 0xff);
26 }
27 
28 static const int kDistanceAdjustLumShift = 5;
29 
dumpInfo() const30 SkString GrAtlasTextBatch::dumpInfo() const {
31     SkString str;
32 
33     for (int i = 0; i < fGeoCount; ++i) {
34         str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
35                     i,
36                     fGeoData[i].fColor,
37                     fGeoData[i].fX,
38                     fGeoData[i].fY,
39                     fGeoData[i].fBlob->runCount());
40     }
41 
42     str.append(INHERITED::dumpInfo());
43     return str;
44 }
45 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const46 void GrAtlasTextBatch::computePipelineOptimizations(GrInitInvariantOutput* color,
47                                                     GrInitInvariantOutput* coverage,
48                                                     GrBatchToXPOverrides* overrides) const {
49     if (kColorBitmapMask_MaskType == fMaskType) {
50         color->setUnknownFourComponents();
51     } else {
52         color->setKnownFourComponents(fBatch.fColor);
53     }
54     switch (fMaskType) {
55         case kGrayscaleDistanceField_MaskType:
56         case kGrayscaleCoverageMask_MaskType:
57             coverage->setUnknownSingleComponent();
58             break;
59         case kLCDCoverageMask_MaskType:
60         case kLCDDistanceField_MaskType:
61             coverage->setUnknownOpaqueFourComponents();
62             coverage->setUsingLCDCoverage();
63             break;
64         case kColorBitmapMask_MaskType:
65             coverage->setKnownSingleComponent(0xff);
66     }
67 }
68 
initBatchTracker(const GrXPOverridesForBatch & overrides)69 void GrAtlasTextBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
70     // Handle any color overrides
71     if (!overrides.readsColor()) {
72         fGeoData[0].fColor = GrColor_ILLEGAL;
73     }
74     overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
75 
76     // setup batch properties
77     fBatch.fColorIgnored = !overrides.readsColor();
78     fBatch.fColor = fGeoData[0].fColor;
79     fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
80     fBatch.fCoverageIgnored = !overrides.readsCoverage();
81 }
82 
onPrepareDraws(Target * target) const83 void GrAtlasTextBatch::onPrepareDraws(Target* target) const {
84     // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
85     // TODO actually only invert if we don't have RGBA
86     SkMatrix localMatrix;
87     if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
88         SkDebugf("Cannot invert viewmatrix\n");
89         return;
90     }
91 
92     GrTexture* texture = fFontCache->getTexture(this->maskFormat());
93     if (!texture) {
94         SkDebugf("Could not allocate backing texture for atlas\n");
95         return;
96     }
97 
98     GrMaskFormat maskFormat = this->maskFormat();
99 
100     SkAutoTUnref<const GrGeometryProcessor> gp;
101     if (this->usesDistanceFields()) {
102         gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
103                                         texture));
104     } else {
105         GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
106         gp.reset(GrBitmapTextGeoProc::Create(this->color(),
107                                              texture,
108                                              params,
109                                              maskFormat,
110                                              localMatrix,
111                                              this->usesLocalCoords()));
112     }
113 
114     FlushInfo flushInfo;
115     flushInfo.fGlyphsToFlush = 0;
116     size_t vertexStride = gp->getVertexStride();
117     SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat));
118 
119     target->initDraw(gp, this->pipeline());
120 
121     int glyphCount = this->numGlyphs();
122     const GrVertexBuffer* vertexBuffer;
123 
124     void* vertices = target->makeVertexSpace(vertexStride,
125                                              glyphCount * kVerticesPerGlyph,
126                                              &vertexBuffer,
127                                              &flushInfo.fVertexOffset);
128     flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
129     flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
130     if (!vertices || !flushInfo.fVertexBuffer) {
131         SkDebugf("Could not allocate vertices\n");
132         return;
133     }
134 
135     unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
136 
137     // We cache some values to avoid going to the glyphcache for the same fontScaler twice
138     // in a row
139     const SkDescriptor* desc = nullptr;
140     SkGlyphCache* cache = nullptr;
141     GrFontScaler* scaler = nullptr;
142     SkTypeface* typeface = nullptr;
143 
144     GrBlobRegenHelper helper(this, target, &flushInfo, gp);
145 
146     for (int i = 0; i < fGeoCount; i++) {
147         const Geometry& args = fGeoData[i];
148         Blob* blob = args.fBlob;
149         size_t byteCount;
150         void* blobVertices;
151         int subRunGlyphCount;
152         blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun, &cache,
153                            &typeface, &scaler, &desc, vertexStride, args.fViewMatrix, args.fX,
154                            args.fY, args.fColor, &blobVertices, &byteCount, &subRunGlyphCount);
155 
156         // now copy all vertices
157         memcpy(currVertex, blobVertices, byteCount);
158 
159 #ifdef SK_DEBUG
160         // bounds sanity check
161         SkRect rect;
162         rect.setLargestInverted();
163         SkPoint* vertex = (SkPoint*) ((char*)blobVertices);
164         rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount);
165 
166         if (this->usesDistanceFields()) {
167             args.fViewMatrix.mapRect(&rect);
168         }
169         SkASSERT(fBounds.contains(rect));
170 #endif
171 
172         currVertex += byteCount;
173     }
174 
175     // Make sure to attach the last cache if applicable
176     if (cache) {
177         SkGlyphCache::AttachCache(cache);
178     }
179     this->flush(target, &flushInfo);
180 }
181 
flush(GrVertexBatch::Target * target,FlushInfo * flushInfo) const182 void GrAtlasTextBatch::flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const {
183     GrVertices vertices;
184     int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
185     vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
186                            flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
187                            kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
188                            maxGlyphsPerDraw);
189     target->draw(vertices);
190     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
191     flushInfo->fGlyphsToFlush = 0;
192 }
193 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)194 bool GrAtlasTextBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
195     GrAtlasTextBatch* that = t->cast<GrAtlasTextBatch>();
196     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
197                                 that->bounds(), caps)) {
198         return false;
199     }
200 
201     if (fMaskType != that->fMaskType) {
202         return false;
203     }
204 
205     if (!this->usesDistanceFields()) {
206         if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
207             return false;
208         }
209         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
210             return false;
211         }
212     } else {
213         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
214             return false;
215         }
216 
217         if (fFilteredColor != that->fFilteredColor) {
218             return false;
219         }
220 
221         if (fUseBGR != that->fUseBGR) {
222             return false;
223         }
224     }
225 
226     fBatch.fNumGlyphs += that->numGlyphs();
227 
228     // Reallocate space for geo data if necessary and then import that's geo data.
229     int newGeoCount = that->fGeoCount + fGeoCount;
230     // We assume (and here enforce) that the allocation size is the smallest power of two that
231     // is greater than or equal to the number of geometries (and at least
232     // kMinGeometryAllocated).
233     int newAllocSize = GrNextPow2(newGeoCount);
234     int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
235 
236     if (newGeoCount > currAllocSize) {
237         fGeoData.realloc(newAllocSize);
238     }
239 
240     memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
241     // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
242     // it doesn't try to unref them.
243 #ifdef SK_DEBUG
244     for (int i = 0; i < that->fGeoCount; ++i) {
245         that->fGeoData.get()[i].fBlob = (Blob*)0x1;
246     }
247 #endif
248     that->fGeoCount = 0;
249     fGeoCount = newGeoCount;
250 
251     this->joinBounds(that->bounds());
252     return true;
253 }
254 
255 // TODO just use class params
256 // TODO trying to figure out why lcd is so whack
setupDfProcessor(const SkMatrix & viewMatrix,SkColor filteredColor,GrColor color,GrTexture * texture) const257 GrGeometryProcessor* GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatrix,
258                                                         SkColor filteredColor,
259                                                         GrColor color, GrTexture* texture) const {
260     GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
261     bool isLCD = this->isLCD();
262     // set up any flags
263     uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
264 
265     // see if we need to create a new effect
266     if (isLCD) {
267         flags |= kUseLCD_DistanceFieldEffectFlag;
268         flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFlag : 0;
269         flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
270 
271         GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
272 
273         float redCorrection =
274             (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
275         float greenCorrection =
276             (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
277         float blueCorrection =
278             (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
279         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
280             GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
281                                                                 greenCorrection,
282                                                                 blueCorrection);
283 
284         return GrDistanceFieldLCDTextGeoProc::Create(color,
285                                                      viewMatrix,
286                                                      texture,
287                                                      params,
288                                                      widthAdjust,
289                                                      flags,
290                                                      this->usesLocalCoords());
291     } else {
292 #ifdef SK_GAMMA_APPLY_TO_A8
293         U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
294         float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
295         return GrDistanceFieldA8TextGeoProc::Create(color,
296                                                     viewMatrix,
297                                                     texture,
298                                                     params,
299                                                     correction,
300                                                     flags,
301                                                     this->usesLocalCoords());
302 #else
303         return GrDistanceFieldA8TextGeoProc::Create(color,
304                                                     viewMatrix,
305                                                     texture,
306                                                     params,
307                                                     flags,
308                                                     this->usesLocalCoords());
309 #endif
310     }
311 
312 }
313 
flush()314 void GrBlobRegenHelper::flush() {
315     fBatch->flush(fTarget, fFlushInfo);
316     fTarget->initDraw(fGP, fBatch->pipeline());
317 }
318