Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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>>
60prepare_for_path_drawing(SkStrike* strike,
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>>
91prepare_for_drawable_drawing(SkStrike* strike,
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>>
120prepare_for_direct_mask_drawing(SkStrike* strike,
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]
SkPoint pos
SkColorType fColorType
SkColorType
Definition SkColorType.h:19
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:500
void drawPath(const SkPath &path, const SkPaint &paint)
void concat(const SkMatrix &matrix)
bool gammaIsLinear() const
SkRect getBounds()
void draw(SkCanvas *, const SkMatrix *=nullptr)
SkScalar getSize() const
Definition SkFont.h:217
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()
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
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:418
size_t maxGlyphRunSize() const
Definition GlyphRun.h:97
SkPoint origin() const
Definition GlyphRun.h:114
const Paint & paint
SkBitmap source
Definition examples.cpp:28
float SkScalar
Definition extension.cpp:12
size_t length
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)
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)
constexpr float y() const
constexpr float x() const