• 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 "src/gpu/ops/GrAtlasTextOp.h"
9 
10 #include "include/core/SkPoint3.h"
11 #include "include/private/GrRecordingContext.h"
12 #include "src/core/SkMathPriv.h"
13 #include "src/core/SkMatrixPriv.h"
14 #include "src/core/SkStrikeCache.h"
15 #include "src/gpu/GrCaps.h"
16 #include "src/gpu/GrMemoryPool.h"
17 #include "src/gpu/GrOpFlushState.h"
18 #include "src/gpu/GrRecordingContextPriv.h"
19 #include "src/gpu/GrResourceProvider.h"
20 #include "src/gpu/effects/GrBitmapTextGeoProc.h"
21 #include "src/gpu/effects/GrDistanceFieldGeoProc.h"
22 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
23 #include "src/gpu/text/GrAtlasManager.h"
24 #include "src/gpu/text/GrStrikeCache.h"
25 
26 ///////////////////////////////////////////////////////////////////////////////////////////////////
27 
MakeBitmap(GrRecordingContext * context,GrPaint && paint,GrMaskFormat maskFormat,int glyphCount,bool needsTransform)28 std::unique_ptr<GrAtlasTextOp> GrAtlasTextOp::MakeBitmap(GrRecordingContext* context,
29                                                          GrPaint&& paint,
30                                                          GrMaskFormat maskFormat,
31                                                          int glyphCount,
32                                                          bool needsTransform) {
33         GrOpMemoryPool* pool = context->priv().opMemoryPool();
34 
35         std::unique_ptr<GrAtlasTextOp> op = pool->allocate<GrAtlasTextOp>(std::move(paint));
36 
37         switch (maskFormat) {
38             case kA8_GrMaskFormat:
39                 op->fMaskType = kGrayscaleCoverageMask_MaskType;
40                 break;
41             case kA565_GrMaskFormat:
42                 op->fMaskType = kLCDCoverageMask_MaskType;
43                 break;
44             case kARGB_GrMaskFormat:
45                 op->fMaskType = kColorBitmapMask_MaskType;
46                 break;
47         }
48         op->fNumGlyphs = glyphCount;
49         op->fGeoCount = 1;
50         op->fLuminanceColor = 0;
51         op->fNeedsGlyphTransform = needsTransform;
52         return op;
53     }
54 
MakeDistanceField(GrRecordingContext * context,GrPaint && paint,int glyphCount,const GrDistanceFieldAdjustTable * distanceAdjustTable,bool useGammaCorrectDistanceTable,SkColor luminanceColor,const SkSurfaceProps & props,bool isAntiAliased,bool useLCD)55 std::unique_ptr<GrAtlasTextOp> GrAtlasTextOp::MakeDistanceField(
56                                             GrRecordingContext* context,
57                                             GrPaint&& paint,
58                                             int glyphCount,
59                                             const GrDistanceFieldAdjustTable* distanceAdjustTable,
60                                             bool useGammaCorrectDistanceTable,
61                                             SkColor luminanceColor,
62                                             const SkSurfaceProps& props,
63                                             bool isAntiAliased,
64                                             bool useLCD) {
65         GrOpMemoryPool* pool = context->priv().opMemoryPool();
66 
67         std::unique_ptr<GrAtlasTextOp> op = pool->allocate<GrAtlasTextOp>(std::move(paint));
68 
69         bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
70         bool isLCD = useLCD && SkPixelGeometryIsH(props.pixelGeometry());
71         op->fMaskType = !isAntiAliased ? kAliasedDistanceField_MaskType
72                                        : isLCD ? (isBGR ? kLCDBGRDistanceField_MaskType
73                                                         : kLCDDistanceField_MaskType)
74                                                : kGrayscaleDistanceField_MaskType;
75         op->fDistanceAdjustTable.reset(SkRef(distanceAdjustTable));
76         op->fUseGammaCorrectDistanceTable = useGammaCorrectDistanceTable;
77         op->fLuminanceColor = luminanceColor;
78         op->fNumGlyphs = glyphCount;
79         op->fGeoCount = 1;
80         return op;
81     }
82 
83 static const int kDistanceAdjustLumShift = 5;
84 
init()85 void GrAtlasTextOp::init() {
86     const Geometry& geo = fGeoData[0];
87     if (this->usesDistanceFields()) {
88         bool isLCD = this->isLCD();
89 
90         const SkMatrix& drawMatrix = geo.fDrawMatrix;
91 
92         fDFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
93         fDFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
94         fDFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
95         fDFGPFlags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
96         fDFGPFlags |= (kAliasedDistanceField_MaskType == fMaskType)
97                               ? kAliased_DistanceFieldEffectFlag
98                               : 0;
99 
100         if (isLCD) {
101             fDFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
102             fDFGPFlags |=
103                     (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
104         }
105 
106         fNeedsGlyphTransform = true;
107     }
108 
109     SkRect bounds;
110     geo.fBlob->computeSubRunBounds(
111             &bounds, *geo.fSubRunPtr, geo.fDrawMatrix, geo.fDrawOrigin, fNeedsGlyphTransform);
112     // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
113     // we treat this as a set of non-AA rects rendered with a texture.
114     this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
115 }
116 
visitProxies(const VisitProxyFunc & func) const117 void GrAtlasTextOp::visitProxies(const VisitProxyFunc& func) const {
118     fProcessors.visitProxies(func);
119 }
120 
121 #ifdef SK_DEBUG
dumpInfo() const122 SkString GrAtlasTextOp::dumpInfo() const {
123     SkString str;
124 
125     for (int i = 0; i < fGeoCount; ++i) {
126         str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f\n",
127                     i,
128                     fGeoData[i].fColor.toBytes_RGBA(),
129                     fGeoData[i].fDrawOrigin.x(),
130                     fGeoData[i].fDrawOrigin.y());
131     }
132 
133     str += fProcessors.dumpProcessors();
134     str += INHERITED::dumpInfo();
135     return str;
136 }
137 #endif
138 
fixedFunctionFlags() const139 GrDrawOp::FixedFunctionFlags GrAtlasTextOp::fixedFunctionFlags() const {
140     return FixedFunctionFlags::kNone;
141 }
142 
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)143 GrProcessorSet::Analysis GrAtlasTextOp::finalize(
144         const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
145         GrClampType clampType) {
146     GrProcessorAnalysisCoverage coverage;
147     GrProcessorAnalysisColor color;
148     if (kColorBitmapMask_MaskType == fMaskType) {
149         color.setToUnknown();
150     } else {
151         color.setToConstant(this->color());
152     }
153     switch (fMaskType) {
154         case kGrayscaleCoverageMask_MaskType:
155         case kAliasedDistanceField_MaskType:
156         case kGrayscaleDistanceField_MaskType:
157             coverage = GrProcessorAnalysisCoverage::kSingleChannel;
158             break;
159         case kLCDCoverageMask_MaskType:
160         case kLCDDistanceField_MaskType:
161         case kLCDBGRDistanceField_MaskType:
162             coverage = GrProcessorAnalysisCoverage::kLCD;
163             break;
164         case kColorBitmapMask_MaskType:
165             coverage = GrProcessorAnalysisCoverage::kNone;
166             break;
167     }
168     auto analysis = fProcessors.finalize(
169             color, coverage, clip, &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps,
170             clampType, &fGeoData[0].fColor);
171     fUsesLocalCoords = analysis.usesLocalCoords();
172     return analysis;
173 }
174 
clip_quads(const SkIRect & clipRect,char * currVertex,const char * blobVertices,size_t vertexStride,int glyphCount)175 static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices,
176                        size_t vertexStride, int glyphCount) {
177     for (int i = 0; i < glyphCount; ++i) {
178         const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices);
179         const SkPoint* blobPositionRB =
180                 reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride);
181 
182         // positions for bitmap glyphs are pixel boundary aligned
183         SkIRect positionRect = SkIRect::MakeLTRB(SkScalarRoundToInt(blobPositionLT->fX),
184                                                  SkScalarRoundToInt(blobPositionLT->fY),
185                                                  SkScalarRoundToInt(blobPositionRB->fX),
186                                                  SkScalarRoundToInt(blobPositionRB->fY));
187         if (clipRect.contains(positionRect)) {
188             memcpy(currVertex, blobVertices, 4 * vertexStride);
189             currVertex += 4 * vertexStride;
190         } else {
191             // Pull out some more data that we'll need.
192             // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
193             // and it avoids a lot of conditionals.
194             auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint));
195             size_t coordOffset = vertexStride - 2*sizeof(uint16_t);
196             auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset);
197             auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride +
198                                                                    coordOffset);
199             // Pull out the texel coordinates and texture index bits
200             uint16_t coordsRectL = blobCoordsLT[0];
201             uint16_t coordsRectT = blobCoordsLT[1];
202             uint16_t coordsRectR = blobCoordsRB[0];
203             uint16_t coordsRectB = blobCoordsRB[1];
204             int index0, index1;
205             std::tie(coordsRectL, coordsRectT, index0) =
206                     GrDrawOpAtlas::UnpackIndexFromTexCoords(coordsRectL, coordsRectT);
207             std::tie(coordsRectR, coordsRectB, index1) =
208                     GrDrawOpAtlas::UnpackIndexFromTexCoords(coordsRectR, coordsRectB);
209             SkASSERT(index0 == index1);
210 
211             int positionRectWidth = positionRect.width();
212             int positionRectHeight = positionRect.height();
213             SkASSERT(positionRectWidth == (coordsRectR - coordsRectL));
214             SkASSERT(positionRectHeight == (coordsRectB - coordsRectT));
215 
216             // Clip position and texCoords to the clipRect
217             unsigned int delta;
218             delta = std::min(std::max(clipRect.fLeft - positionRect.fLeft, 0), positionRectWidth);
219             coordsRectL += delta;
220             positionRect.fLeft += delta;
221 
222             delta = std::min(std::max(clipRect.fTop - positionRect.fTop, 0), positionRectHeight);
223             coordsRectT += delta;
224             positionRect.fTop += delta;
225 
226             delta = std::min(std::max(positionRect.fRight - clipRect.fRight, 0), positionRectWidth);
227             coordsRectR -= delta;
228             positionRect.fRight -= delta;
229 
230             delta = std::min(std::max(positionRect.fBottom - clipRect.fBottom, 0), positionRectHeight);
231             coordsRectB -= delta;
232             positionRect.fBottom -= delta;
233 
234             // Repack texel coordinates and index
235             std::tie(coordsRectL, coordsRectT) =
236                     GrDrawOpAtlas::PackIndexInTexCoords(coordsRectL, coordsRectT, index0);
237             std::tie(coordsRectR, coordsRectB) =
238                     GrDrawOpAtlas::PackIndexInTexCoords(coordsRectR, coordsRectB, index1);
239 
240             // Set new positions and coords
241             SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex);
242             currPosition->fX = positionRect.fLeft;
243             currPosition->fY = positionRect.fTop;
244             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
245             uint16_t* currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
246             currCoords[0] = coordsRectL;
247             currCoords[1] = coordsRectT;
248             currVertex += vertexStride;
249 
250             currPosition = reinterpret_cast<SkPoint*>(currVertex);
251             currPosition->fX = positionRect.fLeft;
252             currPosition->fY = positionRect.fBottom;
253             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
254             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
255             currCoords[0] = coordsRectL;
256             currCoords[1] = coordsRectB;
257             currVertex += vertexStride;
258 
259             currPosition = reinterpret_cast<SkPoint*>(currVertex);
260             currPosition->fX = positionRect.fRight;
261             currPosition->fY = positionRect.fTop;
262             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
263             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
264             currCoords[0] = coordsRectR;
265             currCoords[1] = coordsRectT;
266             currVertex += vertexStride;
267 
268             currPosition = reinterpret_cast<SkPoint*>(currVertex);
269             currPosition->fX = positionRect.fRight;
270             currPosition->fY = positionRect.fBottom;
271             *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
272             currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
273             currCoords[0] = coordsRectR;
274             currCoords[1] = coordsRectB;
275             currVertex += vertexStride;
276         }
277 
278         blobVertices += 4 * vertexStride;
279     }
280 }
281 
onPrepareDraws(Target * target)282 void GrAtlasTextOp::onPrepareDraws(Target* target) {
283     auto resourceProvider = target->resourceProvider();
284 
285     // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
286     // TODO actually only invert if we don't have RGBA
287     SkMatrix localMatrix;
288     if (this->usesLocalCoords() && !fGeoData[0].fDrawMatrix.invert(&localMatrix)) {
289         return;
290     }
291 
292     GrAtlasManager* atlasManager = target->atlasManager();
293 
294     GrMaskFormat maskFormat = this->maskFormat();
295 
296     unsigned int numActiveViews;
297     const GrSurfaceProxyView* views = atlasManager->getViews(maskFormat, &numActiveViews);
298     if (!views) {
299         SkDebugf("Could not allocate backing texture for atlas\n");
300         return;
301     }
302     SkASSERT(views[0].proxy());
303 
304     static constexpr int kMaxTextures = GrBitmapTextGeoProc::kMaxTextures;
305     static_assert(GrDistanceFieldA8TextGeoProc::kMaxTextures == kMaxTextures);
306     static_assert(GrDistanceFieldLCDTextGeoProc::kMaxTextures == kMaxTextures);
307 
308     auto fixedDynamicState = target->makeFixedDynamicState(kMaxTextures);
309     for (unsigned i = 0; i < numActiveViews; ++i) {
310         fixedDynamicState->fPrimitiveProcessorTextures[i] = views[i].proxy();
311         // This op does not know its atlas proxies when it is added to a GrOpsTasks, so the proxies
312         // don't get added during the visitProxies call. Thus we add them here.
313         target->sampledProxyArray()->push_back(views[i].proxy());
314     }
315 
316     FlushInfo flushInfo;
317     flushInfo.fFixedDynamicState = fixedDynamicState;
318 
319     bool vmPerspective = fGeoData[0].fDrawMatrix.hasPerspective();
320     if (this->usesDistanceFields()) {
321         flushInfo.fGeometryProcessor = this->setupDfProcessor(target->allocator(),
322                                                               *target->caps().shaderCaps(),
323                                                               views, numActiveViews);
324     } else {
325         auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kBilerp
326                                            : GrSamplerState::Filter::kNearest;
327         flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
328                 target->allocator(), *target->caps().shaderCaps(), this->color(), false, views,
329                 numActiveViews, filter, maskFormat, localMatrix, vmPerspective);
330     }
331 
332     int vertexStride = (int)flushInfo.fGeometryProcessor->vertexStride();
333 
334     // Ensure we don't request an insanely large contiguous vertex allocation.
335     static const int kMaxVertexBytes = GrBufferAllocPool::kDefaultBufferSize;
336     const int quadSize = vertexStride * kVerticesPerGlyph;
337     const int maxQuadsPerBuffer = kMaxVertexBytes / quadSize;
338 
339     // Where the quad buffer begins and ends relative to totalGlyphsRegened.
340     int quadBufferBegin = 0;
341     int quadBufferEnd = std::min(this->numGlyphs(), maxQuadsPerBuffer);
342 
343     flushInfo.fIndexBuffer = resourceProvider->refNonAAQuadIndexBuffer();
344     void* vertices = target->makeVertexSpace(
345             vertexStride,
346             kVerticesPerGlyph * (quadBufferEnd - quadBufferBegin),
347             &flushInfo.fVertexBuffer,
348             &flushInfo.fVertexOffset);
349     if (!vertices || !flushInfo.fVertexBuffer) {
350         SkDebugf("Could not allocate vertices\n");
351         return;
352     }
353 
354     // totalGlyphsRegened is all the glyphs for the op [0, this->numGlyphs()). The subRun glyph and
355     // quad buffer indices are calculated from this.
356     int totalGlyphsRegened = 0;
357     for (int i = 0; i < fGeoCount; i++) {
358         const Geometry& args = fGeoData[i];
359         auto subRun = args.fSubRunPtr;
360         SkASSERT((int)subRun->vertexStride() == vertexStride);
361 
362         subRun->updateVerticesColorIfNeeded(args.fColor.toBytes_RGBA());
363         subRun->translateVerticesIfNeeded(args.fDrawMatrix, args.fDrawOrigin);
364 
365         // TODO4F: Preserve float colors
366         GrTextBlob::VertexRegenerator regenerator(
367                 resourceProvider, args.fSubRunPtr, target->deferredUploadTarget(), atlasManager);
368 
369         // Where the subRun begins and ends relative to totalGlyphsRegened.
370         int subRunBegin = totalGlyphsRegened;
371         int subRunEnd = subRunBegin + (int)subRun->fGlyphs.size();
372 
373         // Draw all the glyphs in the subRun.
374         while (totalGlyphsRegened < subRunEnd) {
375             // drawBegin and drawEnd are indices for the subRun on the
376             // interval [0, subRun->fGlyphs.size()).
377             int drawBegin = totalGlyphsRegened - subRunBegin;
378             // drawEnd is either the end of the subRun or the end of the current quad buffer.
379             int drawEnd = std::min(subRunEnd, quadBufferEnd) - subRunBegin;
380             auto[ok, glyphsRegenerated] = regenerator.regenerate(drawBegin, drawEnd);
381 
382             // There was a problem allocating the glyph in the atlas. Bail.
383             if(!ok) { return; }
384 
385             // Update all the vertices for glyphsRegenerate glyphs.
386             if (glyphsRegenerated > 0) {
387                 int quadBufferIndex = totalGlyphsRegened - quadBufferBegin;
388                 int subRunIndex = totalGlyphsRegened - subRunBegin;
389                 auto regeneratedQuadBuffer =
390                         SkTAddOffset<char>(vertices, subRun->quadOffset(quadBufferIndex));
391                 if (args.fClipRect.isEmpty()) {
392                     memcpy(regeneratedQuadBuffer,
393                            subRun->quadStart(subRunIndex),
394                            glyphsRegenerated * quadSize);
395                 } else {
396                     SkASSERT(!vmPerspective);
397                     clip_quads(args.fClipRect,
398                                regeneratedQuadBuffer,
399                                subRun->quadStart(subRunIndex),
400                                vertexStride,
401                                glyphsRegenerated);
402                 }
403                 if (fNeedsGlyphTransform && !args.fDrawMatrix.isIdentity()) {
404                     // We always do the distance field view matrix transformation after copying
405                     // rather than during blob vertex generation time in the blob as handling
406                     // successive arbitrary transformations would be complicated and accumulate
407                     // error.
408                     if (args.fDrawMatrix.hasPerspective()) {
409                         auto* pos = reinterpret_cast<SkPoint3*>(regeneratedQuadBuffer);
410                         SkMatrixPriv::MapHomogeneousPointsWithStride(
411                                 args.fDrawMatrix, pos,
412                                 vertexStride, pos,
413                                 vertexStride,
414                                 glyphsRegenerated * kVerticesPerGlyph);
415                     } else {
416                         auto* pos = reinterpret_cast<SkPoint*>(regeneratedQuadBuffer);
417                         SkMatrixPriv::MapPointsWithStride(args.fDrawMatrix, pos, vertexStride,
418                                                           glyphsRegenerated * kVerticesPerGlyph);
419                     }
420                 }
421             }
422 
423             totalGlyphsRegened += glyphsRegenerated;
424             flushInfo.fGlyphsToFlush += glyphsRegenerated;
425 
426             // regenerate() has stopped part way through a SubRun. This means that either the atlas
427             // or the quad buffer is full or both. There is a case were the flow through
428             // the loop is strange. If we run out of quad buffer space at the same time the
429             // SubRun ends, then this is not triggered which is the right result for the last
430             // SubRun. But, if this is not the last SubRun, then advance to the next SubRun which
431             // will process no glyphs, and return to this point where the quad buffer will be
432             // expanded.
433             if (totalGlyphsRegened != subRunEnd) {
434                 // Flush if not all glyphs drawn because either the quad buffer is full or the
435                 // atlas is out of space.
436                 this->createDrawForGeneratedGlyphs(target, &flushInfo);
437                 if (totalGlyphsRegened == quadBufferEnd) {
438                     // Quad buffer is full. Get more buffer.
439                     quadBufferBegin = totalGlyphsRegened;
440                     int quadBufferSize =
441                             std::min(maxQuadsPerBuffer, this->numGlyphs() - totalGlyphsRegened);
442                     quadBufferEnd = quadBufferBegin + quadBufferSize;
443 
444                     vertices = target->makeVertexSpace(
445                             vertexStride,
446                             kVerticesPerGlyph * quadBufferSize,
447                             &flushInfo.fVertexBuffer,
448                             &flushInfo.fVertexOffset);
449                     if (!vertices || !flushInfo.fVertexBuffer) {
450                         SkDebugf("Could not allocate vertices\n");
451                         return;
452                     }
453                 }
454             }
455         }
456     }  // for all geometries
457     this->createDrawForGeneratedGlyphs(target, &flushInfo);
458 }
459 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)460 void GrAtlasTextOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
461     auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState,
462                                                              std::move(fProcessors),
463                                                              GrPipeline::InputFlags::kNone);
464 
465     flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline);
466 }
467 
createDrawForGeneratedGlyphs(GrMeshDrawOp::Target * target,FlushInfo * flushInfo) const468 void GrAtlasTextOp::createDrawForGeneratedGlyphs(
469         GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
470     if (!flushInfo->fGlyphsToFlush) {
471         return;
472     }
473 
474     auto atlasManager = target->atlasManager();
475 
476     GrGeometryProcessor* gp = flushInfo->fGeometryProcessor;
477     GrMaskFormat maskFormat = this->maskFormat();
478 
479     unsigned int numActiveViews;
480     const GrSurfaceProxyView* views = atlasManager->getViews(maskFormat, &numActiveViews);
481     SkASSERT(views);
482     // Something has gone terribly wrong, bail
483     if (!views || 0 == numActiveViews) {
484         return;
485     }
486     if (gp->numTextureSamplers() != (int) numActiveViews) {
487         // During preparation the number of atlas pages has increased.
488         // Update the proxies used in the GP to match.
489         for (unsigned i = gp->numTextureSamplers(); i < numActiveViews; ++i) {
490             flushInfo->fFixedDynamicState->fPrimitiveProcessorTextures[i] = views[i].proxy();
491             // This op does not know its atlas proxies when it is added to a GrOpsTasks, so the
492             // proxies don't get added during the visitProxies call. Thus we add them here.
493             target->sampledProxyArray()->push_back(views[i].proxy());
494             // These will get unreffed when the previously recorded draws destruct.
495             for (int d = 0; d < flushInfo->fNumDraws; ++d) {
496                 flushInfo->fFixedDynamicState->fPrimitiveProcessorTextures[i]->ref();
497             }
498         }
499         if (this->usesDistanceFields()) {
500             if (this->isLCD()) {
501                 reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->addNewViews(
502                         views, numActiveViews, GrSamplerState::Filter::kBilerp);
503             } else {
504                 reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->addNewViews(
505                         views, numActiveViews, GrSamplerState::Filter::kBilerp);
506             }
507         } else {
508             auto filter = fNeedsGlyphTransform ? GrSamplerState::Filter::kBilerp
509                                                : GrSamplerState::Filter::kNearest;
510             reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewViews(views, numActiveViews, filter);
511         }
512     }
513     int maxGlyphsPerDraw = static_cast<int>(flushInfo->fIndexBuffer->size() / sizeof(uint16_t) / 6);
514     GrMesh* mesh = target->allocMesh();
515     mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerGlyph, kVerticesPerGlyph,
516                               flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
517     mesh->setVertexData(flushInfo->fVertexBuffer, flushInfo->fVertexOffset);
518     target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState,
519                        nullptr, GrPrimitiveType::kTriangles);
520     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
521     flushInfo->fGlyphsToFlush = 0;
522     ++flushInfo->fNumDraws;
523 }
524 
onCombineIfPossible(GrOp * t,GrRecordingContext::Arenas *,const GrCaps & caps)525 GrOp::CombineResult GrAtlasTextOp::onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
526                                                        const GrCaps& caps) {
527     GrAtlasTextOp* that = t->cast<GrAtlasTextOp>();
528     if (fProcessors != that->fProcessors) {
529         return CombineResult::kCannotCombine;
530     }
531 
532     if (fMaskType != that->fMaskType) {
533         return CombineResult::kCannotCombine;
534     }
535 
536     const SkMatrix& thisFirstMatrix = fGeoData[0].fDrawMatrix;
537     const SkMatrix& thatFirstMatrix = that->fGeoData[0].fDrawMatrix;
538 
539     if (this->usesLocalCoords() && !SkMatrixPriv::CheapEqual(thisFirstMatrix, thatFirstMatrix)) {
540         return CombineResult::kCannotCombine;
541     }
542 
543     if (fNeedsGlyphTransform != that->fNeedsGlyphTransform) {
544         return CombineResult::kCannotCombine;
545     }
546 
547     if (fNeedsGlyphTransform &&
548         (thisFirstMatrix.hasPerspective() != thatFirstMatrix.hasPerspective())) {
549         return CombineResult::kCannotCombine;
550     }
551 
552     if (this->usesDistanceFields()) {
553         if (fDFGPFlags != that->fDFGPFlags) {
554             return CombineResult::kCannotCombine;
555         }
556 
557         if (fLuminanceColor != that->fLuminanceColor) {
558             return CombineResult::kCannotCombine;
559         }
560     } else {
561         if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
562             return CombineResult::kCannotCombine;
563         }
564     }
565 
566     fNumGlyphs += that->numGlyphs();
567 
568     // Reallocate space for geo data if necessary and then import that geo's data.
569     int newGeoCount = that->fGeoCount + fGeoCount;
570 
571     // We reallocate at a rate of 1.5x to try to get better total memory usage
572     if (newGeoCount > fGeoDataAllocSize) {
573         int newAllocSize = fGeoDataAllocSize + fGeoDataAllocSize / 2;
574         while (newAllocSize < newGeoCount) {
575             newAllocSize += newAllocSize / 2;
576         }
577         fGeoData.realloc(newAllocSize);
578         fGeoDataAllocSize = newAllocSize;
579     }
580 
581     // We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that
582     // it doesn't try to unref them.
583     memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
584 #ifdef SK_DEBUG
585     for (int i = 0; i < that->fGeoCount; ++i) {
586         that->fGeoData.get()[i].fBlob = (GrTextBlob*)0x1;
587     }
588 #endif
589     that->fGeoCount = 0;
590     fGeoCount = newGeoCount;
591 
592     return CombineResult::kMerged;
593 }
594 
595 // TODO trying to figure out why lcd is so whack
596 // (see comments in GrTextContext::ComputeCanonicalColor)
setupDfProcessor(SkArenaAlloc * arena,const GrShaderCaps & caps,const GrSurfaceProxyView * views,unsigned int numActiveViews) const597 GrGeometryProcessor* GrAtlasTextOp::setupDfProcessor(SkArenaAlloc* arena,
598                                                      const GrShaderCaps& caps,
599                                                      const GrSurfaceProxyView* views,
600                                                      unsigned int numActiveViews) const {
601     bool isLCD = this->isLCD();
602 
603     SkMatrix localMatrix = SkMatrix::I();
604     if (this->usesLocalCoords()) {
605         // If this fails we'll just use I().
606         bool result = fGeoData[0].fDrawMatrix.invert(&localMatrix);
607         (void)result;
608     }
609 
610     // see if we need to create a new effect
611     if (isLCD) {
612         float redCorrection = fDistanceAdjustTable->getAdjustment(
613                 SkColorGetR(fLuminanceColor) >> kDistanceAdjustLumShift,
614                 fUseGammaCorrectDistanceTable);
615         float greenCorrection = fDistanceAdjustTable->getAdjustment(
616                 SkColorGetG(fLuminanceColor) >> kDistanceAdjustLumShift,
617                 fUseGammaCorrectDistanceTable);
618         float blueCorrection = fDistanceAdjustTable->getAdjustment(
619                 SkColorGetB(fLuminanceColor) >> kDistanceAdjustLumShift,
620                 fUseGammaCorrectDistanceTable);
621         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
622                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
623                         redCorrection, greenCorrection, blueCorrection);
624         return GrDistanceFieldLCDTextGeoProc::Make(arena, caps, views, numActiveViews,
625                                                    GrSamplerState::Filter::kBilerp, widthAdjust,
626                                                    fDFGPFlags, localMatrix);
627     } else {
628 #ifdef SK_GAMMA_APPLY_TO_A8
629         float correction = 0;
630         if (kAliasedDistanceField_MaskType != fMaskType) {
631             U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT,
632                                                                 fLuminanceColor);
633             correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
634                                                              fUseGammaCorrectDistanceTable);
635         }
636         return GrDistanceFieldA8TextGeoProc::Make(arena, caps, views, numActiveViews,
637                                                   GrSamplerState::Filter::kBilerp, correction,
638                                                   fDFGPFlags, localMatrix);
639 #else
640         return GrDistanceFieldA8TextGeoProc::Make(arena, caps, views, numActiveViews,
641                                                   GrSamplerState::Filter::kBilerp,
642                                                   fDFGPFlags, localMatrix);
643 #endif
644     }
645 }
646 
647