1 /*
2 * Copyright 2018 The Android Open Source Project
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 "src/core/SkGlyphRunPainter.h"
9
10 #if SK_SUPPORT_GPU
11 #include "include/private/GrRecordingContext.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrColorInfo.h"
14 #include "src/gpu/GrContextPriv.h"
15 #include "src/gpu/GrRecordingContextPriv.h"
16 #include "src/gpu/GrRenderTargetContext.h"
17 #include "src/gpu/SkGr.h"
18 #include "src/gpu/text/GrTextBlobCache.h"
19 #include "src/gpu/text/GrTextContext.h"
20 #endif
21
22 #include "include/core/SkColorFilter.h"
23 #include "include/core/SkMaskFilter.h"
24 #include "include/core/SkPathEffect.h"
25 #include "include/private/SkTDArray.h"
26 #include "src/core/SkDevice.h"
27 #include "src/core/SkDistanceFieldGen.h"
28 #include "src/core/SkDraw.h"
29 #include "src/core/SkFontPriv.h"
30 #include "src/core/SkRasterClip.h"
31 #include "src/core/SkStrike.h"
32 #include "src/core/SkStrikeCache.h"
33 #include "src/core/SkStrikeForGPU.h"
34 #include "src/core/SkStrikeSpec.h"
35 #include "src/core/SkTraceEvent.h"
36
37 #include <climits>
38
39 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkScalerContextFlags flags,SkStrikeForGPUCacheInterface * strikeCache)40 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
41 SkColorType colorType,
42 SkScalerContextFlags flags,
43 SkStrikeForGPUCacheInterface* strikeCache)
44 : fDeviceProps{props}
45 , fBitmapFallbackProps{SkSurfaceProps{props.flags(), kUnknown_SkPixelGeometry}}
46 , fColorType{colorType}, fScalerContextFlags{flags}
47 , fStrikeCache{strikeCache} {}
48
49 // TODO: unify with code in GrTextContext.cpp
compute_scaler_context_flags(const SkColorSpace * cs)50 static SkScalerContextFlags compute_scaler_context_flags(const SkColorSpace* cs) {
51 // If we're doing linear blending, then we can disable the gamma hacks.
52 // Otherwise, leave them on. In either case, we still want the contrast boost:
53 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
54 if (cs && cs->gammaIsLinear()) {
55 return SkScalerContextFlags::kBoostContrast;
56 } else {
57 return SkScalerContextFlags::kFakeGammaAndBoostContrast;
58 }
59 }
60
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkColorSpace * cs,SkStrikeForGPUCacheInterface * strikeCache)61 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
62 SkColorType colorType,
63 SkColorSpace* cs,
64 SkStrikeForGPUCacheInterface* strikeCache)
65 : SkGlyphRunListPainter(props, colorType, compute_scaler_context_flags(cs), strikeCache) {}
66
67 #if SK_SUPPORT_GPU
SkGlyphRunListPainter(const SkSurfaceProps & props,const GrColorInfo & csi)68 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props, const GrColorInfo& csi)
69 : SkGlyphRunListPainter(props,
70 kUnknown_SkColorType,
71 compute_scaler_context_flags(csi.colorSpace()),
72 SkStrikeCache::GlobalStrikeCache()) {}
73
SkGlyphRunListPainter(const GrRenderTargetContext & rtc)74 SkGlyphRunListPainter::SkGlyphRunListPainter(const GrRenderTargetContext& rtc)
75 : SkGlyphRunListPainter{rtc.surfaceProps(), rtc.colorInfo()} {}
76
77 #endif
78
DeviceSpacePackedGlyphIDs(const SkGlyphPositionRoundingSpec & roundingSpec,const SkMatrix & viewMatrix,const SkPoint & origin,int n,const SkGlyphID * glyphIDs,const SkPoint * positions,SkPoint * mappedPositions,SkPackedGlyphID * results)79 SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::DeviceSpacePackedGlyphIDs(
80 const SkGlyphPositionRoundingSpec& roundingSpec,
81 const SkMatrix& viewMatrix,
82 const SkPoint& origin,
83 int n,
84 const SkGlyphID* glyphIDs,
85 const SkPoint* positions,
86 SkPoint* mappedPositions,
87 SkPackedGlyphID* results) {
88 // Add rounding and origin.
89 SkMatrix matrix = viewMatrix;
90 matrix.preTranslate(origin.x(), origin.y());
91 SkPoint rounding = roundingSpec.halfAxisSampleFreq;
92 matrix.postTranslate(rounding.x(), rounding.y());
93 matrix.mapPoints(mappedPositions, positions, n);
94
95 SkIPoint mask = roundingSpec.ignorePositionMask;
96
97 for (int i = 0; i < n; i++) {
98 SkFixed subX = SkScalarToFixed(mappedPositions[i].x()) & mask.x(),
99 subY = SkScalarToFixed(mappedPositions[i].y()) & mask.y();
100 results[i] = SkPackedGlyphID{glyphIDs[i], subX, subY};
101 }
102
103 return SkSpan<const SkPackedGlyphID>{results, SkTo<size_t>(n)};
104 }
105
SourceSpacePackedGlyphIDs(const SkPoint & origin,int n,const SkGlyphID * glyphIDs,const SkPoint * positions,SkPoint * mappedPositions,SkPackedGlyphID * results)106 SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::SourceSpacePackedGlyphIDs(
107 const SkPoint& origin,
108 int n,
109 const SkGlyphID* glyphIDs,
110 const SkPoint* positions,
111 SkPoint* mappedPositions,
112 SkPackedGlyphID* results) {
113
114 SkMatrix::MakeTrans(origin.x(), origin.y()).mapPoints(
115 mappedPositions, positions, n);
116
117 SkPackedGlyphID* cursor = results;
118 for (int i = 0; i < n; i++) {
119 *cursor++ = SkPackedGlyphID{glyphIDs[i]};
120 }
121
122 return SkSpan<const SkPackedGlyphID>{results, SkTo<size_t>(n)};
123 }
124
drawForBitmapDevice(const SkGlyphRunList & glyphRunList,const SkMatrix & deviceMatrix,const BitmapDevicePainter * bitmapDevice)125 void SkGlyphRunListPainter::drawForBitmapDevice(
126 const SkGlyphRunList& glyphRunList, const SkMatrix& deviceMatrix,
127 const BitmapDevicePainter* bitmapDevice) {
128 ScopedBuffers _ = this->ensureBuffers(glyphRunList);
129
130 const SkPaint& runPaint = glyphRunList.paint();
131 // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
132 // convert the lcd text into A8 text. The props communicates this to the scaler.
133 auto& props = (kN32_SkColorType == fColorType && runPaint.isSrcOver())
134 ? fDeviceProps
135 : fBitmapFallbackProps;
136
137 SkPoint origin = glyphRunList.origin();
138 for (auto& glyphRun : glyphRunList) {
139 const SkFont& runFont = glyphRun.font();
140
141 if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, deviceMatrix)) {
142
143 SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
144 runFont, runPaint, props, fScalerContextFlags);
145
146 auto strike = strikeSpec.findOrCreateExclusiveStrike();
147
148 fDrawable.startSource(glyphRun.source(), origin);
149 strike->prepareForDrawingPathsCPU(&fDrawable);
150
151 // The paint we draw paths with must have the same anti-aliasing state as the runFont
152 // allowing the paths to have the same edging as the glyph masks.
153 SkPaint pathPaint = runPaint;
154 pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
155
156 bitmapDevice->paintPaths(&fDrawable, strikeSpec.strikeToSourceRatio(), pathPaint);
157 } else {
158 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
159 runFont, runPaint, props, fScalerContextFlags, deviceMatrix);
160
161 auto strike = strikeSpec.findOrCreateExclusiveStrike();
162
163 fDrawable.startDevice(glyphRun.source(), origin, deviceMatrix, strike->roundingSpec());
164 strike->prepareForDrawingMasksCPU(&fDrawable);
165 bitmapDevice->paintMasks(&fDrawable, runPaint);
166 }
167 }
168 }
169
170 // Getting glyphs to the screen in a fallback situation can be complex. Here is the set of
171 // transformations that have to happen. Normally, they would all be accommodated by the font
172 // scaler, but the atlas has an upper limit to the glyphs it can handle. So the GPU is used to
173 // make up the difference from the smaller atlas size to the larger size needed by the final
174 // transform. Here are the transformations that are applied.
175 //
176 // final transform = [view matrix] * [text scale] * [text size]
177 //
178 // There are three cases:
179 // * Go Fast - view matrix is scale and translate, and all the glyphs are small enough
180 // Just scale the positions, and have the glyph cache handle the view matrix transformation.
181 // The text scale is 1.
182 // * It's complicated - view matrix is not scale and translate, and the glyphs are small enough
183 // The glyph cache does not handle the view matrix, but stores the glyphs at the text size
184 // specified by the run paint. The GPU handles the rotation, etc. specified by the view matrix.
185 // The text scale is 1.
186 // * Too big - The glyphs are too big to fit in the atlas
187 // Reduce the text size so the glyphs will fit in the atlas, but don't apply any
188 // transformations from the view matrix. Calculate a text scale based on that reduction. This
189 // scale factor is used to increase the size of the destination rectangles. The destination
190 // rectangles are then scaled, rotated, etc. by the GPU using the view matrix.
processARGBFallback(SkScalar maxSourceGlyphDimension,const SkPaint & runPaint,const SkFont & runFont,const SkMatrix & viewMatrix,SkGlyphRunPainterInterface * process)191 void SkGlyphRunListPainter::processARGBFallback(SkScalar maxSourceGlyphDimension,
192 const SkPaint& runPaint,
193 const SkFont& runFont,
194 const SkMatrix& viewMatrix,
195 SkGlyphRunPainterInterface* process) {
196 SkASSERT(!fARGBGlyphsIDs.empty());
197
198 // if maxSourceGlyphDimension then no pixels will change.
199 if (maxSourceGlyphDimension == 0) { return; }
200
201 SkScalar maxScale = viewMatrix.getMaxScale();
202
203 // This is a linear estimate of the longest dimension among all the glyph widths and heights.
204 SkScalar conservativeMaxGlyphDimension = maxSourceGlyphDimension * maxScale;
205
206 // If the situation that the matrix is simple, and all the glyphs are small enough. Go fast!
207 // N.B. If the matrix has scale, that will be reflected in the strike through the viewMatrix
208 // in the useFastPath case.
209 bool useDeviceCache =
210 viewMatrix.isScaleTranslate()
211 && conservativeMaxGlyphDimension <= SkStrikeCommon::kSkSideTooBigForAtlas;
212
213 // A scaled and translated transform is the common case, and is handled directly in fallback.
214 // Even if the transform is scale and translate, fallback must be careful to use glyphs that
215 // fit in the atlas. If a glyph will not fit in the atlas, then the general transform case is
216 // used to render the glyphs.
217 if (useDeviceCache) {
218 // Translate the positions to device space.
219 // TODO: this code is dubious
220 viewMatrix.mapPoints(fARGBPositions.data(), fARGBPositions.size());
221 for (SkPoint& point : fARGBPositions) {
222 point.fX = SkScalarFloorToScalar(point.fX);
223 point.fY = SkScalarFloorToScalar(point.fY);
224 }
225
226 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
227 runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix);
228
229 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
230
231 SkPackedGlyphID* cursor = fPackedGlyphIDs;
232 for (auto glyphID : fARGBGlyphsIDs) {
233 *cursor++ = SkPackedGlyphID{glyphID};
234 }
235
236 SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
237 fPackedGlyphIDs,
238 fARGBPositions.data(),
239 fARGBGlyphsIDs.size(),
240 SkStrikeCommon::kSkSideTooBigForAtlas,
241 fGlyphPos);
242
243 if (process) {
244 process->processDeviceFallback(glyphPosSpan, strikeSpec);
245 }
246
247 } else {
248 // If the matrix is complicated or if scaling is used to fit the glyphs in the cache,
249 // then this case is used.
250
251 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeSourceFallback(
252 runFont, runPaint, fDeviceProps, fScalerContextFlags, maxSourceGlyphDimension);
253
254 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
255
256 SkPackedGlyphID* cursor = fPackedGlyphIDs;
257 for (auto glyphID : fARGBGlyphsIDs) {
258 *cursor++ = SkPackedGlyphID{glyphID};
259 }
260
261 auto glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
262 fPackedGlyphIDs,
263 fARGBPositions.data(),
264 fARGBGlyphsIDs.size(),
265 SkStrikeCommon::kSkSideTooBigForAtlas,
266 fGlyphPos);
267
268 if (process) {
269 process->processSourceFallback(
270 glyphPosSpan,
271 strikeSpec,
272 viewMatrix.hasPerspective());
273 }
274 }
275 }
276
277 #if SK_SUPPORT_GPU
processGlyphRunList(const SkGlyphRunList & glyphRunList,const SkMatrix & viewMatrix,const SkSurfaceProps & props,bool contextSupportsDistanceFieldText,const GrTextContext::Options & options,SkGlyphRunPainterInterface * process)278 void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunList,
279 const SkMatrix& viewMatrix,
280 const SkSurfaceProps& props,
281 bool contextSupportsDistanceFieldText,
282 const GrTextContext::Options& options,
283 SkGlyphRunPainterInterface* process) {
284
285 SkPoint origin = glyphRunList.origin();
286 const SkPaint& runPaint = glyphRunList.paint();
287
288 for (const auto& glyphRun : glyphRunList) {
289 SkScalar maxFallbackDimension{-SK_ScalarInfinity};
290 ScopedBuffers _ = this->ensureBuffers(glyphRun);
291
292 auto addFallback = [this, &maxFallbackDimension]
293 (const SkGlyph& glyph, SkPoint sourcePosition) {
294 maxFallbackDimension = std::max(maxFallbackDimension,
295 SkIntToScalar(glyph.maxDimension()));
296 fARGBGlyphsIDs.push_back(glyph.getGlyphID());
297 fARGBPositions.push_back(sourcePosition);
298 };
299
300 const SkFont& runFont = glyphRun.font();
301
302 bool useSDFT = GrTextContext::CanDrawAsDistanceFields(
303 runPaint, runFont, viewMatrix, props, contextSupportsDistanceFieldText, options);
304 if (process) {
305 process->startRun(glyphRun, useSDFT);
306 }
307
308 if (useSDFT) {
309 SkScalar minScale, maxScale;
310 SkStrikeSpec strikeSpec;
311 std::tie(strikeSpec, minScale, maxScale) =
312 SkStrikeSpec::MakeSDFT(
313 runFont, runPaint,fDeviceProps, viewMatrix, options);
314
315 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
316
317 auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
318 origin,
319 glyphRun.runSize(),
320 glyphRun.glyphsIDs().data(),
321 glyphRun.positions().data(),
322 fPositions,
323 fPackedGlyphIDs);
324
325 SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
326 packedGlyphIDs.data(),
327 fPositions,
328 glyphRun.runSize(),
329 SkStrikeCommon::kSkSideTooBigForAtlas,
330 fGlyphPos);
331
332 size_t glyphsWithMaskCount = 0;
333 for (const SkGlyphPos& glyphPos : glyphPosSpan) {
334 const SkGlyph& glyph = *glyphPos.glyph;
335 SkPoint position = glyphPos.position;
336
337 // The SDF scaler context system ensures that a glyph is empty, kSDF_Format, or
338 // kARGB32_Format. The following if statements use this assumption.
339 SkASSERT(glyph.maskFormat() == SkMask::kSDF_Format || glyph.isColor());
340
341 if (SkStrikeForGPU::CanDrawAsSDFT(glyph)) {
342 // SDF mask will work.
343 fGlyphPos[glyphsWithMaskCount++] = glyphPos;
344 } else if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
345 // If not color but too big, use a path.
346 fPaths.push_back(glyphPos);
347 } else {
348 // If no path, or it is color, then fallback.
349 addFallback(glyph, position);
350 }
351 }
352
353 if (process) {
354 bool hasWCoord =
355 viewMatrix.hasPerspective() || options.fDistanceFieldVerticesAlwaysHaveW;
356
357 // processSourceSDFT must be called even if there are no glyphs to make sure runs
358 // are set correctly.
359 process->processSourceSDFT(
360 SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithMaskCount},
361 strikeSpec,
362 runFont,
363 minScale,
364 maxScale,
365 hasWCoord);
366
367 if (!fPaths.empty()) {
368 process->processSourcePaths(
369 SkMakeSpan(fPaths),
370 strikeSpec);
371 }
372 }
373
374 // fGlyphPos will be reused here.
375 if (!fARGBGlyphsIDs.empty()) {
376 this->processARGBFallback(maxFallbackDimension * strikeSpec.strikeToSourceRatio(),
377 runPaint, runFont, viewMatrix, process);
378 }
379 } else if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, viewMatrix)) {
380 SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
381 runFont, runPaint, fDeviceProps, fScalerContextFlags);
382
383 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
384
385 auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
386 origin,
387 glyphRun.runSize(),
388 glyphRun.glyphsIDs().data(),
389 glyphRun.positions().data(),
390 fPositions,
391 fPackedGlyphIDs);
392
393 SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
394 packedGlyphIDs.data(),
395 fPositions,
396 glyphRun.runSize(),
397 0,
398 fGlyphPos);
399
400 // As opposed to SDF and mask, path handling puts paths in fGlyphPos instead of fPaths.
401 size_t glyphsWithPathCount = 0;
402 for (const SkGlyphPos& glyphPos : glyphPosSpan) {
403 const SkGlyph& glyph = *glyphPos.glyph;
404 SkPoint position = glyphPos.position;
405 if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
406 // Place paths in fGlyphPos
407 fGlyphPos[glyphsWithPathCount++] = glyphPos;
408 } else {
409 addFallback(glyph, position);
410 }
411 }
412
413 if (process) {
414 // processSourcePaths must be called even if there are no glyphs to make sure runs
415 // are set correctly.
416 process->processSourcePaths(
417 SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithPathCount},
418 strikeSpec);
419 }
420
421 // fGlyphPos will be reused here.
422 if (!fARGBGlyphsIDs.empty()) {
423 this->processARGBFallback(maxFallbackDimension * strikeSpec.strikeToSourceRatio(),
424 runPaint, runFont, viewMatrix, process);
425 }
426 } else {
427 SkStrikeSpec strikeSpec =
428 SkStrikeSpec::MakeMask(runFont, runPaint,
429 fDeviceProps, fScalerContextFlags, viewMatrix);
430
431 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
432
433 auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
434 strike->roundingSpec(),
435 viewMatrix,
436 origin,
437 glyphRun.runSize(),
438 glyphRun.glyphsIDs().data(),
439 glyphRun.positions().data(),
440 fPositions,
441 fPackedGlyphIDs);
442
443 // Lookup all the glyphs from the cache. Strip empty glyphs.
444 SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
445 packedGlyphIDs.data(),
446 fPositions,
447 glyphRun.runSize(),
448 SkStrikeCommon::kSkSideTooBigForAtlas,
449 fGlyphPos);
450
451 // Sort glyphs into the three bins: mask (fGlyphPos), path (fPaths), and fallback.
452 size_t glyphsWithMaskCount = 0;
453 for (const SkGlyphPos& glyphPos : glyphPosSpan) {
454 const SkGlyph& glyph = *glyphPos.glyph;
455 const SkPoint position = glyphPos.position;
456
457 // Does the glyph have work to do or is the code able to position the glyph?
458 if (!SkScalarsAreFinite(position.x(), position.y())) {
459 // Do nothing;
460 } else if (SkStrikeForGPU::CanDrawAsMask(glyph)) {
461 fGlyphPos[glyphsWithMaskCount++] = glyphPos;
462 } else if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
463 fPaths.push_back(glyphPos);
464 } else {
465 addFallback(glyph, origin + glyphRun.positions()[glyphPos.index]);
466 }
467 }
468
469 if (process) {
470 // processDeviceMasks must be called even if there are no glyphs to make sure runs
471 // are set correctly.
472 process->processDeviceMasks(
473 SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithMaskCount}, strikeSpec);
474 if (!fPaths.empty()) {
475 process->processDevicePaths(SkMakeSpan(fPaths));
476 }
477 }
478
479 // fGlyphPos will be reused here.
480 if (!fARGBGlyphsIDs.empty()) {
481 this->processARGBFallback(maxFallbackDimension / viewMatrix.getMaxScale(),
482 runPaint, runFont, viewMatrix, process);
483 }
484 } // Mask case
485 } // For all glyph runs
486 }
487 #endif // SK_SUPPORT_GPU
488
ensureBuffers(const SkGlyphRunList & glyphRunList)489 auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) -> ScopedBuffers {
490 size_t size = 0;
491 for (const SkGlyphRun& run : glyphRunList) {
492 size = std::max(run.runSize(), size);
493 }
494 return ScopedBuffers(this, size);
495 }
496
497 SkGlyphRunListPainter::ScopedBuffers
ensureBuffers(const SkGlyphRun & glyphRun)498 SkGlyphRunListPainter::ensureBuffers(const SkGlyphRun& glyphRun) {
499 return ScopedBuffers(this, glyphRun.runSize());
500 }
501
502 #if SK_SUPPORT_GPU
503 // -- GrTextContext --------------------------------------------------------------------------------
generate_filtered_color(const SkPaint & paint,const GrColorInfo & colorInfo)504 SkPMColor4f generate_filtered_color(const SkPaint& paint, const GrColorInfo& colorInfo) {
505 SkColor4f filteredColor = paint.getColor4f();
506 if (auto* xform = colorInfo.colorSpaceXformFromSRGB()) {
507 filteredColor = xform->apply(filteredColor);
508 }
509 if (paint.getColorFilter() != nullptr) {
510 filteredColor = paint.getColorFilter()->filterColor4f(filteredColor, colorInfo.colorSpace(),
511 colorInfo.colorSpace());
512 }
513 return filteredColor.premul();
514 }
515
drawGlyphRunList(GrRecordingContext * context,GrTextTarget * target,const GrClip & clip,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkGlyphRunList & glyphRunList)516 void GrTextContext::drawGlyphRunList(
517 GrRecordingContext* context, GrTextTarget* target, const GrClip& clip,
518 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
519 const SkGlyphRunList& glyphRunList) {
520 SkPoint origin = glyphRunList.origin();
521
522 // Get the first paint to use as the key paint.
523 const SkPaint& listPaint = glyphRunList.paint();
524
525 SkPMColor4f filteredColor = generate_filtered_color(listPaint, target->colorInfo());
526 GrColor color = generate_filtered_color(listPaint, target->colorInfo()).toBytes_RGBA();
527
528 // If we have been abandoned, then don't draw
529 if (context->priv().abandoned()) {
530 return;
531 }
532
533 SkMaskFilterBase::BlurRec blurRec;
534 // It might be worth caching these things, but its not clear at this time
535 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
536 const SkMaskFilter* mf = listPaint.getMaskFilter();
537 bool canCache = glyphRunList.canCache() && !(listPaint.getPathEffect() ||
538 (mf && !as_MFB(mf)->asABlur(&blurRec)));
539 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorInfo());
540
541 auto grStrikeCache = context->priv().getGrStrikeCache();
542 GrTextBlobCache* textBlobCache = context->priv().getTextBlobCache();
543
544 sk_sp<GrTextBlob> cacheBlob;
545 GrTextBlob::Key key;
546 if (canCache) {
547 bool hasLCD = glyphRunList.anyRunsLCD();
548
549 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
550 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
551 kUnknown_SkPixelGeometry;
552
553 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
554 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
555 // ensure we always match the same key
556 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
557 ComputeCanonicalColor(listPaint, hasLCD);
558
559 key.fPixelGeometry = pixelGeometry;
560 key.fUniqueID = glyphRunList.uniqueID();
561 key.fStyle = listPaint.getStyle();
562 key.fHasBlur = SkToBool(mf);
563 key.fCanonicalColor = canonicalColor;
564 key.fScalerContextFlags = scalerContextFlags;
565 cacheBlob = textBlobCache->find(key);
566 }
567
568 if (cacheBlob) {
569 if (cacheBlob->mustRegenerate(listPaint, glyphRunList.anyRunsSubpixelPositioned(),
570 blurRec, viewMatrix, origin.x(),origin.y())) {
571 // We have to remake the blob because changes may invalidate our masks.
572 // TODO we could probably get away reuse most of the time if the pointer is unique,
573 // but we'd have to clear the subrun information
574 textBlobCache->remove(cacheBlob.get());
575 cacheBlob = textBlobCache->makeCachedBlob(
576 glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
577 cacheBlob->generateFromGlyphRunList(
578 *context->priv().caps()->shaderCaps(), fOptions,
579 listPaint, scalerContextFlags, viewMatrix, props,
580 glyphRunList, target->glyphPainter());
581 } else {
582 textBlobCache->makeMRU(cacheBlob.get());
583
584 if (CACHE_SANITY_CHECK) {
585 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(
586 glyphRunList, color, grStrikeCache));
587 sanityBlob->setupKey(key, blurRec, listPaint);
588 cacheBlob->generateFromGlyphRunList(
589 *context->priv().caps()->shaderCaps(), fOptions,
590 listPaint, scalerContextFlags, viewMatrix, props, glyphRunList,
591 target->glyphPainter());
592 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
593 }
594 }
595 } else {
596 if (canCache) {
597 cacheBlob = textBlobCache->makeCachedBlob(
598 glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
599 } else {
600 cacheBlob = textBlobCache->makeBlob(glyphRunList, color, grStrikeCache);
601 }
602 cacheBlob->generateFromGlyphRunList(
603 *context->priv().caps()->shaderCaps(), fOptions, listPaint,
604 scalerContextFlags, viewMatrix, props, glyphRunList,
605 target->glyphPainter());
606 }
607
608 cacheBlob->flush(target, props, fDistanceAdjustTable.get(), listPaint, filteredColor,
609 clip, viewMatrix, origin.x(), origin.y());
610 }
611
appendGlyph(GrGlyph * glyph,SkRect dstRect)612 void GrTextBlob::SubRun::appendGlyph(GrGlyph* glyph, SkRect dstRect) {
613
614 this->joinGlyphBounds(dstRect);
615
616 GrTextBlob* blob = fRun->fBlob;
617
618 bool hasW = this->hasWCoord();
619 // glyphs drawn in perspective must always have a w coord.
620 SkASSERT(hasW || !blob->fInitialViewMatrix.hasPerspective());
621 auto maskFormat = this->maskFormat();
622 size_t vertexStride = GetVertexStride(maskFormat, hasW);
623
624 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + fVertexEndIndex);
625
626 // We always write the third position component used by SDFs. If it is unused it gets
627 // overwritten. Similarly, we always write the color and the blob will later overwrite it
628 // with texture coords if it is unused.
629 size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
630 // V0
631 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
632 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
633 vertex += vertexStride;
634
635 // V1
636 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
637 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
638 vertex += vertexStride;
639
640 // V2
641 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
642 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
643 vertex += vertexStride;
644
645 // V3
646 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
647 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
648
649 fVertexEndIndex += vertexStride * kVerticesPerGlyph;
650 blob->fGlyphs[fGlyphEndIndex++] = glyph;
651 }
652
switchSubRunIfNeededAndAppendGlyph(GrGlyph * glyph,const sk_sp<GrTextStrike> & strike,const SkRect & destRect,bool needsTransform)653 void GrTextBlob::Run::switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
654 const sk_sp<GrTextStrike>& strike,
655 const SkRect& destRect,
656 bool needsTransform) {
657 GrMaskFormat format = glyph->fMaskFormat;
658
659 SubRun* subRun = &fSubRunInfo.back();
660 if (fInitialized && subRun->maskFormat() != format) {
661 subRun = pushBackSubRun(fStrikeSpec, fColor);
662 subRun->setStrike(strike);
663 } else if (!fInitialized) {
664 subRun->setStrike(strike);
665 }
666
667 fInitialized = true;
668 subRun->setMaskFormat(format);
669 subRun->setNeedsTransform(needsTransform);
670 subRun->appendGlyph(glyph, destRect);
671 }
672
appendDeviceSpaceGlyph(const sk_sp<GrTextStrike> & strike,const SkGlyph & skGlyph,SkPoint origin)673 void GrTextBlob::Run::appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
674 const SkGlyph& skGlyph, SkPoint origin) {
675 if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
676
677 SkRect glyphRect = glyph->destRect(origin);
678
679 if (!glyphRect.isEmpty()) {
680 this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, false);
681 }
682 }
683 }
684
appendSourceSpaceGlyph(const sk_sp<GrTextStrike> & strike,const SkGlyph & skGlyph,SkPoint origin,SkScalar textScale)685 void GrTextBlob::Run::appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
686 const SkGlyph& skGlyph,
687 SkPoint origin,
688 SkScalar textScale) {
689 if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
690
691 SkRect glyphRect = glyph->destRect(origin, textScale);
692
693 if (!glyphRect.isEmpty()) {
694 this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, true);
695 }
696 }
697 }
698
generateFromGlyphRunList(const GrShaderCaps & shaderCaps,const GrTextContext::Options & options,const SkPaint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkGlyphRunList & glyphRunList,SkGlyphRunListPainter * glyphPainter)699 void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
700 const GrTextContext::Options& options,
701 const SkPaint& paint,
702 SkScalerContextFlags scalerContextFlags,
703 const SkMatrix& viewMatrix,
704 const SkSurfaceProps& props,
705 const SkGlyphRunList& glyphRunList,
706 SkGlyphRunListPainter* glyphPainter) {
707 SkPoint origin = glyphRunList.origin();
708 const SkPaint& runPaint = glyphRunList.paint();
709 this->initReusableBlob(SkPaintPriv::ComputeLuminanceColor(runPaint), viewMatrix,
710 origin.x(), origin.y());
711
712 glyphPainter->processGlyphRunList(glyphRunList,
713 viewMatrix,
714 props,
715 shaderCaps.supportsDistanceFieldText(),
716 options,
717 this);
718 }
719
currentRun()720 GrTextBlob::Run* GrTextBlob::currentRun() {
721 return &fRuns[fRunCount - 1];
722 }
723
startRun(const SkGlyphRun & glyphRun,bool useSDFT)724 void GrTextBlob::startRun(const SkGlyphRun& glyphRun, bool useSDFT) {
725 if (useSDFT) {
726 this->setHasDistanceField();
727 }
728 Run* run = this->pushBackRun();
729 run->setRunFontAntiAlias(glyphRun.font().hasSomeAntiAliasing());
730 }
731
processDeviceMasks(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec)732 void GrTextBlob::processDeviceMasks(SkSpan<const SkGlyphPos> masks,
733 const SkStrikeSpec& strikeSpec) {
734 Run* run = this->currentRun();
735 this->setHasBitmap();
736 run->setupFont(strikeSpec);
737 sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
738 for (const auto& mask : masks) {
739 SkPoint pt{SkScalarFloorToScalar(mask.position.fX),
740 SkScalarFloorToScalar(mask.position.fY)};
741 run->appendDeviceSpaceGlyph(currStrike, *mask.glyph, pt);
742 }
743 }
744
processSourcePaths(SkSpan<const SkGlyphPos> paths,const SkStrikeSpec & strikeSpec)745 void GrTextBlob::processSourcePaths(SkSpan<const SkGlyphPos> paths,
746 const SkStrikeSpec& strikeSpec) {
747 Run* run = this->currentRun();
748 this->setHasBitmap();
749 run->setupFont(strikeSpec);
750 for (const auto& path : paths) {
751 if (const SkPath* glyphPath = path.glyph->path()) {
752 run->appendPathGlyph(*glyphPath, path.position, strikeSpec.strikeToSourceRatio(),
753 false);
754 }
755 }
756 }
757
processDevicePaths(SkSpan<const SkGlyphPos> paths)758 void GrTextBlob::processDevicePaths(SkSpan<const SkGlyphPos> paths) {
759 Run* run = this->currentRun();
760 this->setHasBitmap();
761 for (const auto& path : paths) {
762 SkPoint pt{SkScalarFloorToScalar(path.position.fX),
763 SkScalarFloorToScalar(path.position.fY)};
764 // TODO: path should always be set. Remove when proven.
765 if (const SkPath* glyphPath = path.glyph->path()) {
766 run->appendPathGlyph(*glyphPath, pt, SK_Scalar1, true);
767 }
768 }
769 }
770
processSourceSDFT(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec,const SkFont & runFont,SkScalar minScale,SkScalar maxScale,bool hasWCoord)771 void GrTextBlob::processSourceSDFT(SkSpan<const SkGlyphPos> masks,
772 const SkStrikeSpec& strikeSpec,
773 const SkFont& runFont,
774 SkScalar minScale,
775 SkScalar maxScale,
776 bool hasWCoord) {
777
778 Run* run = this->currentRun();
779 run->setSubRunHasDistanceFields(
780 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
781 runFont.hasSomeAntiAliasing(),
782 hasWCoord);
783 this->setMinAndMaxScale(minScale, maxScale);
784 run->setupFont(strikeSpec);
785 sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
786 for (const auto& mask : masks) {
787 run->appendSourceSpaceGlyph(
788 currStrike, *mask.glyph, mask.position, strikeSpec.strikeToSourceRatio());
789 }
790 }
791
processSourceFallback(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec,bool hasW)792 void GrTextBlob::processSourceFallback(SkSpan<const SkGlyphPos> masks,
793 const SkStrikeSpec& strikeSpec,
794 bool hasW) {
795 Run* run = this->currentRun();
796
797 auto subRun = run->initARGBFallback();
798 sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
799 subRun->setStrike(grStrike);
800 subRun->setHasWCoord(hasW);
801
802 this->setHasBitmap();
803 run->setupFont(strikeSpec);
804 for (const auto& mask : masks) {
805 run->appendSourceSpaceGlyph
806 (grStrike, *mask.glyph, mask.position, strikeSpec.strikeToSourceRatio());
807 }
808 }
809
processDeviceFallback(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec)810 void GrTextBlob::processDeviceFallback(SkSpan<const SkGlyphPos> masks,
811 const SkStrikeSpec& strikeSpec) {
812 Run* run = this->currentRun();
813 this->setHasBitmap();
814 sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
815 auto subRun = run->initARGBFallback();
816 run->setupFont(strikeSpec);
817 subRun->setStrike(grStrike);
818 for (const auto& mask : masks) {
819 run->appendDeviceSpaceGlyph(grStrike, *mask.glyph, mask.position);
820 }
821 }
822
823 #if GR_TEST_UTILS
824
825 #include "src/gpu/GrRecordingContextPriv.h"
826 #include "src/gpu/GrRenderTargetContext.h"
827
createOp_TestingOnly(GrRecordingContext * context,GrTextContext * textContext,GrRenderTargetContext * rtc,const SkPaint & skPaint,const SkFont & font,const SkMatrix & viewMatrix,const char * text,int x,int y)828 std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrRecordingContext* context,
829 GrTextContext* textContext,
830 GrRenderTargetContext* rtc,
831 const SkPaint& skPaint,
832 const SkFont& font,
833 const SkMatrix& viewMatrix,
834 const char* text,
835 int x,
836 int y) {
837 auto direct = context->priv().asDirectContext();
838 if (!direct) {
839 return nullptr;
840 }
841
842 auto strikeCache = direct->priv().getGrStrikeCache();
843
844 static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
845
846 size_t textLen = (int)strlen(text);
847
848 SkPMColor4f filteredColor = generate_filtered_color(skPaint, rtc->colorInfo());
849 GrColor color = filteredColor.toBytes_RGBA();
850
851 auto origin = SkPoint::Make(x, y);
852 SkGlyphRunBuilder builder;
853 builder.drawTextUTF8(skPaint, font, text, textLen, origin);
854
855 auto glyphRunList = builder.useGlyphRunList();
856 sk_sp<GrTextBlob> blob;
857 if (!glyphRunList.empty()) {
858 blob = direct->priv().getTextBlobCache()->makeBlob(glyphRunList, color, strikeCache);
859 // Use the text and textLen below, because we don't want to mess with the paint.
860 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(rtc->colorInfo());
861 blob->generateFromGlyphRunList(
862 *context->priv().caps()->shaderCaps(), textContext->fOptions,
863 skPaint, scalerContextFlags, viewMatrix, surfaceProps,
864 glyphRunList, rtc->textTarget()->glyphPainter());
865 }
866
867 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, skPaint, filteredColor, surfaceProps,
868 textContext->dfAdjustTable(), rtc->textTarget());
869 }
870
871 #endif // GR_TEST_UTILS
872 #endif // SK_SUPPORT_GPU
873
ScopedBuffers(SkGlyphRunListPainter * painter,size_t size)874 SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, size_t size)
875 : fPainter{painter} {
876 fPainter->fDrawable.ensureSize(size);
877 if (fPainter->fMaxRunSize < size) {
878 fPainter->fMaxRunSize = size;
879
880 fPainter->fPositions.reset(size);
881 fPainter->fPackedGlyphIDs.reset(size);
882 fPainter->fGlyphPos.reset(size);
883 }
884 }
885
~ScopedBuffers()886 SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
887 fPainter->fDrawable.reset();
888 fPainter->fPaths.clear();
889 fPainter->fARGBGlyphsIDs.clear();
890 fPainter->fARGBPositions.clear();
891
892 if (fPainter->fMaxRunSize > 200) {
893 fPainter->fMaxRunSize = 0;
894 fPainter->fPositions.reset();
895 fPainter->fPackedGlyphIDs.reset();
896 fPainter->fGlyphPos.reset();
897 fPainter->fPaths.shrink_to_fit();
898 fPainter->fARGBGlyphsIDs.shrink_to_fit();
899 fPainter->fARGBPositions.shrink_to_fit();
900 }
901 }
902
HalfAxisSampleFreq(bool isSubpixel,SkAxisAlignment axisAlignment)903 SkVector SkGlyphPositionRoundingSpec::HalfAxisSampleFreq(bool isSubpixel, SkAxisAlignment axisAlignment) {
904 if (!isSubpixel) {
905 return {SK_ScalarHalf, SK_ScalarHalf};
906 } else {
907 static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
908 switch (axisAlignment) {
909 case kX_SkAxisAlignment:
910 return {kSubpixelRounding, SK_ScalarHalf};
911 case kY_SkAxisAlignment:
912 return {SK_ScalarHalf, kSubpixelRounding};
913 case kNone_SkAxisAlignment:
914 return {kSubpixelRounding, kSubpixelRounding};
915 }
916 }
917
918 // Some compilers need this.
919 return {0, 0};
920 }
921
IgnorePositionMask(bool isSubpixel,SkAxisAlignment axisAlignment)922 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionMask(
923 bool isSubpixel, SkAxisAlignment axisAlignment) {
924 return SkIPoint::Make((!isSubpixel || axisAlignment == kY_SkAxisAlignment) ? 0 : ~0,
925 (!isSubpixel || axisAlignment == kX_SkAxisAlignment) ? 0 : ~0);
926 }
927
SkGlyphPositionRoundingSpec(bool isSubpixel,SkAxisAlignment axisAlignment)928 SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(bool isSubpixel,
929 SkAxisAlignment axisAlignment)
930 : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
931 , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)} {
932 }
933