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 "include/core/SkColorFilter.h"
9#include "include/gpu/GrRecordingContext.h"
10#include "include/private/SkTemplates.h"
11#include "src/core/SkMaskFilterBase.h"
12#include "src/core/SkMatrixPriv.h"
13#include "src/core/SkMatrixProvider.h"
14#include "src/core/SkPaintPriv.h"
15#include "src/core/SkStrikeSpec.h"
16#include "src/gpu/GrBlurUtils.h"
17#include "src/gpu/GrClip.h"
18#include "src/gpu/GrMemoryPool.h"
19#include "src/gpu/GrRecordingContextPriv.h"
20#include "src/gpu/GrRenderTargetContext.h"
21#include "src/gpu/GrRenderTargetContextPriv.h"
22#include "src/gpu/GrStyle.h"
23#include "src/gpu/SkGr.h"
24#include "src/gpu/effects/GrDistanceFieldGeoProc.h"
25#include "src/gpu/geometry/GrStyledShape.h"
26#include "src/gpu/ops/GrAtlasTextOp.h"
27#include "src/gpu/text/GrAtlasManager.h"
28#include "src/gpu/text/GrStrikeCache.h"
29#include "src/gpu/text/GrTextBlob.h"
30
31#include <cstddef>
32#include <new>
33
34// -- GrTextBlob::Key ------------------------------------------------------------------------------
35GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); }
36
37bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const {
38 return 0 == memcmp(this, &other, sizeof(Key));
39}
40
41// -- GrPathSubRun::PathGlyph ----------------------------------------------------------------------
42GrPathSubRun::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
43 : fPath(path)
44 , fOrigin(origin) {}
45
46// -- GrPathSubRun ---------------------------------------------------------------------------------
47GrPathSubRun::GrPathSubRun(bool isAntiAliased,
48 const SkStrikeSpec& strikeSpec,
49 SkSpan<PathGlyph> paths)
50 : fIsAntiAliased{isAntiAliased}
51 , fStrikeSpec{strikeSpec}
52 , fPaths{paths} {}
53
54void GrPathSubRun::draw(const GrClip* clip,
55 const SkMatrixProvider& viewMatrix,
56 const SkGlyphRunList& glyphRunList,
57 GrRenderTargetContext* rtc) const {
58 SkASSERT(!fPaths.empty());
59 SkPoint drawOrigin = glyphRunList.origin();
60 const SkPaint& drawPaint = glyphRunList.paint();
61 SkPaint runPaint{drawPaint};
62 runPaint.setAntiAlias(fIsAntiAliased);
63 // If there are shaders, blurs or styles, the path must be scaled into source
64 // space independently of the CTM. This allows the CTM to be correct for the
65 // different effects.
66 GrStyle style(runPaint);
67
68 bool needsExactCTM = runPaint.getShader()
69 || style.applies()
70 || runPaint.getMaskFilter();
71
72 // Calculate the matrix that maps the path glyphs from their size in the strike to
73 // the graphics source space.
74 SkScalar scale = this->fStrikeSpec.strikeToSourceRatio();
75 SkMatrix strikeToSource = SkMatrix::Scale(scale, scale);
76 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
77 if (!needsExactCTM) {
78 for (const auto& pathPos : fPaths) {
79 const SkPath& path = pathPos.fPath;
80 const SkPoint pos = pathPos.fOrigin; // Transform the glyph to source space.
81 SkMatrix pathMatrix = strikeToSource;
82 pathMatrix.postTranslate(pos.x(), pos.y());
83 SkPreConcatMatrixProvider strikeToDevice(viewMatrix, pathMatrix);
84
85 GrStyledShape shape(path, drawPaint);
86 GrBlurUtils::drawShapeWithMaskFilter(
87 rtc->priv().recordingContext(), rtc, clip, runPaint, strikeToDevice, shape);
88 }
89 } else {
90 // Transform the path to device because the deviceMatrix must be unchanged to
91 // draw effect, filter or shader paths.
92 for (const auto& pathPos : fPaths) {
93 const SkPath& path = pathPos.fPath;
94 const SkPoint pos = pathPos.fOrigin;
95 // Transform the glyph to source space.
96 SkMatrix pathMatrix = strikeToSource;
97 pathMatrix.postTranslate(pos.x(), pos.y());
98
99 SkPath deviceOutline;
100 path.transform(pathMatrix, &deviceOutline);
101 deviceOutline.setIsVolatile(true);
102 GrStyledShape shape(deviceOutline, drawPaint);
103 GrBlurUtils::drawShapeWithMaskFilter(
104 rtc->priv().recordingContext(), rtc, clip, runPaint, viewMatrix, shape);
105 }
106 }
107}
108
109auto GrPathSubRun::Make(
110 const SkZip<SkGlyphVariant, SkPoint>& drawables,
111 bool isAntiAliased,
112 const SkStrikeSpec& strikeSpec,
113 SkArenaAlloc* alloc) -> GrSubRun* {
114 PathGlyph* pathData = alloc->makeInitializedArray<PathGlyph>(
115 drawables.size(),
116 [&](size_t i) -> PathGlyph {
117 auto [variant, pos] = drawables[i];
118 return {*variant.path(), pos};
119 });
120
121 return alloc->make<GrPathSubRun>(
122 isAntiAliased, strikeSpec, SkMakeSpan(pathData, drawables.size()));
123};
124
125// -- GrGlyphVector --------------------------------------------------------------------------------
126GrGlyphVector::GrGlyphVector(const SkStrikeSpec& spec, SkSpan<Variant> glyphs)
127 : fStrikeSpec{spec}
128 , fGlyphs{glyphs} { }
129
130GrGlyphVector GrGlyphVector::Make(
131 const SkStrikeSpec &spec, SkSpan<SkGlyphVariant> glyphs, SkArenaAlloc *alloc) {
132
133 Variant* variants = alloc->makeInitializedArray<Variant>(glyphs.size(),
134 [&](int i) {
135 return Variant{glyphs[i].glyph()->getPackedID()};
136 });
137
138 return GrGlyphVector{spec, SkMakeSpan(variants, glyphs.size())};
139}
140
141SkSpan<const GrGlyph*> GrGlyphVector::glyphs() const {
142 return SkMakeSpan(reinterpret_cast<const GrGlyph**>(fGlyphs.data()), fGlyphs.size());
143}
144
145void GrGlyphVector::packedGlyphIDToGrGlyph(GrStrikeCache* cache) {
146 if (fStrike == nullptr) {
147 fStrike = fStrikeSpec.findOrCreateGrStrike(cache);
148
149 for (auto& variant : fGlyphs) {
150 variant.grGlyph = fStrike->getGlyph(variant.packedGlyphID);
151 }
152 }
153}
154
155std::tuple<bool, int> GrGlyphVector::regenerateAtlas(int begin, int end,
156 GrMaskFormat maskFormat,
157 int srcPadding,
158 GrMeshDrawOp::Target* target,
159 bool bilerpPadding) {
160 GrAtlasManager* atlasManager = target->atlasManager();
161 GrDeferredUploadTarget* uploadTarget = target->deferredUploadTarget();
162
163 uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
164
165 this->packedGlyphIDToGrGlyph(target->strikeCache());
166
167 if (fAtlasGeneration != currentAtlasGen) {
168 // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
169 // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
170 fBulkUseToken.reset();
171
172 SkBulkGlyphMetricsAndImages metricsAndImages{fStrikeSpec};
173
174 // Update the atlas information in the GrStrike.
175 auto tokenTracker = uploadTarget->tokenTracker();
176 auto glyphs = fGlyphs.subspan(begin, end - begin);
177 int glyphsPlacedInAtlas = 0;
178 bool success = true;
179 for (const Variant& variant : glyphs) {
180 GrGlyph* grGlyph = variant.grGlyph;
181 SkASSERT(grGlyph != nullptr);
182
183 if (!atlasManager->hasGlyph(maskFormat, grGlyph)) {
184 const SkGlyph& skGlyph = *metricsAndImages.glyph(grGlyph->fPackedID);
185 auto code = atlasManager->addGlyphToAtlas(
186 skGlyph, grGlyph, srcPadding, target->resourceProvider(),
187 uploadTarget, bilerpPadding);
188 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
189 success = code != GrDrawOpAtlas::ErrorCode::kError;
190 break;
191 }
192 }
193 atlasManager->addGlyphToBulkAndSetUseToken(
194 &fBulkUseToken, maskFormat, grGlyph,
195 tokenTracker->nextDrawToken());
196 glyphsPlacedInAtlas++;
197 }
198
199 // Update atlas generation if there are no more glyphs to put in the atlas.
200 if (success && begin + glyphsPlacedInAtlas == fGlyphs.count()) {
201 // Need to get the freshest value of the atlas' generation because
202 // updateTextureCoordinates may have changed it.
203 fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
204 }
205
206 return {success, glyphsPlacedInAtlas};
207 } else {
208 // The atlas hasn't changed, so our texture coordinates are still valid.
209 if (end == fGlyphs.count()) {
210 // The atlas hasn't changed and the texture coordinates are all still valid. Update
211 // all the plots used to the new use token.
212 atlasManager->setUseTokenBulk(fBulkUseToken,
213 uploadTarget->tokenTracker()->nextDrawToken(),
214 maskFormat);
215 }
216 return {true, end - begin};
217 }
218}
219
220// -- GrAtlasSubRun --------------------------------------------------------------------------------
221static GrAtlasTextOp::MaskType op_mask_type(GrMaskFormat grMaskFormat) {
222 switch (grMaskFormat) {
223 case kA8_GrMaskFormat: return GrAtlasTextOp::kGrayscaleCoverageMask_MaskType;
224 case kA565_GrMaskFormat: return GrAtlasTextOp::kLCDCoverageMask_MaskType;
225 case kARGB_GrMaskFormat: return GrAtlasTextOp::kColorBitmapMask_MaskType;
226 // Needed to placate some compilers.
227 default: return GrAtlasTextOp::kGrayscaleCoverageMask_MaskType;
228 }
229}
230
231// -- GrDirectMaskSubRun ---------------------------------------------------------------------------
232GrDirectMaskSubRun::GrDirectMaskSubRun(GrMaskFormat format,
233 SkPoint residual,
234 GrTextBlob* blob,
235 const SkRect& bounds,
236 SkSpan<const VertexData> vertexData,
237 GrGlyphVector glyphs)
238 : fMaskFormat{format}
239 , fResidual{residual}
240 , fBlob{blob}
241 , fVertexBounds{bounds}
242 , fVertexData{vertexData}
243 , fGlyphs{glyphs} { }
244
245GrSubRun* GrDirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
246 const SkStrikeSpec& strikeSpec,
247 GrMaskFormat format,
248 SkPoint residual,
249 GrTextBlob* blob,
250 SkArenaAlloc* alloc) {
251 size_t vertexCount = drawables.size();
252 SkRect bounds = SkRectPriv::MakeLargestInverted();
253
254 auto initializer = [&](size_t i) {
255 auto [variant, pos] = drawables[i];
256 SkGlyph* skGlyph = variant;
257 int16_t l = skGlyph->left();
258 int16_t t = skGlyph->top();
259 int16_t r = l + skGlyph->width();
260 int16_t b = t + skGlyph->height();
261 SkPoint lt = SkPoint::Make(l, t) + pos,
262 rb = SkPoint::Make(r, b) + pos;
263
264 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
265 return VertexData{SkScalarRoundToInt(lt.x()), SkScalarRoundToInt(lt.y())};
266 };
267
268 SkSpan<const VertexData> vertexData{
269 alloc->makeInitializedArray<VertexData>(vertexCount, initializer), vertexCount};
270
271 GrDirectMaskSubRun* subRun = alloc->make<GrDirectMaskSubRun>(
272 format, residual, blob, bounds, vertexData,
273 GrGlyphVector::Make(strikeSpec, drawables.get<0>(), alloc));
274
275 return subRun;
276}
277
278void GrDirectMaskSubRun::draw(const GrClip* clip, const SkMatrixProvider& viewMatrix,
279 const SkGlyphRunList& glyphRunList, GrRenderTargetContext* rtc) const{
280 auto[drawingClip, op] = this->makeAtlasTextOp(clip, viewMatrix, glyphRunList, rtc);
281 if (op != nullptr) {
282 rtc->priv().addDrawOp(drawingClip, std::move(op));
283 }
284}
285
286size_t GrDirectMaskSubRun::vertexStride() const {
287 if (fMaskFormat != kARGB_GrMaskFormat) {
288 return sizeof(Mask2DVertex);
289 } else {
290 return sizeof(ARGB2DVertex);
291 }
292}
293
294int GrDirectMaskSubRun::glyphCount() const {
295 return fGlyphs.glyphs().count();
296}
297
298static SkPMColor4f calculate_colors(GrRenderTargetContext* rtc,
299 const SkPaint& paint,
300 const SkMatrixProvider& matrix,
301 GrMaskFormat grMaskFormat,
302 GrPaint* grPaint) {
303 GrRecordingContext* rContext = rtc->priv().recordingContext();
304 const GrColorInfo& colorInfo = rtc->colorInfo();
305 if (grMaskFormat == kARGB_GrMaskFormat) {
306 SkPaintToGrPaintWithPrimitiveColor(rContext, colorInfo, paint, matrix, grPaint);
307 return SK_PMColor4fWHITE;
308 } else {
309 SkPaintToGrPaint(rContext, colorInfo, paint, matrix, grPaint);
310 return grPaint->getColor4f();
311 }
312}
313
314std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>>
315GrDirectMaskSubRun::makeAtlasTextOp(const GrClip* clip, const SkMatrixProvider& viewMatrix,
316 const SkGlyphRunList& glyphRunList,
317 GrRenderTargetContext* rtc) const {
318 SkASSERT(this->glyphCount() != 0);
319
320 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
321 const SkPoint drawOrigin = glyphRunList.origin();
322
323 // We can clip geometrically using clipRect and ignore clip if we're not using SDFs or
324 // transformed glyphs, and we have an axis-aligned rectangular non-AA clip.
325 SkIRect clipRect = SkIRect::MakeEmpty();
326
327 // We only need to do clipping work if the SubRun isn't contained by the clip
328 const SkRect subRunBounds = this->deviceRect(drawMatrix, drawOrigin);
329 const SkRect renderTargetBounds = SkRect::MakeWH(rtc->width(), rtc->height());
330
331 if (clip == nullptr && !renderTargetBounds.intersects(subRunBounds)) {
332 // If the SubRun is completely outside, don't add an op for it.
333 return {nullptr, nullptr};
334 } else if (clip != nullptr) {
335 const GrClip::PreClipResult result = clip->preApply(subRunBounds, GrAA::kNo);
336 if (result.fEffect == GrClip::Effect::kClipped) {
337 if (result.fIsRRect && result.fRRect.isRect() && result.fAA == GrAA::kNo) {
338 // Clip geometrically during onPrepare using clipRect.
339 result.fRRect.getBounds().round(&clipRect);
340 clip = nullptr;
341 }
342 } else if (result.fEffect == GrClip::Effect::kClippedOut) {
343 return {nullptr, nullptr};
344 }
345 }
346
347 if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
348
349 GrPaint grPaint;
350 const SkPaint& drawPaint = glyphRunList.paint();
351 const SkPMColor4f drawingColor =
352 calculate_colors(rtc, drawPaint, viewMatrix, fMaskFormat, &grPaint);
353 GrAtlasTextOp::Geometry geometry = {
354 *this,
355 drawMatrix,
356 drawOrigin,
357 clipRect,
358 SkRef(fBlob),
359 drawingColor
360 };
361
362 GrOpMemoryPool* const pool = rtc->priv().recordingContext()->priv().opMemoryPool();
363 std::unique_ptr<GrDrawOp> op = pool->allocate<GrAtlasTextOp>(op_mask_type(fMaskFormat),
364 false,
365 this->glyphCount(),
366 subRunBounds,
367 geometry,
368 std::move(grPaint));
369
370 return {clip, std::move(op)};
371}
372
373void GrDirectMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
374 fGlyphs.packedGlyphIDToGrGlyph(cache);
375}
376
377std::tuple<bool, int>
378GrDirectMaskSubRun::regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const {
379 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
380}
381
382template <typename Rect>
383static auto ltbr(const Rect& r) {
384 return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
385}
386
387void GrDirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
388 const SkMatrix& drawMatrix, SkPoint drawOrigin,
389 SkIRect clip) const {
390 SkMatrix matrix = drawMatrix;
391 matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
392
393 auto vertices = [&](auto dst) {
394 return SkMakeZip(dst,
395 fGlyphs.glyphs().subspan(offset, count),
396 fVertexData.subspan(offset, count));
397 };
398
399 auto direct2D = [&](auto dst, SkIRect* clip) {
400 // Rectangles in device space
401 SkPoint originInDeviceSpace = matrix.mapXY(0, 0) + fResidual;
402 SkIPoint originInDeviceSpaceI = {SkScalarRoundToInt(originInDeviceSpace.x()),
403 SkScalarRoundToInt(originInDeviceSpace.y())};
404 for (auto[quad, glyph, leftTop] : vertices(dst)) {
405 GrIRect16 rect = glyph->fAtlasLocator.rect();
406 int16_t w = rect.width(),
407 h = rect.height();
408 auto[l, t] = leftTop + originInDeviceSpaceI;
409 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
410 if (clip == nullptr) {
411 auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
412 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
413 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
414 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
415 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
416 } else {
417 SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
418 SkScalar dl, dt, dr, db;
419 uint16_t tl, tt, tr, tb;
420 if (!clip->containsNoEmptyCheck(devIRect)) {
421 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
422 int lD = clipped.left() - devIRect.left();
423 int tD = clipped.top() - devIRect.top();
424 int rD = clipped.right() - devIRect.right();
425 int bD = clipped.bottom() - devIRect.bottom();
426 std::tie(dl, dt, dr, db) = ltbr(clipped);
427 int index = glyph->fAtlasLocator.pageIndex();
428 std::tie(tl, tt) =
429 GrDrawOpAtlas::PackIndexInTexCoords(
430 rect.fLeft + lD, rect.fTop + tD, index);
431 std::tie(tr, tb) =
432 GrDrawOpAtlas::PackIndexInTexCoords(
433 rect.fRight + rD, rect.fBottom + bD, index);
434 } else {
435 // TODO: omit generating any vertex data for fully clipped glyphs ?
436 std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
437 std::tie(tl, tt, tr, tb) = std::make_tuple(0, 0, 0, 0);
438 }
439
440 } else {
441 std::tie(dl, dt, dr, db) = ltbr(devIRect);
442 std::tie(tl, tt, tr, tb) = std::tie(al, at, ar, ab);
443 }
444 quad[0] = {{dl, dt}, color, {tl, tt}}; // L,T
445 quad[1] = {{dl, db}, color, {tl, tb}}; // L,B
446 quad[2] = {{dr, dt}, color, {tr, tt}}; // R,T
447 quad[3] = {{dr, db}, color, {tr, tb}}; // R,B
448 }
449 }
450 };
451
452 if (clip.isEmpty()) {
453 if (fMaskFormat != kARGB_GrMaskFormat) {
454 using Quad = Mask2DVertex[4];
455 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
456 direct2D((Quad*) vertexDst, nullptr);
457 } else {
458 using Quad = ARGB2DVertex[4];
459 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
460 direct2D((Quad*) vertexDst, nullptr);
461 }
462 } else {
463 if (fMaskFormat != kARGB_GrMaskFormat) {
464 using Quad = Mask2DVertex[4];
465 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
466 direct2D((Quad*) vertexDst, &clip);
467 } else {
468 using Quad = ARGB2DVertex[4];
469 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
470 direct2D((Quad*) vertexDst, &clip);
471 }
472 }
473
474}
475
476SkRect GrDirectMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
477 SkRect outBounds = fVertexBounds;
478
479 SkPoint offset = drawMatrix.mapXY(drawOrigin.x(), drawOrigin.y());
480 // The vertex bounds are already {0, 0} based, so just add the new origin offset.
481 outBounds.offset(offset);
482
483 // Due to floating point numerical inaccuracies, we have to round out here
484 outBounds.roundOut();
485
486 return outBounds;
487}
488
489// -- GrTransformedMaskSubRun ----------------------------------------------------------------------
490GrTransformedMaskSubRun::GrTransformedMaskSubRun(GrMaskFormat format,
491 GrTextBlob* blob,
492 const SkRect& bounds,
493 SkSpan<const VertexData> vertexData,
494 GrGlyphVector glyphs)
495 : fMaskFormat{format}
496 , fBlob{blob}
497 , fVertexBounds{bounds}
498 , fVertexData{vertexData}
499 , fGlyphs{glyphs} { }
500
501GrSubRun* GrTransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
502 const SkStrikeSpec& strikeSpec,
503 GrMaskFormat format,
504 SkPoint residual,
505 GrTextBlob* blob,
506 SkArenaAlloc* alloc) {
507 size_t vertexCount = drawables.size();
508 SkRect bounds = SkRectPriv::MakeLargestInverted();
509 auto initializer = [&, strikeToSource=strikeSpec.strikeToSourceRatio()](size_t i) {
510 auto [variant, pos] = drawables[i];
511 SkGlyph* skGlyph = variant;
512 int16_t l = skGlyph->left();
513 int16_t t = skGlyph->top();
514 int16_t r = l + skGlyph->width();
515 int16_t b = t + skGlyph->height();
516 SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
517 rb = SkPoint::Make(r, b) * strikeToSource + pos;
518
519 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
520 return VertexData{pos, {l, t, r, b}};
521 };
522
523 SkSpan<VertexData> vertexData{
524 alloc->makeInitializedArray<VertexData>(vertexCount, initializer), vertexCount};
525
526 GrAtlasSubRun* subRun = alloc->make<GrTransformedMaskSubRun>(
527 format, blob, bounds, vertexData,
528 GrGlyphVector::Make(strikeSpec, drawables.get<0>(), alloc));
529
530 return subRun;
531}
532
533void GrTransformedMaskSubRun::draw(const GrClip* clip,
534 const SkMatrixProvider& viewMatrix,
535 const SkGlyphRunList& glyphRunList,
536 GrRenderTargetContext* rtc) const {
537 auto[drawingClip, op] = this->makeAtlasTextOp(clip, viewMatrix, glyphRunList, rtc);
538 if (op != nullptr) {
539 rtc->priv().addDrawOp(drawingClip, std::move(op));
540 }
541}
542
543std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>>
544GrTransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
545 const SkMatrixProvider& viewMatrix,
546 const SkGlyphRunList& glyphRunList,
547 GrRenderTargetContext* rtc) const {
548 SkASSERT(this->glyphCount() != 0);
549
550 SkPoint drawOrigin = glyphRunList.origin();
551 const SkPaint& drawPaint = glyphRunList.paint();
552 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
553 GrOpMemoryPool* pool = rtc->priv().recordingContext()->priv().opMemoryPool();
554
555 GrPaint grPaint;
556 SkPMColor4f drawingColor = calculate_colors(rtc, drawPaint, viewMatrix, fMaskFormat, &grPaint);
557
558 // We can clip geometrically using clipRect and ignore clip if we're not using SDFs or
559 // transformed glyphs, and we have an axis-aligned rectangular non-AA clip.
560 GrAtlasTextOp::Geometry geometry = {
561 *this,
562 drawMatrix,
563 drawOrigin,
564 SkIRect::MakeEmpty(),
565 SkRef(fBlob),
566 drawingColor
567 };
568
569 std::unique_ptr<GrDrawOp> op = pool->allocate<GrAtlasTextOp>(
570 op_mask_type(fMaskFormat),
571 true,
572 this->glyphCount(),
573 this->deviceRect(drawMatrix, drawOrigin),
574 geometry,
575 std::move(grPaint));
576 return {clip, std::move(op)};
577}
578
579void GrTransformedMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
580 fGlyphs.packedGlyphIDToGrGlyph(cache);
581}
582
583std::tuple<bool, int> GrTransformedMaskSubRun::regenerateAtlas(int begin, int end,
584 GrMeshDrawOp::Target* target) const {
585 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
586}
587
588template<typename Quad, typename VertexData>
589static void fill_transformed_vertices_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
590 SkScalar dstPadding,
591 SkScalar strikeToSource,
592 GrColor color,
593 const SkMatrix& matrix) {
594 SkPoint inset = {dstPadding, dstPadding};
595 for (auto[quad, glyph, vertexData] : quadData) {
596 auto[pos, rect] = vertexData;
597 auto[l, t, r, b] = rect;
598 SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
599 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
600 SkPoint lt = matrix.mapXY(sLT.x(), sLT.y()),
601 lb = matrix.mapXY(sLT.x(), sRB.y()),
602 rt = matrix.mapXY(sRB.x(), sLT.y()),
603 rb = matrix.mapXY(sRB.x(), sRB.y());
604 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
605 quad[0] = {lt, color, {al, at}}; // L,T
606 quad[1] = {lb, color, {al, ab}}; // L,B
607 quad[2] = {rt, color, {ar, at}}; // R,T
608 quad[3] = {rb, color, {ar, ab}}; // R,B
609 }
610}
611
612template<typename Quad, typename VertexData>
613static void fill_transformed_vertices_3D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
614 SkScalar dstPadding,
615 SkScalar strikeToSource,
616 GrColor color,
617 const SkMatrix& matrix) {
618 SkPoint inset = {dstPadding, dstPadding};
619 auto mapXYZ = [&](SkScalar x, SkScalar y) {
620 SkPoint pt{x, y};
621 SkPoint3 result;
622 matrix.mapHomogeneousPoints(&result, &pt, 1);
623 return result;
624 };
625 for (auto[quad, glyph, vertexData] : quadData) {
626 auto[pos, rect] = vertexData;
627 auto [l, t, r, b] = rect;
628 SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
629 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
630 SkPoint3 lt = mapXYZ(sLT.x(), sLT.y()),
631 lb = mapXYZ(sLT.x(), sRB.y()),
632 rt = mapXYZ(sRB.x(), sLT.y()),
633 rb = mapXYZ(sRB.x(), sRB.y());
634 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
635 quad[0] = {lt, color, {al, at}}; // L,T
636 quad[1] = {lb, color, {al, ab}}; // L,B
637 quad[2] = {rt, color, {ar, at}}; // R,T
638 quad[3] = {rb, color, {ar, ab}}; // R,B
639 }
640}
641
642void GrTransformedMaskSubRun::fillVertexData(void* vertexDst,
643 int offset, int count,
644 GrColor color,
645 const SkMatrix& drawMatrix, SkPoint drawOrigin,
646 SkIRect clip) const {
647 constexpr SkScalar kDstPadding = 0.f;
648 SkMatrix matrix = drawMatrix;
649 matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
650
651 auto quadData = [&](auto dst) {
652 return SkMakeZip(dst,
653 fGlyphs.glyphs().subspan(offset, count),
654 fVertexData.subspan(offset, count));
655 };
656
657 if (!this->hasW()) {
658 if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
659 using Quad = ARGB2DVertex[4];
660 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
661 fill_transformed_vertices_2D(
662 quadData((Quad*) vertexDst),
663 kDstPadding,
664 fGlyphs.strikeToSourceRatio(),
665 color,
666 matrix);
667 } else {
668 using Quad = Mask2DVertex[4];
669 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
670 fill_transformed_vertices_2D(
671 quadData((Quad*) vertexDst),
672 kDstPadding,
673 fGlyphs.strikeToSourceRatio(),
674 color,
675 matrix);
676 }
677 } else {
678 if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
679 using Quad = ARGB3DVertex[4];
680 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
681 fill_transformed_vertices_3D(
682 quadData((Quad*) vertexDst),
683 kDstPadding,
684 fGlyphs.strikeToSourceRatio(),
685 color,
686 matrix);
687 } else {
688 using Quad = Mask3DVertex[4];
689 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
690 fill_transformed_vertices_3D(
691 quadData((Quad*) vertexDst),
692 kDstPadding,
693 fGlyphs.strikeToSourceRatio(),
694 color,
695 matrix);
696 }
697 }
698}
699
700size_t GrTransformedMaskSubRun::vertexStride() const {
701 switch (fMaskFormat) {
702 case kA8_GrMaskFormat:
703 return this->hasW() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
704 case kARGB_GrMaskFormat:
705 return this->hasW() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
706 default:
707 SkASSERT(!this->hasW());
708 return sizeof(Mask2DVertex);
709 }
710 SkUNREACHABLE;
711}
712
713int GrTransformedMaskSubRun::glyphCount() const {
714 return fVertexData.count();
715}
716
717bool GrTransformedMaskSubRun::hasW() const {
718 return fBlob->hasPerspective();
719}
720
721SkRect GrTransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
722 SkRect outBounds = fVertexBounds;
723 outBounds.offset(drawOrigin);
724 return drawMatrix.mapRect(outBounds);
725}
726
727// -- GrSDFTSubRun ---------------------------------------------------------------------------------
728GrSDFTSubRun::GrSDFTSubRun(GrMaskFormat format,
729 GrTextBlob* textBlob,
730 SkRect vertexBounds,
731 SkSpan<const VertexData> vertexData,
732 GrGlyphVector glyphs,
733 bool useLCDText,
734 bool antiAliased)
735 : fMaskFormat{format}
736 , fBlob{textBlob}
737 , fVertexBounds{vertexBounds}
738 , fVertexData{vertexData}
739 , fGlyphs{glyphs}
740 , fUseLCDText{useLCDText}
741 , fAntiAliased{antiAliased} { }
742
743GrSubRun* GrSDFTSubRun::Make(
744 const SkZip<SkGlyphVariant, SkPoint>& drawables,
745 const SkFont& runFont,
746 const SkStrikeSpec& strikeSpec,
747 GrTextBlob* blob,
748 SkArenaAlloc* alloc) {
749
750 size_t vertexCount = drawables.size();
751 SkRect bounds = SkRectPriv::MakeLargestInverted();
752 auto initializer = [&, strikeToSource=strikeSpec.strikeToSourceRatio()](size_t i) {
753 auto [variant, pos] = drawables[i];
754 SkGlyph* skGlyph = variant;
755 int16_t l = skGlyph->left();
756 int16_t t = skGlyph->top();
757 int16_t r = l + skGlyph->width();
758 int16_t b = t + skGlyph->height();
759 SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
760 rb = SkPoint::Make(r, b) * strikeToSource + pos;
761
762 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
763 return VertexData{pos, {l, t, r, b}};
764 };
765
766 SkSpan<VertexData> vertexData{
767 alloc->makeInitializedArray<VertexData>(vertexCount, initializer), vertexCount};
768
769 return alloc->make<GrSDFTSubRun>(
770 kA8_GrMaskFormat,
771 blob,
772 bounds,
773 vertexData,
774 GrGlyphVector::Make(strikeSpec, drawables.get<0>(), alloc),
775 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
776 runFont.hasSomeAntiAliasing());
777}
778
779std::tuple<const GrClip*, std::unique_ptr<GrDrawOp> >
780GrSDFTSubRun::makeAtlasTextOp(const GrClip* clip,
781 const SkMatrixProvider& viewMatrix,
782 const SkGlyphRunList& glyphRunList,
783 GrRenderTargetContext* rtc) const {
784 SkASSERT(this->glyphCount() != 0);
785
786 SkPoint drawOrigin = glyphRunList.origin();
787 const SkPaint& drawPaint = glyphRunList.paint();
788 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
789 GrOpMemoryPool* pool = rtc->priv().recordingContext()->priv().opMemoryPool();
790
791 GrPaint grPaint;
792 SkPMColor4f drawingColor = calculate_colors(rtc, drawPaint, viewMatrix, fMaskFormat, &grPaint);
793
794 const GrColorInfo& colorInfo = rtc->colorInfo();
795 const SkSurfaceProps& props = rtc->surfaceProps();
796 bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
797 bool isLCD = fUseLCDText && SkPixelGeometryIsH(props.pixelGeometry());
798 using MT = GrAtlasTextOp::MaskType;
799 MT maskType = !fAntiAliased ? MT::kAliasedDistanceField_MaskType
800 : isLCD ? (isBGR ? MT::kLCDBGRDistanceField_MaskType
801 : MT::kLCDDistanceField_MaskType)
802 : MT::kGrayscaleDistanceField_MaskType;
803
804 bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
805 uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
806 DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
807 DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
808 DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
809 DFGPFlags |= MT::kAliasedDistanceField_MaskType == maskType ?
810 kAliased_DistanceFieldEffectFlag : 0;
811
812 if (isLCD) {
813 DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
814 DFGPFlags |= MT::kLCDBGRDistanceField_MaskType == maskType ?
815 kBGR_DistanceFieldEffectFlag : 0;
816 }
817
818 GrAtlasTextOp::Geometry geometry = {
819 *this,
820 drawMatrix,
821 drawOrigin,
822 SkIRect::MakeEmpty(),
823 SkRef(fBlob),
824 drawingColor
825 };
826
827 std::unique_ptr<GrDrawOp> op = pool->allocate<GrAtlasTextOp>(
828 maskType,
829 true,
830 this->glyphCount(),
831 this->deviceRect(drawMatrix, drawOrigin),
832 SkPaintPriv::ComputeLuminanceColor(drawPaint),
833 useGammaCorrectDistanceTable,
834 DFGPFlags,
835 geometry,
836 std::move(grPaint));
837
838 return {clip, std::move(op)};
839}
840
841void GrSDFTSubRun::draw(const GrClip* clip,
842 const SkMatrixProvider& viewMatrix,
843 const SkGlyphRunList& glyphRunList,
844 GrRenderTargetContext* rtc) const {
845 auto[drawingClip, op] = this->makeAtlasTextOp(clip, viewMatrix, glyphRunList, rtc);
846 if (op != nullptr) {
847 rtc->priv().addDrawOp(drawingClip, std::move(op));
848 }
849}
850
851void GrSDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
852 fGlyphs.packedGlyphIDToGrGlyph(cache);
853}
854
855std::tuple<bool, int> GrSDFTSubRun::regenerateAtlas(
856 int begin, int end, GrMeshDrawOp::Target *target) const {
857
858 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
859}
860
861size_t GrSDFTSubRun::vertexStride() const {
862 return this->hasW() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
863}
864
865void GrSDFTSubRun::fillVertexData(
866 void *vertexDst, int offset, int count,
867 GrColor color, const SkMatrix& drawMatrix, SkPoint drawOrigin, SkIRect clip) const {
868
869 SkMatrix matrix = drawMatrix;
870 matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
871
872 auto quadData = [&](auto dst) {
873 return SkMakeZip(dst,
874 fGlyphs.glyphs().subspan(offset, count),
875 fVertexData.subspan(offset, count));
876 };
877
878 if (!this->hasW()) {
879 using Quad = Mask2DVertex[4];
880 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
881 fill_transformed_vertices_2D(
882 quadData((Quad*) vertexDst),
883 SK_DistanceFieldInset,
884 fGlyphs.strikeToSourceRatio(),
885 color,
886 matrix);
887 } else {
888 using Quad = Mask3DVertex[4];
889 SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
890 fill_transformed_vertices_3D(
891 quadData((Quad*) vertexDst),
892 SK_DistanceFieldInset,
893 fGlyphs.strikeToSourceRatio(),
894 color,
895 matrix);
896 }
897}
898
899int GrSDFTSubRun::glyphCount() const {
900 return fVertexData.count();
901}
902
903bool GrSDFTSubRun::hasW() const {
904 return fBlob->hasPerspective();
905}
906
907SkRect GrSDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
908 SkRect outBounds = fVertexBounds;
909 outBounds.offset(drawOrigin);
910 return drawMatrix.mapRect(outBounds);
911}
912
913// -- GrTextBlob -----------------------------------------------------------------------------------
914void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
915void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
916void* GrTextBlob::operator new(size_t, void* p) { return p; }
917
918GrTextBlob::~GrTextBlob() = default;
919
920sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList, const SkMatrix& drawMatrix) {
921 // The difference in alignment from the storage of VertexData to SubRun;
922 using AllSubRuns = std::aligned_union_t<1,
923 GrDirectMaskSubRun,
924 GrTransformedMaskSubRun,
925 GrSDFTSubRun,
926 GrPathSubRun>;
927
928 using AllVertexData = std::aligned_union<1,
929 GrDirectMaskSubRun::VertexData,
930 GrTransformedMaskSubRun::VertexData,
931 GrSDFTSubRun::VertexData>;
932 constexpr size_t alignDiff = alignof(AllSubRuns) - alignof(AllVertexData);
933 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
934 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
935 size_t arenaSize =
936 totalGlyphCount * sizeof(AllVertexData)
937 + GrGlyphVector::GlyphVectorSize(totalGlyphCount)
938 + glyphRunList.runCount() * (sizeof(AllSubRuns) + vertexDataToSubRunPadding)
939 + 32; // Misc arena overhead.
940
941 size_t allocationSize = sizeof(GrTextBlob) + arenaSize;
942
943 void* allocation = ::operator new (allocationSize);
944
945 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(glyphRunList.paint());
946 sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{
947 arenaSize, drawMatrix, glyphRunList.origin(), initialLuminance}};
948
949 return blob;
950}
951
952void GrTextBlob::setupKey(const GrTextBlob::Key& key, const SkMaskFilterBase::BlurRec& blurRec,
953 const SkPaint& paint) {
954 fKey = key;
955 if (key.fHasBlur) {
956 fBlurRec = blurRec;
957 }
958 if (key.fStyle != SkPaint::kFill_Style) {
959 fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
960 fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
961 fStrokeInfo.fJoin = paint.getStrokeJoin();
962 }
963}
964const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; }
965uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); }
966
967bool GrTextBlob::hasDistanceField() const {
968 return SkToBool(fTextType & kHasDistanceField_TextType);
969}
970bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
971bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
972
973void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
974void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; }
975void GrTextBlob::setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
976 // we init fMaxMinScale and fMinMaxScale in the constructor
977 fMaxMinScale = std::max(scaledMin, fMaxMinScale);
978 fMinMaxScale = std::min(scaledMax, fMinMaxScale);
979}
980
981bool GrTextBlob::canReuse(const SkPaint& paint,
982 const SkMaskFilterBase::BlurRec& blurRec,
983 const SkMatrix& drawMatrix,
984 SkPoint drawOrigin) {
985 // A singular matrix will create a GrTextBlob with no SubRuns, but unknown glyphs can
986 // also cause empty runs. If there are no subRuns, and the matrix is complicated, then
987 // regenerate.
988 if (fSubRunList.isEmpty() && !fInitialMatrix.rectStaysRect()) {
989 return false;
990 }
991
992 // If we have LCD text then our canonical color will be set to transparent, in this case we have
993 // to regenerate the blob on any color change
994 // We use the grPaint to get any color filter effects
995 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
996 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
997 return false;
998 }
999
1000 if (fInitialMatrix.hasPerspective() != drawMatrix.hasPerspective()) {
1001 return false;
1002 }
1003
1004 /** This could be relaxed for blobs with only distance field glyphs. */
1005 if (fInitialMatrix.hasPerspective() && !SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix)) {
1006 return false;
1007 }
1008
1009 // We only cache one masked version
1010 if (fKey.fHasBlur &&
1011 (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) {
1012 return false;
1013 }
1014
1015 // Similarly, we only cache one version for each style
1016 if (fKey.fStyle != SkPaint::kFill_Style &&
1017 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
1018 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
1019 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
1020 return false;
1021 }
1022
1023 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
1024 // for mixed blobs if this becomes an issue.
1025 if (this->hasBitmap() && this->hasDistanceField()) {
1026 // Identical view matrices and we can reuse in all cases
1027 return SkMatrixPriv::CheapEqual(fInitialMatrix, drawMatrix) && drawOrigin == fInitialOrigin;
1028 }
1029
1030 if (this->hasBitmap()) {
1031 if (fInitialMatrix.getScaleX() != drawMatrix.getScaleX() ||
1032 fInitialMatrix.getScaleY() != drawMatrix.getScaleY() ||
1033 fInitialMatrix.getSkewX() != drawMatrix.getSkewX() ||
1034 fInitialMatrix.getSkewY() != drawMatrix.getSkewY()) {
1035 return false;
1036 }
1037
1038 // TODO(herb): this is not needed for full pixel glyph choice, but is needed to adjust
1039 // the quads properly. Devise a system that regenerates the quads from original data
1040 // using the transform to allow this to be used in general.
1041
1042 // We can update the positions in the text blob without regenerating the whole
1043 // blob, but only for integer translations.
1044 // Calculate the translation in source space to a translation in device space by mapping
1045 // (0, 0) through both the initial matrix and the draw matrix; take the difference.
1046 SkMatrix initialMatrix{fInitialMatrix};
1047 initialMatrix.preTranslate(fInitialOrigin.x(), fInitialOrigin.y());
1048 SkPoint initialDeviceOrigin{0, 0};
1049 initialMatrix.mapPoints(&initialDeviceOrigin, 1);
1050 SkMatrix completeDrawMatrix{drawMatrix};
1051 completeDrawMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
1052 SkPoint drawDeviceOrigin{0, 0};
1053 completeDrawMatrix.mapPoints(&drawDeviceOrigin, 1);
1054 SkPoint translation = drawDeviceOrigin - initialDeviceOrigin;
1055
1056 if (!SkScalarIsInt(translation.x()) || !SkScalarIsInt(translation.y())) {
1057 return false;
1058 }
1059 } else if (this->hasDistanceField()) {
1060 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
1061 // distance field being generated, so we have to regenerate in those cases
1062 SkScalar newMaxScale = drawMatrix.getMaxScale();
1063 SkScalar oldMaxScale = fInitialMatrix.getMaxScale();
1064 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
1065 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
1066 return false;
1067 }
1068 }
1069
1070 // If the blob is all paths, there is no reason to regenerate.
1071 return true;
1072}
1073
1074const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
1075size_t GrTextBlob::size() const { return fSize; }
1076
1077template<typename AddSingleMaskFormat>
1078void GrTextBlob::addMultiMaskFormat(
1079 AddSingleMaskFormat addSingle,
1080 const SkZip<SkGlyphVariant, SkPoint>& drawables,
1081 const SkStrikeSpec& strikeSpec,
1082 SkPoint residual) {
1083 this->setHasBitmap();
1084 if (drawables.empty()) { return; }
1085
1086 auto glyphSpan = drawables.get<0>();
1087 SkGlyph* glyph = glyphSpan[0];
1088 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1089 size_t startIndex = 0;
1090 for (size_t i = 1; i < drawables.size(); i++) {
1091 glyph = glyphSpan[i];
1092 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1093 if (format != nextFormat) {
1094 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
1095 GrSubRun* subRun = addSingle(sameFormat, strikeSpec, format, residual, this, &fAlloc);
1096 this->insertSubRun(subRun);
1097 format = nextFormat;
1098 startIndex = i;
1099 }
1100 }
1101 auto sameFormat = drawables.last(drawables.size() - startIndex);
1102 GrSubRun* subRun = addSingle(sameFormat,
1103 strikeSpec,
1104 format,
1105 residual,
1106 this,
1107 &fAlloc);
1108 this->insertSubRun(subRun);
1109}
1110
1111GrTextBlob::GrTextBlob(size_t allocSize,
1112 const SkMatrix& drawMatrix,
1113 SkPoint origin,
1114 SkColor initialLuminance)
1115 : fSize{allocSize}
1116 , fInitialMatrix{drawMatrix}
1117 , fInitialOrigin{origin}
1118 , fInitialLuminance{initialLuminance}
1119 , fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2} { }
1120
1121void GrTextBlob::insertSubRun(GrSubRun* subRun) {
1122 fSubRunList.addToTail(subRun);
1123}
1124
1125void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1126 const SkStrikeSpec& strikeSpec,
1127 SkPoint residual) {
1128
1129 this->addMultiMaskFormat(GrDirectMaskSubRun::Make, drawables, strikeSpec, residual);
1130}
1131
1132void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1133 const SkFont& runFont,
1134 const SkStrikeSpec& strikeSpec) {
1135 this->setHasBitmap();
1136 GrSubRun* subRun = GrPathSubRun::Make(drawables,
1137 runFont.hasSomeAntiAliasing(),
1138 strikeSpec,
1139 &fAlloc);
1140 this->insertSubRun(subRun);
1141}
1142
1143void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1144 const SkStrikeSpec& strikeSpec,
1145 const SkFont& runFont,
1146 SkScalar minScale,
1147 SkScalar maxScale) {
1148 this->setHasDistanceField();
1149 this->setMinAndMaxScale(minScale, maxScale);
1150 GrSubRun* subRun = GrSDFTSubRun::Make(drawables, runFont, strikeSpec, this, &fAlloc);
1151 this->insertSubRun(subRun);
1152}
1153
1154void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1155 const SkStrikeSpec& strikeSpec) {
1156 // In this case the residual is {0, 0} because it is not used to calculate the positions of
1157 // transformed mask. Any value would do.
1158 this->addMultiMaskFormat(GrTransformedMaskSubRun::Make, drawables, strikeSpec, {0, 0});
1159}
1160