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