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/files/file_util.h"
13#include "base/path_service.h"
14#include "base/strings/string_util.h"
15#include "base/strings/stringprintf.h"
16#include "base/strings/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 base::FilePath& filename) : ignore_alpha_(true) {
88    std::string compressed;
89    base::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 base::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 = base::OpenFile(filename, "wb");
139    ASSERT_TRUE(f);
140    ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
141              compressed.size());
142    base::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    base::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_ = base::FilePath(tmp);
245
246    if (action_ == GENERATE) {
247      // Make sure the directory exist.
248      base::CreateDirectory(test_dir_);
249    }
250  }
251
252  // Returns the fully qualified path of a data file.
253  base::FilePath test_file(const base::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 = base::UTF16ToASCII(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                       base::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 base::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 base::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  base::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 base::FilePath& filename,
335                           SkBitmap* bitmap,
336                           bool is_opaque) {
337  std::string compressed;
338  base::ReadFileToString(base::MakeAbsoluteFilePath(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(
394        VectorPlatformDeviceEmf::CreateDevice(
395            size_, size_, true, context_->context()));
396    pcanvas_ = CreatePlatformCanvas(size_, size_, false);
397
398    // Clear white.
399    vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
400    pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
401  }
402
403  // Compares both canvas and returns the pixel difference in percentage between
404  // both images. 0 on success and ]0, 100] on failure.
405  double ProcessImage(const base::FilePath::StringType& filename) {
406    std::wstring number(base::StringPrintf(L"%02d_", number_++));
407    double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
408    double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
409    if (!compare_canvas_)
410      return std::max(diff1, diff2);
411
412    Image image1(*vcanvas_);
413    Image image2(*pcanvas_);
414    double diff = image1.PercentageDifferent(image2);
415    return std::max(std::max(diff1, diff2), diff);
416  }
417
418  // Returns COMPARE, which is the default. If kGenerateSwitch command
419  // line argument is used to start this process, GENERATE is returned instead.
420  static ProcessAction CurrentMode() {
421    return CommandLine::ForCurrentProcess()->HasSwitch(kGenerateSwitch) ?
422               GENERATE : COMPARE;
423  }
424
425  // Length in x and y of the square canvas.
426  int size_;
427
428  // Current image number in the current test. Used to number of test files.
429  int number_;
430
431  // A temporary HDC to draw into.
432  Context* context_;
433
434  // Bitmap created inside context_.
435  Bitmap* bitmap_;
436
437  // Vector based canvas.
438  VectorCanvas* vcanvas_;
439
440  // Pixel based canvas.
441  PlatformCanvas* pcanvas_;
442
443  // When true (default), vcanvas_ and pcanvas_ contents are compared and
444  // verified to be identical.
445  bool compare_canvas_;
446};
447
448
449////////////////////////////////////////////////////////////////////////////////
450// Actual tests
451
452#if !defined(USE_AURA)  // http://crbug.com/154358
453
454TEST_F(VectorCanvasTest, BasicDrawing) {
455  EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
456      << L"clean";
457  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clean")));
458
459  // Clear white.
460  {
461    vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
462    pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
463  }
464  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawARGB")));
465
466  // Diagonal line top-left to bottom-right.
467  {
468    SkPaint paint;
469    // Default color is black.
470    vcanvas_->drawLine(10, 10, 90, 90, paint);
471    pcanvas_->drawLine(10, 10, 90, 90, paint);
472  }
473  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_black")));
474
475  // Rect.
476  {
477    SkPaint paint;
478    paint.setColor(SK_ColorGREEN);
479    vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
480    pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
481  }
482  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_green")));
483
484  // A single-point rect doesn't leave any mark.
485  {
486    SkPaint paint;
487    paint.setColor(SK_ColorBLUE);
488    vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
489    pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
490  }
491  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
492
493  // Rect.
494  {
495    SkPaint paint;
496    paint.setColor(SK_ColorBLUE);
497    vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
498    pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
499  }
500  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
501
502  // Empty again
503  {
504    vcanvas_->drawPaint(SkPaint());
505    pcanvas_->drawPaint(SkPaint());
506  }
507  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPaint_black")));
508
509  // Horizontal line left to right.
510  {
511    SkPaint paint;
512    paint.setColor(SK_ColorRED);
513    vcanvas_->drawLine(10, 20, 90, 20, paint);
514    pcanvas_->drawLine(10, 20, 90, 20, paint);
515  }
516  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_left_to_right")));
517
518  // Vertical line downward.
519  {
520    SkPaint paint;
521    paint.setColor(SK_ColorRED);
522    vcanvas_->drawLine(30, 10, 30, 90, paint);
523    pcanvas_->drawLine(30, 10, 30, 90, paint);
524  }
525  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_red")));
526}
527
528TEST_F(VectorCanvasTest, Circles) {
529  // There is NO WAY to make them agree. At least verify that the output doesn't
530  // change across versions. This test is disabled. See bug 1060231.
531  compare_canvas_ = false;
532
533  // Stroked Circle.
534  {
535    SkPaint paint;
536    SkPath path;
537    path.addCircle(50, 75, 10);
538    paint.setStyle(SkPaint::kStroke_Style);
539    paint.setColor(SK_ColorMAGENTA);
540    vcanvas_->drawPath(path, paint);
541    pcanvas_->drawPath(path, paint);
542  }
543  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke")));
544
545  // Filled Circle.
546  {
547    SkPaint paint;
548    SkPath path;
549    path.addCircle(50, 25, 10);
550    paint.setStyle(SkPaint::kFill_Style);
551    vcanvas_->drawPath(path, paint);
552    pcanvas_->drawPath(path, paint);
553  }
554  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_fill")));
555
556  // Stroked Circle over.
557  {
558    SkPaint paint;
559    SkPath path;
560    path.addCircle(50, 25, 10);
561    paint.setStyle(SkPaint::kStroke_Style);
562    paint.setColor(SK_ColorBLUE);
563    vcanvas_->drawPath(path, paint);
564    pcanvas_->drawPath(path, paint);
565  }
566  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_over_strike")));
567
568  // Stroke and Fill Circle.
569  {
570    SkPaint paint;
571    SkPath path;
572    path.addCircle(12, 50, 10);
573    paint.setStyle(SkPaint::kStrokeAndFill_Style);
574    paint.setColor(SK_ColorRED);
575    vcanvas_->drawPath(path, paint);
576    pcanvas_->drawPath(path, paint);
577  }
578  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke_and_fill")));
579
580  // Line + Quad + Cubic.
581  {
582    SkPaint paint;
583    SkPath path;
584    paint.setStyle(SkPaint::kStroke_Style);
585    paint.setColor(SK_ColorGREEN);
586    path.moveTo(1, 1);
587    path.lineTo(60, 40);
588    path.lineTo(80, 80);
589    path.quadTo(20, 50, 10, 90);
590    path.quadTo(50, 20, 90, 10);
591    path.cubicTo(20, 40, 50, 50, 10, 10);
592    path.cubicTo(30, 20, 50, 50, 90, 10);
593    path.addRect(90, 90, 95, 96);
594    vcanvas_->drawPath(path, paint);
595    pcanvas_->drawPath(path, paint);
596  }
597  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("mixed_stroke")));
598}
599
600TEST_F(VectorCanvasTest, LineOrientation) {
601  // There is NO WAY to make them agree. At least verify that the output doesn't
602  // change across versions. This test is disabled. See bug 1060231.
603  compare_canvas_ = false;
604
605  // Horizontal lines.
606  {
607    SkPaint paint;
608    paint.setColor(SK_ColorRED);
609    // Left to right.
610    vcanvas_->drawLine(10, 20, 90, 20, paint);
611    pcanvas_->drawLine(10, 20, 90, 20, paint);
612    // Right to left.
613    vcanvas_->drawLine(90, 30, 10, 30, paint);
614    pcanvas_->drawLine(90, 30, 10, 30, paint);
615  }
616  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal")));
617
618  // Vertical lines.
619  {
620    SkPaint paint;
621    paint.setColor(SK_ColorRED);
622    // Top down.
623    vcanvas_->drawLine(20, 10, 20, 90, paint);
624    pcanvas_->drawLine(20, 10, 20, 90, paint);
625    // Bottom up.
626    vcanvas_->drawLine(30, 90, 30, 10, paint);
627    pcanvas_->drawLine(30, 90, 30, 10, paint);
628  }
629  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical")));
630
631  // Try again with a 180 degres rotation.
632  vcanvas_->rotate(180);
633  pcanvas_->rotate(180);
634
635  // Horizontal lines (rotated).
636  {
637    SkPaint paint;
638    paint.setColor(SK_ColorRED);
639    vcanvas_->drawLine(-10, -25, -90, -25, paint);
640    pcanvas_->drawLine(-10, -25, -90, -25, paint);
641    vcanvas_->drawLine(-90, -35, -10, -35, paint);
642    pcanvas_->drawLine(-90, -35, -10, -35, paint);
643  }
644  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal_180")));
645
646  // Vertical lines (rotated).
647  {
648    SkPaint paint;
649    paint.setColor(SK_ColorRED);
650    vcanvas_->drawLine(-25, -10, -25, -90, paint);
651    pcanvas_->drawLine(-25, -10, -25, -90, paint);
652    vcanvas_->drawLine(-35, -90, -35, -10, paint);
653    pcanvas_->drawLine(-35, -90, -35, -10, paint);
654  }
655  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical_180")));
656}
657
658TEST_F(VectorCanvasTest, PathOrientation) {
659  // There is NO WAY to make them agree. At least verify that the output doesn't
660  // change across versions. This test is disabled. See bug 1060231.
661  compare_canvas_ = false;
662
663  // Horizontal lines.
664  {
665    SkPaint paint;
666    paint.setStyle(SkPaint::kStroke_Style);
667    paint.setColor(SK_ColorRED);
668    SkPath path;
669    SkPoint start;
670    start.set(10, 20);
671    SkPoint end;
672    end.set(90, 20);
673    path.moveTo(start);
674    path.lineTo(end);
675    vcanvas_->drawPath(path, paint);
676    pcanvas_->drawPath(path, paint);
677  }
678  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_ltr")));
679
680  // Horizontal lines.
681  {
682    SkPaint paint;
683    paint.setStyle(SkPaint::kStroke_Style);
684    paint.setColor(SK_ColorRED);
685    SkPath path;
686    SkPoint start;
687    start.set(90, 30);
688    SkPoint end;
689    end.set(10, 30);
690    path.moveTo(start);
691    path.lineTo(end);
692    vcanvas_->drawPath(path, paint);
693    pcanvas_->drawPath(path, paint);
694  }
695  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_rtl")));
696}
697
698TEST_F(VectorCanvasTest, DiagonalLines) {
699  SkPaint paint;
700  paint.setColor(SK_ColorRED);
701
702  vcanvas_->drawLine(10, 10, 90, 90, paint);
703  pcanvas_->drawLine(10, 10, 90, 90, paint);
704  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("nw-se")));
705
706  // Starting here, there is NO WAY to make them agree. At least verify that the
707  // output doesn't change across versions. This test is disabled. See bug
708  // 1060231.
709  compare_canvas_ = false;
710
711  vcanvas_->drawLine(10, 95, 90, 15, paint);
712  pcanvas_->drawLine(10, 95, 90, 15, paint);
713  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("sw-ne")));
714
715  vcanvas_->drawLine(90, 10, 10, 90, paint);
716  pcanvas_->drawLine(90, 10, 10, 90, paint);
717  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("ne-sw")));
718
719  vcanvas_->drawLine(95, 90, 15, 10, paint);
720  pcanvas_->drawLine(95, 90, 15, 10, paint);
721  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("se-nw")));
722}
723
724#if defined(OS_WIN)
725#define MAYBE_PathEffects DISABLED_PathEffects
726#else
727#define MAYBE_PathEffects PathEffects
728#endif
729TEST_F(VectorCanvasTest, MAYBE_PathEffects) {
730  {
731    SkPaint paint;
732    SkScalar intervals[] = { 1, 1 };
733    skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
734        new SkDashPathEffect(intervals, arraysize(intervals), 0));
735    paint.setPathEffect(effect.get());
736    paint.setColor(SK_ColorMAGENTA);
737    paint.setStyle(SkPaint::kStroke_Style);
738
739    vcanvas_->drawLine(10, 10, 90, 10, paint);
740    pcanvas_->drawLine(10, 10, 90, 10, paint);
741  }
742  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_line")));
743
744
745  // Starting here, there is NO WAY to make them agree. At least verify that the
746  // output doesn't change across versions. This test is disabled. See bug
747  // 1060231.
748  compare_canvas_ = false;
749
750  {
751    SkPaint paint;
752    SkScalar intervals[] = { 3, 5 };
753    skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
754        new SkDashPathEffect(intervals, arraysize(intervals), 0));
755    paint.setPathEffect(effect.get());
756    paint.setColor(SK_ColorMAGENTA);
757    paint.setStyle(SkPaint::kStroke_Style);
758
759    SkPath path;
760    path.moveTo(10, 15);
761    path.lineTo(90, 15);
762    path.lineTo(90, 90);
763    vcanvas_->drawPath(path, paint);
764    pcanvas_->drawPath(path, paint);
765  }
766  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_path")));
767
768  {
769    SkPaint paint;
770    SkScalar intervals[] = { 2, 1 };
771    skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
772        new SkDashPathEffect(intervals, arraysize(intervals), 0));
773    paint.setPathEffect(effect.get());
774    paint.setColor(SK_ColorMAGENTA);
775    paint.setStyle(SkPaint::kStroke_Style);
776
777    vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
778    pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
779  }
780  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_rect")));
781
782  // This thing looks like it has been drawn by a 3 years old kid. I haven't
783  // filed a bug on this since I guess nobody is expecting this to look nice.
784  {
785    SkPaint paint;
786    SkScalar intervals[] = { 1, 1 };
787    skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
788        new SkDashPathEffect(intervals, arraysize(intervals), 0));
789    paint.setPathEffect(effect.get());
790    paint.setColor(SK_ColorMAGENTA);
791    paint.setStyle(SkPaint::kStroke_Style);
792
793    SkPath path;
794    path.addCircle(50, 75, 10);
795    vcanvas_->drawPath(path, paint);
796    pcanvas_->drawPath(path, paint);
797    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle")));
798  }
799}
800
801TEST_F(VectorCanvasTest, Bitmaps) {
802  {
803    SkBitmap bitmap;
804    LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap, true);
805    vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
806    pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
807    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("opaque")));
808  }
809
810  {
811    SkBitmap bitmap;
812    LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap, false);
813    vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
814    pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
815    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("alpha")));
816  }
817}
818
819TEST_F(VectorCanvasTest, ClippingRect) {
820  SkBitmap bitmap;
821  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
822                        true);
823  SkRect rect;
824  rect.fLeft = 2;
825  rect.fTop = 2;
826  rect.fRight = 30.5f;
827  rect.fBottom = 30.5f;
828  vcanvas_->clipRect(rect);
829  pcanvas_->clipRect(rect);
830
831  vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
832  pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
833  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rect")));
834}
835
836TEST_F(VectorCanvasTest, ClippingPath) {
837  SkBitmap bitmap;
838  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
839                        true);
840  SkPath path;
841  path.addCircle(20, 20, 10);
842  vcanvas_->clipPath(path);
843  pcanvas_->clipPath(path);
844
845  vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
846  pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
847  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("path")));
848}
849
850TEST_F(VectorCanvasTest, ClippingCombined) {
851  SkBitmap bitmap;
852  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
853                        true);
854
855  SkRect rect;
856  rect.fLeft = 2;
857  rect.fTop = 2;
858  rect.fRight = 30.5f;
859  rect.fBottom = 30.5f;
860  vcanvas_->clipRect(rect);
861  pcanvas_->clipRect(rect);
862  SkPath path;
863  path.addCircle(20, 20, 10);
864  vcanvas_->clipPath(path, SkRegion::kUnion_Op);
865  pcanvas_->clipPath(path, SkRegion::kUnion_Op);
866
867  vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
868  pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
869  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("combined")));
870}
871
872TEST_F(VectorCanvasTest, ClippingIntersect) {
873  SkBitmap bitmap;
874  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
875                        true);
876
877  SkRect rect;
878  rect.fLeft = 2;
879  rect.fTop = 2;
880  rect.fRight = 30.5f;
881  rect.fBottom = 30.5f;
882  vcanvas_->clipRect(rect);
883  pcanvas_->clipRect(rect);
884  SkPath path;
885  path.addCircle(23, 23, 15);
886  vcanvas_->clipPath(path);
887  pcanvas_->clipPath(path);
888
889  vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
890  pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
891  EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("intersect")));
892}
893
894TEST_F(VectorCanvasTest, ClippingClean) {
895  SkBitmap bitmap;
896  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
897                        true);
898  {
899    SkAutoCanvasRestore acrv(vcanvas_, true);
900    SkAutoCanvasRestore acrp(pcanvas_, true);
901    SkRect rect;
902    rect.fLeft = 2;
903    rect.fTop = 2;
904    rect.fRight = 30.5f;
905    rect.fBottom = 30.5f;
906    vcanvas_->clipRect(rect);
907    pcanvas_->clipRect(rect);
908
909    vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
910    pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
911    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clipped")));
912  }
913  {
914    // Verify that the clipping region has been fixed back.
915    vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
916    pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
917    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("unclipped")));
918  }
919}
920
921// See http://crbug.com/26938
922TEST_F(VectorCanvasTest, DISABLED_Matrix) {
923  SkBitmap bitmap;
924  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
925                        true);
926  {
927    vcanvas_->translate(15, 3);
928    pcanvas_->translate(15, 3);
929    vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
930    pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
931    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate1")));
932  }
933  {
934    vcanvas_->translate(-30, -23);
935    pcanvas_->translate(-30, -23);
936    vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
937    pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
938    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate2")));
939  }
940  vcanvas_->resetMatrix();
941  pcanvas_->resetMatrix();
942
943  // For scaling and rotation, they use a different algorithm (nearest
944  // neighborhood vs smoothing). At least verify that the output doesn't change
945  // across versions.
946  compare_canvas_ = false;
947
948  {
949    vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
950    pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
951    vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
952    pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
953    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("scale")));
954  }
955  vcanvas_->resetMatrix();
956  pcanvas_->resetMatrix();
957
958  {
959    vcanvas_->rotate(67);
960    pcanvas_->rotate(67);
961    vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
962    pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
963    EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rotate")));
964  }
965}
966
967#endif  // !defined(USE_AURA)
968
969}  // namespace skia
970