feat(pngdec): implement libScePngDec on the shared PNG decoder
This commit is contained in:
parent
5853fec906
commit
a0e2f88048
|
|
@ -425,6 +425,15 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_jpeg_enc PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceJpegEnc>")
|
||||
endif()
|
||||
add_executable(guest_png_dec_tests tests/GuestPngDec.cpp)
|
||||
target_include_directories(guest_png_dec_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_png_dec_tests PRIVATE libScePngDec decoder_png libc)
|
||||
configure_windows_unwind(guest_png_dec_tests)
|
||||
add_test(NAME guest_png_dec COMMAND guest_png_dec_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_png_dec PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libScePngDec>")
|
||||
endif()
|
||||
add_executable(guest_font_tests tests/GuestFont.cpp)
|
||||
target_include_directories(guest_font_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_font_tests PRIVATE libSceFont libSceFontFt libc)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
add_library(libScePngDec SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp)
|
||||
target_include_directories(libScePngDec PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
set_target_properties(libScePngDec PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(libScePngDec)
|
||||
target_link_libraries(libScePngDec PRIVATE decoder_png)
|
||||
|
|
@ -1,44 +1,188 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include "Decoder/Png.hpp"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
static_assert(sizeof(PngDecCreateParam) == 0xC);
|
||||
static_assert(sizeof(PngDecParseParam) == 0x10);
|
||||
static_assert(sizeof(PngDecDecodeParam) == 0x20);
|
||||
static_assert(sizeof(PngDecImageInfo) == 0x10);
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_INVALID_ADDR = static_cast<std::int32_t>(0x80690001);
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_INVALID_SIZE = static_cast<std::int32_t>(0x80690002);
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_INVALID_PARAM = static_cast<std::int32_t>(0x80690003);
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_INVALID_HANDLE = static_cast<std::int32_t>(0x80690004);
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_INVALID_WORK_MEMORY = static_cast<std::int32_t>(0x80690005);
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_INVALID_DATA = static_cast<std::int32_t>(0x80690010);
|
||||
constexpr std::int32_t SCE_PNG_DEC_ERROR_DECODE_ERROR = static_cast<std::int32_t>(0x80690012);
|
||||
|
||||
constexpr std::uint32_t ATTRIBUTE_NONE = 0;
|
||||
constexpr std::uint32_t ATTRIBUTE_BIT_DEPTH_16 = 1;
|
||||
constexpr std::uint32_t MAX_IMAGE_WIDTH = 1000000;
|
||||
constexpr std::uint32_t MEMORY_SIZE = 0x20;
|
||||
constexpr std::uintptr_t HANDLE_ALIGNMENT = 8;
|
||||
|
||||
constexpr std::uint16_t PIXEL_FORMAT_R8G8B8A8 = 0;
|
||||
constexpr std::uint16_t PIXEL_FORMAT_B8G8R8A8 = 1;
|
||||
|
||||
constexpr std::uint16_t COLOR_SPACE_GRAYSCALE = 2;
|
||||
constexpr std::uint16_t COLOR_SPACE_RGB = 3;
|
||||
constexpr std::uint16_t COLOR_SPACE_CLUT = 4;
|
||||
constexpr std::uint16_t COLOR_SPACE_GRAYSCALE_ALPHA = 18;
|
||||
constexpr std::uint16_t COLOR_SPACE_RGBA = 19;
|
||||
|
||||
constexpr std::uint32_t IMAGE_FLAG_ADAM7_INTERLACE = 1;
|
||||
constexpr std::uint32_t IMAGE_FLAG_TRNS_CHUNK_EXIST = 2;
|
||||
|
||||
constexpr std::uint32_t BYTES_PER_PIXEL = 4;
|
||||
constexpr std::uint32_t MAX_PACKED_DIMENSION = 32767;
|
||||
|
||||
struct Context {
|
||||
Context* self;
|
||||
std::uint32_t attribute;
|
||||
std::uint32_t maxImageWidth;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Context) + HANDLE_ALIGNMENT - 1 <= MEMORY_SIZE);
|
||||
|
||||
std::int32_t validateCreateParam(const PngDecCreateParam* param) {
|
||||
if (!param) return SCE_PNG_DEC_ERROR_INVALID_PARAM;
|
||||
if (param->attribute != ATTRIBUTE_NONE && param->attribute != ATTRIBUTE_BIT_DEPTH_16) return SCE_PNG_DEC_ERROR_INVALID_PARAM;
|
||||
if (param->max_image_width == 0 || param->max_image_width - 1 > MAX_IMAGE_WIDTH) return SCE_PNG_DEC_ERROR_INVALID_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Context* toContext(void* handle) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(handle);
|
||||
if (address == 0 || address % HANDLE_ALIGNMENT != 0) return nullptr;
|
||||
auto* context = reinterpret_cast<Context*>(handle);
|
||||
return context->self == context ? context : nullptr;
|
||||
}
|
||||
|
||||
std::uint16_t toColorSpace(Decoder::Png::ColorType colorType) {
|
||||
switch (colorType) {
|
||||
case Decoder::Png::ColorType::Grayscale:
|
||||
return COLOR_SPACE_GRAYSCALE;
|
||||
case Decoder::Png::ColorType::Rgb:
|
||||
return COLOR_SPACE_RGB;
|
||||
case Decoder::Png::ColorType::Palette:
|
||||
return COLOR_SPACE_CLUT;
|
||||
case Decoder::Png::ColorType::GrayscaleAlpha:
|
||||
return COLOR_SPACE_GRAYSCALE_ALPHA;
|
||||
case Decoder::Png::ColorType::Rgba:
|
||||
return COLOR_SPACE_RGBA;
|
||||
}
|
||||
throw std::runtime_error("libScePngDec: unknown PNG color type");
|
||||
}
|
||||
|
||||
void fillImageInfo(const Decoder::Png::Header& header, PngDecImageInfo* info) {
|
||||
info->image_width = header.width;
|
||||
info->image_height = header.height;
|
||||
info->color_space = toColorSpace(header.colorType);
|
||||
info->bit_depth = header.bitDepth;
|
||||
info->image_flag = (header.interlaced ? IMAGE_FLAG_ADAM7_INTERLACE : 0) | (header.hasTransparency ? IMAGE_FLAG_TRNS_CHUNK_EXIST : 0);
|
||||
}
|
||||
|
||||
bool hasAlpha(const Decoder::Png::Header& header) {
|
||||
return header.hasTransparency || header.colorType == Decoder::Png::ColorType::GrayscaleAlpha
|
||||
|| header.colorType == Decoder::Png::ColorType::Rgba;
|
||||
}
|
||||
|
||||
std::span<const std::uint8_t> toBytes(const void* data, std::uint32_t size) {
|
||||
return {static_cast<const std::uint8_t*>(data), size};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int32_t APS5_VABI scePngDecCreate(const PngDecCreateParam* param, void* memory_address, uint32_t memory_size, void** handle) {
|
||||
(void)param;
|
||||
(void)memory_address;
|
||||
(void)memory_size;
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
const std::int32_t result = validateCreateParam(param);
|
||||
if (result != 0) return result;
|
||||
if (!memory_address || !handle) return SCE_PNG_DEC_ERROR_INVALID_ADDR;
|
||||
if (memory_size < MEMORY_SIZE) return SCE_PNG_DEC_ERROR_INVALID_WORK_MEMORY;
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(memory_address);
|
||||
const std::uintptr_t aligned = (address + HANDLE_ALIGNMENT - 1) & ~(HANDLE_ALIGNMENT - 1);
|
||||
auto* context = new (reinterpret_cast<void*>(aligned)) Context{};
|
||||
context->self = context;
|
||||
context->attribute = param->attribute;
|
||||
context->maxImageWidth = param->max_image_width;
|
||||
*handle = context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI scePngDecDecode(void* handle, const PngDecDecodeParam* param, PngDecImageInfo* image_info) {
|
||||
(void)handle;
|
||||
(void)param;
|
||||
(void)image_info;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
const Context* context = toContext(handle);
|
||||
if (!context) return SCE_PNG_DEC_ERROR_INVALID_HANDLE;
|
||||
if (!param) return SCE_PNG_DEC_ERROR_INVALID_PARAM;
|
||||
if (!param->png_mem_addr || !param->image_mem_addr) return SCE_PNG_DEC_ERROR_INVALID_ADDR;
|
||||
if (param->png_mem_size == 0 || param->image_mem_size == 0) return SCE_PNG_DEC_ERROR_INVALID_SIZE;
|
||||
if (param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) return SCE_PNG_DEC_ERROR_INVALID_PARAM;
|
||||
|
||||
const std::span<const std::uint8_t> png = toBytes(param->png_mem_addr, param->png_mem_size);
|
||||
const std::optional<Decoder::Png::Header> header = Decoder::Png::ParseHeader(png);
|
||||
if (!header) return SCE_PNG_DEC_ERROR_INVALID_DATA;
|
||||
if (header->bitDepth == 16 && context->attribute == ATTRIBUTE_BIT_DEPTH_16) {
|
||||
throw std::runtime_error("scePngDecDecode: 16-bit output is not implemented");
|
||||
}
|
||||
|
||||
const std::uint64_t rowSize = static_cast<std::uint64_t>(header->width) * BYTES_PER_PIXEL;
|
||||
const std::uint64_t pitch = param->image_pitch == 0 ? rowSize : param->image_pitch;
|
||||
if (pitch < rowSize) return SCE_PNG_DEC_ERROR_INVALID_PARAM;
|
||||
if ((header->height - 1) * pitch + rowSize > param->image_mem_size) return SCE_PNG_DEC_ERROR_INVALID_SIZE;
|
||||
|
||||
const std::optional<Decoder::Png::Image> image = Decoder::Png::Decode(png);
|
||||
if (!image || image->width != header->width || image->height != header->height) return SCE_PNG_DEC_ERROR_DECODE_ERROR;
|
||||
|
||||
const bool swapRedBlue = param->pixel_format == PIXEL_FORMAT_B8G8R8A8;
|
||||
const bool fillAlpha = !hasAlpha(*header);
|
||||
const auto alpha = static_cast<std::uint8_t>(param->alpha_value);
|
||||
auto* output = static_cast<std::uint8_t*>(param->image_mem_addr);
|
||||
for (std::uint32_t y = 0; y < image->height; ++y) {
|
||||
const std::uint8_t* in = image->pixels.data() + y * rowSize;
|
||||
std::uint8_t* out = output + y * pitch;
|
||||
std::memcpy(out, in, rowSize);
|
||||
for (std::uint32_t x = 0; x < image->width; ++x) {
|
||||
std::uint8_t* pixel = out + x * BYTES_PER_PIXEL;
|
||||
if (swapRedBlue) std::swap(pixel[0], pixel[2]);
|
||||
if (fillAlpha) pixel[3] = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
if (image_info) fillImageInfo(*header, image_info);
|
||||
if (header->width > MAX_PACKED_DIMENSION || header->height > MAX_PACKED_DIMENSION) return 0;
|
||||
return static_cast<std::int32_t>(header->width << 16 | header->height);
|
||||
}
|
||||
|
||||
int32_t APS5_VABI scePngDecDelete(void* handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
Context* context = toContext(handle);
|
||||
if (!context) return SCE_PNG_DEC_ERROR_INVALID_HANDLE;
|
||||
context->self = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI scePngDecParseHeader(const PngDecParseParam* param, PngDecImageInfo* image_info) {
|
||||
(void)param;
|
||||
(void)image_info;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!param) return SCE_PNG_DEC_ERROR_INVALID_PARAM;
|
||||
if (!param->png_mem_addr || !image_info) return SCE_PNG_DEC_ERROR_INVALID_ADDR;
|
||||
if (param->png_mem_size == 0) return SCE_PNG_DEC_ERROR_INVALID_SIZE;
|
||||
const std::optional<Decoder::Png::Header> header = Decoder::Png::ParseHeader(toBytes(param->png_mem_addr, param->png_mem_size));
|
||||
if (!header) return SCE_PNG_DEC_ERROR_INVALID_DATA;
|
||||
fillImageInfo(*header, image_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI scePngDecQueryMemorySize(const PngDecCreateParam* param) {
|
||||
(void)param;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
const std::int32_t result = validateCreateParam(param);
|
||||
if (result != 0) return result;
|
||||
return static_cast<std::int32_t>(MEMORY_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "Decoder/Png.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
std::int32_t APS5_VABI scePngDecQueryMemorySize(const PngDecCreateParam*);
|
||||
std::int32_t APS5_VABI scePngDecCreate(const PngDecCreateParam*, void*, std::uint32_t, void**);
|
||||
std::int32_t APS5_VABI scePngDecDelete(void*);
|
||||
std::int32_t APS5_VABI scePngDecParseHeader(const PngDecParseParam*, PngDecImageInfo*);
|
||||
std::int32_t APS5_VABI scePngDecDecode(void*, const PngDecDecodeParam*, PngDecImageInfo*);
|
||||
}
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
template<typename TFunction>
|
||||
static bool ThrowsRuntimeError(TFunction function) {
|
||||
try {
|
||||
function();
|
||||
} catch (const std::runtime_error&) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const std::uint8_t PALETTE_TRNS[] = {
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x0F, 0xD8, 0xE5,
|
||||
0xB7, 0x00, 0x00, 0x00, 0x0C, 0x50, 0x4C, 0x54, 0x45, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xD6, 0x02, 0x8F, 0x7B, 0x00, 0x00, 0x00, 0x04, 0x74, 0x52, 0x4E,
|
||||
0x53, 0xFF, 0x00, 0xFF, 0x80, 0x13, 0x0A, 0x1E, 0x39, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41,
|
||||
0x54, 0x78, 0x9C, 0x63, 0x10, 0x60, 0xD8, 0x00, 0x00, 0x00, 0xE4, 0x00, 0xC1, 0x27, 0xA8, 0xE8,
|
||||
0x57, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
|
||||
};
|
||||
|
||||
static const std::uint8_t GRAY16[] = {
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x81, 0xD9, 0xFC,
|
||||
0x15, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C, 0x63, 0x10, 0x32, 0x59, 0x7D,
|
||||
0x16, 0x00, 0x03, 0x0C, 0x01, 0xBF, 0x6E, 0xB9, 0xC6, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
|
||||
0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
|
||||
};
|
||||
|
||||
static std::vector<std::uint8_t> RgbPng() {
|
||||
std::vector<std::uint8_t> rgb(3 * 2 * 3);
|
||||
for (std::size_t i = 0; i < rgb.size(); ++i) rgb[i] = static_cast<std::uint8_t>(10 + i * 13);
|
||||
return Decoder::Png::Encode(rgb, 3, 2, 3);
|
||||
}
|
||||
|
||||
static PngDecDecodeParam DecodeParam(const std::vector<std::uint8_t>& png, std::vector<std::uint8_t>& image) {
|
||||
PngDecDecodeParam param{};
|
||||
param.png_mem_addr = png.data();
|
||||
param.image_mem_addr = image.data();
|
||||
param.png_mem_size = static_cast<std::uint32_t>(png.size());
|
||||
param.image_mem_size = static_cast<std::uint32_t>(image.size());
|
||||
param.pixel_format = 0;
|
||||
param.alpha_value = 0x77;
|
||||
param.image_pitch = 0;
|
||||
return param;
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr std::int32_t invalidAddr = static_cast<std::int32_t>(0x80690001);
|
||||
constexpr std::int32_t invalidSize = static_cast<std::int32_t>(0x80690002);
|
||||
constexpr std::int32_t invalidParam = static_cast<std::int32_t>(0x80690003);
|
||||
constexpr std::int32_t invalidHandle = static_cast<std::int32_t>(0x80690004);
|
||||
constexpr std::int32_t invalidWorkMemory = static_cast<std::int32_t>(0x80690005);
|
||||
constexpr std::int32_t invalidData = static_cast<std::int32_t>(0x80690010);
|
||||
constexpr std::int32_t decodeError = static_cast<std::int32_t>(0x80690012);
|
||||
|
||||
const PngDecCreateParam param{sizeof(PngDecCreateParam), 0, 1920};
|
||||
const std::int32_t memorySize = scePngDecQueryMemorySize(¶m);
|
||||
Require(memorySize > 0);
|
||||
Require(scePngDecQueryMemorySize(nullptr) == invalidParam);
|
||||
const PngDecCreateParam badAttribute{sizeof(PngDecCreateParam), 2, 1920};
|
||||
Require(scePngDecQueryMemorySize(&badAttribute) == invalidParam);
|
||||
const PngDecCreateParam zeroWidth{sizeof(PngDecCreateParam), 0, 0};
|
||||
Require(scePngDecQueryMemorySize(&zeroWidth) == invalidSize);
|
||||
const PngDecCreateParam hugeWidth{sizeof(PngDecCreateParam), 0, 1000002};
|
||||
Require(scePngDecQueryMemorySize(&hugeWidth) == invalidSize);
|
||||
const PngDecCreateParam maxWidth{sizeof(PngDecCreateParam), 1, 1000001};
|
||||
Require(scePngDecQueryMemorySize(&maxWidth) == memorySize);
|
||||
|
||||
std::vector<unsigned char> memory(static_cast<std::size_t>(memorySize) + 1);
|
||||
unsigned char* unaligned = memory.data() + 1;
|
||||
void* handle = nullptr;
|
||||
Require(scePngDecCreate(nullptr, unaligned, static_cast<std::uint32_t>(memorySize), &handle) == invalidParam);
|
||||
Require(scePngDecCreate(¶m, nullptr, static_cast<std::uint32_t>(memorySize), &handle) == invalidAddr);
|
||||
Require(scePngDecCreate(¶m, unaligned, static_cast<std::uint32_t>(memorySize), nullptr) == invalidAddr);
|
||||
Require(scePngDecCreate(¶m, unaligned, static_cast<std::uint32_t>(memorySize) - 1, &handle) == invalidWorkMemory);
|
||||
Require(handle == nullptr);
|
||||
Require(scePngDecCreate(¶m, unaligned, static_cast<std::uint32_t>(memorySize), &handle) == 0);
|
||||
Require(reinterpret_cast<std::uintptr_t>(handle) % 8 == 0);
|
||||
Require(static_cast<unsigned char*>(handle) >= unaligned && static_cast<unsigned char*>(handle) < unaligned + 8);
|
||||
|
||||
const std::vector<std::uint8_t> rgbPng = RgbPng();
|
||||
PngDecImageInfo info{};
|
||||
const PngDecParseParam parse{rgbPng.data(), static_cast<std::uint32_t>(rgbPng.size()), 0};
|
||||
Require(scePngDecParseHeader(&parse, &info) == 0);
|
||||
Require(info.image_width == 3 && info.image_height == 2 && info.color_space == 3 && info.bit_depth == 8 && info.image_flag == 0);
|
||||
const PngDecParseParam parsePalette{PALETTE_TRNS, sizeof(PALETTE_TRNS), 0};
|
||||
Require(scePngDecParseHeader(&parsePalette, &info) == 0);
|
||||
Require(info.color_space == 4 && info.bit_depth == 2 && info.image_flag == 2);
|
||||
Require(scePngDecParseHeader(nullptr, &info) == invalidParam);
|
||||
Require(scePngDecParseHeader(&parse, nullptr) == invalidAddr);
|
||||
const PngDecParseParam parseNull{nullptr, 16, 0};
|
||||
Require(scePngDecParseHeader(&parseNull, &info) == invalidAddr);
|
||||
const PngDecParseParam parseEmpty{rgbPng.data(), 0, 0};
|
||||
Require(scePngDecParseHeader(&parseEmpty, &info) == invalidSize);
|
||||
const std::uint8_t garbage[40] = {1, 2, 3};
|
||||
const PngDecParseParam parseGarbage{garbage, sizeof(garbage), 0};
|
||||
Require(scePngDecParseHeader(&parseGarbage, &info) == invalidData);
|
||||
|
||||
std::vector<std::uint8_t> image(3 * 2 * 4);
|
||||
PngDecDecodeParam decode = DecodeParam(rgbPng, image);
|
||||
info = {};
|
||||
Require(scePngDecDecode(handle, &decode, &info) == (3 << 16 | 2));
|
||||
Require(info.image_width == 3 && info.image_height == 2 && info.color_space == 3);
|
||||
for (std::size_t pixel = 0; pixel < 6; ++pixel) {
|
||||
for (std::size_t c = 0; c < 3; ++c) Require(image[pixel * 4 + c] == static_cast<std::uint8_t>(10 + (pixel * 3 + c) * 13));
|
||||
Require(image[pixel * 4 + 3] == 0x77);
|
||||
}
|
||||
|
||||
constexpr std::uint32_t pitch = 3 * 4 + 8;
|
||||
std::vector<std::uint8_t> padded(pitch + 3 * 4, 0xEE);
|
||||
decode = DecodeParam(rgbPng, padded);
|
||||
decode.pixel_format = 1;
|
||||
decode.image_pitch = pitch;
|
||||
Require(scePngDecDecode(handle, &decode, nullptr) == (3 << 16 | 2));
|
||||
for (std::size_t y = 0; y < 2; ++y) {
|
||||
for (std::size_t x = 0; x < 3; ++x) {
|
||||
const std::uint8_t* pixel = &padded[y * pitch + x * 4];
|
||||
const std::size_t source = (y * 3 + x) * 3;
|
||||
Require(pixel[0] == static_cast<std::uint8_t>(10 + (source + 2) * 13));
|
||||
Require(pixel[2] == static_cast<std::uint8_t>(10 + source * 13));
|
||||
}
|
||||
}
|
||||
for (std::size_t x = 3 * 4; x < pitch; ++x) Require(padded[x] == 0xEE);
|
||||
|
||||
const std::vector<std::uint8_t> palettePng(PALETTE_TRNS, PALETTE_TRNS + sizeof(PALETTE_TRNS));
|
||||
std::vector<std::uint8_t> paletteImage(2 * 2 * 4);
|
||||
decode = DecodeParam(palettePng, paletteImage);
|
||||
Require(scePngDecDecode(handle, &decode, &info) == (2 << 16 | 2));
|
||||
Require(paletteImage == std::vector<std::uint8_t>({255, 0, 0, 255, 0, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 128}));
|
||||
Require(info.image_flag == 2);
|
||||
|
||||
const std::vector<std::uint8_t> gray16Png(GRAY16, GRAY16 + sizeof(GRAY16));
|
||||
std::vector<std::uint8_t> gray16Image(2 * 4);
|
||||
decode = DecodeParam(gray16Png, gray16Image);
|
||||
Require(scePngDecDecode(handle, &decode, &info) == (2 << 16 | 1));
|
||||
Require(gray16Image == std::vector<std::uint8_t>({0x12, 0x12, 0x12, 0x77, 0xAB, 0xAB, 0xAB, 0x77}));
|
||||
Require(info.bit_depth == 16);
|
||||
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
Require(scePngDecDecode(nullptr, &decode, &info) == invalidHandle);
|
||||
Require(scePngDecDecode(handle, nullptr, &info) == invalidParam);
|
||||
decode.png_mem_addr = nullptr;
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidAddr);
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
decode.image_mem_addr = nullptr;
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidAddr);
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
decode.png_mem_size = 0;
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidSize);
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
decode.pixel_format = 2;
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidParam);
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
decode.image_pitch = 3 * 4 - 1;
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidParam);
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
decode.image_mem_size = 3 * 2 * 4 - 1;
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidSize);
|
||||
const std::vector<std::uint8_t> garbagePng(garbage, garbage + sizeof(garbage));
|
||||
decode = DecodeParam(garbagePng, image);
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidData);
|
||||
const std::vector<std::uint8_t> truncatedPng(rgbPng.begin(), rgbPng.begin() + 40);
|
||||
decode = DecodeParam(truncatedPng, image);
|
||||
Require(scePngDecDecode(handle, &decode, &info) == decodeError);
|
||||
|
||||
void* deepHandle = nullptr;
|
||||
std::vector<unsigned char> deepMemory(static_cast<std::size_t>(memorySize));
|
||||
const PngDecCreateParam deepParam{sizeof(PngDecCreateParam), 1, 1920};
|
||||
Require(scePngDecCreate(&deepParam, deepMemory.data(), static_cast<std::uint32_t>(memorySize), &deepHandle) == 0);
|
||||
decode = DecodeParam(gray16Png, gray16Image);
|
||||
Require(ThrowsRuntimeError([&] { scePngDecDecode(deepHandle, &decode, &info); }));
|
||||
decode = DecodeParam(rgbPng, image);
|
||||
Require(scePngDecDecode(deepHandle, &decode, &info) == (3 << 16 | 2));
|
||||
Require(scePngDecDelete(deepHandle) == 0);
|
||||
|
||||
Require(scePngDecDelete(nullptr) == invalidHandle);
|
||||
Require(scePngDecDelete(static_cast<unsigned char*>(handle) + 1) == invalidHandle);
|
||||
Require(scePngDecDelete(handle) == 0);
|
||||
Require(scePngDecDelete(handle) == invalidHandle);
|
||||
Require(scePngDecDecode(handle, &decode, &info) == invalidHandle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue