• 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/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