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 "Test.h" 9 10 #include "../include/core/SkCanvas.h" 11 #include "../include/core/SkPicture.h" 12 #include "../include/core/SkStream.h" 13 #include "../include/core/SkString.h" 14 #include "../include/record/SkRecording.h" 15 #include "../include/core/SkPictureRecorder.h" 16 #include <cstring> 17 18 // Verify that replay of a recording into a clipped canvas 19 // produces the correct bitmap. 20 // This arose from http://crbug.com/401593 which has 21 // https://code.google.com/p/skia/issues/detail?id=1291 as its root cause. 22 23 24 namespace { 25 26 class Drawer { 27 public: 28 explicit Drawer() 29 : fImageInfo(SkImageInfo::MakeN32Premul(200,100)) 30 { 31 fCircleBM.allocPixels( SkImageInfo::MakeN32Premul(100,100) ); 32 SkCanvas canvas(fCircleBM); 33 canvas.clear(0xffffffff); 34 SkPaint circlePaint; 35 circlePaint.setColor(0xff000000); 36 canvas.drawCircle(50,50,50,circlePaint); 37 } 38 39 const SkImageInfo& imageInfo() const { return fImageInfo; } 40 41 void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode 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.setXfermodeMode(mode); 51 SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),SkIntToScalar(fImageInfo.height()))); 52 53 canvas->clipRect(clipRect); 54 canvas->clear(0xff000000); 55 56 canvas->saveLayer(NULL,&blackPaint); 57 canvas->drawRect(canvasRect,greenPaint); 58 canvas->saveLayer(NULL,&layerPaint); 59 canvas->drawBitmapRect(fCircleBM,SkRect::MakeXYWH(20,20,60,60),&blackPaint); 60 canvas->restore(); 61 canvas->restore(); 62 } 63 64 private: 65 const SkImageInfo fImageInfo; 66 SkBitmap fCircleBM; 67 }; 68 69 class RecordingStrategy { 70 public: 71 virtual ~RecordingStrategy() {} 72 virtual void init(const SkImageInfo&) = 0; 73 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, 74 const SkRect& intoClip, 75 SkXfermode::Mode) = 0; 76 }; 77 78 class BitmapBackedCanvasStrategy : public RecordingStrategy { 79 // This version just draws into a bitmap-backed canvas. 80 public: 81 BitmapBackedCanvasStrategy() {} 82 83 virtual void init(const SkImageInfo& imageInfo) { 84 fBitmap.allocPixels(imageInfo); 85 } 86 87 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, 88 const SkRect& intoClip, 89 SkXfermode::Mode mode) { 90 SkCanvas canvas(fBitmap); 91 canvas.clear(0xffffffff); 92 // Note that the scene is drawn just into the clipped region! 93 canvas.clipRect(intoClip); 94 drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide... 95 return fBitmap; 96 } 97 98 private: 99 SkBitmap fBitmap; 100 }; 101 102 class DeprecatedRecorderStrategy : public RecordingStrategy { 103 // This version draws the entire scene into an SkPictureRecorder, 104 // using the deprecated recording backend. 105 // Then it then replays the scene through a clip rectangle. 106 // This backend proved to be buggy. 107 public: 108 DeprecatedRecorderStrategy() {} 109 110 virtual void init(const SkImageInfo& imageInfo) { 111 fBitmap.allocPixels(imageInfo); 112 fWidth = imageInfo.width(); 113 fHeight= imageInfo.height(); 114 } 115 116 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, 117 const SkRect& intoClip, 118 SkXfermode::Mode mode) { 119 SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; 120 SkTileGridFactory factory(tileGridInfo); 121 SkPictureRecorder recorder; 122 SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight))); 123 SkCanvas* canvas = recorder.DEPRECATED_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory); 124 drawer.draw(canvas, canvasRect, mode); 125 SkAutoTDelete<SkPicture> picture(recorder.endRecording()); 126 127 SkCanvas replayCanvas(fBitmap); 128 replayCanvas.clear(0xffffffff); 129 replayCanvas.clipRect(intoClip); 130 picture->playback(&replayCanvas); 131 132 return fBitmap; 133 } 134 135 private: 136 SkBitmap fBitmap; 137 int fWidth; 138 int fHeight; 139 }; 140 141 class NewRecordingStrategy : public RecordingStrategy { 142 // This version draws the entire scene into an SkPictureRecorder, 143 // using the new recording backend. 144 // Then it then replays the scene through a clip rectangle. 145 // This backend proved to be buggy. 146 public: 147 NewRecordingStrategy() {} 148 149 virtual void init(const SkImageInfo& imageInfo) { 150 fBitmap.allocPixels(imageInfo); 151 fWidth = imageInfo.width(); 152 fHeight= imageInfo.height(); 153 } 154 155 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, 156 const SkRect& intoClip, 157 SkXfermode::Mode mode) { 158 SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; 159 SkTileGridFactory factory(tileGridInfo); 160 SkPictureRecorder recorder; 161 SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight))); 162 SkCanvas* canvas = recorder.EXPERIMENTAL_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory); 163 164 drawer.draw(canvas, canvasRect, mode); 165 SkAutoTDelete<SkPicture> picture(recorder.endRecording()); 166 167 SkCanvas replayCanvas(fBitmap); 168 replayCanvas.clear(0xffffffff); 169 replayCanvas.clipRect(intoClip); 170 picture->playback(&replayCanvas); 171 return fBitmap; 172 } 173 174 private: 175 SkBitmap fBitmap; 176 int fWidth; 177 int fHeight; 178 }; 179 180 } 181 182 183 184 DEF_TEST(SkRecordingAccuracyXfermode, reporter) { 185 #define FINEGRAIN 0 186 187 const Drawer drawer; 188 189 BitmapBackedCanvasStrategy golden; // This is the expected result. 190 DeprecatedRecorderStrategy deprecatedRecording; 191 NewRecordingStrategy newRecording; 192 193 golden.init(drawer.imageInfo()); 194 deprecatedRecording.init(drawer.imageInfo()); 195 newRecording.init(drawer.imageInfo()); 196 197 #if !FINEGRAIN 198 unsigned numErrors = 0; 199 SkString errors; 200 #endif 201 202 for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) { 203 const SkRect& clip = SkRect::MakeXYWH(100,0,100,100); 204 SkXfermode::Mode mode = SkXfermode::Mode(iMode); 205 206 const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode); 207 const SkBitmap& deprecatedBM = deprecatedRecording.recordAndReplay(drawer, clip, mode); 208 const SkBitmap& newRecordingBM = newRecording.recordAndReplay(drawer, clip, mode); 209 210 size_t pixelsSize = goldenBM.getSize(); 211 REPORTER_ASSERT( reporter, pixelsSize == deprecatedBM.getSize() ); 212 REPORTER_ASSERT( reporter, pixelsSize == newRecordingBM.getSize() ); 213 214 // The pixel arrays should match. 215 #if FINEGRAIN 216 REPORTER_ASSERT_MESSAGE( reporter, 217 0==memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ), 218 "Tiled bitmap is wrong"); 219 REPORTER_ASSERT_MESSAGE( reporter, 220 0==memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ), 221 "SkRecorder bitmap is wrong"); 222 #else 223 if ( memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ) ) { 224 numErrors++; 225 SkString str; 226 str.printf("For SkXfermode %d %s: Deprecated recorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); 227 errors.append(str); 228 } 229 if ( memcmp( goldenBM.getPixels(), newRecordingBM.getPixels(), pixelsSize ) ) { 230 numErrors++; 231 SkString str; 232 str.printf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); 233 errors.append(str); 234 } 235 #endif 236 } 237 #if !FINEGRAIN 238 REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() ); 239 #endif 240 } 241