Flutter Engine
The Flutter Engine
SkPDFShader.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 Google Inc.
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
19#include "include/core/SkSize.h"
24#include "src/core/SkDevice.h"
25#include "src/core/SkTHash.h"
27#include "src/pdf/SkPDFDevice.h"
30#include "src/pdf/SkPDFUtils.h"
32
33#include <memory>
34#include <utility>
35
36static void draw(SkCanvas* canvas, const SkImage* image, SkColor4f paintColor) {
37 SkPaint paint(paintColor);
38 canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
39}
40
44 bitmap.allocN32Pixels(image->width(), image->height());
45 bitmap.eraseColor(0x00000000);
46 }
47 return bitmap;
48}
49
50static void draw_matrix(SkCanvas* canvas, const SkImage* image,
51 const SkMatrix& matrix, SkColor4f paintColor) {
52 SkAutoCanvasRestore acr(canvas, true);
53 canvas->concat(matrix);
54 draw(canvas, image, paintColor);
55}
56
57static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm,
58 const SkMatrix& matrix, SkColor4f paintColor) {
59 SkAutoCanvasRestore acr(canvas, true);
60 canvas->concat(matrix);
61 SkPaint paint(paintColor);
62 canvas->drawImage(bm.asImage(), 0, 0, SkSamplingOptions(), &paint);
63}
64
65static void fill_color_from_bitmap(SkCanvas* canvas,
66 float left, float top, float right, float bottom,
67 const SkBitmap& bitmap, int x, int y, float alpha) {
68 SkRect rect{left, top, right, bottom};
69 if (!rect.isEmpty()) {
70 SkColor4f color = SkColor4f::FromColor(bitmap.getColor(x, y));
71 SkPaint paint(SkColor4f{color.fR, color.fG, color.fB, alpha * color.fA});
72 canvas->drawRect(rect, paint);
73 }
74}
75
77 SkMatrix m;
78 m.setScaleTranslate(sx, sy, tx, ty);
79 return m;
80}
81
83
85 SkMatrix finalMatrix,
86 SkTileMode tileModesX,
87 SkTileMode tileModesY,
88 SkRect bBox,
89 const SkImage* image,
90 SkColor4f paintColor) {
91 // The image shader pattern cell will be drawn into a separate device
92 // in pattern cell space (no scaling on the bitmap, though there may be
93 // translations so that all content is in the device, coordinates > 0).
94
95 // Map clip bounds to shader space to ensure the device is large enough
96 // to handle fake clamping.
97
98 SkRect deviceBounds = bBox;
99 if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
100 return SkPDFIndirectReference();
101 }
102
104
105 // For tiling modes, the bounds should be extended to include the bitmap,
106 // otherwise the bitmap gets clipped out and the shader is empty and awful.
107 // For clamp modes, we're only interested in the clip region, whether
108 // or not the main bitmap is in it.
109 if (is_tiled(tileModesX) || is_tiled(tileModesY)) {
110 deviceBounds.join(bitmapBounds);
111 }
112
113 SkISize patternDeviceSize = {SkScalarCeilToInt(deviceBounds.width()),
114 SkScalarCeilToInt(deviceBounds.height())};
115 auto patternDevice = sk_make_sp<SkPDFDevice>(patternDeviceSize, doc);
116 SkCanvas canvas(patternDevice);
117
119 SkScalar width = patternBBox.width();
120 SkScalar height = patternBBox.height();
121
122 // Translate the canvas so that the bitmap origin is at (0, 0).
123 canvas.translate(-deviceBounds.left(), -deviceBounds.top());
124 patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
125 // Undo the translation in the final matrix
126 finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
127
128 // If the bitmap is out of bounds (i.e. clamp mode where we only see the
129 // stretched sides), canvas will clip this out and the extraneous data
130 // won't be saved to the PDF.
131 draw(&canvas, image, paintColor);
132
133 // Tiling is implied. First we handle mirroring.
134 if (tileModesX == SkTileMode::kMirror) {
135 draw_matrix(&canvas, image, scale_translate(-1, 1, 2 * width, 0), paintColor);
136 patternBBox.fRight += width;
137 }
138 if (tileModesY == SkTileMode::kMirror) {
139 draw_matrix(&canvas, image, scale_translate(1, -1, 0, 2 * height), paintColor);
140 patternBBox.fBottom += height;
141 }
142 if (tileModesX == SkTileMode::kMirror && tileModesY == SkTileMode::kMirror) {
143 draw_matrix(&canvas, image, scale_translate(-1, -1, 2 * width, 2 * height), paintColor);
144 }
145
146 // Then handle Clamping, which requires expanding the pattern canvas to
147 // cover the entire surfaceBBox.
148
150 if (tileModesX == SkTileMode::kClamp || tileModesY == SkTileMode::kClamp) {
151 // For now, the easiest way to access the colors in the corners and sides is
152 // to just make a bitmap from the image.
154 }
155
156 // If both x and y are in clamp mode, we start by filling in the corners.
157 // (Which are just a rectangles of the corner colors.)
158 if (tileModesX == SkTileMode::kClamp && tileModesY == SkTileMode::kClamp) {
159 SkASSERT(!bitmap.drawsNothing());
160
161 fill_color_from_bitmap(&canvas, deviceBounds.left(), deviceBounds.top(), 0, 0,
162 bitmap, 0, 0, paintColor.fA);
163
164 fill_color_from_bitmap(&canvas, width, deviceBounds.top(), deviceBounds.right(), 0,
165 bitmap, bitmap.width() - 1, 0, paintColor.fA);
166
167 fill_color_from_bitmap(&canvas, width, height, deviceBounds.right(), deviceBounds.bottom(),
168 bitmap, bitmap.width() - 1, bitmap.height() - 1, paintColor.fA);
169
170 fill_color_from_bitmap(&canvas, deviceBounds.left(), height, 0, deviceBounds.bottom(),
171 bitmap, 0, bitmap.height() - 1, paintColor.fA);
172 }
173
174 // Then expand the left, right, top, then bottom.
175 if (tileModesX == SkTileMode::kClamp) {
176 SkASSERT(!bitmap.drawsNothing());
177 SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, bitmap.height());
178 if (deviceBounds.left() < 0) {
180 SkAssertResult(bitmap.extractSubset(&left, subset));
181
182 SkMatrix leftMatrix = scale_translate(-deviceBounds.left(), 1, deviceBounds.left(), 0);
183 draw_bitmap_matrix(&canvas, left, leftMatrix, paintColor);
184
185 if (tileModesY == SkTileMode::kMirror) {
186 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
187 leftMatrix.postTranslate(0, 2 * height);
188 draw_bitmap_matrix(&canvas, left, leftMatrix, paintColor);
189 }
190 patternBBox.fLeft = 0;
191 }
192
193 if (deviceBounds.right() > width) {
195 subset.offset(bitmap.width() - 1, 0);
196 SkAssertResult(bitmap.extractSubset(&right, subset));
197
198 SkMatrix rightMatrix = scale_translate(deviceBounds.right() - width, 1, width, 0);
199 draw_bitmap_matrix(&canvas, right, rightMatrix, paintColor);
200
201 if (tileModesY == SkTileMode::kMirror) {
202 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
203 rightMatrix.postTranslate(0, 2 * height);
204 draw_bitmap_matrix(&canvas, right, rightMatrix, paintColor);
205 }
206 patternBBox.fRight = deviceBounds.width();
207 }
208 }
209 if (tileModesX == SkTileMode::kDecal) {
210 if (deviceBounds.left() < 0) {
211 patternBBox.fLeft = 0;
212 }
213 if (deviceBounds.right() > width) {
214 patternBBox.fRight = deviceBounds.width();
215 }
216 }
217
218 if (tileModesY == SkTileMode::kClamp) {
219 SkASSERT(!bitmap.drawsNothing());
220 SkIRect subset = SkIRect::MakeXYWH(0, 0, bitmap.width(), 1);
221 if (deviceBounds.top() < 0) {
222 SkBitmap top;
223 SkAssertResult(bitmap.extractSubset(&top, subset));
224
225 SkMatrix topMatrix = scale_translate(1, -deviceBounds.top(), 0, deviceBounds.top());
226 draw_bitmap_matrix(&canvas, top, topMatrix, paintColor);
227
228 if (tileModesX == SkTileMode::kMirror) {
229 topMatrix.postScale(-1, 1);
230 topMatrix.postTranslate(2 * width, 0);
231 draw_bitmap_matrix(&canvas, top, topMatrix, paintColor);
232 }
233 patternBBox.fTop = 0;
234 }
235
236 if (deviceBounds.bottom() > height) {
237 SkBitmap bottom;
238 subset.offset(0, bitmap.height() - 1);
239 SkAssertResult(bitmap.extractSubset(&bottom, subset));
240
241 SkMatrix bottomMatrix = scale_translate(1, deviceBounds.bottom() - height, 0, height);
242 draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paintColor);
243
244 if (tileModesX == SkTileMode::kMirror) {
245 bottomMatrix.postScale(-1, 1);
246 bottomMatrix.postTranslate(2 * width, 0);
247 draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paintColor);
248 }
249 patternBBox.fBottom = deviceBounds.height();
250 }
251 }
252 if (tileModesY == SkTileMode::kDecal) {
253 if (deviceBounds.top() < 0) {
254 patternBBox.fTop = 0;
255 }
256 if (deviceBounds.bottom() > height) {
257 patternBBox.fBottom = deviceBounds.height();
258 }
259 }
260
261 auto imageShader = patternDevice->content();
262 std::unique_ptr<SkPDFDict> resourceDict = patternDevice->makeResourceDict();
263 std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
264 SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox,
265 std::move(resourceDict), finalMatrix);
266 return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc);
267}
268
269// Generic fallback for unsupported shaders:
270// * allocate a surfaceBBox-sized bitmap
271// * shade the whole area
272// * use the result as a bitmap shader
274 SkShader* shader,
275 const SkMatrix& canvasTransform,
276 const SkIRect& surfaceBBox,
277 SkColor4f paintColor) {
278 // surfaceBBox is in device space. While that's exactly what we
279 // want for sizing our bitmap, we need to map it into
280 // shader space for adjustments (to match
281 // MakeImageShader's behavior).
282 SkRect shaderRect = SkRect::Make(surfaceBBox);
283 if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) {
284 return SkPDFIndirectReference();
285 }
286 // Clamp the bitmap size to about 1M pixels
287 static const int kMaxBitmapArea = 1024 * 1024;
288 SkScalar bitmapArea = (float)surfaceBBox.width() * (float)surfaceBBox.height();
289 SkScalar rasterScale = 1.0f;
290 if (bitmapArea > (float)kMaxBitmapArea) {
291 rasterScale *= SkScalarSqrt((float)kMaxBitmapArea / bitmapArea);
292 }
293
294 SkISize size = {
295 SkTPin(SkScalarCeilToInt(rasterScale * surfaceBBox.width()), 1, kMaxBitmapArea),
296 SkTPin(SkScalarCeilToInt(rasterScale * surfaceBBox.height()), 1, kMaxBitmapArea)};
297 SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(),
298 SkIntToScalar(size.height()) / shaderRect.height()};
299
302 SkCanvas* canvas = surface->getCanvas();
303 canvas->clear(SK_ColorTRANSPARENT);
304
305 SkPaint p(paintColor);
306 p.setShader(sk_ref_sp(shader));
307
308 canvas->scale(scale.width(), scale.height());
309 canvas->translate(-shaderRect.x(), -shaderRect.y());
310 canvas->drawPaint(p);
311
312 auto shaderTransform = SkMatrix::Translate(shaderRect.x(), shaderRect.y());
313 shaderTransform.preScale(1 / scale.width(), 1 / scale.height());
314
315 sk_sp<SkImage> image = surface->makeImageSnapshot();
317 return make_image_shader(doc,
318 SkMatrix::Concat(canvasTransform, shaderTransform),
320 SkRect::Make(surfaceBBox),
321 image.get(),
322 paintColor);
323}
324
325static SkColor4f adjust_color(SkShader* shader, SkColor4f paintColor) {
326 if (SkImage* img = shader->isAImage(nullptr, (SkTileMode*)nullptr)) {
327 if (img->isAlphaOnly()) {
328 return paintColor;
329 }
330 }
331 return SkColor4f{0, 0, 0, paintColor.fA}; // only preserve the alpha.
332}
333
335 SkShader* shader,
336 const SkMatrix& canvasTransform,
337 const SkIRect& surfaceBBox,
338 SkColor4f paintColor) {
339 SkASSERT(shader);
340 SkASSERT(doc);
341 if (as_SB(shader)->asGradient() != SkShaderBase::GradientType::kNone) {
342 return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
343 }
344 if (surfaceBBox.isEmpty()) {
345 return SkPDFIndirectReference();
346 }
348
349 paintColor = adjust_color(shader, paintColor);
350 SkMatrix shaderTransform;
351 SkTileMode imageTileModes[2];
352 if (SkImage* skimg = shader->isAImage(&shaderTransform, imageTileModes)) {
353 SkMatrix finalMatrix = SkMatrix::Concat(canvasTransform, shaderTransform);
355 finalMatrix,
356 surfaceBBox,
358 {imageTileModes[0], imageTileModes[1]},
359 paintColor};
360 SkPDFIndirectReference* shaderPtr = doc->fImageShaderMap.find(key);
361 if (shaderPtr) {
362 return *shaderPtr;
363 }
364 SkPDFIndirectReference pdfShader =
366 finalMatrix,
367 imageTileModes[0],
368 imageTileModes[1],
369 SkRect::Make(surfaceBBox),
370 skimg,
371 paintColor);
372 doc->fImageShaderMap.set(std::move(key), pdfShader);
373 return pdfShader;
374 }
375 // Don't bother to de-dup fallback shader.
376 return make_fallback_shader(doc, shader, canvasTransform, surfaceBBox, paintColor);
377}
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
SkBitmapKey SkBitmapKeyFromImage(const SkImage *image)
static void draw_matrix(SkCanvas *canvas, const SkImage *image, const SkMatrix &matrix, SkColor4f paintColor)
Definition: SkPDFShader.cpp:50
static SkPDFIndirectReference make_image_shader(SkPDFDocument *doc, SkMatrix finalMatrix, SkTileMode tileModesX, SkTileMode tileModesY, SkRect bBox, const SkImage *image, SkColor4f paintColor)
Definition: SkPDFShader.cpp:84
static void draw_bitmap_matrix(SkCanvas *canvas, const SkBitmap &bm, const SkMatrix &matrix, SkColor4f paintColor)
Definition: SkPDFShader.cpp:57
SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument *doc, SkShader *shader, const SkMatrix &canvasTransform, const SkIRect &surfaceBBox, SkColor4f paintColor)
static bool is_tiled(SkTileMode m)
Definition: SkPDFShader.cpp:82
static void fill_color_from_bitmap(SkCanvas *canvas, float left, float top, float right, float bottom, const SkBitmap &bitmap, int x, int y, float alpha)
Definition: SkPDFShader.cpp:65
static SkPDFIndirectReference make_fallback_shader(SkPDFDocument *doc, SkShader *shader, const SkMatrix &canvasTransform, const SkIRect &surfaceBBox, SkColor4f paintColor)
static SkBitmap to_bitmap(const SkImage *image)
Definition: SkPDFShader.cpp:41
static void draw(SkCanvas *canvas, const SkImage *image, SkColor4f paintColor)
Definition: SkPDFShader.cpp:36
static SkColor4f adjust_color(SkShader *shader, SkColor4f paintColor)
static SkMatrix scale_translate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty)
Definition: SkPDFShader.cpp:76
SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr< SkPDFDict > dict, std::unique_ptr< SkStreamAsset > content, SkPDFDocument *doc, SkPDFSteamCompressionEnabled compress)
Definition: SkPDFTypes.cpp:591
static std::unique_ptr< SkPDFDict > SkPDFMakeDict(const char *type=nullptr)
Definition: SkPDFTypes.h:185
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define SkScalarSqrt(x)
Definition: SkScalar.h:42
SkShaderBase * as_SB(SkShader *shader)
Definition: SkShaderBase.h:412
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
SkTileMode
Definition: SkTileMode.h:13
sk_sp< SkImage > asImage() const
Definition: SkBitmap.cpp:645
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
void clear(SkColor color)
Definition: SkCanvas.h:1199
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
SkISize dimensions() const
Definition: SkImage.h:297
int width() const
Definition: SkImage.h:285
int height() const
Definition: SkImage.h:291
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
SkMatrix & postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:360
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
static SkMatrix Concat(const SkMatrix &a, const SkMatrix &b)
Definition: SkMatrix.h:1775
SkMatrix & preTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:263
skia_private::THashMap< SkPDFImageShaderKey, SkPDFIndirectReference, SkPDFImageShaderKey::Hash > fImageShaderMap
SkImage * isAImage(SkMatrix *localMatrix, SkTileMode xy[2]) const
Definition: SkShader.cpp:22
T * get() const
Definition: SkRefCnt.h:303
const Paint & paint
Definition: color_source.cc:38
DlColor color
VkSurfaceKHR surface
Definition: main.cc:49
float SkScalar
Definition: extension.cpp:12
double y
double x
SkPDFIndirectReference Make(SkPDFDocument *doc, SkShader *shader, const SkMatrix &matrix, const SkIRect &surfaceBBox)
void PopulateTilingPatternDict(SkPDFDict *pattern, SkRect &bbox, std::unique_ptr< SkPDFDict > resources, const SkMatrix &matrix)
Definition: SkPDFUtils.cpp:327
bool ToBitmap(const SkImage *img, SkBitmap *dst)
Definition: SkPDFUtils.cpp:348
bool InverseTransformBBox(const SkMatrix &matrix, SkRect *bbox)
Definition: SkPDFUtils.cpp:318
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
Definition: bitmap.py:1
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SkSamplingOptions(SkFilterMode::kLinear))
int32_t height
int32_t width
const Scalar scale
Definition: SkRect.h:32
constexpr int32_t height() const
Definition: SkRect.h:165
constexpr int32_t width() const
Definition: SkRect.h:158
void offset(int32_t dx, int32_t dy)
Definition: SkRect.h:367
bool isEmpty() const
Definition: SkRect.h:202
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
Definition: SkSize.h:16
static SkImageInfo MakeN32Premul(int width, int height)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
constexpr float x() const
Definition: SkRect.h:720
constexpr float y() const
Definition: SkRect.h:727
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
void offset(float dx, float dy)
Definition: SkRect.h:1016
static constexpr SkRect MakeSize(const SkSize &size)
Definition: SkRect.h:633
constexpr float height() const
Definition: SkRect.h:769
constexpr float right() const
Definition: SkRect.h:748
constexpr float width() const
Definition: SkRect.h:762
void join(const SkRect &r)
Definition: SkRect.cpp:126
constexpr float bottom() const
Definition: SkRect.h:755
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
Definition: SkSize.h:52
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition: SkSize.h:56