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