Loading libs/renderengine/skia/filters/LinearEffect.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,7 @@ static void generateXYZTransforms(SkString& shader) { uniform float4x4 in_rgbToXyz; uniform float4x4 in_xyzToRgb; float3 ToXYZ(float3 rgb) { return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0); return (in_rgbToXyz * float4(rgb, 1.0)).rgb; } float3 ToRGB(float3 xyz) { Loading libs/renderengine/tests/RenderEngineTest.cpp +195 −22 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <system/graphics-base-v1.0.h> #include <tonemap/tonemap.h> #include <ui/ColorSpace.h> #include <ui/PixelFormat.h> #include <chrono> Loading Loading @@ -282,6 +285,13 @@ public: void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { auto generator = [=](Point) { return ubyte4(r, g, b, a); }; expectBufferColor(rect, generator, tolerance); } using ColorGenerator = std::function<ubyte4(Point location)>; void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) { auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) { auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) { uint8_t tmp = a >= b ? a - b : b - a; Loading @@ -290,10 +300,10 @@ public: return std::equal(colorA, colorA + 4, colorB, colorBitCompare); }; expectBufferColor(rect, r, g, b, a, colorCompare); expectBufferColor(rect, generator, colorCompare); } void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, void expectBufferColor(const Rect& region, ColorGenerator generator, std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) { uint8_t* pixels; mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, Loading @@ -304,19 +314,22 @@ public: const uint8_t* src = pixels + (mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4; for (int32_t i = 0; i < region.getWidth(); i++) { const uint8_t expected[4] = {r, g, b, a}; bool equal = colorCompare(src, expected); EXPECT_TRUE(equal) const auto location = Point(region.left + i, region.top + j); const ubyte4 colors = generator(location); const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a}; bool colorMatches = colorCompare(src, expected); EXPECT_TRUE(colorMatches) << GetParam()->name().c_str() << ": " << "pixel @ (" << region.left + i << ", " << region.top + j << "): " << "expected (" << static_cast<uint32_t>(r) << ", " << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", " << static_cast<uint32_t>(a) << "), " << "pixel @ (" << location.x << ", " << location.y << "): " << "expected (" << static_cast<uint32_t>(colors.r) << ", " << static_cast<uint32_t>(colors.g) << ", " << static_cast<uint32_t>(colors.b) << ", " << static_cast<uint32_t>(colors.a) << "), " << "got (" << static_cast<uint32_t>(src[0]) << ", " << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2]) << ", " << static_cast<uint32_t>(src[3]) << ")"; src += 4; if (!equal && ++fails >= maxFails) { if (!colorMatches && ++fails >= maxFails) { break; } } Loading @@ -328,10 +341,11 @@ public: } void expectAlpha(const Rect& rect, uint8_t a) { auto generator = [=](Point) { return ubyte4(0, 0, 0, a); }; auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) { return colorA[3] == colorB[3]; }; expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare); expectBufferColor(rect, generator, colorCompare); } void expectShadowColor(const renderengine::LayerSettings& castingLayer, Loading Loading @@ -1099,7 +1113,7 @@ void RenderEngineTest::fillRedBufferTextureTransform() { layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; // Transform coordinates to only be inside the red quadrant. layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1)); layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f)); layer.alpha = 1.0f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); Loading Loading @@ -1281,7 +1295,8 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { settings.clip = fullscreenRect(); // 255, 255, 255, 255 is full opaque white. const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); // Create layer with given color. renderengine::LayerSettings bgLayer; bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; Loading Loading @@ -1615,7 +1630,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { initializeRenderEngine(); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1630,8 +1646,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(1, 1); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1649,8 +1667,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1669,8 +1689,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1690,8 +1712,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading Loading @@ -2027,6 +2051,155 @@ TEST_P(RenderEngineTest, test_isOpaque) { expectBufferColor(rect, 0, 255, 0, 255); } } double EOTF_PQ(double channel) { float m1 = (2610.0 / 4096.0) / 4.0; float m2 = (2523.0 / 4096.0) * 128.0; float c1 = (3424.0 / 4096.0); float c2 = (2413.0 / 4096.0) * 32.0; float c3 = (2392.0 / 4096.0) * 32.0; float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); return std::pow(tmp, 1.0 / m1); } vec3 EOTF_PQ(vec3 color) { return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); } double OETF_sRGB(double channel) { return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; } int sign(float in) { return in >= 0.0 ? 1 : -1; } vec3 OETF_sRGB(vec3 linear) { return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), sign(linear.b) * OETF_sRGB(linear.b)); } TEST_P(RenderEngineTest, test_tonemapPQMatches) { if (!GetParam()->useColorManagement()) { return; } if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { return; } initializeRenderEngine(); constexpr int32_t kGreyLevels = 256; const auto rect = Rect(0, 0, kGreyLevels, 1); const renderengine::DisplaySettings display{ .physicalDisplay = rect, .clip = rect, .maxLuminance = 750.0f, .outputDataspace = ui::Dataspace::DISPLAY_P3, }; auto buf = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "input"), *mRE, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); ASSERT_EQ(0, buf->getBuffer()->initCheck()); { uint8_t* pixels; buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); uint8_t color = 0; for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { dest[0] = color; dest[1] = color; dest[2] = color; dest[3] = 255; color++; dest += 4; } } buf->getBuffer()->unlock(); } mBuffer = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "output"), *mRE, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); const renderengine::LayerSettings layer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = std::move(buf), .usePremultipliedAlpha = true, }, }, .alpha = 1.0f, .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL), }; std::vector<renderengine::LayerSettings> layers{layer}; invokeDraw(display, layers); ColorSpace displayP3 = ColorSpace::DisplayP3(); ColorSpace bt2020 = ColorSpace::BT2020(); tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; auto generator = [=](Point location) { const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); const vec3 rgb = vec3(normColor, normColor, normColor); const vec3 linearRGB = EOTF_PQ(rgb); static constexpr float kMaxPQLuminance = 10000.f; const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance; const double gain = tonemap::getToneMapper() ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: Dataspace>( HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL), static_cast<aidl::android::hardware::graphics::common:: Dataspace>( ui::Dataspace::DISPLAY_P3), linearRGB * 10000.0, xyz, metadata); const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance; const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255; return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), static_cast<uint8_t>(targetRGB.b), 255); }; expectBufferColor(Rect(kGreyLevels, 1), generator, 2); } } // namespace renderengine } // namespace android Loading libs/tonemap/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -30,7 +30,13 @@ cc_library_static { shared_libs: [ "android.hardware.graphics.common-V3-ndk", "liblog", ], static_libs: [ "libmath", ], srcs: [ "tonemap.cpp", ], Loading libs/tonemap/include/tonemap/tonemap.h +17 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #pragma once #include <aidl/android/hardware/graphics/common/Dataspace.h> #include <math/vec3.h> #include <string> #include <vector> Loading Loading @@ -48,7 +49,9 @@ struct Metadata { class ToneMapper { public: virtual ~ToneMapper() {} // Constructs a tonemap shader whose shader language is SkSL // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an // input whose dataspace is described by sourceDataspace, to an output whose dataspace // is described by destinationDataspace // // The returned shader string *must* contain a function with the following signature: // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz); Loading Loading @@ -94,6 +97,19 @@ public: // assume that there are predefined floats in_libtonemap_displayMaxLuminance and // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader. virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0; // CPU implementation of the tonemapping gain. This must match the GPU implementation returned // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for // differences in hardware precision. // // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output // described by destinationDataspace. To compute the gain, the input colors are provided by // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also // provided. Metadata is also provided for helping to compute the tonemapping curve. virtual double lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0; }; // Retrieves a tonemapper instance. Loading libs/tonemap/tests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ cc_test { "android.hardware.graphics.common-V3-ndk", ], static_libs: [ "libmath", "libgmock", "libgtest", "libtonemap", Loading Loading
libs/renderengine/skia/filters/LinearEffect.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,7 @@ static void generateXYZTransforms(SkString& shader) { uniform float4x4 in_rgbToXyz; uniform float4x4 in_xyzToRgb; float3 ToXYZ(float3 rgb) { return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0); return (in_rgbToXyz * float4(rgb, 1.0)).rgb; } float3 ToRGB(float3 xyz) { Loading
libs/renderengine/tests/RenderEngineTest.cpp +195 −22 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <system/graphics-base-v1.0.h> #include <tonemap/tonemap.h> #include <ui/ColorSpace.h> #include <ui/PixelFormat.h> #include <chrono> Loading Loading @@ -282,6 +285,13 @@ public: void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { auto generator = [=](Point) { return ubyte4(r, g, b, a); }; expectBufferColor(rect, generator, tolerance); } using ColorGenerator = std::function<ubyte4(Point location)>; void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) { auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) { auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) { uint8_t tmp = a >= b ? a - b : b - a; Loading @@ -290,10 +300,10 @@ public: return std::equal(colorA, colorA + 4, colorB, colorBitCompare); }; expectBufferColor(rect, r, g, b, a, colorCompare); expectBufferColor(rect, generator, colorCompare); } void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, void expectBufferColor(const Rect& region, ColorGenerator generator, std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) { uint8_t* pixels; mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, Loading @@ -304,19 +314,22 @@ public: const uint8_t* src = pixels + (mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4; for (int32_t i = 0; i < region.getWidth(); i++) { const uint8_t expected[4] = {r, g, b, a}; bool equal = colorCompare(src, expected); EXPECT_TRUE(equal) const auto location = Point(region.left + i, region.top + j); const ubyte4 colors = generator(location); const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a}; bool colorMatches = colorCompare(src, expected); EXPECT_TRUE(colorMatches) << GetParam()->name().c_str() << ": " << "pixel @ (" << region.left + i << ", " << region.top + j << "): " << "expected (" << static_cast<uint32_t>(r) << ", " << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", " << static_cast<uint32_t>(a) << "), " << "pixel @ (" << location.x << ", " << location.y << "): " << "expected (" << static_cast<uint32_t>(colors.r) << ", " << static_cast<uint32_t>(colors.g) << ", " << static_cast<uint32_t>(colors.b) << ", " << static_cast<uint32_t>(colors.a) << "), " << "got (" << static_cast<uint32_t>(src[0]) << ", " << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2]) << ", " << static_cast<uint32_t>(src[3]) << ")"; src += 4; if (!equal && ++fails >= maxFails) { if (!colorMatches && ++fails >= maxFails) { break; } } Loading @@ -328,10 +341,11 @@ public: } void expectAlpha(const Rect& rect, uint8_t a) { auto generator = [=](Point) { return ubyte4(0, 0, 0, a); }; auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) { return colorA[3] == colorB[3]; }; expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare); expectBufferColor(rect, generator, colorCompare); } void expectShadowColor(const renderengine::LayerSettings& castingLayer, Loading Loading @@ -1099,7 +1113,7 @@ void RenderEngineTest::fillRedBufferTextureTransform() { layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; // Transform coordinates to only be inside the red quadrant. layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1)); layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f)); layer.alpha = 1.0f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); Loading Loading @@ -1281,7 +1295,8 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { settings.clip = fullscreenRect(); // 255, 255, 255, 255 is full opaque white. const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); // Create layer with given color. renderengine::LayerSettings bgLayer; bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; Loading Loading @@ -1615,7 +1630,8 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { initializeRenderEngine(); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1630,8 +1646,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(1, 1); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1649,8 +1667,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1669,8 +1689,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading @@ -1690,8 +1712,10 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { initializeRenderEngine(); const ubyte4 casterColor(255, 0, 0, 255); const ubyte4 backgroundColor(255, 255, 255, 255); const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(255)); const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255), static_cast<uint8_t>(255)); const float shadowLength = 5.0f; Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); Loading Loading @@ -2027,6 +2051,155 @@ TEST_P(RenderEngineTest, test_isOpaque) { expectBufferColor(rect, 0, 255, 0, 255); } } double EOTF_PQ(double channel) { float m1 = (2610.0 / 4096.0) / 4.0; float m2 = (2523.0 / 4096.0) * 128.0; float c1 = (3424.0 / 4096.0); float c2 = (2413.0 / 4096.0) * 32.0; float c3 = (2392.0 / 4096.0) * 32.0; float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); return std::pow(tmp, 1.0 / m1); } vec3 EOTF_PQ(vec3 color) { return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); } double OETF_sRGB(double channel) { return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; } int sign(float in) { return in >= 0.0 ? 1 : -1; } vec3 OETF_sRGB(vec3 linear) { return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), sign(linear.b) * OETF_sRGB(linear.b)); } TEST_P(RenderEngineTest, test_tonemapPQMatches) { if (!GetParam()->useColorManagement()) { return; } if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { return; } initializeRenderEngine(); constexpr int32_t kGreyLevels = 256; const auto rect = Rect(0, 0, kGreyLevels, 1); const renderengine::DisplaySettings display{ .physicalDisplay = rect, .clip = rect, .maxLuminance = 750.0f, .outputDataspace = ui::Dataspace::DISPLAY_P3, }; auto buf = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "input"), *mRE, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); ASSERT_EQ(0, buf->getBuffer()->initCheck()); { uint8_t* pixels; buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); uint8_t color = 0; for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { dest[0] = color; dest[1] = color; dest[2] = color; dest[3] = 255; color++; dest += 4; } } buf->getBuffer()->unlock(); } mBuffer = std::make_shared< renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "output"), *mRE, renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); const renderengine::LayerSettings layer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = std::move(buf), .usePremultipliedAlpha = true, }, }, .alpha = 1.0f, .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL), }; std::vector<renderengine::LayerSettings> layers{layer}; invokeDraw(display, layers); ColorSpace displayP3 = ColorSpace::DisplayP3(); ColorSpace bt2020 = ColorSpace::BT2020(); tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; auto generator = [=](Point location) { const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); const vec3 rgb = vec3(normColor, normColor, normColor); const vec3 linearRGB = EOTF_PQ(rgb); static constexpr float kMaxPQLuminance = 10000.f; const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance; const double gain = tonemap::getToneMapper() ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: Dataspace>( HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL), static_cast<aidl::android::hardware::graphics::common:: Dataspace>( ui::Dataspace::DISPLAY_P3), linearRGB * 10000.0, xyz, metadata); const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance; const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255; return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), static_cast<uint8_t>(targetRGB.b), 255); }; expectBufferColor(Rect(kGreyLevels, 1), generator, 2); } } // namespace renderengine } // namespace android Loading
libs/tonemap/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -30,7 +30,13 @@ cc_library_static { shared_libs: [ "android.hardware.graphics.common-V3-ndk", "liblog", ], static_libs: [ "libmath", ], srcs: [ "tonemap.cpp", ], Loading
libs/tonemap/include/tonemap/tonemap.h +17 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #pragma once #include <aidl/android/hardware/graphics/common/Dataspace.h> #include <math/vec3.h> #include <string> #include <vector> Loading Loading @@ -48,7 +49,9 @@ struct Metadata { class ToneMapper { public: virtual ~ToneMapper() {} // Constructs a tonemap shader whose shader language is SkSL // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an // input whose dataspace is described by sourceDataspace, to an output whose dataspace // is described by destinationDataspace // // The returned shader string *must* contain a function with the following signature: // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz); Loading Loading @@ -94,6 +97,19 @@ public: // assume that there are predefined floats in_libtonemap_displayMaxLuminance and // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader. virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0; // CPU implementation of the tonemapping gain. This must match the GPU implementation returned // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for // differences in hardware precision. // // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output // described by destinationDataspace. To compute the gain, the input colors are provided by // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also // provided. Metadata is also provided for helping to compute the tonemapping curve. virtual double lookupTonemapGain( aidl::android::hardware::graphics::common::Dataspace sourceDataspace, aidl::android::hardware::graphics::common::Dataspace destinationDataspace, vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0; }; // Retrieves a tonemapper instance. Loading
libs/tonemap/tests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ cc_test { "android.hardware.graphics.common-V3-ndk", ], static_libs: [ "libmath", "libgmock", "libgtest", "libtonemap", Loading