OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "modules/canvas2d/CanvasRenderingContext2D.h" | 5 #include "modules/canvas2d/CanvasRenderingContext2D.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 #include "bindings/core/v8/V8BindingForCore.h" | 8 #include "bindings/core/v8/V8BindingForCore.h" |
9 #include "bindings/core/v8/V8BindingForTesting.h" | 9 #include "bindings/core/v8/V8BindingForTesting.h" |
10 #include "core/dom/Document.h" | 10 #include "core/dom/Document.h" |
(...skipping 19 matching lines...) Expand all Loading... |
30 #include "platform/graphics/test/FakeWebGraphicsContext3DProvider.h" | 30 #include "platform/graphics/test/FakeWebGraphicsContext3DProvider.h" |
31 #include "platform/loader/fetch/MemoryCache.h" | 31 #include "platform/loader/fetch/MemoryCache.h" |
32 #include "platform/testing/TestingPlatformSupport.h" | 32 #include "platform/testing/TestingPlatformSupport.h" |
33 #include "platform/wtf/PtrUtil.h" | 33 #include "platform/wtf/PtrUtil.h" |
34 #include "public/platform/scheduler/test/renderer_scheduler_test_support.h" | 34 #include "public/platform/scheduler/test/renderer_scheduler_test_support.h" |
35 #include "testing/gmock/include/gmock/gmock.h" | 35 #include "testing/gmock/include/gmock/gmock.h" |
36 #include "testing/gtest/include/gtest/gtest.h" | 36 #include "testing/gtest/include/gtest/gtest.h" |
37 #include "third_party/skia/include/core/SkColorSpaceXform.h" | 37 #include "third_party/skia/include/core/SkColorSpaceXform.h" |
38 #include "third_party/skia/include/core/SkImage.h" | 38 #include "third_party/skia/include/core/SkImage.h" |
39 #include "third_party/skia/include/core/SkSurface.h" | 39 #include "third_party/skia/include/core/SkSurface.h" |
| 40 #include "third_party/skia/include/core/SkSwizzle.h" |
40 | 41 |
41 using ::testing::Mock; | 42 using ::testing::Mock; |
42 | 43 |
43 namespace blink { | 44 namespace blink { |
44 | 45 |
45 namespace { | 46 namespace { |
46 | 47 |
47 gfx::ColorSpace AdobeRGBColorSpace() { | 48 gfx::ColorSpace AdobeRGBColorSpace() { |
48 return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB, | 49 return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB, |
49 gfx::ColorSpace::TransferID::GAMMA22); | 50 gfx::ColorSpace::TransferID::GAMMA22); |
(...skipping 1285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1335 } | 1336 } |
1336 | 1337 |
1337 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( | 1338 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( |
1338 experimental_canvas_features_runtime_flag); | 1339 experimental_canvas_features_runtime_flag); |
1339 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( | 1340 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( |
1340 color_correct_rendering_runtime_flag); | 1341 color_correct_rendering_runtime_flag); |
1341 RuntimeEnabledFeatures::setColorCorrectRenderingDefaultModeEnabled( | 1342 RuntimeEnabledFeatures::setColorCorrectRenderingDefaultModeEnabled( |
1342 color_correct_rendering_default_mode_runtime_flag); | 1343 color_correct_rendering_default_mode_runtime_flag); |
1343 } | 1344 } |
1344 | 1345 |
| 1346 bool ConvertPixelsToColorSpaceAndPixelFormatForTest( |
| 1347 DOMArrayBufferView* data_array, |
| 1348 CanvasColorSpace src_color_space, |
| 1349 CanvasColorSpace dst_color_space, |
| 1350 CanvasPixelFormat dst_pixel_format, |
| 1351 std::unique_ptr<uint8_t[]>& converted_pixels) { |
| 1352 // Setting SkColorSpaceXform::apply parameters |
| 1353 SkColorSpaceXform::ColorFormat src_color_format = |
| 1354 SkColorSpaceXform::kRGBA_8888_ColorFormat; |
| 1355 |
| 1356 unsigned data_length = data_array->byteLength() / data_array->TypeSize(); |
| 1357 unsigned num_pixels = data_length / 4; |
| 1358 DOMUint8ClampedArray* u8_array = nullptr; |
| 1359 DOMUint16Array* u16_array = nullptr; |
| 1360 DOMFloat32Array* f32_array = nullptr; |
| 1361 void* src_data = nullptr; |
| 1362 |
| 1363 switch (data_array->GetType()) { |
| 1364 case ArrayBufferView::ViewType::kTypeUint8Clamped: |
| 1365 u8_array = const_cast<DOMUint8ClampedArray*>( |
| 1366 static_cast<const DOMUint8ClampedArray*>(data_array)); |
| 1367 src_data = static_cast<void*>(u8_array->Data()); |
| 1368 break; |
| 1369 |
| 1370 case ArrayBufferView::ViewType::kTypeUint16: |
| 1371 u16_array = const_cast<DOMUint16Array*>( |
| 1372 static_cast<const DOMUint16Array*>(data_array)); |
| 1373 src_color_format = |
| 1374 SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat; |
| 1375 src_data = static_cast<void*>(u16_array->Data()); |
| 1376 break; |
| 1377 |
| 1378 case ArrayBufferView::ViewType::kTypeFloat32: |
| 1379 f32_array = const_cast<DOMFloat32Array*>( |
| 1380 static_cast<const DOMFloat32Array*>(data_array)); |
| 1381 src_color_format = SkColorSpaceXform::kRGBA_F32_ColorFormat; |
| 1382 src_data = static_cast<void*>(f32_array->Data()); |
| 1383 break; |
| 1384 default: |
| 1385 NOTREACHED(); |
| 1386 return false; |
| 1387 } |
| 1388 |
| 1389 SkColorSpaceXform::ColorFormat dst_color_format = |
| 1390 SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat; |
| 1391 if (dst_pixel_format == kF16CanvasPixelFormat) |
| 1392 dst_color_format = SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat; |
| 1393 |
| 1394 sk_sp<SkColorSpace> src_sk_color_space = nullptr; |
| 1395 if (u8_array) { |
| 1396 src_sk_color_space = ImageData::GetSkColorSpaceForTest( |
| 1397 src_color_space, kRGBA8CanvasPixelFormat); |
| 1398 } else { |
| 1399 src_sk_color_space = ImageData::GetSkColorSpaceForTest( |
| 1400 src_color_space, kF16CanvasPixelFormat); |
| 1401 } |
| 1402 |
| 1403 sk_sp<SkColorSpace> dst_sk_color_space = |
| 1404 ImageData::GetSkColorSpaceForTest(dst_color_space, dst_pixel_format); |
| 1405 |
| 1406 // When the input dataArray is in Uint16, we normally should convert the |
| 1407 // values from Little Endian to Big Endian before passing the buffer to |
| 1408 // SkColorSpaceXform::apply. However, in this test scenario we are creating |
| 1409 // the Uin16 dataArray by multiplying a Uint8Clamped array members by 257, |
| 1410 // hence the Big Endian and Little Endian representations are the same. |
| 1411 |
| 1412 std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New( |
| 1413 src_sk_color_space.get(), dst_sk_color_space.get()); |
| 1414 |
| 1415 if (!xform->apply(dst_color_format, converted_pixels.get(), src_color_format, |
| 1416 src_data, num_pixels, kUnpremul_SkAlphaType)) |
| 1417 return false; |
| 1418 return true; |
| 1419 } |
| 1420 |
| 1421 // The color settings of the surface of the canvas always remaines loyal to the |
| 1422 // first created context 2D. Therefore, we have to test different canvas color |
| 1423 // space settings for CanvasRenderingContext2D::putImageData() in different |
| 1424 // tests. |
| 1425 enum class CanvasColorSpaceSettings : uint8_t { |
| 1426 CANVAS_SRGB = 0, |
| 1427 CANVAS_LINEARSRGB = 1, |
| 1428 CANVAS_REC2020 = 2, |
| 1429 CANVAS_P3 = 3, |
| 1430 |
| 1431 LAST = CANVAS_P3 |
| 1432 }; |
| 1433 |
| 1434 // This test verifies the correct behavior of putImageData member function in |
| 1435 // color managed mode. |
| 1436 void TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1437 HTMLCanvasElement& canvas_element, |
| 1438 CanvasColorSpaceSettings canvas_colorspace_setting, |
| 1439 float color_tolerance) { |
| 1440 bool experimental_canvas_features_runtime_flag = |
| 1441 RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); |
| 1442 bool color_correct_rendering_runtime_flag = |
| 1443 RuntimeEnabledFeatures::colorCorrectRenderingEnabled(); |
| 1444 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(true); |
| 1445 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(true); |
| 1446 |
| 1447 bool test_passed = true; |
| 1448 unsigned num_image_data_color_spaces = 3; |
| 1449 CanvasColorSpace image_data_color_spaces[] = { |
| 1450 kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, kP3CanvasColorSpace, |
| 1451 }; |
| 1452 |
| 1453 unsigned num_image_data_storage_formats = 3; |
| 1454 ImageDataStorageFormat image_data_storage_formats[] = { |
| 1455 kUint8ClampedArrayStorageFormat, kUint16ArrayStorageFormat, |
| 1456 kFloat32ArrayStorageFormat, |
| 1457 }; |
| 1458 |
| 1459 CanvasColorSpace canvas_color_spaces[] = { |
| 1460 kSRGBCanvasColorSpace, kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, |
| 1461 kP3CanvasColorSpace, |
| 1462 }; |
| 1463 |
| 1464 String canvas_color_space_names[] = { |
| 1465 kSRGBCanvasColorSpaceName, kSRGBCanvasColorSpaceName, |
| 1466 kRec2020CanvasColorSpaceName, kP3CanvasColorSpaceName}; |
| 1467 |
| 1468 CanvasPixelFormat canvas_pixel_formats[] = { |
| 1469 kRGBA8CanvasPixelFormat, kF16CanvasPixelFormat, kF16CanvasPixelFormat, |
| 1470 kF16CanvasPixelFormat, |
| 1471 }; |
| 1472 |
| 1473 String canvas_pixel_format_names[] = { |
| 1474 kRGBA8CanvasPixelFormatName, kF16CanvasPixelFormatName, |
| 1475 kF16CanvasPixelFormatName, kF16CanvasPixelFormatName}; |
| 1476 |
| 1477 // Source pixels in RGBA32 |
| 1478 uint8_t u8_pixels[] = {255, 0, 0, 255, // Red |
| 1479 0, 0, 0, 0, // Transparent |
| 1480 255, 192, 128, 64, // Decreasing values |
| 1481 93, 117, 205, 11}; // Random values |
| 1482 unsigned data_length = 16; |
| 1483 |
| 1484 uint16_t* u16_pixels = new uint16_t[data_length]; |
| 1485 for (unsigned i = 0; i < data_length; i++) |
| 1486 u16_pixels[i] = u8_pixels[i] * 257; |
| 1487 |
| 1488 float* f32_pixels = new float[data_length]; |
| 1489 for (unsigned i = 0; i < data_length; i++) |
| 1490 f32_pixels[i] = u8_pixels[i] / 255.0; |
| 1491 |
| 1492 DOMArrayBufferView* data_array = nullptr; |
| 1493 |
| 1494 DOMUint8ClampedArray* data_u8 = |
| 1495 DOMUint8ClampedArray::Create(u8_pixels, data_length); |
| 1496 DCHECK(data_u8); |
| 1497 EXPECT_EQ(data_length, data_u8->length()); |
| 1498 DOMUint16Array* data_u16 = DOMUint16Array::Create(u16_pixels, data_length); |
| 1499 DCHECK(data_u16); |
| 1500 EXPECT_EQ(data_length, data_u16->length()); |
| 1501 DOMFloat32Array* data_f32 = DOMFloat32Array::Create(f32_pixels, data_length); |
| 1502 DCHECK(data_f32); |
| 1503 EXPECT_EQ(data_length, data_f32->length()); |
| 1504 |
| 1505 ImageData* image_data = nullptr; |
| 1506 ImageDataColorSettings color_settings; |
| 1507 |
| 1508 // At most four bytes are needed for Float32 output per color component. |
| 1509 std::unique_ptr<uint8_t[]> pixels_converted_manually( |
| 1510 new uint8_t[data_length * 4]()); |
| 1511 |
| 1512 // Loop through different possible combinations of image data color space and |
| 1513 // storage formats and create the respective test image data objects. |
| 1514 for (unsigned i = 0; i < num_image_data_color_spaces; i++) { |
| 1515 color_settings.setColorSpace( |
| 1516 ImageData::CanvasColorSpaceName(image_data_color_spaces[i])); |
| 1517 |
| 1518 for (unsigned j = 0; j < num_image_data_storage_formats; j++) { |
| 1519 switch (image_data_storage_formats[j]) { |
| 1520 case kUint8ClampedArrayStorageFormat: |
| 1521 data_array = static_cast<DOMArrayBufferView*>(data_u8); |
| 1522 color_settings.setStorageFormat(kUint8ClampedArrayStorageFormatName); |
| 1523 break; |
| 1524 case kUint16ArrayStorageFormat: |
| 1525 data_array = static_cast<DOMArrayBufferView*>(data_u16); |
| 1526 color_settings.setStorageFormat(kUint16ArrayStorageFormatName); |
| 1527 break; |
| 1528 case kFloat32ArrayStorageFormat: |
| 1529 data_array = static_cast<DOMArrayBufferView*>(data_f32); |
| 1530 color_settings.setStorageFormat(kFloat32ArrayStorageFormatName); |
| 1531 break; |
| 1532 default: |
| 1533 NOTREACHED(); |
| 1534 } |
| 1535 |
| 1536 image_data = |
| 1537 ImageData::CreateForTest(IntSize(2, 2), data_array, &color_settings); |
| 1538 |
| 1539 unsigned k = (unsigned)(canvas_colorspace_setting); |
| 1540 // Convert the original data used to create ImageData to the |
| 1541 // canvas color space and canvas pixel format. |
| 1542 EXPECT_TRUE(ConvertPixelsToColorSpaceAndPixelFormatForTest( |
| 1543 data_array, image_data_color_spaces[i], canvas_color_spaces[k], |
| 1544 canvas_pixel_formats[k], pixels_converted_manually)); |
| 1545 |
| 1546 // Create a canvas and call putImageData and getImageData to make sure |
| 1547 // the conversion is done correctly. |
| 1548 CanvasContextCreationAttributes attributes; |
| 1549 attributes.setAlpha(true); |
| 1550 attributes.setColorSpace(canvas_color_space_names[k]); |
| 1551 attributes.setPixelFormat(canvas_pixel_format_names[k]); |
| 1552 CanvasRenderingContext2D* context = |
| 1553 static_cast<CanvasRenderingContext2D*>( |
| 1554 canvas_element.GetCanvasRenderingContext("2d", attributes)); |
| 1555 NonThrowableExceptionState exception_state; |
| 1556 context->putImageData(image_data, 0, 0, exception_state); |
| 1557 |
| 1558 void* pixels_from_get_image_data = nullptr; |
| 1559 if (canvas_pixel_formats[k] == kRGBA8CanvasPixelFormat) { |
| 1560 pixels_from_get_image_data = |
| 1561 context->getImageData(0, 0, 2, 2, exception_state)->data()->Data(); |
| 1562 // Swizzle if needed |
| 1563 if (kN32_SkColorType == kBGRA_8888_SkColorType) { |
| 1564 SkSwapRB(static_cast<uint32_t*>(pixels_from_get_image_data), |
| 1565 static_cast<uint32_t*>(pixels_from_get_image_data), |
| 1566 data_length / 4); |
| 1567 } |
| 1568 |
| 1569 unsigned char* cpixels1 = |
| 1570 static_cast<unsigned char*>(pixels_converted_manually.get()); |
| 1571 unsigned char* cpixels2 = |
| 1572 static_cast<unsigned char*>(pixels_from_get_image_data); |
| 1573 for (unsigned m = 0; m < data_length; m++) { |
| 1574 if (abs(cpixels1[m] - cpixels2[m]) > color_tolerance) |
| 1575 test_passed = false; |
| 1576 } |
| 1577 } else { |
| 1578 pixels_from_get_image_data = |
| 1579 context->getImageData(0, 0, 2, 2, exception_state) |
| 1580 ->dataUnion() |
| 1581 .getAsFloat32Array() |
| 1582 .View() |
| 1583 ->Data(); |
| 1584 float* fpixels1 = nullptr; |
| 1585 float* fpixels2 = nullptr; |
| 1586 void* vpointer = pixels_converted_manually.get(); |
| 1587 fpixels1 = static_cast<float*>(vpointer); |
| 1588 fpixels2 = static_cast<float*>(pixels_from_get_image_data); |
| 1589 for (unsigned m = 0; m < data_length; m++) { |
| 1590 if (fpixels1[m] < 0) |
| 1591 fpixels1[m] = 0; |
| 1592 if (fabs(fpixels1[m] - fpixels2[m]) > color_tolerance) { |
| 1593 test_passed = false; |
| 1594 } |
| 1595 } |
| 1596 |
| 1597 ASSERT_TRUE(test_passed); |
| 1598 } |
| 1599 } |
| 1600 } |
| 1601 delete[] u16_pixels; |
| 1602 delete[] f32_pixels; |
| 1603 |
| 1604 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( |
| 1605 experimental_canvas_features_runtime_flag); |
| 1606 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( |
| 1607 color_correct_rendering_runtime_flag); |
| 1608 } |
| 1609 |
| 1610 TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnSRGBCanvas) { |
| 1611 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1612 CanvasElement(), CanvasColorSpaceSettings::CANVAS_SRGB, 0); |
| 1613 } |
| 1614 |
| 1615 TEST_F(CanvasRenderingContext2DTest, |
| 1616 ColorManagedPutImageDataOnLinearSRGBCanvas) { |
| 1617 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1618 CanvasElement(), CanvasColorSpaceSettings::CANVAS_LINEARSRGB, 0.15); |
| 1619 } |
| 1620 |
| 1621 TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnRec2020Canvas) { |
| 1622 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1623 CanvasElement(), CanvasColorSpaceSettings::CANVAS_REC2020, 0.1); |
| 1624 } |
| 1625 |
| 1626 TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnP3Canvas) { |
| 1627 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1628 CanvasElement(), CanvasColorSpaceSettings::CANVAS_P3, 0.1); |
| 1629 } |
| 1630 |
1345 void OverrideScriptEnabled(Settings& settings) { | 1631 void OverrideScriptEnabled(Settings& settings) { |
1346 // Simulate that we allow scripts, so that HTMLCanvasElement uses | 1632 // Simulate that we allow scripts, so that HTMLCanvasElement uses |
1347 // LayoutHTMLCanvas. | 1633 // LayoutHTMLCanvas. |
1348 settings.SetScriptEnabled(true); | 1634 settings.SetScriptEnabled(true); |
1349 } | 1635 } |
1350 | 1636 |
1351 class CanvasRenderingContext2DTestWithTestingPlatform | 1637 class CanvasRenderingContext2DTestWithTestingPlatform |
1352 : public CanvasRenderingContext2DTest { | 1638 : public CanvasRenderingContext2DTest { |
1353 protected: | 1639 protected: |
1354 void SetUp() override { | 1640 void SetUp() override { |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1412 | 1698 |
1413 // Wake up again, which should request a compositing update synchronously. | 1699 // Wake up again, which should request a compositing update synchronously. |
1414 GetDocument().GetPage()->SetVisibilityState(kPageVisibilityStateVisible, | 1700 GetDocument().GetPage()->SetVisibilityState(kPageVisibilityStateVisible, |
1415 false); | 1701 false); |
1416 EXPECT_EQ(!!CANVAS2D_HIBERNATION_ENABLED, | 1702 EXPECT_EQ(!!CANVAS2D_HIBERNATION_ENABLED, |
1417 layer->NeedsCompositingInputsUpdate()); | 1703 layer->NeedsCompositingInputsUpdate()); |
1418 RunUntilIdle(); // Clear task queue. | 1704 RunUntilIdle(); // Clear task queue. |
1419 } | 1705 } |
1420 | 1706 |
1421 } // namespace blink | 1707 } // namespace blink |
OLD | NEW |