| 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/V8Binding.h" | 8 #include "bindings/core/v8/V8Binding.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 14 matching lines...) Expand all Loading... |
| 25 #include "platform/graphics/UnacceleratedImageBufferSurface.h" | 25 #include "platform/graphics/UnacceleratedImageBufferSurface.h" |
| 26 #include "platform/graphics/test/FakeGLES2Interface.h" | 26 #include "platform/graphics/test/FakeGLES2Interface.h" |
| 27 #include "platform/graphics/test/FakeWebGraphicsContext3DProvider.h" | 27 #include "platform/graphics/test/FakeWebGraphicsContext3DProvider.h" |
| 28 #include "platform/loader/fetch/MemoryCache.h" | 28 #include "platform/loader/fetch/MemoryCache.h" |
| 29 #include "platform/wtf/PtrUtil.h" | 29 #include "platform/wtf/PtrUtil.h" |
| 30 #include "testing/gmock/include/gmock/gmock.h" | 30 #include "testing/gmock/include/gmock/gmock.h" |
| 31 #include "testing/gtest/include/gtest/gtest.h" | 31 #include "testing/gtest/include/gtest/gtest.h" |
| 32 #include "third_party/skia/include/core/SkColorSpaceXform.h" | 32 #include "third_party/skia/include/core/SkColorSpaceXform.h" |
| 33 #include "third_party/skia/include/core/SkImage.h" | 33 #include "third_party/skia/include/core/SkImage.h" |
| 34 #include "third_party/skia/include/core/SkSurface.h" | 34 #include "third_party/skia/include/core/SkSurface.h" |
| 35 #include "third_party/skia/include/core/SkSwizzle.h" |
| 35 | 36 |
| 36 using ::testing::Mock; | 37 using ::testing::Mock; |
| 37 | 38 |
| 38 namespace blink { | 39 namespace blink { |
| 39 | 40 |
| 40 namespace { | 41 namespace { |
| 41 | 42 |
| 42 gfx::ColorSpace AdobeRGBColorSpace() { | 43 gfx::ColorSpace AdobeRGBColorSpace() { |
| 43 return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB, | 44 return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB, |
| 44 gfx::ColorSpace::TransferID::GAMMA22); | 45 gfx::ColorSpace::TransferID::GAMMA22); |
| (...skipping 1333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1378 } | 1379 } |
| 1379 | 1380 |
| 1380 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( | 1381 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( |
| 1381 experimental_canvas_features_runtime_flag); | 1382 experimental_canvas_features_runtime_flag); |
| 1382 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( | 1383 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( |
| 1383 color_correct_rendering_runtime_flag); | 1384 color_correct_rendering_runtime_flag); |
| 1384 RuntimeEnabledFeatures::setColorCorrectRenderingDefaultModeEnabled( | 1385 RuntimeEnabledFeatures::setColorCorrectRenderingDefaultModeEnabled( |
| 1385 color_correct_rendering_default_mode_runtime_flag); | 1386 color_correct_rendering_default_mode_runtime_flag); |
| 1386 } | 1387 } |
| 1387 | 1388 |
| 1389 bool ConvertPixelsToColorSpaceAndPixelFormatForTest( |
| 1390 DOMArrayBufferView* data_array, |
| 1391 CanvasColorSpace src_color_space, |
| 1392 CanvasColorSpace dst_color_space, |
| 1393 CanvasPixelFormat dst_pixel_format, |
| 1394 std::unique_ptr<uint8_t[]>& converted_pixels) { |
| 1395 // Setting SkColorSpaceXform::apply parameters |
| 1396 SkColorSpaceXform::ColorFormat src_color_format = |
| 1397 SkColorSpaceXform::kRGBA_8888_ColorFormat; |
| 1398 |
| 1399 unsigned data_length = data_array->byteLength() / data_array->TypeSize(); |
| 1400 unsigned num_pixels = data_length / 4; |
| 1401 DOMUint8ClampedArray* u8_array = nullptr; |
| 1402 DOMUint16Array* u16_array = nullptr; |
| 1403 DOMFloat32Array* f32_array = nullptr; |
| 1404 void* src_data = nullptr; |
| 1405 |
| 1406 switch (data_array->GetType()) { |
| 1407 case ArrayBufferView::ViewType::kTypeUint8Clamped: |
| 1408 u8_array = const_cast<DOMUint8ClampedArray*>( |
| 1409 static_cast<const DOMUint8ClampedArray*>(data_array)); |
| 1410 src_data = static_cast<void*>(u8_array->Data()); |
| 1411 break; |
| 1412 |
| 1413 case ArrayBufferView::ViewType::kTypeUint16: |
| 1414 u16_array = const_cast<DOMUint16Array*>( |
| 1415 static_cast<const DOMUint16Array*>(data_array)); |
| 1416 src_color_format = |
| 1417 SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat; |
| 1418 src_data = static_cast<void*>(u16_array->Data()); |
| 1419 break; |
| 1420 |
| 1421 case ArrayBufferView::ViewType::kTypeFloat32: |
| 1422 f32_array = const_cast<DOMFloat32Array*>( |
| 1423 static_cast<const DOMFloat32Array*>(data_array)); |
| 1424 src_color_format = SkColorSpaceXform::kRGBA_F32_ColorFormat; |
| 1425 src_data = static_cast<void*>(f32_array->Data()); |
| 1426 break; |
| 1427 default: |
| 1428 NOTREACHED(); |
| 1429 return false; |
| 1430 } |
| 1431 |
| 1432 SkColorSpaceXform::ColorFormat dst_color_format = |
| 1433 SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat; |
| 1434 if (dst_pixel_format == kF16CanvasPixelFormat) |
| 1435 dst_color_format = SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat; |
| 1436 |
| 1437 sk_sp<SkColorSpace> src_sk_color_space = nullptr; |
| 1438 if (u8_array) { |
| 1439 src_sk_color_space = ImageData::GetSkColorSpaceForTest( |
| 1440 src_color_space, kRGBA8CanvasPixelFormat); |
| 1441 } else { |
| 1442 src_sk_color_space = ImageData::GetSkColorSpaceForTest( |
| 1443 src_color_space, kF16CanvasPixelFormat); |
| 1444 } |
| 1445 |
| 1446 sk_sp<SkColorSpace> dst_sk_color_space = |
| 1447 ImageData::GetSkColorSpaceForTest(dst_color_space, dst_pixel_format); |
| 1448 |
| 1449 // When the input dataArray is in Uint16, we normally should convert the |
| 1450 // values from Little Endian to Big Endian before passing the buffer to |
| 1451 // SkColorSpaceXform::apply. However, in this test scenario we are creating |
| 1452 // the Uin16 dataArray by multiplying a Uint8Clamped array members by 257, |
| 1453 // hence the Big Endian and Little Endian representations are the same. |
| 1454 |
| 1455 std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New( |
| 1456 src_sk_color_space.get(), dst_sk_color_space.get()); |
| 1457 |
| 1458 if (!xform->apply(dst_color_format, converted_pixels.get(), src_color_format, |
| 1459 src_data, num_pixels, kUnpremul_SkAlphaType)) |
| 1460 return false; |
| 1461 return true; |
| 1462 } |
| 1463 |
| 1464 // The color settings of the surface of the canvas always remaines loyal to the |
| 1465 // first created context 2D. Therefore, we have to test different canvas color |
| 1466 // space settings for CanvasRenderingContext2D::putImageData() in different |
| 1467 // tests. |
| 1468 enum class CanvasColorSpaceSettings : uint8_t { |
| 1469 CANVAS_SRGB = 0, |
| 1470 CANVAS_LINEARSRGB = 1, |
| 1471 CANVAS_REC2020 = 2, |
| 1472 CANVAS_P3 = 3, |
| 1473 |
| 1474 LAST = CANVAS_P3 |
| 1475 }; |
| 1476 |
| 1477 // This test verifies the correct behavior of putImageData member function in |
| 1478 // color managed mode. |
| 1479 void TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1480 HTMLCanvasElement& canvas_element, |
| 1481 CanvasColorSpaceSettings canvas_colorspace_setting, |
| 1482 float color_tolerance) { |
| 1483 bool experimental_canvas_features_runtime_flag = |
| 1484 RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); |
| 1485 bool color_correct_rendering_runtime_flag = |
| 1486 RuntimeEnabledFeatures::colorCorrectRenderingEnabled(); |
| 1487 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(true); |
| 1488 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(true); |
| 1489 |
| 1490 bool test_passed = true; |
| 1491 unsigned num_image_data_color_spaces = 3; |
| 1492 CanvasColorSpace image_data_color_spaces[] = { |
| 1493 kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, kP3CanvasColorSpace, |
| 1494 }; |
| 1495 |
| 1496 unsigned num_image_data_storage_formats = 3; |
| 1497 ImageDataStorageFormat image_data_storage_formats[] = { |
| 1498 kUint8ClampedArrayStorageFormat, kUint16ArrayStorageFormat, |
| 1499 kFloat32ArrayStorageFormat, |
| 1500 }; |
| 1501 |
| 1502 CanvasColorSpace canvas_color_spaces[] = { |
| 1503 kSRGBCanvasColorSpace, kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, |
| 1504 kP3CanvasColorSpace, |
| 1505 }; |
| 1506 |
| 1507 String canvas_color_space_names[] = { |
| 1508 kSRGBCanvasColorSpaceName, kSRGBCanvasColorSpaceName, |
| 1509 kRec2020CanvasColorSpaceName, kP3CanvasColorSpaceName}; |
| 1510 |
| 1511 CanvasPixelFormat canvas_pixel_formats[] = { |
| 1512 kRGBA8CanvasPixelFormat, kF16CanvasPixelFormat, kF16CanvasPixelFormat, |
| 1513 kF16CanvasPixelFormat, |
| 1514 }; |
| 1515 |
| 1516 String canvas_pixel_format_names[] = { |
| 1517 kRGBA8CanvasPixelFormatName, kF16CanvasPixelFormatName, |
| 1518 kF16CanvasPixelFormatName, kF16CanvasPixelFormatName}; |
| 1519 |
| 1520 // Source pixels in RGBA32 |
| 1521 uint8_t u8_pixels[] = {255, 0, 0, 255, // Red |
| 1522 0, 0, 0, 0, // Transparent |
| 1523 255, 192, 128, 64, // Decreasing values |
| 1524 93, 117, 205, 11}; // Random values |
| 1525 unsigned data_length = 16; |
| 1526 |
| 1527 uint16_t* u16_pixels = new uint16_t[data_length]; |
| 1528 for (unsigned i = 0; i < data_length; i++) |
| 1529 u16_pixels[i] = u8_pixels[i] * 257; |
| 1530 |
| 1531 float* f32_pixels = new float[data_length]; |
| 1532 for (unsigned i = 0; i < data_length; i++) |
| 1533 f32_pixels[i] = u8_pixels[i] / 255.0; |
| 1534 |
| 1535 DOMArrayBufferView* data_array = nullptr; |
| 1536 |
| 1537 DOMUint8ClampedArray* data_u8 = |
| 1538 DOMUint8ClampedArray::Create(u8_pixels, data_length); |
| 1539 DCHECK(data_u8); |
| 1540 EXPECT_EQ(data_length, data_u8->length()); |
| 1541 DOMUint16Array* data_u16 = DOMUint16Array::Create(u16_pixels, data_length); |
| 1542 DCHECK(data_u16); |
| 1543 EXPECT_EQ(data_length, data_u16->length()); |
| 1544 DOMFloat32Array* data_f32 = DOMFloat32Array::Create(f32_pixels, data_length); |
| 1545 DCHECK(data_f32); |
| 1546 EXPECT_EQ(data_length, data_f32->length()); |
| 1547 |
| 1548 ImageData* image_data = nullptr; |
| 1549 ImageDataColorSettings color_settings; |
| 1550 |
| 1551 // At most four bytes are needed for Float32 output per color component. |
| 1552 std::unique_ptr<uint8_t[]> pixels_converted_manually( |
| 1553 new uint8_t[data_length * 4]()); |
| 1554 |
| 1555 // Loop through different possible combinations of image data color space and |
| 1556 // storage formats and create the respective test image data objects. |
| 1557 for (unsigned i = 0; i < num_image_data_color_spaces; i++) { |
| 1558 color_settings.setColorSpace( |
| 1559 ImageData::CanvasColorSpaceName(image_data_color_spaces[i])); |
| 1560 |
| 1561 for (unsigned j = 0; j < num_image_data_storage_formats; j++) { |
| 1562 switch (image_data_storage_formats[j]) { |
| 1563 case kUint8ClampedArrayStorageFormat: |
| 1564 data_array = static_cast<DOMArrayBufferView*>(data_u8); |
| 1565 color_settings.setStorageFormat(kUint8ClampedArrayStorageFormatName); |
| 1566 break; |
| 1567 case kUint16ArrayStorageFormat: |
| 1568 data_array = static_cast<DOMArrayBufferView*>(data_u16); |
| 1569 color_settings.setStorageFormat(kUint16ArrayStorageFormatName); |
| 1570 break; |
| 1571 case kFloat32ArrayStorageFormat: |
| 1572 data_array = static_cast<DOMArrayBufferView*>(data_f32); |
| 1573 color_settings.setStorageFormat(kFloat32ArrayStorageFormatName); |
| 1574 break; |
| 1575 default: |
| 1576 NOTREACHED(); |
| 1577 } |
| 1578 |
| 1579 image_data = |
| 1580 ImageData::CreateForTest(IntSize(2, 2), data_array, &color_settings); |
| 1581 |
| 1582 unsigned k = (unsigned)(canvas_colorspace_setting); |
| 1583 // Convert the original data used to create ImageData to the |
| 1584 // canvas color space and canvas pixel format. |
| 1585 EXPECT_TRUE(ConvertPixelsToColorSpaceAndPixelFormatForTest( |
| 1586 data_array, image_data_color_spaces[i], canvas_color_spaces[k], |
| 1587 canvas_pixel_formats[k], pixels_converted_manually)); |
| 1588 |
| 1589 // Create a canvas and call putImageData and getImageData to make sure |
| 1590 // the conversion is done correctly. |
| 1591 CanvasContextCreationAttributes attributes; |
| 1592 attributes.setAlpha(true); |
| 1593 attributes.setColorSpace(canvas_color_space_names[k]); |
| 1594 attributes.setPixelFormat(canvas_pixel_format_names[k]); |
| 1595 CanvasRenderingContext2D* context = |
| 1596 static_cast<CanvasRenderingContext2D*>( |
| 1597 canvas_element.GetCanvasRenderingContext("2d", attributes)); |
| 1598 NonThrowableExceptionState exception_state; |
| 1599 context->putImageData(image_data, 0, 0, exception_state); |
| 1600 |
| 1601 void* pixels_from_get_image_data = nullptr; |
| 1602 if (canvas_pixel_formats[k] == kRGBA8CanvasPixelFormat) { |
| 1603 pixels_from_get_image_data = |
| 1604 context->getImageData(0, 0, 2, 2, exception_state)->data()->Data(); |
| 1605 // Swizzle if needed |
| 1606 if (kN32_SkColorType == kBGRA_8888_SkColorType) { |
| 1607 SkSwapRB(static_cast<uint32_t*>(pixels_from_get_image_data), |
| 1608 static_cast<uint32_t*>(pixels_from_get_image_data), |
| 1609 data_length / 4); |
| 1610 } |
| 1611 |
| 1612 unsigned char* cpixels1 = |
| 1613 static_cast<unsigned char*>(pixels_converted_manually.get()); |
| 1614 unsigned char* cpixels2 = |
| 1615 static_cast<unsigned char*>(pixels_from_get_image_data); |
| 1616 for (unsigned m = 0; m < data_length; m++) { |
| 1617 if (abs(cpixels1[m] - cpixels2[m]) > color_tolerance) |
| 1618 test_passed = false; |
| 1619 } |
| 1620 } else { |
| 1621 pixels_from_get_image_data = |
| 1622 context->getImageData(0, 0, 2, 2, exception_state) |
| 1623 ->dataUnion() |
| 1624 .getAsFloat32Array() |
| 1625 .View() |
| 1626 ->Data(); |
| 1627 float* fpixels1 = nullptr; |
| 1628 float* fpixels2 = nullptr; |
| 1629 void* vpointer = pixels_converted_manually.get(); |
| 1630 fpixels1 = static_cast<float*>(vpointer); |
| 1631 fpixels2 = static_cast<float*>(pixels_from_get_image_data); |
| 1632 for (unsigned m = 0; m < data_length; m++) { |
| 1633 if (fpixels1[m] < 0) |
| 1634 fpixels1[m] = 0; |
| 1635 if (fabs(fpixels1[m] - fpixels2[m]) > color_tolerance) { |
| 1636 test_passed = false; |
| 1637 } |
| 1638 } |
| 1639 |
| 1640 ASSERT_TRUE(test_passed); |
| 1641 } |
| 1642 } |
| 1643 } |
| 1644 delete[] u16_pixels; |
| 1645 delete[] f32_pixels; |
| 1646 |
| 1647 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( |
| 1648 experimental_canvas_features_runtime_flag); |
| 1649 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( |
| 1650 color_correct_rendering_runtime_flag); |
| 1651 } |
| 1652 |
| 1653 TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnSRGBCanvas) { |
| 1654 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1655 CanvasElement(), CanvasColorSpaceSettings::CANVAS_SRGB, 0); |
| 1656 } |
| 1657 |
| 1658 TEST_F(CanvasRenderingContext2DTest, |
| 1659 ColorManagedPutImageDataOnLinearSRGBCanvas) { |
| 1660 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1661 CanvasElement(), CanvasColorSpaceSettings::CANVAS_LINEARSRGB, 0.15); |
| 1662 } |
| 1663 |
| 1664 TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnRec2020Canvas) { |
| 1665 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1666 CanvasElement(), CanvasColorSpaceSettings::CANVAS_REC2020, 0.1); |
| 1667 } |
| 1668 |
| 1669 TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnP3Canvas) { |
| 1670 TestPutImageDataOnCanvasWithColorSpaceSettings( |
| 1671 CanvasElement(), CanvasColorSpaceSettings::CANVAS_P3, 0.1); |
| 1672 } |
| 1673 |
| 1388 } // namespace blink | 1674 } // namespace blink |
| OLD | NEW |