1/* 2 * Copyright 2014 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 8#include "tests/Test.h" 9 10#include "include/core/SkBitmap.h" 11#include "include/core/SkCanvas.h" 12#include "include/core/SkPicture.h" 13#include "include/core/SkPictureRecorder.h" 14#include "include/core/SkStream.h" 15#include "include/core/SkString.h" 16#include "include/core/SkSurface.h" 17#include "src/core/SkBlendModePriv.h" 18 19#include <cstring> 20 21// Verify that replay of a recording into a clipped canvas 22// produces the correct bitmap. 23// This arose from http://crbug.com/401593 which has 24// https://code.google.com/p/skia/issues/detail?id=1291 as its root cause. 25 26namespace { 27 28class Drawer { 29 public: 30 explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) { 31 auto surf = SkSurface::MakeRasterN32Premul(100, 100); 32 surf->getCanvas()->clear(0xffffffff); 33 SkPaint circlePaint; 34 circlePaint.setColor(0xff000000); 35 surf->getCanvas()->drawCircle(50, 50, 50, circlePaint); 36 fCircleImage = surf->makeImageSnapshot(); 37 } 38 39 const SkImageInfo& imageInfo() const { return fImageInfo; } 40 41 void draw(SkCanvas* canvas, const SkRect& clipRect, SkBlendMode mode) const { 42 SkPaint greenPaint; 43 greenPaint.setColor(0xff008000); 44 SkPaint blackPaint; 45 blackPaint.setColor(0xff000000); 46 SkPaint whitePaint; 47 whitePaint.setColor(0xffffffff); 48 SkPaint layerPaint; 49 layerPaint.setColor(0xff000000); 50 layerPaint.setBlendMode(mode); 51 SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()), 52 SkIntToScalar(fImageInfo.height()))); 53 54 canvas->clipRect(clipRect); 55 canvas->clear(0xff000000); 56 57 canvas->saveLayer(nullptr, &blackPaint); 58 canvas->drawRect(canvasRect, greenPaint); 59 canvas->saveLayer(nullptr, &layerPaint); 60 canvas->drawImageRect(fCircleImage, SkRect::MakeXYWH(20,20,60,60), 61 SkSamplingOptions(), &blackPaint); 62 canvas->restore(); 63 canvas->restore(); 64 } 65 66 private: 67 const SkImageInfo fImageInfo; 68 sk_sp<SkImage> fCircleImage; 69}; 70 71class RecordingStrategy { 72 public: 73 virtual ~RecordingStrategy() {} 74 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, 75 const SkRect& intoClip, 76 SkBlendMode) = 0; 77}; 78 79class BitmapBackedCanvasStrategy : public RecordingStrategy { 80 // This version just draws into a bitmap-backed canvas. 81 public: 82 BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) { 83 fBitmap.allocPixels(imageInfo); 84 } 85 86 const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip, 87 SkBlendMode mode) override { 88 SkCanvas canvas(fBitmap); 89 canvas.clear(0xffffffff); 90 // Note that the scene is drawn just into the clipped region! 91 canvas.clipRect(intoClip); 92 drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide... 93 return fBitmap; 94 } 95 96 private: 97 SkBitmap fBitmap; 98}; 99 100class PictureStrategy : public RecordingStrategy { 101 // This version draws the entire scene into an SkPictureRecorder. 102 // Then it then replays the scene through a clip rectangle. 103 // This backend proved to be buggy. 104 public: 105 PictureStrategy(const SkImageInfo& imageInfo) { 106 fBitmap.allocPixels(imageInfo); 107 fWidth = imageInfo.width(); 108 fHeight = imageInfo.height(); 109 } 110 111 const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip, 112 SkBlendMode mode) override { 113 SkRTreeFactory factory; 114 SkPictureRecorder recorder; 115 SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight))); 116 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth), 117 SkIntToScalar(fHeight), 118 &factory); 119 drawer.draw(canvas, canvasRect, mode); 120 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 121 122 SkCanvas replayCanvas(fBitmap); 123 replayCanvas.clear(0xffffffff); 124 replayCanvas.clipRect(intoClip); 125 picture->playback(&replayCanvas); 126 return fBitmap; 127 } 128 129 private: 130 SkBitmap fBitmap; 131 int fWidth; 132 int fHeight; 133}; 134 135} // namespace 136 137 138DEF_TEST(SkRecordingAccuracyXfermode, reporter) { 139#define FINEGRAIN 0 140 const Drawer drawer; 141 142 BitmapBackedCanvasStrategy golden(drawer.imageInfo()); 143 PictureStrategy picture(drawer.imageInfo()); 144 145#if !FINEGRAIN 146 unsigned numErrors = 0; 147 SkString errors; 148#endif 149 150 for (int iMode = 0; iMode < int(SkBlendMode::kLastMode); iMode++) { 151 const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100); 152 SkBlendMode mode = SkBlendMode(iMode); 153 154 const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode); 155 const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode); 156 157 size_t pixelsSize = goldenBM.computeByteSize(); 158 REPORTER_ASSERT(reporter, pixelsSize == pictureBM.computeByteSize()); 159 160 // The pixel arrays should match. 161#if FINEGRAIN 162 REPORTER_ASSERT(reporter, 163 0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)); 164#else 165 if (0 != memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) { 166 numErrors++; 167 errors.appendf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n", 168 iMode, SkBlendMode_Name(mode)); 169 } 170#endif 171 } 172 173#if !FINEGRAIN 174 REPORTER_ASSERT(reporter, 0 == numErrors, errors.c_str()); 175#endif 176} 177