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