vector_canvas_unittest.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "build/build_config.h"
6
7#if !defined(OS_WIN)
8#include <unistd.h>
9#endif
10
11#include "base/command_line.h"
12#include "base/file_util.h"
13#include "base/path_service.h"
14#include "base/string_util.h"
15#include "base/stringprintf.h"
16#include "base/utf_string_conversions.h"
17#include "skia/ext/vector_canvas.h"
18#include "skia/ext/vector_platform_device_emf_win.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "third_party/skia/include/effects/SkDashPathEffect.h"
21#include "ui/gfx/codec/png_codec.h"
22#include "ui/gfx/size.h"
23
24namespace skia {
25
26namespace {
27
28const char kGenerateSwitch[] = "vector-canvas-generate";
29
30// Lightweight HDC management.
31class Context {
32 public:
33  Context() : context_(CreateCompatibleDC(NULL)) {
34    EXPECT_TRUE(context_);
35  }
36  ~Context() {
37    DeleteDC(context_);
38  }
39
40  HDC context() const { return context_; }
41
42 private:
43  HDC context_;
44
45  DISALLOW_COPY_AND_ASSIGN(Context);
46};
47
48// Lightweight HBITMAP management.
49class Bitmap {
50 public:
51  Bitmap(const Context& context, int x, int y) {
52    BITMAPINFOHEADER hdr;
53    hdr.biSize = sizeof(BITMAPINFOHEADER);
54    hdr.biWidth = x;
55    hdr.biHeight = -y;  // Minus means top-down bitmap.
56    hdr.biPlanes = 1;
57    hdr.biBitCount = 32;
58    hdr.biCompression = BI_RGB;  // No compression.
59    hdr.biSizeImage = 0;
60    hdr.biXPelsPerMeter = 1;
61    hdr.biYPelsPerMeter = 1;
62    hdr.biClrUsed = 0;
63    hdr.biClrImportant = 0;
64    bitmap_ = CreateDIBSection(context.context(),
65                               reinterpret_cast<BITMAPINFO*>(&hdr), 0,
66                               &data_, NULL, 0);
67    EXPECT_TRUE(bitmap_);
68    EXPECT_TRUE(SelectObject(context.context(), bitmap_));
69  }
70  ~Bitmap() {
71    EXPECT_TRUE(DeleteObject(bitmap_));
72  }
73
74 private:
75  HBITMAP bitmap_;
76
77  void* data_;
78
79  DISALLOW_COPY_AND_ASSIGN(Bitmap);
80};
81
82// Lightweight raw-bitmap management. The image, once initialized, is immuable.
83// It is mainly used for comparison.
84class Image {
85 public:
86  // Creates the image from the given filename on disk.
87  explicit Image(const FilePath& filename) : ignore_alpha_(true) {
88    std::string compressed;
89    file_util::ReadFileToString(filename, &compressed);
90    EXPECT_TRUE(compressed.size());
91
92    SkBitmap bitmap;
93    EXPECT_TRUE(gfx::PNGCodec::Decode(
94        reinterpret_cast<const unsigned char*>(compressed.data()),
95        compressed.size(), &bitmap));
96    SetSkBitmap(bitmap);
97  }
98
99  // Loads the image from a canvas.
100  Image(skia::PlatformCanvas& canvas) : ignore_alpha_(true) {
101    // Use a different way to access the bitmap. The normal way would be to
102    // query the SkBitmap.
103    skia::ScopedPlatformPaint scoped_platform_paint(&canvas);
104    HDC context = scoped_platform_paint.GetPlatformSurface();
105    HGDIOBJ bitmap = GetCurrentObject(context, OBJ_BITMAP);
106    EXPECT_TRUE(bitmap != NULL);
107    // Initialize the clip region to the entire bitmap.
108    BITMAP bitmap_data;
109    EXPECT_EQ(GetObject(bitmap, sizeof(BITMAP), &bitmap_data), sizeof(BITMAP));
110    width_ = bitmap_data.bmWidth;
111    height_ = bitmap_data.bmHeight;
112    row_length_ = bitmap_data.bmWidthBytes;
113    size_t size = row_length_ * height_;
114    data_.resize(size);
115    memcpy(&*data_.begin(), bitmap_data.bmBits, size);
116  }
117
118  // Loads the image from a canvas.
119  Image(const SkBitmap& bitmap) : ignore_alpha_(true) {
120    SetSkBitmap(bitmap);
121  }
122
123  int width() const { return width_; }
124  int height() const { return height_; }
125  int row_length() const { return row_length_; }
126
127  // Save the image to a png file. Used to create the initial test files.
128  void SaveToFile(const FilePath& filename) {
129    std::vector<unsigned char> compressed;
130    ASSERT_TRUE(gfx::PNGCodec::Encode(&*data_.begin(),
131                                      gfx::PNGCodec::FORMAT_BGRA,
132                                      gfx::Size(width_, height_),
133                                      row_length_,
134                                      true,
135                                      std::vector<gfx::PNGCodec::Comment>(),
136                                      &compressed));
137    ASSERT_TRUE(compressed.size());
138    FILE* f = file_util::OpenFile(filename, "wb");
139    ASSERT_TRUE(f);
140    ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
141              compressed.size());
142    file_util::CloseFile(f);
143  }
144
145  // Returns the percentage of the image that is different from the other,
146  // between 0 and 100.
147  double PercentageDifferent(const Image& rhs) const {
148    if (width_ != rhs.width_ ||
149        height_ != rhs.height_ ||
150        row_length_ != rhs.row_length_ ||
151        width_ == 0 ||
152        height_ == 0) {
153      return 100.;  // When of different size or empty, they are 100% different.
154    }
155    // Compute pixels different in the overlap
156    int pixels_different = 0;
157    for (int y = 0; y < height_; ++y) {
158      for (int x = 0; x < width_; ++x) {
159        uint32_t lhs_pixel = pixel_at(x, y);
160        uint32_t rhs_pixel = rhs.pixel_at(x, y);
161        if (lhs_pixel != rhs_pixel)
162          ++pixels_different;
163      }
164    }
165
166    // Like the WebKit ImageDiff tool, we define percentage different in terms
167    // of the size of the 'actual' bitmap.
168    double total_pixels = static_cast<double>(width_) *
169                          static_cast<double>(height_);
170    return static_cast<double>(pixels_different) / total_pixels * 100.;
171  }
172
173  // Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
174  // depending on ignore_alpha_.
175  uint32 pixel_at(int x, int y) const {
176    EXPECT_TRUE(x >= 0 && x < width_);
177    EXPECT_TRUE(y >= 0 && y < height_);
178    const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
179    const uint32* data_row = data + y * row_length_ / sizeof(uint32);
180    if (ignore_alpha_)
181      return data_row[x] & 0xFFFFFF;  // Strip out A.
182    else
183      return data_row[x];
184  }
185
186 protected:
187  void SetSkBitmap(const SkBitmap& bitmap) {
188    SkAutoLockPixels lock(bitmap);
189    width_ = bitmap.width();
190    height_ = bitmap.height();
191    row_length_ = static_cast<int>(bitmap.rowBytes());
192    size_t size = row_length_ * height_;
193    data_.resize(size);
194    memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size);
195  }
196
197 private:
198  // Pixel dimensions of the image.
199  int width_;
200  int height_;
201
202  // Length of a line in bytes.
203  int row_length_;
204
205  // Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
206  // 0xABGR).
207  std::vector<unsigned char> data_;
208
209  // Flag to signal if the comparison functions should ignore the alpha channel.
210  const bool ignore_alpha_;
211
212  DISALLOW_COPY_AND_ASSIGN(Image);
213};
214
215// Base for tests. Capability to process an image.
216class ImageTest : public testing::Test {
217 public:
218  // In what state is the test running.
219  enum ProcessAction {
220    GENERATE,
221    COMPARE,
222    NOOP,
223  };
224
225  ImageTest(ProcessAction default_action)
226      : action_(default_action) {
227  }
228
229 protected:
230  virtual void SetUp() {
231    const testing::TestInfo& test_info =
232        *testing::UnitTest::GetInstance()->current_test_info();
233    PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_);
234    test_dir_ = test_dir_.AppendASCII("skia").
235                          AppendASCII("ext").
236                          AppendASCII("data").
237                          AppendASCII(test_info.test_case_name()).
238                          AppendASCII(test_info.name());
239
240    // Hack for a quick lowercase. We assume all the tests names are ASCII.
241    FilePath::StringType tmp(test_dir_.value());
242    for (size_t i = 0; i < tmp.size(); ++i)
243      tmp[i] = base::ToLowerASCII(tmp[i]);
244    test_dir_ = FilePath(tmp);
245
246    if (action_ == GENERATE) {
247      // Make sure the directory exist.
248      file_util::CreateDirectory(test_dir_);
249    }
250  }
251
252  // Returns the fully qualified path of a data file.
253  FilePath test_file(const FilePath::StringType& filename) const {
254    // Hack for a quick lowercase. We assume all the test data file names are
255    // ASCII.
256#if defined(OS_WIN)
257    std::string tmp = WideToASCII(filename);
258#else
259    std::string tmp(filename);
260#endif
261    for (size_t i = 0; i < tmp.size(); ++i)
262      tmp[i] = base::ToLowerASCII(tmp[i]);
263
264    return test_dir_.AppendASCII(tmp);
265  }
266
267  // Compares or saves the bitmap currently loaded in the context, depending on
268  // kGenerating value. Returns 0 on success or any positive value between ]0,
269  // 100] on failure. The return value is the percentage of difference between
270  // the image in the file and the image in the canvas.
271  double ProcessCanvas(skia::PlatformCanvas& canvas,
272                       FilePath::StringType filename) const {
273    filename = filename + FILE_PATH_LITERAL(".png");
274    switch (action_) {
275      case GENERATE:
276        SaveImage(canvas, filename);
277        return 0.;
278      case COMPARE:
279        return CompareImage(canvas, filename);
280      case NOOP:
281        return 0;
282      default:
283        // Invalid state, returns that the image is 100 different.
284        return 100.;
285    }
286  }
287
288  // Compares the bitmap currently loaded in the context with the file. Returns
289  // the percentage of pixel difference between both images, between 0 and 100.
290  double CompareImage(skia::PlatformCanvas& canvas,
291                      const FilePath::StringType& filename) const {
292    Image image1(canvas);
293    Image image2(test_file(filename));
294    double diff = image1.PercentageDifferent(image2);
295    return diff;
296  }
297
298  // Saves the bitmap currently loaded in the context into the file.
299  void SaveImage(skia::PlatformCanvas& canvas,
300                 const FilePath::StringType& filename) const {
301    Image(canvas).SaveToFile(test_file(filename));
302  }
303
304  ProcessAction action_;
305
306  // Path to directory used to contain the test data.
307  FilePath test_dir_;
308
309  DISALLOW_COPY_AND_ASSIGN(ImageTest);
310};
311
312// Premultiply the Alpha channel on the R, B and G channels.
313void Premultiply(SkBitmap bitmap) {
314  SkAutoLockPixels lock(bitmap);
315  for (int x = 0; x < bitmap.width(); ++x) {
316    for (int y = 0; y < bitmap.height(); ++y) {
317      uint32_t* pixel_addr = bitmap.getAddr32(x, y);
318      uint32_t color = *pixel_addr;
319      BYTE alpha = SkColorGetA(color);
320      if (!alpha) {
321        *pixel_addr = 0;
322      } else {
323        BYTE alpha_offset = alpha / 2;
324        *pixel_addr = SkColorSetARGB(
325            SkColorGetA(color),
326            (SkColorGetR(color) * 255 + alpha_offset) / alpha,
327            (SkColorGetG(color) * 255 + alpha_offset) / alpha,
328            (SkColorGetB(color) * 255 + alpha_offset) / alpha);
329      }
330    }
331  }
332}
333
334void LoadPngFileToSkBitmap(const FilePath& filename,
335                           SkBitmap* bitmap,
336                           bool is_opaque) {
337  std::string compressed;
338  file_util::ReadFileToString(filename, &compressed);
339  ASSERT_TRUE(compressed.size());
340
341  ASSERT_TRUE(gfx::PNGCodec::Decode(
342      reinterpret_cast<const unsigned char*>(compressed.data()),
343      compressed.size(), bitmap));
344
345  EXPECT_EQ(is_opaque, bitmap->isOpaque());
346  Premultiply(*bitmap);
347}
348
349}  // namespace
350
351// Streams an image.
352inline std::ostream& operator<<(std::ostream& out, const Image& image) {
353  return out << "Image(" << image.width() << ", "
354             << image.height() << ", " << image.row_length() << ")";
355}
356
357// Runs simultaneously the same drawing commands on VectorCanvas and
358// PlatformCanvas and compare the results.
359class VectorCanvasTest : public ImageTest {
360 public:
361  typedef ImageTest parent;
362
363  VectorCanvasTest() : parent(CurrentMode()), compare_canvas_(true) {
364  }
365
366 protected:
367  virtual void SetUp() {
368    parent::SetUp();
369    Init(100);
370    number_ = 0;
371  }
372
373  virtual void TearDown() {
374    delete pcanvas_;
375    pcanvas_ = NULL;
376
377    delete vcanvas_;
378    vcanvas_ = NULL;
379
380    delete bitmap_;
381    bitmap_ = NULL;
382
383    delete context_;
384    context_ = NULL;
385
386    parent::TearDown();
387  }
388
389  void Init(int size) {
390    size_ = size;
391    context_ = new Context();
392    bitmap_ = new Bitmap(*context_, size_, size_);
393    vcanvas_ = new VectorCanvas(VectorPlatformDeviceEmf::CreateDevice(
394        size_, size_, true, context_->context()));
395    pcanvas_ = new PlatformCanvas(size_, size_, false);
396
397    // Clear white.
398    vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
399    pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
400  }
401
402  // Compares both canvas and returns the pixel difference in percentage between
403  // both images. 0 on success and ]0, 100] on failure.
404  double ProcessImage(const FilePath::StringType& filename) {
405    std::wstring number(base::StringPrintf(L"%02d_", number_++));
406    double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
407    double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
408    if (!compare_canvas_)
409      return std::max(diff1, diff2);
410
411    Image image1(*vcanvas_);
412    Image image2(*pcanvas_);
413    double diff = image1.PercentageDifferent(image2);
414    return std::max(std::max(diff1, diff2), diff);
415  }
416
417  // Returns COMPARE, which is the default. If kGenerateSwitch command
418  // line argument is used to start this process, GENERATE is returned instead.
419  static ProcessAction CurrentMode() {
420    return CommandLine::ForCurrentProcess()->HasSwitch(kGenerateSwitch) ?
421               GENERATE : COMPARE;
422  }
423
424  // Length in x and y of the square canvas.
425  int size_;
426
427  // Current image number in the current test. Used to number of test files.
428  int number_;
429
430  // A temporary HDC to draw into.
431  Context* context_;
432
433  // Bitmap created inside context_.
434  Bitmap* bitmap_;
435
436  // Vector based canvas.
437  VectorCanvas* vcanvas_;
438
439  // Pixel based canvas.
440  PlatformCanvas* pcanvas_;
441
442  // When true (default), vcanvas_ and pcanvas_ contents are compared and
443  // verified to be identical.
444  bool compare_canvas_;
445};
446
447
448////////////////////////////////////////////////////////////////////////////////
449// Actual tests
450
451#if !defined(USE_AURA)  // http://crbug.com/154358
452
453TEST_F(VectorCanvasTest, BasicDrawing) {
454  EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
455      << L"clean";
456  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clean")));
457
458  // Clear white.
459  {
460    vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
461    pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
462  }
463  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawARGB")));
464
465  // Diagonal line top-left to bottom-right.
466  {
467    SkPaint paint;
468    // Default color is black.
469    vcanvas_->drawLine(10, 10, 90, 90, paint);
470    pcanvas_->drawLine(10, 10, 90, 90, paint);
471  }
472  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_black")));
473
474  // Rect.
475  {
476    SkPaint paint;
477    paint.setColor(SK_ColorGREEN);
478    vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
479    pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
480  }
481  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_green")));
482
483  // A single-point rect doesn't leave any mark.
484  {
485    SkPaint paint;
486    paint.setColor(SK_ColorBLUE);
487    vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
488    pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
489  }
490  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
491
492  // Rect.
493  {
494    SkPaint paint;
495    paint.setColor(SK_ColorBLUE);
496    vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
497    pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
498  }
499  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
500
501  // Empty again
502  {
503    vcanvas_->drawPaint(SkPaint());
504    pcanvas_->drawPaint(SkPaint());
505  }
506  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPaint_black")));
507
508  // Horizontal line left to right.
509  {
510    SkPaint paint;
511    paint.setColor(SK_ColorRED);
512    vcanvas_->drawLine(10, 20, 90, 20, paint);
513    pcanvas_->drawLine(10, 20, 90, 20, paint);
514  }
515  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_left_to_right")));
516
517  // Vertical line downward.
518  {
519    SkPaint paint;
520    paint.setColor(SK_ColorRED);
521    vcanvas_->drawLine(30, 10, 30, 90, paint);
522    pcanvas_->drawLine(30, 10, 30, 90, paint);
523  }
524  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_red")));
525}
526
527TEST_F(VectorCanvasTest, Circles) {
528  // There is NO WAY to make them agree. At least verify that the output doesn't
529  // change across versions. This test is disabled. See bug 1060231.
530  compare_canvas_ = false;
531
532  // Stroked Circle.
533  {
534    SkPaint paint;
535    SkPath path;
536    path.addCircle(50, 75, 10);
537    paint.setStyle(SkPaint::kStroke_Style);
538    paint.setColor(SK_ColorMAGENTA);
539    vcanvas_->drawPath(path, paint);
540    pcanvas_->drawPath(path, paint);
541  }
542  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke")));
543
544  // Filled Circle.
545  {
546    SkPaint paint;
547    SkPath path;
548    path.addCircle(50, 25, 10);
549    paint.setStyle(SkPaint::kFill_Style);
550    vcanvas_->drawPath(path, paint);
551    pcanvas_->drawPath(path, paint);
552  }
553  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_fill")));
554
555  // Stroked Circle over.
556  {
557    SkPaint paint;
558    SkPath path;
559    path.addCircle(50, 25, 10);
560    paint.setStyle(SkPaint::kStroke_Style);
561    paint.setColor(SK_ColorBLUE);
562    vcanvas_->drawPath(path, paint);
563    pcanvas_->drawPath(path, paint);
564  }
565  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_over_strike")));
566
567  // Stroke and Fill Circle.
568  {
569    SkPaint paint;
570    SkPath path;
571    path.addCircle(12, 50, 10);
572    paint.setStyle(SkPaint::kStrokeAndFill_Style);
573    paint.setColor(SK_ColorRED);
574    vcanvas_->drawPath(path, paint);
575    pcanvas_->drawPath(path, paint);
576  }
577  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke_and_fill")));
578
579  // Line + Quad + Cubic.
580  {
581    SkPaint paint;
582    SkPath path;
583    paint.setStyle(SkPaint::kStroke_Style);
584    paint.setColor(SK_ColorGREEN);
585    path.moveTo(1, 1);
586    path.lineTo(60, 40);
587    path.lineTo(80, 80);
588    path.quadTo(20, 50, 10, 90);
589    path.quadTo(50, 20, 90, 10);
590    path.cubicTo(20, 40, 50, 50, 10, 10);
591    path.cubicTo(30, 20, 50, 50, 90, 10);
592    path.addRect(90, 90, 95, 96);
593    vcanvas_->drawPath(path, paint);
594    pcanvas_->drawPath(path, paint);
595  }
596  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("mixed_stroke")));
597}
598
599TEST_F(VectorCanvasTest, LineOrientation) {
600  // There is NO WAY to make them agree. At least verify that the output doesn't
601  // change across versions. This test is disabled. See bug 1060231.
602  compare_canvas_ = false;
603
604  // Horizontal lines.
605  {
606    SkPaint paint;
607    paint.setColor(SK_ColorRED);
608    // Left to right.
609    vcanvas_->drawLine(10, 20, 90, 20, paint);
610    pcanvas_->drawLine(10, 20, 90, 20, paint);
611    // Right to left.
612    vcanvas_->drawLine(90, 30, 10, 30, paint);
613    pcanvas_->drawLine(90, 30, 10, 30, paint);
614  }
615  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal")));
616
617  // Vertical lines.
618  {
619    SkPaint paint;
620    paint.setColor(SK_ColorRED);
621    // Top down.
622    vcanvas_->drawLine(20, 10, 20, 90, paint);
623    pcanvas_->drawLine(20, 10, 20, 90, paint);
624    // Bottom up.
625    vcanvas_->drawLine(30, 90, 30, 10, paint);
626    pcanvas_->drawLine(30, 90, 30, 10, paint);
627  }
628  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical")));
629
630  // Try again with a 180 degres rotation.
631  vcanvas_->rotate(180);
632  pcanvas_->rotate(180);
633
634  // Horizontal lines (rotated).
635  {
636    SkPaint paint;
637    paint.setColor(SK_ColorRED);
638    vcanvas_->drawLine(-10, -25, -90, -25, paint);
639    pcanvas_->drawLine(-10, -25, -90, -25, paint);
640    vcanvas_->drawLine(-90, -35, -10, -35, paint);
641    pcanvas_->drawLine(-90, -35, -10, -35, paint);
642  }
643  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal_180")));
644
645  // Vertical lines (rotated).
646  {
647    SkPaint paint;
648    paint.setColor(SK_ColorRED);
649    vcanvas_->drawLine(-25, -10, -25, -90, paint);
650    pcanvas_->drawLine(-25, -10, -25, -90, paint);
651    vcanvas_->drawLine(-35, -90, -35, -10, paint);
652    pcanvas_->drawLine(-35, -90, -35, -10, paint);
653  }
654  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical_180")));
655}
656
657TEST_F(VectorCanvasTest, PathOrientation) {
658  // There is NO WAY to make them agree. At least verify that the output doesn't
659  // change across versions. This test is disabled. See bug 1060231.
660  compare_canvas_ = false;
661
662  // Horizontal lines.
663  {
664    SkPaint paint;
665    paint.setStyle(SkPaint::kStroke_Style);
666    paint.setColor(SK_ColorRED);
667    SkPath path;
668    SkPoint start;
669    start.set(10, 20);
670    SkPoint end;
671    end.set(90, 20);
672    path.moveTo(start);
673    path.lineTo(end);
674    vcanvas_->drawPath(path, paint);
675    pcanvas_->drawPath(path, paint);
676  }
677  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_ltr")));
678
679  // Horizontal lines.
680  {
681    SkPaint paint;
682    paint.setStyle(SkPaint::kStroke_Style);
683    paint.setColor(SK_ColorRED);
684    SkPath path;
685    SkPoint start;
686    start.set(90, 30);
687    SkPoint end;
688    end.set(10, 30);
689    path.moveTo(start);
690    path.lineTo(end);
691    vcanvas_->drawPath(path, paint);
692    pcanvas_->drawPath(path, paint);
693  }
694  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_rtl")));
695}
696
697TEST_F(VectorCanvasTest, DiagonalLines) {
698  SkPaint paint;
699  paint.setColor(SK_ColorRED);
700
701  vcanvas_->drawLine(10, 10, 90, 90, paint);
702  pcanvas_->drawLine(10, 10, 90, 90, paint);
703  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("nw-se")));
704
705  // Starting here, there is NO WAY to make them agree. At least verify that the
706  // output doesn't change across versions. This test is disabled. See bug
707  // 1060231.
708  compare_canvas_ = false;
709
710  vcanvas_->drawLine(10, 95, 90, 15, paint);
711  pcanvas_->drawLine(10, 95, 90, 15, paint);
712  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("sw-ne")));
713
714  vcanvas_->drawLine(90, 10, 10, 90, paint);
715  pcanvas_->drawLine(90, 10, 10, 90, paint);
716  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("ne-sw")));
717
718  vcanvas_->drawLine(95, 90, 15, 10, paint);
719  pcanvas_->drawLine(95, 90, 15, 10, paint);
720  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("se-nw")));
721}
722
723#if defined(OS_WIN)
724#define MAYBE_PathEffects DISABLED_PathEffects
725#else
726#define MAYBE_PathEffects PathEffects
727#endif
728TEST_F(VectorCanvasTest, MAYBE_PathEffects) {
729  {
730    SkPaint paint;
731    SkScalar intervals[] = { 1, 1 };
732    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
733                                                0);
734    paint.setPathEffect(effect)->unref();
735    paint.setColor(SK_ColorMAGENTA);
736    paint.setStyle(SkPaint::kStroke_Style);
737
738    vcanvas_->drawLine(10, 10, 90, 10, paint);
739    pcanvas_->drawLine(10, 10, 90, 10, paint);
740  }
741  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_line")));
742
743
744  // Starting here, there is NO WAY to make them agree. At least verify that the
745  // output doesn't change across versions. This test is disabled. See bug
746  // 1060231.
747  compare_canvas_ = false;
748
749  {
750    SkPaint paint;
751    SkScalar intervals[] = { 3, 5 };
752    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
753                                                0);
754    paint.setPathEffect(effect)->unref();
755    paint.setColor(SK_ColorMAGENTA);
756    paint.setStyle(SkPaint::kStroke_Style);
757
758    SkPath path;
759    path.moveTo(10, 15);
760    path.lineTo(90, 15);
761    path.lineTo(90, 90);
762    vcanvas_->drawPath(path, paint);
763    pcanvas_->drawPath(path, paint);
764  }
765  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_path")));
766
767  {
768    SkPaint paint;
769    SkScalar intervals[] = { 2, 1 };
770    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
771                                                0);
772    paint.setPathEffect(effect)->unref();
773    paint.setColor(SK_ColorMAGENTA);
774    paint.setStyle(SkPaint::kStroke_Style);
775
776    vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
777    pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
778  }
779  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_rect")));
780
781  // This thing looks like it has been drawn by a 3 years old kid. I haven't
782  // filed a bug on this since I guess nobody is expecting this to look nice.
783  {
784    SkPaint paint;
785    SkScalar intervals[] = { 1, 1 };
786    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
787                                                0);
788    paint.setPathEffect(effect)->unref();
789    paint.setColor(SK_ColorMAGENTA);
790    paint.setStyle(SkPaint::kStroke_Style);
791
792    SkPath path;
793    path.addCircle(50, 75, 10);
794    vcanvas_->drawPath(path, paint);
795    pcanvas_->drawPath(path, paint);
796    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle")));
797  }
798}
799
800TEST_F(VectorCanvasTest, Bitmaps) {
801  {
802    SkBitmap bitmap;
803    LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap, true);
804    vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
805    pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
806    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("opaque")));
807  }
808
809  {
810    SkBitmap bitmap;
811    LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap, false);
812    vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
813    pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
814    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("alpha")));
815  }
816}
817
818TEST_F(VectorCanvasTest, ClippingRect) {
819  SkBitmap bitmap;
820  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
821                        true);
822  SkRect rect;
823  rect.fLeft = 2;
824  rect.fTop = 2;
825  rect.fRight = 30.5f;
826  rect.fBottom = 30.5f;
827  vcanvas_->clipRect(rect);
828  pcanvas_->clipRect(rect);
829
830  vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
831  pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
832  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rect")));
833}
834
835TEST_F(VectorCanvasTest, ClippingPath) {
836  SkBitmap bitmap;
837  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
838                        true);
839  SkPath path;
840  path.addCircle(20, 20, 10);
841  vcanvas_->clipPath(path);
842  pcanvas_->clipPath(path);
843
844  vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
845  pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
846  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("path")));
847}
848
849TEST_F(VectorCanvasTest, ClippingCombined) {
850  SkBitmap bitmap;
851  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
852                        true);
853
854  SkRect rect;
855  rect.fLeft = 2;
856  rect.fTop = 2;
857  rect.fRight = 30.5f;
858  rect.fBottom = 30.5f;
859  vcanvas_->clipRect(rect);
860  pcanvas_->clipRect(rect);
861  SkPath path;
862  path.addCircle(20, 20, 10);
863  vcanvas_->clipPath(path, SkRegion::kUnion_Op);
864  pcanvas_->clipPath(path, SkRegion::kUnion_Op);
865
866  vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
867  pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
868  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("combined")));
869}
870
871TEST_F(VectorCanvasTest, ClippingIntersect) {
872  SkBitmap bitmap;
873  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
874                        true);
875
876  SkRect rect;
877  rect.fLeft = 2;
878  rect.fTop = 2;
879  rect.fRight = 30.5f;
880  rect.fBottom = 30.5f;
881  vcanvas_->clipRect(rect);
882  pcanvas_->clipRect(rect);
883  SkPath path;
884  path.addCircle(23, 23, 15);
885  vcanvas_->clipPath(path);
886  pcanvas_->clipPath(path);
887
888  vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
889  pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
890  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("intersect")));
891}
892
893TEST_F(VectorCanvasTest, ClippingClean) {
894  SkBitmap bitmap;
895  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
896                        true);
897  {
898    SkAutoCanvasRestore acrv(vcanvas_, true);
899    SkAutoCanvasRestore acrp(pcanvas_, true);
900    SkRect rect;
901    rect.fLeft = 2;
902    rect.fTop = 2;
903    rect.fRight = 30.5f;
904    rect.fBottom = 30.5f;
905    vcanvas_->clipRect(rect);
906    pcanvas_->clipRect(rect);
907
908    vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
909    pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
910    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clipped")));
911  }
912  {
913    // Verify that the clipping region has been fixed back.
914    vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
915    pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
916    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("unclipped")));
917  }
918}
919
920// See http://crbug.com/26938
921TEST_F(VectorCanvasTest, DISABLED_Matrix) {
922  SkBitmap bitmap;
923  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
924                        true);
925  {
926    vcanvas_->translate(15, 3);
927    pcanvas_->translate(15, 3);
928    vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
929    pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
930    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate1")));
931  }
932  {
933    vcanvas_->translate(-30, -23);
934    pcanvas_->translate(-30, -23);
935    vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
936    pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
937    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate2")));
938  }
939  vcanvas_->resetMatrix();
940  pcanvas_->resetMatrix();
941
942  // For scaling and rotation, they use a different algorithm (nearest
943  // neighborhood vs smoothing). At least verify that the output doesn't change
944  // across versions.
945  compare_canvas_ = false;
946
947  {
948    vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
949    pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
950    vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
951    pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
952    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("scale")));
953  }
954  vcanvas_->resetMatrix();
955  pcanvas_->resetMatrix();
956
957  {
958    vcanvas_->rotate(67);
959    pcanvas_->rotate(67);
960    vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
961    pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
962    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rotate")));
963  }
964}
965
966#endif  // !defined(USE_AURA)
967
968}  // namespace skia
969