Add platform image encoder for using NDK APIs
Bug: skia:10369 Add SkEncodeImageWithNDK, mirroring the CG and WIC versions, for encoding with the NDK APIs added to R. Rename SK_ENABLE_NDK_DECODING to SK_ENABLE_NDK_IMAGES and use it for both encoding and decoding. Move code for converting to/from NDK types into a common location. Update encode_platform.cpp to use NDK encoding APIs when available and to use both types of webp (lossy and lossless). Add tests specifically for the new implementation. Update NdkDecodeTest to use ToolUtils::equal_pixels for comparing pixels. Change-Id: Ic62f89af27372ccce90b8e028e01c388a135a68c Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308800 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
8a8be22597
commit
326b98981e
14
BUILD.gn
14
BUILD.gn
@ -848,10 +848,14 @@ optional("jpeg_encode") {
|
||||
]
|
||||
}
|
||||
|
||||
optional("ndk_decode") {
|
||||
enabled = skia_use_ndk_decode
|
||||
public_defines = [ "SK_ENABLE_NDK_DECODING" ]
|
||||
sources = [ "src/ports/SkImageGeneratorNDK.cpp" ]
|
||||
optional("ndk_images") {
|
||||
enabled = skia_use_ndk_images
|
||||
public_defines = [ "SK_ENABLE_NDK_IMAGES" ]
|
||||
sources = [
|
||||
"src/ports/SkImageEncoder_NDK.cpp",
|
||||
"src/ports/SkImageGeneratorNDK.cpp",
|
||||
"src/ports/SkNDKConversions.cpp",
|
||||
]
|
||||
libs = [ "jnigraphics" ]
|
||||
}
|
||||
|
||||
@ -1079,7 +1083,7 @@ component("skia") {
|
||||
":hsw",
|
||||
":jpeg_decode",
|
||||
":jpeg_encode",
|
||||
":ndk_decode",
|
||||
":ndk_images",
|
||||
":none",
|
||||
":png_decode",
|
||||
":png_encode",
|
||||
|
@ -11,8 +11,10 @@ Milestone 86
|
||||
as 'uniforms'.
|
||||
https://review.skia.org/309050
|
||||
|
||||
* Add SkImageGeneratorNDK for using Android's NDK APIs to decode.
|
||||
https://review.skia.org/305689
|
||||
* Add SkImageGeneratorNDK and SkEncodeImageWithNDK for using Android's NDK APIs to decode and
|
||||
encode.
|
||||
https://review.skia.org/308185
|
||||
https://review.skia.org/308800
|
||||
|
||||
* SkImage:remove DecodeToRaster, DecodeToTexture
|
||||
https://review.skia.org/306331
|
||||
|
@ -838,7 +838,7 @@ static void push_codec_srcs(Path path) {
|
||||
{
|
||||
push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false);
|
||||
}
|
||||
#elif defined(SK_ENABLE_NDK_DECODING)
|
||||
#elif defined(SK_ENABLE_NDK_IMAGES)
|
||||
push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false);
|
||||
#endif
|
||||
}
|
||||
|
@ -924,7 +924,7 @@ Result ImageGenSrc::draw(GrDirectContext*, SkCanvas* canvas) const {
|
||||
gen = SkImageGeneratorCG::MakeFromEncodedCG(encoded);
|
||||
#elif defined(SK_BUILD_FOR_WIN)
|
||||
gen = SkImageGeneratorWIC::MakeFromEncodedWIC(encoded);
|
||||
#elif defined(SK_ENABLE_NDK_DECODING)
|
||||
#elif defined(SK_ENABLE_NDK_IMAGES)
|
||||
gen = SkImageGeneratorNDK::MakeFromEncodedNDK(encoded);
|
||||
#endif
|
||||
if (!gen) {
|
||||
|
@ -24,32 +24,43 @@
|
||||
#include "include/encode/SkWebpEncoder.h"
|
||||
#include "tools/Resources.h"
|
||||
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_WIN)
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_WIN) \
|
||||
|| defined(SK_ENABLE_NDK_IMAGES)
|
||||
#include "src/images/SkImageEncoderPriv.h"
|
||||
#endif
|
||||
|
||||
namespace skiagm {
|
||||
namespace {
|
||||
|
||||
static const struct {
|
||||
SkEncodedImageFormat format;
|
||||
int quality;
|
||||
} gRecs[] = {
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
|
||||
static SkEncodedImageFormat kTypes[] {
|
||||
SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG, SkEncodedImageFormat::kGIF,
|
||||
SkEncodedImageFormat::kBMP, SkEncodedImageFormat::kICO,
|
||||
};
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
{ SkEncodedImageFormat::kJPEG, 100},
|
||||
{ SkEncodedImageFormat::kGIF, 100},
|
||||
{ SkEncodedImageFormat::kBMP, 100},
|
||||
{ SkEncodedImageFormat::kICO, 100},
|
||||
#elif defined(SK_BUILD_FOR_WIN)
|
||||
// Use PNG multiple times because our WIC encoder does not support GIF, BMP, or ICO.
|
||||
static SkEncodedImageFormat kTypes[] {
|
||||
SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG, SkEncodedImageFormat::kPNG,
|
||||
SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kPNG,
|
||||
};
|
||||
// Our WIC encoder does not support GIF, BMP, or ICO.
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
{ SkEncodedImageFormat::kJPEG, 100},
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
#else
|
||||
// Use WEBP in place of GIF. Use PNG two extra times. We don't support GIF, BMP, or ICO.
|
||||
static SkEncodedImageFormat kTypes[] {
|
||||
SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG, SkEncodedImageFormat::kWEBP,
|
||||
SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kPNG,
|
||||
};
|
||||
// We don't support GIF, BMP, or ICO. This applies to both NDK and SkEncoder.
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
{ SkEncodedImageFormat::kJPEG, 100},
|
||||
{ SkEncodedImageFormat::kWEBP, 100}, // Lossless
|
||||
{ SkEncodedImageFormat::kWEBP, 80}, // Lossy
|
||||
{ SkEncodedImageFormat::kPNG, 100},
|
||||
#endif
|
||||
};
|
||||
|
||||
static sk_sp<SkData> encode_data(SkEncodedImageFormat type, const SkBitmap& bitmap) {
|
||||
} // anonymous namespace
|
||||
|
||||
static sk_sp<SkData> encode_data(SkEncodedImageFormat type, const SkBitmap& bitmap, int quality) {
|
||||
SkPixmap src;
|
||||
if (!bitmap.peekPixels(&src)) {
|
||||
return nullptr;
|
||||
@ -58,7 +69,9 @@ static sk_sp<SkData> encode_data(SkEncodedImageFormat type, const SkBitmap& bitm
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
|
||||
return SkEncodeImageWithCG(&buf, src, type) ? buf.detachAsData() : nullptr;
|
||||
#elif defined(SK_BUILD_FOR_WIN)
|
||||
return SkEncodeImageWithWIC(&buf, src, type, 100) ? buf.detachAsData() : nullptr;
|
||||
return SkEncodeImageWithWIC(&buf, src, type, quality) ? buf.detachAsData() : nullptr;
|
||||
#elif defined(SK_ENABLE_NDK_IMAGES)
|
||||
return SkEncodeImageWithNDK(&buf, src, type, quality) ? buf.detachAsData() : nullptr;
|
||||
#else
|
||||
switch (type) {
|
||||
case SkEncodedImageFormat::kPNG: {
|
||||
@ -74,12 +87,13 @@ static sk_sp<SkData> encode_data(SkEncodedImageFormat type, const SkBitmap& bitm
|
||||
return success ? buf.detachAsData() : nullptr;
|
||||
}
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return nullptr;
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
class EncodePlatformGM : public GM {
|
||||
public:
|
||||
EncodePlatformGM() {}
|
||||
@ -90,7 +104,7 @@ protected:
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(256 * SK_ARRAY_COUNT(kTypes), 256 * 3);
|
||||
return SkISize::Make(256 * SK_ARRAY_COUNT(gRecs), 256 * 3);
|
||||
}
|
||||
|
||||
DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
|
||||
@ -112,10 +126,11 @@ protected:
|
||||
unpremulBm.allocPixels(premulBm.info().makeAlphaType(kUnpremul_SkAlphaType));
|
||||
SkAssertResult(premulBm.readPixels(unpremulBm.pixmap()));
|
||||
|
||||
for (SkEncodedImageFormat type : kTypes) {
|
||||
auto opaqueImage = SkImage::MakeFromEncoded(encode_data(type, opaqueBm));
|
||||
auto premulImage = SkImage::MakeFromEncoded(encode_data(type, premulBm));
|
||||
auto unpremulImage = SkImage::MakeFromEncoded(encode_data(type, unpremulBm));
|
||||
for (const auto& rec : gRecs) {
|
||||
auto fmt = rec.format; int q = rec.quality;
|
||||
auto opaqueImage = SkImage::MakeFromEncoded(encode_data(fmt, opaqueBm, q));
|
||||
auto premulImage = SkImage::MakeFromEncoded(encode_data(fmt, premulBm, q));
|
||||
auto unpremulImage = SkImage::MakeFromEncoded(encode_data(fmt, unpremulBm, q));
|
||||
|
||||
canvas->drawImage(opaqueImage.get(), 0.0f, 0.0f);
|
||||
canvas->drawImage(premulImage.get(), 0.0f, 256.0f);
|
||||
|
@ -64,7 +64,7 @@ declare_args() {
|
||||
skia_use_libwebp_encode = true
|
||||
skia_use_lua = is_skia_dev_build && !is_ios
|
||||
skia_use_metal = false
|
||||
skia_use_ndk_decode = is_android && defined(ndk_api) && ndk_api >= 30
|
||||
skia_use_ndk_images = is_android && defined(ndk_api) && ndk_api >= 30
|
||||
skia_use_opencl = false
|
||||
skia_use_piex = !is_win
|
||||
skia_use_wuffs = false
|
||||
|
@ -162,6 +162,7 @@ tests_sources = [
|
||||
"$_tests/MipMapTest.cpp",
|
||||
"$_tests/MultiPictureDocumentTest.cpp",
|
||||
"$_tests/NdkDecodeTest.cpp",
|
||||
"$_tests/NdkEncodeTest.cpp",
|
||||
"$_tests/NonlinearBlendingTest.cpp",
|
||||
"$_tests/OSPathTest.cpp",
|
||||
"$_tests/OctoBoundsTest.cpp",
|
||||
|
@ -5,8 +5,11 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkImageGeneratorNDK_DEFINED
|
||||
#define SkImageGeneratorNDK_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#ifdef SK_ENABLE_NDK_DECODING
|
||||
#ifdef SK_ENABLE_NDK_IMAGES
|
||||
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkImageGenerator.h"
|
||||
@ -33,4 +36,5 @@ namespace SkImageGeneratorNDK {
|
||||
SK_API std::unique_ptr<SkImageGenerator> MakeFromEncodedNDK(sk_sp<SkData>);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // SK_ENABLE_NDK_IMAGES
|
||||
#endif // SkImageGeneratorNDK_DEFINED
|
||||
|
@ -35,6 +35,8 @@ bool SkEncodeImage(SkWStream* dst, const SkPixmap& src,
|
||||
return SkEncodeImageWithCG(dst, src, format);
|
||||
#elif SK_USE_WIC_ENCODER
|
||||
return SkEncodeImageWithWIC(dst, src, format, quality);
|
||||
#elif SK_ENABLE_NDK_IMAGES
|
||||
return SkEncodeImageWithNDK(dst, src, format, quality);
|
||||
#else
|
||||
switch(format) {
|
||||
case SkEncodedImageFormat::kJPEG: {
|
||||
|
@ -35,4 +35,10 @@ static inline bool SkPixmapIsValid(const SkPixmap& src) {
|
||||
#define SkEncodeImageWithWIC(...) false
|
||||
#endif
|
||||
|
||||
#ifdef SK_ENABLE_NDK_IMAGES
|
||||
bool SkEncodeImageWithNDK(SkWStream*, const SkPixmap&, SkEncodedImageFormat, int quality);
|
||||
#else
|
||||
#define SkEncodeImageWithNDK(...) false
|
||||
#endif
|
||||
|
||||
#endif // SkImageEncoderPriv_DEFINED
|
||||
|
70
src/ports/SkImageEncoder_NDK.cpp
Normal file
70
src/ports/SkImageEncoder_NDK.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/private/SkTFitsIn.h"
|
||||
#include "include/private/SkTo.h"
|
||||
#include "src/images/SkImageEncoderPriv.h"
|
||||
#include "src/ports/SkNDKConversions.h"
|
||||
|
||||
bool SkEncodeImageWithNDK(SkWStream* stream, const SkPixmap& pmap, SkEncodedImageFormat format,
|
||||
int quality) {
|
||||
// If any of these values is invalid (e.g. set to zero), the info will be rejected by
|
||||
// AndroidBitmap_compress.
|
||||
AndroidBitmapInfo info {
|
||||
.width = SkTFitsIn<uint32_t>(pmap.width()) ? SkToU32(pmap.width()) : 0,
|
||||
.height = SkTFitsIn<uint32_t>(pmap.height()) ? SkToU32(pmap.height()) : 0,
|
||||
.stride = SkTFitsIn<uint32_t>(pmap.rowBytes()) ? SkToU32(pmap.rowBytes()) : 0,
|
||||
.format = SkNDKConversions::toAndroidBitmapFormat(pmap.colorType())
|
||||
};
|
||||
|
||||
switch (pmap.alphaType()) {
|
||||
case kPremul_SkAlphaType:
|
||||
info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
|
||||
break;
|
||||
case kOpaque_SkAlphaType:
|
||||
info.flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
|
||||
break;
|
||||
case kUnpremul_SkAlphaType:
|
||||
info.flags = ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
AndroidBitmapCompressFormat androidFormat;
|
||||
switch (format) {
|
||||
case SkEncodedImageFormat::kJPEG:
|
||||
androidFormat = ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
|
||||
break;
|
||||
case SkEncodedImageFormat::kPNG:
|
||||
androidFormat = ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
|
||||
break;
|
||||
case SkEncodedImageFormat::kWEBP:
|
||||
if (quality == 100) {
|
||||
// Mimic the behavior of SkImageEncoder.cpp. In LOSSLESS mode, libwebp
|
||||
// interprets quality as the amount of effort (time) to spend making
|
||||
// the encoded image smaller, while the visual quality remains constant.
|
||||
// This value of 75 (on a scale of 0 - 100, where 100 spends the most
|
||||
// time for the smallest encoding) matches WebPConfigInit.
|
||||
androidFormat = ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS;
|
||||
quality = 75;
|
||||
} else {
|
||||
androidFormat = ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
auto write_to_stream = [](void* userContext, const void* data, size_t size) {
|
||||
return reinterpret_cast<SkWStream*>(userContext)->write(data, size);
|
||||
};
|
||||
|
||||
return ANDROID_BITMAP_RESULT_SUCCESS == AndroidBitmap_compress(&info,
|
||||
SkNDKConversions::toDataSpace(pmap.colorSpace()), pmap.addr(), androidFormat, quality,
|
||||
reinterpret_cast<void*>(stream), write_to_stream);
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include "include/core/SkImageGenerator.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/ports/SkImageGeneratorNDK.h"
|
||||
#include "src/ports/SkNDKConversions.h"
|
||||
|
||||
#include <android/bitmap.h>
|
||||
#include <android/data_space.h>
|
||||
@ -44,27 +45,9 @@ static bool ok(int result) {
|
||||
return result == ANDROID_IMAGE_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
namespace {
|
||||
static const struct {
|
||||
SkColorType colorType;
|
||||
AndroidBitmapFormat format;
|
||||
} gColorTypeTable[] = {
|
||||
{ kRGBA_8888_SkColorType, ANDROID_BITMAP_FORMAT_RGBA_8888 },
|
||||
{ kRGBA_F16_SkColorType, ANDROID_BITMAP_FORMAT_RGBA_F16 },
|
||||
{ kRGB_565_SkColorType, ANDROID_BITMAP_FORMAT_RGB_565 },
|
||||
// Android allows using its alpha 8 format to get 8 bit gray pixels.
|
||||
{ kGray_8_SkColorType, ANDROID_BITMAP_FORMAT_A_8 },
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static bool set_android_bitmap_format(AImageDecoder* decoder, SkColorType colorType) {
|
||||
for (const auto& entry : gColorTypeTable) {
|
||||
if (entry.colorType == colorType) {
|
||||
return ok(AImageDecoder_setAndroidBitmapFormat(decoder, entry.format));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
auto format = SkNDKConversions::toAndroidBitmapFormat(colorType);
|
||||
return ok(AImageDecoder_setAndroidBitmapFormat(decoder, format));
|
||||
}
|
||||
|
||||
static SkColorType colorType(AImageDecoder* decoder, const AImageDecoderHeaderInfo* headerInfo) {
|
||||
@ -73,49 +56,15 @@ static SkColorType colorType(AImageDecoder* decoder, const AImageDecoderHeaderIn
|
||||
return kGray_8_SkColorType;
|
||||
}
|
||||
|
||||
const auto format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo);
|
||||
for (const auto& entry : gColorTypeTable) {
|
||||
if (format == entry.format) {
|
||||
return entry.colorType;
|
||||
}
|
||||
}
|
||||
|
||||
SkUNREACHABLE;
|
||||
auto format = static_cast<AndroidBitmapFormat>(
|
||||
AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo));
|
||||
return SkNDKConversions::toColorType(format);
|
||||
}
|
||||
|
||||
static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
static constexpr skcms_Matrix3x3 kDCIP3 = {{
|
||||
{0.486143, 0.323835, 0.154234},
|
||||
{0.226676, 0.710327, 0.0629966},
|
||||
{0.000800549, 0.0432385, 0.78275},
|
||||
}};
|
||||
|
||||
namespace {
|
||||
static const struct {
|
||||
ADataSpace dataSpace;
|
||||
skcms_TransferFunction transferFunction;
|
||||
skcms_Matrix3x3 gamut;
|
||||
} gColorSpaceTable[] = {
|
||||
// Note: ADATASPACE_SCRGB would look the same as ADATASPACE_SRGB. Leaving it out of the table is
|
||||
// fine, since users of the table will will still use SRGB.
|
||||
{ ADATASPACE_SRGB, SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_SCRGB_LINEAR, SkNamedTransferFn::kLinear, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_SRGB_LINEAR, SkNamedTransferFn::kLinear, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_ADOBE_RGB, SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB },
|
||||
{ ADATASPACE_DISPLAY_P3, SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3 },
|
||||
{ ADATASPACE_BT2020, SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020 },
|
||||
{ ADATASPACE_BT709, SkNamedTransferFn::kRec2020, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_DCI_P3, k2Dot6, kDCIP3 },
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
static sk_sp<SkColorSpace> get_default_colorSpace(const AImageDecoderHeaderInfo* headerInfo) {
|
||||
auto dataSpace = AImageDecoderHeaderInfo_getDataSpace(headerInfo);
|
||||
for (const auto& entry : gColorSpaceTable) {
|
||||
if (entry.dataSpace == dataSpace) {
|
||||
return SkColorSpace::MakeRGB(entry.transferFunction, entry.gamut);
|
||||
}
|
||||
auto dataSpace = static_cast<ADataSpace>(AImageDecoderHeaderInfo_getDataSpace(headerInfo));
|
||||
if (auto cs = SkNDKConversions::toColorSpace(dataSpace)) {
|
||||
return cs;
|
||||
}
|
||||
|
||||
return SkColorSpace::MakeSRGB();
|
||||
@ -157,28 +106,6 @@ ImageGeneratorNDK::~ImageGeneratorNDK() {
|
||||
AImageDecoder_delete(fDecoder);
|
||||
}
|
||||
|
||||
static bool nearly_equal(float a, float b) {
|
||||
return fabs(a - b) < .002f;
|
||||
}
|
||||
|
||||
static bool nearly_equal(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
|
||||
return nearly_equal(x.g, y.g)
|
||||
&& nearly_equal(x.a, y.a)
|
||||
&& nearly_equal(x.b, y.b)
|
||||
&& nearly_equal(x.c, y.c)
|
||||
&& nearly_equal(x.d, y.d)
|
||||
&& nearly_equal(x.e, y.e)
|
||||
&& nearly_equal(x.f, y.f);
|
||||
}
|
||||
|
||||
static bool nearly_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (!nearly_equal(a.vals[i][j], b.vals[i][j])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool set_target_size(AImageDecoder* decoder, const SkISize& size, const SkISize targetSize) {
|
||||
if (size != targetSize) {
|
||||
// AImageDecoder will scale to arbitrary sizes. Only support a size if it's supported by the
|
||||
@ -212,19 +139,7 @@ static bool set_target_size(AImageDecoder* decoder, const SkISize& size, const S
|
||||
bool ImageGeneratorNDK::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
|
||||
const Options& opts) {
|
||||
if (auto* cs = info.colorSpace()) {
|
||||
skcms_TransferFunction fn;
|
||||
skcms_Matrix3x3 gamut;
|
||||
if (!cs->isNumericalTransferFn(&fn) || !cs->toXYZD50(&gamut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ADataSpace dataSpace = ADATASPACE_UNKNOWN;
|
||||
for (const auto& entry : gColorSpaceTable) {
|
||||
if (nearly_equal(gamut, entry.gamut) && nearly_equal(fn, entry.transferFunction)) {
|
||||
dataSpace = entry.dataSpace;
|
||||
}
|
||||
}
|
||||
if (!ok(AImageDecoder_setDataSpace(fDecoder, dataSpace))) {
|
||||
if (!ok(AImageDecoder_setDataSpace(fDecoder, SkNDKConversions::toDataSpace(cs)))) {
|
||||
return false;
|
||||
}
|
||||
fPreviouslySetADataSpace = true;
|
||||
|
119
src/ports/SkNDKConversions.cpp
Normal file
119
src/ports/SkNDKConversions.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/ports/SkNDKConversions.h"
|
||||
|
||||
namespace {
|
||||
static const struct {
|
||||
SkColorType colorType;
|
||||
AndroidBitmapFormat format;
|
||||
} gColorTypeTable[] = {
|
||||
{ kRGBA_8888_SkColorType, ANDROID_BITMAP_FORMAT_RGBA_8888 },
|
||||
{ kRGBA_F16_SkColorType, ANDROID_BITMAP_FORMAT_RGBA_F16 },
|
||||
{ kRGB_565_SkColorType, ANDROID_BITMAP_FORMAT_RGB_565 },
|
||||
// Android allows using its alpha 8 format to get 8 bit gray pixels.
|
||||
{ kGray_8_SkColorType, ANDROID_BITMAP_FORMAT_A_8 },
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace SkNDKConversions {
|
||||
AndroidBitmapFormat toAndroidBitmapFormat(SkColorType colorType) {
|
||||
for (const auto& entry : gColorTypeTable) {
|
||||
if (entry.colorType == colorType) {
|
||||
return entry.format;
|
||||
}
|
||||
}
|
||||
return ANDROID_BITMAP_FORMAT_NONE;
|
||||
}
|
||||
|
||||
SkColorType toColorType(AndroidBitmapFormat format) {
|
||||
for (const auto& entry : gColorTypeTable) {
|
||||
if (entry.format == format) {
|
||||
return entry.colorType;
|
||||
}
|
||||
}
|
||||
return kUnknown_SkColorType;
|
||||
}
|
||||
|
||||
} // SkNDKConversions
|
||||
|
||||
static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
static constexpr skcms_Matrix3x3 kDCIP3 = {{
|
||||
{0.486143, 0.323835, 0.154234},
|
||||
{0.226676, 0.710327, 0.0629966},
|
||||
{0.000800549, 0.0432385, 0.78275},
|
||||
}};
|
||||
|
||||
namespace {
|
||||
static const struct {
|
||||
ADataSpace dataSpace;
|
||||
skcms_TransferFunction transferFunction;
|
||||
skcms_Matrix3x3 gamut;
|
||||
} gColorSpaceTable[] = {
|
||||
{ ADATASPACE_SRGB, SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_SCRGB, SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_SCRGB_LINEAR, SkNamedTransferFn::kLinear, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_SRGB_LINEAR, SkNamedTransferFn::kLinear, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_ADOBE_RGB, SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB },
|
||||
{ ADATASPACE_DISPLAY_P3, SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3 },
|
||||
{ ADATASPACE_BT2020, SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020 },
|
||||
{ ADATASPACE_BT709, SkNamedTransferFn::kRec2020, SkNamedGamut::kSRGB },
|
||||
{ ADATASPACE_DCI_P3, k2Dot6, kDCIP3 },
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static bool nearly_equal(float a, float b) {
|
||||
return fabs(a - b) < .002f;
|
||||
}
|
||||
|
||||
static bool nearly_equal(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
|
||||
return nearly_equal(x.g, y.g)
|
||||
&& nearly_equal(x.a, y.a)
|
||||
&& nearly_equal(x.b, y.b)
|
||||
&& nearly_equal(x.c, y.c)
|
||||
&& nearly_equal(x.d, y.d)
|
||||
&& nearly_equal(x.e, y.e)
|
||||
&& nearly_equal(x.f, y.f);
|
||||
}
|
||||
|
||||
static bool nearly_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (!nearly_equal(a.vals[i][j], b.vals[i][j])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace SkNDKConversions {
|
||||
ADataSpace toDataSpace(SkColorSpace* cs) {
|
||||
if (!cs) return ADATASPACE_SRGB;
|
||||
|
||||
skcms_TransferFunction fn;
|
||||
skcms_Matrix3x3 gamut;
|
||||
if (cs->isNumericalTransferFn(&fn) && cs->toXYZD50(&gamut)) {
|
||||
for (const auto& entry : gColorSpaceTable) {
|
||||
if (nearly_equal(gamut, entry.gamut) && nearly_equal(fn, entry.transferFunction)) {
|
||||
return entry.dataSpace;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ADATASPACE_UNKNOWN;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> toColorSpace(ADataSpace dataSpace) {
|
||||
for (const auto& entry : gColorSpaceTable) {
|
||||
if (entry.dataSpace == dataSpace) {
|
||||
return SkColorSpace::MakeRGB(entry.transferFunction, entry.gamut);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
31
src/ports/SkNDKConversions.h
Normal file
31
src/ports/SkNDKConversions.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkNDKConversions_DEFINED
|
||||
#define SkNDKConversions_DEFINED
|
||||
|
||||
#include "include/core/SkColorSpace.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
|
||||
#include <android/bitmap.h>
|
||||
#include <android/data_space.h>
|
||||
|
||||
namespace SkNDKConversions {
|
||||
// Supports a small subset of SkColorType. Others are treated as
|
||||
// ANDROID_BITMAP_FORMAT_NONE.
|
||||
AndroidBitmapFormat toAndroidBitmapFormat(SkColorType);
|
||||
|
||||
SkColorType toColorType(AndroidBitmapFormat);
|
||||
|
||||
// Treats null as ADATASPACE_SRGB.
|
||||
ADataSpace toDataSpace(SkColorSpace*);
|
||||
|
||||
// Treats ADATASPACE_UNKNOWN as nullptr.
|
||||
sk_sp<SkColorSpace> toColorSpace(ADataSpace);
|
||||
}
|
||||
|
||||
#endif // SkNDKConversions_DEFINED
|
@ -6,10 +6,11 @@
|
||||
*/
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#ifdef SK_ENABLE_NDK_DECODING
|
||||
#ifdef SK_ENABLE_NDK_IMAGES
|
||||
#include "include/ports/SkImageGeneratorNDK.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/Resources.h"
|
||||
#include "tools/ToolUtils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -211,29 +212,13 @@ DEF_TEST(NdkDecode_reuseNoColorSpace, r) {
|
||||
corrected.allocPixels(noColorCorrection.makeColorSpace(rec.fCorrectedColorSpace));
|
||||
REPORTER_ASSERT(r, gen->getPixels(corrected.pixmap()));
|
||||
|
||||
bool same = true;
|
||||
for (int y = 0; y < gen->getInfo().height(); y++) {
|
||||
auto* origLine = orig.getAddr(0, y);
|
||||
auto* correctedLine = corrected.getAddr(0, y);
|
||||
if (memcmp(origLine, correctedLine, orig.rowBytes()) != 0) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REPORTER_ASSERT(r, !same);
|
||||
REPORTER_ASSERT(r, !ToolUtils::equal_pixels(orig, corrected));
|
||||
|
||||
SkBitmap reuse;
|
||||
reuse.allocPixels(noColorCorrection);
|
||||
REPORTER_ASSERT(r, gen->getPixels(reuse.pixmap()));
|
||||
|
||||
for (int y = 0; y < gen->getInfo().height(); y++) {
|
||||
auto* origLine = orig.getAddr(0, y);
|
||||
auto* reuseLine = reuse.getAddr(0, y);
|
||||
if (memcmp(origLine, reuseLine, orig.rowBytes()) != 0) {
|
||||
ERRORF(r, "Bitmaps do not match!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
REPORTER_ASSERT(r, ToolUtils::equal_pixels(orig, reuse));
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,14 +339,7 @@ DEF_TEST(NdkDecode_reuseJpeg, r) {
|
||||
reuse.allocPixels(gen->getInfo());
|
||||
REPORTER_ASSERT(r, gen->getPixels(reuse.pixmap()));
|
||||
|
||||
for (int y = 0; y < gen->getInfo().height(); y++) {
|
||||
auto* origLine = orig.getAddr32(0, y);
|
||||
auto* reuseLine = reuse.getAddr32(0, y);
|
||||
if (memcmp(origLine, reuseLine, orig.rowBytes()) != 0) {
|
||||
ERRORF(r, "Bitmaps do not match!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
REPORTER_ASSERT(r, ToolUtils::equal_pixels(orig, reuse));
|
||||
}
|
||||
|
||||
// The NDK supports scaling down to arbitrary dimensions. Skia forces clients to do this in a
|
||||
@ -562,4 +540,4 @@ DEF_TEST(NdkDecode_UnsupportedColorTypes, r) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // SK_ENABLE_NDK_DECODING
|
||||
#endif // SK_ENABLE_NDK_IMAGES
|
||||
|
357
tests/NdkEncodeTest.cpp
Normal file
357
tests/NdkEncodeTest.cpp
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
#ifdef SK_ENABLE_NDK_IMAGES
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkImageEncoder.h"
|
||||
#include "include/core/SkImageGenerator.h"
|
||||
#include "include/private/SkMalloc.h"
|
||||
#include "src/images/SkImageEncoderPriv.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/Resources.h"
|
||||
#include "tools/ToolUtils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
static const char* kPng = "png";
|
||||
static const char* kJpeg = "jpeg";
|
||||
static const char* kWebpLossless = "webp_lossless";
|
||||
static const char* kWebpLossy = "webp_lossy";
|
||||
|
||||
namespace {
|
||||
static const struct {
|
||||
SkEncodedImageFormat format;
|
||||
int quality;
|
||||
const char* name;
|
||||
} gRecs[] = {
|
||||
{ SkEncodedImageFormat::kPNG, 100, kPng},
|
||||
{ SkEncodedImageFormat::kJPEG, 100, kJpeg},
|
||||
{ SkEncodedImageFormat::kWEBP, 100, kWebpLossless},
|
||||
{ SkEncodedImageFormat::kWEBP, 80, kWebpLossy},
|
||||
};
|
||||
}
|
||||
|
||||
static sk_sp<SkData> encode_ndk(const SkPixmap& pmap, SkEncodedImageFormat format, int quality) {
|
||||
SkDynamicMemoryWStream stream;
|
||||
return SkEncodeImageWithNDK(&stream, pmap, format, quality) ? stream.detachAsData() : nullptr;
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode, r) {
|
||||
for (auto ct : { kRGBA_8888_SkColorType,
|
||||
kRGB_565_SkColorType,
|
||||
kRGBA_F16_SkColorType }) {
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType));
|
||||
bm.eraseColor(SK_ColorBLUE);
|
||||
for (const auto& rec : gRecs) {
|
||||
auto encoded = encode_ndk(bm.pixmap(), rec.format, rec.quality);
|
||||
if (!encoded) {
|
||||
ERRORF(r, "Failed to encode %s to %s\n", ToolUtils::colortype_name(ct), rec.name);
|
||||
continue;
|
||||
}
|
||||
auto gen = SkImageGenerator::MakeFromEncoded(std::move(encoded));
|
||||
if (!gen) {
|
||||
ERRORF(r, "Failed to decode from %s as %s\n", ToolUtils::colortype_name(ct),
|
||||
rec.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rec.name == kPng && bm.colorType() == kRGB_565_SkColorType) {
|
||||
REPORTER_ASSERT(r, gen->getInfo().colorType() == kRGB_565_SkColorType);
|
||||
} else {
|
||||
REPORTER_ASSERT(r, gen->getInfo().colorType() == kN32_SkColorType);
|
||||
}
|
||||
|
||||
SkBitmap bm2;
|
||||
bm2.allocPixels(bm.info());
|
||||
REPORTER_ASSERT(r, gen->getPixels(bm2.pixmap()));
|
||||
|
||||
for (int x = 0; x < bm.width(); x++)
|
||||
for (int y = 0; y < bm.height(); y++) {
|
||||
SkColor orig = bm .getColor(x, y);
|
||||
SkColor actual = bm2.getColor(x, y);
|
||||
|
||||
REPORTER_ASSERT(r, SkColorGetA(orig) == SkColorGetA(actual));
|
||||
REPORTER_ASSERT(r, SkColorGetA(orig) == 0xFF);
|
||||
|
||||
if (rec.name == kPng || rec.name == kWebpLossless) {
|
||||
REPORTER_ASSERT(r, orig == actual);
|
||||
} else {
|
||||
int diffR = std::abs((int) SkColorGetR(orig) - (int) SkColorGetR(actual));
|
||||
int diffG = std::abs((int) SkColorGetG(orig) - (int) SkColorGetG(actual));
|
||||
int diffB = std::abs((int) SkColorGetB(orig) - (int) SkColorGetB(actual));
|
||||
REPORTER_ASSERT(r, diffR <= 2 && diffG <= 1 && diffB <= 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_unsupportedFormats, r) {
|
||||
for (auto ct : { kRGBA_8888_SkColorType,
|
||||
kRGB_565_SkColorType,
|
||||
kRGBA_F16_SkColorType }) {
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType));
|
||||
bm.eraseColor(SK_ColorBLUE);
|
||||
for (auto format : { SkEncodedImageFormat::kBMP,
|
||||
SkEncodedImageFormat::kGIF,
|
||||
SkEncodedImageFormat::kICO,
|
||||
SkEncodedImageFormat::kWBMP,
|
||||
SkEncodedImageFormat::kPKM,
|
||||
SkEncodedImageFormat::kKTX,
|
||||
SkEncodedImageFormat::kASTC,
|
||||
SkEncodedImageFormat::kDNG,
|
||||
SkEncodedImageFormat::kHEIF }) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), format, 100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_badQuality, r) {
|
||||
for (auto ct : { kRGBA_8888_SkColorType,
|
||||
kRGB_565_SkColorType,
|
||||
kRGBA_F16_SkColorType }) {
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType));
|
||||
bm.eraseColor(SK_ColorBLUE);
|
||||
for (auto format : { SkEncodedImageFormat::kJPEG,
|
||||
SkEncodedImageFormat::kPNG,
|
||||
SkEncodedImageFormat::kWEBP }) {
|
||||
for (int quality : {-1, -100, 101, 200}) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), format, quality));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_nullPixels, r) {
|
||||
for (auto info : { SkImageInfo::MakeUnknown(),
|
||||
SkImageInfo::MakeN32Premul(10, 10),
|
||||
SkImageInfo::MakeN32Premul(0, 0)}) {
|
||||
SkPixmap pm(info, nullptr, info.minRowBytes());
|
||||
for (const auto& rec : gRecs) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(pm, rec.format, rec.quality));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_badInfo, r) {
|
||||
// Allocate an arbitrary amount of memory. These infos don't have a natural
|
||||
// amount to allocate, and the encoder shouldn't touch the memory anyway.
|
||||
// But this allows us to verify that the bad info fails, even when the pixel
|
||||
// pointer is not null.
|
||||
void* pixels = sk_malloc_throw(1024);
|
||||
std::vector<SkPixmap> pixmaps{ SkPixmap(SkImageInfo::MakeN32Premul(-10, 10), pixels, 1000),
|
||||
SkPixmap(SkImageInfo::MakeN32Premul(10, -10), pixels, 200),
|
||||
SkPixmap(SkImageInfo::MakeN32Premul(10, 10), pixels, 20),
|
||||
SkPixmap(SkImageInfo::MakeN32Premul(10, 10), pixels, 41),
|
||||
SkPixmap(SkImageInfo::MakeN32Premul(10, 10), pixels, 0),
|
||||
SkPixmap(SkImageInfo::MakeN32Premul( 0, 0), pixels, 40)};
|
||||
if (sizeof(size_t) > sizeof(uint32_t)) {
|
||||
pixmaps.emplace_back(SkImageInfo::MakeN32Premul(10, 10), pixels,
|
||||
static_cast<size_t>(UINT32_MAX) + 1);
|
||||
}
|
||||
for (const auto& pm : pixmaps) {
|
||||
for (const auto& rec : gRecs) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(pm, rec.format, rec.quality));
|
||||
}
|
||||
}
|
||||
free(pixels);
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_unsupportedColorTypes, r) {
|
||||
for (SkColorType ct : {
|
||||
kUnknown_SkColorType,
|
||||
kAlpha_8_SkColorType,
|
||||
kARGB_4444_SkColorType,
|
||||
kRGB_888x_SkColorType,
|
||||
kBGRA_8888_SkColorType,
|
||||
kRGBA_1010102_SkColorType,
|
||||
kBGRA_1010102_SkColorType,
|
||||
kRGB_101010x_SkColorType,
|
||||
kBGR_101010x_SkColorType,
|
||||
kGray_8_SkColorType,
|
||||
kRGBA_F16Norm_SkColorType,
|
||||
kRGBA_F32_SkColorType,
|
||||
kR8G8_unorm_SkColorType,
|
||||
kA16_float_SkColorType,
|
||||
kR16G16_float_SkColorType,
|
||||
kA16_unorm_SkColorType,
|
||||
kR16G16_unorm_SkColorType,
|
||||
kR16G16B16A16_unorm_SkColorType,
|
||||
}) {
|
||||
auto info = SkImageInfo::Make(7, 13, ct, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(info);
|
||||
bm.eraseColor(SK_ColorGREEN);
|
||||
for (const auto& rec : gRecs) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), rec.format, rec.quality));
|
||||
}
|
||||
if (!SkColorTypeIsAlwaysOpaque(ct)) {
|
||||
for (auto at : { kPremul_SkAlphaType, kUnpremul_SkAlphaType}) {
|
||||
info = info.makeAlphaType(at);
|
||||
bm.allocPixels(info);
|
||||
bm.eraseARGB(0x7F, 0xFF, 0xFF, 0xFF);
|
||||
}
|
||||
for (const auto& rec : gRecs) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), rec.format, rec.quality));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_unsupportedAlphaTypes, r) {
|
||||
for (auto ct : { kRGBA_8888_SkColorType,
|
||||
kRGB_565_SkColorType,
|
||||
kRGBA_F16_SkColorType }) {
|
||||
for (auto at : { kUnknown_SkAlphaType, (SkAlphaType) -1}) {
|
||||
auto info = SkImageInfo::Make(10, 10, ct, at);
|
||||
size_t rowBytes = info.minRowBytes();
|
||||
void* pixels = sk_malloc_throw(info.computeByteSize(rowBytes));
|
||||
SkPixmap pm(info, pixels, rowBytes);
|
||||
for (const auto& rec : gRecs) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(pm, rec.format, rec.quality));
|
||||
}
|
||||
free(pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
static constexpr skcms_Matrix3x3 kDCIP3 = {{
|
||||
{0.486143, 0.323835, 0.154234},
|
||||
{0.226676, 0.710327, 0.0629966},
|
||||
{0.000800549, 0.0432385, 0.78275},
|
||||
}};
|
||||
|
||||
|
||||
static bool nearly_equal(float a, float b) {
|
||||
return fabs(a - b) < .002f;
|
||||
}
|
||||
|
||||
static bool nearly_equal(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
|
||||
return nearly_equal(x.g, y.g)
|
||||
&& nearly_equal(x.a, y.a)
|
||||
&& nearly_equal(x.b, y.b)
|
||||
&& nearly_equal(x.c, y.c)
|
||||
&& nearly_equal(x.d, y.d)
|
||||
&& nearly_equal(x.e, y.e)
|
||||
&& nearly_equal(x.f, y.f);
|
||||
}
|
||||
|
||||
static bool nearly_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (!nearly_equal(a.vals[i][j], b.vals[i][j])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nearly_equal(SkColorSpace* a, SkColorSpace* b) {
|
||||
skcms_TransferFunction fnA, fnB;
|
||||
skcms_Matrix3x3 gamutA, gamutB;
|
||||
return a && b && a->isNumericalTransferFn(&fnA) && a->toXYZD50(&gamutA)
|
||||
&& b->isNumericalTransferFn(&fnB) && b->toXYZD50(&gamutB)
|
||||
&& nearly_equal(fnA, fnB) && nearly_equal(gamutA, gamutB);
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_ColorSpace, r) {
|
||||
const struct {
|
||||
sk_sp<SkColorSpace> cs;
|
||||
const char* name;
|
||||
} colorSpaces[] = {
|
||||
{ sk_sp<SkColorSpace>(nullptr), "null" },
|
||||
{ SkColorSpace::MakeSRGB(), "srgb" },
|
||||
{ SkColorSpace::MakeSRGBLinear(), "srgb-linear"},
|
||||
{ SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kSRGB), "bt709" },
|
||||
{ SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020), "rec2020" },
|
||||
{ SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3), "p3" },
|
||||
{ SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB), "adobeRGB"},
|
||||
{ SkColorSpace::MakeRGB(k2Dot6, kDCIP3), "dci-p3" },
|
||||
};
|
||||
for (const auto& colorSpace : colorSpaces) {
|
||||
for (auto ct : { kRGBA_8888_SkColorType, kRGB_565_SkColorType, kRGBA_F16_SkColorType }) {
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType, colorSpace.cs));
|
||||
bm.eraseColor(SK_ColorRED);
|
||||
|
||||
for (const auto& rec : gRecs) {
|
||||
auto encoded = encode_ndk(bm.pixmap(), rec.format, rec.quality);
|
||||
REPORTER_ASSERT(r, encoded);
|
||||
auto gen = SkImageGenerator::MakeFromEncoded(std::move(encoded));
|
||||
REPORTER_ASSERT(r, gen);
|
||||
|
||||
auto expected = colorSpace.cs ? colorSpace.cs : SkColorSpace::MakeSRGB();
|
||||
auto* actual = gen->getInfo().colorSpace();
|
||||
if (!nearly_equal(actual, expected.get())) {
|
||||
const char* name = "unknown";
|
||||
for (auto named : colorSpaces) {
|
||||
if (nearly_equal(actual, named.cs.get())) {
|
||||
name = named.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERRORF(r, "Mismatch: expected: %s\tactual:%s", colorSpace.name, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(NdkEncode_unsupportedColorSpace, r) {
|
||||
std::vector<sk_sp<SkColorSpace>> unsupportedCs;
|
||||
for (auto gamut : { SkNamedGamut::kSRGB, SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3,
|
||||
SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut));
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut));
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(k2Dot6, gamut));
|
||||
}
|
||||
|
||||
for (auto gamut : { SkNamedGamut::kSRGB, SkNamedGamut::kDisplayP3,
|
||||
SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, gamut));
|
||||
}
|
||||
|
||||
for (auto gamut : { SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3,
|
||||
SkNamedGamut::kXYZ }) {
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut));
|
||||
}
|
||||
|
||||
for (auto gamut : { SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3,
|
||||
SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut));
|
||||
}
|
||||
|
||||
for (auto gamut : { SkNamedGamut::kAdobeRGB,
|
||||
SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut));
|
||||
}
|
||||
|
||||
for (auto fn : { SkNamedTransferFn::kSRGB, SkNamedTransferFn::k2Dot2,
|
||||
SkNamedTransferFn::kLinear, SkNamedTransferFn::kRec2020 }) {
|
||||
unsupportedCs.push_back(SkColorSpace::MakeRGB(fn, kDCIP3));
|
||||
}
|
||||
|
||||
for (auto unsupported : unsupportedCs) {
|
||||
for (auto ct : { kRGBA_8888_SkColorType, kRGB_565_SkColorType, kRGBA_F16_SkColorType }) {
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType, unsupported));
|
||||
bm.eraseColor(SK_ColorBLUE);
|
||||
|
||||
for (const auto& rec : gRecs) {
|
||||
REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), rec.format, rec.quality));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SK_ENABLE_NDK_IMAGES
|
Loading…
Reference in New Issue
Block a user