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