Flutter Engine
The Flutter Engine
SkGlyphRunPainter.cpp
Go to the documentation of this file.
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
9
15#include "include/core/SkFont.h"
19#include "include/core/SkPath.h"
21#include "include/core/SkRect.h"
28#include "src/core/SkGlyph.h"
29#include "src/core/SkMask.h"
31#include "src/core/SkStrike.h"
33#include "src/text/GlyphRun.h"
34
35#include <algorithm>
36#include <initializer_list>
37#include <tuple>
38#include <vector>
39
40using namespace skia_private;
41
42using namespace skglyph;
43using namespace sktext;
44
45namespace {
46SkScalerContextFlags compute_scaler_context_flags(const SkColorSpace* cs) {
47 // If we're doing linear blending, then we can disable the gamma hacks.
48 // Otherwise, leave them on. In either case, we still want the contrast boost:
49 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
50 if (cs && cs->gammaIsLinear()) {
52 } else {
54 }
55}
56
57// TODO: collect this up into a single class when all the details are worked out.
58// This is duplicate code. The original is in SubRunContainer.cpp.
59std::tuple<SkZip<const SkGlyph*, SkPoint>, SkZip<SkGlyphID, SkPoint>>
62 SkZip<const SkGlyph*, SkPoint> acceptedBuffer,
63 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
64 int acceptedSize = 0;
65 int rejectedSize = 0;
66 strike->lock();
67 for (auto [glyphID, pos] : source) {
68 if (!SkIsFinite(pos.x(), pos.y())) {
69 continue;
70 }
71 const SkPackedGlyphID packedID{glyphID};
72 switch (SkGlyphDigest digest = strike->digestFor(kPath, packedID);
73 digest.actionFor(kPath)) {
74 case GlyphAction::kAccept:
75 acceptedBuffer[acceptedSize++] = std::make_tuple(strike->glyph(digest), pos);
76 break;
77 case GlyphAction::kReject:
78 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
79 break;
80 default:
81 break;
82 }
83 }
84 strike->unlock();
85 return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
86}
87
88// TODO: collect this up into a single class when all the details are worked out.
89// This is duplicate code. The original is in SubRunContainer.cpp.
90std::tuple<SkZip<const SkGlyph*, SkPoint>, SkZip<SkGlyphID, SkPoint>>
93 SkZip<const SkGlyph*, SkPoint> acceptedBuffer,
94 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
95 int acceptedSize = 0;
96 int rejectedSize = 0;
97 strike->lock();
98 for (auto [glyphID, pos] : source) {
99 if (!SkIsFinite(pos.x(), pos.y())) {
100 continue;
101 }
102 const SkPackedGlyphID packedID{glyphID};
103 switch (SkGlyphDigest digest = strike->digestFor(kDrawable, packedID);
104 digest.actionFor(kDrawable)) {
105 case GlyphAction::kAccept:
106 acceptedBuffer[acceptedSize++] = std::make_tuple(strike->glyph(digest), pos);
107 break;
108 case GlyphAction::kReject:
109 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
110 break;
111 default:
112 break;
113 }
114 }
115 strike->unlock();
116 return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
117}
118
119std::tuple<SkZip<const SkGlyph*, SkPoint>, SkZip<SkGlyphID, SkPoint>>
121 const SkMatrix& creationMatrix,
123 SkZip<const SkGlyph*, SkPoint> acceptedBuffer,
124 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
125 const SkIPoint mask = strike->roundingSpec().ignorePositionFieldMask;
126 const SkPoint halfSampleFreq = strike->roundingSpec().halfAxisSampleFreq;
127
128 // Build up the mapping from source space to device space. Add the rounding constant
129 // halfSampleFreq, so we just need to floor to get the device result.
130 SkMatrix positionMatrixWithRounding = creationMatrix;
131 positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
132
133 int acceptedSize = 0;
134 int rejectedSize = 0;
135 strike->lock();
136 for (auto [glyphID, pos] : source) {
137 if (!SkIsFinite(pos.x(), pos.y())) {
138 continue;
139 }
140
141 const SkPoint mappedPos = positionMatrixWithRounding.mapPoint(pos);
142 const SkPackedGlyphID packedGlyphID = SkPackedGlyphID{glyphID, mappedPos, mask};
143 switch (SkGlyphDigest digest = strike->digestFor(kDirectMaskCPU, packedGlyphID);
144 digest.actionFor(kDirectMaskCPU)) {
145 case GlyphAction::kAccept: {
146 const SkPoint roundedPos{SkScalarFloorToScalar(mappedPos.x()),
147 SkScalarFloorToScalar(mappedPos.y())};
148 acceptedBuffer[acceptedSize++] =
149 std::make_tuple(strike->glyph(digest), roundedPos);
150 break;
151 }
152 case GlyphAction::kReject:
153 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
154 break;
155 default:
156 break;
157 }
158 }
159 strike->unlock();
160
161 return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
162}
163} // namespace
164
165// -- SkGlyphRunListPainterCPU ---------------------------------------------------------------------
168 SkColorSpace* cs)
169 : fDeviceProps{props}
170 , fBitmapFallbackProps{props.cloneWithPixelGeometry(kUnknown_SkPixelGeometry)}
172 , fScalerContextFlags{compute_scaler_context_flags(cs)} {}
173
175 const BitmapDevicePainter* bitmapDevice,
176 const sktext::GlyphRunList& glyphRunList,
177 const SkPaint& paint,
178 const SkMatrix& drawMatrix) {
179 STArray<64, const SkGlyph*> acceptedPackedGlyphIDs;
180 STArray<64, SkPoint> acceptedPositions;
181 STArray<64, SkGlyphID> rejectedGlyphIDs;
182 STArray<64, SkPoint> rejectedPositions;
183 const int maxGlyphRunSize = glyphRunList.maxGlyphRunSize();
184 acceptedPackedGlyphIDs.resize(maxGlyphRunSize);
185 acceptedPositions.resize(maxGlyphRunSize);
186 const auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions);
187 rejectedGlyphIDs.resize(maxGlyphRunSize);
188 rejectedPositions.resize(maxGlyphRunSize);
189 const auto rejectedBuffer = SkMakeZip(rejectedGlyphIDs, rejectedPositions);
190
191 // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
192 // convert the lcd text into A8 text. The props communicate this to the scaler.
193 auto& props = (kN32_SkColorType == fColorType && paint.isSrcOver())
194 ? fDeviceProps
195 : fBitmapFallbackProps;
196
197 SkPoint drawOrigin = glyphRunList.origin();
198 SkMatrix positionMatrix{drawMatrix};
199 positionMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
200 for (auto& glyphRun : glyphRunList) {
201 const SkFont& runFont = glyphRun.font();
202
204
205 if (SkStrikeSpec::ShouldDrawAsPath(paint, runFont, positionMatrix)) {
206 auto [strikeSpec, strikeToSourceScale] =
207 SkStrikeSpec::MakePath(runFont, paint, props, fScalerContextFlags);
208
209 auto strike = strikeSpec.findOrCreateStrike();
210
211 {
212 auto [accepted, rejected] = prepare_for_path_drawing(strike.get(),
213 source,
214 acceptedBuffer,
215 rejectedBuffer);
216
217 source = rejected;
218 // The paint we draw paths with must have the same anti-aliasing state as the
219 // runFont allowing the paths to have the same edging as the glyph masks.
220 SkPaint pathPaint = paint;
221 pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
222
223 const bool stroking = pathPaint.getStyle() != SkPaint::kFill_Style;
224 const bool hairline = pathPaint.getStrokeWidth() == 0;
225 const bool needsExactCTM = pathPaint.getShader() ||
226 pathPaint.getPathEffect() ||
227 pathPaint.getMaskFilter() ||
228 (stroking && !hairline);
229
230 if (!needsExactCTM) {
231 for (auto [glyph, pos] : accepted) {
232 const SkPath* path = glyph->path();
233 SkMatrix m;
234 SkPoint translate = drawOrigin + pos;
235 m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
236 translate.x(), translate.y());
237 SkAutoCanvasRestore acr(canvas, true);
238 canvas->concat(m);
239 canvas->drawPath(*path, pathPaint);
240 }
241 } else {
242 for (auto [glyph, pos] : accepted) {
243 const SkPath* path = glyph->path();
244 SkMatrix m;
245 SkPoint translate = drawOrigin + pos;
246 m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
247 translate.x(), translate.y());
248
249 SkPath deviceOutline;
250 path->transform(m, &deviceOutline);
251 deviceOutline.setIsVolatile(true);
252 canvas->drawPath(deviceOutline, pathPaint);
253 }
254 }
255 }
256
257 if (!source.empty()) {
258 auto [accepted, rejected] = prepare_for_drawable_drawing(strike.get(),
259 source,
260 acceptedBuffer,
261 rejectedBuffer);
262 source = rejected;
263
264 for (auto [glyph, pos] : accepted) {
265 SkDrawable* drawable = glyph->drawable();
266 SkMatrix m;
267 SkPoint translate = drawOrigin + pos;
268 m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
269 translate.x(), translate.y());
270 SkAutoCanvasRestore acr(canvas, false);
271 SkRect drawableBounds = drawable->getBounds();
272 m.mapRect(&drawableBounds);
273 canvas->saveLayer(&drawableBounds, &paint);
274 drawable->draw(canvas, &m);
275 }
276 }
277 }
278 if (!source.empty() && !positionMatrix.hasPerspective()) {
280 runFont, paint, props, fScalerContextFlags, positionMatrix);
281
282 auto strike = strikeSpec.findOrCreateStrike();
283
284 auto [accepted, rejected] = prepare_for_direct_mask_drawing(strike.get(),
285 positionMatrix,
286 source,
287 acceptedBuffer,
288 rejectedBuffer);
289 source = rejected;
290 bitmapDevice->paintMasks(accepted, paint);
291 }
292 if (!source.empty()) {
293 std::vector<SkPoint> sourcePositions;
294
295 // Create a strike is source space to calculate scale information.
296 SkStrikeSpec scaleStrikeSpec = SkStrikeSpec::MakeMask(
297 runFont, paint, props, fScalerContextFlags, SkMatrix::I());
298 SkBulkGlyphMetrics metrics{scaleStrikeSpec};
299
300 auto glyphIDs = source.get<0>();
301 auto positions = source.get<1>();
302 SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
303 SkScalar maxScale = SK_ScalarMin;
304
305 // Calculate the scale that makes the longest edge 1:1 with its side in the cache.
306 for (auto [glyph, pos] : SkMakeZip(glyphs, positions)) {
307 if (glyph->isEmpty()) {
308 continue;
309 }
310 SkPoint corners[4];
311 SkPoint srcPos = pos + drawOrigin;
312 // Store off the positions in device space to position the glyphs during drawing.
313 sourcePositions.push_back(srcPos);
314 SkRect rect = glyph->rect();
315 rect.makeOffset(srcPos);
316 positionMatrix.mapRectToQuad(corners, rect);
317 // left top -> right top
318 SkScalar scale = (corners[1] - corners[0]).length() / rect.width();
319 maxScale = std::max(maxScale, scale);
320 // right top -> right bottom
321 scale = (corners[2] - corners[1]).length() / rect.height();
322 maxScale = std::max(maxScale, scale);
323 // right bottom -> left bottom
324 scale = (corners[3] - corners[2]).length() / rect.width();
325 maxScale = std::max(maxScale, scale);
326 // left bottom -> left top
327 scale = (corners[0] - corners[3]).length() / rect.height();
328 maxScale = std::max(maxScale, scale);
329 }
330
331 if (maxScale <= 0) {
332 continue; // to the next run.
333 }
334
335 if (maxScale * runFont.getSize() > 256) {
336 maxScale = 256.0f / runFont.getSize();
337 }
338
339 SkMatrix cacheScale = SkMatrix::Scale(maxScale, maxScale);
341 runFont, paint, props, fScalerContextFlags, cacheScale);
342
343 auto strike = strikeSpec.findOrCreateStrike();
344
345 auto [accepted, rejected] = prepare_for_direct_mask_drawing(strike.get(),
346 positionMatrix,
347 source,
348 acceptedBuffer,
349 rejectedBuffer);
350 const SkScalar invMaxScale = 1.0f/maxScale;
351 for (auto [glyph, srcPos] : SkMakeZip(accepted.get<0>(), sourcePositions)) {
352 SkMask mask = glyph->mask();
353 // TODO: is this needed will A8 and BW just work?
354 if (mask.fFormat != SkMask::kARGB32_Format) {
355 continue;
356 }
357 SkBitmap bm;
359 const_cast<uint8_t*>(mask.fImage),
360 mask.fRowBytes);
361 bm.setImmutable();
362
363 // Since the glyph in the cache is scaled by maxScale, its top left vector is too
364 // long. Reduce it to find proper positions on the device.
365 SkPoint realPos =
366 srcPos + SkPoint::Make(mask.fBounds.left(), mask.fBounds.top())*invMaxScale;
367
368 // Calculate the preConcat matrix for drawBitmap to get the rectangle from the
369 // glyph cache (which is multiplied by maxScale) to land in the right place.
370 SkMatrix translate = SkMatrix::Translate(realPos);
371 translate.preScale(invMaxScale, invMaxScale);
372
373 // Draw the bitmap using the rect from the scaled cache, and not the source
374 // rectangle for the glyph.
375 bitmapDevice->drawBitmap(bm, translate, nullptr, SkFilterMode::kLinear, paint);
376 }
377 }
378
379 // TODO: have the mask stage above reject the glyphs that are too big, and handle the
380 // rejects in a more sophisticated stage.
381 }
382}
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
SkPoint pos
SkColorType fColorType
SkColorType
Definition: SkColorType.h:19
@ kDrawable
static bool SkIsFinite(T x, Pack... values)
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
#define SkScalarFloorToScalar(x)
Definition: SkScalar.h:30
#define SK_ScalarMin
Definition: SkScalar.h:25
SkScalerContextFlags
@ kUnknown_SkPixelGeometry
constexpr auto SkMakeZip(Ts &&... ts)
Definition: SkZip.h:212
void setImmutable()
Definition: SkBitmap.cpp:400
bool installPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, void(*releaseProc)(void *addr, void *context), void *context)
Definition: SkBitmap.cpp:323
bool empty() const
Definition: SkBitmap.h:210
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
bool gammaIsLinear() const
SkRect getBounds()
Definition: SkDrawable.cpp:71
void draw(SkCanvas *, const SkMatrix *=nullptr)
Definition: SkDrawable.cpp:43
Definition: SkFont.h:35
SkScalar getSize() const
Definition: SkFont.h:217
skglyph::GlyphAction actionFor(skglyph::ActionType actionType) const
Definition: SkGlyph.h:343
virtual void drawBitmap(const SkBitmap &, const SkMatrix &, const SkRect *dstOrNull, const SkSamplingOptions &, const SkPaint &) const =0
virtual void paintMasks(SkZip< const SkGlyph *, SkPoint > accepted, const SkPaint &paint) const =0
void drawForBitmapDevice(SkCanvas *canvas, const BitmapDevicePainter *bitmapDevice, const sktext::GlyphRunList &glyphRunList, const SkPaint &paint, const SkMatrix &drawMatrix)
SkGlyphRunListPainterCPU(const SkSurfaceProps &props, SkColorType colorType, SkColorSpace *cs)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
SkPoint mapPoint(SkPoint pt) const
Definition: SkMatrix.h:1374
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkMatrix & preTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:263
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:315
Style getStyle() const
Definition: SkPaint.h:204
SkPathEffect * getPathEffect() const
Definition: SkPaint.h:506
void setAntiAlias(bool aa)
Definition: SkPaint.h:170
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
SkMaskFilter * getMaskFilter() const
Definition: SkPaint.h:534
SkScalar getStrokeWidth() const
Definition: SkPaint.h:300
SkShader * getShader() const
Definition: SkPaint.h:397
Definition: SkPath.h:59
SkPath & setIsVolatile(bool isVolatile)
Definition: SkPath.h:370
static SkStrikeSpec MakeMask(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, SkScalerContextFlags scalerContextFlags, const SkMatrix &deviceMatrix)
static bool ShouldDrawAsPath(const SkPaint &paint, const SkFont &font, const SkMatrix &matrix)
sk_sp< SkStrike > findOrCreateStrike() const
static std::tuple< SkStrikeSpec, SkScalar > MakePath(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, SkScalerContextFlags scalerContextFlags)
void unlock() override SK_RELEASE_CAPABILITY(fStrikeLock)
Definition: SkStrike.cpp:80
void lock() override SK_ACQUIRE(fStrikeLock)
Definition: SkStrike.cpp:75
SkGlyph * glyph(SkGlyphDigest) SK_REQUIRES(fStrikeLock)
Definition: SkStrike.cpp:322
const SkGlyphPositionRoundingSpec & roundingSpec() const override
Definition: SkStrike.h:107
SkGlyphDigest digestFor(skglyph::ActionType, SkPackedGlyphID) override SK_REQUIRES(fStrikeLock)
Definition: SkStrike.cpp:331
Definition: SkZip.h:25
constexpr SkZip first(size_t n) const
Definition: SkZip.h:86
void resize(size_t count)
Definition: SkTArray.h:423
size_t maxGlyphRunSize() const
Definition: GlyphRun.h:97
SkPoint origin() const
Definition: GlyphRun.h:114
const Paint & paint
Definition: color_source.cc:38
SkBitmap source
Definition: examples.cpp:28
float SkScalar
Definition: extension.cpp:12
static float max(float r, float g, float b)
Definition: hsl.cpp:49
size_t length
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
@ kPath
Definition: SkGlyph.h:317
@ kDirectMaskCPU
Definition: SkGlyph.h:314
std::tuple< SkZip< const SkGlyphID, const SkPoint >, SkZip< SkGlyphID, SkPoint > > prepare_for_drawable_drawing(StrikeForGPU *strike, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkGlyphID, SkPoint > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
std::tuple< SkZip< const SkGlyphID, const SkPoint >, SkZip< SkGlyphID, SkPoint > > prepare_for_path_drawing(StrikeForGPU *strike, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkGlyphID, SkPoint > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
std::tuple< SkZip< const SkPackedGlyphID, const SkPoint, const SkMask::Format >, SkZip< SkGlyphID, SkPoint >, SkRect > prepare_for_direct_mask_drawing(StrikeForGPU *strike, const SkMatrix &positionMatrix, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkPackedGlyphID, SkPoint, SkMask::Format > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
const Scalar scale
const SkVector halfAxisSampleFreq
Definition: SkGlyph.h:233
const SkIPoint ignorePositionFieldMask
Definition: SkGlyph.h:235
constexpr int32_t top() const
Definition: SkRect.h:120
constexpr SkISize size() const
Definition: SkRect.h:172
constexpr int32_t left() const
Definition: SkRect.h:113
static SkImageInfo MakeN32Premul(int width, int height)
Definition: SkMask.h:25
const uint32_t fRowBytes
Definition: SkMask.h:43
@ kARGB32_Format
SkPMColor.
Definition: SkMask.h:30
uint8_t const *const fImage
Definition: SkMask.h:41
const SkIRect fBounds
Definition: SkMask.h:42
const Format fFormat
Definition: SkMask.h:44
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181