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" |
Hal Canary | 4ca9fa3 | 2018-12-21 16:15:01 -0500 | [diff] [blame] | 12 | #include "SkPDFDocument.h" |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 13 | #include "SkPDFDevice.h" |
Hal Canary | 23564b9 | 2018-09-07 14:33:14 -0400 | [diff] [blame] | 14 | #include "SkPDFDocumentPriv.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" |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 22 | #include "SkSurface.h" |
twiz@google.com | 316338a | 2011-03-09 23:14:04 +0000 | [diff] [blame] | 23 | #include "SkTemplates.h" |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 24 | |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 25 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 26 | static void draw_image_matrix(SkCanvas* canvas, const SkImage* img, |
| 27 | const SkMatrix& matrix, const SkPaint& paint) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 28 | SkAutoCanvasRestore acr(canvas, true); |
| 29 | canvas->concat(matrix); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 30 | canvas->drawImage(img, 0, 0, &paint); |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 31 | } |
| 32 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 33 | static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm, |
| 34 | const SkMatrix& matrix, const SkPaint& paint) { |
Florin Malita | c54d8db | 2014-12-10 12:02:16 -0500 | [diff] [blame] | 35 | SkAutoCanvasRestore acr(canvas, true); |
| 36 | canvas->concat(matrix); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 37 | canvas->drawBitmap(bm, 0, 0, &paint); |
Florin Malita | c54d8db | 2014-12-10 12:02:16 -0500 | [diff] [blame] | 38 | } |
| 39 | |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 40 | static SkPDFIndirectReference make_image_shader(SkPDFDocument* doc, |
| 41 | const SkPDFImageShaderKey& key, |
| 42 | SkImage* image) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 43 | SkASSERT(image); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 44 | |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 45 | // 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 Canary | ec25768 | 2017-07-06 08:37:02 -0400 | [diff] [blame] | 51 | SkMatrix finalMatrix = key.fCanvasTransform; |
| 52 | finalMatrix.preConcat(key.fShaderTransform); |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 53 | SkRect deviceBounds = SkRect::Make(key.fBBox); |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 54 | if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) { |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 55 | return SkPDFIndirectReference(); |
vandebo@chromium.org | 386dfc0 | 2012-04-17 22:31:52 +0000 | [diff] [blame] | 56 | } |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 57 | |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 58 | SkRect bitmapBounds = SkRect::Make(image->bounds()); |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 59 | |
| 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.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 64 | SkShader::TileMode tileModes[2]; |
Hal Canary | ec25768 | 2017-07-06 08:37:02 -0400 | [diff] [blame] | 65 | tileModes[0] = key.fImageTileModes[0]; |
| 66 | tileModes[1] = key.fImageTileModes[1]; |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 67 | if (tileModes[0] != SkShader::kClamp_TileMode || |
| 68 | tileModes[1] != SkShader::kClamp_TileMode) { |
| 69 | deviceBounds.join(bitmapBounds); |
| 70 | } |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 71 | |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 72 | SkISize patternDeviceSize = {SkScalarCeilToInt(deviceBounds.width()), |
| 73 | SkScalarCeilToInt(deviceBounds.height())}; |
| 74 | auto patternDevice = sk_make_sp<SkPDFDevice>(patternDeviceSize, doc); |
Herb Derby | efe39bc | 2018-05-01 17:06:20 -0400 | [diff] [blame] | 75 | SkCanvas canvas(patternDevice); |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 76 | |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 77 | SkRect patternBBox = SkRect::Make(image->bounds()); |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 78 | |
| 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 Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 88 | canvas.drawImage(image, 0, 0); |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 89 | |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 90 | SkScalar width = SkIntToScalar(image->width()); |
| 91 | SkScalar height = SkIntToScalar(image->height()); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 92 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 93 | SkPaint paint; |
| 94 | paint.setColor(key.fPaintColor); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 95 | // 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 Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 100 | draw_image_matrix(&canvas, image, xMirror, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 101 | patternBBox.fRight += width; |
| 102 | } |
| 103 | if (tileModes[1] == SkShader::kMirror_TileMode) { |
| 104 | SkMatrix yMirror; |
vandebo@chromium.org | 663515b | 2012-01-05 18:45:27 +0000 | [diff] [blame] | 105 | yMirror.setScale(SK_Scalar1, -SK_Scalar1); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 106 | yMirror.postTranslate(0, 2 * height); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 107 | draw_image_matrix(&canvas, image, yMirror, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 108 | 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 Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 115 | draw_image_matrix(&canvas, image, mirror, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | // Then handle Clamping, which requires expanding the pattern canvas to |
| 119 | // cover the entire surfaceBBox. |
| 120 | |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 121 | 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.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 132 | // 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 Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 136 | SkASSERT(!bitmap.drawsNothing()); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 137 | SkPaint paint; |
| 138 | SkRect rect; |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 139 | rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 140 | if (!rect.isEmpty()) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 141 | paint.setColor(bitmap.getColor(0, 0)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 142 | canvas.drawRect(rect, paint); |
| 143 | } |
| 144 | |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 145 | rect = SkRect::MakeLTRB(width, deviceBounds.top(), |
| 146 | deviceBounds.right(), 0); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 147 | if (!rect.isEmpty()) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 148 | paint.setColor(bitmap.getColor(bitmap.width() - 1, 0)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 149 | canvas.drawRect(rect, paint); |
| 150 | } |
| 151 | |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 152 | rect = SkRect::MakeLTRB(width, height, |
| 153 | deviceBounds.right(), deviceBounds.bottom()); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 154 | if (!rect.isEmpty()) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 155 | paint.setColor(bitmap.getColor(bitmap.width() - 1, |
| 156 | bitmap.height() - 1)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 157 | canvas.drawRect(rect, paint); |
| 158 | } |
| 159 | |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 160 | rect = SkRect::MakeLTRB(deviceBounds.left(), height, |
| 161 | 0, deviceBounds.bottom()); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 162 | if (!rect.isEmpty()) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 163 | paint.setColor(bitmap.getColor(0, bitmap.height() - 1)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 164 | canvas.drawRect(rect, paint); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | // Then expand the left, right, top, then bottom. |
| 169 | if (tileModes[0] == SkShader::kClamp_TileMode) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 170 | SkASSERT(!bitmap.drawsNothing()); |
| 171 | SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, bitmap.height()); |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 172 | if (deviceBounds.left() < 0) { |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 173 | SkBitmap left; |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 174 | SkAssertResult(bitmap.extractSubset(&left, subset)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 175 | |
| 176 | SkMatrix leftMatrix; |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 177 | leftMatrix.setScale(-deviceBounds.left(), 1); |
| 178 | leftMatrix.postTranslate(deviceBounds.left(), 0); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 179 | draw_bitmap_matrix(&canvas, left, leftMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 180 | |
| 181 | if (tileModes[1] == SkShader::kMirror_TileMode) { |
vandebo@chromium.org | 663515b | 2012-01-05 18:45:27 +0000 | [diff] [blame] | 182 | leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 183 | leftMatrix.postTranslate(0, 2 * height); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 184 | draw_bitmap_matrix(&canvas, left, leftMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 185 | } |
vandebo@chromium.org | be2048a | 2011-05-02 15:24:01 +0000 | [diff] [blame] | 186 | patternBBox.fLeft = 0; |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 187 | } |
| 188 | |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 189 | if (deviceBounds.right() > width) { |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 190 | SkBitmap right; |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 191 | subset.offset(bitmap.width() - 1, 0); |
| 192 | SkAssertResult(bitmap.extractSubset(&right, subset)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 193 | |
| 194 | SkMatrix rightMatrix; |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 195 | rightMatrix.setScale(deviceBounds.right() - width, 1); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 196 | rightMatrix.postTranslate(width, 0); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 197 | draw_bitmap_matrix(&canvas, right, rightMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 198 | |
| 199 | if (tileModes[1] == SkShader::kMirror_TileMode) { |
vandebo@chromium.org | 663515b | 2012-01-05 18:45:27 +0000 | [diff] [blame] | 200 | rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 201 | rightMatrix.postTranslate(0, 2 * height); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 202 | draw_bitmap_matrix(&canvas, right, rightMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 203 | } |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 204 | patternBBox.fRight = deviceBounds.width(); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 205 | } |
| 206 | } |
| 207 | |
| 208 | if (tileModes[1] == SkShader::kClamp_TileMode) { |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 209 | SkASSERT(!bitmap.drawsNothing()); |
| 210 | SkIRect subset = SkIRect::MakeXYWH(0, 0, bitmap.width(), 1); |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 211 | if (deviceBounds.top() < 0) { |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 212 | SkBitmap top; |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 213 | SkAssertResult(bitmap.extractSubset(&top, subset)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 214 | |
| 215 | SkMatrix topMatrix; |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 216 | topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); |
| 217 | topMatrix.postTranslate(0, deviceBounds.top()); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 218 | draw_bitmap_matrix(&canvas, top, topMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 219 | |
| 220 | if (tileModes[0] == SkShader::kMirror_TileMode) { |
| 221 | topMatrix.postScale(-1, 1); |
| 222 | topMatrix.postTranslate(2 * width, 0); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 223 | draw_bitmap_matrix(&canvas, top, topMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 224 | } |
vandebo@chromium.org | be2048a | 2011-05-02 15:24:01 +0000 | [diff] [blame] | 225 | patternBBox.fTop = 0; |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 226 | } |
| 227 | |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 228 | if (deviceBounds.bottom() > height) { |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 229 | SkBitmap bottom; |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 230 | subset.offset(0, bitmap.height() - 1); |
| 231 | SkAssertResult(bitmap.extractSubset(&bottom, subset)); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 232 | |
| 233 | SkMatrix bottomMatrix; |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 234 | bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 235 | bottomMatrix.postTranslate(0, height); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 236 | draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 237 | |
| 238 | if (tileModes[0] == SkShader::kMirror_TileMode) { |
| 239 | bottomMatrix.postScale(-1, 1); |
| 240 | bottomMatrix.postTranslate(2 * width, 0); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 241 | draw_bitmap_matrix(&canvas, bottom, bottomMatrix, paint); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 242 | } |
commit-bot@chromium.org | e324cc6 | 2013-08-21 23:10:45 +0000 | [diff] [blame] | 243 | patternBBox.fBottom = deviceBounds.height(); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 244 | } |
| 245 | } |
| 246 | |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 247 | auto imageShader = patternDevice->content(); |
Hal Canary | 7480158 | 2018-12-18 16:30:41 -0500 | [diff] [blame] | 248 | std::unique_ptr<SkPDFDict> resourceDict = patternDevice->makeResourceDict(); |
| 249 | std::unique_ptr<SkPDFDict> dict = SkPDFMakeDict(); |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 250 | SkPDFUtils::PopulateTilingPatternDict(dict.get(), patternBBox, |
Hal Canary | 9e41c21 | 2018-09-03 12:00:23 -0400 | [diff] [blame] | 251 | std::move(resourceDict), finalMatrix); |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 252 | return SkPDFStreamOut(std::move(dict), std::move(imageShader), doc); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 253 | } |
| 254 | |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 255 | // 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 Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 259 | static SkPDFIndirectReference make_fallback_shader(SkPDFDocument* doc, |
| 260 | SkShader* shader, |
| 261 | const SkMatrix& canvasTransform, |
| 262 | const SkIRect& surfaceBBox, |
| 263 | SkColor paintColor) { |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 264 | // 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 Canary | ec25768 | 2017-07-06 08:37:02 -0400 | [diff] [blame] | 268 | SkPDFImageShaderKey key = { |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 269 | canvasTransform, |
| 270 | SkMatrix::I(), |
| 271 | surfaceBBox, |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 272 | {{0, 0, 0, 0}, 0}, // don't need the key; won't de-dup. |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 273 | {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode}, |
| 274 | paintColor}; |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 275 | |
Hal Canary | ec25768 | 2017-07-06 08:37:02 -0400 | [diff] [blame] | 276 | key.fShaderTransform = shader->getLocalMatrix(); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 277 | |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 278 | // surfaceBBox is in device space. While that's exactly what we |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 279 | // want for sizing our bitmap, we need to map it into |
| 280 | // shader space for adjustments (to match |
| 281 | // MakeImageShader's behavior). |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 282 | SkRect shaderRect = SkRect::Make(surfaceBBox); |
| 283 | if (!SkPDFUtils::InverseTransformBBox(canvasTransform, &shaderRect)) { |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 284 | return SkPDFIndirectReference(); |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 285 | } |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 286 | // Clamp the bitmap size to about 1M pixels |
| 287 | static const SkScalar kMaxBitmapArea = 1024 * 1024; |
Hal Canary | 8259114 | 2018-05-09 14:20:12 -0400 | [diff] [blame] | 288 | SkScalar bitmapArea = surfaceBBox.width() * surfaceBBox.height(); |
| 289 | SkScalar rasterScale = 1.0f; |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 290 | if (bitmapArea > kMaxBitmapArea) { |
| 291 | rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea); |
| 292 | } |
| 293 | |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 294 | SkISize size = {SkScalarRoundToInt(rasterScale * surfaceBBox.width()), |
| 295 | SkScalarRoundToInt(rasterScale * surfaceBBox.height())}; |
Hal Canary | fafe135 | 2017-04-11 12:12:02 -0400 | [diff] [blame] | 296 | SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(), |
| 297 | SkIntToScalar(size.height()) / shaderRect.height()}; |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 298 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 299 | auto surface = SkSurface::MakeRasterN32Premul(size.width(), size.height()); |
| 300 | SkCanvas* canvas = surface->getCanvas(); |
| 301 | canvas->clear(SK_ColorTRANSPARENT); |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 302 | |
| 303 | SkPaint p; |
| 304 | p.setShader(sk_ref_sp(shader)); |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 305 | p.setColor(paintColor); |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 306 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 307 | canvas->scale(scale.width(), scale.height()); |
| 308 | canvas->translate(-shaderRect.x(), -shaderRect.y()); |
| 309 | canvas->drawPaint(p); |
Hal Canary | 47bf4c0 | 2016-09-28 11:53:33 -0400 | [diff] [blame] | 310 | |
Hal Canary | ec25768 | 2017-07-06 08:37:02 -0400 | [diff] [blame] | 311 | key.fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); |
| 312 | key.fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 313 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 314 | sk_sp<SkImage> image = surface->makeImageSnapshot(); |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 315 | return make_image_shader(doc, key, image.get()); |
vandebo@chromium.org | da912d6 | 2011-03-08 18:31:02 +0000 | [diff] [blame] | 316 | } |
commit-bot@chromium.org | 93a2e21 | 2013-07-23 23:16:03 +0000 | [diff] [blame] | 317 | |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 318 | static 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 Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 328 | SkPDFIndirectReference SkPDFMakeShader(SkPDFDocument* doc, |
| 329 | SkShader* shader, |
| 330 | const SkMatrix& canvasTransform, |
| 331 | const SkIRect& surfaceBBox, |
| 332 | SkColor paintColor) { |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 333 | 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 Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 339 | return SkPDFIndirectReference(); |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 340 | } |
| 341 | SkBitmap image; |
Hal Canary | ec25768 | 2017-07-06 08:37:02 -0400 | [diff] [blame] | 342 | SkPDFImageShaderKey key = { |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 343 | canvasTransform, |
| 344 | SkMatrix::I(), |
| 345 | surfaceBBox, |
| 346 | {{0, 0, 0, 0}, 0}, |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 347 | {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode}, |
| 348 | adjust_color(shader, paintColor)}; |
commit-bot@chromium.org | 93a2e21 | 2013-07-23 23:16:03 +0000 | [diff] [blame] | 349 | |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 350 | SkASSERT(shader->asAGradient(nullptr) == SkShader::kNone_GradientType) ; |
Hal Canary | 4f29c20 | 2017-07-18 10:28:31 -0400 | [diff] [blame] | 351 | if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) { |
| 352 | key.fBitmapKey = SkBitmapKeyFromImage(skimg); |
Hal Canary | 4ca9fa3 | 2018-12-21 16:15:01 -0500 | [diff] [blame] | 353 | SkPDFIndirectReference* shaderPtr = doc->fImageShaderMap.find(key); |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 354 | if (shaderPtr) { |
| 355 | return *shaderPtr; |
commit-bot@chromium.org | 93a2e21 | 2013-07-23 23:16:03 +0000 | [diff] [blame] | 356 | } |
Hal Canary | 9a3f554 | 2018-12-10 19:59:07 -0500 | [diff] [blame] | 357 | SkPDFIndirectReference pdfShader = make_image_shader(doc, key, skimg); |
Hal Canary | 4ca9fa3 | 2018-12-21 16:15:01 -0500 | [diff] [blame] | 358 | doc->fImageShaderMap.set(std::move(key), pdfShader); |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 359 | return pdfShader; |
commit-bot@chromium.org | 93a2e21 | 2013-07-23 23:16:03 +0000 | [diff] [blame] | 360 | } |
Hal Canary | 94fd66c | 2017-07-05 11:25:42 -0400 | [diff] [blame] | 361 | // Don't bother to de-dup fallback shader. |
Hal Canary | 7e872ca | 2017-07-19 15:51:18 -0400 | [diff] [blame] | 362 | return make_fallback_shader(doc, shader, canvasTransform, surfaceBBox, key.fPaintColor); |
commit-bot@chromium.org | 93a2e21 | 2013-07-23 23:16:03 +0000 | [diff] [blame] | 363 | } |