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 |