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