• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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