// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#if !defined(OS_WIN)
#include <unistd.h>
#endif
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "skia/ext/vector_canvas.h"
#include "skia/ext/vector_platform_device_emf_win.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkDashPathEffect.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/size.h"
namespace skia {
namespace {
const char kGenerateSwitch[] = "vector-canvas-generate";
// Lightweight HDC management.
class Context {
public:
Context() : context_(CreateCompatibleDC(NULL)) {
EXPECT_TRUE(context_);
}
~Context() {
DeleteDC(context_);
}
HDC context() const { return context_; }
private:
HDC context_;
DISALLOW_COPY_AND_ASSIGN(Context);
};
// Lightweight HBITMAP management.
class Bitmap {
public:
Bitmap(const Context& context, int x, int y) {
BITMAPINFOHEADER hdr;
hdr.biSize = sizeof(BITMAPINFOHEADER);
hdr.biWidth = x;
hdr.biHeight = -y; // Minus means top-down bitmap.
hdr.biPlanes = 1;
hdr.biBitCount = 32;
hdr.biCompression = BI_RGB; // No compression.
hdr.biSizeImage = 0;
hdr.biXPelsPerMeter = 1;
hdr.biYPelsPerMeter = 1;
hdr.biClrUsed = 0;
hdr.biClrImportant = 0;
bitmap_ = CreateDIBSection(context.context(),
reinterpret_cast<BITMAPINFO*>(&hdr), 0,
&data_, NULL, 0);
EXPECT_TRUE(bitmap_);
EXPECT_TRUE(SelectObject(context.context(), bitmap_));
}
~Bitmap() {
EXPECT_TRUE(DeleteObject(bitmap_));
}
private:
HBITMAP bitmap_;
void* data_;
DISALLOW_COPY_AND_ASSIGN(Bitmap);
};
// Lightweight raw-bitmap management. The image, once initialized, is immuable.
// It is mainly used for comparison.
class Image {
public:
// Creates the image from the given filename on disk.
explicit Image(const base::FilePath& filename) : ignore_alpha_(true) {
std::string compressed;
base::ReadFileToString(filename, &compressed);
EXPECT_TRUE(compressed.size());
SkBitmap bitmap;
EXPECT_TRUE(gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(compressed.data()),
compressed.size(), &bitmap));
SetSkBitmap(bitmap);
}
// Loads the image from a canvas.
Image(skia::PlatformCanvas& canvas) : ignore_alpha_(true) {
// Use a different way to access the bitmap. The normal way would be to
// query the SkBitmap.
skia::ScopedPlatformPaint scoped_platform_paint(&canvas);
HDC context = scoped_platform_paint.GetPlatformSurface();
HGDIOBJ bitmap = GetCurrentObject(context, OBJ_BITMAP);
EXPECT_TRUE(bitmap != NULL);
// Initialize the clip region to the entire bitmap.
BITMAP bitmap_data;
EXPECT_EQ(GetObject(bitmap, sizeof(BITMAP), &bitmap_data), sizeof(BITMAP));
width_ = bitmap_data.bmWidth;
height_ = bitmap_data.bmHeight;
row_length_ = bitmap_data.bmWidthBytes;
size_t size = row_length_ * height_;
data_.resize(size);
memcpy(&*data_.begin(), bitmap_data.bmBits, size);
}
// Loads the image from a canvas.
Image(const SkBitmap& bitmap) : ignore_alpha_(true) {
SetSkBitmap(bitmap);
}
int width() const { return width_; }
int height() const { return height_; }
int row_length() const { return row_length_; }
// Save the image to a png file. Used to create the initial test files.
void SaveToFile(const base::FilePath& filename) {
std::vector<unsigned char> compressed;
ASSERT_TRUE(gfx::PNGCodec::Encode(&*data_.begin(),
gfx::PNGCodec::FORMAT_BGRA,
gfx::Size(width_, height_),
row_length_,
true,
std::vector<gfx::PNGCodec::Comment>(),
&compressed));
ASSERT_TRUE(compressed.size());
FILE* f = base::OpenFile(filename, "wb");
ASSERT_TRUE(f);
ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
compressed.size());
base::CloseFile(f);
}
// Returns the percentage of the image that is different from the other,
// between 0 and 100.
double PercentageDifferent(const Image& rhs) const {
if (width_ != rhs.width_ ||
height_ != rhs.height_ ||
row_length_ != rhs.row_length_ ||
width_ == 0 ||
height_ == 0) {
return 100.; // When of different size or empty, they are 100% different.
}
// Compute pixels different in the overlap
int pixels_different = 0;
for (int y = 0; y < height_; ++y) {
for (int x = 0; x < width_; ++x) {
uint32_t lhs_pixel = pixel_at(x, y);
uint32_t rhs_pixel = rhs.pixel_at(x, y);
if (lhs_pixel != rhs_pixel)
++pixels_different;
}
}
// Like the WebKit ImageDiff tool, we define percentage different in terms
// of the size of the 'actual' bitmap.
double total_pixels = static_cast<double>(width_) *
static_cast<double>(height_);
return static_cast<double>(pixels_different) / total_pixels * 100.;
}
// Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
// depending on ignore_alpha_.
uint32 pixel_at(int x, int y) const {
EXPECT_TRUE(x >= 0 && x < width_);
EXPECT_TRUE(y >= 0 && y < height_);
const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
const uint32* data_row = data + y * row_length_ / sizeof(uint32);
if (ignore_alpha_)
return data_row[x] & 0xFFFFFF; // Strip out A.
else
return data_row[x];
}
protected:
void SetSkBitmap(const SkBitmap& bitmap) {
SkAutoLockPixels lock(bitmap);
width_ = bitmap.width();
height_ = bitmap.height();
row_length_ = static_cast<int>(bitmap.rowBytes());
size_t size = row_length_ * height_;
data_.resize(size);
memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size);
}
private:
// Pixel dimensions of the image.
int width_;
int height_;
// Length of a line in bytes.
int row_length_;
// Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
// 0xABGR).
std::vector<unsigned char> data_;
// Flag to signal if the comparison functions should ignore the alpha channel.
const bool ignore_alpha_;
DISALLOW_COPY_AND_ASSIGN(Image);
};
// Base for tests. Capability to process an image.
class ImageTest : public testing::Test {
public:
// In what state is the test running.
enum ProcessAction {
GENERATE,
COMPARE,
NOOP,
};
ImageTest(ProcessAction default_action)
: action_(default_action) {
}
protected:
virtual void SetUp() {
const testing::TestInfo& test_info =
*testing::UnitTest::GetInstance()->current_test_info();
PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_);
test_dir_ = test_dir_.AppendASCII("skia").
AppendASCII("ext").
AppendASCII("data").
AppendASCII(test_info.test_case_name()).
AppendASCII(test_info.name());
// Hack for a quick lowercase. We assume all the tests names are ASCII.
base::FilePath::StringType tmp(test_dir_.value());
for (size_t i = 0; i < tmp.size(); ++i)
tmp[i] = base::ToLowerASCII(tmp[i]);
test_dir_ = base::FilePath(tmp);
if (action_ == GENERATE) {
// Make sure the directory exist.
base::CreateDirectory(test_dir_);
}
}
// Returns the fully qualified path of a data file.
base::FilePath test_file(const base::FilePath::StringType& filename) const {
// Hack for a quick lowercase. We assume all the test data file names are
// ASCII.
#if defined(OS_WIN)
std::string tmp = base::UTF16ToASCII(filename);
#else
std::string tmp(filename);
#endif
for (size_t i = 0; i < tmp.size(); ++i)
tmp[i] = base::ToLowerASCII(tmp[i]);
return test_dir_.AppendASCII(tmp);
}
// Compares or saves the bitmap currently loaded in the context, depending on
// kGenerating value. Returns 0 on success or any positive value between ]0,
// 100] on failure. The return value is the percentage of difference between
// the image in the file and the image in the canvas.
double ProcessCanvas(skia::PlatformCanvas& canvas,
base::FilePath::StringType filename) const {
filename = filename + FILE_PATH_LITERAL(".png");
switch (action_) {
case GENERATE:
SaveImage(canvas, filename);
return 0.;
case COMPARE:
return CompareImage(canvas, filename);
case NOOP:
return 0;
default:
// Invalid state, returns that the image is 100 different.
return 100.;
}
}
// Compares the bitmap currently loaded in the context with the file. Returns
// the percentage of pixel difference between both images, between 0 and 100.
double CompareImage(skia::PlatformCanvas& canvas,
const base::FilePath::StringType& filename) const {
Image image1(canvas);
Image image2(test_file(filename));
double diff = image1.PercentageDifferent(image2);
return diff;
}
// Saves the bitmap currently loaded in the context into the file.
void SaveImage(skia::PlatformCanvas& canvas,
const base::FilePath::StringType& filename) const {
Image(canvas).SaveToFile(test_file(filename));
}
ProcessAction action_;
// Path to directory used to contain the test data.
base::FilePath test_dir_;
DISALLOW_COPY_AND_ASSIGN(ImageTest);
};
// Premultiply the Alpha channel on the R, B and G channels.
void Premultiply(SkBitmap bitmap) {
SkAutoLockPixels lock(bitmap);
for (int x = 0; x < bitmap.width(); ++x) {
for (int y = 0; y < bitmap.height(); ++y) {
uint32_t* pixel_addr = bitmap.getAddr32(x, y);
uint32_t color = *pixel_addr;
BYTE alpha = SkColorGetA(color);
if (!alpha) {
*pixel_addr = 0;
} else {
BYTE alpha_offset = alpha / 2;
*pixel_addr = SkColorSetARGB(
SkColorGetA(color),
(SkColorGetR(color) * 255 + alpha_offset) / alpha,
(SkColorGetG(color) * 255 + alpha_offset) / alpha,
(SkColorGetB(color) * 255 + alpha_offset) / alpha);
}
}
}
}
void LoadPngFileToSkBitmap(const base::FilePath& filename,
SkBitmap* bitmap,
bool is_opaque) {
std::string compressed;
base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed);
ASSERT_TRUE(compressed.size());
ASSERT_TRUE(gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(compressed.data()),
compressed.size(), bitmap));
EXPECT_EQ(is_opaque, bitmap->isOpaque());
Premultiply(*bitmap);
}
} // namespace
// Streams an image.
inline std::ostream& operator<<(std::ostream& out, const Image& image) {
return out << "Image(" << image.width() << ", "
<< image.height() << ", " << image.row_length() << ")";
}
// Runs simultaneously the same drawing commands on VectorCanvas and
// PlatformCanvas and compare the results.
class VectorCanvasTest : public ImageTest {
public:
typedef ImageTest parent;
VectorCanvasTest() : parent(CurrentMode()), compare_canvas_(true) {
}
protected:
virtual void SetUp() {
parent::SetUp();
Init(100);
number_ = 0;
}
virtual void TearDown() {
delete pcanvas_;
pcanvas_ = NULL;
delete vcanvas_;
vcanvas_ = NULL;
delete bitmap_;
bitmap_ = NULL;
delete context_;
context_ = NULL;
parent::TearDown();
}
void Init(int size) {
size_ = size;
context_ = new Context();
bitmap_ = new Bitmap(*context_, size_, size_);
vcanvas_ = new VectorCanvas(
VectorPlatformDeviceEmf::CreateDevice(
size_, size_, true, context_->context()));
pcanvas_ = CreatePlatformCanvas(size_, size_, false);
// Clear white.
vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
}
// Compares both canvas and returns the pixel difference in percentage between
// both images. 0 on success and ]0, 100] on failure.
double ProcessImage(const base::FilePath::StringType& filename) {
std::wstring number(base::StringPrintf(L"%02d_", number_++));
double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
if (!compare_canvas_)
return std::max(diff1, diff2);
Image image1(*vcanvas_);
Image image2(*pcanvas_);
double diff = image1.PercentageDifferent(image2);
return std::max(std::max(diff1, diff2), diff);
}
// Returns COMPARE, which is the default. If kGenerateSwitch command
// line argument is used to start this process, GENERATE is returned instead.
static ProcessAction CurrentMode() {
return CommandLine::ForCurrentProcess()->HasSwitch(kGenerateSwitch) ?
GENERATE : COMPARE;
}
// Length in x and y of the square canvas.
int size_;
// Current image number in the current test. Used to number of test files.
int number_;
// A temporary HDC to draw into.
Context* context_;
// Bitmap created inside context_.
Bitmap* bitmap_;
// Vector based canvas.
VectorCanvas* vcanvas_;
// Pixel based canvas.
PlatformCanvas* pcanvas_;
// When true (default), vcanvas_ and pcanvas_ contents are compared and
// verified to be identical.
bool compare_canvas_;
};
////////////////////////////////////////////////////////////////////////////////
// Actual tests
#if !defined(USE_AURA) // http://crbug.com/154358
TEST_F(VectorCanvasTest, BasicDrawing) {
EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
<< L"clean";
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clean")));
// Clear white.
{
vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawARGB")));
// Diagonal line top-left to bottom-right.
{
SkPaint paint;
// Default color is black.
vcanvas_->drawLine(10, 10, 90, 90, paint);
pcanvas_->drawLine(10, 10, 90, 90, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_black")));
// Rect.
{
SkPaint paint;
paint.setColor(SK_ColorGREEN);
vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_green")));
// A single-point rect doesn't leave any mark.
{
SkPaint paint;
paint.setColor(SK_ColorBLUE);
vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
// Rect.
{
SkPaint paint;
paint.setColor(SK_ColorBLUE);
vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
// Empty again
{
vcanvas_->drawPaint(SkPaint());
pcanvas_->drawPaint(SkPaint());
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPaint_black")));
// Horizontal line left to right.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(10, 20, 90, 20, paint);
pcanvas_->drawLine(10, 20, 90, 20, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_left_to_right")));
// Vertical line downward.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(30, 10, 30, 90, paint);
pcanvas_->drawLine(30, 10, 30, 90, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_red")));
}
TEST_F(VectorCanvasTest, Circles) {
// There is NO WAY to make them agree. At least verify that the output doesn't
// change across versions. This test is disabled. See bug 1060231.
compare_canvas_ = false;
// Stroked Circle.
{
SkPaint paint;
SkPath path;
path.addCircle(50, 75, 10);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorMAGENTA);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke")));
// Filled Circle.
{
SkPaint paint;
SkPath path;
path.addCircle(50, 25, 10);
paint.setStyle(SkPaint::kFill_Style);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_fill")));
// Stroked Circle over.
{
SkPaint paint;
SkPath path;
path.addCircle(50, 25, 10);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorBLUE);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_over_strike")));
// Stroke and Fill Circle.
{
SkPaint paint;
SkPath path;
path.addCircle(12, 50, 10);
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setColor(SK_ColorRED);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke_and_fill")));
// Line + Quad + Cubic.
{
SkPaint paint;
SkPath path;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorGREEN);
path.moveTo(1, 1);
path.lineTo(60, 40);
path.lineTo(80, 80);
path.quadTo(20, 50, 10, 90);
path.quadTo(50, 20, 90, 10);
path.cubicTo(20, 40, 50, 50, 10, 10);
path.cubicTo(30, 20, 50, 50, 90, 10);
path.addRect(90, 90, 95, 96);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("mixed_stroke")));
}
TEST_F(VectorCanvasTest, LineOrientation) {
// There is NO WAY to make them agree. At least verify that the output doesn't
// change across versions. This test is disabled. See bug 1060231.
compare_canvas_ = false;
// Horizontal lines.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
// Left to right.
vcanvas_->drawLine(10, 20, 90, 20, paint);
pcanvas_->drawLine(10, 20, 90, 20, paint);
// Right to left.
vcanvas_->drawLine(90, 30, 10, 30, paint);
pcanvas_->drawLine(90, 30, 10, 30, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal")));
// Vertical lines.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
// Top down.
vcanvas_->drawLine(20, 10, 20, 90, paint);
pcanvas_->drawLine(20, 10, 20, 90, paint);
// Bottom up.
vcanvas_->drawLine(30, 90, 30, 10, paint);
pcanvas_->drawLine(30, 90, 30, 10, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical")));
// Try again with a 180 degres rotation.
vcanvas_->rotate(180);
pcanvas_->rotate(180);
// Horizontal lines (rotated).
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(-10, -25, -90, -25, paint);
pcanvas_->drawLine(-10, -25, -90, -25, paint);
vcanvas_->drawLine(-90, -35, -10, -35, paint);
pcanvas_->drawLine(-90, -35, -10, -35, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal_180")));
// Vertical lines (rotated).
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(-25, -10, -25, -90, paint);
pcanvas_->drawLine(-25, -10, -25, -90, paint);
vcanvas_->drawLine(-35, -90, -35, -10, paint);
pcanvas_->drawLine(-35, -90, -35, -10, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical_180")));
}
TEST_F(VectorCanvasTest, PathOrientation) {
// There is NO WAY to make them agree. At least verify that the output doesn't
// change across versions. This test is disabled. See bug 1060231.
compare_canvas_ = false;
// Horizontal lines.
{
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorRED);
SkPath path;
SkPoint start;
start.set(10, 20);
SkPoint end;
end.set(90, 20);
path.moveTo(start);
path.lineTo(end);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_ltr")));
// Horizontal lines.
{
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorRED);
SkPath path;
SkPoint start;
start.set(90, 30);
SkPoint end;
end.set(10, 30);
path.moveTo(start);
path.lineTo(end);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_rtl")));
}
TEST_F(VectorCanvasTest, DiagonalLines) {
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(10, 10, 90, 90, paint);
pcanvas_->drawLine(10, 10, 90, 90, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("nw-se")));
// Starting here, there is NO WAY to make them agree. At least verify that the
// output doesn't change across versions. This test is disabled. See bug
// 1060231.
compare_canvas_ = false;
vcanvas_->drawLine(10, 95, 90, 15, paint);
pcanvas_->drawLine(10, 95, 90, 15, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("sw-ne")));
vcanvas_->drawLine(90, 10, 10, 90, paint);
pcanvas_->drawLine(90, 10, 10, 90, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("ne-sw")));
vcanvas_->drawLine(95, 90, 15, 10, paint);
pcanvas_->drawLine(95, 90, 15, 10, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("se-nw")));
}
#if defined(OS_WIN)
#define MAYBE_PathEffects DISABLED_PathEffects
#else
#define MAYBE_PathEffects PathEffects
#endif
TEST_F(VectorCanvasTest, MAYBE_PathEffects) {
{
SkPaint paint;
SkScalar intervals[] = { 1, 1 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
vcanvas_->drawLine(10, 10, 90, 10, paint);
pcanvas_->drawLine(10, 10, 90, 10, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_line")));
// Starting here, there is NO WAY to make them agree. At least verify that the
// output doesn't change across versions. This test is disabled. See bug
// 1060231.
compare_canvas_ = false;
{
SkPaint paint;
SkScalar intervals[] = { 3, 5 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
path.moveTo(10, 15);
path.lineTo(90, 15);
path.lineTo(90, 90);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_path")));
{
SkPaint paint;
SkScalar intervals[] = { 2, 1 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_rect")));
// This thing looks like it has been drawn by a 3 years old kid. I haven't
// filed a bug on this since I guess nobody is expecting this to look nice.
{
SkPaint paint;
SkScalar intervals[] = { 1, 1 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
path.addCircle(50, 75, 10);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle")));
}
}
TEST_F(VectorCanvasTest, Bitmaps) {
{
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap, true);
vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("opaque")));
}
{
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap, false);
vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("alpha")));
}
}
TEST_F(VectorCanvasTest, ClippingRect) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rect")));
}
TEST_F(VectorCanvasTest, ClippingPath) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkPath path;
path.addCircle(20, 20, 10);
vcanvas_->clipPath(path);
pcanvas_->clipPath(path);
vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("path")));
}
TEST_F(VectorCanvasTest, ClippingCombined) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
SkPath path;
path.addCircle(20, 20, 10);
vcanvas_->clipPath(path, SkRegion::kUnion_Op);
pcanvas_->clipPath(path, SkRegion::kUnion_Op);
vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("combined")));
}
TEST_F(VectorCanvasTest, ClippingIntersect) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
SkPath path;
path.addCircle(23, 23, 15);
vcanvas_->clipPath(path);
pcanvas_->clipPath(path);
vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("intersect")));
}
TEST_F(VectorCanvasTest, ClippingClean) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
{
SkAutoCanvasRestore acrv(vcanvas_, true);
SkAutoCanvasRestore acrp(pcanvas_, true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clipped")));
}
{
// Verify that the clipping region has been fixed back.
vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("unclipped")));
}
}
// See http://crbug.com/26938
TEST_F(VectorCanvasTest, DISABLED_Matrix) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
{
vcanvas_->translate(15, 3);
pcanvas_->translate(15, 3);
vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate1")));
}
{
vcanvas_->translate(-30, -23);
pcanvas_->translate(-30, -23);
vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate2")));
}
vcanvas_->resetMatrix();
pcanvas_->resetMatrix();
// For scaling and rotation, they use a different algorithm (nearest
// neighborhood vs smoothing). At least verify that the output doesn't change
// across versions.
compare_canvas_ = false;
{
vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("scale")));
}
vcanvas_->resetMatrix();
pcanvas_->resetMatrix();
{
vcanvas_->rotate(67);
pcanvas_->rotate(67);
vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rotate")));
}
}
#endif // !defined(USE_AURA)
} // namespace skia