#include "prx/libcairo/include/Surface.hpp" #include #include #ifdef NDEBUG #undef NDEBUG #endif #include #include #include #include #include #include #include static_assert(std::is_same_v); static_assert(std::is_same_v); static std::atomic 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 bits{32, 32, 8, 1, 16, 32, 96, 128}; for (int format = 0; format < static_cast(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 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::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"); }