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 ------------------------------------------------------------------------------ |
35 | GrTextBlob::Key::Key() { sk_bzero(this, sizeof(Key)); } |
36 | |
37 | bool GrTextBlob::Key::operator==(const GrTextBlob::Key& other) const { |
38 | return 0 == memcmp(this, &other, sizeof(Key)); |
39 | } |
40 | |
41 | // -- GrPathSubRun::PathGlyph ---------------------------------------------------------------------- |
42 | GrPathSubRun::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin) |
43 | : fPath(path) |
44 | , fOrigin(origin) {} |
45 | |
46 | // -- GrPathSubRun --------------------------------------------------------------------------------- |
47 | GrPathSubRun::GrPathSubRun(bool isAntiAliased, |
48 | const SkStrikeSpec& strikeSpec, |
49 | SkSpan<PathGlyph> paths) |
50 | : fIsAntiAliased{isAntiAliased} |
51 | , fStrikeSpec{strikeSpec} |
52 | , fPaths{paths} {} |
53 | |
54 | void 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 | |
109 | auto 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 -------------------------------------------------------------------------------- |
126 | GrGlyphVector::GrGlyphVector(const SkStrikeSpec& spec, SkSpan<Variant> glyphs) |
127 | : fStrikeSpec{spec} |
128 | , fGlyphs{glyphs} { } |
129 | |
130 | GrGlyphVector 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 | |
141 | SkSpan<const GrGlyph*> GrGlyphVector::glyphs() const { |
142 | return SkMakeSpan(reinterpret_cast<const GrGlyph**>(fGlyphs.data()), fGlyphs.size()); |
143 | } |
144 | |
145 | void 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 | |
155 | std::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 -------------------------------------------------------------------------------- |
221 | static 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 --------------------------------------------------------------------------- |
232 | GrDirectMaskSubRun::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 | |
245 | GrSubRun* 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 | |
278 | void 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 | |
286 | size_t GrDirectMaskSubRun::vertexStride() const { |
287 | if (fMaskFormat != kARGB_GrMaskFormat) { |
288 | return sizeof(Mask2DVertex); |
289 | } else { |
290 | return sizeof(ARGB2DVertex); |
291 | } |
292 | } |
293 | |
294 | int GrDirectMaskSubRun::glyphCount() const { |
295 | return fGlyphs.glyphs().count(); |
296 | } |
297 | |
298 | static 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 | |
314 | std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>> |
315 | GrDirectMaskSubRun::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 | |
373 | void GrDirectMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) { |
374 | fGlyphs.packedGlyphIDToGrGlyph(cache); |
375 | } |
376 | |
377 | std::tuple<bool, int> |
378 | GrDirectMaskSubRun::regenerateAtlas(int begin, int end, GrMeshDrawOp::Target* target) const { |
379 | return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target); |
380 | } |
381 | |
382 | template <typename Rect> |
383 | static auto ltbr(const Rect& r) { |
384 | return std::make_tuple(r.left(), r.top(), r.right(), r.bottom()); |
385 | } |
386 | |
387 | void 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 | |
476 | SkRect 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 ---------------------------------------------------------------------- |
490 | GrTransformedMaskSubRun::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 | |
501 | GrSubRun* 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 | |
533 | void 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 | |
543 | std::tuple<const GrClip*, std::unique_ptr<GrDrawOp>> |
544 | GrTransformedMaskSubRun::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 | |
579 | void GrTransformedMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) { |
580 | fGlyphs.packedGlyphIDToGrGlyph(cache); |
581 | } |
582 | |
583 | std::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 | |
588 | template<typename Quad, typename VertexData> |
589 | static 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 | |
612 | template<typename Quad, typename VertexData> |
613 | static 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 | |
642 | void 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 | |
700 | size_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 | |
713 | int GrTransformedMaskSubRun::glyphCount() const { |
714 | return fVertexData.count(); |
715 | } |
716 | |
717 | bool GrTransformedMaskSubRun::hasW() const { |
718 | return fBlob->hasPerspective(); |
719 | } |
720 | |
721 | SkRect 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 --------------------------------------------------------------------------------- |
728 | GrSDFTSubRun::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 | |
743 | GrSubRun* 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 | |
779 | std::tuple<const GrClip*, std::unique_ptr<GrDrawOp> > |
780 | GrSDFTSubRun::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 | |
841 | void 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 | |
851 | void GrSDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) { |
852 | fGlyphs.packedGlyphIDToGrGlyph(cache); |
853 | } |
854 | |
855 | std::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 | |
861 | size_t GrSDFTSubRun::vertexStride() const { |
862 | return this->hasW() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex); |
863 | } |
864 | |
865 | void 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 | |
899 | int GrSDFTSubRun::glyphCount() const { |
900 | return fVertexData.count(); |
901 | } |
902 | |
903 | bool GrSDFTSubRun::hasW() const { |
904 | return fBlob->hasPerspective(); |
905 | } |
906 | |
907 | SkRect 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 ----------------------------------------------------------------------------------- |
914 | void GrTextBlob::operator delete(void* p) { ::operator delete(p); } |
915 | void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new." ); } |
916 | void* GrTextBlob::operator new(size_t, void* p) { return p; } |
917 | |
918 | GrTextBlob::~GrTextBlob() = default; |
919 | |
920 | sk_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 | |
952 | void 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 | } |
964 | const GrTextBlob::Key& GrTextBlob::GetKey(const GrTextBlob& blob) { return blob.fKey; } |
965 | uint32_t GrTextBlob::Hash(const GrTextBlob::Key& key) { return SkOpts::hash(&key, sizeof(Key)); } |
966 | |
967 | bool GrTextBlob::hasDistanceField() const { |
968 | return SkToBool(fTextType & kHasDistanceField_TextType); |
969 | } |
970 | bool GrTextBlob::hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); } |
971 | bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); } |
972 | |
973 | void GrTextBlob::setHasDistanceField() { fTextType |= kHasDistanceField_TextType; } |
974 | void GrTextBlob::setHasBitmap() { fTextType |= kHasBitmap_TextType; } |
975 | void 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 | |
981 | bool 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 | |
1074 | const GrTextBlob::Key& GrTextBlob::key() const { return fKey; } |
1075 | size_t GrTextBlob::size() const { return fSize; } |
1076 | |
1077 | template<typename AddSingleMaskFormat> |
1078 | void 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 | |
1111 | GrTextBlob::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 | |
1121 | void GrTextBlob::insertSubRun(GrSubRun* subRun) { |
1122 | fSubRunList.addToTail(subRun); |
1123 | } |
1124 | |
1125 | void 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 | |
1132 | void 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 | |
1143 | void 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 | |
1154 | void 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 | |