blob: 24ae2d6087ebd4da20775ca7710072190a42d8e3 [file] [log] [blame]
scroggo478652e2015-03-25 07:11:02 -07001/*
2 * Copyright 2015 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
mtklein748ca3b2015-01-15 10:56:12 -08008#include "DMSrcSink.h"
9#include "SamplePipeControllers.h"
msarett3d9d7a72015-10-21 10:27:10 -070010#include "SkAndroidCodec.h"
scroggof24f2242015-03-03 08:59:20 -080011#include "SkCodec.h"
msarett7f691442015-09-22 11:56:16 -070012#include "SkCodecTools.h"
mtkleina16e69e2015-05-05 11:38:45 -070013#include "SkCommonFlags.h"
mtkleinb3e5e4d2015-03-25 13:13:43 -070014#include "SkData.h"
mtklein748ca3b2015-01-15 10:56:12 -080015#include "SkDocument.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080016#include "SkError.h"
mtkleine44b5082015-05-07 10:53:34 -070017#include "SkFunction.h"
mtkleinb3e5e4d2015-03-25 13:13:43 -070018#include "SkImageGenerator.h"
mtklein748ca3b2015-01-15 10:56:12 -080019#include "SkMultiPictureDraw.h"
mtkleinad66f9b2015-02-13 15:11:10 -080020#include "SkNullCanvas.h"
mtklein748ca3b2015-01-15 10:56:12 -080021#include "SkOSFile.h"
mtkleinffa901a2015-03-16 10:38:07 -070022#include "SkPictureData.h"
mtklein748ca3b2015-01-15 10:56:12 -080023#include "SkPictureRecorder.h"
24#include "SkRandom.h"
mtkleind31c13d2015-05-05 12:59:56 -070025#include "SkRecordDraw.h"
26#include "SkRecorder.h"
mtklein2e2ea382015-10-16 10:29:41 -070027#include "SkRemote.h"
fmalita2aafe6f2015-02-06 12:51:10 -080028#include "SkSVGCanvas.h"
scroggoa1193e42015-01-21 12:09:53 -080029#include "SkStream.h"
mtklein449d9b72015-09-28 10:33:02 -070030#include "SkTLogic.h"
fmalita2aafe6f2015-02-06 12:51:10 -080031#include "SkXMLWriter.h"
msarette6dd0042015-10-09 11:07:34 -070032#include "SkSwizzler.h"
mtklein748ca3b2015-01-15 10:56:12 -080033
halcanary7e798182015-04-14 14:06:18 -070034DEFINE_bool(multiPage, false, "For document-type backends, render the source"
35 " into multiple pages");
36
mtkleinb3e5e4d2015-03-25 13:13:43 -070037static bool lazy_decode_bitmap(const void* src, size_t size, SkBitmap* dst) {
38 SkAutoTUnref<SkData> encoded(SkData::NewWithCopy(src, size));
reedd1146452015-09-25 06:56:57 -070039 return encoded && SkDEPRECATED_InstallDiscardablePixelRef(encoded, dst);
mtkleinb3e5e4d2015-03-25 13:13:43 -070040}
41
mtklein748ca3b2015-01-15 10:56:12 -080042namespace DM {
43
mtklein748ca3b2015-01-15 10:56:12 -080044GMSrc::GMSrc(skiagm::GMRegistry::Factory factory) : fFactory(factory) {}
45
46Error GMSrc::draw(SkCanvas* canvas) const {
halcanary96fcdcc2015-08-27 07:41:13 -070047 SkAutoTDelete<skiagm::GM> gm(fFactory(nullptr));
mtklein748ca3b2015-01-15 10:56:12 -080048 canvas->concat(gm->getInitialTransform());
49 gm->draw(canvas);
50 return "";
51}
52
53SkISize GMSrc::size() const {
halcanary96fcdcc2015-08-27 07:41:13 -070054 SkAutoTDelete<skiagm::GM> gm(fFactory(nullptr));
mtklein748ca3b2015-01-15 10:56:12 -080055 return gm->getISize();
56}
57
58Name GMSrc::name() const {
halcanary96fcdcc2015-08-27 07:41:13 -070059 SkAutoTDelete<skiagm::GM> gm(fFactory(nullptr));
mtklein748ca3b2015-01-15 10:56:12 -080060 return gm->getName();
61}
62
bsalomon4ee6bd82015-05-27 13:23:23 -070063void GMSrc::modifyGrContextOptions(GrContextOptions* options) const {
halcanary96fcdcc2015-08-27 07:41:13 -070064 SkAutoTDelete<skiagm::GM> gm(fFactory(nullptr));
bsalomon4ee6bd82015-05-27 13:23:23 -070065 gm->modifyGrContextOptions(options);
66}
67
mtklein748ca3b2015-01-15 10:56:12 -080068/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
69
msaretta5783ae2015-09-08 15:35:32 -070070BRDSrc::BRDSrc(Path path, SkBitmapRegionDecoderInterface::Strategy strategy, Mode mode,
71 CodecSrc::DstColorType dstColorType, uint32_t sampleSize)
72 : fPath(path)
73 , fStrategy(strategy)
74 , fMode(mode)
75 , fDstColorType(dstColorType)
76 , fSampleSize(sampleSize)
77{}
78
79bool BRDSrc::veto(SinkFlags flags) const {
80 // No need to test to non-raster or indirect backends.
81 return flags.type != SinkFlags::kRaster
82 || flags.approach != SinkFlags::kDirect;
83}
84
85static SkBitmapRegionDecoderInterface* create_brd(Path path,
86 SkBitmapRegionDecoderInterface::Strategy strategy) {
87 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
88 if (!encoded) {
89 return NULL;
90 }
91 return SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(new SkMemoryStream(encoded),
92 strategy);
93}
94
95Error BRDSrc::draw(SkCanvas* canvas) const {
96 SkColorType colorType = canvas->imageInfo().colorType();
97 if (kRGB_565_SkColorType == colorType &&
98 CodecSrc::kGetFromCanvas_DstColorType != fDstColorType) {
99 return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
100 }
101 switch (fDstColorType) {
102 case CodecSrc::kGetFromCanvas_DstColorType:
103 break;
104 case CodecSrc::kIndex8_Always_DstColorType:
105 colorType = kIndex_8_SkColorType;
106 break;
107 case CodecSrc::kGrayscale_Always_DstColorType:
108 colorType = kGray_8_SkColorType;
109 break;
110 }
111
112 SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(create_brd(fPath, fStrategy));
113 if (nullptr == brd.get()) {
114 return Error::Nonfatal(SkStringPrintf("Could not create brd for %s.", fPath.c_str()));
115 }
116
msarett04965c62015-10-12 10:24:38 -0700117 if (!brd->conversionSupported(colorType)) {
118 return Error::Nonfatal("Cannot convert to color type.\n");
119 }
120
msaretta5783ae2015-09-08 15:35:32 -0700121 const uint32_t width = brd->width();
122 const uint32_t height = brd->height();
123 // Visually inspecting very small output images is not necessary.
124 if ((width / fSampleSize <= 10 || height / fSampleSize <= 10) && 1 != fSampleSize) {
125 return Error::Nonfatal("Scaling very small images is uninteresting.");
126 }
127 switch (fMode) {
128 case kFullImage_Mode: {
129 SkAutoTDelete<SkBitmap> bitmap(brd->decodeRegion(0, 0, width, height, fSampleSize,
130 colorType));
131 if (nullptr == bitmap.get() || colorType != bitmap->colorType()) {
132 return Error::Nonfatal("Cannot convert to color type.\n");
133 }
134 canvas->drawBitmap(*bitmap, 0, 0);
135 return "";
136 }
137 case kDivisor_Mode: {
138 const uint32_t divisor = 2;
139 if (width < divisor || height < divisor) {
140 return Error::Nonfatal("Divisor is larger than image dimension.\n");
141 }
142
143 // Use a border to test subsets that extend outside the image.
144 // We will not allow the border to be larger than the image dimensions. Allowing
145 // these large borders causes off by one errors that indicate a problem with the
146 // test suite, not a problem with the implementation.
147 const uint32_t maxBorder = SkTMin(width, height) / (fSampleSize * divisor);
148 const uint32_t scaledBorder = SkTMin(5u, maxBorder);
149 const uint32_t unscaledBorder = scaledBorder * fSampleSize;
150
151 // We may need to clear the canvas to avoid uninitialized memory.
152 // Assume we are scaling a 780x780 image with sampleSize = 8.
153 // The output image should be 97x97.
154 // Each subset will be 390x390.
155 // Each scaled subset be 48x48.
156 // Four scaled subsets will only fill a 96x96 image.
157 // The bottom row and last column will not be touched.
158 // This is an unfortunate result of our rounding rules when scaling.
159 // Maybe we need to consider testing scaled subsets without trying to
160 // combine them to match the full scaled image? Or maybe this is the
161 // best we can do?
162 canvas->clear(0);
163
164 for (uint32_t x = 0; x < divisor; x++) {
165 for (uint32_t y = 0; y < divisor; y++) {
166 // Calculate the subset dimensions
167 uint32_t subsetWidth = width / divisor;
168 uint32_t subsetHeight = height / divisor;
169 const int left = x * subsetWidth;
170 const int top = y * subsetHeight;
171
172 // Increase the size of the last subset in each row or column, when the
173 // divisor does not divide evenly into the image dimensions
174 subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
175 subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
176
177 // Increase the size of the subset in order to have a border on each side
178 const int decodeLeft = left - unscaledBorder;
179 const int decodeTop = top - unscaledBorder;
180 const uint32_t decodeWidth = subsetWidth + unscaledBorder * 2;
181 const uint32_t decodeHeight = subsetHeight + unscaledBorder * 2;
182 SkAutoTDelete<SkBitmap> bitmap(brd->decodeRegion(decodeLeft,
183 decodeTop, decodeWidth, decodeHeight, fSampleSize, colorType));
184 if (nullptr == bitmap.get() || colorType != bitmap->colorType()) {
185 return Error::Nonfatal("Cannot convert to color type.\n");
186 }
187
188 canvas->drawBitmapRect(*bitmap,
189 SkRect::MakeXYWH((SkScalar) scaledBorder, (SkScalar) scaledBorder,
190 (SkScalar) (subsetWidth / fSampleSize),
191 (SkScalar) (subsetHeight / fSampleSize)),
192 SkRect::MakeXYWH((SkScalar) (left / fSampleSize),
193 (SkScalar) (top / fSampleSize),
194 (SkScalar) (subsetWidth / fSampleSize),
195 (SkScalar) (subsetHeight / fSampleSize)),
196 nullptr);
197 }
198 }
199 return "";
200 }
201 default:
202 SkASSERT(false);
203 return "Error: Should not be reached.\n";
204 }
205}
206
207SkISize BRDSrc::size() const {
208 SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(create_brd(fPath, fStrategy));
209 if (brd) {
210 return SkISize::Make(SkTMax(1, brd->width() / (int) fSampleSize),
211 SkTMax(1, brd->height() / (int) fSampleSize));
212 }
213 return SkISize::Make(0, 0);
214}
215
216static SkString get_scaled_name(const Path& path, float scale) {
217 return SkStringPrintf("%s_%.3f", SkOSPath::Basename(path.c_str()).c_str(), scale);
218}
219
220Name BRDSrc::name() const {
221 // We will replicate the names used by CodecSrc so that images can
222 // be compared in Gold.
223 if (1 == fSampleSize) {
224 return SkOSPath::Basename(fPath.c_str());
225 }
msarett7f691442015-09-22 11:56:16 -0700226 return get_scaled_name(fPath, get_scale_from_sample_size(fSampleSize));
msaretta5783ae2015-09-08 15:35:32 -0700227}
228
229/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
230
msarett0a242972015-06-11 14:27:27 -0700231CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType, float scale)
msarett438b2ad2015-04-09 12:43:10 -0700232 : fPath(path)
233 , fMode(mode)
234 , fDstColorType(dstColorType)
msarett0a242972015-06-11 14:27:27 -0700235 , fScale(scale)
msarett438b2ad2015-04-09 12:43:10 -0700236{}
mtklein748ca3b2015-01-15 10:56:12 -0800237
mtklein99cab4e2015-07-31 06:43:04 -0700238bool CodecSrc::veto(SinkFlags flags) const {
239 // No need to test decoding to non-raster or indirect backend.
mtkleine0effd62015-07-29 06:37:28 -0700240 // TODO: Once we implement GPU paths (e.g. JPEG YUV), we should use a deferred decode to
241 // let the GPU handle it.
mtklein99cab4e2015-07-31 06:43:04 -0700242 return flags.type != SinkFlags::kRaster
243 || flags.approach != SinkFlags::kDirect;
mtkleine0effd62015-07-29 06:37:28 -0700244}
scroggo9b77ddd2015-03-19 06:03:39 -0700245
msarett3d9d7a72015-10-21 10:27:10 -0700246bool get_decode_info(SkImageInfo* decodeInfo, const SkImageInfo& defaultInfo,
247 SkColorType canvasColorType, CodecSrc::DstColorType dstColorType) {
248 switch (dstColorType) {
249 case CodecSrc::kIndex8_Always_DstColorType:
250 if (kRGB_565_SkColorType == canvasColorType) {
251 return false;
252 }
253 *decodeInfo = defaultInfo.makeColorType(kIndex_8_SkColorType);
254 break;
255 case CodecSrc::kGrayscale_Always_DstColorType:
256 if (kRGB_565_SkColorType == canvasColorType) {
257 return false;
258 }
259 *decodeInfo = defaultInfo.makeColorType(kGray_8_SkColorType);
260 break;
261 default:
262 *decodeInfo = defaultInfo.makeColorType(canvasColorType);
263 break;
264 }
265
266 // FIXME: Currently we cannot draw unpremultiplied sources.
267 if (decodeInfo->alphaType() == kUnpremul_SkAlphaType) {
268 decodeInfo->makeAlphaType(kPremul_SkAlphaType);
269 }
270 return true;
271}
272
mtkleine0effd62015-07-29 06:37:28 -0700273Error CodecSrc::draw(SkCanvas* canvas) const {
mtklein75d98fd2015-01-18 07:05:01 -0800274 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
mtklein748ca3b2015-01-15 10:56:12 -0800275 if (!encoded) {
276 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
277 }
msarett3d9d7a72015-10-21 10:27:10 -0700278 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
msarett9e707a02015-09-01 14:57:57 -0700279 if (nullptr == codec.get()) {
280 return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str());
scroggo9b77ddd2015-03-19 06:03:39 -0700281 }
282
msarett3d9d7a72015-10-21 10:27:10 -0700283 SkImageInfo decodeInfo;
284 if (!get_decode_info(&decodeInfo, codec->getInfo(), canvas->imageInfo().colorType(),
285 fDstColorType)) {
286 return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
msarett438b2ad2015-04-09 12:43:10 -0700287 }
288
msarett0a242972015-06-11 14:27:27 -0700289 // Try to scale the image if it is desired
290 SkISize size = codec->getScaledDimensions(fScale);
291 if (size == decodeInfo.dimensions() && 1.0f != fScale) {
292 return Error::Nonfatal("Test without scaling is uninteresting.");
293 }
msarettb32758a2015-08-18 13:22:46 -0700294
295 // Visually inspecting very small output images is not necessary. We will
296 // cover these cases in unit testing.
297 if ((size.width() <= 10 || size.height() <= 10) && 1.0f != fScale) {
298 return Error::Nonfatal("Scaling very small images is uninteresting.");
299 }
msarett0a242972015-06-11 14:27:27 -0700300 decodeInfo = decodeInfo.makeWH(size.width(), size.height());
301
msarett438b2ad2015-04-09 12:43:10 -0700302 // Construct a color table for the decode if necessary
halcanary96fcdcc2015-08-27 07:41:13 -0700303 SkAutoTUnref<SkColorTable> colorTable(nullptr);
304 SkPMColor* colorPtr = nullptr;
305 int* colorCountPtr = nullptr;
msarett438b2ad2015-04-09 12:43:10 -0700306 int maxColors = 256;
307 if (kIndex_8_SkColorType == decodeInfo.colorType()) {
308 SkPMColor colors[256];
halcanary385fe4d2015-08-26 13:07:48 -0700309 colorTable.reset(new SkColorTable(colors, maxColors));
msarett438b2ad2015-04-09 12:43:10 -0700310 colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
311 colorCountPtr = &maxColors;
312 }
313
scroggo9b77ddd2015-03-19 06:03:39 -0700314 SkBitmap bitmap;
halcanary96fcdcc2015-08-27 07:41:13 -0700315 if (!bitmap.tryAllocPixels(decodeInfo, nullptr, colorTable.get())) {
scroggo9b77ddd2015-03-19 06:03:39 -0700316 return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
317 decodeInfo.width(), decodeInfo.height());
318 }
319
scroggo9c59ebc2015-03-25 13:48:49 -0700320 switch (fMode) {
msarett9e707a02015-09-01 14:57:57 -0700321 case kCodec_Mode: {
halcanary96fcdcc2015-08-27 07:41:13 -0700322 switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), nullptr,
msarett438b2ad2015-04-09 12:43:10 -0700323 colorPtr, colorCountPtr)) {
scroggoeb602a52015-07-09 08:16:03 -0700324 case SkCodec::kSuccess:
scroggo9c59ebc2015-03-25 13:48:49 -0700325 // We consider incomplete to be valid, since we should still decode what is
326 // available.
scroggoeb602a52015-07-09 08:16:03 -0700327 case SkCodec::kIncompleteInput:
scroggo9c59ebc2015-03-25 13:48:49 -0700328 break;
scroggoeb602a52015-07-09 08:16:03 -0700329 case SkCodec::kInvalidConversion:
scroggo9c59ebc2015-03-25 13:48:49 -0700330 return Error::Nonfatal("Incompatible colortype conversion");
331 default:
332 // Everything else is considered a failure.
333 return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
334 }
emmaleer97002062015-05-27 12:36:10 -0700335 canvas->drawBitmap(bitmap, 0, 0);
scroggo9c59ebc2015-03-25 13:48:49 -0700336 break;
emmaleer0a4c3cb2015-06-22 10:40:21 -0700337 }
scroggo9c59ebc2015-03-25 13:48:49 -0700338 case kScanline_Mode: {
scroggo46c57472015-09-30 08:57:13 -0700339 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
340 colorCountPtr)) {
msarett9e707a02015-09-01 14:57:57 -0700341 return Error::Nonfatal("Could not start scanline decoder");
scroggo9c59ebc2015-03-25 13:48:49 -0700342 }
scroggo1c005e42015-08-04 09:24:45 -0700343
msarette6dd0042015-10-09 11:07:34 -0700344 void* dst = bitmap.getAddr(0, 0);
345 size_t rowBytes = bitmap.rowBytes();
346 uint32_t height = decodeInfo.height();
scroggo46c57472015-09-30 08:57:13 -0700347 switch (codec->getScanlineOrder()) {
348 case SkCodec::kTopDown_SkScanlineOrder:
349 case SkCodec::kBottomUp_SkScanlineOrder:
350 case SkCodec::kNone_SkScanlineOrder:
msarette6dd0042015-10-09 11:07:34 -0700351 // We do not need to check the return value. On an incomplete
352 // image, memory will be filled with a default value.
353 codec->getScanlines(dst, height, rowBytes);
msarett10522ff2015-09-07 08:54:01 -0700354 break;
scroggo46c57472015-09-30 08:57:13 -0700355 case SkCodec::kOutOfOrder_SkScanlineOrder: {
msarett10522ff2015-09-07 08:54:01 -0700356 for (int y = 0; y < decodeInfo.height(); y++) {
msarette6dd0042015-10-09 11:07:34 -0700357 int dstY = codec->outputScanline(y);
msarett10522ff2015-09-07 08:54:01 -0700358 void* dstPtr = bitmap.getAddr(0, dstY);
msarette6dd0042015-10-09 11:07:34 -0700359 // We complete the loop, even if this call begins to fail
360 // due to an incomplete image. This ensures any uninitialized
361 // memory will be filled with the proper value.
362 codec->getScanlines(dstPtr, 1, bitmap.rowBytes());
msarett10522ff2015-09-07 08:54:01 -0700363 }
364 break;
365 }
366 }
367
emmaleer97002062015-05-27 12:36:10 -0700368 canvas->drawBitmap(bitmap, 0, 0);
369 break;
370 }
371 case kScanline_Subset_Mode: {
372 //this mode decodes the image in divisor*divisor subsets, using a scanline decoder
373 const int divisor = 2;
374 const int w = decodeInfo.width();
375 const int h = decodeInfo.height();
emmaleer97002062015-05-27 12:36:10 -0700376 if (divisor > w || divisor > h) {
msarett70542572015-06-19 07:44:05 -0700377 return Error::Nonfatal(SkStringPrintf("Cannot decode subset: divisor %d is too big"
378 "for %s with dimensions (%d x %d)", divisor, fPath.c_str(), w, h));
emmaleer97002062015-05-27 12:36:10 -0700379 }
380 const int subsetWidth = w/divisor;
381 const int subsetHeight = h/divisor;
382 // One of our subsets will be larger to contain any pixels that do not divide evenly.
383 const int extraX = w % divisor;
384 const int extraY = h % divisor;
385 /*
386 * if w or h are not evenly divided by divisor need to adjust width and height of end
387 * subsets to cover entire image.
388 * Add extraX and extraY to largestSubsetBm's width and height to adjust width
389 * and height of end subsets.
390 * subsetBm is extracted from largestSubsetBm.
391 * subsetBm's size is determined based on the current subset and may be larger for end
392 * subsets.
393 */
msarett0a242972015-06-11 14:27:27 -0700394 SkImageInfo largestSubsetDecodeInfo =
emmaleer97002062015-05-27 12:36:10 -0700395 decodeInfo.makeWH(subsetWidth + extraX, subsetHeight + extraY);
396 SkBitmap largestSubsetBm;
msarett9e707a02015-09-01 14:57:57 -0700397 if (!largestSubsetBm.tryAllocPixels(largestSubsetDecodeInfo, nullptr,
398 colorTable.get())) {
emmaleer97002062015-05-27 12:36:10 -0700399 return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
400 largestSubsetDecodeInfo.width(), largestSubsetDecodeInfo.height());
401 }
emmaleer97002062015-05-27 12:36:10 -0700402 for (int col = 0; col < divisor; col++) {
403 //currentSubsetWidth may be larger than subsetWidth for rightmost subsets
404 const int currentSubsetWidth = (col + 1 == divisor) ?
405 subsetWidth + extraX : subsetWidth;
406 const int x = col * subsetWidth;
407 for (int row = 0; row < divisor; row++) {
408 //currentSubsetHeight may be larger than subsetHeight for bottom subsets
409 const int currentSubsetHeight = (row + 1 == divisor) ?
410 subsetHeight + extraY : subsetHeight;
411 const int y = row * subsetHeight;
412 //create scanline decoder for each subset
msarettfdb47572015-10-13 12:50:14 -0700413 SkCodec::Options options;
414 SkIRect subset = SkIRect::MakeXYWH(x, 0, currentSubsetWidth, h);
415 options.fSubset = &subset;
416 // TODO (msarett): Support this mode for all scanline orderings.
417 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options,
418 colorPtr, colorCountPtr) ||
419 SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
emmaleer97002062015-05-27 12:36:10 -0700420 if (x == 0 && y == 0) {
421 //first try, image may not be compatible
msarett5406d6f2015-08-31 06:55:13 -0700422 return Error::Nonfatal("Could not start top-down scanline decoder");
emmaleer97002062015-05-27 12:36:10 -0700423 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700424 return "Error scanline decoder is nullptr";
emmaleer97002062015-05-27 12:36:10 -0700425 }
426 }
msarette6dd0042015-10-09 11:07:34 -0700427 // Skip to the first line of subset. We ignore the result value here.
428 // If the skip value fails, this will indicate an incomplete image.
429 // This means that the call to getScanlines() will also fail, but it
430 // will fill the buffer with a default value, so we can still draw the
431 // image.
432 codec->skipScanlines(y);
433
emmaleer97002062015-05-27 12:36:10 -0700434 //create and set size of subsetBm
435 SkBitmap subsetBm;
scroggo6634cbb2015-09-30 13:01:55 -0700436 SkIRect bounds = SkIRect::MakeWH(currentSubsetWidth, currentSubsetHeight);
emmaleer97002062015-05-27 12:36:10 -0700437 SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds));
msarettfdb47572015-10-13 12:50:14 -0700438 SkAutoLockPixels autolock(subsetBm, true);
439 codec->getScanlines(subsetBm.getAddr(0, 0), currentSubsetHeight,
440 subsetBm.rowBytes());
scroggo4358f132015-07-30 11:33:04 -0700441 subsetBm.notifyPixelsChanged();
emmaleer97002062015-05-27 12:36:10 -0700442 canvas->drawBitmap(subsetBm, SkIntToScalar(x), SkIntToScalar(y));
443 }
444 }
scroggo9c59ebc2015-03-25 13:48:49 -0700445 break;
446 }
msarett0a242972015-06-11 14:27:27 -0700447 case kStripe_Mode: {
448 const int height = decodeInfo.height();
449 // This value is chosen arbitrarily. We exercise more cases by choosing a value that
450 // does not align with image blocks.
451 const int stripeHeight = 37;
452 const int numStripes = (height + stripeHeight - 1) / stripeHeight;
453
454 // Decode odd stripes
scroggo46c57472015-09-30 08:57:13 -0700455 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
456 colorCountPtr)
457 || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
msarett9e707a02015-09-01 14:57:57 -0700458 // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
459 // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
460 // to run this test for image types that do not have this scanline ordering.
msarett5406d6f2015-08-31 06:55:13 -0700461 return Error::Nonfatal("Could not start top-down scanline decoder");
msarett0a242972015-06-11 14:27:27 -0700462 }
msarette6dd0042015-10-09 11:07:34 -0700463
msarett0a242972015-06-11 14:27:27 -0700464 for (int i = 0; i < numStripes; i += 2) {
465 // Skip a stripe
466 const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight);
msarette6dd0042015-10-09 11:07:34 -0700467 codec->skipScanlines(linesToSkip);
msarett0a242972015-06-11 14:27:27 -0700468
469 // Read a stripe
470 const int startY = (i + 1) * stripeHeight;
471 const int linesToRead = SkTMin(stripeHeight, height - startY);
472 if (linesToRead > 0) {
msarette6dd0042015-10-09 11:07:34 -0700473 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
msarett0a242972015-06-11 14:27:27 -0700474 }
475 }
476
477 // Decode even stripes
scroggo46c57472015-09-30 08:57:13 -0700478 const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo, nullptr,
479 colorPtr, colorCountPtr);
scroggo1c005e42015-08-04 09:24:45 -0700480 if (SkCodec::kSuccess != startResult) {
481 return "Failed to restart scanline decoder with same parameters.";
msarett0a242972015-06-11 14:27:27 -0700482 }
483 for (int i = 0; i < numStripes; i += 2) {
484 // Read a stripe
485 const int startY = i * stripeHeight;
486 const int linesToRead = SkTMin(stripeHeight, height - startY);
msarette6dd0042015-10-09 11:07:34 -0700487 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
msarett0a242972015-06-11 14:27:27 -0700488
489 // Skip a stripe
msarettf6db27e2015-06-12 09:34:04 -0700490 const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
491 if (linesToSkip > 0) {
msarette6dd0042015-10-09 11:07:34 -0700492 codec->skipScanlines(linesToSkip);
msarett0a242972015-06-11 14:27:27 -0700493 }
494 }
495 canvas->drawBitmap(bitmap, 0, 0);
emmaleer0a4c3cb2015-06-22 10:40:21 -0700496 break;
msarett0a242972015-06-11 14:27:27 -0700497 }
scroggob636b452015-07-22 07:16:20 -0700498 case kSubset_Mode: {
499 // Arbitrarily choose a divisor.
500 int divisor = 2;
501 // Total width/height of the image.
502 const int W = codec->getInfo().width();
503 const int H = codec->getInfo().height();
504 if (divisor > W || divisor > H) {
505 return Error::Nonfatal(SkStringPrintf("Cannot codec subset: divisor %d is too big "
506 "for %s with dimensions (%d x %d)", divisor,
507 fPath.c_str(), W, H));
508 }
509 // subset dimensions
510 // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
511 const int w = SkAlign2(W / divisor);
512 const int h = SkAlign2(H / divisor);
513 SkIRect subset;
514 SkCodec::Options opts;
515 opts.fSubset = &subset;
516 SkBitmap subsetBm;
517 // We will reuse pixel memory from bitmap.
518 void* pixels = bitmap.getPixels();
519 // Keep track of left and top (for drawing subsetBm into canvas). We could use
520 // fScale * x and fScale * y, but we want integers such that the next subset will start
521 // where the last one ended. So we'll add decodeInfo.width() and height().
522 int left = 0;
523 for (int x = 0; x < W; x += w) {
524 int top = 0;
525 for (int y = 0; y < H; y+= h) {
526 // Do not make the subset go off the edge of the image.
527 const int preScaleW = SkTMin(w, W - x);
528 const int preScaleH = SkTMin(h, H - y);
529 subset.setXYWH(x, y, preScaleW, preScaleH);
530 // And scale
531 // FIXME: Should we have a version of getScaledDimensions that takes a subset
532 // into account?
msarette6dd0042015-10-09 11:07:34 -0700533 decodeInfo = decodeInfo.makeWH(
534 SkTMax(1, SkScalarRoundToInt(preScaleW * fScale)),
535 SkTMax(1, SkScalarRoundToInt(preScaleH * fScale)));
scroggob636b452015-07-22 07:16:20 -0700536 size_t rowBytes = decodeInfo.minRowBytes();
537 if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes, colorTable.get(),
halcanary96fcdcc2015-08-27 07:41:13 -0700538 nullptr, nullptr)) {
scroggob636b452015-07-22 07:16:20 -0700539 return SkStringPrintf("could not install pixels for %s.", fPath.c_str());
540 }
541 const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
542 &opts, colorPtr, colorCountPtr);
543 switch (result) {
544 case SkCodec::kSuccess:
545 case SkCodec::kIncompleteInput:
546 break;
547 case SkCodec::kInvalidConversion:
548 if (0 == (x|y)) {
549 // First subset is okay to return unimplemented.
550 return Error::Nonfatal("Incompatible colortype conversion");
551 }
552 // If the first subset succeeded, a later one should not fail.
553 // fall through to failure
554 case SkCodec::kUnimplemented:
555 if (0 == (x|y)) {
556 // First subset is okay to return unimplemented.
557 return Error::Nonfatal("subset codec not supported");
558 }
559 // If the first subset succeeded, why would a later one fail?
560 // fall through to failure
561 default:
562 return SkStringPrintf("subset codec failed to decode (%d, %d, %d, %d) "
563 "from %s with dimensions (%d x %d)\t error %d",
564 x, y, decodeInfo.width(), decodeInfo.height(),
565 fPath.c_str(), W, H, result);
566 }
567 canvas->drawBitmap(subsetBm, SkIntToScalar(left), SkIntToScalar(top));
568 // translate by the scaled height.
569 top += decodeInfo.height();
570 }
571 // translate by the scaled width.
572 left += decodeInfo.width();
573 }
574 return "";
575 }
scroggo9b77ddd2015-03-19 06:03:39 -0700576 }
scroggo9c59ebc2015-03-25 13:48:49 -0700577 return "";
scroggo9b77ddd2015-03-19 06:03:39 -0700578}
579
580SkISize CodecSrc::size() const {
581 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
msarett3d9d7a72015-10-21 10:27:10 -0700582 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
scroggo7fac5af2015-09-30 11:33:12 -0700583 if (nullptr == codec) {
584 return SkISize::Make(0, 0);
585 }
586 return codec->getScaledDimensions(fScale);
scroggo9b77ddd2015-03-19 06:03:39 -0700587}
588
589Name CodecSrc::name() const {
msarett0a242972015-06-11 14:27:27 -0700590 if (1.0f == fScale) {
591 return SkOSPath::Basename(fPath.c_str());
msarett0a242972015-06-11 14:27:27 -0700592 }
msaretta5783ae2015-09-08 15:35:32 -0700593 return get_scaled_name(fPath, fScale);
scroggo9b77ddd2015-03-19 06:03:39 -0700594}
595
596/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
597
msarett3d9d7a72015-10-21 10:27:10 -0700598AndroidCodecSrc::AndroidCodecSrc(Path path, Mode mode, CodecSrc::DstColorType dstColorType,
599 int sampleSize)
600 : fPath(path)
601 , fMode(mode)
602 , fDstColorType(dstColorType)
603 , fSampleSize(sampleSize)
604{}
605
606bool AndroidCodecSrc::veto(SinkFlags flags) const {
607 // No need to test decoding to non-raster or indirect backend.
608 // TODO: Once we implement GPU paths (e.g. JPEG YUV), we should use a deferred decode to
609 // let the GPU handle it.
610 return flags.type != SinkFlags::kRaster
611 || flags.approach != SinkFlags::kDirect;
612}
613
614Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
615 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
616 if (!encoded) {
617 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
618 }
619 SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(encoded));
620 if (nullptr == codec.get()) {
621 return SkStringPrintf("Couldn't create android codec for %s.", fPath.c_str());
622 }
623
624 SkImageInfo decodeInfo;
625 if (!get_decode_info(&decodeInfo, codec->getInfo(), canvas->imageInfo().colorType(),
626 fDstColorType)) {
627 return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
628 }
629
630 // Scale the image if it is desired.
631 SkISize size = codec->getSampledDimensions(fSampleSize);
632
633 // Visually inspecting very small output images is not necessary. We will
634 // cover these cases in unit testing.
635 if ((size.width() <= 10 || size.height() <= 10) && 1 != fSampleSize) {
636 return Error::Nonfatal("Scaling very small images is uninteresting.");
637 }
638 decodeInfo = decodeInfo.makeWH(size.width(), size.height());
639
640 // Construct a color table for the decode if necessary
641 SkAutoTUnref<SkColorTable> colorTable(nullptr);
642 SkPMColor* colorPtr = nullptr;
643 int* colorCountPtr = nullptr;
644 int maxColors = 256;
645 if (kIndex_8_SkColorType == decodeInfo.colorType()) {
646 SkPMColor colors[256];
647 colorTable.reset(new SkColorTable(colors, maxColors));
648 colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
649 colorCountPtr = &maxColors;
650 }
651
652 SkBitmap bitmap;
653 if (!bitmap.tryAllocPixels(decodeInfo, nullptr, colorTable.get())) {
654 return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
655 decodeInfo.width(), decodeInfo.height());
656 }
657
658 // Create options for the codec.
659 SkAndroidCodec::AndroidOptions options;
660 options.fColorPtr = colorPtr;
661 options.fColorCount = colorCountPtr;
662 options.fSampleSize = fSampleSize;
663
664 switch (fMode) {
665 case kFullImage_Mode: {
666 switch (codec->getAndroidPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(),
667 &options)) {
668 case SkCodec::kSuccess:
669 case SkCodec::kIncompleteInput:
670 break;
671 case SkCodec::kInvalidConversion:
672 return Error::Nonfatal("Cannot convert to requested color type.\n");
673 default:
674 return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
675 }
676 canvas->drawBitmap(bitmap, 0, 0);
677 return "";
678 }
679 case kDivisor_Mode: {
680 const int width = codec->getInfo().width();
681 const int height = codec->getInfo().height();
682 const int divisor = 2;
683 if (width < divisor || height < divisor) {
684 return Error::Nonfatal("Divisor is larger than image dimension.\n");
685 }
686
687 // Rounding the size of the subsets may leave some pixels uninitialized on the bottom
688 // and right edges of the bitmap.
689 bitmap.eraseColor(0);
690 for (int x = 0; x < divisor; x++) {
691 for (int y = 0; y < divisor; y++) {
692 // Calculate the subset dimensions
693 int subsetWidth = width / divisor;
694 int subsetHeight = height / divisor;
695 const int left = x * subsetWidth;
696 const int top = y * subsetHeight;
697
698 // Increase the size of the last subset in each row or column, when the
699 // divisor does not divide evenly into the image dimensions
700 subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
701 subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
702 SkIRect subset = SkIRect::MakeXYWH(left, top, subsetWidth, subsetHeight);
703 if (!codec->getSupportedSubset(&subset)) {
704 return "Could not get supported subset to decode.\n";
705 }
706 options.fSubset = &subset;
707 void* pixels = bitmap.getAddr(subset.left() / fSampleSize,
708 subset.top() / fSampleSize);
709 SkISize scaledSubsetSize = codec->getSampledSubsetDimensions(fSampleSize,
710 subset);
711 SkImageInfo subsetDecodeInfo = decodeInfo.makeWH(scaledSubsetSize.width(),
712 scaledSubsetSize.height());
713
714 switch (codec->getAndroidPixels(subsetDecodeInfo, pixels, bitmap.rowBytes(),
715 &options)) {
716 case SkCodec::kSuccess:
717 case SkCodec::kIncompleteInput:
718 break;
719 case SkCodec::kInvalidConversion:
720 return Error::Nonfatal("Cannot convert to requested color type.\n");
721 default:
722 return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
723 }
724 }
725 }
726 canvas->drawBitmap(bitmap, 0, 0);
727 return "";
728 }
729 default:
730 SkASSERT(false);
731 return "Error: Should not be reached.\n";
732 }
733}
734
735SkISize AndroidCodecSrc::size() const {
736 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
737 SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(encoded));
738 if (nullptr == codec) {
739 return SkISize::Make(0, 0);
740 }
741 return codec->getSampledDimensions(fSampleSize);
742}
743
744Name AndroidCodecSrc::name() const {
745 // We will replicate the names used by CodecSrc so that images can
746 // be compared in Gold.
747 if (1 == fSampleSize) {
748 return SkOSPath::Basename(fPath.c_str());
749 }
750 return get_scaled_name(fPath, get_scale_from_sample_size(fSampleSize));
751}
752
753/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
754
scroggo9b77ddd2015-03-19 06:03:39 -0700755ImageSrc::ImageSrc(Path path, int divisor) : fPath(path), fDivisor(divisor) {}
756
mtklein99cab4e2015-07-31 06:43:04 -0700757bool ImageSrc::veto(SinkFlags flags) const {
758 // No need to test decoding to non-raster or indirect backend.
mtkleine0effd62015-07-29 06:37:28 -0700759 // TODO: Instead, use lazy decoding to allow the GPU to handle cases like YUV.
mtklein99cab4e2015-07-31 06:43:04 -0700760 return flags.type != SinkFlags::kRaster
761 || flags.approach != SinkFlags::kDirect;
mtkleine0effd62015-07-29 06:37:28 -0700762}
scroggo9b77ddd2015-03-19 06:03:39 -0700763
mtkleine0effd62015-07-29 06:37:28 -0700764Error ImageSrc::draw(SkCanvas* canvas) const {
scroggo9b77ddd2015-03-19 06:03:39 -0700765 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
766 if (!encoded) {
767 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
768 }
mtkleine0effd62015-07-29 06:37:28 -0700769 const SkColorType dstColorType = canvas->imageInfo().colorType();
mtkleinedc93bc2015-01-30 13:22:23 -0800770 if (fDivisor == 0) {
mtklein748ca3b2015-01-15 10:56:12 -0800771 // Decode the full image.
772 SkBitmap bitmap;
scroggo9b77ddd2015-03-19 06:03:39 -0700773 if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap,
774 dstColorType, SkImageDecoder::kDecodePixels_Mode)) {
775 return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
776 }
777 if (kRGB_565_SkColorType == dstColorType && !bitmap.isOpaque()) {
778 // Do not draw a bitmap with alpha to a destination without alpha.
779 return Error::Nonfatal("Uninteresting to decode image with alpha into 565.");
mtklein748ca3b2015-01-15 10:56:12 -0800780 }
halcanary96fcdcc2015-08-27 07:41:13 -0700781 encoded.reset((SkData*)nullptr); // Might as well drop this when we're done with it.
mtklein748ca3b2015-01-15 10:56:12 -0800782 canvas->drawBitmap(bitmap, 0,0);
783 return "";
784 }
mtkleinedc93bc2015-01-30 13:22:23 -0800785 // Decode subsets. This is a little involved.
scroggoa1193e42015-01-21 12:09:53 -0800786 SkAutoTDelete<SkMemoryStream> stream(new SkMemoryStream(encoded));
787 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream.get()));
mtklein748ca3b2015-01-15 10:56:12 -0800788 if (!decoder) {
789 return SkStringPrintf("Can't find a good decoder for %s.", fPath.c_str());
790 }
scroggoa1193e42015-01-21 12:09:53 -0800791 stream->rewind();
mtklein748ca3b2015-01-15 10:56:12 -0800792 int w,h;
msarett70542572015-06-19 07:44:05 -0700793 if (!decoder->buildTileIndex(stream.detach(), &w, &h)) {
mtklein4089ef72015-03-05 08:40:28 -0800794 return Error::Nonfatal("Subset decoding not supported.");
mtklein748ca3b2015-01-15 10:56:12 -0800795 }
mtkleinedc93bc2015-01-30 13:22:23 -0800796
797 // Divide the image into subsets that cover the entire image.
798 if (fDivisor > w || fDivisor > h) {
msarett70542572015-06-19 07:44:05 -0700799 return Error::Nonfatal(SkStringPrintf("Cannot decode subset: divisor %d is too big"
800 "for %s with dimensions (%d x %d)", fDivisor, fPath.c_str(), w, h));
mtkleinedc93bc2015-01-30 13:22:23 -0800801 }
802 const int subsetWidth = w / fDivisor,
803 subsetHeight = h / fDivisor;
804 for (int y = 0; y < h; y += subsetHeight) {
805 for (int x = 0; x < w; x += subsetWidth) {
806 SkBitmap subset;
807 SkIRect rect = SkIRect::MakeXYWH(x, y, subsetWidth, subsetHeight);
808 if (!decoder->decodeSubset(&subset, rect, dstColorType)) {
809 return SkStringPrintf("Could not decode subset (%d, %d, %d, %d).",
810 x, y, x+subsetWidth, y+subsetHeight);
811 }
scroggo56e25dd2015-03-05 11:46:40 -0800812 if (kRGB_565_SkColorType == dstColorType && !subset.isOpaque()) {
813 // Do not draw a bitmap with alpha to a destination without alpha.
814 // This is not an error, but there is nothing interesting to show.
815
816 // This should only happen on the first iteration through the loop.
817 SkASSERT(0 == x && 0 == y);
818
819 return Error::Nonfatal("Uninteresting to decode image with alpha into 565.");
820 }
mtkleinedc93bc2015-01-30 13:22:23 -0800821 canvas->drawBitmap(subset, SkIntToScalar(x), SkIntToScalar(y));
mtklein748ca3b2015-01-15 10:56:12 -0800822 }
mtklein748ca3b2015-01-15 10:56:12 -0800823 }
824 return "";
825}
826
827SkISize ImageSrc::size() const {
mtklein75d98fd2015-01-18 07:05:01 -0800828 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
scroggo9b77ddd2015-03-19 06:03:39 -0700829 SkBitmap bitmap;
830 if (!encoded || !SkImageDecoder::DecodeMemory(encoded->data(),
831 encoded->size(),
832 &bitmap,
833 kUnknown_SkColorType,
834 SkImageDecoder::kDecodeBounds_Mode)) {
835 return SkISize::Make(0,0);
mtklein748ca3b2015-01-15 10:56:12 -0800836 }
scroggo9b77ddd2015-03-19 06:03:39 -0700837 return bitmap.dimensions();
mtklein748ca3b2015-01-15 10:56:12 -0800838}
839
mtklein9264a952015-01-20 10:11:53 -0800840Name ImageSrc::name() const {
mtkleinedc93bc2015-01-30 13:22:23 -0800841 return SkOSPath::Basename(fPath.c_str());
mtklein9264a952015-01-20 10:11:53 -0800842}
mtklein748ca3b2015-01-15 10:56:12 -0800843
844/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
845
mtkleinf4ba3212015-01-28 15:32:24 -0800846static const SkRect kSKPViewport = {0,0, 1000,1000};
847
mtklein8d17a132015-01-30 11:42:31 -0800848SKPSrc::SKPSrc(Path path) : fPath(path) {}
mtklein748ca3b2015-01-15 10:56:12 -0800849
850Error SKPSrc::draw(SkCanvas* canvas) const {
scroggoa1193e42015-01-21 12:09:53 -0800851 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(fPath.c_str()));
mtklein75d98fd2015-01-18 07:05:01 -0800852 if (!stream) {
mtklein748ca3b2015-01-15 10:56:12 -0800853 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
854 }
mtkleinb3e5e4d2015-03-25 13:13:43 -0700855 SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(stream, &lazy_decode_bitmap));
mtklein75d98fd2015-01-18 07:05:01 -0800856 if (!pic) {
857 return SkStringPrintf("Couldn't decode %s as a picture.", fPath.c_str());
858 }
halcanary96fcdcc2015-08-27 07:41:13 -0700859 stream.reset((SkStream*)nullptr); // Might as well drop this when we're done with it.
joshualitt7c3a2f82015-03-31 13:32:05 -0700860
mtkleinf4ba3212015-01-28 15:32:24 -0800861 canvas->clipRect(kSKPViewport);
mtklein748ca3b2015-01-15 10:56:12 -0800862 canvas->drawPicture(pic);
863 return "";
864}
865
866SkISize SKPSrc::size() const {
mtkleinffa901a2015-03-16 10:38:07 -0700867 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(fPath.c_str()));
868 if (!stream) {
869 return SkISize::Make(0,0);
870 }
871 SkPictInfo info;
872 if (!SkPicture::InternalOnly_StreamIsSKP(stream, &info)) {
873 return SkISize::Make(0,0);
874 }
875 SkRect viewport = kSKPViewport;
876 if (!viewport.intersect(info.fCullRect)) {
877 return SkISize::Make(0,0);
878 }
879 return viewport.roundOut().size();
mtklein748ca3b2015-01-15 10:56:12 -0800880}
881
882Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
883
884/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
885
mtkleinad66f9b2015-02-13 15:11:10 -0800886Error NullSink::draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const {
887 SkAutoTDelete<SkCanvas> canvas(SkCreateNullCanvas());
888 return src.draw(canvas);
889}
890
891/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
892
mtkleinb9eb4ac2015-02-02 18:26:03 -0800893DEFINE_bool(gpuStats, false, "Append GPU stats to the log for each GPU task?");
894
mtklein82d28432015-01-15 12:46:02 -0800895GPUSink::GPUSink(GrContextFactory::GLContextType ct,
896 GrGLStandard api,
897 int samples,
bsalomonafcd7cd2015-08-31 12:39:41 -0700898 bool diText,
mtklein82d28432015-01-15 12:46:02 -0800899 bool threaded)
mtklein748ca3b2015-01-15 10:56:12 -0800900 : fContextType(ct)
901 , fGpuAPI(api)
902 , fSampleCount(samples)
bsalomonafcd7cd2015-08-31 12:39:41 -0700903 , fUseDIText(diText)
mtklein82d28432015-01-15 12:46:02 -0800904 , fThreaded(threaded) {}
mtklein748ca3b2015-01-15 10:56:12 -0800905
906int GPUSink::enclave() const {
mtklein55e88b22015-01-21 15:50:13 -0800907 return fThreaded ? kAnyThread_Enclave : kGPU_Enclave;
mtklein748ca3b2015-01-15 10:56:12 -0800908}
909
joshualitt5f5a8d72015-02-25 14:09:45 -0800910void PreAbandonGpuContextErrorHandler(SkError, void*) {}
911
mtkleinb9eb4ac2015-02-02 18:26:03 -0800912Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const {
bsalomon4ee6bd82015-05-27 13:23:23 -0700913 GrContextOptions options;
914 src.modifyGrContextOptions(&options);
915
916 GrContextFactory factory(options);
mtkleinf4ba3212015-01-28 15:32:24 -0800917 const SkISize size = src.size();
mtklein748ca3b2015-01-15 10:56:12 -0800918 const SkImageInfo info =
919 SkImageInfo::Make(size.width(), size.height(), kN32_SkColorType, kPremul_SkAlphaType);
920 SkAutoTUnref<SkSurface> surface(
bsalomonafcd7cd2015-08-31 12:39:41 -0700921 NewGpuSurface(&factory, fContextType, fGpuAPI, info, fSampleCount, fUseDIText));
mtklein748ca3b2015-01-15 10:56:12 -0800922 if (!surface) {
923 return "Could not create a surface.";
924 }
joshualitt5f5a8d72015-02-25 14:09:45 -0800925 if (FLAGS_preAbandonGpuContext) {
halcanary96fcdcc2015-08-27 07:41:13 -0700926 SkSetErrorCallback(&PreAbandonGpuContextErrorHandler, nullptr);
joshualitt5f5a8d72015-02-25 14:09:45 -0800927 factory.abandonContexts();
928 }
mtklein748ca3b2015-01-15 10:56:12 -0800929 SkCanvas* canvas = surface->getCanvas();
930 Error err = src.draw(canvas);
931 if (!err.isEmpty()) {
932 return err;
933 }
934 canvas->flush();
mtkleinb9eb4ac2015-02-02 18:26:03 -0800935 if (FLAGS_gpuStats) {
936 canvas->getGrContext()->dumpCacheStats(log);
937 canvas->getGrContext()->dumpGpuStats(log);
938 }
mtklein748ca3b2015-01-15 10:56:12 -0800939 dst->allocPixels(info);
joshualitt5f5a8d72015-02-25 14:09:45 -0800940 canvas->readPixels(dst, 0, 0);
mtklein55e88b22015-01-21 15:50:13 -0800941 if (FLAGS_abandonGpuContext) {
942 factory.abandonContexts();
943 }
mtklein748ca3b2015-01-15 10:56:12 -0800944 return "";
945}
946
947/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
948
halcanary47ef4d52015-03-03 09:13:09 -0800949static Error draw_skdocument(const Src& src, SkDocument* doc, SkWStream* dst) {
950 // Print the given DM:Src to a document, breaking on 8.5x11 pages.
951 SkASSERT(doc);
halcanaryfd4a9932015-01-28 11:45:58 -0800952 int width = src.size().width(),
953 height = src.size().height();
954
halcanary7e798182015-04-14 14:06:18 -0700955 if (FLAGS_multiPage) {
956 const int kLetterWidth = 612, // 8.5 * 72
957 kLetterHeight = 792; // 11 * 72
958 const SkRect letter = SkRect::MakeWH(SkIntToScalar(kLetterWidth),
959 SkIntToScalar(kLetterHeight));
halcanaryfd4a9932015-01-28 11:45:58 -0800960
halcanary7e798182015-04-14 14:06:18 -0700961 int xPages = ((width - 1) / kLetterWidth) + 1;
962 int yPages = ((height - 1) / kLetterHeight) + 1;
halcanaryfd4a9932015-01-28 11:45:58 -0800963
halcanary7e798182015-04-14 14:06:18 -0700964 for (int y = 0; y < yPages; ++y) {
965 for (int x = 0; x < xPages; ++x) {
966 int w = SkTMin(kLetterWidth, width - (x * kLetterWidth));
967 int h = SkTMin(kLetterHeight, height - (y * kLetterHeight));
968 SkCanvas* canvas =
969 doc->beginPage(SkIntToScalar(w), SkIntToScalar(h));
970 if (!canvas) {
halcanary96fcdcc2015-08-27 07:41:13 -0700971 return "SkDocument::beginPage(w,h) returned nullptr";
halcanary7e798182015-04-14 14:06:18 -0700972 }
973 canvas->clipRect(letter);
974 canvas->translate(-letter.width() * x, -letter.height() * y);
975 Error err = src.draw(canvas);
976 if (!err.isEmpty()) {
977 return err;
978 }
979 doc->endPage();
djsollen2ab90002015-04-03 06:38:31 -0700980 }
halcanaryfd4a9932015-01-28 11:45:58 -0800981 }
halcanary7e798182015-04-14 14:06:18 -0700982 } else {
983 SkCanvas* canvas =
984 doc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
985 if (!canvas) {
halcanary96fcdcc2015-08-27 07:41:13 -0700986 return "SkDocument::beginPage(w,h) returned nullptr";
halcanary7e798182015-04-14 14:06:18 -0700987 }
988 Error err = src.draw(canvas);
989 if (!err.isEmpty()) {
990 return err;
991 }
992 doc->endPage();
mtklein748ca3b2015-01-15 10:56:12 -0800993 }
halcanary7e798182015-04-14 14:06:18 -0700994 if (!doc->close()) {
995 return "SkDocument::close() returned false";
996 }
halcanaryfd4a9932015-01-28 11:45:58 -0800997 dst->flush();
mtklein748ca3b2015-01-15 10:56:12 -0800998 return "";
999}
1000
halcanaryc11c62f2015-09-28 11:51:54 -07001001PDFSink::PDFSink(const char* rasterizer) : fRasterizer(rasterizer) {}
halcanary47ef4d52015-03-03 09:13:09 -08001002
1003Error PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1004 SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(dst));
1005 if (!doc) {
halcanary96fcdcc2015-08-27 07:41:13 -07001006 return "SkDocument::CreatePDF() returned nullptr";
halcanary47ef4d52015-03-03 09:13:09 -08001007 }
halcanaryf12a1672015-09-23 12:45:49 -07001008 SkTArray<SkDocument::Attribute> info;
1009 info.emplace_back(SkString("Title"), src.name());
1010 info.emplace_back(SkString("Subject"),
1011 SkString("rendering correctness test"));
1012 info.emplace_back(SkString("Creator"), SkString("Skia/DM"));
halcanaryc11c62f2015-09-28 11:51:54 -07001013
1014 info.emplace_back(SkString("Keywords"),
1015 SkStringPrintf("Rasterizer:%s;", fRasterizer));
halcanaryf12a1672015-09-23 12:45:49 -07001016 doc->setMetadata(info, nullptr, nullptr);
halcanary47ef4d52015-03-03 09:13:09 -08001017 return draw_skdocument(src, doc.get(), dst);
1018}
1019
1020/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1021
1022XPSSink::XPSSink() {}
1023
1024Error XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1025 SkAutoTUnref<SkDocument> doc(SkDocument::CreateXPS(dst));
1026 if (!doc) {
halcanary96fcdcc2015-08-27 07:41:13 -07001027 return "SkDocument::CreateXPS() returned nullptr";
halcanary47ef4d52015-03-03 09:13:09 -08001028 }
1029 return draw_skdocument(src, doc.get(), dst);
1030}
mtklein748ca3b2015-01-15 10:56:12 -08001031/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1032
mtklein9c3f17d2015-01-28 11:35:18 -08001033SKPSink::SKPSink() {}
1034
mtkleinb9eb4ac2015-02-02 18:26:03 -08001035Error SKPSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
mtklein9c3f17d2015-01-28 11:35:18 -08001036 SkSize size;
1037 size = src.size();
1038 SkPictureRecorder recorder;
1039 Error err = src.draw(recorder.beginRecording(size.width(), size.height()));
1040 if (!err.isEmpty()) {
1041 return err;
1042 }
1043 SkAutoTUnref<SkPicture> pic(recorder.endRecording());
1044 pic->serialize(dst);
1045 return "";
1046}
1047
1048/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1049
mtklein8a4527e2015-01-31 20:00:58 -08001050SVGSink::SVGSink() {}
1051
mtkleinb9eb4ac2015-02-02 18:26:03 -08001052Error SVGSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
halcanary385fe4d2015-08-26 13:07:48 -07001053 SkAutoTDelete<SkXMLWriter> xmlWriter(new SkXMLStreamWriter(dst));
fmalita2aafe6f2015-02-06 12:51:10 -08001054 SkAutoTUnref<SkCanvas> canvas(SkSVGCanvas::Create(
1055 SkRect::MakeWH(SkIntToScalar(src.size().width()), SkIntToScalar(src.size().height())),
1056 xmlWriter));
1057 return src.draw(canvas);
mtklein8a4527e2015-01-31 20:00:58 -08001058}
1059
1060/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1061
mtklein748ca3b2015-01-15 10:56:12 -08001062RasterSink::RasterSink(SkColorType colorType) : fColorType(colorType) {}
1063
mtkleinb9eb4ac2015-02-02 18:26:03 -08001064Error RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const {
mtkleinf4ba3212015-01-28 15:32:24 -08001065 const SkISize size = src.size();
mtklein748ca3b2015-01-15 10:56:12 -08001066 // If there's an appropriate alpha type for this color type, use it, otherwise use premul.
1067 SkAlphaType alphaType = kPremul_SkAlphaType;
1068 (void)SkColorTypeValidateAlphaType(fColorType, alphaType, &alphaType);
1069
1070 dst->allocPixels(SkImageInfo::Make(size.width(), size.height(), fColorType, alphaType));
1071 dst->eraseColor(SK_ColorTRANSPARENT);
1072 SkCanvas canvas(*dst);
1073 return src.draw(&canvas);
1074}
1075
1076/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1077
mtkleina16e69e2015-05-05 11:38:45 -07001078// Handy for front-patching a Src. Do whatever up-front work you need, then call draw_to_canvas(),
mtkleine44b5082015-05-07 10:53:34 -07001079// passing the Sink draw() arguments, a size, and a function draws into an SkCanvas.
mtkleina16e69e2015-05-05 11:38:45 -07001080// Several examples below.
1081
mtkleina16e69e2015-05-05 11:38:45 -07001082static Error draw_to_canvas(Sink* sink, SkBitmap* bitmap, SkWStream* stream, SkString* log,
mtkleine44b5082015-05-07 10:53:34 -07001083 SkISize size, SkFunction<Error(SkCanvas*)> draw) {
mtkleina16e69e2015-05-05 11:38:45 -07001084 class ProxySrc : public Src {
1085 public:
mtkleine44b5082015-05-07 10:53:34 -07001086 ProxySrc(SkISize size, SkFunction<Error(SkCanvas*)> draw) : fSize(size), fDraw(draw) {}
mtkleina16e69e2015-05-05 11:38:45 -07001087 Error draw(SkCanvas* canvas) const override { return fDraw(canvas); }
1088 Name name() const override { sk_throw(); return ""; } // Won't be called.
1089 SkISize size() const override { return fSize; }
1090 private:
mtkleine44b5082015-05-07 10:53:34 -07001091 SkISize fSize;
1092 SkFunction<Error(SkCanvas*)> fDraw;
mtkleina16e69e2015-05-05 11:38:45 -07001093 };
1094 return sink->draw(ProxySrc(size, draw), bitmap, stream, log);
1095}
1096
1097/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1098
mtkleind603b222015-02-17 11:13:33 -08001099static SkISize auto_compute_translate(SkMatrix* matrix, int srcW, int srcH) {
1100 SkRect bounds = SkRect::MakeIWH(srcW, srcH);
1101 matrix->mapRect(&bounds);
1102 matrix->postTranslate(-bounds.x(), -bounds.y());
1103 return SkISize::Make(SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height()));
1104}
1105
mtklein78829242015-05-06 07:54:07 -07001106ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
mtklein748ca3b2015-01-15 10:56:12 -08001107
mtkleinb9eb4ac2015-02-02 18:26:03 -08001108Error ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
mtkleina16e69e2015-05-05 11:38:45 -07001109 SkMatrix matrix = fMatrix;
1110 SkISize size = auto_compute_translate(&matrix, src.size().width(), src.size().height());
1111 return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
1112 canvas->concat(matrix);
1113 return src.draw(canvas);
1114 });
mtklein748ca3b2015-01-15 10:56:12 -08001115}
1116
mtkleind603b222015-02-17 11:13:33 -08001117// Undoes any flip or 90 degree rotate without changing the scale of the bitmap.
1118// This should be pixel-preserving.
mtklein78829242015-05-06 07:54:07 -07001119ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
mtkleind603b222015-02-17 11:13:33 -08001120
1121Error ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1122 Error err = fSink->draw(src, bitmap, stream, log);
1123 if (!err.isEmpty()) {
1124 return err;
1125 }
1126
1127 SkMatrix inverse;
1128 if (!fMatrix.rectStaysRect() || !fMatrix.invert(&inverse)) {
1129 return "Cannot upright --matrix.";
1130 }
1131 SkMatrix upright = SkMatrix::I();
1132 upright.setScaleX(SkScalarSignAsScalar(inverse.getScaleX()));
1133 upright.setScaleY(SkScalarSignAsScalar(inverse.getScaleY()));
1134 upright.setSkewX(SkScalarSignAsScalar(inverse.getSkewX()));
1135 upright.setSkewY(SkScalarSignAsScalar(inverse.getSkewY()));
1136
1137 SkBitmap uprighted;
1138 SkISize size = auto_compute_translate(&upright, bitmap->width(), bitmap->height());
1139 uprighted.allocPixels(bitmap->info().makeWH(size.width(), size.height()));
1140
1141 SkCanvas canvas(uprighted);
1142 canvas.concat(upright);
1143 SkPaint paint;
1144 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1145 canvas.drawBitmap(*bitmap, 0, 0, &paint);
1146
1147 *bitmap = uprighted;
1148 bitmap->lockPixels();
1149 return "";
1150}
1151
mtklein748ca3b2015-01-15 10:56:12 -08001152/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1153
mtkleinb9eb4ac2015-02-02 18:26:03 -08001154Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
mtkleina16e69e2015-05-05 11:38:45 -07001155 auto size = src.size();
1156 return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
1157 PipeController controller(canvas, &SkImageDecoder::DecodeMemory);
1158 SkGPipeWriter pipe;
reed451af502015-08-19 08:18:04 -07001159 const uint32_t kFlags = 0;
mtkleina16e69e2015-05-05 11:38:45 -07001160 return src.draw(pipe.startRecording(&controller, kFlags, size.width(), size.height()));
1161 });
mtklein748ca3b2015-01-15 10:56:12 -08001162}
1163
mtklein2e2ea382015-10-16 10:29:41 -07001164Error ViaRemote::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1165 return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
1166 SkAutoTDelete<SkRemote::Cache> cache(fCache ? SkRemote::Cache::CreateAlwaysCache()
1167 : SkRemote::Cache::CreateNeverCache());
1168 SkRemote::Server server(canvas);
1169 SkRemote::Client client(cache, &server);
1170 return src.draw(&client);
1171 });
1172}
1173
mtklein748ca3b2015-01-15 10:56:12 -08001174/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
mtkleina16e69e2015-05-05 11:38:45 -07001175
mtkleina16e69e2015-05-05 11:38:45 -07001176Error ViaSerialization::draw(
1177 const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
mtklein748ca3b2015-01-15 10:56:12 -08001178 // Record our Src into a picture.
mtkleina16e69e2015-05-05 11:38:45 -07001179 auto size = src.size();
mtklein748ca3b2015-01-15 10:56:12 -08001180 SkPictureRecorder recorder;
mtkleina16e69e2015-05-05 11:38:45 -07001181 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1182 SkIntToScalar(size.height())));
mtklein748ca3b2015-01-15 10:56:12 -08001183 if (!err.isEmpty()) {
1184 return err;
1185 }
1186 SkAutoTUnref<SkPicture> pic(recorder.endRecording());
1187
1188 // Serialize it and then deserialize it.
1189 SkDynamicMemoryWStream wStream;
1190 pic->serialize(&wStream);
scroggoa1193e42015-01-21 12:09:53 -08001191 SkAutoTDelete<SkStream> rStream(wStream.detachAsStream());
mtkleinb3e5e4d2015-03-25 13:13:43 -07001192 SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rStream, &lazy_decode_bitmap));
mtklein748ca3b2015-01-15 10:56:12 -08001193
mtkleina16e69e2015-05-05 11:38:45 -07001194 return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
1195 canvas->drawPicture(deserialized);
1196 return "";
1197 });
mtklein748ca3b2015-01-15 10:56:12 -08001198}
1199
1200/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1201
1202ViaTiles::ViaTiles(int w, int h, SkBBHFactory* factory, Sink* sink)
mtklein78829242015-05-06 07:54:07 -07001203 : Via(sink)
1204 , fW(w)
mtklein748ca3b2015-01-15 10:56:12 -08001205 , fH(h)
mtklein78829242015-05-06 07:54:07 -07001206 , fFactory(factory) {}
mtklein748ca3b2015-01-15 10:56:12 -08001207
mtkleinb9eb4ac2015-02-02 18:26:03 -08001208Error ViaTiles::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
mtkleina16e69e2015-05-05 11:38:45 -07001209 auto size = src.size();
mtklein748ca3b2015-01-15 10:56:12 -08001210 SkPictureRecorder recorder;
mtkleina16e69e2015-05-05 11:38:45 -07001211 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1212 SkIntToScalar(size.height()),
1213 fFactory.get()));
mtklein748ca3b2015-01-15 10:56:12 -08001214 if (!err.isEmpty()) {
1215 return err;
1216 }
mtkleinb7e8d692015-04-07 08:30:32 -07001217 SkAutoTUnref<SkPicture> pic(recorder.endRecordingAsPicture());
mtklein748ca3b2015-01-15 10:56:12 -08001218
mtkleina16e69e2015-05-05 11:38:45 -07001219 return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
1220 const int xTiles = (size.width() + fW - 1) / fW,
1221 yTiles = (size.height() + fH - 1) / fH;
1222 SkMultiPictureDraw mpd(xTiles*yTiles);
1223 SkTDArray<SkSurface*> surfaces;
1224 surfaces.setReserve(xTiles*yTiles);
mtklein748ca3b2015-01-15 10:56:12 -08001225
mtkleina16e69e2015-05-05 11:38:45 -07001226 SkImageInfo info = canvas->imageInfo().makeWH(fW, fH);
1227 for (int j = 0; j < yTiles; j++) {
1228 for (int i = 0; i < xTiles; i++) {
1229 // This lets our ultimate Sink determine the best kind of surface.
1230 // E.g., if it's a GpuSink, the surfaces and images are textures.
1231 SkSurface* s = canvas->newSurface(info);
1232 if (!s) {
1233 s = SkSurface::NewRaster(info); // Some canvases can't create surfaces.
mtklein748ca3b2015-01-15 10:56:12 -08001234 }
mtkleina16e69e2015-05-05 11:38:45 -07001235 surfaces.push(s);
1236 SkCanvas* c = s->getCanvas();
1237 c->translate(SkIntToScalar(-i * fW),
1238 SkIntToScalar(-j * fH)); // Line up the canvas with this tile.
1239 mpd.add(c, pic);
mtklein748ca3b2015-01-15 10:56:12 -08001240 }
mtklein748ca3b2015-01-15 10:56:12 -08001241 }
mtkleina16e69e2015-05-05 11:38:45 -07001242 mpd.draw();
1243 for (int j = 0; j < yTiles; j++) {
1244 for (int i = 0; i < xTiles; i++) {
1245 SkAutoTUnref<SkImage> image(surfaces[i+xTiles*j]->newImageSnapshot());
1246 canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH));
1247 }
1248 }
1249 surfaces.unrefAll();
1250 return "";
1251 });
mtklein748ca3b2015-01-15 10:56:12 -08001252}
1253
mtkleinb7e8d692015-04-07 08:30:32 -07001254/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1255
mtkleinb7e8d692015-04-07 08:30:32 -07001256// Draw the Src into two pictures, then draw the second picture into the wrapped Sink.
1257// This tests that any shortcuts we may take while recording that second picture are legal.
1258Error ViaSecondPicture::draw(
1259 const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
mtkleina16e69e2015-05-05 11:38:45 -07001260 auto size = src.size();
1261 return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) -> Error {
1262 SkPictureRecorder recorder;
1263 SkAutoTUnref<SkPicture> pic;
1264 for (int i = 0; i < 2; i++) {
1265 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1266 SkIntToScalar(size.height())));
1267 if (!err.isEmpty()) {
1268 return err;
mtkleinb7e8d692015-04-07 08:30:32 -07001269 }
mtkleina16e69e2015-05-05 11:38:45 -07001270 pic.reset(recorder.endRecordingAsPicture());
mtkleinb7e8d692015-04-07 08:30:32 -07001271 }
mtkleina16e69e2015-05-05 11:38:45 -07001272 canvas->drawPicture(pic);
1273 return "";
1274 });
mtkleinb7e8d692015-04-07 08:30:32 -07001275}
1276
mtkleind31c13d2015-05-05 12:59:56 -07001277/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1278
mtklein6fbf4b32015-05-06 11:35:40 -07001279// Draw the Src twice. This can help exercise caching.
1280Error ViaTwice::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1281 return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) -> Error {
1282 for (int i = 0; i < 2; i++) {
1283 SkAutoCanvasRestore acr(canvas, true/*save now*/);
1284 canvas->clear(SK_ColorTRANSPARENT);
1285 Error err = src.draw(canvas);
1286 if (err.isEmpty()) {
1287 return err;
1288 }
1289 }
1290 return "";
1291 });
1292}
1293
1294/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1295
mtkleind31c13d2015-05-05 12:59:56 -07001296// This is like SkRecords::Draw, in that it plays back SkRecords ops into a Canvas.
1297// Unlike SkRecords::Draw, it builds a single-op sub-picture out of each Draw-type op.
1298// This is an only-slightly-exaggerated simluation of Blink's Slimming Paint pictures.
1299struct DrawsAsSingletonPictures {
1300 SkCanvas* fCanvas;
mtkleind2baa902015-07-07 09:43:28 -07001301 const SkDrawableList& fDrawables;
mtkleind31c13d2015-05-05 12:59:56 -07001302
mtkleind31c13d2015-05-05 12:59:56 -07001303 template <typename T>
1304 void draw(const T& op, SkCanvas* canvas) {
1305 // We must pass SkMatrix::I() as our initial matrix.
1306 // By default SkRecords::Draw() uses the canvas' matrix as its initial matrix,
1307 // which would have the funky effect of applying transforms over and over.
mtkleind2baa902015-07-07 09:43:28 -07001308 SkRecords::Draw d(canvas, nullptr, fDrawables.begin(), fDrawables.count(), &SkMatrix::I());
1309 d(op);
mtkleind31c13d2015-05-05 12:59:56 -07001310 }
1311
mtklein449d9b72015-09-28 10:33:02 -07001312 // Draws get their own picture.
mtkleind31c13d2015-05-05 12:59:56 -07001313 template <typename T>
mtklein449d9b72015-09-28 10:33:02 -07001314 SK_WHEN(T::kTags & SkRecords::kDraw_Tag, void) operator()(const T& op) {
mtkleind31c13d2015-05-05 12:59:56 -07001315 SkPictureRecorder rec;
1316 this->draw(op, rec.beginRecording(SkRect::MakeLargest()));
1317 SkAutoTUnref<SkPicture> pic(rec.endRecordingAsPicture());
1318 fCanvas->drawPicture(pic);
1319 }
1320
mtklein449d9b72015-09-28 10:33:02 -07001321 // We'll just issue non-draws directly.
mtkleind31c13d2015-05-05 12:59:56 -07001322 template <typename T>
mtklein449d9b72015-09-28 10:33:02 -07001323 skstd::enable_if_t<!(T::kTags & SkRecords::kDraw_Tag), void> operator()(const T& op) {
1324 this->draw(op, fCanvas);
1325 }
mtkleind31c13d2015-05-05 12:59:56 -07001326};
1327
mtkleind31c13d2015-05-05 12:59:56 -07001328// Record Src into a picture, then record it into a macro picture with a sub-picture for each draw.
1329// Then play back that macro picture into our wrapped sink.
1330Error ViaSingletonPictures::draw(
1331 const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1332 auto size = src.size();
1333 return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) -> Error {
1334 // Use low-level (Skia-private) recording APIs so we can read the SkRecord.
1335 SkRecord skr;
1336 SkRecorder recorder(&skr, size.width(), size.height());
1337 Error err = src.draw(&recorder);
1338 if (!err.isEmpty()) {
1339 return err;
1340 }
1341
1342 // Record our macro-picture, with each draw op as its own sub-picture.
1343 SkPictureRecorder macroRec;
1344 SkCanvas* macroCanvas = macroRec.beginRecording(SkIntToScalar(size.width()),
1345 SkIntToScalar(size.height()));
mtkleind2baa902015-07-07 09:43:28 -07001346
1347 SkAutoTDelete<SkDrawableList> drawables(recorder.detachDrawableList());
1348 const SkDrawableList empty;
1349
1350 DrawsAsSingletonPictures drawsAsSingletonPictures = {
1351 macroCanvas,
1352 drawables ? *drawables : empty,
1353 };
mtkleinc6ad06a2015-08-19 09:51:00 -07001354 for (int i = 0; i < skr.count(); i++) {
mtkleind31c13d2015-05-05 12:59:56 -07001355 skr.visit<void>(i, drawsAsSingletonPictures);
1356 }
1357 SkAutoTUnref<SkPicture> macroPic(macroRec.endRecordingAsPicture());
1358
1359 canvas->drawPicture(macroPic);
1360 return "";
1361 });
1362}
1363
mtklein748ca3b2015-01-15 10:56:12 -08001364} // namespace DM