| /* |
| * Copyright 2025 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/codec/SkAndroidCodec.h" |
| #include "include/codec/SkCodec.h" |
| #include "include/codec/SkPngDecoder.h" |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorType.h" |
| #include "include/core/SkImage.h" |
| #include "include/core/SkImageInfo.h" |
| #include "include/core/SkShader.h" |
| #include "include/core/SkSize.h" |
| #include "include/core/SkStream.h" |
| #include "include/encode/SkPngEncoder.h" |
| #include "include/private/SkGainmapInfo.h" |
| #include "include/private/SkGainmapShader.h" |
| #include "tests/GainmapTestCommon.h" |
| #include "tests/Test.h" |
| #include "tools/Resources.h" |
| |
| #include <cstring> |
| #include <memory> |
| #include <utility> |
| |
| |
| |
| // Decode an image and its gainmap. |
| template <typename Reporter> |
| void decode_all(Reporter& r, |
| std::unique_ptr<SkStream> stream, |
| SkBitmap& baseBitmap, |
| SkBitmap& gainmapBitmap, |
| SkGainmapInfo& gainmapInfo, |
| bool& decodedGainmap) { |
| // Decode the base bitmap. |
| SkCodec::Result result = SkCodec::kSuccess; |
| std::unique_ptr<SkCodec> baseCodec = |
| SkPngDecoder::Decode(std::move(stream), &result); |
| REPORTER_ASSERT(r, baseCodec); |
| baseBitmap.allocPixels(baseCodec->getInfo()); |
| REPORTER_ASSERT(r, |
| SkCodec::kSuccess == baseCodec->getPixels(baseBitmap.info(), |
| baseBitmap.getPixels(), |
| baseBitmap.rowBytes())); |
| std::unique_ptr<SkAndroidCodec> androidCodec = |
| SkAndroidCodec::MakeFromCodec(std::move(baseCodec)); |
| REPORTER_ASSERT(r, androidCodec); |
| |
| // Extract the gainmap info and codec. |
| std::unique_ptr<SkAndroidCodec> gainmapCodec; |
| |
| decodedGainmap = androidCodec->getGainmapAndroidCodec(&gainmapInfo, &gainmapCodec); |
| if (decodedGainmap) { |
| REPORTER_ASSERT(r, gainmapCodec); |
| // Decode the gainmap bitmap. |
| gainmapBitmap.allocPixels(gainmapCodec->getInfo()); |
| REPORTER_ASSERT( |
| r, |
| SkCodec::kSuccess == gainmapCodec->getAndroidPixels(gainmapBitmap.info(), |
| gainmapBitmap.getPixels(), |
| gainmapBitmap.rowBytes())); |
| } |
| } |
| |
| DEF_TEST(AndroidCodec_pngGainmapDecode, r) { |
| const struct Rec { |
| const char* path; |
| SkISize dimensions; |
| SkColor originColor; |
| SkColor farCornerColor; |
| SkGainmapInfo info; |
| } recs[] = { |
| {"images/gainmap.png", |
| SkISize::Make(32, 32), |
| 0xffffffff, |
| 0xff000000, |
| {{25.f, 0.5f, 1.f, 1.f}, |
| {2.f, 4.f, 8.f, 1.f}, |
| {0.5, 1.f, 2.f, 1.f}, |
| {0.01f, 0.001f, 0.0001f, 1.f}, |
| {0.0001f, 0.001f, 0.01f, 1.f}, |
| 2.f, |
| 4.f, |
| SkGainmapInfo::BaseImageType::kHDR, |
| SkGainmapInfo::Type::kDefault, |
| nullptr}}, |
| }; |
| |
| for (const auto& rec : recs) { |
| auto stream = GetResourceAsStream(rec.path, false); |
| REPORTER_ASSERT(r, stream); |
| |
| SkBitmap baseBitmap; |
| SkBitmap gainmapBitmap; |
| SkGainmapInfo gainmapInfo; |
| bool decodedGainmap; |
| decode_all(r, std::move(stream), baseBitmap, gainmapBitmap, gainmapInfo, decodedGainmap); |
| |
| REPORTER_ASSERT(r, decodedGainmap); |
| |
| // Spot-check the image size and pixels. |
| REPORTER_ASSERT(r, gainmapBitmap.dimensions() == rec.dimensions); |
| REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == rec.originColor); |
| REPORTER_ASSERT(r, |
| gainmapBitmap.getColor(rec.dimensions.fWidth - 1, |
| rec.dimensions.fHeight - 1) == rec.farCornerColor); |
| |
| // Verify the gainmap rendering parameters. |
| skiatest::ExpectApproxEqInfo(r, rec.info, gainmapInfo); |
| } |
| } |
| |
| DEF_TEST(AndroidCodec_pngGainmapInvalidDecode, r) { |
| const char* paths[] = { |
| "images/gainmap_no_gdat.png", |
| "images/gainmap_gdat_no_gmap.png", |
| |
| }; |
| |
| for (const auto& path : paths) { |
| auto stream = GetResourceAsStream(path, false); |
| REPORTER_ASSERT(r, stream); |
| |
| SkBitmap baseBitmap; |
| SkBitmap gainmapBitmap; |
| SkGainmapInfo gainmapInfo; |
| bool decodedGainmap; |
| decode_all(r, std::move(stream), baseBitmap, gainmapBitmap, gainmapInfo, decodedGainmap); |
| |
| REPORTER_ASSERT(r, !decodedGainmap); |
| } |
| } |
| |
| DEF_TEST(AndroidCodec_pngGainmapEncodeAndDecode, r) { |
| SkColorType colorTypes[] = { |
| kRGBA_8888_SkColorType, |
| kAlpha_8_SkColorType, |
| }; |
| |
| for (const auto& colorType : colorTypes) { |
| SkDynamicMemoryWStream stream; |
| SkGainmapInfo sourceGainmapInfo; |
| sourceGainmapInfo.fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f}; |
| sourceGainmapInfo.fGainmapRatioMax = {5.f, 5.f, 5.f, 1.f}; |
| sourceGainmapInfo.fGainmapGamma = {1.f, 1.f, 1.f, 1.f}; |
| sourceGainmapInfo.fEpsilonSdr = {0.01f, 0.01f, 0.01f, 0.01f}; |
| sourceGainmapInfo.fEpsilonHdr = {0.001f, 0.001f, 0.001f, 0.001f}; |
| sourceGainmapInfo.fDisplayRatioSdr = 1.f; |
| sourceGainmapInfo.fDisplayRatioHdr = 3.f; |
| sourceGainmapInfo.fGainmapMathColorSpace = SkColorSpace::MakeSRGB(); |
| SkBitmap sourceBase; |
| sourceBase.allocPixels( |
| SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kOpaque_SkAlphaType)); |
| sourceBase.eraseColor(SK_ColorRED); |
| SkBitmap sourceGainmap; |
| sourceGainmap.allocPixels(SkImageInfo::Make(4, 4, colorType, kOpaque_SkAlphaType)); |
| sourceGainmap.eraseColor(SK_ColorGREEN); |
| |
| SkPngEncoder::Options options; |
| options.fGainmap = &(sourceGainmap.pixmap()); |
| options.fGainmapInfo = &sourceGainmapInfo; |
| REPORTER_ASSERT(r, SkPngEncoder::Encode(&stream, sourceBase.pixmap(), options)); |
| |
| SkBitmap baseBitmap; |
| SkBitmap gainmapBitmap; |
| SkGainmapInfo gainmapInfo; |
| bool decodedGainmap; |
| decode_all( |
| r, stream.detachAsStream(), baseBitmap, gainmapBitmap, gainmapInfo, decodedGainmap); |
| |
| REPORTER_ASSERT(r, decodedGainmap); |
| |
| REPORTER_ASSERT(r, baseBitmap.dimensions().fHeight == 16); |
| REPORTER_ASSERT(r, baseBitmap.dimensions().fWidth == 16); |
| REPORTER_ASSERT(r, baseBitmap.getColor(0, 0) == SK_ColorRED); |
| REPORTER_ASSERT(r, baseBitmap.getColor(15, 15) == SK_ColorRED); |
| |
| REPORTER_ASSERT(r, gainmapBitmap.dimensions().fHeight == 4); |
| REPORTER_ASSERT(r, gainmapBitmap.dimensions().fWidth == 4); |
| |
| if (colorType == kAlpha_8_SkColorType) { |
| REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == SK_ColorBLACK); |
| REPORTER_ASSERT(r, gainmapBitmap.getColor(3, 3) == SK_ColorBLACK); |
| } else { |
| REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == SK_ColorGREEN); |
| REPORTER_ASSERT(r, gainmapBitmap.getColor(3, 3) == SK_ColorGREEN); |
| } |
| // Verify the gainmap rendering parameters. |
| if (colorType == kAlpha_8_SkColorType) { |
| sourceGainmapInfo.fGainmapMathColorSpace = nullptr; |
| } |
| |
| skiatest::ExpectApproxEqInfo(r, sourceGainmapInfo, gainmapInfo); |
| } |
| } |