DMSrcSink.cpp revision 56e25ddf6e2c1f85c5addbe498a082268ebee6ea
1#include "DMSrcSink.h" 2#include "SamplePipeControllers.h" 3#include "SkCommonFlags.h" 4#include "SkCodec.h" 5#include "SkDocument.h" 6#include "SkError.h" 7#include "SkMultiPictureDraw.h" 8#include "SkNullCanvas.h" 9#include "SkOSFile.h" 10#include "SkPictureRecorder.h" 11#include "SkRandom.h" 12#include "SkSVGCanvas.h" 13#include "SkStream.h" 14#include "SkXMLWriter.h" 15 16DEFINE_bool(codec, false, "Use SkCodec instead of SkImageDecoder"); 17 18namespace DM { 19 20GMSrc::GMSrc(skiagm::GMRegistry::Factory factory) : fFactory(factory) {} 21 22Error GMSrc::draw(SkCanvas* canvas) const { 23 SkAutoTDelete<skiagm::GM> gm(fFactory(NULL)); 24 canvas->concat(gm->getInitialTransform()); 25 gm->draw(canvas); 26 return ""; 27} 28 29SkISize GMSrc::size() const { 30 SkAutoTDelete<skiagm::GM> gm(fFactory(NULL)); 31 return gm->getISize(); 32} 33 34Name GMSrc::name() const { 35 SkAutoTDelete<skiagm::GM> gm(fFactory(NULL)); 36 return gm->getName(); 37} 38 39/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 40 41ImageSrc::ImageSrc(Path path, int divisor) : fPath(path), fDivisor(divisor) {} 42 43Error ImageSrc::draw(SkCanvas* canvas) const { 44 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); 45 if (!encoded) { 46 return SkStringPrintf("Couldn't read %s.", fPath.c_str()); 47 } 48 const SkColorType dstColorType = canvas->imageInfo().colorType(); 49 if (fDivisor == 0) { 50 // Decode the full image. 51 SkBitmap bitmap; 52 if (FLAGS_codec) { 53 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); 54 if (!codec) { 55 return SkStringPrintf("Couldn't decode %s.", fPath.c_str()); 56 } 57 SkImageInfo info; 58 if (!codec->getInfo(&info)) { 59 return SkStringPrintf("Couldn't getInfo %s.", fPath.c_str()); 60 } 61 info = info.makeColorType(dstColorType); 62 if (info.alphaType() == kUnpremul_SkAlphaType) { 63 // FIXME: Currently we cannot draw unpremultiplied sources. 64 info = info.makeAlphaType(kPremul_SkAlphaType); 65 } 66 if (!bitmap.tryAllocPixels(info)) { 67 return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(), 68 info.width(), info.height()); 69 } 70 SkAutoLockPixels alp(bitmap); 71 const SkImageGenerator::Result result = codec->getPixels(info, bitmap.getPixels(), 72 bitmap.rowBytes()); 73 switch (result) { 74 case SkImageGenerator::kSuccess: 75 // We consider incomplete to be valid, since we should still decode what is 76 // available. 77 case SkImageGenerator::kIncompleteInput: 78 break; 79 case SkImageGenerator::kInvalidConversion: 80 return Error::Nonfatal("Incompatible colortype conversion"); 81 default: 82 // Everything else is considered a failure. 83 return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str()); 84 } 85 } else { 86 if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap, 87 dstColorType, SkImageDecoder::kDecodePixels_Mode)) { 88 return SkStringPrintf("Couldn't decode %s.", fPath.c_str()); 89 } 90 if (kRGB_565_SkColorType == dstColorType && !bitmap.isOpaque()) { 91 // Do not draw a bitmap with alpha to a destination without alpha. 92 return Error::Nonfatal("Uninteresting to decode image with alpha into 565."); 93 } 94 } 95 encoded.reset((SkData*)NULL); // Might as well drop this when we're done with it. 96 canvas->drawBitmap(bitmap, 0,0); 97 return ""; 98 } 99 // Decode subsets. This is a little involved. 100 SkAutoTDelete<SkMemoryStream> stream(new SkMemoryStream(encoded)); 101 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream.get())); 102 if (!decoder) { 103 return SkStringPrintf("Can't find a good decoder for %s.", fPath.c_str()); 104 } 105 stream->rewind(); 106 int w,h; 107 if (!decoder->buildTileIndex(stream.detach(), &w, &h) || w*h == 1) { 108 return Error::Nonfatal("Subset decoding not supported."); 109 } 110 111 // Divide the image into subsets that cover the entire image. 112 if (fDivisor > w || fDivisor > h) { 113 return SkStringPrintf("divisor %d is too big for %s with dimensions (%d x %d)", 114 fDivisor, fPath.c_str(), w, h); 115 } 116 const int subsetWidth = w / fDivisor, 117 subsetHeight = h / fDivisor; 118 for (int y = 0; y < h; y += subsetHeight) { 119 for (int x = 0; x < w; x += subsetWidth) { 120 SkBitmap subset; 121 SkIRect rect = SkIRect::MakeXYWH(x, y, subsetWidth, subsetHeight); 122 if (!decoder->decodeSubset(&subset, rect, dstColorType)) { 123 return SkStringPrintf("Could not decode subset (%d, %d, %d, %d).", 124 x, y, x+subsetWidth, y+subsetHeight); 125 } 126 if (kRGB_565_SkColorType == dstColorType && !subset.isOpaque()) { 127 // Do not draw a bitmap with alpha to a destination without alpha. 128 // This is not an error, but there is nothing interesting to show. 129 130 // This should only happen on the first iteration through the loop. 131 SkASSERT(0 == x && 0 == y); 132 133 return Error::Nonfatal("Uninteresting to decode image with alpha into 565."); 134 } 135 canvas->drawBitmap(subset, SkIntToScalar(x), SkIntToScalar(y)); 136 } 137 } 138 return ""; 139} 140 141SkISize ImageSrc::size() const { 142 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); 143 SkBitmap bitmap; 144 if (!encoded || !SkImageDecoder::DecodeMemory(encoded->data(), 145 encoded->size(), 146 &bitmap, 147 kUnknown_SkColorType, 148 SkImageDecoder::kDecodeBounds_Mode)) { 149 return SkISize::Make(0,0); 150 } 151 return bitmap.dimensions(); 152} 153 154Name ImageSrc::name() const { 155 return SkOSPath::Basename(fPath.c_str()); 156} 157 158/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 159 160static const SkRect kSKPViewport = {0,0, 1000,1000}; 161 162SKPSrc::SKPSrc(Path path) : fPath(path) {} 163 164Error SKPSrc::draw(SkCanvas* canvas) const { 165 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(fPath.c_str())); 166 if (!stream) { 167 return SkStringPrintf("Couldn't read %s.", fPath.c_str()); 168 } 169 SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(stream)); 170 if (!pic) { 171 return SkStringPrintf("Couldn't decode %s as a picture.", fPath.c_str()); 172 } 173 stream.reset((SkStream*)NULL); // Might as well drop this when we're done with it. 174 canvas->clipRect(kSKPViewport); 175 canvas->drawPicture(pic); 176 return ""; 177} 178 179SkISize SKPSrc::size() const { 180 // This may be unnecessarily large. 181 return kSKPViewport.roundOut().size(); 182} 183 184Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } 185 186/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 187 188Error NullSink::draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const { 189 SkAutoTDelete<SkCanvas> canvas(SkCreateNullCanvas()); 190 return src.draw(canvas); 191} 192 193/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 194 195DEFINE_bool(gpuStats, false, "Append GPU stats to the log for each GPU task?"); 196 197GPUSink::GPUSink(GrContextFactory::GLContextType ct, 198 GrGLStandard api, 199 int samples, 200 bool dfText, 201 bool threaded) 202 : fContextType(ct) 203 , fGpuAPI(api) 204 , fSampleCount(samples) 205 , fUseDFText(dfText) 206 , fThreaded(threaded) {} 207 208int GPUSink::enclave() const { 209 return fThreaded ? kAnyThread_Enclave : kGPU_Enclave; 210} 211 212void PreAbandonGpuContextErrorHandler(SkError, void*) {} 213 214Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const { 215 GrContextFactory factory; 216 const SkISize size = src.size(); 217 const SkImageInfo info = 218 SkImageInfo::Make(size.width(), size.height(), kN32_SkColorType, kPremul_SkAlphaType); 219 SkAutoTUnref<SkSurface> surface( 220 NewGpuSurface(&factory, fContextType, fGpuAPI, info, fSampleCount, fUseDFText)); 221 if (!surface) { 222 return "Could not create a surface."; 223 } 224 if (FLAGS_preAbandonGpuContext) { 225 SkSetErrorCallback(&PreAbandonGpuContextErrorHandler, NULL); 226 factory.abandonContexts(); 227 } 228 SkCanvas* canvas = surface->getCanvas(); 229 Error err = src.draw(canvas); 230 if (!err.isEmpty()) { 231 return err; 232 } 233 canvas->flush(); 234 if (FLAGS_gpuStats) { 235 canvas->getGrContext()->dumpCacheStats(log); 236 canvas->getGrContext()->dumpGpuStats(log); 237 } 238 dst->allocPixels(info); 239 canvas->readPixels(dst, 0, 0); 240 if (FLAGS_abandonGpuContext) { 241 factory.abandonContexts(); 242 } 243 return ""; 244} 245 246/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 247 248static Error draw_skdocument(const Src& src, SkDocument* doc, SkWStream* dst) { 249 // Print the given DM:Src to a document, breaking on 8.5x11 pages. 250 SkASSERT(doc); 251 int width = src.size().width(), 252 height = src.size().height(); 253 254 const int kLetterWidth = 612, // 8.5 * 72 255 kLetterHeight = 792; // 11 * 72 256 const SkRect letter = SkRect::MakeWH(SkIntToScalar(kLetterWidth), 257 SkIntToScalar(kLetterHeight)); 258 259 int xPages = ((width - 1) / kLetterWidth) + 1; 260 int yPages = ((height - 1) / kLetterHeight) + 1; 261 262 for (int y = 0; y < yPages; ++y) { 263 for (int x = 0; x < xPages; ++x) { 264 int w = SkTMin(kLetterWidth, width - (x * kLetterWidth)); 265 int h = SkTMin(kLetterHeight, height - (y * kLetterHeight)); 266 SkCanvas* canvas = 267 doc->beginPage(SkIntToScalar(w), SkIntToScalar(h)); 268 canvas->clipRect(letter); 269 canvas->translate(-letter.width() * x, -letter.height() * y); 270 Error err = src.draw(canvas); 271 if (!err.isEmpty()) { 272 return err; 273 } 274 doc->endPage(); 275 } 276 } 277 doc->close(); 278 dst->flush(); 279 return ""; 280} 281 282PDFSink::PDFSink() {} 283 284Error PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const { 285 SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(dst)); 286 if (!doc) { 287 return "SkDocument::CreatePDF() returned NULL"; 288 } 289 return draw_skdocument(src, doc.get(), dst); 290} 291 292/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 293 294XPSSink::XPSSink() {} 295 296Error XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const { 297 SkAutoTUnref<SkDocument> doc(SkDocument::CreateXPS(dst)); 298 if (!doc) { 299 return "SkDocument::CreateXPS() returned NULL"; 300 } 301 return draw_skdocument(src, doc.get(), dst); 302} 303/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 304 305SKPSink::SKPSink() {} 306 307Error SKPSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const { 308 SkSize size; 309 size = src.size(); 310 SkPictureRecorder recorder; 311 Error err = src.draw(recorder.beginRecording(size.width(), size.height())); 312 if (!err.isEmpty()) { 313 return err; 314 } 315 SkAutoTUnref<SkPicture> pic(recorder.endRecording()); 316 pic->serialize(dst); 317 return ""; 318} 319 320/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 321 322SVGSink::SVGSink() {} 323 324Error SVGSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const { 325 SkAutoTDelete<SkXMLWriter> xmlWriter(SkNEW_ARGS(SkXMLStreamWriter, (dst))); 326 SkAutoTUnref<SkCanvas> canvas(SkSVGCanvas::Create( 327 SkRect::MakeWH(SkIntToScalar(src.size().width()), SkIntToScalar(src.size().height())), 328 xmlWriter)); 329 return src.draw(canvas); 330} 331 332/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 333 334RasterSink::RasterSink(SkColorType colorType) : fColorType(colorType) {} 335 336Error RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const { 337 const SkISize size = src.size(); 338 // If there's an appropriate alpha type for this color type, use it, otherwise use premul. 339 SkAlphaType alphaType = kPremul_SkAlphaType; 340 (void)SkColorTypeValidateAlphaType(fColorType, alphaType, &alphaType); 341 342 dst->allocPixels(SkImageInfo::Make(size.width(), size.height(), fColorType, alphaType)); 343 dst->eraseColor(SK_ColorTRANSPARENT); 344 SkCanvas canvas(*dst); 345 return src.draw(&canvas); 346} 347 348/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 349 350static SkISize auto_compute_translate(SkMatrix* matrix, int srcW, int srcH) { 351 SkRect bounds = SkRect::MakeIWH(srcW, srcH); 352 matrix->mapRect(&bounds); 353 matrix->postTranslate(-bounds.x(), -bounds.y()); 354 return SkISize::Make(SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())); 355} 356 357ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : fMatrix(matrix), fSink(sink) {} 358 359Error ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { 360 // We turn our arguments into a Src, then draw that Src into our Sink to fill bitmap or stream. 361 struct ProxySrc : public Src { 362 const Src& fSrc; 363 SkMatrix fMatrix; 364 SkISize fSize; 365 366 ProxySrc(const Src& src, SkMatrix matrix) : fSrc(src), fMatrix(matrix) { 367 fSize = auto_compute_translate(&fMatrix, src.size().width(), src.size().height()); 368 } 369 370 Error draw(SkCanvas* canvas) const SK_OVERRIDE { 371 canvas->concat(fMatrix); 372 return fSrc.draw(canvas); 373 } 374 SkISize size() const SK_OVERRIDE { return fSize; } 375 Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this. 376 } proxy(src, fMatrix); 377 return fSink->draw(proxy, bitmap, stream, log); 378} 379 380// Undoes any flip or 90 degree rotate without changing the scale of the bitmap. 381// This should be pixel-preserving. 382ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : fMatrix(matrix), fSink(sink) {} 383 384Error ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { 385 Error err = fSink->draw(src, bitmap, stream, log); 386 if (!err.isEmpty()) { 387 return err; 388 } 389 390 SkMatrix inverse; 391 if (!fMatrix.rectStaysRect() || !fMatrix.invert(&inverse)) { 392 return "Cannot upright --matrix."; 393 } 394 SkMatrix upright = SkMatrix::I(); 395 upright.setScaleX(SkScalarSignAsScalar(inverse.getScaleX())); 396 upright.setScaleY(SkScalarSignAsScalar(inverse.getScaleY())); 397 upright.setSkewX(SkScalarSignAsScalar(inverse.getSkewX())); 398 upright.setSkewY(SkScalarSignAsScalar(inverse.getSkewY())); 399 400 SkBitmap uprighted; 401 SkISize size = auto_compute_translate(&upright, bitmap->width(), bitmap->height()); 402 uprighted.allocPixels(bitmap->info().makeWH(size.width(), size.height())); 403 404 SkCanvas canvas(uprighted); 405 canvas.concat(upright); 406 SkPaint paint; 407 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 408 canvas.drawBitmap(*bitmap, 0, 0, &paint); 409 410 *bitmap = uprighted; 411 bitmap->lockPixels(); 412 return ""; 413} 414 415/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 416 417ViaPipe::ViaPipe(Sink* sink) : fSink(sink) {} 418 419Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { 420 // We turn ourselves into another Src that draws our argument into bitmap/stream via pipe. 421 struct ProxySrc : public Src { 422 const Src& fSrc; 423 ProxySrc(const Src& src) : fSrc(src) {} 424 425 Error draw(SkCanvas* canvas) const SK_OVERRIDE { 426 SkISize size = this->size(); 427 PipeController controller(canvas, &SkImageDecoder::DecodeMemory); 428 SkGPipeWriter pipe; 429 const uint32_t kFlags = 0; // We mirror SkDeferredCanvas, which doesn't use any flags. 430 return fSrc.draw(pipe.startRecording(&controller, kFlags, size.width(), size.height())); 431 } 432 SkISize size() const SK_OVERRIDE { return fSrc.size(); } 433 Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this. 434 } proxy(src); 435 return fSink->draw(proxy, bitmap, stream, log); 436} 437 438/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 439 440ViaSerialization::ViaSerialization(Sink* sink) : fSink(sink) {} 441 442Error ViaSerialization::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) 443 const { 444 // Record our Src into a picture. 445 SkSize size; 446 size = src.size(); 447 SkPictureRecorder recorder; 448 Error err = src.draw(recorder.beginRecording(size.width(), size.height())); 449 if (!err.isEmpty()) { 450 return err; 451 } 452 SkAutoTUnref<SkPicture> pic(recorder.endRecording()); 453 454 // Serialize it and then deserialize it. 455 SkDynamicMemoryWStream wStream; 456 pic->serialize(&wStream); 457 SkAutoTDelete<SkStream> rStream(wStream.detachAsStream()); 458 SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rStream)); 459 460 // Turn that deserialized picture into a Src, draw it into our Sink to fill bitmap or stream. 461 struct ProxySrc : public Src { 462 const SkPicture* fPic; 463 const SkISize fSize; 464 ProxySrc(const SkPicture* pic, SkISize size) : fPic(pic), fSize(size) {} 465 466 Error draw(SkCanvas* canvas) const SK_OVERRIDE { 467 canvas->drawPicture(fPic); 468 return ""; 469 } 470 SkISize size() const SK_OVERRIDE { return fSize; } 471 Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this. 472 } proxy(deserialized, src.size()); 473 return fSink->draw(proxy, bitmap, stream, log); 474} 475 476/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 477 478ViaTiles::ViaTiles(int w, int h, SkBBHFactory* factory, Sink* sink) 479 : fW(w) 480 , fH(h) 481 , fFactory(factory) 482 , fSink(sink) {} 483 484Error ViaTiles::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { 485 // Record our Src into a picture. 486 SkSize size; 487 size = src.size(); 488 SkPictureRecorder recorder; 489 Error err = src.draw(recorder.beginRecording(size.width(), size.height(), fFactory.get())); 490 if (!err.isEmpty()) { 491 return err; 492 } 493 SkAutoTUnref<SkPicture> pic(recorder.endRecording()); 494 495 // Turn that picture into a Src that draws into our Sink via tiles + MPD. 496 struct ProxySrc : public Src { 497 const int fW, fH; 498 const SkPicture* fPic; 499 const SkISize fSize; 500 ProxySrc(int w, int h, const SkPicture* pic, SkISize size) 501 : fW(w), fH(h), fPic(pic), fSize(size) {} 502 503 Error draw(SkCanvas* canvas) const SK_OVERRIDE { 504 const int xTiles = (fSize.width() + fW - 1) / fW, 505 yTiles = (fSize.height() + fH - 1) / fH; 506 SkMultiPictureDraw mpd(xTiles*yTiles); 507 SkTDArray<SkSurface*> surfaces; 508 surfaces.setReserve(xTiles*yTiles); 509 510 SkImageInfo info = canvas->imageInfo().makeWH(fW, fH); 511 for (int j = 0; j < yTiles; j++) { 512 for (int i = 0; i < xTiles; i++) { 513 // This lets our ultimate Sink determine the best kind of surface. 514 // E.g., if it's a GpuSink, the surfaces and images are textures. 515 SkSurface* s = canvas->newSurface(info); 516 if (!s) { 517 s = SkSurface::NewRaster(info); // Some canvases can't create surfaces. 518 } 519 surfaces.push(s); 520 SkCanvas* c = s->getCanvas(); 521 c->translate(SkIntToScalar(-i * fW), 522 SkIntToScalar(-j * fH)); // Line up the canvas with this tile. 523 mpd.add(c, fPic); 524 } 525 } 526 mpd.draw(); 527 for (int j = 0; j < yTiles; j++) { 528 for (int i = 0; i < xTiles; i++) { 529 SkAutoTUnref<SkImage> image(surfaces[i+xTiles*j]->newImageSnapshot()); 530 canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH)); 531 } 532 } 533 surfaces.unrefAll(); 534 return ""; 535 } 536 SkISize size() const SK_OVERRIDE { return fSize; } 537 Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this. 538 } proxy(fW, fH, pic, src.size()); 539 return fSink->draw(proxy, bitmap, stream, log); 540} 541 542} // namespace DM 543