Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
10#include "include/core/SkData.h"
19#include "src/pdf/SkPDFDevice.h"
25#include "src/pdf/SkPDFUtils.h"
26
27static void draw(SkCanvas* canvas, const SkImage* image, SkColor4f paintColor) {
28 SkPaint paint(paintColor);
29 canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
30}
31
35 bitmap.allocN32Pixels(image->width(), image->height());
36 bitmap.eraseColor(0x00000000);
37 }
38 return bitmap;
39}
40
41static void draw_matrix(SkCanvas* canvas, const SkImage* image,
42 const SkMatrix& matrix, SkColor4f paintColor) {
43 SkAutoCanvasRestore acr(canvas, true);
44 canvas->concat(matrix);
45 draw(canvas, image, paintColor);
46}
47
48static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm,
49 const SkMatrix& matrix, SkColor4f paintColor) {
50 SkAutoCanvasRestore acr(canvas, true);
51 canvas->concat(matrix);
52 SkPaint paint(paintColor);
53 canvas->drawImage(bm.asImage(), 0, 0, SkSamplingOptions(), &paint);
54}
55
56static void fill_color_from_bitmap(SkCanvas* canvas,
57 float left, float top, float right, float bottom,
58 const SkBitmap& bitmap, int x, int y, float alpha) {
59 SkRect rect{left, top, right, bottom};
60 if (!rect.isEmpty()) {
62 SkPaint paint(SkColor4f{color.fR, color.fG, color.fB, alpha * color.fA});
63 canvas->drawRect(rect, paint);
64 }
65}
66
68 SkMatrix m;
69 m.setScaleTranslate(sx, sy, tx, ty);
70 return m;
71}
72
73static bool is_tiled(SkTileMode m) { return SkTileMode::kMirror == m || SkTileMode::kRepeat == m; }
74
76 SkMatrix finalMatrix,
77 SkTileMode tileModesX,
78 SkTileMode tileModesY,
79 SkRect bBox,
80 const SkImage* image,
81 SkColor4f paintColor) {
82 // The image shader pattern cell will be drawn into a separate device
83 // in pattern cell space (no scaling on the bitmap, though there may be
84 // translations so that all content is in the device, coordinates > 0).
85
86 // Map clip bounds to shader space to ensure the device is large enough
87 // to handle fake clamping.
88
89 SkRect deviceBounds = bBox;
90 if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
92 }
93
95
96 // For tiling modes, the bounds should be extended to include the bitmap,
97 // otherwise the bitmap gets clipped out and the shader is empty and awful.
98 // For clamp modes, we're only interested in the clip region, whether
99 // or not the main bitmap is in it.
100 if (is_tiled(tileModesX) || is_tiled(tileModesY)) {
101 deviceBounds.join(bitmapBounds);
102 }
103
104 SkISize patternDeviceSize = {SkScalarCeilToInt(deviceBounds.width()),
105 SkScalarCeilToInt(deviceBounds.height())};
106 auto patternDevice = sk_make_sp<SkPDFDevice>(patternDeviceSize, doc);
107 SkCanvas canvas(patternDevice);
108
110 SkScalar width = patternBBox.width();
111 SkScalar height = patternBBox.height();
112
113 // Translate the canvas so that the bitmap origin is at (0, 0).
114 canvas.translate(-deviceBounds.left(), -deviceBounds.top());
115 patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
116 // Undo the translation in the final matrix
117 finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
118
119 // If the bitmap is out of bounds (i.e. clamp mode where we only see the
120 // stretched sides), canvas will clip this out and the extraneous data
121 // won't be saved to the PDF.
122 draw(&canvas, image, paintColor);
123
124 // Tiling is implied. First we handle mirroring.
125 if (tileModesX == SkTileMode::kMirror) {
126 draw_matrix(&canvas, image, scale_translate(-1, 1, 2 * width, 0), paintColor);
127 patternBBox.fRight += width;
128 }
129 if (tileModesY == SkTileMode::kMirror) {
130 draw_matrix(&canvas, image, scale_translate(1, -1, 0, 2 * height), paintColor);
131 patternBBox.fBottom += height;
132 }
133 if (tileModesX == SkTileMode::kMirror && tileModesY == SkTileMode::kMirror) {
134 draw_matrix(&canvas, image, scale_translate(-1, -1, 2 * width, 2 * height), paintColor);
135 }
136
137 // Then handle Clamping, which requires expanding the pattern canvas to
138 // cover the entire surfaceBBox.
139
141 if (tileModesX == SkTileMode::kClamp || tileModesY == SkTileMode::kClamp) {
142 // For now, the easiest way to access the colors in the corners and sides is
143 // to just make a bitmap from the image.
145 }
146
147 // If both x and y are in clamp mode, we start by filling in the corners.
148 // (Which are just a rectangles of the corner colors.)
149 if (tileModesX == SkTileMode::kClamp && tileModesY == SkTileMode::kClamp) {
150 SkASSERT(!bitmap.drawsNothing());
151
152 fill_color_from_bitmap(&canvas, deviceBounds.left(), deviceBounds.top(), 0, 0,
153 bitmap, 0, 0, paintColor.fA);
154
155 fill_color_from_bitmap(&canvas, width, deviceBounds.top(), deviceBounds.right(), 0,
156 bitmap, bitmap.width() - 1, 0, paintColor.fA);
157
158 fill_color_from_bitmap(&canvas, width, height, deviceBounds.right(), deviceBounds.bottom(),
159 bitmap, bitmap.width() - 1, bitmap.height() - 1, paintColor.fA);
160
161 fill_color_from_bitmap(&canvas, deviceBounds.left(), height, 0, deviceBounds.bottom(),
162 bitmap, 0, bitmap.height() - 1, paintColor.fA);
163 }
164
165 // Then expand the left, right, top, then bottom.
166 if (tileModesX == SkTileMode::kClamp) {
167 SkASSERT(!bitmap.drawsNothing());
168 SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, bitmap.height());
169 if (deviceBounds.left() < 0) {
171 SkAssertResult(bitmap.extractSubset(&left, subset));
172
173 SkMatrix leftMatrix = scale_translate(-deviceBounds.left(), 1, deviceBounds.left(), 0);
174 draw_bitmap_matrix(&canvas, left, leftMatrix, paintColor);
175
176 if (tileModesY == SkTileMode::kMirror) {
177 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
178 leftMatrix.postTranslate(0, 2 * height);
179 draw_bitmap_matrix(&canvas, left, leftMatrix, paintColor);
180 }
181 patternBBox.fLeft = 0;
182 }
183
184 if (deviceBounds.right() > width) {
186 subset.offset(bitmap.width() - 1, 0);
187 SkAssertResult(bitmap.extractSubset(&right, subset));
188
189 SkMatrix rightMatrix = scale_translate(deviceBounds.right() - width, 1, width, 0);
190 draw_bitmap_matrix(&canvas, right, rightMatrix, paintColor);
191
192 if (tileModesY == SkTileMode::kMirror) {
193 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
194 rightMatrix.postTranslate(0, 2 * height);
195 draw_bitmap_matrix(&canvas, right, rightMatrix, paintColor);
196 }
197 patternBBox.fRight = deviceBounds.width();
198 }
199 }
200 if (tileModesX == SkTileMode::kDecal) {
201 if (deviceBounds.left() < 0) {
202 patternBBox.fLeft = 0;
203 }
204 if (deviceBounds.right() > width) {
205 patternBBox.fRight = deviceBounds.width();
206 }
207 }
208
209 if (tileModesY == SkTileMode::kClamp) {
210 SkASSERT(!bitmap.drawsNothing());
211 SkIRect subset = SkIRect::MakeXYWH(0, 0, bitmap.width(), 1);
212 if (deviceBounds.top() < 0) {
213 SkBitmap top;
214 SkAssertResult(bitmap.extractSubset(&top, subset));
215
216 SkMatrix topMatrix = scale_translate(1, -deviceBounds.top(), 0, deviceBounds.top());
217 draw_bitmap_matrix(&canvas, top, topMatrix, paintColor);
218
219 if (tileModesX == SkTileMode::kMirror) {
220 topMatrix.postScale(-1, 1);
221 topMatrix.postTranslate(2 * width, 0);
222 draw_bitmap_matrix(&canvas, top, topMatrix, paintColor);
223 }
224 patternBBox.fTop = 0;
225 }
226
227 if (deviceBounds.bottom() > height) {
228 SkBitmap bottom;
229 subset.offset(0, bitmap.height() - 1);
230 SkAssertResult(bitmap.extractSubset(&bottom, subset));
231
232 SkMatrix bottomMatrix = scale_translate(1, deviceBounds.bottom() - height, 0, height);
233 draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paintColor);
234
235 if (tileModesX == SkTileMode::kMirror) {
236 bottomMatrix.postScale(-1, 1);
237 bottomMatrix.postTranslate(2 * width, 0);
238 draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paintColor);
239 }
240 patternBBox.fBottom = deviceBounds.height();
241 }
242 }
243 if (tileModesY == SkTileMode::kDecal) {
244 if (deviceBounds.top() < 0) {
245 patternBBox.fTop = 0;
246 }
247 if (deviceBounds.bottom() > height) {
248 patternBBox.fBottom = deviceBounds.height();
249 }
250 }
251
252 auto imageShader = patternDevice->content();
253 std::unique_ptr<SkPDFDict> resourceDict = patternDevice->makeResourceDict();
254 std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
255 SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox,
256 std::move(resourceDict), finalMatrix);
257 return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc);
258}
259
260// Generic fallback for unsupported shaders:
261// * allocate a surfaceBBox-sized bitmap
262// * shade the whole area
263// * use the result as a bitmap shader
265 SkShader* shader,
266 const SkMatrix& canvasTransform,
267 const SkIRect& surfaceBBox,
268 SkColor4f paintColor) {
269 // surfaceBBox is in device space. While that's exactly what we
270 // want for sizing our bitmap, we need to map it into
271 // shader space for adjustments (to match
272 // MakeImageShader's behavior).
273 SkRect shaderRect = SkRect::Make(surfaceBBox);
274 if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) {
275 return SkPDFIndirectReference();
276 }
277 // Clamp the bitmap size to about 1M pixels
278 static const int kMaxBitmapArea = 1024 * 1024;
279 SkScalar bitmapArea = (float)surfaceBBox.width() * (float)surfaceBBox.height();
280 SkScalar rasterScale = 1.0f;
281 if (bitmapArea > (float)kMaxBitmapArea) {
282 rasterScale *= SkScalarSqrt((float)kMaxBitmapArea / bitmapArea);
283 }
284
285 SkISize size = {
286 SkTPin(SkScalarCeilToInt(rasterScale * surfaceBBox.width()), 1, kMaxBitmapArea),
287 SkTPin(SkScalarCeilToInt(rasterScale * surfaceBBox.height()), 1, kMaxBitmapArea)};
288 SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(),
289 SkIntToScalar(size.height()) / shaderRect.height()};
290
291 auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(size.width(), size.height()));
293 SkCanvas* canvas = surface->getCanvas();
294 canvas->clear(SK_ColorTRANSPARENT);
295
296 SkPaint p(paintColor);
297 p.setShader(sk_ref_sp(shader));
298
299 canvas->scale(scale.width(), scale.height());
300 canvas->translate(-shaderRect.x(), -shaderRect.y());
301 canvas->drawPaint(p);
302
303 auto shaderTransform = SkMatrix::Translate(shaderRect.x(), shaderRect.y());
304 shaderTransform.preScale(1 / scale.width(), 1 / scale.height());
305
306 sk_sp<SkImage> image = surface->makeImageSnapshot();
308 return make_image_shader(doc,
309 SkMatrix::Concat(canvasTransform, shaderTransform),
311 SkRect::Make(surfaceBBox),
312 image.get(),
313 paintColor);
314}
315
316static SkColor4f adjust_color(SkShader* shader, SkColor4f paintColor) {
317 if (SkImage* img = shader->isAImage(nullptr, (SkTileMode*)nullptr)) {
318 if (img->isAlphaOnly()) {
319 return paintColor;
320 }
321 }
322 return SkColor4f{0, 0, 0, paintColor.fA}; // only preserve the alpha.
323}
324
326 SkShader* shader,
327 const SkMatrix& canvasTransform,
328 const SkIRect& surfaceBBox,
329 SkColor4f paintColor) {
330 SkASSERT(shader);
331 SkASSERT(doc);
332 if (as_SB(shader)->asGradient() != SkShaderBase::GradientType::kNone) {
333 return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
334 }
335 if (surfaceBBox.isEmpty()) {
336 return SkPDFIndirectReference();
337 }
339
340 paintColor = adjust_color(shader, paintColor);
341 SkMatrix shaderTransform;
342 SkTileMode imageTileModes[2];
343 if (SkImage* skimg = shader->isAImage(&shaderTransform, imageTileModes)) {
344 SkMatrix finalMatrix = SkMatrix::Concat(canvasTransform, shaderTransform);
346 finalMatrix,
347 surfaceBBox,
349 {imageTileModes[0], imageTileModes[1]},
350 paintColor};
351 SkPDFIndirectReference* shaderPtr = doc->fImageShaderMap.find(key);
352 if (shaderPtr) {
353 return *shaderPtr;
354 }
355 SkPDFIndirectReference pdfShader =
357 finalMatrix,
358 imageTileModes[0],
359 imageTileModes[1],
360 SkRect::Make(surfaceBBox),
361 skimg,
362 paintColor);
363 doc->fImageShaderMap.set(std::move(key), pdfShader);
364 return pdfShader;
365 }
366 // Don't bother to de-dup fallback shader.
367 return make_fallback_shader(doc, shader, canvasTransform, surfaceBBox, paintColor);
368}
SkColor4f color
#define SkAssertResult(cond)
Definition SkAssert.h:123
#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)
static SkPDFIndirectReference make_image_shader(SkPDFDocument *doc, SkMatrix finalMatrix, SkTileMode tileModesX, SkTileMode tileModesY, SkRect bBox, const SkImage *image, SkColor4f paintColor)
static void draw_bitmap_matrix(SkCanvas *canvas, const SkBitmap &bm, const SkMatrix &matrix, SkColor4f paintColor)
SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument *doc, SkShader *shader, const SkMatrix &canvasTransform, const SkIRect &surfaceBBox, SkColor4f paintColor)
static bool is_tiled(SkTileMode m)
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)
static SkPDFIndirectReference make_fallback_shader(SkPDFDocument *doc, SkShader *shader, const SkMatrix &canvasTransform, const SkIRect &surfaceBBox, SkColor4f paintColor)
static SkBitmap to_bitmap(const SkImage *image)
static void draw(SkCanvas *canvas, const SkImage *image, SkColor4f paintColor)
static SkColor4f adjust_color(SkShader *shader, SkColor4f paintColor)
static SkMatrix scale_translate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty)
SkPDFIndirectReference SkPDFStreamOut(std::unique_ptr< SkPDFDict > dict, std::unique_ptr< SkStreamAsset > content, SkPDFDocument *doc, SkPDFSteamCompressionEnabled compress)
static std::unique_ptr< SkPDFDict > SkPDFMakeDict(const char *type=nullptr)
Definition SkPDFTypes.h:195
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)
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)
void translate(SkScalar dx, SkScalar dy)
void drawPaint(const SkPaint &paint)
void clear(SkColor color)
Definition SkCanvas.h:1199
void scale(SkScalar sx, SkScalar sy)
void concat(const SkMatrix &matrix)
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
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
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)
bool ToBitmap(const SkImage *img, SkBitmap *dst)
bool InverseTransformBBox(const SkMatrix &matrix, SkRect *bbox)
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
int32_t height
int32_t width
const Scalar scale
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
static SkImageInfo MakeN32Premul(int width, int height)
static SkRGBA4f FromColor(SkColor color)
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
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition SkSize.h:56