blob: d35f6bd93564ccbd76aedd5003d4bf48a1aaaf12 [file] [log] [blame]
/*
* 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 "include/core/SkBBHFactory.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkScalar.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "tests/Test.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)) {
auto surf = SkSurface::MakeRasterN32Premul(100, 100);
surf->getCanvas()->clear(0xffffffff);
SkPaint circlePaint;
circlePaint.setColor(0xff000000);
surf->getCanvas()->drawCircle(50, 50, 50, circlePaint);
fCircleImage = surf->makeImageSnapshot();
}
const SkImageInfo& imageInfo() const { return fImageInfo; }
void draw(SkCanvas* canvas, const SkRect& clipRect, SkBlendMode mode) const {
SkPaint greenPaint;
greenPaint.setColor(0xff008000);
SkPaint blackPaint;
blackPaint.setColor(0xff000000);
SkPaint whitePaint;
whitePaint.setColor(0xffffffff);
SkPaint layerPaint;
layerPaint.setColor(0xff000000);
layerPaint.setBlendMode(mode);
SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),
SkIntToScalar(fImageInfo.height())));
canvas->clipRect(clipRect);
canvas->clear(0xff000000);
canvas->saveLayer(nullptr, &blackPaint);
canvas->drawRect(canvasRect, greenPaint);
canvas->saveLayer(nullptr, &layerPaint);
canvas->drawImageRect(fCircleImage, SkRect::MakeXYWH(20,20,60,60),
SkSamplingOptions(), &blackPaint);
canvas->restore();
canvas->restore();
}
private:
const SkImageInfo fImageInfo;
sk_sp<SkImage> fCircleImage;
};
class RecordingStrategy {
public:
virtual ~RecordingStrategy() {}
virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
const SkRect& intoClip,
SkBlendMode) = 0;
};
class BitmapBackedCanvasStrategy : public RecordingStrategy {
// This version just draws into a bitmap-backed canvas.
public:
BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) {
fBitmap.allocPixels(imageInfo);
}
const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
SkBlendMode mode) override {
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 PictureStrategy : public RecordingStrategy {
// This version draws the entire scene into an SkPictureRecorder.
// Then it then replays the scene through a clip rectangle.
// This backend proved to be buggy.
public:
PictureStrategy(const SkImageInfo& imageInfo) {
fBitmap.allocPixels(imageInfo);
fWidth = imageInfo.width();
fHeight = imageInfo.height();
}
const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
SkBlendMode mode) override {
SkRTreeFactory factory;
SkPictureRecorder recorder;
SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth),
SkIntToScalar(fHeight),
&factory);
drawer.draw(canvas, canvasRect, mode);
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
SkCanvas replayCanvas(fBitmap);
replayCanvas.clear(0xffffffff);
replayCanvas.clipRect(intoClip);
picture->playback(&replayCanvas);
return fBitmap;
}
private:
SkBitmap fBitmap;
int fWidth;
int fHeight;
};
} // namespace
DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
#define FINEGRAIN 0
const Drawer drawer;
BitmapBackedCanvasStrategy golden(drawer.imageInfo());
PictureStrategy picture(drawer.imageInfo());
#if !FINEGRAIN
unsigned numErrors = 0;
SkString errors;
#endif
for (int iMode = 0; iMode < kSkBlendModeCount; iMode++) {
const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
SkBlendMode mode = SkBlendMode(iMode);
const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);
size_t pixelsSize = goldenBM.computeByteSize();
REPORTER_ASSERT(reporter, pixelsSize == pictureBM.computeByteSize());
// The pixel arrays should match.
#if FINEGRAIN
REPORTER_ASSERT(reporter,
0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
#else
if (0 != memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
numErrors++;
errors.appendf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n",
iMode, SkBlendMode_Name(mode));
}
#endif
}
#if !FINEGRAIN
REPORTER_ASSERT(reporter, 0 == numErrors, "%s", errors.c_str());
#endif
}