// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "base/gfx/vector_canvas.h" #include #include "base/command_line.h" #include "base/file_util.h" #include "base/gfx/bitmap_header.h" #include "base/gfx/png_decoder.h" #include "base/gfx/png_encoder.h" #include "base/gfx/size.h" #include "base/path_service.h" #include "base/string_util.h" #include "base/win_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "SkDashPathEffect.h" namespace { const wchar_t* const kGenerateSwitch = L"vector-canvas-generate"; // Base class for unit test that uses data. It initializes a directory path // based on the test's name. class DataUnitTest : public testing::Test { public: DataUnitTest(const std::wstring& base_path) : base_path_(base_path) { } protected: // Load the test's data path. virtual void SetUp() { const testing::TestInfo& test_info = *testing::UnitTest::GetInstance()->current_test_info(); PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_); file_util::AppendToPath(&test_dir_, base_path_); file_util::AppendToPath(&test_dir_, L"data"); file_util::AppendToPath(&test_dir_, ASCIIToWide(test_info.test_case_name())); file_util::AppendToPath(&test_dir_, ASCIIToWide(test_info.name())); // Hack for a quick lowercase. We assume all the tests names are ASCII. std::string tmp(WideToASCII(test_dir_)); for (size_t i = 0; i < tmp.size(); ++i) tmp[i] = ToLowerASCII(tmp[i]); test_dir_ = ASCIIToWide(tmp); } // Returns the fully qualified path of directory containing test data files. const std::wstring& test_dir() const { return test_dir_; } // Returns the fully qualified path of a data file. std::wstring test_file(const std::wstring& filename) const { // Hack for a quick lowercase. We assume all the test data file names are // ASCII. std::string tmp(WideToASCII(filename)); for (size_t i = 0; i < tmp.size(); ++i) tmp[i] = ToLowerASCII(tmp[i]); std::wstring path(test_dir()); file_util::AppendToPath(&path, ASCIIToWide(tmp)); return path; } private: // Path where the unit test is coming from: base, net, chrome, etc. std::wstring base_path_; // Path to directory used to contain the test data. std::wstring test_dir_; DISALLOW_EVIL_CONSTRUCTORS(DataUnitTest); }; // 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_EVIL_CONSTRUCTORS(Context); }; // Lightweight HBITMAP management. class Bitmap { public: Bitmap(const Context& context, int x, int y) { BITMAPINFOHEADER hdr; gfx::CreateBitmapHeader(x, y, &hdr); bitmap_ = CreateDIBSection(context.context(), reinterpret_cast(&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_EVIL_CONSTRUCTORS(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. Image(const std::wstring& filename) : ignore_alpha_(true) { std::string compressed; file_util::ReadFileToString(filename, &compressed); EXPECT_TRUE(compressed.size()); int w; int h; EXPECT_TRUE(PNGDecoder::Decode( reinterpret_cast(compressed.c_str()), compressed.size(), PNGDecoder::FORMAT_BGRA, &data_, &w, &h)); size_.SetSize(w, h); row_length_ = w * sizeof(uint32); } // Loads the image from a canvas. Image(const gfx::PlatformCanvasWin& canvas) : ignore_alpha_(true) { // Use a different way to access the bitmap. The normal way would be to // query the SkBitmap. HDC context = canvas.getTopPlatformDevice().getBitmapDC(); 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)); size_.SetSize(bitmap_data.bmWidth, bitmap_data.bmHeight); row_length_ = bitmap_data.bmWidthBytes; size_t size = row_length_ * size_.height(); data_.resize(size); memcpy(&*data_.begin(), bitmap_data.bmBits, size); } // Loads the image from a canvas. Image(const SkBitmap& bitmap) : ignore_alpha_(true) { SkAutoLockPixels lock(bitmap); size_.SetSize(bitmap.width(), bitmap.height()); row_length_ = static_cast(bitmap.rowBytes()); size_t size = row_length_ * size_.height(); data_.resize(size); memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size); } const gfx::Size& size() const { return size_; } int row_length() const { return row_length_; } // Save the image to a png file. Used to create the initial test files. void SaveToFile(const std::wstring& filename) { std::vector compressed; ASSERT_TRUE(PNGEncoder::Encode(&*data_.begin(), PNGEncoder::FORMAT_BGRA, size_.width(), size_.height(), row_length_, true, &compressed)); ASSERT_TRUE(compressed.size()); FILE* f; ASSERT_EQ(_wfopen_s(&f, filename.c_str(), L"wbS"), 0); ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f), compressed.size()); fclose(f); } // Returns the percentage of the image that is different from the other, // between 0 and 100. double PercentageDifferent(const Image& rhs) const { if (size_ != rhs.size_ || row_length_ != rhs.row_length_ || size_.width() == 0 || size_.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 < size_.height(); ++y) { for (int x = 0; x < size_.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(size_.width()) * static_cast(size_.height()); return static_cast(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 < size_.width()); EXPECT_TRUE(y >= 0 && y < size_.height()); const uint32* data = reinterpret_cast(&*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]; } private: // Pixel dimensions of the image. gfx::Size size_; // 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 data_; // Flag to signal if the comparison functions should ignore the alpha channel. const bool ignore_alpha_; DISALLOW_EVIL_CONSTRUCTORS(Image); }; // Base for tests. Capability to process an image. class ImageTest : public DataUnitTest { public: typedef DataUnitTest parent; // In what state is the test running. enum ProcessAction { GENERATE, COMPARE, NOOP, }; ImageTest(const std::wstring& base_path, ProcessAction default_action) : parent(base_path), action_(default_action) { } protected: virtual void SetUp() { parent::SetUp(); if (action_ == GENERATE) { // Make sure the directory exist. file_util::CreateDirectory(test_dir()); } } // 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(const gfx::PlatformCanvasWin& canvas, std::wstring filename) const { filename += L".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(const gfx::PlatformCanvasWin& canvas, const std::wstring& 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(const gfx::PlatformCanvasWin& canvas, const std::wstring& filename) const { Image(canvas).SaveToFile(test_file(filename)); } ProcessAction action_; DISALLOW_EVIL_CONSTRUCTORS(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 std::wstring& file, SkBitmap* bitmap) { std::string compressed; file_util::ReadFileToString(file, &compressed); EXPECT_TRUE(compressed.size()); // Extra-lame. If you care, fix it. std::vector data; data.assign(reinterpret_cast(compressed.c_str()), reinterpret_cast(compressed.c_str() + compressed.size())); EXPECT_TRUE(PNGDecoder::Decode(&data, bitmap)); EXPECT_FALSE(bitmap->isOpaque()); Premultiply(*bitmap); } } // namespace // Streams an image. inline std::ostream& operator<<(std::ostream& out, const Image& image) { return out << "Image(" << image.size() << ", " << 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(L"base", 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 gfx::VectorCanvas(context_->context(), size_, size_); pcanvas_ = new gfx::PlatformCanvasWin(size_, size_, false); // Clear white. vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::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 std::wstring& filename) { std::wstring number(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().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. gfx::VectorCanvas* vcanvas_; // Pixel based canvas. gfx::PlatformCanvasWin* pcanvas_; // When true (default), vcanvas_ and pcanvas_ contents are compared and // verified to be identical. bool compare_canvas_; }; //////////////////////////////////////////////////////////////////////////////// // Actual tests TEST_F(VectorCanvasTest, Uninitialized) { // Do a little mubadumba do get uninitialized stuff. VectorCanvasTest::TearDown(); // The goal is not to verify that have the same uninitialized data. compare_canvas_ = false; context_ = new Context(); bitmap_ = new Bitmap(*context_, size_, size_); vcanvas_ = new gfx::VectorCanvas(context_->context(), size_, size_); pcanvas_ = new gfx::PlatformCanvasWin(size_, size_, false); // VectorCanvas default initialization is black. // PlatformCanvas default initialization is almost white 0x01FFFEFD (invalid // Skia color) in both Debug and Release. See magicTransparencyColor in // platform_device.cc EXPECT_EQ(0., ProcessImage(L"empty")); } TEST_F(VectorCanvasTest, BasicDrawing) { EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.) << L"clean"; EXPECT_EQ(0., ProcessImage(L"clean")); // Clear white. { vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); } EXPECT_EQ(0., ProcessImage(L"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(L"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(L"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(L"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(L"drawRect_noop")); // Empty again { vcanvas_->drawPaint(SkPaint()); pcanvas_->drawPaint(SkPaint()); } EXPECT_EQ(0., ProcessImage(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"sw-ne")); vcanvas_->drawLine(90, 10, 10, 90, paint); pcanvas_->drawLine(90, 10, 10, 90, paint); EXPECT_EQ(0., ProcessImage(L"ne-sw")); vcanvas_->drawLine(95, 90, 15, 10, paint); pcanvas_->drawLine(95, 90, 15, 10, paint); EXPECT_EQ(0., ProcessImage(L"se-nw")); } TEST_F(VectorCanvasTest, PathEffects) { { SkPaint paint; SkScalar intervals[] = { 1, 1 }; SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals), 0); paint.setPathEffect(effect)->unref(); 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(L"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 }; SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals), 0); paint.setPathEffect(effect)->unref(); 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(L"dash_path")); { SkPaint paint; SkScalar intervals[] = { 2, 1 }; SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals), 0); paint.setPathEffect(effect)->unref(); 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(L"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 }; SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals), 0); paint.setPathEffect(effect)->unref(); 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(L"circle")); } } TEST_F(VectorCanvasTest, Bitmaps) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; { SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap); vcanvas_->drawBitmap(bitmap, 13, 3, NULL); pcanvas_->drawBitmap(bitmap, 13, 3, NULL); EXPECT_EQ(0., ProcessImage(L"opaque")); } { SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap); vcanvas_->drawBitmap(bitmap, 5, 15, NULL); pcanvas_->drawBitmap(bitmap, 5, 15, NULL); EXPECT_EQ(0., ProcessImage(L"alpha")); } } TEST_F(VectorCanvasTest, ClippingRect) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); 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(L"rect")); } TEST_F(VectorCanvasTest, ClippingPath) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); 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(L"path")); } TEST_F(VectorCanvasTest, ClippingCombined) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); 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(L"combined")); } TEST_F(VectorCanvasTest, ClippingIntersect) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); 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(L"intersect")); } TEST_F(VectorCanvasTest, ClippingClean) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); { SkRegion old_region(pcanvas_->getTotalClip()); 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(L"clipped")); vcanvas_->clipRegion(old_region, SkRegion::kReplace_Op); pcanvas_->clipRegion(old_region, SkRegion::kReplace_Op); } { // 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(L"unclipped")); } } TEST_F(VectorCanvasTest, Matrix) { // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't // really care about Windows 2000 pixel colors. if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) return; SkBitmap bitmap; LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); { vcanvas_->translate(15, 3); pcanvas_->translate(15, 3); vcanvas_->drawBitmap(bitmap, 0, 0, NULL); pcanvas_->drawBitmap(bitmap, 0, 0, NULL); EXPECT_EQ(0., ProcessImage(L"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(L"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(L"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(L"rotate")); } }