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 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) c onst { | |
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(fImageInfo.width(),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 #if 0 | |
70 void writeToFile(std::string fname, SkPicture* picture) { | |
mtklein
2014/09/12 20:57:17
remove before submitting?
| |
71 SkFILEWStream fs(fname.c_str()); | |
72 picture->serialize(&fs,NULL); | |
73 fs.flush(); | |
74 } | |
75 #endif | |
76 | |
77 class RecordingStrategy { | |
78 public: | |
79 virtual ~RecordingStrategy() {} | |
80 virtual void init(const SkImageInfo&) = 0; | |
81 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
82 const SkRect& intoClip, | |
mtklein
2014/09/12 20:57:17
some of the indents seem to have gone crazy here.
| |
83 SkXfermode::Mode) = 0; | |
84 }; | |
85 | |
86 class BitmapBackedCanvasStrategy : public RecordingStrategy { | |
87 // This version just draws into a bitmap-backed canvas. | |
88 public: | |
89 BitmapBackedCanvasStrategy() {} | |
90 | |
91 virtual void init(const SkImageInfo& imageInfo) { | |
92 fBitmap.allocPixels(imageInfo); | |
93 } | |
94 | |
95 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
96 const SkRect& intoClip, | |
97 SkXfermode::Mode mode) { | |
98 SkCanvas canvas(fBitmap); | |
99 canvas.clear(0xffffffff); | |
100 // Note that the scene is drawn just into the clipped region! | |
101 canvas.clipRect(intoClip); | |
102 drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide... | |
103 return fBitmap; | |
104 } | |
105 | |
106 private: | |
107 SkBitmap fBitmap; | |
108 }; | |
109 | |
110 class TiledPictureRecorderStrategy : public RecordingStrategy { | |
111 // This version draws the entire scene into an SkPictureRecorder, | |
112 // then replays through a clip rectangle. | |
113 // As of this writing, SkPictureRecorder is a deprecated API, but is | |
114 // still used in Chrome. | |
115 // It's also the one that proved to be buggy. | |
116 public: | |
117 TiledPictureRecorderStrategy() {} | |
118 | |
119 virtual void init(const SkImageInfo& imageInfo) { | |
120 fBitmap.allocPixels(imageInfo); | |
121 fWidth = imageInfo.width(); | |
122 fHeight= imageInfo.height(); | |
123 } | |
124 | |
125 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
126 const SkRect& intoClip, | |
127 SkXfermode::Mode mode) { | |
128 SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; | |
129 SkTileGridFactory factory(tileGridInfo); | |
130 SkPictureRecorder recorder; | |
131 SkRect canvasRect(SkRect::MakeWH(fWidth,fHeight)); | |
132 SkCanvas* canvas = recorder.beginRecording( fWidth, fHeight, &factory); | |
mtklein
2014/09/12 20:38:27
If you'd like to test new vs. old, the better thin
| |
133 drawer.draw(canvas, canvasRect, mode); | |
134 SkAutoTDelete<SkPicture> picture(recorder.endRecording()); | |
135 | |
136 SkCanvas replayCanvas(fBitmap); | |
137 replayCanvas.clear(0xffffffff); | |
138 replayCanvas.clipRect(intoClip); | |
139 picture->playback(&replayCanvas); | |
140 | |
141 return fBitmap; | |
142 } | |
143 | |
144 private: | |
145 SkBitmap fBitmap; | |
146 int fWidth; | |
147 int fHeight; | |
148 }; | |
149 | |
150 class SkRecordingStrategy : public RecordingStrategy { | |
151 // This version draws the entire scene via the SkRecording API. | |
152 // then replays through a clip rectangle. | |
153 // As of this writing, SkRecording is an experimental API. | |
154 public: | |
155 SkRecordingStrategy() {} | |
156 | |
157 virtual void init(const SkImageInfo& imageInfo) { | |
158 fBitmap.allocPixels(imageInfo); | |
159 fWidth = imageInfo.width(); | |
160 fHeight= imageInfo.height(); | |
161 } | |
162 | |
163 virtual const SkBitmap& recordAndReplay(const Drawer& drawer, | |
164 const SkRect& intoClip, | |
165 SkXfermode::Mode mode) { | |
166 EXPERIMENTAL::SkRecording recording(fWidth, fHeight); | |
167 SkRect canvasRect(SkRect::MakeWH(fWidth,fHeight)); | |
168 drawer.draw(recording.canvas(), canvasRect, mode); | |
169 SkAutoTDelete<const EXPERIMENTAL::SkPlayback> playback(recording.release Playback()); | |
170 | |
171 SkCanvas replayCanvas(fBitmap); | |
172 replayCanvas.clear(0xffffffff); | |
173 replayCanvas.clipRect(intoClip); | |
174 playback->draw(&replayCanvas); | |
175 | |
176 return fBitmap; | |
177 } | |
178 | |
179 private: | |
180 SkBitmap fBitmap; | |
181 int fWidth; | |
182 int fHeight; | |
183 }; | |
184 | |
185 } | |
186 | |
187 | |
188 | |
189 DEF_TEST(SkRecordingAccuracyXfermode, reporter) { | |
190 #define FINEGRAIN 0 | |
191 | |
192 const Drawer drawer; | |
193 | |
194 BitmapBackedCanvasStrategy golden; // This is the expected result. | |
195 TiledPictureRecorderStrategy tiled; | |
196 SkRecordingStrategy recording; | |
197 | |
198 golden.init(drawer.imageInfo()); | |
199 tiled.init(drawer.imageInfo()); | |
200 recording.init(drawer.imageInfo()); | |
201 | |
202 #if !FINEGRAIN | |
203 unsigned numErrors = 0; | |
204 SkString errors; | |
205 #endif | |
206 | |
207 for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) { | |
208 const SkRect& clip = SkRect::MakeXYWH(100,0,100,100); | |
209 SkXfermode::Mode mode = SkXfermode::Mode(iMode); | |
210 | |
211 const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode); | |
212 const SkBitmap& tiledBM = tiled.recordAndReplay(drawer, clip, mode); | |
213 const SkBitmap& recordingBM = recording.recordAndReplay(drawer, clip, mo de); | |
214 | |
215 size_t pixelsSize = goldenBM.getSize(); | |
216 REPORTER_ASSERT( reporter, pixelsSize == tiledBM.getSize() ); | |
217 REPORTER_ASSERT( reporter, pixelsSize == recordingBM.getSize() ); | |
218 | |
219 // The pixel arrays should match. | |
220 #if FINEGRAIN | |
221 REPORTER_ASSERT_MESSAGE( reporter, | |
222 0==memcmp( goldenBM.getPixels(), tiledBM.getPix els(), pixelsSize ), | |
223 "Tiled bitmap is wrong"); | |
224 REPORTER_ASSERT_MESSAGE( reporter, | |
225 0==memcmp( goldenBM.getPixels(), recordingBM.ge tPixels(), pixelsSize ), | |
226 "SkRecorder bitmap is wrong"); | |
227 #else | |
228 if ( memcmp( goldenBM.getPixels(), tiledBM.getPixels(), pixelsSize ) ) { | |
229 numErrors++; | |
230 SkString str; | |
231 str.printf("For SkXfermode %d %s: Tiled recorder bitmap is wrong\ n", iMode, SkXfermode::ModeName(mode)); | |
232 errors.append(str); | |
233 } | |
234 if ( memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ) ) { | |
235 numErrors++; | |
236 SkString str; | |
237 str.printf("For SkXfermode %d %s: SkRecording bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); | |
238 errors.append(str); | |
239 } | |
240 #endif | |
241 } | |
242 #if !FINEGRAIN | |
243 REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() ); | |
mtklein
2014/09/12 20:57:17
might just move this line up into the #else?
| |
244 #endif | |
245 } | |
OLD | NEW |