Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 virtual ~Drawer() {} | |
| 29 virtual const SkImageInfo& imageInfo() const = 0; | |
| 30 virtual void draw(SkCanvas*, const SkRect&, SkXfermode::Mode) const = 0; | |
| 31 }; | |
| 32 | |
| 33 class ExampleDrawer : public Drawer { | |
|
mtklein
2014/09/12 19:20:08
Generally I'm keen on interfaces like this, but pe
dneto
2014/09/12 20:41:42
Done.
| |
| 34 public: | |
| 35 explicit ExampleDrawer() | |
| 36 : imageInfo_(SkImageInfo::MakeN32Premul(200,100)) | |
|
mtklein
2014/09/12 19:20:08
Generally, Skia style wants a big "foo_ -> fFoo" a
dneto
2014/09/12 20:41:42
Done.
| |
| 37 { | |
| 38 circleBM_.allocPixels( SkImageInfo::MakeN32Premul(100,100) ); | |
| 39 SkCanvas canvas(circleBM_); | |
| 40 canvas.clear(0xffffffff); | |
| 41 SkPaint circlePaint; | |
| 42 circlePaint.setColor(0xff000000); | |
| 43 canvas.drawCircle(50,50,50,circlePaint); | |
| 44 } | |
| 45 | |
| 46 const SkImageInfo& imageInfo() const { return imageInfo_; } | |
| 47 | |
| 48 void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode mode) con st { | |
| 49 SkPaint greenPaint; | |
| 50 greenPaint.setColor(0xff008000); | |
| 51 SkPaint blackPaint; | |
| 52 blackPaint.setColor(0xff000000); | |
| 53 SkPaint whitePaint; | |
| 54 whitePaint.setColor(0xffffffff); | |
| 55 SkPaint layerPaint; | |
| 56 layerPaint.setColor(0xff000000); | |
| 57 layerPaint.setXfermodeMode(mode); | |
| 58 SkRect canvasRect(SkRect::MakeWH(imageInfo_.width(),imageInfo_.height())); | |
| 59 | |
| 60 canvas->clipRect(clipRect); | |
| 61 canvas->clear(0xff000000); | |
| 62 | |
| 63 canvas->saveLayer(NULL,&blackPaint); | |
| 64 canvas->drawRect(canvasRect,greenPaint); | |
| 65 canvas->saveLayer(NULL,&layerPaint); | |
| 66 canvas->drawBitmapRect(circleBM_,SkRect::MakeXYWH(20,20,60,60),&blackPai nt); | |
| 67 canvas->restore(); | |
| 68 canvas->restore(); | |
| 69 } | |
| 70 | |
| 71 private: | |
| 72 const SkImageInfo imageInfo_; | |
| 73 SkBitmap circleBM_; | |
| 74 }; | |
| 75 | |
| 76 #if 0 | |
| 77 void writeToFile(std::string fname, SkPicture* picture) { | |
| 78 SkFILEWStream fs(fname.c_str()); | |
| 79 picture->serialize(&fs,NULL); | |
| 80 fs.flush(); | |
| 81 } | |
| 82 #endif | |
| 83 | |
| 84 class RecordingStrategy { | |
| 85 public: | |
| 86 virtual ~RecordingStrategy() {} | |
| 87 virtual void init(const SkImageInfo&) = 0; | |
| 88 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
| 89 const SkRect& intoClip, | |
| 90 SkXfermode::Mode) = 0; | |
| 91 }; | |
| 92 | |
| 93 class BitmapBackedCanvasStrategy : public RecordingStrategy { | |
| 94 // This version just draws into a bitmap-backed canvas. | |
| 95 public: | |
| 96 BitmapBackedCanvasStrategy() {} | |
| 97 | |
| 98 virtual void init(const SkImageInfo& imageInfo) { | |
| 99 bitmap_.allocPixels(imageInfo); | |
| 100 } | |
| 101 | |
| 102 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
| 103 const SkRect& intoClip, | |
| 104 SkXfermode::Mode mode) { | |
| 105 SkCanvas canvas(bitmap_); | |
| 106 canvas.clear(0xffffffff); | |
| 107 // Note that the scene is drawn just into the clipped region! | |
| 108 canvas.clipRect(intoClip); | |
| 109 drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide... | |
| 110 return bitmap_; | |
| 111 } | |
| 112 | |
| 113 private: | |
| 114 SkBitmap bitmap_; | |
| 115 }; | |
| 116 | |
| 117 class TiledPictureRecorderStrategy : public RecordingStrategy { | |
| 118 // This version draws the entire scene into an SkPictureRecorder, | |
| 119 // then replays through a clip rectangle. | |
| 120 // As of this writing, SkPictureRecorder is a deprecated API, but is | |
| 121 // still used in Chrome. | |
| 122 // It's also the one that proved to be buggy. | |
| 123 public: | |
| 124 TiledPictureRecorderStrategy() {} | |
| 125 | |
| 126 virtual void init(const SkImageInfo& imageInfo) { | |
| 127 bitmap_.allocPixels(imageInfo); | |
| 128 width_ = imageInfo.width(); | |
| 129 height_= imageInfo.height(); | |
| 130 } | |
| 131 | |
| 132 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
| 133 const SkRect& intoClip, | |
| 134 SkXfermode::Mode mode) { | |
| 135 SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; | |
| 136 SkTileGridFactory factory(tileGridInfo); | |
| 137 SkPictureRecorder recorder; | |
| 138 SkRect canvasRect(SkRect::MakeWH(width_,height_)); | |
| 139 SkCanvas* canvas = recorder.beginRecording( width_, height_, &factory); | |
| 140 drawer.draw(canvas, canvasRect, mode); | |
| 141 SkAutoTDelete<SkPicture> picture(recorder.endRecording()); | |
| 142 | |
| 143 SkCanvas replayCanvas(bitmap_); | |
| 144 replayCanvas.clear(0xffffffff); | |
| 145 replayCanvas.clipRect(intoClip); | |
| 146 picture->playback(&replayCanvas); | |
| 147 | |
| 148 return bitmap_; | |
| 149 } | |
| 150 | |
| 151 private: | |
| 152 SkBitmap bitmap_; | |
| 153 int width_; | |
| 154 int height_; | |
| 155 }; | |
| 156 | |
| 157 class SkRecordingStrategy : public RecordingStrategy { | |
| 158 // This version draws the entire scene via the SkRecording API. | |
| 159 // then replays through a clip rectangle. | |
| 160 // As of this writing, SkRecording is an experimental API. | |
| 161 public: | |
| 162 SkRecordingStrategy() {} | |
| 163 | |
| 164 virtual void init(const SkImageInfo& imageInfo) { | |
| 165 bitmap_.allocPixels(imageInfo); | |
| 166 width_ = imageInfo.width(); | |
| 167 height_= imageInfo.height(); | |
| 168 } | |
| 169 | |
| 170 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
| 171 const SkRect& intoClip, | |
| 172 SkXfermode::Mode mode) { | |
| 173 EXPERIMENTAL::SkRecording recording(width_, height_); | |
| 174 SkRect canvasRect(SkRect::MakeWH(width_,height_)); | |
| 175 drawer.draw(recording.canvas(), canvasRect, mode); | |
| 176 SkAutoTDelete<const EXPERIMENTAL::SkPlayback> playback(recording.releasePlay back()); | |
| 177 | |
| 178 SkCanvas replayCanvas(bitmap_); | |
| 179 replayCanvas.clear(0xffffffff); | |
| 180 replayCanvas.clipRect(intoClip); | |
| 181 playback->draw(&replayCanvas); | |
| 182 | |
| 183 return bitmap_; | |
| 184 } | |
| 185 | |
| 186 private: | |
| 187 SkBitmap bitmap_; | |
| 188 int width_; | |
| 189 int height_; | |
| 190 }; | |
| 191 | |
| 192 } | |
| 193 | |
| 194 | |
| 195 | |
| 196 DEF_TEST(SkRecordingAccuracyXfermode, reporter) { | |
| 197 #define FINEGRAIN 0 | |
| 198 | |
| 199 const ExampleDrawer drawer; | |
| 200 | |
| 201 BitmapBackedCanvasStrategy golden; // This is the expected result. | |
| 202 TiledPictureRecorderStrategy tiled; | |
| 203 SkRecordingStrategy recording; | |
| 204 | |
| 205 golden.init(drawer.imageInfo()); | |
| 206 tiled.init(drawer.imageInfo()); | |
| 207 recording.init(drawer.imageInfo()); | |
| 208 | |
| 209 #if !FINEGRAIN | |
| 210 unsigned numErrors = 0; | |
| 211 SkString errors; | |
| 212 #endif | |
| 213 | |
| 214 for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) { | |
| 215 const SkRect& clip = SkRect::MakeXYWH(100,0,100,100); | |
| 216 SkXfermode::Mode mode = SkXfermode::Mode(iMode); | |
| 217 | |
| 218 const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode); | |
| 219 const SkBitmap& tiledBM = tiled.recordAndReplay(drawer, clip, mode); | |
| 220 const SkBitmap& recordingBM = recording.recordAndReplay(drawer, clip, mode); | |
| 221 | |
| 222 size_t pixelsSize = goldenBM.getSize(); | |
| 223 REPORTER_ASSERT( reporter, pixelsSize == tiledBM.getSize() ); | |
| 224 REPORTER_ASSERT( reporter, pixelsSize == recordingBM.getSize() ); | |
| 225 | |
| 226 // The pixel arrays should match. | |
| 227 #if FINEGRAIN | |
| 228 REPORTER_ASSERT_MESSAGE( reporter, | |
| 229 0==memcmp( goldenBM.getPixels(), tiledBM.getPixels( ), pixelsSize ), | |
| 230 "Tiled bitmap is wrong"); | |
| 231 REPORTER_ASSERT_MESSAGE( reporter, | |
| 232 0==memcmp( goldenBM.getPixels(), recordingBM.getPix els(), pixelsSize ) | |
| 233 "SkRecorder bitmap is wrong"); | |
| 234 #else | |
| 235 if ( memcmp( goldenBM.getPixels(), tiledBM.getPixels(), pixelsSize ) ) { | |
| 236 numErrors++; | |
| 237 SkString str; | |
| 238 str.printf("For SkXfermode %d %s: Tiled recorder bitmap is wrong\n", iMod e, SkXfermode::ModeName(mode)); | |
| 239 errors.append(str); | |
| 240 } | |
| 241 if ( memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ) ) { | |
| 242 numErrors++; | |
| 243 SkString str; | |
| 244 str.printf("For SkXfermode %d %s: SkRecording bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); | |
| 245 errors.append(str); | |
| 246 } | |
| 247 #endif | |
| 248 } | |
| 249 REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() ); | |
| 250 } | |
| OLD | NEW |