• 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 #include "GrContext.h"
10 #include "GrOpFlushState.h"
11 #include "GrResourceProvider.h"
12 #include "SkGlyphCache.h"
13 #include "SkMathPriv.h"
14 #include "SkMatrixPriv.h"
15 #include "SkPoint3.h"
16 #include "effects/GrBitmapTextGeoProc.h"
17 #include "effects/GrDistanceFieldGeoProc.h"
18 #include "text/GrAtlasGlyphCache.h"
19 
20 ///////////////////////////////////////////////////////////////////////////////////////////////////
21 
22 static const int kDistanceAdjustLumShift = 5;
23 
init()24 void GrAtlasTextOp::init() {
25     const Geometry& geo = fGeoData[0];
26     SkRect bounds;
27     geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX, geo.fY);
28     // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
29     // we treat this as a set of non-AA rects rendered with a texture.
30     this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
31     if (this->usesDistanceFields()) {
32         bool isLCD = this->isLCD();
33 
34         const SkMatrix& viewMatrix = geo.fViewMatrix;
35 
36         fDFGPFlags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
37         fDFGPFlags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
38         fDFGPFlags |= viewMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
39         fDFGPFlags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
40         fDFGPFlags |= (kAliasedDistanceField_MaskType == fMaskType)
41                               ? kAliased_DistanceFieldEffectFlag
42                               : 0;
43 
44         if (isLCD) {
45             fDFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
46             fDFGPFlags |=
47                     (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
48         }
49     }
50 }
51 
dumpInfo() const52 SkString GrAtlasTextOp::dumpInfo() const {
53     SkString str;
54 
55     for (int i = 0; i < fGeoCount; ++i) {
56         str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
57                     i,
58                     fGeoData[i].fColor,
59                     fGeoData[i].fX,
60                     fGeoData[i].fY,
61                     fGeoData[i].fBlob->runCount());
62     }
63 
64     str += fProcessors.dumpProcessors();
65     str += INHERITED::dumpInfo();
66     return str;
67 }
68 
fixedFunctionFlags() const69 GrDrawOp::FixedFunctionFlags GrAtlasTextOp::fixedFunctionFlags() const {
70     return FixedFunctionFlags::kNone;
71 }
72 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrPixelConfigIsClamped dstIsClamped)73 GrDrawOp::RequiresDstTexture GrAtlasTextOp::finalize(const GrCaps& caps,
74                                                      const GrAppliedClip* clip,
75                                                      GrPixelConfigIsClamped dstIsClamped) {
76     GrProcessorAnalysisCoverage coverage;
77     GrProcessorAnalysisColor color;
78     if (kColorBitmapMask_MaskType == fMaskType) {
79         color.setToUnknown();
80     } else {
81         color.setToConstant(this->color());
82     }
83     switch (fMaskType) {
84         case kGrayscaleCoverageMask_MaskType:
85         case kAliasedDistanceField_MaskType:
86         case kGrayscaleDistanceField_MaskType:
87             coverage = GrProcessorAnalysisCoverage::kSingleChannel;
88             break;
89         case kLCDCoverageMask_MaskType:
90         case kLCDDistanceField_MaskType:
91         case kLCDBGRDistanceField_MaskType:
92             coverage = GrProcessorAnalysisCoverage::kLCD;
93             break;
94         case kColorBitmapMask_MaskType:
95             coverage = GrProcessorAnalysisCoverage::kNone;
96             break;
97     }
98     auto analysis = fProcessors.finalize(color, coverage, clip, false, caps, dstIsClamped,
99                                          &fGeoData[0].fColor);
100     fUsesLocalCoords = analysis.usesLocalCoords();
101     fCanCombineOnTouchOrOverlap =
102             !analysis.requiresDstTexture() &&
103             !(fProcessors.xferProcessor() && fProcessors.xferProcessor()->xferBarrierType(caps));
104     return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
105 }
106 
clip_quads(const SkIRect & clipRect,char * currVertex,const char * blobVertices,size_t vertexStride,int glyphCount)107 static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices,
108                        size_t vertexStride, int glyphCount) {
109     for (int i = 0; i < glyphCount; ++i) {
110         const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices);
111         const SkPoint* blobPositionRB =
112                 reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride);
113 
114         // positions for bitmap glyphs are pixel boundary aligned
115         SkIRect positionRect = SkIRect::MakeLTRB(SkScalarRoundToInt(blobPositionLT->fX),
116                                                  SkScalarRoundToInt(blobPositionLT->fY),
117                                                  SkScalarRoundToInt(blobPositionRB->fX),
118                                                  SkScalarRoundToInt(blobPositionRB->fY));
119         if (clipRect.contains(positionRect)) {
120             memcpy(currVertex, blobVertices, 4 * vertexStride);
121             currVertex += 4 * vertexStride;
122         } else {
123             // Pull out some more data that we'll need.
124             // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
125             // and it avoids a lot of conditionals.
126             auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint));
127             size_t coordOffset = vertexStride - 2*sizeof(uint16_t);
128             auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset);
129             auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride +
130                                                                    coordOffset);
131             // Pull out the texel coordinates and texture index bits
132             uint16_t coordsRectL = blobCoordsLT[0] >> 1;
133             uint16_t coordsRectT = blobCoordsLT[1] >> 1;
134             uint16_t coordsRectR = blobCoordsRB[0] >> 1;
135             uint16_t coordsRectB = blobCoordsRB[1] >> 1;
136             uint16_t pageIndexX = blobCoordsLT[0] & 0x1;
137             uint16_t pageIndexY = blobCoordsLT[1] & 0x1;
138 
139             int positionRectWidth = positionRect.width();
140             int positionRectHeight = positionRect.height();
141             SkASSERT(positionRectWidth == (coordsRectR - coordsRectL));
142             SkASSERT(positionRectHeight == (coordsRectB - coordsRectT));
143 
144             // Clip position and texCoords to the clipRect
145             unsigned int delta;
146             delta = SkTMin(SkTMax(clipRect.fLeft - positionRect.fLeft, 0), positionRectWidth);
147             coordsRectL += delta;
148             positionRect.fLeft += delta;
149 
150             delta = SkTMin(SkTMax(clipRect.fTop - positionRect.fTop, 0), positionRectHeight);
151             coordsRectT += delta;
152             positionRect.fTop += delta;
153 
154             delta = SkTMin(SkTMax(positionRect.fRight - clipRect.fRight, 0), positionRectWidth);
155             coordsRectR -= delta;
156             positionRect.fRight -= delta;
157 
158             delta = SkTMin(SkTMax(positionRect.fBottom - clipRect.fBottom, 0), positionRectHeight);
159             coordsRectB -= delta;
160             positionRect.fBottom -= delta;
161 
162             // Repack texel coordinates and index
163             coordsRectL = coordsRectL << 1 | pageIndexX;
164             coordsRectT = coordsRectT << 1 | pageIndexY;
165             coordsRectR = coordsRectR << 1 | pageIndexX;
166             coordsRectB = coordsRectB << 1 | pageIndexY;
167 
168             // Set new positions and coords
169             SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex);
170             currPosition->fX = positionRect.fLeft;
171             currPosition->fY = positionRect.fTop;
172             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
173             uint16_t* currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
174             currCoords[0] = coordsRectL;
175             currCoords[1] = coordsRectT;
176             currVertex += vertexStride;
177 
178             currPosition = reinterpret_cast<SkPoint*>(currVertex);
179             currPosition->fX = positionRect.fLeft;
180             currPosition->fY = positionRect.fBottom;
181             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
182             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
183             currCoords[0] = coordsRectL;
184             currCoords[1] = coordsRectB;
185             currVertex += vertexStride;
186 
187             currPosition = reinterpret_cast<SkPoint*>(currVertex);
188             currPosition->fX = positionRect.fRight;
189             currPosition->fY = positionRect.fTop;
190             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
191             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
192             currCoords[0] = coordsRectR;
193             currCoords[1] = coordsRectT;
194             currVertex += vertexStride;
195 
196             currPosition = reinterpret_cast<SkPoint*>(currVertex);
197             currPosition->fX = positionRect.fRight;
198             currPosition->fY = positionRect.fBottom;
199             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
200             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
201             currCoords[0] = coordsRectR;
202             currCoords[1] = coordsRectB;
203             currVertex += vertexStride;
204         }
205 
206         blobVertices += 4 * vertexStride;
207     }
208 }
209 
onPrepareDraws(Target * target)210 void GrAtlasTextOp::onPrepareDraws(Target* target) {
211     // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
212     // TODO actually only invert if we don't have RGBA
213     SkMatrix localMatrix;
214     if (this->usesLocalCoords() && !fGeoData[0].fViewMatrix.invert(&localMatrix)) {
215         SkDebugf("Cannot invert viewmatrix\n");
216         return;
217     }
218 
219     GrMaskFormat maskFormat = this->maskFormat();
220 
221     uint32_t atlasPageCount = fFontCache->getAtlasPageCount(maskFormat);
222     const sk_sp<GrTextureProxy>* proxies = fFontCache->getProxies(maskFormat);
223     if (!atlasPageCount || !proxies[0]) {
224         SkDebugf("Could not allocate backing texture for atlas\n");
225         return;
226     }
227 
228     FlushInfo flushInfo;
229     flushInfo.fPipeline =
230             target->makePipeline(fSRGBFlags, std::move(fProcessors), target->detachAppliedClip());
231     SkDEBUGCODE(bool dfPerspective = false);
232     if (this->usesDistanceFields()) {
233         flushInfo.fGeometryProcessor = this->setupDfProcessor();
234         SkDEBUGCODE(dfPerspective = fGeoData[0].fViewMatrix.hasPerspective());
235     } else {
236         flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
237             this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat,
238             localMatrix, this->usesLocalCoords());
239     }
240 
241     flushInfo.fGlyphsToFlush = 0;
242     size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
243     SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat, dfPerspective));
244 
245     int glyphCount = this->numGlyphs();
246     const GrBuffer* vertexBuffer;
247 
248     void* vertices = target->makeVertexSpace(
249             vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset);
250     flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
251     flushInfo.fIndexBuffer = target->resourceProvider()->refQuadIndexBuffer();
252     if (!vertices || !flushInfo.fVertexBuffer) {
253         SkDebugf("Could not allocate vertices\n");
254         return;
255     }
256 
257     char* currVertex = reinterpret_cast<char*>(vertices);
258 
259     SkAutoGlyphCache glyphCache;
260     // each of these is a SubRun
261     for (int i = 0; i < fGeoCount; i++) {
262         const Geometry& args = fGeoData[i];
263         Blob* blob = args.fBlob;
264         GrAtlasTextBlob::VertexRegenerator regenerator(
265                 blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY, args.fColor,
266                 target->deferredUploadTarget(), fFontCache, &glyphCache);
267         GrAtlasTextBlob::VertexRegenerator::Result result;
268         do {
269             result = regenerator.regenerate();
270             // Copy regenerated vertices from the blob to our vertex buffer.
271             size_t vertexBytes = result.fGlyphsRegenerated * kVerticesPerGlyph * vertexStride;
272             if (args.fClipRect.isEmpty()) {
273                 memcpy(currVertex, result.fFirstVertex, vertexBytes);
274             } else {
275                 SkASSERT(!dfPerspective);
276                 clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
277                            result.fGlyphsRegenerated);
278             }
279             if (this->usesDistanceFields() && !args.fViewMatrix.isIdentity()) {
280                 // We always do the distance field view matrix transformation after copying rather
281                 // than during blob vertex generation time in the blob as handling successive
282                 // arbitrary transformations would be complicated and accumulate error.
283                 if (args.fViewMatrix.hasPerspective()) {
284                     auto* pos = reinterpret_cast<SkPoint3*>(currVertex);
285                     SkMatrixPriv::MapHomogeneousPointsWithStride(
286                             args.fViewMatrix, pos, vertexStride, pos, vertexStride,
287                             result.fGlyphsRegenerated * kVerticesPerGlyph);
288                 } else {
289                     auto* pos = reinterpret_cast<SkPoint*>(currVertex);
290                     SkMatrixPriv::MapPointsWithStride(
291                             args.fViewMatrix, pos, vertexStride,
292                             result.fGlyphsRegenerated * kVerticesPerGlyph);
293                 }
294             }
295             flushInfo.fGlyphsToFlush += result.fGlyphsRegenerated;
296             if (!result.fFinished) {
297                 this->flush(target, &flushInfo);
298             }
299             currVertex += vertexBytes;
300         } while (!result.fFinished);
301     }
302     this->flush(target, &flushInfo);
303 }
304 
flush(GrMeshDrawOp::Target * target,FlushInfo * flushInfo) const305 void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
306     GrGeometryProcessor* gp = flushInfo->fGeometryProcessor.get();
307     GrMaskFormat maskFormat = this->maskFormat();
308     if (gp->numTextureSamplers() != (int)fFontCache->getAtlasPageCount(maskFormat)) {
309         // During preparation the number of atlas pages has increased.
310         // Update the proxies used in the GP to match.
311         if (this->usesDistanceFields()) {
312             if (this->isLCD()) {
313                 reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->addNewProxies(
314                     fFontCache->getProxies(maskFormat), GrSamplerState::ClampBilerp());
315             } else {
316                 reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->addNewProxies(
317                     fFontCache->getProxies(maskFormat), GrSamplerState::ClampBilerp());
318             }
319         } else {
320             reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewProxies(
321                 fFontCache->getProxies(maskFormat), GrSamplerState::ClampNearest());
322         }
323     }
324 
325     GrMesh mesh(GrPrimitiveType::kTriangles);
326     int maxGlyphsPerDraw =
327             static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
328     mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerGlyph, kVerticesPerGlyph,
329                              flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
330     mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
331     target->draw(flushInfo->fGeometryProcessor.get(), flushInfo->fPipeline, mesh);
332     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
333     flushInfo->fGlyphsToFlush = 0;
334 }
335 
onCombineIfPossible(GrOp * t,const GrCaps & caps)336 bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
337     GrAtlasTextOp* that = t->cast<GrAtlasTextOp>();
338     if (fProcessors != that->fProcessors) {
339         return false;
340     }
341 
342     if (!fCanCombineOnTouchOrOverlap && GrRectsTouchOrOverlap(this->bounds(), that->bounds())) {
343         return false;
344     }
345 
346     if (fMaskType != that->fMaskType) {
347         return false;
348     }
349 
350     const SkMatrix& thisFirstMatrix = fGeoData[0].fViewMatrix;
351     const SkMatrix& thatFirstMatrix = that->fGeoData[0].fViewMatrix;
352 
353     if (this->usesLocalCoords() && !thisFirstMatrix.cheapEqualTo(thatFirstMatrix)) {
354         return false;
355     }
356 
357     if (this->usesDistanceFields()) {
358         if (fDFGPFlags != that->fDFGPFlags) {
359             return false;
360         }
361 
362         if (fLuminanceColor != that->fLuminanceColor) {
363             return false;
364         }
365     } else {
366         if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
367             return false;
368         }
369     }
370 
371     // Keep the batch vertex buffer size below 32K so we don't have to create a special one
372     // We use the largest possible vertex size for this
373     static const int kVertexSize = sizeof(SkPoint) + sizeof(SkColor) + 2 * sizeof(uint16_t);
374     static const int kMaxGlyphs = 32768 / (kVerticesPerGlyph * kVertexSize);
375     if (this->fNumGlyphs + that->fNumGlyphs > kMaxGlyphs) {
376         return false;
377     }
378 
379     fNumGlyphs += that->numGlyphs();
380 
381     // Reallocate space for geo data if necessary and then import that geo's data.
382     int newGeoCount = that->fGeoCount + fGeoCount;
383 
384     // We reallocate at a rate of 1.5x to try to get better total memory usage
385     if (newGeoCount > fGeoDataAllocSize) {
386         int newAllocSize = fGeoDataAllocSize + fGeoDataAllocSize / 2;
387         while (newAllocSize < newGeoCount) {
388             newAllocSize += newAllocSize / 2;
389         }
390         fGeoData.realloc(newAllocSize);
391         fGeoDataAllocSize = newAllocSize;
392     }
393 
394     // We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that
395     // it doesn't try to unref them.
396     memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
397 #ifdef SK_DEBUG
398     for (int i = 0; i < that->fGeoCount; ++i) {
399         that->fGeoData.get()[i].fBlob = (Blob*)0x1;
400     }
401 #endif
402     that->fGeoCount = 0;
403     fGeoCount = newGeoCount;
404 
405     this->joinBounds(*that);
406     return true;
407 }
408 
409 // TODO trying to figure out why lcd is so whack
410 // (see comments in GrAtlasTextContext::ComputeCanonicalColor)
setupDfProcessor() const411 sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
412     const sk_sp<GrTextureProxy>* p = fFontCache->getProxies(this->maskFormat());
413     bool isLCD = this->isLCD();
414 
415     SkMatrix localMatrix = SkMatrix::I();
416     if (this->usesLocalCoords()) {
417         // If this fails we'll just use I().
418         bool result = fGeoData[0].fViewMatrix.invert(&localMatrix);
419         (void)result;
420     }
421 
422     // see if we need to create a new effect
423     if (isLCD) {
424         float redCorrection = fDistanceAdjustTable->getAdjustment(
425                 SkColorGetR(fLuminanceColor) >> kDistanceAdjustLumShift,
426                 fUseGammaCorrectDistanceTable);
427         float greenCorrection = fDistanceAdjustTable->getAdjustment(
428                 SkColorGetG(fLuminanceColor) >> kDistanceAdjustLumShift,
429                 fUseGammaCorrectDistanceTable);
430         float blueCorrection = fDistanceAdjustTable->getAdjustment(
431                 SkColorGetB(fLuminanceColor) >> kDistanceAdjustLumShift,
432                 fUseGammaCorrectDistanceTable);
433         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
434                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
435                         redCorrection, greenCorrection, blueCorrection);
436         return GrDistanceFieldLCDTextGeoProc::Make(p, GrSamplerState::ClampBilerp(), widthAdjust,
437                                                    fDFGPFlags, localMatrix);
438     } else {
439 #ifdef SK_GAMMA_APPLY_TO_A8
440         float correction = 0;
441         if (kAliasedDistanceField_MaskType != fMaskType) {
442             U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT,
443                                                                 fLuminanceColor);
444             correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
445                                                              fUseGammaCorrectDistanceTable);
446         }
447         return GrDistanceFieldA8TextGeoProc::Make(p, GrSamplerState::ClampBilerp(),
448                                                   correction, fDFGPFlags, localMatrix);
449 #else
450         return GrDistanceFieldA8TextGeoProc::Make(p, GrSamplerState::ClampBilerp(),
451                                                   fDFGPFlags, localMatrix);
452 #endif
453     }
454 }
455 
456