#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "Decoder/Png.hpp" #include #include #include #include #include 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(0x80690101); constexpr int invalidSize = static_cast(0x80690102); constexpr int invalidParam = static_cast(0x80690103); constexpr int invalidHandle = static_cast(0x80690104); constexpr int dataOverflow = static_cast(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 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 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(x * 40); pixel[1] = static_cast(y * 80); pixel[2] = static_cast(200 - x * 10); pixel[3] = static_cast(100 + x + y); } } std::vector png(4096); PngEncEncodeParam encode{image.data(), png.data(), static_cast(image.size()), static_cast(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(size) && info.processed_height == height); const auto header = Decoder::Png::ParseHeader(std::span(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(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)); } } } } constexpr std::uint32_t rowSize = 16 * 4; std::vector rows(rowSize * 6); std::uint32_t noise = 12345; for (std::uint32_t y = 0; y < 6; ++y) { for (std::uint32_t i = 0; i < rowSize; ++i) { noise = noise * 1103515245u + 12345u; rows[y * rowSize + i] = y == 2 ? static_cast(noise >> 16) : static_cast(i / 4 * 8); } } std::copy_n(rows.begin() + 2 * rowSize, rowSize, rows.begin() + 3 * rowSize); PngEncEncodeParam filtered{rows.data(), png.data(), static_cast(rows.size()), static_cast(png.size()), 16, 6, rowSize, 0, 19, 8, 0, 0, 6}; const auto encodeWith = [&](std::uint16_t filterType) { filtered.filter_type = filterType; const int size = scePngEncEncode(handle, &filtered, nullptr); Require(size > 0); return std::vector(png.begin(), png.begin() + size); }; using namespace Decoder::Png; const std::pair filterSets[] = { {0, FILTER_NONE}, {1, FILTER_SUB}, {2, FILTER_UP}, {4, FILTER_AVERAGE}, {8, FILTER_PAETH}, {3, FILTER_SUB | FILTER_UP}, {6, FILTER_UP | FILTER_AVERAGE}, {9, FILTER_SUB | FILTER_PAETH}, {14, FILTER_UP | FILTER_AVERAGE | FILTER_PAETH}, {15, FILTER_ALL}}; for (const auto& [filterType, filters] : filterSets) Require(encodeWith(filterType) == Encode(rows, 16, 6, 4, {6, filters})); Require(encodeWith(3) != encodeWith(1) && encodeWith(3) != encodeWith(2) && encodeWith(15) != encodeWith(14)); 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(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); }