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 "GrAtlasTextBlob.h"
9 #include "GrBlurUtils.h"
10 #include "GrContext.h"
11 #include "GrPipelineBuilder.h"
12 #include "GrRenderTargetContext.h"
13 #include "GrTextUtils.h"
14 #include "SkColorFilter.h"
15 #include "SkDrawFilter.h"
16 #include "SkGlyphCache.h"
17 #include "SkTextBlobRunIterator.h"
18 #include "ops/GrAtlasTextOp.h"
19 
Make(GrMemoryPool * pool,int glyphCount,int runCount)20 sk_sp<GrAtlasTextBlob> GrAtlasTextBlob::Make(GrMemoryPool* pool, int glyphCount, int runCount) {
21     // We allocate size for the GrAtlasTextBlob itself, plus size for the vertices array,
22     // and size for the glyphIds array.
23     size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize;
24     size_t size = sizeof(GrAtlasTextBlob) +
25                   verticesCount +
26                   glyphCount * sizeof(GrGlyph**) +
27                   sizeof(GrAtlasTextBlob::Run) * runCount;
28 
29     void* allocation = pool->allocate(size);
30     if (CACHE_SANITY_CHECK) {
31         sk_bzero(allocation, size);
32     }
33 
34     sk_sp<GrAtlasTextBlob> cacheBlob(new (allocation) GrAtlasTextBlob);
35     cacheBlob->fSize = size;
36 
37     // setup offsets for vertices / glyphs
38     cacheBlob->fVertices = sizeof(GrAtlasTextBlob) +
39                            reinterpret_cast<unsigned char*>(cacheBlob.get());
40     cacheBlob->fGlyphs = reinterpret_cast<GrGlyph**>(cacheBlob->fVertices + verticesCount);
41     cacheBlob->fRuns = reinterpret_cast<GrAtlasTextBlob::Run*>(cacheBlob->fGlyphs + glyphCount);
42 
43     // Initialize runs
44     for (int i = 0; i < runCount; i++) {
45         new (&cacheBlob->fRuns[i]) GrAtlasTextBlob::Run;
46     }
47     cacheBlob->fRunCount = runCount;
48     cacheBlob->fPool = pool;
49     return cacheBlob;
50 }
51 
setupCache(int runIndex,const SkSurfaceProps & props,uint32_t scalerContextFlags,const SkPaint & skPaint,const SkMatrix * viewMatrix)52 SkGlyphCache* GrAtlasTextBlob::setupCache(int runIndex,
53                                           const SkSurfaceProps& props,
54                                           uint32_t scalerContextFlags,
55                                           const SkPaint& skPaint,
56                                           const SkMatrix* viewMatrix) {
57     GrAtlasTextBlob::Run* run = &fRuns[runIndex];
58 
59     // if we have an override descriptor for the run, then we should use that
60     SkAutoDescriptor* desc = run->fOverrideDescriptor.get() ? run->fOverrideDescriptor.get() :
61                                                               &run->fDescriptor;
62     SkScalerContextEffects effects;
63     skPaint.getScalerContextDescriptor(&effects, desc, props, scalerContextFlags, viewMatrix);
64     run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
65     run->fPathEffect = sk_ref_sp(effects.fPathEffect);
66     run->fRasterizer = sk_ref_sp(effects.fRasterizer);
67     run->fMaskFilter = sk_ref_sp(effects.fMaskFilter);
68     return SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc->getDesc());
69 }
70 
appendGlyph(int runIndex,const SkRect & positions,GrColor color,GrAtlasTextStrike * strike,GrGlyph * glyph,SkGlyphCache * cache,const SkGlyph & skGlyph,SkScalar x,SkScalar y,SkScalar scale,bool treatAsBMP)71 void GrAtlasTextBlob::appendGlyph(int runIndex,
72                                   const SkRect& positions,
73                                   GrColor color,
74                                   GrAtlasTextStrike* strike,
75                                   GrGlyph* glyph,
76                                   SkGlyphCache* cache, const SkGlyph& skGlyph,
77                                   SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
78     if (positions.isEmpty()) {
79         return;
80     }
81 
82     // If the glyph is too large we fall back to paths
83     if (glyph->fTooLargeForAtlas) {
84         this->appendLargeGlyph(glyph, cache, skGlyph, x, y, scale, treatAsBMP);
85         return;
86     }
87 
88     Run& run = fRuns[runIndex];
89     GrMaskFormat format = glyph->fMaskFormat;
90 
91     Run::SubRunInfo* subRun = &run.fSubRunInfo.back();
92     if (run.fInitialized && subRun->maskFormat() != format) {
93         subRun = &run.push_back();
94         subRun->setStrike(strike);
95     } else if (!run.fInitialized) {
96         subRun->setStrike(strike);
97     }
98 
99     run.fInitialized = true;
100 
101     size_t vertexStride = GetVertexStride(format);
102 
103     subRun->setMaskFormat(format);
104 
105     subRun->joinGlyphBounds(positions);
106     subRun->setColor(color);
107 
108     intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex());
109 
110     if (kARGB_GrMaskFormat != glyph->fMaskFormat) {
111         // V0
112         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
113         position->set(positions.fLeft, positions.fTop);
114         SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
115         *colorPtr = color;
116         vertex += vertexStride;
117 
118         // V1
119         position = reinterpret_cast<SkPoint*>(vertex);
120         position->set(positions.fLeft, positions.fBottom);
121         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
122         *colorPtr = color;
123         vertex += vertexStride;
124 
125         // V2
126         position = reinterpret_cast<SkPoint*>(vertex);
127         position->set(positions.fRight, positions.fBottom);
128         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
129         *colorPtr = color;
130         vertex += vertexStride;
131 
132         // V3
133         position = reinterpret_cast<SkPoint*>(vertex);
134         position->set(positions.fRight, positions.fTop);
135         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
136         *colorPtr = color;
137     } else {
138         // V0
139         SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
140         position->set(positions.fLeft, positions.fTop);
141         vertex += vertexStride;
142 
143         // V1
144         position = reinterpret_cast<SkPoint*>(vertex);
145         position->set(positions.fLeft, positions.fBottom);
146         vertex += vertexStride;
147 
148         // V2
149         position = reinterpret_cast<SkPoint*>(vertex);
150         position->set(positions.fRight, positions.fBottom);
151         vertex += vertexStride;
152 
153         // V3
154         position = reinterpret_cast<SkPoint*>(vertex);
155         position->set(positions.fRight, positions.fTop);
156     }
157     subRun->appendVertices(vertexStride);
158     fGlyphs[subRun->glyphEndIndex()] = glyph;
159     subRun->glyphAppended();
160 }
161 
appendLargeGlyph(GrGlyph * glyph,SkGlyphCache * cache,const SkGlyph & skGlyph,SkScalar x,SkScalar y,SkScalar scale,bool treatAsBMP)162 void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, SkGlyphCache* cache, const SkGlyph& skGlyph,
163                                        SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
164     if (nullptr == glyph->fPath) {
165         const SkPath* glyphPath = cache->findPath(skGlyph);
166         if (!glyphPath) {
167             return;
168         }
169 
170         glyph->fPath = new SkPath(*glyphPath);
171     }
172     fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, treatAsBMP));
173 }
174 
mustRegenerate(const GrTextUtils::Paint & paint,const SkMaskFilter::BlurRec & blurRec,const SkMatrix & viewMatrix,SkScalar x,SkScalar y)175 bool GrAtlasTextBlob::mustRegenerate(const GrTextUtils::Paint& paint,
176                                      const SkMaskFilter::BlurRec& blurRec,
177                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
178     // If we have LCD text then our canonical color will be set to transparent, in this case we have
179     // to regenerate the blob on any color change
180     // We use the grPaint to get any color filter effects
181     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
182         fFilteredPaintColor != paint.filteredSkColor()) {
183         return true;
184     }
185 
186     if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
187         return true;
188     }
189 
190     if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
191         return true;
192     }
193 
194     // We only cache one masked version
195     if (fKey.fHasBlur &&
196         (fBlurRec.fSigma != blurRec.fSigma ||
197          fBlurRec.fStyle != blurRec.fStyle ||
198          fBlurRec.fQuality != blurRec.fQuality)) {
199         return true;
200     }
201 
202     // Similarly, we only cache one version for each style
203     if (fKey.fStyle != SkPaint::kFill_Style &&
204         (fStrokeInfo.fFrameWidth != paint.skPaint().getStrokeWidth() ||
205          fStrokeInfo.fMiterLimit != paint.skPaint().getStrokeMiter() ||
206          fStrokeInfo.fJoin != paint.skPaint().getStrokeJoin())) {
207         return true;
208     }
209 
210     // Mixed blobs must be regenerated.  We could probably figure out a way to do integer scrolls
211     // for mixed blobs if this becomes an issue.
212     if (this->hasBitmap() && this->hasDistanceField()) {
213         // Identical viewmatrices and we can reuse in all cases
214         if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) {
215             return false;
216         }
217         return true;
218     }
219 
220     if (this->hasBitmap()) {
221         if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
222             fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
223             fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
224             fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
225             return true;
226         }
227 
228         // We can update the positions in the cachedtextblobs without regenerating the whole blob,
229         // but only for integer translations.
230         // This cool bit of math will determine the necessary translation to apply to the already
231         // generated vertex coordinates to move them to the correct position
232         SkScalar transX = viewMatrix.getTranslateX() +
233                           viewMatrix.getScaleX() * (x - fInitialX) +
234                           viewMatrix.getSkewX() * (y - fInitialY) -
235                           fInitialViewMatrix.getTranslateX();
236         SkScalar transY = viewMatrix.getTranslateY() +
237                           viewMatrix.getSkewY() * (x - fInitialX) +
238                           viewMatrix.getScaleY() * (y - fInitialY) -
239                           fInitialViewMatrix.getTranslateY();
240         if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
241             return true;
242         }
243     } else if (this->hasDistanceField()) {
244         // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
245         // distance field being generated, so we have to regenerate in those cases
246         SkScalar newMaxScale = viewMatrix.getMaxScale();
247         SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale();
248         SkScalar scaleAdjust = newMaxScale / oldMaxScale;
249         if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
250             return true;
251         }
252     }
253 
254     // It is possible that a blob has neither distanceField nor bitmaptext.  This is in the case
255     // when all of the runs inside the blob are drawn as paths.  In this case, we always regenerate
256     // the blob anyways at flush time, so no need to regenerate explicitly
257     return false;
258 }
259 
makeOp(const Run::SubRunInfo & info,int glyphCount,int run,int subRun,const SkMatrix & viewMatrix,SkScalar x,SkScalar y,const GrTextUtils::Paint & paint,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,bool useGammaCorrectDistanceTable,GrAtlasGlyphCache * cache)260 inline std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::makeOp(
261         const Run::SubRunInfo& info, int glyphCount, int run, int subRun,
262         const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint,
263         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
264         bool useGammaCorrectDistanceTable, GrAtlasGlyphCache* cache) {
265     GrMaskFormat format = info.maskFormat();
266 
267     std::unique_ptr<GrAtlasTextOp> op;
268     if (info.drawAsDistanceFields()) {
269         SkColor filteredColor = paint.filteredSkColor();
270         bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
271         op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable,
272                                               useGammaCorrectDistanceTable, filteredColor,
273                                               info.hasUseLCDText(), useBGR);
274     } else {
275         op = GrAtlasTextOp::MakeBitmap(format, glyphCount, cache);
276     }
277     GrAtlasTextOp::Geometry& geometry = op->geometry();
278     geometry.fViewMatrix = viewMatrix;
279     geometry.fBlob = SkRef(this);
280     geometry.fRun = run;
281     geometry.fSubRun = subRun;
282     geometry.fColor =
283             info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor();
284     geometry.fX = x;
285     geometry.fY = y;
286     op->init();
287 
288     return std::move(op);
289 }
290 
flushRun(GrRenderTargetContext * rtc,const GrClip & clip,int run,const SkMatrix & viewMatrix,SkScalar x,SkScalar y,const GrTextUtils::Paint & paint,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,GrAtlasGlyphCache * cache)291 inline void GrAtlasTextBlob::flushRun(GrRenderTargetContext* rtc, const GrClip& clip, int run,
292                                       const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
293                                       const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
294                                       const GrDistanceFieldAdjustTable* distanceAdjustTable,
295                                       GrAtlasGlyphCache* cache) {
296     int lastRun = fRuns[run].fSubRunInfo.count() - 1;
297     for (int subRun = 0; subRun <= lastRun; subRun++) {
298         const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
299         GrPaint grPaint;
300         if (!paint.toGrPaint(info.maskFormat(), rtc, viewMatrix, &grPaint)) {
301             continue;
302         }
303         int glyphCount = info.glyphCount();
304         if (0 == glyphCount) {
305             continue;
306         }
307 
308         std::unique_ptr<GrMeshDrawOp> op(this->makeOp(info, glyphCount, run, subRun, viewMatrix, x,
309                                                       y, paint, props, distanceAdjustTable,
310                                                       rtc->isGammaCorrect(), cache));
311         GrPipelineBuilder pipelineBuilder(std::move(grPaint), GrAAType::kNone);
312 
313         rtc->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
314     }
315 }
316 
calculate_translation(bool applyVM,const SkMatrix & newViewMatrix,SkScalar newX,SkScalar newY,const SkMatrix & currentViewMatrix,SkScalar currentX,SkScalar currentY,SkScalar * transX,SkScalar * transY)317 static void calculate_translation(bool applyVM,
318                                   const SkMatrix& newViewMatrix, SkScalar newX, SkScalar newY,
319                                   const SkMatrix& currentViewMatrix, SkScalar currentX,
320                                   SkScalar currentY, SkScalar* transX, SkScalar* transY) {
321     if (applyVM) {
322         *transX = newViewMatrix.getTranslateX() +
323                   newViewMatrix.getScaleX() * (newX - currentX) +
324                   newViewMatrix.getSkewX() * (newY - currentY) -
325                   currentViewMatrix.getTranslateX();
326 
327         *transY = newViewMatrix.getTranslateY() +
328                   newViewMatrix.getSkewY() * (newX - currentX) +
329                   newViewMatrix.getScaleY() * (newY - currentY) -
330                   currentViewMatrix.getTranslateY();
331     } else {
332         *transX = newX - currentX;
333         *transY = newY - currentY;
334     }
335 }
336 
flushBigGlyphs(GrContext * context,GrRenderTargetContext * rtc,const GrClip & clip,const SkPaint & paint,const SkMatrix & viewMatrix,SkScalar x,SkScalar y,const SkIRect & clipBounds)337 void GrAtlasTextBlob::flushBigGlyphs(GrContext* context, GrRenderTargetContext* rtc,
338                                      const GrClip& clip, const SkPaint& paint,
339                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
340                                      const SkIRect& clipBounds) {
341     SkScalar transX, transY;
342     for (int i = 0; i < fBigGlyphs.count(); i++) {
343         GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i];
344         calculate_translation(bigGlyph.fTreatAsBMP, viewMatrix, x, y,
345                               fInitialViewMatrix, fInitialX, fInitialY, &transX, &transY);
346         SkMatrix ctm;
347         ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
348         ctm.postTranslate(bigGlyph.fX + transX, bigGlyph.fY + transY);
349         if (!bigGlyph.fTreatAsBMP) {
350             ctm.postConcat(viewMatrix);
351         }
352 
353         GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, bigGlyph.fPath, paint, ctm, nullptr,
354                                             clipBounds, false);
355     }
356 }
357 
flushRunAsPaths(GrContext * context,GrRenderTargetContext * rtc,const SkSurfaceProps & props,const SkTextBlobRunIterator & it,const GrClip & clip,const GrTextUtils::Paint & paint,SkDrawFilter * drawFilter,const SkMatrix & viewMatrix,const SkIRect & clipBounds,SkScalar x,SkScalar y)358 void GrAtlasTextBlob::flushRunAsPaths(GrContext* context, GrRenderTargetContext* rtc,
359                                       const SkSurfaceProps& props, const SkTextBlobRunIterator& it,
360                                       const GrClip& clip, const GrTextUtils::Paint& paint,
361                                       SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
362                                       const SkIRect& clipBounds, SkScalar x, SkScalar y) {
363     size_t textLen = it.glyphCount() * sizeof(uint16_t);
364     const SkPoint& offset = it.offset();
365 
366     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
367     if (!runPaint.modifyForRun(it)) {
368         return;
369     }
370 
371     switch (it.positioning()) {
372         case SkTextBlob::kDefault_Positioning:
373             GrTextUtils::DrawTextAsPath(context, rtc, clip, runPaint, viewMatrix,
374                                         (const char*)it.glyphs(), textLen, x + offset.x(),
375                                         y + offset.y(), clipBounds);
376             break;
377         case SkTextBlob::kHorizontal_Positioning:
378             GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
379                                            (const char*)it.glyphs(), textLen, it.pos(), 1,
380                                            SkPoint::Make(x, y + offset.y()), clipBounds);
381             break;
382         case SkTextBlob::kFull_Positioning:
383             GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
384                                            (const char*)it.glyphs(), textLen, it.pos(), 2,
385                                            SkPoint::Make(x, y), clipBounds);
386             break;
387     }
388 }
389 
flushCached(GrContext * context,GrRenderTargetContext * rtc,const SkTextBlob * blob,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,const GrTextUtils::Paint & paint,SkDrawFilter * drawFilter,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & clipBounds,SkScalar x,SkScalar y)390 void GrAtlasTextBlob::flushCached(GrContext* context, GrRenderTargetContext* rtc,
391                                   const SkTextBlob* blob, const SkSurfaceProps& props,
392                                   const GrDistanceFieldAdjustTable* distanceAdjustTable,
393                                   const GrTextUtils::Paint& paint, SkDrawFilter* drawFilter,
394                                   const GrClip& clip, const SkMatrix& viewMatrix,
395                                   const SkIRect& clipBounds, SkScalar x, SkScalar y) {
396     // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
397     // it as paths
398     SkTextBlobRunIterator it(blob);
399     for (int run = 0; !it.done(); it.next(), run++) {
400         if (fRuns[run].fDrawAsPaths) {
401             this->flushRunAsPaths(context, rtc, props, it, clip, paint, drawFilter, viewMatrix,
402                                   clipBounds, x, y);
403             continue;
404         }
405         this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
406                        context->getAtlasGlyphCache());
407     }
408 
409     // Now flush big glyphs
410     this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
411 }
412 
flushThrowaway(GrContext * context,GrRenderTargetContext * rtc,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,const GrTextUtils::Paint & paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & clipBounds,SkScalar x,SkScalar y)413 void GrAtlasTextBlob::flushThrowaway(GrContext* context, GrRenderTargetContext* rtc,
414                                      const SkSurfaceProps& props,
415                                      const GrDistanceFieldAdjustTable* distanceAdjustTable,
416                                      const GrTextUtils::Paint& paint, const GrClip& clip,
417                                      const SkMatrix& viewMatrix, const SkIRect& clipBounds,
418                                      SkScalar x, SkScalar y) {
419     for (int run = 0; run < fRunCount; run++) {
420         this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
421                        context->getAtlasGlyphCache());
422     }
423 
424     // Now flush big glyphs
425     this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
426 }
427 
test_makeOp(int glyphCount,int run,int subRun,const SkMatrix & viewMatrix,SkScalar x,SkScalar y,const GrTextUtils::Paint & paint,const SkSurfaceProps & props,const GrDistanceFieldAdjustTable * distanceAdjustTable,GrAtlasGlyphCache * cache)428 std::unique_ptr<GrMeshDrawOp> GrAtlasTextBlob::test_makeOp(
429         int glyphCount, int run, int subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
430         const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
431         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache) {
432     const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
433     return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
434                         distanceAdjustTable, false, cache);
435 }
436 
AssertEqual(const GrAtlasTextBlob & l,const GrAtlasTextBlob & r)437 void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
438     SkASSERT_RELEASE(l.fSize == r.fSize);
439     SkASSERT_RELEASE(l.fPool == r.fPool);
440 
441     SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
442     SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
443     SkASSERT_RELEASE(l.fBlurRec.fQuality == r.fBlurRec.fQuality);
444 
445     SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
446     SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
447     SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
448 
449     SkASSERT_RELEASE(l.fBigGlyphs.count() == r.fBigGlyphs.count());
450     for (int i = 0; i < l.fBigGlyphs.count(); i++) {
451         const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
452         const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
453 
454         SkASSERT_RELEASE(lBigGlyph.fPath == rBigGlyph.fPath);
455         // We can't assert that these have the same translations
456     }
457 
458     SkASSERT_RELEASE(l.fKey == r.fKey);
459     //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical
460     SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale);
461     SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
462     SkASSERT_RELEASE(l.fTextType == r.fTextType);
463 
464     SkASSERT_RELEASE(l.fRunCount == r.fRunCount);
465     for (int i = 0; i < l.fRunCount; i++) {
466         const Run& lRun = l.fRuns[i];
467         const Run& rRun = r.fRuns[i];
468 
469         if (lRun.fTypeface.get()) {
470             SkASSERT_RELEASE(rRun.fTypeface.get());
471             SkASSERT_RELEASE(SkTypeface::Equal(lRun.fTypeface.get(), rRun.fTypeface.get()));
472         } else {
473             SkASSERT_RELEASE(!rRun.fTypeface.get());
474         }
475 
476 
477         SkASSERT_RELEASE(lRun.fDescriptor.getDesc());
478         SkASSERT_RELEASE(rRun.fDescriptor.getDesc());
479         SkASSERT_RELEASE(*lRun.fDescriptor.getDesc() == *rRun.fDescriptor.getDesc());
480 
481         if (lRun.fOverrideDescriptor.get()) {
482             SkASSERT_RELEASE(lRun.fOverrideDescriptor->getDesc());
483             SkASSERT_RELEASE(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());
484             SkASSERT_RELEASE(*lRun.fOverrideDescriptor->getDesc() ==
485                              *rRun.fOverrideDescriptor->getDesc());
486         } else {
487             SkASSERT_RELEASE(!rRun.fOverrideDescriptor.get());
488         }
489 
490         // color can be changed
491         //SkASSERT(lRun.fColor == rRun.fColor);
492         SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
493         SkASSERT_RELEASE(lRun.fDrawAsPaths == rRun.fDrawAsPaths);
494 
495         SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
496         for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
497             const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j];
498             const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j];
499 
500             // TODO we can do this check, but we have to apply the VM to the old vertex bounds
501             //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds());
502 
503             if (lSubRun.strike()) {
504                 SkASSERT_RELEASE(rSubRun.strike());
505                 SkASSERT_RELEASE(GrAtlasTextStrike::GetKey(*lSubRun.strike()) ==
506                                  GrAtlasTextStrike::GetKey(*rSubRun.strike()));
507 
508             } else {
509                 SkASSERT_RELEASE(!rSubRun.strike());
510             }
511 
512             SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
513             SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex());
514             SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
515             SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex());
516             SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
517             SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
518             SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
519         }
520     }
521 }
522 
computeTranslation(const SkMatrix & viewMatrix,SkScalar x,SkScalar y,SkScalar * transX,SkScalar * transY)523 void GrAtlasTextBlob::Run::SubRunInfo::computeTranslation(const SkMatrix& viewMatrix,
524                                                           SkScalar x, SkScalar y, SkScalar* transX,
525                                                           SkScalar* transY) {
526     calculate_translation(!this->drawAsDistanceFields(), viewMatrix, x, y,
527                           fCurrentViewMatrix, fX, fY, transX, transY);
528     fCurrentViewMatrix = viewMatrix;
529     fX = x;
530     fY = y;
531 }
532