blob: 5301be7b82c43bbaa2227fbac266d44a3a180b0a [file] [log] [blame]
vandebo@chromium.orgda912d62011-03-08 18:31:02 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2011 Google Inc.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +00006 */
7
epoger@google.comec3ed6a2011-07-28 14:26:00 +00008
vandebo@chromium.orgda912d62011-03-08 18:31:02 +00009#include "SkPDFShader.h"
10
vandebo@chromium.org421d6442011-07-20 17:39:01 +000011#include "SkData.h"
Hal Canary4ca9fa32018-12-21 16:15:01 -050012#include "SkPDFDocument.h"
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000013#include "SkPDFDevice.h"
Hal Canary23564b92018-09-07 14:33:14 -040014#include "SkPDFDocumentPriv.h"
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +000015#include "SkPDFFormXObject.h"
Hal Canary94fd66c2017-07-05 11:25:42 -040016#include "SkPDFGradientShader.h"
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +000017#include "SkPDFGraphicState.h"
commit-bot@chromium.org47401352013-07-23 21:49:29 +000018#include "SkPDFResourceDict.h"
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000019#include "SkPDFUtils.h"
20#include "SkScalar.h"
21#include "SkStream.h"
Hal Canary7e872ca2017-07-19 15:51:18 -040022#include "SkSurface.h"
twiz@google.com316338a2011-03-09 23:14:04 +000023#include "SkTemplates.h"
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000024
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000025
Hal Canary7e872ca2017-07-19 15:51:18 -040026static void draw_image_matrix(SkCanvas* canvas, const SkImage* img,
27 const SkMatrix& matrix, const SkPaint& paint) {
Hal Canary4f29c202017-07-18 10:28:31 -040028 SkAutoCanvasRestore acr(canvas, true);
29 canvas->concat(matrix);
Hal Canary7e872ca2017-07-19 15:51:18 -040030 canvas->drawImage(img, 0, 0, &paint);
Hal Canary4f29c202017-07-18 10:28:31 -040031}
32
Hal Canary7e872ca2017-07-19 15:51:18 -040033static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm,
34 const SkMatrix& matrix, const SkPaint& paint) {
Florin Malitac54d8db2014-12-10 12:02:16 -050035 SkAutoCanvasRestore acr(canvas, true);
36 canvas->concat(matrix);
Hal Canary7e872ca2017-07-19 15:51:18 -040037 canvas->drawBitmap(bm, 0, 0, &paint);
Florin Malitac54d8db2014-12-10 12:02:16 -050038}
39
Hal Canary9a3f5542018-12-10 19:59:07 -050040static SkPDFIndirectReference make_image_shader(SkPDFDocument* doc,
41 const SkPDFImageShaderKey& key,
42 SkImage* image) {
Hal Canary4f29c202017-07-18 10:28:31 -040043 SkASSERT(image);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000044
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +000045 // The image shader pattern cell will be drawn into a separate device
46 // in pattern cell space (no scaling on the bitmap, though there may be
47 // translations so that all content is in the device, coordinates > 0).
48
49 // Map clip bounds to shader space to ensure the device is large enough
50 // to handle fake clamping.
Hal Canaryec257682017-07-06 08:37:02 -040051 SkMatrix finalMatrix = key.fCanvasTransform;
52 finalMatrix.preConcat(key.fShaderTransform);
Hal Canary4f29c202017-07-18 10:28:31 -040053 SkRect deviceBounds = SkRect::Make(key.fBBox);
Hal Canary94fd66c2017-07-05 11:25:42 -040054 if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) {
Hal Canary9a3f5542018-12-10 19:59:07 -050055 return SkPDFIndirectReference();
vandebo@chromium.org386dfc02012-04-17 22:31:52 +000056 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000057
Hal Canary4f29c202017-07-18 10:28:31 -040058 SkRect bitmapBounds = SkRect::Make(image->bounds());
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +000059
60 // For tiling modes, the bounds should be extended to include the bitmap,
61 // otherwise the bitmap gets clipped out and the shader is empty and awful.
62 // For clamp modes, we're only interested in the clip region, whether
63 // or not the main bitmap is in it.
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000064 SkShader::TileMode tileModes[2];
Hal Canaryec257682017-07-06 08:37:02 -040065 tileModes[0] = key.fImageTileModes[0];
66 tileModes[1] = key.fImageTileModes[1];
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +000067 if (tileModes[0] != SkShader::kClamp_TileMode ||
68 tileModes[1] != SkShader::kClamp_TileMode) {
69 deviceBounds.join(bitmapBounds);
70 }
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000071
Hal Canary4f29c202017-07-18 10:28:31 -040072 SkISize patternDeviceSize = {SkScalarCeilToInt(deviceBounds.width()),
73 SkScalarCeilToInt(deviceBounds.height())};
74 auto patternDevice = sk_make_sp<SkPDFDevice>(patternDeviceSize, doc);
Herb Derbyefe39bc2018-05-01 17:06:20 -040075 SkCanvas canvas(patternDevice);
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +000076
Hal Canary4f29c202017-07-18 10:28:31 -040077 SkRect patternBBox = SkRect::Make(image->bounds());
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +000078
79 // Translate the canvas so that the bitmap origin is at (0, 0).
80 canvas.translate(-deviceBounds.left(), -deviceBounds.top());
81 patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
82 // Undo the translation in the final matrix
83 finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
84
85 // If the bitmap is out of bounds (i.e. clamp mode where we only see the
86 // stretched sides), canvas will clip this out and the extraneous data
87 // won't be saved to the PDF.
Hal Canary4f29c202017-07-18 10:28:31 -040088 canvas.drawImage(image, 0, 0);
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +000089
Hal Canary4f29c202017-07-18 10:28:31 -040090 SkScalar width = SkIntToScalar(image->width());
91 SkScalar height = SkIntToScalar(image->height());
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000092
Hal Canary7e872ca2017-07-19 15:51:18 -040093 SkPaint paint;
94 paint.setColor(key.fPaintColor);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +000095 // Tiling is implied. First we handle mirroring.
96 if (tileModes[0] == SkShader::kMirror_TileMode) {
97 SkMatrix xMirror;
98 xMirror.setScale(-1, 1);
99 xMirror.postTranslate(2 * width, 0);
Hal Canary7e872ca2017-07-19 15:51:18 -0400100 draw_image_matrix(&canvas, image, xMirror, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000101 patternBBox.fRight += width;
102 }
103 if (tileModes[1] == SkShader::kMirror_TileMode) {
104 SkMatrix yMirror;
vandebo@chromium.org663515b2012-01-05 18:45:27 +0000105 yMirror.setScale(SK_Scalar1, -SK_Scalar1);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000106 yMirror.postTranslate(0, 2 * height);
Hal Canary7e872ca2017-07-19 15:51:18 -0400107 draw_image_matrix(&canvas, image, yMirror, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000108 patternBBox.fBottom += height;
109 }
110 if (tileModes[0] == SkShader::kMirror_TileMode &&
111 tileModes[1] == SkShader::kMirror_TileMode) {
112 SkMatrix mirror;
113 mirror.setScale(-1, -1);
114 mirror.postTranslate(2 * width, 2 * height);
Hal Canary7e872ca2017-07-19 15:51:18 -0400115 draw_image_matrix(&canvas, image, mirror, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000116 }
117
118 // Then handle Clamping, which requires expanding the pattern canvas to
119 // cover the entire surfaceBBox.
120
Hal Canary4f29c202017-07-18 10:28:31 -0400121 SkBitmap bitmap;
122 if (tileModes[0] == SkShader::kClamp_TileMode ||
123 tileModes[1] == SkShader::kClamp_TileMode) {
124 // For now, the easiest way to access the colors in the corners and sides is
125 // to just make a bitmap from the image.
126 if (!SkPDFUtils::ToBitmap(image, &bitmap)) {
127 bitmap.allocN32Pixels(image->width(), image->height());
128 bitmap.eraseColor(0x00000000);
129 }
130 }
131
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000132 // If both x and y are in clamp mode, we start by filling in the corners.
133 // (Which are just a rectangles of the corner colors.)
134 if (tileModes[0] == SkShader::kClamp_TileMode &&
135 tileModes[1] == SkShader::kClamp_TileMode) {
Hal Canary4f29c202017-07-18 10:28:31 -0400136 SkASSERT(!bitmap.drawsNothing());
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000137 SkPaint paint;
138 SkRect rect;
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000139 rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000140 if (!rect.isEmpty()) {
Hal Canary4f29c202017-07-18 10:28:31 -0400141 paint.setColor(bitmap.getColor(0, 0));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000142 canvas.drawRect(rect, paint);
143 }
144
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000145 rect = SkRect::MakeLTRB(width, deviceBounds.top(),
146 deviceBounds.right(), 0);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000147 if (!rect.isEmpty()) {
Hal Canary4f29c202017-07-18 10:28:31 -0400148 paint.setColor(bitmap.getColor(bitmap.width() - 1, 0));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000149 canvas.drawRect(rect, paint);
150 }
151
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000152 rect = SkRect::MakeLTRB(width, height,
153 deviceBounds.right(), deviceBounds.bottom());
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000154 if (!rect.isEmpty()) {
Hal Canary4f29c202017-07-18 10:28:31 -0400155 paint.setColor(bitmap.getColor(bitmap.width() - 1,
156 bitmap.height() - 1));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000157 canvas.drawRect(rect, paint);
158 }
159
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000160 rect = SkRect::MakeLTRB(deviceBounds.left(), height,
161 0, deviceBounds.bottom());
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000162 if (!rect.isEmpty()) {
Hal Canary4f29c202017-07-18 10:28:31 -0400163 paint.setColor(bitmap.getColor(0, bitmap.height() - 1));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000164 canvas.drawRect(rect, paint);
165 }
166 }
167
168 // Then expand the left, right, top, then bottom.
169 if (tileModes[0] == SkShader::kClamp_TileMode) {
Hal Canary4f29c202017-07-18 10:28:31 -0400170 SkASSERT(!bitmap.drawsNothing());
171 SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, bitmap.height());
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000172 if (deviceBounds.left() < 0) {
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000173 SkBitmap left;
Hal Canary4f29c202017-07-18 10:28:31 -0400174 SkAssertResult(bitmap.extractSubset(&left, subset));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000175
176 SkMatrix leftMatrix;
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000177 leftMatrix.setScale(-deviceBounds.left(), 1);
178 leftMatrix.postTranslate(deviceBounds.left(), 0);
Hal Canary7e872ca2017-07-19 15:51:18 -0400179 draw_bitmap_matrix(&canvas, left, leftMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000180
181 if (tileModes[1] == SkShader::kMirror_TileMode) {
vandebo@chromium.org663515b2012-01-05 18:45:27 +0000182 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000183 leftMatrix.postTranslate(0, 2 * height);
Hal Canary7e872ca2017-07-19 15:51:18 -0400184 draw_bitmap_matrix(&canvas, left, leftMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000185 }
vandebo@chromium.orgbe2048a2011-05-02 15:24:01 +0000186 patternBBox.fLeft = 0;
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000187 }
188
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000189 if (deviceBounds.right() > width) {
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000190 SkBitmap right;
Hal Canary4f29c202017-07-18 10:28:31 -0400191 subset.offset(bitmap.width() - 1, 0);
192 SkAssertResult(bitmap.extractSubset(&right, subset));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000193
194 SkMatrix rightMatrix;
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000195 rightMatrix.setScale(deviceBounds.right() - width, 1);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000196 rightMatrix.postTranslate(width, 0);
Hal Canary7e872ca2017-07-19 15:51:18 -0400197 draw_bitmap_matrix(&canvas, right, rightMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000198
199 if (tileModes[1] == SkShader::kMirror_TileMode) {
vandebo@chromium.org663515b2012-01-05 18:45:27 +0000200 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000201 rightMatrix.postTranslate(0, 2 * height);
Hal Canary7e872ca2017-07-19 15:51:18 -0400202 draw_bitmap_matrix(&canvas, right, rightMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000203 }
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000204 patternBBox.fRight = deviceBounds.width();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000205 }
206 }
207
208 if (tileModes[1] == SkShader::kClamp_TileMode) {
Hal Canary4f29c202017-07-18 10:28:31 -0400209 SkASSERT(!bitmap.drawsNothing());
210 SkIRect subset = SkIRect::MakeXYWH(0, 0, bitmap.width(), 1);
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000211 if (deviceBounds.top() < 0) {
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000212 SkBitmap top;
Hal Canary4f29c202017-07-18 10:28:31 -0400213 SkAssertResult(bitmap.extractSubset(&top, subset));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000214
215 SkMatrix topMatrix;
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000216 topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
217 topMatrix.postTranslate(0, deviceBounds.top());
Hal Canary7e872ca2017-07-19 15:51:18 -0400218 draw_bitmap_matrix(&canvas, top, topMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000219
220 if (tileModes[0] == SkShader::kMirror_TileMode) {
221 topMatrix.postScale(-1, 1);
222 topMatrix.postTranslate(2 * width, 0);
Hal Canary7e872ca2017-07-19 15:51:18 -0400223 draw_bitmap_matrix(&canvas, top, topMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000224 }
vandebo@chromium.orgbe2048a2011-05-02 15:24:01 +0000225 patternBBox.fTop = 0;
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000226 }
227
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000228 if (deviceBounds.bottom() > height) {
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000229 SkBitmap bottom;
Hal Canary4f29c202017-07-18 10:28:31 -0400230 subset.offset(0, bitmap.height() - 1);
231 SkAssertResult(bitmap.extractSubset(&bottom, subset));
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000232
233 SkMatrix bottomMatrix;
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000234 bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000235 bottomMatrix.postTranslate(0, height);
Hal Canary7e872ca2017-07-19 15:51:18 -0400236 draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000237
238 if (tileModes[0] == SkShader::kMirror_TileMode) {
239 bottomMatrix.postScale(-1, 1);
240 bottomMatrix.postTranslate(2 * width, 0);
Hal Canary7e872ca2017-07-19 15:51:18 -0400241 draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paint);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000242 }
commit-bot@chromium.orge324cc62013-08-21 23:10:45 +0000243 patternBBox.fBottom = deviceBounds.height();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000244 }
245 }
246
Hal Canary9a3f5542018-12-10 19:59:07 -0500247 auto imageShader = patternDevice->content();
Hal Canary74801582018-12-18 16:30:41 -0500248 std::unique_ptr<SkPDFDict> resourceDict = patternDevice->makeResourceDict();
249 std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict();
Hal Canary9a3f5542018-12-10 19:59:07 -0500250 SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox,
Hal Canary9e41c212018-09-03 12:00:23 -0400251 std::move(resourceDict), finalMatrix);
Hal Canary9a3f5542018-12-10 19:59:07 -0500252 return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc);
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000253}
254
Hal Canary94fd66c2017-07-05 11:25:42 -0400255// Generic fallback for unsupported shaders:
256// * allocate a surfaceBBox-sized bitmap
257// * shade the whole area
258// * use the result as a bitmap shader
Hal Canary9a3f5542018-12-10 19:59:07 -0500259static SkPDFIndirectReference make_fallback_shader(SkPDFDocument* doc,
260 SkShader* shader,
261 const SkMatrix& canvasTransform,
262 const SkIRect& surfaceBBox,
263 SkColor paintColor) {
Hal Canary94fd66c2017-07-05 11:25:42 -0400264 // TODO(vandebo) This drops SKComposeShader on the floor. We could
265 // handle compose shader by pulling things up to a layer, drawing with
266 // the first shader, applying the xfer mode and drawing again with the
267 // second shader, then applying the layer to the original drawing.
Hal Canaryec257682017-07-06 08:37:02 -0400268 SkPDFImageShaderKey key = {
Hal Canary94fd66c2017-07-05 11:25:42 -0400269 canvasTransform,
270 SkMatrix::I(),
271 surfaceBBox,
Hal Canary4f29c202017-07-18 10:28:31 -0400272 {{0, 0, 0, 0}, 0}, // don't need the key; won't de-dup.
Hal Canary7e872ca2017-07-19 15:51:18 -0400273 {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode},
274 paintColor};
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000275
Hal Canaryec257682017-07-06 08:37:02 -0400276 key.fShaderTransform = shader->getLocalMatrix();
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000277
Hal Canary94fd66c2017-07-05 11:25:42 -0400278 // surfaceBBox is in device space. While that's exactly what we
Hal Canary47bf4c02016-09-28 11:53:33 -0400279 // want for sizing our bitmap, we need to map it into
280 // shader space for adjustments (to match
281 // MakeImageShader's behavior).
Hal Canary94fd66c2017-07-05 11:25:42 -0400282 SkRect shaderRect = SkRect::Make(surfaceBBox);
283 if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) {
Hal Canary9a3f5542018-12-10 19:59:07 -0500284 return SkPDFIndirectReference();
Hal Canary47bf4c02016-09-28 11:53:33 -0400285 }
Hal Canary47bf4c02016-09-28 11:53:33 -0400286 // Clamp the bitmap size to about 1M pixels
287 static const SkScalar kMaxBitmapArea = 1024 * 1024;
Hal Canary82591142018-05-09 14:20:12 -0400288 SkScalar bitmapArea = surfaceBBox.width() * surfaceBBox.height();
289 SkScalar rasterScale = 1.0f;
Hal Canary47bf4c02016-09-28 11:53:33 -0400290 if (bitmapArea > kMaxBitmapArea) {
291 rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
292 }
293
Hal Canary94fd66c2017-07-05 11:25:42 -0400294 SkISize size = {SkScalarRoundToInt(rasterScale * surfaceBBox.width()),
295 SkScalarRoundToInt(rasterScale * surfaceBBox.height())};
Hal Canaryfafe1352017-04-11 12:12:02 -0400296 SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(),
297 SkIntToScalar(size.height()) / shaderRect.height()};
Hal Canary47bf4c02016-09-28 11:53:33 -0400298
Hal Canary7e872ca2017-07-19 15:51:18 -0400299 auto surface = SkSurface::MakeRasterN32Premul(size.width(), size.height());
300 SkCanvas* canvas = surface->getCanvas();
301 canvas->clear(SK_ColorTRANSPARENT);
Hal Canary47bf4c02016-09-28 11:53:33 -0400302
303 SkPaint p;
304 p.setShader(sk_ref_sp(shader));
Hal Canary7e872ca2017-07-19 15:51:18 -0400305 p.setColor(paintColor);
Hal Canary47bf4c02016-09-28 11:53:33 -0400306
Hal Canary7e872ca2017-07-19 15:51:18 -0400307 canvas->scale(scale.width(), scale.height());
308 canvas->translate(-shaderRect.x(), -shaderRect.y());
309 canvas->drawPaint(p);
Hal Canary47bf4c02016-09-28 11:53:33 -0400310
Hal Canaryec257682017-07-06 08:37:02 -0400311 key.fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
312 key.fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
Hal Canary4f29c202017-07-18 10:28:31 -0400313
Hal Canary7e872ca2017-07-19 15:51:18 -0400314 sk_sp<SkImage> image = surface->makeImageSnapshot();
Hal Canary4f29c202017-07-18 10:28:31 -0400315 return make_image_shader(doc, key, image.get());
vandebo@chromium.orgda912d62011-03-08 18:31:02 +0000316}
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000317
Hal Canary7e872ca2017-07-19 15:51:18 -0400318static SkColor adjust_color(SkShader* shader, SkColor paintColor) {
319 if (SkImage* img = shader->isAImage(nullptr, nullptr)) {
320 if (img->isAlphaOnly()) {
321 return paintColor;
322 }
323 }
324 // only preserve the alpha.
325 return paintColor & SK_ColorBLACK;
326}
327
Hal Canary9a3f5542018-12-10 19:59:07 -0500328SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc,
329 SkShader* shader,
330 const SkMatrix& canvasTransform,
331 const SkIRect& surfaceBBox,
332 SkColor paintColor) {
Hal Canary94fd66c2017-07-05 11:25:42 -0400333 SkASSERT(shader);
334 SkASSERT(doc);
335 if (SkShader::kNone_GradientType != shader->asAGradient(nullptr)) {
336 return SkPDFGradientShader::Make(doc, shader, canvasTransform, surfaceBBox);
337 }
338 if (surfaceBBox.isEmpty()) {
Hal Canary9a3f5542018-12-10 19:59:07 -0500339 return SkPDFIndirectReference();
Hal Canary94fd66c2017-07-05 11:25:42 -0400340 }
341 SkBitmap image;
Hal Canaryec257682017-07-06 08:37:02 -0400342 SkPDFImageShaderKey key = {
Hal Canary94fd66c2017-07-05 11:25:42 -0400343 canvasTransform,
344 SkMatrix::I(),
345 surfaceBBox,
346 {{0, 0, 0, 0}, 0},
Hal Canary7e872ca2017-07-19 15:51:18 -0400347 {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode},
348 adjust_color(shader, paintColor)};
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000349
Hal Canary94fd66c2017-07-05 11:25:42 -0400350 SkASSERT(shader->asAGradient(nullptr) == SkShader::kNone_GradientType) ;
Hal Canary4f29c202017-07-18 10:28:31 -0400351 if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) {
352 key.fBitmapKey = SkBitmapKeyFromImage(skimg);
Hal Canary4ca9fa32018-12-21 16:15:01 -0500353 SkPDFIndirectReference* shaderPtr = doc->fImageShaderMap.find(key);
Hal Canary94fd66c2017-07-05 11:25:42 -0400354 if (shaderPtr) {
355 return *shaderPtr;
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000356 }
Hal Canary9a3f5542018-12-10 19:59:07 -0500357 SkPDFIndirectReference pdfShader = make_image_shader(doc, key, skimg);
Hal Canary4ca9fa32018-12-21 16:15:01 -0500358 doc->fImageShaderMap.set(std::move(key), pdfShader);
Hal Canary94fd66c2017-07-05 11:25:42 -0400359 return pdfShader;
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000360 }
Hal Canary94fd66c2017-07-05 11:25:42 -0400361 // Don't bother to de-dup fallback shader.
Hal Canary7e872ca2017-07-19 15:51:18 -0400362 return make_fallback_shader(doc, shader, canvasTransform, surfaceBBox, key.fPaintColor);
commit-bot@chromium.org93a2e212013-07-23 23:16:03 +0000363}