Chromium Code Reviews| 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 |
| +} |