Files
AnyPS5/core/libs/tests/GuestCairoSurface.cpp
T

260 lines
11 KiB
C++

#include "prx/libcairo/include/Surface.hpp"
#include <array>
#include <atomic>
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <cassert>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <limits>
#include <new>
#include <type_traits>
static_assert(std::is_same_v<decltype(cairo_image_surface_create(0, 1, 1)), CairoSurface*>);
static_assert(std::is_same_v<decltype(cairo_image_surface_get_data(nullptr)), unsigned char*>);
static std::atomic<bool> failNextNothrowNew{false};
void* operator new(std::size_t size, const std::nothrow_t&) noexcept {
if (failNextNothrowNew.exchange(false)) return nullptr;
try {
return ::operator new(size);
} catch (const std::bad_alloc&) {
return nullptr;
}
}
namespace {
void CheckStrideAndErrors() {
constexpr std::array<int, 8> bits{32, 32, 8, 1, 16, 32, 96, 128};
for (int format = 0; format < static_cast<int>(bits.size()); ++format) {
for (int width : {0, 1, 2, 5, 20, 61}) {
const int expected = (width * bits[format] + 31) / 32 * 4;
assert(cairo_format_stride_for_width(format, width) == expected);
CairoSurface* image = cairo_image_surface_create(format, width, 3);
assert(image && cairo_surface_status(image) == 0);
assert(cairo_image_surface_get_format(image) == format);
assert(cairo_image_surface_get_width(image) == width);
assert(cairo_image_surface_get_height(image) == 3);
assert(cairo_image_surface_get_stride(image) == expected);
assert(cairo_surface_get_type(image) == 0);
assert(cairo_surface_get_content(image) ==
((format == 0 || format == 7) ? 0x3000 :
(format == 2 || format == 3) ? 0x2000 : 0x1000));
auto* bytes = cairo_image_surface_get_data(image);
assert((bytes != nullptr) == (expected > 0));
for (int n = 0; n < expected * 3; ++n) assert(bytes[n] == 0);
cairo_surface_destroy(image);
}
}
for (int format : {-1, 8, 99}) {
assert(cairo_format_stride_for_width(format, 8) == -1);
CairoSurface* invalid = cairo_image_surface_create(format, 2, 2);
assert(invalid && cairo_surface_status(invalid) == 16);
assert(cairo_image_surface_get_data(invalid) == nullptr);
assert(cairo_image_surface_get_format(invalid) == -1);
assert(cairo_image_surface_get_width(invalid) == 0);
cairo_surface_destroy(invalid);
}
assert(cairo_format_stride_for_width(0, -1) == -1);
assert(cairo_format_stride_for_width(0, INT_MAX) == -1);
assert(cairo_format_stride_for_width(3, INT_MAX) == -1);
assert(cairo_format_stride_for_width(3, 1000000000) == 125000000);
CairoSurface* empty = cairo_image_surface_create(0, 0, 4);
assert(empty && cairo_surface_status(empty) == 0);
assert(cairo_image_surface_get_data(empty) == nullptr);
cairo_surface_destroy(empty);
empty = cairo_image_surface_create(0, 7, 0);
assert(empty && cairo_surface_status(empty) == 0);
assert(cairo_image_surface_get_width(empty) == 7);
assert(cairo_image_surface_get_stride(empty) == 0);
assert(cairo_image_surface_get_data(empty) == nullptr);
cairo_surface_destroy(empty);
CairoSurface* oversized = cairo_image_surface_create(0, 32768, 0);
assert(oversized && cairo_surface_status(oversized) == 32);
cairo_surface_destroy(oversized);
for (auto dims : {std::array{-1, 2}, std::array{2, -1}}) {
CairoSurface* invalid = cairo_image_surface_create(0, dims[0], dims[1]);
assert(invalid && cairo_surface_status(invalid) == 32);
cairo_surface_destroy(invalid);
}
assert(cairo_surface_status(nullptr) == 7);
}
void CheckBorrowedDataAndLifetime() {
alignas(4) std::array<unsigned char, 64> pixels{};
pixels[4] = 123;
CairoSurface* surface = cairo_image_surface_create_for_data(pixels.data(), 0, 3, 4, 16);
assert(surface && cairo_surface_status(surface) == 0);
assert(cairo_image_surface_get_data(surface) == pixels.data());
assert(cairo_image_surface_get_stride(surface) == 16);
assert(cairo_surface_reference(surface) == surface);
cairo_surface_destroy(surface);
assert(cairo_image_surface_get_data(surface)[4] == 123);
cairo_image_surface_get_data(surface)[8] = 42;
cairo_surface_mark_dirty_rectangle(surface, 0, 0, 1, 1);
cairo_surface_flush(surface);
assert(pixels[8] == 42);
cairo_surface_destroy(surface);
assert(pixels[4] == 123);
surface = cairo_image_surface_create_for_data(pixels.data() + 8, 0, 1, 2, -4);
assert(surface && cairo_surface_status(surface) == 0);
assert(cairo_image_surface_get_data(surface) == pixels.data() + 8);
assert(cairo_image_surface_get_stride(surface) == -4);
cairo_surface_destroy(surface);
for (int stride : {0, 4, 8, 11, 13, -1}) {
surface = cairo_image_surface_create_for_data(pixels.data(), 0, 3, 4, stride);
assert(surface && cairo_surface_status(surface) == 24);
assert(cairo_image_surface_get_data(surface) == nullptr);
cairo_surface_destroy(surface);
}
surface = cairo_image_surface_create_for_data(pixels.data(), 0, -1, 2, -1);
assert(surface && cairo_surface_status(surface) == 24);
cairo_surface_destroy(surface);
surface = cairo_image_surface_create_for_data(nullptr, 0, 3, 4, 16);
assert(surface && cairo_surface_status(surface) == 0);
assert(cairo_image_surface_get_data(surface) != nullptr);
assert(cairo_image_surface_get_stride(surface) == 12);
for (int n = 0; n < 48; ++n) assert(cairo_image_surface_get_data(surface)[n] == 0);
cairo_surface_destroy(surface);
surface = cairo_image_surface_create_for_data(nullptr, 0, 2, 0, 16);
assert(surface && cairo_surface_status(surface) == 0);
assert(cairo_image_surface_get_data(surface) == nullptr);
assert(cairo_image_surface_get_stride(surface) == 16);
cairo_surface_destroy(surface);
surface = cairo_image_surface_create_for_data(nullptr, 7, 0, 2, 4);
assert(surface && cairo_surface_status(surface) == 1);
cairo_surface_destroy(surface);
}
void CheckSimilarAndSubsurfaces() {
CairoSurface* parent = cairo_image_surface_create(0, 8, 8);
assert(parent && cairo_surface_status(parent) == 0);
cairo_surface_set_device_scale(parent, 2, 3);
for (int content : {0x1000, 0x2000, 0x3000}) {
CairoSurface* similar = cairo_surface_create_similar(parent, content, 5, 4);
assert(similar && cairo_surface_status(similar) == 0);
assert(cairo_surface_get_content(similar) == content);
assert(cairo_image_surface_get_format(similar) ==
(content == 0x1000 ? 1 : content == 0x2000 ? 2 : 0));
assert(cairo_image_surface_get_width(similar) == 10);
assert(cairo_image_surface_get_height(similar) == 12);
assert(cairo_image_surface_get_stride(similar) ==
(content == 0x2000 ? 12 : 40));
double sx = 0, sy = 0;
cairo_surface_get_device_scale(similar, &sx, &sy);
assert(sx == 2 && sy == 3);
cairo_surface_destroy(similar);
}
CairoSurface* bad = cairo_surface_create_similar(parent, 0, 2, 2);
assert(bad && cairo_surface_status(bad) == 15);
assert(cairo_surface_reference(bad) == bad);
cairo_surface_destroy(bad);
assert(cairo_surface_status(bad) == 15);
cairo_surface_destroy(bad);
bad = cairo_surface_create_similar(parent, 0, -1, 2);
assert(bad && cairo_surface_status(bad) == 32);
cairo_surface_destroy(bad);
cairo_surface_set_device_scale(parent, 1.5, 2.5);
CairoSurface* fractional = cairo_surface_create_similar(parent, 0x3000, 3, 2);
assert(fractional && cairo_surface_status(fractional) == 0);
assert(cairo_image_surface_get_width(fractional) == 4);
assert(cairo_image_surface_get_height(fractional) == 5);
assert(cairo_image_surface_get_stride(fractional) == 16);
cairo_surface_destroy(fractional);
cairo_surface_set_device_scale(parent, 2, 3);
for (const auto [format, content, stride] :
{std::array{3, 0x2000, 4}, std::array{4, 0x1000, 8},
std::array{5, 0x1000, 12}, std::array{6, 0x1000, 36},
std::array{7, 0x3000, 48}}) {
CairoSurface* image = cairo_image_surface_create(format, 3, 2);
CairoSurface* similar = cairo_surface_create_similar(image, content, 3, 2);
assert(similar && cairo_surface_status(similar) == 0);
assert(cairo_image_surface_get_format(similar) == format);
assert(cairo_image_surface_get_stride(similar) == stride);
cairo_surface_destroy(similar);
cairo_surface_destroy(image);
}
CairoSurface* subsection = cairo_surface_create_for_rectangle(parent, 1.5, 2.5, 3.5, 4.5);
assert(subsection && cairo_surface_status(subsection) == 0);
assert(cairo_surface_get_type(subsection) == 0);
assert(cairo_surface_get_content(subsection) == 0x3000);
assert(cairo_image_surface_get_format(subsection) == -1);
assert(cairo_image_surface_get_data(subsection) == nullptr);
assert(cairo_image_surface_get_stride(subsection) == 0);
double sx = 0, sy = 0;
cairo_surface_get_device_scale(subsection, &sx, &sy);
assert(sx == 2 && sy == 3);
cairo_surface_destroy(parent);
assert(cairo_surface_status(subsection) == 0);
cairo_surface_destroy(subsection);
CairoSurface* live = cairo_image_surface_create(0, 4, 4);
bad = cairo_surface_create_for_rectangle(live, 0, 0, -2, 2);
assert(bad && cairo_surface_status(bad) == 32);
cairo_surface_destroy(bad);
bad = cairo_surface_create_for_rectangle(
live, 0, 0, std::numeric_limits<double>::quiet_NaN(), 2);
assert(bad && cairo_surface_status(bad) == 0);
cairo_surface_destroy(bad);
cairo_surface_destroy(live);
bad = cairo_surface_create_for_rectangle(nullptr, 0, 0, 2, 2);
assert(bad && cairo_surface_status(bad) == 7);
cairo_surface_destroy(bad);
bad = cairo_surface_create_similar(nullptr, 0x3000, 4, 4);
assert(bad && cairo_surface_status(bad) == 7);
cairo_surface_destroy(bad);
}
void CheckDeviceScale() {
CairoSurface* surface = cairo_image_surface_create(2, 5, 4);
double sx = -1, sy = -1;
cairo_surface_get_device_scale(surface, &sx, &sy);
assert(sx == 1 && sy == 1);
cairo_surface_set_device_scale(surface, 2, 3.5);
cairo_surface_get_device_scale(surface, &sx, &sy);
assert(sx == 2 && sy == 3.5);
assert(cairo_surface_status(surface) == 0);
cairo_surface_destroy(surface);
cairo_surface_destroy(nullptr);
}
void CheckOutOfMemory() {
failNextNothrowNew = true;
CairoSurface* failed = cairo_image_surface_create(0, 2, 2);
assert(failed && cairo_surface_status(failed) == 1);
assert(cairo_image_surface_get_data(failed) == nullptr);
assert(cairo_surface_reference(failed) == failed);
cairo_surface_destroy(failed);
cairo_surface_destroy(failed);
CairoSurface* target = cairo_image_surface_create(0, 4, 4);
assert(target && cairo_surface_status(target) == 0);
failNextNothrowNew = true;
failed = cairo_surface_create_for_rectangle(target, 0, 0, 2, 2);
assert(failed && cairo_surface_status(failed) == 1);
cairo_surface_destroy(failed);
cairo_surface_destroy(target);
assert(!failNextNothrowNew);
}
}
int main() {
CheckStrideAndErrors();
CheckBorrowedDataAndLifetime();
CheckSimilarAndSubsurfaces();
CheckDeviceScale();
CheckOutOfMemory();
std::puts("guest cairo image/surface tests passed");
}