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/gpu/GrRecordingContext.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrColorInfo.h"
14 #include "src/gpu/GrDirectContextPriv.h"
15 #include "src/gpu/GrRecordingContextPriv.h"
16 #include "src/gpu/SkGr.h"
17 #include "src/gpu/text/GrSDFTControl.h"
18 #include "src/gpu/text/GrTextBlobCache.h"
19 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
20 #endif // SK_SUPPORT_GPU
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/SkEnumerate.h"
30 #include "src/core/SkFontPriv.h"
31 #include "src/core/SkRasterClip.h"
32 #include "src/core/SkScalerCache.h"
33 #include "src/core/SkStrikeCache.h"
34 #include "src/core/SkStrikeForGPU.h"
35 #include "src/core/SkStrikeSpec.h"
36 #include "src/core/SkTraceEvent.h"
37
38 #include <climits>
39
40 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkScalerContextFlags flags,SkStrikeForGPUCacheInterface * strikeCache)41 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
42 SkColorType colorType,
43 SkScalerContextFlags flags,
44 SkStrikeForGPUCacheInterface* strikeCache)
45 : fDeviceProps{props}
46 , fBitmapFallbackProps{SkSurfaceProps{props.flags(), kUnknown_SkPixelGeometry}}
47 , fColorType{colorType}, fScalerContextFlags{flags}
48 , fStrikeCache{strikeCache} {}
49
50 // TODO: unify with code in GrSDFTControl.cpp
compute_scaler_context_flags(const SkColorSpace * cs)51 static SkScalerContextFlags compute_scaler_context_flags(const SkColorSpace* cs) {
52 // If we're doing linear blending, then we can disable the gamma hacks.
53 // Otherwise, leave them on. In either case, we still want the contrast boost:
54 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
55 if (cs && cs->gammaIsLinear()) {
56 return SkScalerContextFlags::kBoostContrast;
57 } else {
58 return SkScalerContextFlags::kFakeGammaAndBoostContrast;
59 }
60 }
61
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkColorSpace * cs,SkStrikeForGPUCacheInterface * strikeCache)62 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
63 SkColorType colorType,
64 SkColorSpace* cs,
65 SkStrikeForGPUCacheInterface* strikeCache)
66 : SkGlyphRunListPainter(props, colorType, compute_scaler_context_flags(cs), strikeCache) {}
67
68 #if SK_SUPPORT_GPU
SkGlyphRunListPainter(const SkSurfaceProps & props,const GrColorInfo & csi)69 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props, const GrColorInfo& csi)
70 : SkGlyphRunListPainter(props,
71 kUnknown_SkColorType,
72 compute_scaler_context_flags(csi.colorSpace()),
73 SkStrikeCache::GlobalStrikeCache()) {}
74
SkGlyphRunListPainter(const skgpu::v1::SurfaceDrawContext & sdc)75 SkGlyphRunListPainter::SkGlyphRunListPainter(const skgpu::v1::SurfaceDrawContext& sdc)
76 : SkGlyphRunListPainter{sdc.surfaceProps(), sdc.colorInfo()} {}
77
78 #endif // SK_SUPPORT_GPU
79
drawForBitmapDevice(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkMatrix & deviceMatrix,const BitmapDevicePainter * bitmapDevice)80 void SkGlyphRunListPainter::drawForBitmapDevice(
81 const SkGlyphRunList& glyphRunList, const SkPaint& paint, const SkMatrix& deviceMatrix,
82 const BitmapDevicePainter* bitmapDevice) {
83 ScopedBuffers _ = this->ensureBuffers(glyphRunList);
84
85 // TODO: fStrikeCache is only used for GPU, and some compilers complain about it during the no
86 // gpu build. Remove when SkGlyphRunListPainter is split into GPU and CPU version.
87 (void)fStrikeCache;
88
89 // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
90 // convert the lcd text into A8 text. The props communicates this to the scaler.
91 auto& props = (kN32_SkColorType == fColorType && paint.isSrcOver())
92 ? fDeviceProps
93 : fBitmapFallbackProps;
94
95 SkPoint drawOrigin = glyphRunList.origin();
96 for (auto& glyphRun : glyphRunList) {
97 const SkFont& runFont = glyphRun.font();
98
99 fRejects.setSource(glyphRun.source());
100
101 if (SkStrikeSpec::ShouldDrawAsPath(paint, runFont, deviceMatrix)) {
102
103 auto [strikeSpec, strikeToSourceScale] =
104 SkStrikeSpec::MakePath(runFont, paint, props, fScalerContextFlags);
105
106 auto strike = strikeSpec.findOrCreateStrike();
107
108 fDrawable.startSource(fRejects.source());
109 strike->prepareForPathDrawing(&fDrawable, &fRejects);
110 fRejects.flipRejectsToSource();
111
112 // The paint we draw paths with must have the same anti-aliasing state as the runFont
113 // allowing the paths to have the same edging as the glyph masks.
114 SkPaint pathPaint = paint;
115 pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
116
117 bitmapDevice->paintPaths(
118 &fDrawable, strikeToSourceScale, drawOrigin, pathPaint);
119 }
120 if (!fRejects.source().empty() && !deviceMatrix.hasPerspective()) {
121 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
122 runFont, paint, props, fScalerContextFlags, deviceMatrix);
123
124 auto strike = strikeSpec.findOrCreateStrike();
125
126 fDrawable.startBitmapDevice(
127 fRejects.source(), drawOrigin, deviceMatrix, strike->roundingSpec());
128 strike->prepareForDrawingMasksCPU(&fDrawable);
129 fRejects.flipRejectsToSource();
130 bitmapDevice->paintMasks(&fDrawable, paint);
131 }
132 if (!fRejects.source().empty()) {
133 SkMatrix runMatrix = deviceMatrix;
134 runMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
135 std::vector<SkPoint> sourcePositions;
136
137 // Create a strike is source space to calculate scale information.
138 SkStrikeSpec scaleStrikeSpec = SkStrikeSpec::MakeMask(
139 runFont, paint, props, fScalerContextFlags, SkMatrix::I());
140 SkBulkGlyphMetrics metrics{scaleStrikeSpec};
141
142 auto glyphIDs = fRejects.source().get<0>();
143 auto positions = fRejects.source().get<1>();
144 SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
145 SkScalar maxScale = SK_ScalarMin;
146
147 // Calculate the scale that makes the longest edge 1:1 with its side in the cache.
148 for (auto [glyph, pos] : SkMakeZip(glyphs, positions)) {
149 SkPoint corners[4];
150 SkPoint srcPos = pos + drawOrigin;
151 // Store off the positions in device space to position the glyphs during drawing.
152 sourcePositions.push_back(srcPos);
153 SkRect rect = glyph->rect();
154 rect.makeOffset(srcPos);
155 runMatrix.mapRectToQuad(corners, rect);
156 // left top -> right top
157 SkScalar scale = (corners[1] - corners[0]).length() / rect.width();
158 maxScale = std::max(maxScale, scale);
159 // right top -> right bottom
160 scale = (corners[2] - corners[1]).length() / rect.height();
161 maxScale = std::max(maxScale, scale);
162 // right bottom -> left bottom
163 scale = (corners[3] - corners[2]).length() / rect.width();
164 maxScale = std::max(maxScale, scale);
165 // left bottom -> left top
166 scale = (corners[0] - corners[3]).length() / rect.height();
167 maxScale = std::max(maxScale, scale);
168 }
169
170 if (maxScale * runFont.getSize() > 256) {
171 maxScale = 256.0f / runFont.getSize();
172 }
173
174 SkMatrix cacheScale = SkMatrix::Scale(maxScale, maxScale);
175 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
176 runFont, paint, props, fScalerContextFlags, cacheScale);
177
178 auto strike = strikeSpec.findOrCreateStrike();
179
180 // Figure out all the positions and packed glyphIDs based on the device matrix.
181 fDrawable.startBitmapDevice(
182 fRejects.source(), drawOrigin, deviceMatrix, strike->roundingSpec());
183
184 strike->prepareForDrawingMasksCPU(&fDrawable);
185 auto variants = fDrawable.drawable().get<0>();
186 for (auto [variant, srcPos] : SkMakeZip(variants, sourcePositions)) {
187 SkGlyph* glyph = variant.glyph();
188 SkMask mask = glyph->mask();
189 // TODO: is this needed will A8 and BW just work?
190 if (mask.fFormat != SkMask::kARGB32_Format) {
191 continue;
192 }
193 SkBitmap bm;
194 bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.size()),
195 mask.fImage,
196 mask.fRowBytes);
197
198 // Since the glyph in the cache is scaled by maxScale, its top left vector is too
199 // long. Reduce it to find proper positions on the device.
200 SkPoint realPos = srcPos
201 + SkPoint::Make(mask.fBounds.left(), mask.fBounds.top()) * (1.0f/maxScale);
202
203 // Calculate the preConcat matrix for drawBitmap to get the rectangle from the
204 // glyph cache (which is multiplied by maxScale) to land in the right place.
205 SkMatrix translate = SkMatrix::Translate(realPos);
206 translate.preScale(1.0f/maxScale, 1.0f/maxScale);
207
208 // Draw the bitmap using the rect from the scaled cache, and not the source
209 // rectangle for the glyph.
210 bitmapDevice->drawBitmap(
211 bm, translate, nullptr, SkSamplingOptions{SkFilterMode::kLinear},
212 paint);
213 }
214 fRejects.flipRejectsToSource();
215 }
216
217 // TODO: have the mask stage above reject the glyphs that are too big, and handle the
218 // rejects in a more sophisticated stage.
219 }
220 }
221
222 // Use the following in your args.gn to dump telemetry for diagnosing chrome Renderer/GPU
223 // differences.
224 // extra_cflags = ["-D", "SK_TRACE_GLYPH_RUN_PROCESS"]
225
226 #if SK_SUPPORT_GPU
processGlyphRun(const SkGlyphRun & glyphRun,const SkMatrix & drawMatrix,const SkPaint & runPaint,const GrSDFTControl & control,SkGlyphRunPainterInterface * process,const char * tag)227 void SkGlyphRunListPainter::processGlyphRun(const SkGlyphRun& glyphRun,
228 const SkMatrix& drawMatrix,
229 const SkPaint& runPaint,
230 const GrSDFTControl& control,
231 SkGlyphRunPainterInterface* process,
232 const char* tag) {
233 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
234 SkString msg;
235 msg.appendf("\nStart glyph run processing");
236 if (tag != nullptr) {
237 msg.appendf(" for %s ", tag);
238 }
239 msg.appendf("\n");
240 #endif
241 ScopedBuffers _ = this->ensureBuffers(glyphRun);
242 fRejects.setSource(glyphRun.source());
243 const SkFont& runFont = glyphRun.font();
244
245 GrSDFTControl::DrawingType drawingType = control.drawingType(runFont, runPaint, drawMatrix);
246
247 if (drawingType == GrSDFTControl::kSDFT) {
248 // Process SDFT - This should be the .009% case.
249 const auto& [strikeSpec, strikeToSourceScale, minScale, maxScale] =
250 SkStrikeSpec::MakeSDFT(runFont, runPaint, fDeviceProps, drawMatrix, control);
251
252 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
253 msg.appendf(" SDFT case:\n%s", strikeSpec.dump().c_str());
254 #endif
255
256 if (!SkScalarNearlyZero(strikeToSourceScale)) {
257 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
258
259 fDrawable.startSource(fRejects.source());
260 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
261 msg.appendf(" glyphs:(x,y):\n %s\n", fDrawable.dumpInput().c_str());
262 #endif
263 strike->prepareForSDFTDrawing(&fDrawable, &fRejects);
264 fRejects.flipRejectsToSource();
265
266 if (process && !fDrawable.drawableIsEmpty()) {
267 // processSourceSDFT must be called even if there are no glyphs to make sure
268 // runs are set correctly.
269 process->processSourceSDFT(fDrawable.drawable(),
270 strike->getUnderlyingStrike(),
271 strikeToSourceScale,
272 runFont,
273 minScale, maxScale);
274 }
275 }
276 }
277
278 if (drawingType != GrSDFTControl::kPath && !fRejects.source().empty()) {
279 // Process masks including ARGB - this should be the 99.99% case.
280
281 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
282 runFont, runPaint, fDeviceProps, fScalerContextFlags, drawMatrix);
283
284 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
285 msg.appendf(" Mask case:\n%s", strikeSpec.dump().c_str());
286 #endif
287
288 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
289
290 fDrawable.startGPUDevice(fRejects.source(), drawMatrix, strike->roundingSpec());
291 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
292 msg.appendf(" glyphs:(x,y):\n %s\n", fDrawable.dumpInput().c_str());
293 #endif
294 strike->prepareForMaskDrawing(&fDrawable, &fRejects);
295 fRejects.flipRejectsToSource();
296
297 if (process && !fDrawable.drawableIsEmpty()) {
298 // processDeviceMasks must be called even if there are no glyphs to make sure runs
299 // are set correctly.
300 process->processDeviceMasks(fDrawable.drawable(), strike->getUnderlyingStrike());
301 }
302 }
303
304 // Glyphs are generated in different scales relative to the source space. Masks are drawn
305 // in device space, and SDFT and Paths are draw in a fixed constant space. The
306 // maxDimensionInSourceSpace is used to calculate the factor from strike space to source
307 // space.
308 SkScalar maxDimensionInSourceSpace = 0.0;
309 if (!fRejects.source().empty()) {
310 // Path case - handle big things without color and that have a path.
311 auto [strikeSpec, strikeToSourceScale] =
312 SkStrikeSpec::MakePath(runFont, runPaint, fDeviceProps, fScalerContextFlags);
313
314 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
315 msg.appendf(" Path case:\n%s", strikeSpec.dump().c_str());
316 #endif
317
318 if (!SkScalarNearlyZero(strikeToSourceScale)) {
319 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
320
321 fDrawable.startSource(fRejects.source());
322 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
323 msg.appendf(" glyphs:(x,y):\n %s\n", fDrawable.dumpInput().c_str());
324 #endif
325 strike->prepareForPathDrawing(&fDrawable, &fRejects);
326 fRejects.flipRejectsToSource();
327 maxDimensionInSourceSpace = fRejects.rejectedMaxDimension() * strikeToSourceScale;
328
329 if (process && !fDrawable.drawableIsEmpty()) {
330 // processSourcePaths must be called even if there are no glyphs to make sure
331 // runs are set correctly.
332 process->processSourcePaths(
333 fDrawable.drawable(), runFont, strikeToSourceScale);
334 }
335 }
336 }
337
338 if (!fRejects.source().empty() && maxDimensionInSourceSpace != 0) {
339 // Draw of last resort. Scale the bitmap to the screen.
340 auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeSourceFallback(
341 runFont, runPaint, fDeviceProps,
342 fScalerContextFlags, maxDimensionInSourceSpace);
343
344 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
345 msg.appendf("Transformed case:\n%s", strikeSpec.dump().c_str());
346 #endif
347
348 if (!SkScalarNearlyZero(strikeToSourceScale)) {
349 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
350
351 fDrawable.startSource(fRejects.source());
352 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
353 msg.appendf("glyphs:(x,y):\n %s\n", fDrawable.dumpInput().c_str());
354 #endif
355 strike->prepareForMaskDrawing(&fDrawable, &fRejects);
356 fRejects.flipRejectsToSource();
357 SkASSERT(fRejects.source().empty());
358
359 if (process && !fDrawable.drawableIsEmpty()) {
360 process->processSourceMasks(
361 fDrawable.drawable(), strike->getUnderlyingStrike(), strikeToSourceScale);
362 }
363 }
364 }
365 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
366 msg.appendf("End glyph run processing");
367 if (tag != nullptr) {
368 msg.appendf(" for %s ", tag);
369 }
370 SkDebugf("%s\n", msg.c_str());
371 #endif
372 }
373 #endif // SK_SUPPORT_GPU
374
ensureBuffers(const SkGlyphRunList & glyphRunList)375 auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) -> ScopedBuffers {
376 size_t size = 0;
377 for (const SkGlyphRun& run : glyphRunList) {
378 size = std::max(run.runSize(), size);
379 }
380 return ScopedBuffers(this, size);
381 }
382
ensureBuffers(const SkGlyphRun & glyphRun)383 auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRun& glyphRun) -> ScopedBuffers {
384 return ScopedBuffers(this, glyphRun.runSize());
385 }
386
ScopedBuffers(SkGlyphRunListPainter * painter,size_t size)387 SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, size_t size)
388 : fPainter{painter} {
389 fPainter->fDrawable.ensureSize(size);
390 }
391
~ScopedBuffers()392 SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
393 fPainter->fDrawable.reset();
394 fPainter->fRejects.reset();
395 }
396
HalfAxisSampleFreq(bool isSubpixel,SkAxisAlignment axisAlignment)397 SkVector SkGlyphPositionRoundingSpec::HalfAxisSampleFreq(
398 bool isSubpixel, SkAxisAlignment axisAlignment) {
399 if (!isSubpixel) {
400 return {SK_ScalarHalf, SK_ScalarHalf};
401 } else {
402 switch (axisAlignment) {
403 case kX_SkAxisAlignment:
404 return {SkPackedGlyphID::kSubpixelRound, SK_ScalarHalf};
405 case kY_SkAxisAlignment:
406 return {SK_ScalarHalf, SkPackedGlyphID::kSubpixelRound};
407 case kNone_SkAxisAlignment:
408 return {SkPackedGlyphID::kSubpixelRound, SkPackedGlyphID::kSubpixelRound};
409 }
410 }
411
412 // Some compilers need this.
413 return {0, 0};
414 }
415
IgnorePositionMask(bool isSubpixel,SkAxisAlignment axisAlignment)416 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionMask(
417 bool isSubpixel, SkAxisAlignment axisAlignment) {
418 return SkIPoint::Make((!isSubpixel || axisAlignment == kY_SkAxisAlignment) ? 0 : ~0,
419 (!isSubpixel || axisAlignment == kX_SkAxisAlignment) ? 0 : ~0);
420 }
421
IgnorePositionFieldMask(bool isSubpixel,SkAxisAlignment axisAlignment)422 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionFieldMask(bool isSubpixel,
423 SkAxisAlignment axisAlignment) {
424 SkIPoint ignoreMask = IgnorePositionMask(isSubpixel, axisAlignment);
425 SkIPoint answer{ignoreMask.x() & SkPackedGlyphID::kXYFieldMask.x(),
426 ignoreMask.y() & SkPackedGlyphID::kXYFieldMask.y()};
427 return answer;
428 }
429
SkGlyphPositionRoundingSpec(bool isSubpixel,SkAxisAlignment axisAlignment)430 SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(
431 bool isSubpixel,SkAxisAlignment axisAlignment)
432 : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
433 , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)}
434 , ignorePositionFieldMask {IgnorePositionFieldMask(isSubpixel, axisAlignment)}{ }
435