blob: 5366ab599f503fcc7aab38049bb9bb8485317761 [file]
/*
* 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);
}
}