Index: tests/RecordingXfermodeTest.cpp |
diff --git a/tests/RecordingXfermodeTest.cpp b/tests/RecordingXfermodeTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ce0df00b699d7442c73b30b832b97b8d8f476e47 |
--- /dev/null |
+++ b/tests/RecordingXfermodeTest.cpp |
@@ -0,0 +1,245 @@ |
+/* |
+ * Copyright 2014 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "Test.h" |
+ |
+#include "../include/core/SkCanvas.h" |
+#include "../include/core/SkPicture.h" |
+#include "../include/core/SkStream.h" |
+#include "../include/core/SkString.h" |
+#include "../include/record/SkRecording.h" |
+#include "../include/core/SkPictureRecorder.h" |
+#include <cstring> |
+ |
+// Verify that replay of a recording into a clipped canvas |
+// produces the correct bitmap. |
+// This arose from http://crbug.com/401593 which has |
+// https://code.google.com/p/skia/issues/detail?id=1291 as its root cause. |
+ |
+ |
+namespace { |
+ |
+class Drawer { |
+ public: |
+ explicit Drawer() |
+ : fImageInfo(SkImageInfo::MakeN32Premul(200,100)) |
+ { |
+ fCircleBM.allocPixels( SkImageInfo::MakeN32Premul(100,100) ); |
+ SkCanvas canvas(fCircleBM); |
+ canvas.clear(0xffffffff); |
+ SkPaint circlePaint; |
+ circlePaint.setColor(0xff000000); |
+ canvas.drawCircle(50,50,50,circlePaint); |
+ } |
+ |
+ const SkImageInfo& imageInfo() const { return fImageInfo; } |
+ |
+ void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode mode) const { |
+ SkPaint greenPaint; |
+ greenPaint.setColor(0xff008000); |
+ SkPaint blackPaint; |
+ blackPaint.setColor(0xff000000); |
+ SkPaint whitePaint; |
+ whitePaint.setColor(0xffffffff); |
+ SkPaint layerPaint; |
+ layerPaint.setColor(0xff000000); |
+ layerPaint.setXfermodeMode(mode); |
+ SkRect canvasRect(SkRect::MakeWH(fImageInfo.width(),fImageInfo.height())); |
+ |
+ canvas->clipRect(clipRect); |
+ canvas->clear(0xff000000); |
+ |
+ canvas->saveLayer(NULL,&blackPaint); |
+ canvas->drawRect(canvasRect,greenPaint); |
+ canvas->saveLayer(NULL,&layerPaint); |
+ canvas->drawBitmapRect(fCircleBM,SkRect::MakeXYWH(20,20,60,60),&blackPaint); |
+ canvas->restore(); |
+ canvas->restore(); |
+ } |
+ |
+ private: |
+ const SkImageInfo fImageInfo; |
+ SkBitmap fCircleBM; |
+}; |
+ |
+#if 0 |
+void writeToFile(std::string fname, SkPicture* picture) { |
mtklein
2014/09/12 20:57:17
remove before submitting?
|
+ SkFILEWStream fs(fname.c_str()); |
+ picture->serialize(&fs,NULL); |
+ fs.flush(); |
+} |
+#endif |
+ |
+class RecordingStrategy { |
+ public: |
+ virtual ~RecordingStrategy() {} |
+ virtual void init(const SkImageInfo&) = 0; |
+ virtual const SkBitmap& recordAndReplay(const Drawer& drawer, |
+ const SkRect& intoClip, |
mtklein
2014/09/12 20:57:17
some of the indents seem to have gone crazy here.
|
+ SkXfermode::Mode) = 0; |
+}; |
+ |
+class BitmapBackedCanvasStrategy : public RecordingStrategy { |
+ // This version just draws into a bitmap-backed canvas. |
+ public: |
+ BitmapBackedCanvasStrategy() {} |
+ |
+ virtual void init(const SkImageInfo& imageInfo) { |
+ fBitmap.allocPixels(imageInfo); |
+ } |
+ |
+ virtual const SkBitmap& recordAndReplay(const Drawer& drawer, |
+ const SkRect& intoClip, |
+ SkXfermode::Mode mode) { |
+ SkCanvas canvas(fBitmap); |
+ canvas.clear(0xffffffff); |
+ // Note that the scene is drawn just into the clipped region! |
+ canvas.clipRect(intoClip); |
+ drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide... |
+ return fBitmap; |
+ } |
+ |
+ private: |
+ SkBitmap fBitmap; |
+}; |
+ |
+class TiledPictureRecorderStrategy : public RecordingStrategy { |
+ // This version draws the entire scene into an SkPictureRecorder, |
+ // then replays through a clip rectangle. |
+ // As of this writing, SkPictureRecorder is a deprecated API, but is |
+ // still used in Chrome. |
+ // It's also the one that proved to be buggy. |
+ public: |
+ TiledPictureRecorderStrategy() {} |
+ |
+ virtual void init(const SkImageInfo& imageInfo) { |
+ fBitmap.allocPixels(imageInfo); |
+ fWidth = imageInfo.width(); |
+ fHeight= imageInfo.height(); |
+ } |
+ |
+ virtual const SkBitmap& recordAndReplay(const Drawer& drawer, |
+ const SkRect& intoClip, |
+ SkXfermode::Mode mode) { |
+ SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} }; |
+ SkTileGridFactory factory(tileGridInfo); |
+ SkPictureRecorder recorder; |
+ SkRect canvasRect(SkRect::MakeWH(fWidth,fHeight)); |
+ 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
|
+ drawer.draw(canvas, canvasRect, mode); |
+ SkAutoTDelete<SkPicture> picture(recorder.endRecording()); |
+ |
+ SkCanvas replayCanvas(fBitmap); |
+ replayCanvas.clear(0xffffffff); |
+ replayCanvas.clipRect(intoClip); |
+ picture->playback(&replayCanvas); |
+ |
+ return fBitmap; |
+ } |
+ |
+ private: |
+ SkBitmap fBitmap; |
+ int fWidth; |
+ int fHeight; |
+}; |
+ |
+class SkRecordingStrategy : public RecordingStrategy { |
+ // This version draws the entire scene via the SkRecording API. |
+ // then replays through a clip rectangle. |
+ // As of this writing, SkRecording is an experimental API. |
+ public: |
+ SkRecordingStrategy() {} |
+ |
+ virtual void init(const SkImageInfo& imageInfo) { |
+ fBitmap.allocPixels(imageInfo); |
+ fWidth = imageInfo.width(); |
+ fHeight= imageInfo.height(); |
+ } |
+ |
+ virtual const SkBitmap& recordAndReplay(const Drawer& drawer, |
+ const SkRect& intoClip, |
+ SkXfermode::Mode mode) { |
+ EXPERIMENTAL::SkRecording recording(fWidth, fHeight); |
+ SkRect canvasRect(SkRect::MakeWH(fWidth,fHeight)); |
+ drawer.draw(recording.canvas(), canvasRect, mode); |
+ SkAutoTDelete<const EXPERIMENTAL::SkPlayback> playback(recording.releasePlayback()); |
+ |
+ SkCanvas replayCanvas(fBitmap); |
+ replayCanvas.clear(0xffffffff); |
+ replayCanvas.clipRect(intoClip); |
+ playback->draw(&replayCanvas); |
+ |
+ return fBitmap; |
+ } |
+ |
+ private: |
+ SkBitmap fBitmap; |
+ int fWidth; |
+ int fHeight; |
+}; |
+ |
+} |
+ |
+ |
+ |
+DEF_TEST(SkRecordingAccuracyXfermode, reporter) { |
+#define FINEGRAIN 0 |
+ |
+ const Drawer drawer; |
+ |
+ BitmapBackedCanvasStrategy golden; // This is the expected result. |
+ TiledPictureRecorderStrategy tiled; |
+ SkRecordingStrategy recording; |
+ |
+ golden.init(drawer.imageInfo()); |
+ tiled.init(drawer.imageInfo()); |
+ recording.init(drawer.imageInfo()); |
+ |
+#if !FINEGRAIN |
+ unsigned numErrors = 0; |
+ SkString errors; |
+#endif |
+ |
+ for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) { |
+ const SkRect& clip = SkRect::MakeXYWH(100,0,100,100); |
+ SkXfermode::Mode mode = SkXfermode::Mode(iMode); |
+ |
+ const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode); |
+ const SkBitmap& tiledBM = tiled.recordAndReplay(drawer, clip, mode); |
+ const SkBitmap& recordingBM = recording.recordAndReplay(drawer, clip, mode); |
+ |
+ size_t pixelsSize = goldenBM.getSize(); |
+ REPORTER_ASSERT( reporter, pixelsSize == tiledBM.getSize() ); |
+ REPORTER_ASSERT( reporter, pixelsSize == recordingBM.getSize() ); |
+ |
+ // The pixel arrays should match. |
+#if FINEGRAIN |
+ REPORTER_ASSERT_MESSAGE( reporter, |
+ 0==memcmp( goldenBM.getPixels(), tiledBM.getPixels(), pixelsSize ), |
+ "Tiled bitmap is wrong"); |
+ REPORTER_ASSERT_MESSAGE( reporter, |
+ 0==memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ), |
+ "SkRecorder bitmap is wrong"); |
+#else |
+ if ( memcmp( goldenBM.getPixels(), tiledBM.getPixels(), pixelsSize ) ) { |
+ numErrors++; |
+ SkString str; |
+ str.printf("For SkXfermode %d %s: Tiled recorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); |
+ errors.append(str); |
+ } |
+ if ( memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ) ) { |
+ numErrors++; |
+ SkString str; |
+ str.printf("For SkXfermode %d %s: SkRecording bitmap is wrong\n", iMode, SkXfermode::ModeName(mode)); |
+ errors.append(str); |
+ } |
+#endif |
+ } |
+#if !FINEGRAIN |
+ 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?
|
+#endif |
+} |