AnyPS5/core/libs/tests/GuestPngEnc.cpp

119 lines
5.9 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include "Decoder/Png.hpp"
#include <cstdint>
#include <cstdlib>
#include <vector>
extern "C" {
int APS5_VABI scePngEncQueryMemorySize(const PngEncCreateParam*);
int APS5_VABI scePngEncCreate(const PngEncCreateParam*, void*, std::uint32_t, void**);
int APS5_VABI scePngEncDelete(void*);
int APS5_VABI scePngEncEncode(void*, const PngEncEncodeParam*, PngEncOutputInfo*);
}
static void Require(bool value) { if (!value) std::abort(); }
int main() {
constexpr int invalidAddr = static_cast<int>(0x80690101);
constexpr int invalidSize = static_cast<int>(0x80690102);
constexpr int invalidParam = static_cast<int>(0x80690103);
constexpr int invalidHandle = static_cast<int>(0x80690104);
constexpr int dataOverflow = static_cast<int>(0x80690110);
constexpr std::uint32_t width = 5;
constexpr std::uint32_t height = 3;
constexpr std::uint32_t pitch = 24;
PngEncCreateParam create{sizeof(PngEncCreateParam), 0, 16, 5};
const int memorySize = scePngEncQueryMemorySize(&create);
Require(memorySize > 0);
Require(scePngEncQueryMemorySize(nullptr) == invalidAddr);
PngEncCreateParam bad = create;
bad.attribute = 1;
Require(scePngEncQueryMemorySize(&bad) == invalidParam);
bad = create;
bad.max_filter_number = 6;
Require(scePngEncQueryMemorySize(&bad) == invalidParam);
bad = create;
bad.max_image_width = 0;
Require(scePngEncQueryMemorySize(&bad) == invalidSize);
bad.max_image_width = 1000001;
Require(scePngEncQueryMemorySize(&bad) == invalidSize);
std::vector<std::uint64_t> memory((memorySize + 7) / 8);
void* handle = nullptr;
Require(scePngEncCreate(&create, nullptr, memorySize, &handle) == invalidAddr);
Require(scePngEncCreate(&create, memory.data(), memorySize - 1, &handle) == invalidSize);
Require(scePngEncCreate(&create, memory.data(), memorySize, &handle) == 0 && handle == memory.data());
std::vector<std::uint8_t> image(pitch * height, 0xEE);
for (std::uint32_t y = 0; y < height; ++y) {
for (std::uint32_t x = 0; x < width; ++x) {
std::uint8_t* pixel = &image[y * pitch + x * 4];
pixel[0] = static_cast<std::uint8_t>(x * 40);
pixel[1] = static_cast<std::uint8_t>(y * 80);
pixel[2] = static_cast<std::uint8_t>(200 - x * 10);
pixel[3] = static_cast<std::uint8_t>(100 + x + y);
}
}
std::vector<std::uint8_t> png(4096);
PngEncEncodeParam encode{image.data(), png.data(), static_cast<std::uint32_t>(image.size()), static_cast<std::uint32_t>(png.size()),
width, height, pitch, 0, 19, 8, 0, 15, 6};
for (const std::uint16_t pixelFormat : {0, 1}) {
for (const std::uint16_t colorSpace : {3, 19}) {
encode.pixel_format = pixelFormat;
encode.color_space = colorSpace;
PngEncOutputInfo info{};
const int size = scePngEncEncode(handle, &encode, &info);
Require(size > 0 && info.data_size == static_cast<std::uint32_t>(size) && info.processed_height == height);
const auto header = Decoder::Png::ParseHeader(std::span<const std::uint8_t>(png.data(), size));
Require(header.has_value());
Require(header->colorType == (colorSpace == 19 ? Decoder::Png::ColorType::Rgba : Decoder::Png::ColorType::Rgb));
const auto decoded = Decoder::Png::Decode(std::span<const std::uint8_t>(png.data(), size));
Require(decoded.has_value() && decoded->width == width && decoded->height == height);
for (std::uint32_t y = 0; y < height; ++y) {
for (std::uint32_t x = 0; x < width; ++x) {
const std::uint8_t* source = &image[y * pitch + x * 4];
const std::uint8_t* result = &decoded->pixels[(y * width + x) * 4];
Require(result[0] == source[pixelFormat == 1 ? 2 : 0] && result[1] == source[1]);
Require(result[2] == source[pixelFormat == 1 ? 0 : 2]);
Require(result[3] == (colorSpace == 19 ? source[3] : 0xFF));
}
}
}
}
encode.png_mem_size = 20;
PngEncOutputInfo info{1, 1};
Require(scePngEncEncode(handle, &encode, &info) == dataOverflow && info.data_size == 0 && info.processed_height == 0);
encode.png_mem_size = static_cast<std::uint32_t>(png.size());
PngEncEncodeParam invalid = encode;
Require(scePngEncEncode(handle, nullptr, nullptr) == invalidParam);
invalid.image_mem_addr = nullptr;
Require(scePngEncEncode(handle, &invalid, nullptr) == invalidAddr);
for (auto change : {+[](PngEncEncodeParam& p) { p.pixel_format = 2; }, +[](PngEncEncodeParam& p) { p.color_space = 4; },
+[](PngEncEncodeParam& p) { p.bit_depth = 16; }, +[](PngEncEncodeParam& p) { p.clut_number = 1; },
+[](PngEncEncodeParam& p) { p.filter_type = 16; }, +[](PngEncEncodeParam& p) { p.compression_level = 10; }}) {
invalid = encode;
change(invalid);
Require(scePngEncEncode(handle, &invalid, nullptr) == invalidParam);
}
for (auto change : {+[](PngEncEncodeParam& p) { p.image_width = 0; }, +[](PngEncEncodeParam& p) { p.image_width = 17; },
+[](PngEncEncodeParam& p) { p.image_pitch = 19; }, +[](PngEncEncodeParam& p) { p.image_mem_size = 67; },
+[](PngEncEncodeParam& p) { p.png_mem_size = 0; }}) {
invalid = encode;
change(invalid);
Require(scePngEncEncode(handle, &invalid, nullptr) == invalidSize);
}
invalid = encode;
invalid.image_mem_size = pitch * (height - 1) + width * 4;
Require(scePngEncEncode(handle, &invalid, nullptr) > 0);
Require(scePngEncDelete(handle) == 0);
Require(scePngEncDelete(handle) == invalidHandle);
Require(scePngEncEncode(handle, &encode, nullptr) == invalidHandle);
Require(scePngEncDelete(nullptr) == invalidHandle);
}