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
8
9 #include "SkPDFShader.h"
10
11 #include "SkData.h"
12 #include "SkPDFCanon.h"
13 #include "SkPDFDevice.h"
14 #include "SkPDFDocument.h"
15 #include "SkPDFFormXObject.h"
16 #include "SkPDFGradientShader.h"
17 #include "SkPDFGraphicState.h"
18 #include "SkPDFResourceDict.h"
19 #include "SkPDFUtils.h"
20 #include "SkScalar.h"
21 #include "SkStream.h"
22 #include "SkTemplates.h"
23
24
draw_image_matrix(SkCanvas * canvas,const SkImage * img,const SkMatrix & matrix)25 static void draw_image_matrix(SkCanvas* canvas, const SkImage* img, const SkMatrix& matrix) {
26 SkAutoCanvasRestore acr(canvas, true);
27 canvas->concat(matrix);
28 canvas->drawImage(img, 0, 0);
29 }
30
draw_bitmap_matrix(SkCanvas * canvas,const SkBitmap & bm,const SkMatrix & matrix)31 static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& matrix) {
32 SkAutoCanvasRestore acr(canvas, true);
33 canvas->concat(matrix);
34 canvas->drawBitmap(bm, 0, 0);
35 }
36
make_image_shader(SkPDFDocument * doc,const SkPDFImageShaderKey & key,SkImage * image)37 static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
38 const SkPDFImageShaderKey& key,
39 SkImage* image) {
40 SkASSERT(image);
41
42 // The image shader pattern cell will be drawn into a separate device
43 // in pattern cell space (no scaling on the bitmap, though there may be
44 // translations so that all content is in the device, coordinates > 0).
45
46 // Map clip bounds to shader space to ensure the device is large enough
47 // to handle fake clamping.
48 SkMatrix finalMatrix = key.fCanvasTransform;
49 finalMatrix.preConcat(key.fShaderTransform);
50 SkRect deviceBounds = SkRect::Make(key.fBBox);
51 if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
52 return nullptr;
53 }
54
55 SkRect bitmapBounds = SkRect::Make(image->bounds());
56
57 // For tiling modes, the bounds should be extended to include the bitmap,
58 // otherwise the bitmap gets clipped out and the shader is empty and awful.
59 // For clamp modes, we're only interested in the clip region, whether
60 // or not the main bitmap is in it.
61 SkShader::TileMode tileModes[2];
62 tileModes[0] = key.fImageTileModes[0];
63 tileModes[1] = key.fImageTileModes[1];
64 if (tileModes[0] != SkShader::kClamp_TileMode ||
65 tileModes[1] != SkShader::kClamp_TileMode) {
66 deviceBounds.join(bitmapBounds);
67 }
68
69 SkISize patternDeviceSize = {SkScalarCeilToInt(deviceBounds.width()),
70 SkScalarCeilToInt(deviceBounds.height())};
71 auto patternDevice = sk_make_sp<SkPDFDevice>(patternDeviceSize, doc);
72 SkCanvas canvas(patternDevice.get());
73
74 SkRect patternBBox = SkRect::Make(image->bounds());
75
76 // Translate the canvas so that the bitmap origin is at (0, 0).
77 canvas.translate(-deviceBounds.left(), -deviceBounds.top());
78 patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
79 // Undo the translation in the final matrix
80 finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
81
82 // If the bitmap is out of bounds (i.e. clamp mode where we only see the
83 // stretched sides), canvas will clip this out and the extraneous data
84 // won't be saved to the PDF.
85 canvas.drawImage(image, 0, 0);
86
87 SkScalar width = SkIntToScalar(image->width());
88 SkScalar height = SkIntToScalar(image->height());
89
90 // Tiling is implied. First we handle mirroring.
91 if (tileModes[0] == SkShader::kMirror_TileMode) {
92 SkMatrix xMirror;
93 xMirror.setScale(-1, 1);
94 xMirror.postTranslate(2 * width, 0);
95 draw_image_matrix(&canvas, image, xMirror);
96 patternBBox.fRight += width;
97 }
98 if (tileModes[1] == SkShader::kMirror_TileMode) {
99 SkMatrix yMirror;
100 yMirror.setScale(SK_Scalar1, -SK_Scalar1);
101 yMirror.postTranslate(0, 2 * height);
102 draw_image_matrix(&canvas, image, yMirror);
103 patternBBox.fBottom += height;
104 }
105 if (tileModes[0] == SkShader::kMirror_TileMode &&
106 tileModes[1] == SkShader::kMirror_TileMode) {
107 SkMatrix mirror;
108 mirror.setScale(-1, -1);
109 mirror.postTranslate(2 * width, 2 * height);
110 draw_image_matrix(&canvas, image, mirror);
111 }
112
113 // Then handle Clamping, which requires expanding the pattern canvas to
114 // cover the entire surfaceBBox.
115
116 SkBitmap bitmap;
117 if (tileModes[0] == SkShader::kClamp_TileMode ||
118 tileModes[1] == SkShader::kClamp_TileMode) {
119 // For now, the easiest way to access the colors in the corners and sides is
120 // to just make a bitmap from the image.
121 if (!SkPDFUtils::ToBitmap(image, &bitmap)) {
122 bitmap.allocN32Pixels(image->width(), image->height());
123 bitmap.eraseColor(0x00000000);
124 }
125 }
126
127 // If both x and y are in clamp mode, we start by filling in the corners.
128 // (Which are just a rectangles of the corner colors.)
129 if (tileModes[0] == SkShader::kClamp_TileMode &&
130 tileModes[1] == SkShader::kClamp_TileMode) {
131 SkASSERT(!bitmap.drawsNothing());
132 SkPaint paint;
133 SkRect rect;
134 rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
135 if (!rect.isEmpty()) {
136 paint.setColor(bitmap.getColor(0, 0));
137 canvas.drawRect(rect, paint);
138 }
139
140 rect = SkRect::MakeLTRB(width, deviceBounds.top(),
141 deviceBounds.right(), 0);
142 if (!rect.isEmpty()) {
143 paint.setColor(bitmap.getColor(bitmap.width() - 1, 0));
144 canvas.drawRect(rect, paint);
145 }
146
147 rect = SkRect::MakeLTRB(width, height,
148 deviceBounds.right(), deviceBounds.bottom());
149 if (!rect.isEmpty()) {
150 paint.setColor(bitmap.getColor(bitmap.width() - 1,
151 bitmap.height() - 1));
152 canvas.drawRect(rect, paint);
153 }
154
155 rect = SkRect::MakeLTRB(deviceBounds.left(), height,
156 0, deviceBounds.bottom());
157 if (!rect.isEmpty()) {
158 paint.setColor(bitmap.getColor(0, bitmap.height() - 1));
159 canvas.drawRect(rect, paint);
160 }
161 }
162
163 // Then expand the left, right, top, then bottom.
164 if (tileModes[0] == SkShader::kClamp_TileMode) {
165 SkASSERT(!bitmap.drawsNothing());
166 SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, bitmap.height());
167 if (deviceBounds.left() < 0) {
168 SkBitmap left;
169 SkAssertResult(bitmap.extractSubset(&left, subset));
170
171 SkMatrix leftMatrix;
172 leftMatrix.setScale(-deviceBounds.left(), 1);
173 leftMatrix.postTranslate(deviceBounds.left(), 0);
174 draw_bitmap_matrix(&canvas, left, leftMatrix);
175
176 if (tileModes[1] == SkShader::kMirror_TileMode) {
177 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
178 leftMatrix.postTranslate(0, 2 * height);
179 draw_bitmap_matrix(&canvas, left, leftMatrix);
180 }
181 patternBBox.fLeft = 0;
182 }
183
184 if (deviceBounds.right() > width) {
185 SkBitmap right;
186 subset.offset(bitmap.width() - 1, 0);
187 SkAssertResult(bitmap.extractSubset(&right, subset));
188
189 SkMatrix rightMatrix;
190 rightMatrix.setScale(deviceBounds.right() - width, 1);
191 rightMatrix.postTranslate(width, 0);
192 draw_bitmap_matrix(&canvas, right, rightMatrix);
193
194 if (tileModes[1] == SkShader::kMirror_TileMode) {
195 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
196 rightMatrix.postTranslate(0, 2 * height);
197 draw_bitmap_matrix(&canvas, right, rightMatrix);
198 }
199 patternBBox.fRight = deviceBounds.width();
200 }
201 }
202
203 if (tileModes[1] == SkShader::kClamp_TileMode) {
204 SkASSERT(!bitmap.drawsNothing());
205 SkIRect subset = SkIRect::MakeXYWH(0, 0, bitmap.width(), 1);
206 if (deviceBounds.top() < 0) {
207 SkBitmap top;
208 SkAssertResult(bitmap.extractSubset(&top, subset));
209
210 SkMatrix topMatrix;
211 topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
212 topMatrix.postTranslate(0, deviceBounds.top());
213 draw_bitmap_matrix(&canvas, top, topMatrix);
214
215 if (tileModes[0] == SkShader::kMirror_TileMode) {
216 topMatrix.postScale(-1, 1);
217 topMatrix.postTranslate(2 * width, 0);
218 draw_bitmap_matrix(&canvas, top, topMatrix);
219 }
220 patternBBox.fTop = 0;
221 }
222
223 if (deviceBounds.bottom() > height) {
224 SkBitmap bottom;
225 subset.offset(0, bitmap.height() - 1);
226 SkAssertResult(bitmap.extractSubset(&bottom, subset));
227
228 SkMatrix bottomMatrix;
229 bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
230 bottomMatrix.postTranslate(0, height);
231 draw_bitmap_matrix(&canvas, bottom, bottomMatrix);
232
233 if (tileModes[0] == SkShader::kMirror_TileMode) {
234 bottomMatrix.postScale(-1, 1);
235 bottomMatrix.postTranslate(2 * width, 0);
236 draw_bitmap_matrix(&canvas, bottom, bottomMatrix);
237 }
238 patternBBox.fBottom = deviceBounds.height();
239 }
240 }
241
242 auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content());
243 SkPDFUtils::PopulateTilingPatternDict(imageShader->dict(), patternBBox,
244 patternDevice->makeResourceDict(), finalMatrix);
245 return imageShader;
246 }
247
248 // Generic fallback for unsupported shaders:
249 // * allocate a surfaceBBox-sized bitmap
250 // * shade the whole area
251 // * use the result as a bitmap shader
make_fallback_shader(SkPDFDocument * doc,SkShader * shader,const SkMatrix & canvasTransform,const SkIRect & surfaceBBox)252 static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc,
253 SkShader* shader,
254 const SkMatrix& canvasTransform,
255 const SkIRect& surfaceBBox) {
256 // TODO(vandebo) This drops SKComposeShader on the floor. We could
257 // handle compose shader by pulling things up to a layer, drawing with
258 // the first shader, applying the xfer mode and drawing again with the
259 // second shader, then applying the layer to the original drawing.
260 SkPDFImageShaderKey key = {
261 canvasTransform,
262 SkMatrix::I(),
263 surfaceBBox,
264 {{0, 0, 0, 0}, 0}, // don't need the key; won't de-dup.
265 {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode}};
266
267 key.fShaderTransform = shader->getLocalMatrix();
268
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 nullptr;
276 }
277 // Clamp the bitmap size to about 1M pixels
278 static const SkScalar kMaxBitmapArea = 1024 * 1024;
279 SkScalar rasterScale = SkIntToScalar(doc->rasterDpi()) / SkPDFUtils::kDpiForRasterScaleOne;
280 SkScalar bitmapArea = rasterScale * surfaceBBox.width() * rasterScale * surfaceBBox.height();
281 if (bitmapArea > kMaxBitmapArea) {
282 rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
283 }
284
285 SkISize size = {SkScalarRoundToInt(rasterScale * surfaceBBox.width()),
286 SkScalarRoundToInt(rasterScale * surfaceBBox.height())};
287 SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(),
288 SkIntToScalar(size.height()) / shaderRect.height()};
289
290 SkBitmap bitmap;
291 bitmap.allocN32Pixels(size.width(), size.height());
292 bitmap.eraseColor(SK_ColorTRANSPARENT);
293
294 SkPaint p;
295 p.setShader(sk_ref_sp(shader));
296
297 SkCanvas canvas(bitmap);
298 canvas.scale(scale.width(), scale.height());
299 canvas.translate(-shaderRect.x(), -shaderRect.y());
300 canvas.drawPaint(p);
301
302 key.fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
303 key.fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
304
305 SkASSERT (!bitmap.isNull());
306 bitmap.setImmutable();
307 sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
308 return make_image_shader(doc, key, image.get());
309 }
310
SkPDFMakeShader(SkPDFDocument * doc,SkShader * shader,const SkMatrix & canvasTransform,const SkIRect & surfaceBBox)311 sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc,
312 SkShader* shader,
313 const SkMatrix& canvasTransform,
314 const SkIRect& surfaceBBox) {
315 SkASSERT(shader);
316 SkASSERT(doc);
317 if (SkShader::kNone_GradientType != shader->asAGradient(nullptr)) {
318 return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
319 }
320 if (surfaceBBox.isEmpty()) {
321 return nullptr;
322 }
323 SkBitmap image;
324 SkPDFImageShaderKey key = {
325 canvasTransform,
326 SkMatrix::I(),
327 surfaceBBox,
328 {{0, 0, 0, 0}, 0},
329 {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode}};
330
331 SkASSERT(shader->asAGradient(nullptr) == SkShader::kNone_GradientType) ;
332 if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) {
333 key.fBitmapKey = SkBitmapKeyFromImage(skimg);
334 SkPDFCanon* canon = doc->canon();
335 sk_sp<SkPDFObject>* shaderPtr = canon->fImageShaderMap.find(key);
336 if (shaderPtr) {
337 return *shaderPtr;
338 }
339 sk_sp<SkPDFObject> pdfShader = make_image_shader(doc, key, skimg);
340 canon->fImageShaderMap.set(std::move(key), pdfShader);
341 return pdfShader;
342 }
343 // Don't bother to de-dup fallback shader.
344 return make_fallback_shader(doc, shader, canvasTransform, surfaceBBox);
345 }
346