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