Line data Source code
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 :
20 0 : 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 0 : size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize;
24 : size_t size = sizeof(GrAtlasTextBlob) +
25 : verticesCount +
26 0 : glyphCount * sizeof(GrGlyph**) +
27 0 : sizeof(GrAtlasTextBlob::Run) * runCount;
28 :
29 0 : void* allocation = pool->allocate(size);
30 : if (CACHE_SANITY_CHECK) {
31 : sk_bzero(allocation, size);
32 : }
33 :
34 0 : sk_sp<GrAtlasTextBlob> cacheBlob(new (allocation) GrAtlasTextBlob);
35 0 : cacheBlob->fSize = size;
36 :
37 : // setup offsets for vertices / glyphs
38 0 : cacheBlob->fVertices = sizeof(GrAtlasTextBlob) +
39 0 : reinterpret_cast<unsigned char*>(cacheBlob.get());
40 0 : cacheBlob->fGlyphs = reinterpret_cast<GrGlyph**>(cacheBlob->fVertices + verticesCount);
41 0 : cacheBlob->fRuns = reinterpret_cast<GrAtlasTextBlob::Run*>(cacheBlob->fGlyphs + glyphCount);
42 :
43 : // Initialize runs
44 0 : for (int i = 0; i < runCount; i++) {
45 0 : new (&cacheBlob->fRuns[i]) GrAtlasTextBlob::Run;
46 : }
47 0 : cacheBlob->fRunCount = runCount;
48 0 : cacheBlob->fPool = pool;
49 0 : return cacheBlob;
50 : }
51 :
52 0 : SkGlyphCache* GrAtlasTextBlob::setupCache(int runIndex,
53 : const SkSurfaceProps& props,
54 : uint32_t scalerContextFlags,
55 : const SkPaint& skPaint,
56 : const SkMatrix* viewMatrix) {
57 0 : GrAtlasTextBlob::Run* run = &fRuns[runIndex];
58 :
59 : // if we have an override descriptor for the run, then we should use that
60 0 : SkAutoDescriptor* desc = run->fOverrideDescriptor.get() ? run->fOverrideDescriptor.get() :
61 0 : &run->fDescriptor;
62 0 : SkScalerContextEffects effects;
63 0 : skPaint.getScalerContextDescriptor(&effects, desc, props, scalerContextFlags, viewMatrix);
64 0 : run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
65 0 : run->fPathEffect = sk_ref_sp(effects.fPathEffect);
66 0 : run->fRasterizer = sk_ref_sp(effects.fRasterizer);
67 0 : run->fMaskFilter = sk_ref_sp(effects.fMaskFilter);
68 0 : return SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc->getDesc());
69 : }
70 :
71 0 : 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 0 : if (positions.isEmpty()) {
79 0 : return;
80 : }
81 :
82 : // If the glyph is too large we fall back to paths
83 0 : if (glyph->fTooLargeForAtlas) {
84 0 : this->appendLargeGlyph(glyph, cache, skGlyph, x, y, scale, treatAsBMP);
85 0 : return;
86 : }
87 :
88 0 : Run& run = fRuns[runIndex];
89 0 : GrMaskFormat format = glyph->fMaskFormat;
90 :
91 0 : Run::SubRunInfo* subRun = &run.fSubRunInfo.back();
92 0 : if (run.fInitialized && subRun->maskFormat() != format) {
93 0 : subRun = &run.push_back();
94 0 : subRun->setStrike(strike);
95 0 : } else if (!run.fInitialized) {
96 0 : subRun->setStrike(strike);
97 : }
98 :
99 0 : run.fInitialized = true;
100 :
101 0 : size_t vertexStride = GetVertexStride(format);
102 :
103 0 : subRun->setMaskFormat(format);
104 :
105 0 : subRun->joinGlyphBounds(positions);
106 0 : subRun->setColor(color);
107 :
108 0 : intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex());
109 :
110 0 : if (kARGB_GrMaskFormat != glyph->fMaskFormat) {
111 : // V0
112 0 : SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
113 0 : position->set(positions.fLeft, positions.fTop);
114 0 : SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
115 0 : *colorPtr = color;
116 0 : vertex += vertexStride;
117 :
118 : // V1
119 0 : position = reinterpret_cast<SkPoint*>(vertex);
120 0 : position->set(positions.fLeft, positions.fBottom);
121 0 : colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
122 0 : *colorPtr = color;
123 0 : vertex += vertexStride;
124 :
125 : // V2
126 0 : position = reinterpret_cast<SkPoint*>(vertex);
127 0 : position->set(positions.fRight, positions.fBottom);
128 0 : colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
129 0 : *colorPtr = color;
130 0 : vertex += vertexStride;
131 :
132 : // V3
133 0 : position = reinterpret_cast<SkPoint*>(vertex);
134 0 : position->set(positions.fRight, positions.fTop);
135 0 : colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
136 0 : *colorPtr = color;
137 : } else {
138 : // V0
139 0 : SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
140 0 : position->set(positions.fLeft, positions.fTop);
141 0 : vertex += vertexStride;
142 :
143 : // V1
144 0 : position = reinterpret_cast<SkPoint*>(vertex);
145 0 : position->set(positions.fLeft, positions.fBottom);
146 0 : vertex += vertexStride;
147 :
148 : // V2
149 0 : position = reinterpret_cast<SkPoint*>(vertex);
150 0 : position->set(positions.fRight, positions.fBottom);
151 0 : vertex += vertexStride;
152 :
153 : // V3
154 0 : position = reinterpret_cast<SkPoint*>(vertex);
155 0 : position->set(positions.fRight, positions.fTop);
156 : }
157 0 : subRun->appendVertices(vertexStride);
158 0 : fGlyphs[subRun->glyphEndIndex()] = glyph;
159 0 : subRun->glyphAppended();
160 : }
161 :
162 0 : void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, SkGlyphCache* cache, const SkGlyph& skGlyph,
163 : SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP) {
164 0 : if (nullptr == glyph->fPath) {
165 0 : const SkPath* glyphPath = cache->findPath(skGlyph);
166 0 : if (!glyphPath) {
167 0 : return;
168 : }
169 :
170 0 : glyph->fPath = new SkPath(*glyphPath);
171 : }
172 0 : fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, treatAsBMP));
173 : }
174 :
175 0 : 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 0 : if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
182 0 : fFilteredPaintColor != paint.filteredSkColor()) {
183 0 : return true;
184 : }
185 :
186 0 : if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
187 0 : return true;
188 : }
189 :
190 0 : if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
191 0 : return true;
192 : }
193 :
194 : // We only cache one masked version
195 0 : if (fKey.fHasBlur &&
196 0 : (fBlurRec.fSigma != blurRec.fSigma ||
197 0 : fBlurRec.fStyle != blurRec.fStyle ||
198 0 : fBlurRec.fQuality != blurRec.fQuality)) {
199 0 : return true;
200 : }
201 :
202 : // Similarly, we only cache one version for each style
203 0 : if (fKey.fStyle != SkPaint::kFill_Style &&
204 0 : (fStrokeInfo.fFrameWidth != paint.skPaint().getStrokeWidth() ||
205 0 : fStrokeInfo.fMiterLimit != paint.skPaint().getStrokeMiter() ||
206 0 : fStrokeInfo.fJoin != paint.skPaint().getStrokeJoin())) {
207 0 : 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 0 : if (this->hasBitmap() && this->hasDistanceField()) {
213 : // Identical viewmatrices and we can reuse in all cases
214 0 : if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) {
215 0 : return false;
216 : }
217 0 : return true;
218 : }
219 :
220 0 : if (this->hasBitmap()) {
221 0 : if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
222 0 : fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
223 0 : fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
224 0 : fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
225 0 : 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 0 : SkScalar transX = viewMatrix.getTranslateX() +
233 0 : viewMatrix.getScaleX() * (x - fInitialX) +
234 0 : viewMatrix.getSkewX() * (y - fInitialY) -
235 0 : fInitialViewMatrix.getTranslateX();
236 0 : SkScalar transY = viewMatrix.getTranslateY() +
237 0 : viewMatrix.getSkewY() * (x - fInitialX) +
238 0 : viewMatrix.getScaleY() * (y - fInitialY) -
239 0 : fInitialViewMatrix.getTranslateY();
240 0 : if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) {
241 0 : return true;
242 : }
243 0 : } 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 0 : SkScalar newMaxScale = viewMatrix.getMaxScale();
247 0 : SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale();
248 0 : SkScalar scaleAdjust = newMaxScale / oldMaxScale;
249 0 : if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
250 0 : 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 0 : return false;
258 : }
259 :
260 0 : inline std::unique_ptr<GrLegacyMeshDrawOp> 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 0 : GrMaskFormat format = info.maskFormat();
266 :
267 0 : std::unique_ptr<GrAtlasTextOp> op;
268 0 : if (info.drawAsDistanceFields()) {
269 0 : SkColor filteredColor = paint.filteredSkColor();
270 0 : bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
271 0 : op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable,
272 : useGammaCorrectDistanceTable, filteredColor,
273 0 : info.hasUseLCDText(), useBGR);
274 : } else {
275 0 : op = GrAtlasTextOp::MakeBitmap(format, glyphCount, cache);
276 : }
277 0 : GrAtlasTextOp::Geometry& geometry = op->geometry();
278 0 : geometry.fViewMatrix = viewMatrix;
279 0 : geometry.fBlob = SkRef(this);
280 0 : geometry.fRun = run;
281 0 : geometry.fSubRun = subRun;
282 0 : geometry.fColor =
283 0 : info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor();
284 0 : geometry.fX = x;
285 0 : geometry.fY = y;
286 0 : op->init();
287 :
288 0 : return std::move(op);
289 : }
290 :
291 0 : 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 0 : int lastRun = fRuns[run].fSubRunInfo.count() - 1;
297 0 : for (int subRun = 0; subRun <= lastRun; subRun++) {
298 0 : const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
299 0 : GrPaint grPaint;
300 0 : if (!paint.toGrPaint(info.maskFormat(), rtc, viewMatrix, &grPaint)) {
301 0 : continue;
302 : }
303 0 : int glyphCount = info.glyphCount();
304 0 : if (0 == glyphCount) {
305 0 : continue;
306 : }
307 :
308 : std::unique_ptr<GrLegacyMeshDrawOp> op(
309 : this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
310 0 : distanceAdjustTable, rtc->isGammaCorrect(), cache));
311 0 : GrPipelineBuilder pipelineBuilder(std::move(grPaint), GrAAType::kNone);
312 :
313 0 : rtc->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
314 : }
315 0 : }
316 :
317 0 : 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 0 : if (applyVM) {
322 0 : *transX = newViewMatrix.getTranslateX() +
323 0 : newViewMatrix.getScaleX() * (newX - currentX) +
324 0 : newViewMatrix.getSkewX() * (newY - currentY) -
325 0 : currentViewMatrix.getTranslateX();
326 :
327 0 : *transY = newViewMatrix.getTranslateY() +
328 0 : newViewMatrix.getSkewY() * (newX - currentX) +
329 0 : newViewMatrix.getScaleY() * (newY - currentY) -
330 0 : currentViewMatrix.getTranslateY();
331 : } else {
332 0 : *transX = newX - currentX;
333 0 : *transY = newY - currentY;
334 : }
335 0 : }
336 :
337 0 : 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 0 : for (int i = 0; i < fBigGlyphs.count(); i++) {
343 0 : GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i];
344 0 : calculate_translation(bigGlyph.fTreatAsBMP, viewMatrix, x, y,
345 0 : fInitialViewMatrix, fInitialX, fInitialY, &transX, &transY);
346 : SkMatrix ctm;
347 0 : ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
348 0 : ctm.postTranslate(bigGlyph.fX + transX, bigGlyph.fY + transY);
349 0 : if (!bigGlyph.fTreatAsBMP) {
350 0 : ctm.postConcat(viewMatrix);
351 : }
352 :
353 0 : GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, bigGlyph.fPath, paint, ctm, nullptr,
354 0 : clipBounds, false);
355 : }
356 0 : }
357 :
358 0 : 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 0 : size_t textLen = it.glyphCount() * sizeof(uint16_t);
364 0 : const SkPoint& offset = it.offset();
365 :
366 0 : GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
367 0 : if (!runPaint.modifyForRun(it)) {
368 0 : return;
369 : }
370 :
371 0 : switch (it.positioning()) {
372 : case SkTextBlob::kDefault_Positioning:
373 0 : GrTextUtils::DrawTextAsPath(context, rtc, clip, runPaint, viewMatrix,
374 0 : (const char*)it.glyphs(), textLen, x + offset.x(),
375 0 : y + offset.y(), clipBounds);
376 0 : break;
377 : case SkTextBlob::kHorizontal_Positioning:
378 0 : GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
379 0 : (const char*)it.glyphs(), textLen, it.pos(), 1,
380 0 : SkPoint::Make(x, y + offset.y()), clipBounds);
381 0 : break;
382 : case SkTextBlob::kFull_Positioning:
383 0 : GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
384 0 : (const char*)it.glyphs(), textLen, it.pos(), 2,
385 0 : SkPoint::Make(x, y), clipBounds);
386 0 : break;
387 : }
388 : }
389 :
390 0 : 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 0 : SkTextBlobRunIterator it(blob);
399 0 : for (int run = 0; !it.done(); it.next(), run++) {
400 0 : if (fRuns[run].fDrawAsPaths) {
401 : this->flushRunAsPaths(context, rtc, props, it, clip, paint, drawFilter, viewMatrix,
402 0 : clipBounds, x, y);
403 0 : continue;
404 : }
405 0 : this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
406 0 : context->getAtlasGlyphCache());
407 : }
408 :
409 : // Now flush big glyphs
410 0 : this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
411 0 : }
412 :
413 0 : 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 0 : for (int run = 0; run < fRunCount; run++) {
420 0 : this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
421 0 : context->getAtlasGlyphCache());
422 : }
423 :
424 : // Now flush big glyphs
425 0 : this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
426 0 : }
427 :
428 0 : std::unique_ptr<GrLegacyMeshDrawOp> 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 0 : const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
433 : return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
434 0 : distanceAdjustTable, false, cache);
435 : }
436 :
437 0 : void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
438 0 : SkASSERT_RELEASE(l.fSize == r.fSize);
439 0 : SkASSERT_RELEASE(l.fPool == r.fPool);
440 :
441 0 : SkASSERT_RELEASE(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
442 0 : SkASSERT_RELEASE(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
443 0 : SkASSERT_RELEASE(l.fBlurRec.fQuality == r.fBlurRec.fQuality);
444 :
445 0 : SkASSERT_RELEASE(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
446 0 : SkASSERT_RELEASE(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
447 0 : SkASSERT_RELEASE(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
448 :
449 0 : SkASSERT_RELEASE(l.fBigGlyphs.count() == r.fBigGlyphs.count());
450 0 : for (int i = 0; i < l.fBigGlyphs.count(); i++) {
451 0 : const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
452 0 : const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
453 :
454 0 : SkASSERT_RELEASE(lBigGlyph.fPath == rBigGlyph.fPath);
455 : // We can't assert that these have the same translations
456 : }
457 :
458 0 : SkASSERT_RELEASE(l.fKey == r.fKey);
459 : //SkASSERT_RELEASE(l.fPaintColor == r.fPaintColor); // Colors might not actually be identical
460 0 : SkASSERT_RELEASE(l.fMaxMinScale == r.fMaxMinScale);
461 0 : SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
462 0 : SkASSERT_RELEASE(l.fTextType == r.fTextType);
463 :
464 0 : SkASSERT_RELEASE(l.fRunCount == r.fRunCount);
465 0 : for (int i = 0; i < l.fRunCount; i++) {
466 0 : const Run& lRun = l.fRuns[i];
467 0 : const Run& rRun = r.fRuns[i];
468 :
469 0 : if (lRun.fTypeface.get()) {
470 0 : SkASSERT_RELEASE(rRun.fTypeface.get());
471 0 : SkASSERT_RELEASE(SkTypeface::Equal(lRun.fTypeface.get(), rRun.fTypeface.get()));
472 : } else {
473 0 : SkASSERT_RELEASE(!rRun.fTypeface.get());
474 : }
475 :
476 :
477 0 : SkASSERT_RELEASE(lRun.fDescriptor.getDesc());
478 0 : SkASSERT_RELEASE(rRun.fDescriptor.getDesc());
479 0 : SkASSERT_RELEASE(*lRun.fDescriptor.getDesc() == *rRun.fDescriptor.getDesc());
480 :
481 0 : if (lRun.fOverrideDescriptor.get()) {
482 0 : SkASSERT_RELEASE(lRun.fOverrideDescriptor->getDesc());
483 0 : SkASSERT_RELEASE(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());
484 0 : SkASSERT_RELEASE(*lRun.fOverrideDescriptor->getDesc() ==
485 : *rRun.fOverrideDescriptor->getDesc());
486 : } else {
487 0 : SkASSERT_RELEASE(!rRun.fOverrideDescriptor.get());
488 : }
489 :
490 : // color can be changed
491 : //SkASSERT(lRun.fColor == rRun.fColor);
492 0 : SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
493 0 : SkASSERT_RELEASE(lRun.fDrawAsPaths == rRun.fDrawAsPaths);
494 :
495 0 : SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
496 0 : for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
497 0 : const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j];
498 0 : 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 0 : if (lSubRun.strike()) {
504 0 : SkASSERT_RELEASE(rSubRun.strike());
505 0 : SkASSERT_RELEASE(GrAtlasTextStrike::GetKey(*lSubRun.strike()) ==
506 : GrAtlasTextStrike::GetKey(*rSubRun.strike()));
507 :
508 : } else {
509 0 : SkASSERT_RELEASE(!rSubRun.strike());
510 : }
511 :
512 0 : SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
513 0 : SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex());
514 0 : SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
515 0 : SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex());
516 0 : SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
517 0 : SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
518 0 : SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
519 : }
520 : }
521 0 : }
522 :
523 0 : void GrAtlasTextBlob::Run::SubRunInfo::computeTranslation(const SkMatrix& viewMatrix,
524 : SkScalar x, SkScalar y, SkScalar* transX,
525 : SkScalar* transY) {
526 0 : calculate_translation(!this->drawAsDistanceFields(), viewMatrix, x, y,
527 0 : fCurrentViewMatrix, fX, fY, transX, transY);
528 0 : fCurrentViewMatrix = viewMatrix;
529 0 : fX = x;
530 0 : fY = y;
531 0 : }
|