diff --git a/sources/enduro2d/utils/image.cpp b/sources/enduro2d/utils/image.cpp index 13681e72..01e9f677 100644 --- a/sources/enduro2d/utils/image.cpp +++ b/sources/enduro2d/utils/image.cpp @@ -147,26 +147,16 @@ namespace e2d switch ( format_ ) { case image_data_format::g8: E2D_ASSERT(bytes_per_pixel == 1); - return color32(pixel[0], - pixel[0], - pixel[0]); + return color32(pixel[0], pixel[0], pixel[0]); case image_data_format::ga8: E2D_ASSERT(bytes_per_pixel == 2); - return color32(pixel[0], - pixel[0], - pixel[0], - pixel[1]); + return color32(pixel[0], pixel[0], pixel[0], pixel[1]); case image_data_format::rgb8: E2D_ASSERT(bytes_per_pixel == 3); - return color32(pixel[0], - pixel[1], - pixel[2]); + return color32(pixel[0], pixel[1], pixel[2]); case image_data_format::rgba8: E2D_ASSERT(bytes_per_pixel == 4); - return color32(pixel[0], - pixel[1], - pixel[2], - pixel[3]); + return color32(pixel[0], pixel[1], pixel[2], pixel[3]); default: E2D_ASSERT_MSG(false, "unexpected image data format"); throw bad_image_access(); @@ -213,9 +203,13 @@ namespace e2d::images image& dst, const buffer& src) noexcept { - return impl::try_load_image_dds(dst, src) - || impl::try_load_image_pvr(dst, src) - || impl::try_load_image_stb(dst, src); + try { + return impl::load_image_dds(dst, src) + || impl::load_image_pvr(dst, src) + || impl::load_image_stb(dst, src); + } catch (...) { + return false; + } } bool try_load_image( @@ -232,20 +226,24 @@ namespace e2d::images image_file_format format, buffer& dst) noexcept { - switch ( format ) { - case image_file_format::dds: - return impl::try_save_image_dds(src, dst); - case image_file_format::jpg: - return impl::try_save_image_jpg(src, dst); - case image_file_format::png: - return impl::try_save_image_png(src, dst); - case image_file_format::pvr: - return impl::try_save_image_pvr(src, dst); - case image_file_format::tga: - return impl::try_save_image_tga(src, dst); - default: - E2D_ASSERT_MSG(false, "unexpected image file format"); - return false; + try { + switch ( format ) { + case image_file_format::dds: + return impl::save_image_dds(src, dst); + case image_file_format::jpg: + return impl::save_image_jpg(src, dst); + case image_file_format::png: + return impl::save_image_png(src, dst); + case image_file_format::pvr: + return impl::save_image_pvr(src, dst); + case image_file_format::tga: + return impl::save_image_tga(src, dst); + default: + E2D_ASSERT_MSG(false, "unexpected image file format"); + return false; + } + } catch (...) { + return false; } } diff --git a/sources/enduro2d/utils/image_impl/image_impl.hpp b/sources/enduro2d/utils/image_impl/image_impl.hpp index c9786f1a..ad4f5d64 100644 --- a/sources/enduro2d/utils/image_impl/image_impl.hpp +++ b/sources/enduro2d/utils/image_impl/image_impl.hpp @@ -11,13 +11,13 @@ namespace e2d::images::impl { - bool try_load_image_dds(image& dst, const buffer& src) noexcept; - bool try_load_image_pvr(image& dst, const buffer& src) noexcept; - bool try_load_image_stb(image& dst, const buffer& src) noexcept; + bool load_image_dds(image& dst, const buffer& src); + bool load_image_pvr(image& dst, const buffer& src); + bool load_image_stb(image& dst, const buffer& src); - bool try_save_image_dds(const image& src, buffer& dst) noexcept; - bool try_save_image_jpg(const image& src, buffer& dst) noexcept; - bool try_save_image_png(const image& src, buffer& dst) noexcept; - bool try_save_image_pvr(const image& src, buffer& dst) noexcept; - bool try_save_image_tga(const image& src, buffer& dst) noexcept; + bool save_image_dds(const image& src, buffer& dst); + bool save_image_jpg(const image& src, buffer& dst); + bool save_image_png(const image& src, buffer& dst); + bool save_image_pvr(const image& src, buffer& dst); + bool save_image_tga(const image& src, buffer& dst); } diff --git a/sources/enduro2d/utils/image_impl/image_reader_dds.cpp b/sources/enduro2d/utils/image_impl/image_reader_dds.cpp index c64785aa..fb0a6916 100644 --- a/sources/enduro2d/utils/image_impl/image_reader_dds.cpp +++ b/sources/enduro2d/utils/image_impl/image_reader_dds.cpp @@ -116,7 +116,7 @@ namespace namespace e2d::images::impl { - bool try_load_image_dds(image& dst, const buffer& src) noexcept { + bool load_image_dds(image& dst, const buffer& src) { if ( !is_dds(src.data(), src.size()) ) { return false; } @@ -139,6 +139,7 @@ namespace e2d::images::impl const v2u dimension = v2u(hdr.dwWidth, hdr.dwHeight); std::size_t size; + switch ( format ) { case image_data_format::rgb_dxt1: case image_data_format::rgba_dxt3: diff --git a/sources/enduro2d/utils/image_impl/image_reader_pvr.cpp b/sources/enduro2d/utils/image_impl/image_reader_pvr.cpp index 2da13b58..7910c481 100644 --- a/sources/enduro2d/utils/image_impl/image_reader_pvr.cpp +++ b/sources/enduro2d/utils/image_impl/image_reader_pvr.cpp @@ -108,7 +108,7 @@ namespace static_assert(sizeof(pvr_header) == 52, "invalid PVR header size"); - bool is_pvr(const void* data, std::size_t byte_size) { + bool is_pvr(const void* data, std::size_t byte_size) noexcept { if ( byte_size > sizeof(pvr_header) ) { const pvr_header* hdr = reinterpret_cast(data); return hdr->version == 0x03525650; @@ -116,11 +116,17 @@ namespace return false; } - bool get_pvr_format(const pvr_header& hdr, image_data_format& out_format, u32& out_bytes_per_block, v2u& out_block_size) { + bool get_pvr_format( + const pvr_header& hdr, + image_data_format& out_format, + u32& out_bytes_per_block, + v2u& out_block_size) noexcept + { if ( pvr_color_space(hdr.colorSpace) != pvr_color_space::linear ) { return false; } - const pvr_pixel_format fmt = pvr_pixel_format(hdr.pixelFormat0 | (u64(hdr.pixelFormat1) << 32)); + const pvr_pixel_format fmt = pvr_pixel_format( + hdr.pixelFormat0 | (u64(hdr.pixelFormat1) << 32)); switch (fmt) { case pvr_pixel_format::pvrtc_2bpp_rgb: @@ -197,25 +203,30 @@ namespace namespace e2d::images::impl { - bool try_load_image_pvr(image& dst, const buffer& src) noexcept { + bool load_image_pvr(image& dst, const buffer& src) { if ( !is_pvr(src.data(), src.size()) ) { return false; } + const pvr_header& hdr = *reinterpret_cast(src.data()); const u8* content = src.data() + sizeof(pvr_header) + hdr.metaDataSize; + if ( hdr.numSurfaces != 1 || hdr.numFaces != 1 || hdr.depth > 1 ) { return false; // cubemap and volume textures are not supported } + image_data_format format = image_data_format(-1); u32 bytes_per_block = 0; v2u block_size = v2u(1,1); if ( !get_pvr_format(hdr, format, bytes_per_block, block_size) ) { return false; } + v2u dimension = v2u(hdr.width, hdr.height); std::size_t size = bytes_per_block * (dimension.x + block_size.x - 1) / block_size.x * (dimension.y + block_size.y - 1) / block_size.y; + dst = image(dimension, format, buffer(content, size)); return true; } diff --git a/sources/enduro2d/utils/image_impl/image_reader_stb.cpp b/sources/enduro2d/utils/image_impl/image_reader_stb.cpp index c20e73b4..5505f224 100644 --- a/sources/enduro2d/utils/image_impl/image_reader_stb.cpp +++ b/sources/enduro2d/utils/image_impl/image_reader_stb.cpp @@ -57,33 +57,22 @@ namespace return image_data_format::rgba8; } } - - bool image_from_stb_description( - image& dst, const stbi_img_uptr& img, const v2u& img_size, u32 img_channels) noexcept - { - try { - const image_data_format img_format = image_format_from_stb_channels(img_channels); - if ( img && img_size.x > 0 && img_size.y > 0 ) { - buffer img_buffer( - img.get(), - math::numeric_cast(img_size.x * img_size.y * img_channels)); - dst.assign(img_size, img_format, std::move(img_buffer)); - return true; - } - } catch (...) { - // nothing - } - return false; - } } namespace e2d::images::impl { - bool try_load_image_stb(image& dst, const buffer& src) noexcept { + bool load_image_stb(image& dst, const buffer& src) { v2u img_size; u32 img_channels = 0; const stbi_img_uptr img_ptr = load_stb_image(src, img_size, img_channels); - return img_ptr - && image_from_stb_description(dst, img_ptr, img_size, img_channels); + if ( !img_ptr || !img_size.x || !img_size.y ) { + return false; + } + + const image_data_format img_format = image_format_from_stb_channels(img_channels); + const std::size_t img_buffer_size = img_size.x * img_size.y * img_channels; + + dst = image(img_size, img_format, buffer(img_ptr.get(), img_buffer_size)); + return true; } } diff --git a/sources/enduro2d/utils/image_impl/image_writer_dds.cpp b/sources/enduro2d/utils/image_impl/image_writer_dds.cpp index e8ec7e2e..0d397d42 100644 --- a/sources/enduro2d/utils/image_impl/image_writer_dds.cpp +++ b/sources/enduro2d/utils/image_impl/image_writer_dds.cpp @@ -29,7 +29,7 @@ namespace namespace e2d::images::impl { - bool try_save_image_dds(const image& src, buffer& dst) noexcept { + bool save_image_dds(const image& src, buffer& dst) { E2D_UNUSED(src, dst); if ( is_save_image_dds_supported(src.format()) ) { //TODO(BlackMat): implme diff --git a/sources/enduro2d/utils/image_impl/image_writer_pvr.cpp b/sources/enduro2d/utils/image_impl/image_writer_pvr.cpp index 011190a6..33611c5b 100644 --- a/sources/enduro2d/utils/image_impl/image_writer_pvr.cpp +++ b/sources/enduro2d/utils/image_impl/image_writer_pvr.cpp @@ -35,7 +35,7 @@ namespace namespace e2d::images::impl { - bool try_save_image_pvr(const image& src, buffer& dst) noexcept { + bool save_image_pvr(const image& src, buffer& dst) { E2D_UNUSED(src, dst); if ( is_save_image_pvr_supported(src.format()) ) { //TODO(BlackMat): implme diff --git a/sources/enduro2d/utils/image_impl/image_writer_stb.cpp b/sources/enduro2d/utils/image_impl/image_writer_stb.cpp index 6fdb258b..29c13538 100644 --- a/sources/enduro2d/utils/image_impl/image_writer_stb.cpp +++ b/sources/enduro2d/utils/image_impl/image_writer_stb.cpp @@ -21,6 +21,27 @@ namespace { using namespace e2d; + struct stb_write_context { + vector data; + std::exception_ptr exception{nullptr}; + }; + + void stb_write_callback(void *context, void *data, int size) { + E2D_ASSERT(context && data && size > 0); + stb_write_context& ctx = *static_cast(context); + if ( ctx.exception ) { + return; + } + try { + ctx.data.insert( + ctx.data.cend(), + reinterpret_cast(data), + reinterpret_cast(data) + math::numeric_cast(size)); + } catch (...) { + ctx.exception = std::current_exception(); + } + } + int stb_channels_from_image_format(image_data_format format) noexcept { switch ( format ) { case image_data_format::g8: return 1; @@ -30,70 +51,85 @@ namespace default: return 0; } } - - void stb_write_callback(void *context, void *data, int size) { - E2D_ASSERT(context && data && size > 0); - vector& ctx = *static_cast*>(context); - ctx.insert( - ctx.cend(), - reinterpret_cast(data), - reinterpret_cast(data) + math::numeric_cast(size)); - } } namespace e2d::images::impl { - bool try_save_image_jpg(const image& src, buffer& dst) noexcept { + bool save_image_jpg(const image& src, buffer& dst) { int img_w = math::numeric_cast(src.size().x); int img_h = math::numeric_cast(src.size().y); int img_c = stb_channels_from_image_format(src.format()); - try { - vector data; - if ( 0 != stbi_write_jpg_to_func( - stb_write_callback, &data, img_w, img_h, img_c, src.data().data(), 80) ) - { - dst.assign(data.data(), data.size()); - return true; - } - } catch (...) { - // nothing + const auto write_ctx = std::make_unique(); + + if ( !stbi_write_jpg_to_func( + stb_write_callback, + write_ctx.get(), + img_w, + img_h, + img_c, + src.data().data(), + 80) ) + { + return false; } - return false; + + if ( write_ctx->exception ) { + std::rethrow_exception(write_ctx->exception); + } + + dst.assign(write_ctx->data.data(), write_ctx->data.size()); + return true; } - bool try_save_image_png(const image& src, buffer& dst) noexcept { + bool save_image_png(const image& src, buffer& dst) { int img_w = math::numeric_cast(src.size().x); int img_h = math::numeric_cast(src.size().y); int img_c = stb_channels_from_image_format(src.format()); - try { - vector data; - if ( 0 != stbi_write_png_to_func( - stb_write_callback, &data, img_w, img_h, img_c, src.data().data(), img_w * img_c) ) - { - dst.assign(data.data(), data.size()); - return true; - } - } catch (...) { - // nothing + const auto write_ctx = std::make_unique(); + + if ( !stbi_write_png_to_func( + stb_write_callback, + write_ctx.get(), + img_w, + img_h, + img_c, + src.data().data(), + img_w * img_c) ) + { + return false; } - return false; + + if ( write_ctx->exception ) { + std::rethrow_exception(write_ctx->exception); + } + + dst.assign(write_ctx->data.data(), write_ctx->data.size()); + return true; } - bool try_save_image_tga(const image& src, buffer& dst) noexcept { + bool save_image_tga(const image& src, buffer& dst) { int img_w = math::numeric_cast(src.size().x); int img_h = math::numeric_cast(src.size().y); int img_c = stb_channels_from_image_format(src.format()); - try { - vector data; - if ( 0 != stbi_write_tga_to_func( - stb_write_callback, &data, img_w, img_h, img_c, src.data().data()) ) - { - dst.assign(data.data(), data.size()); - return true; - } - } catch (...) { - // nothing + const auto write_ctx = std::make_unique(); + + vector data; + if ( !stbi_write_tga_to_func( + stb_write_callback, + write_ctx.get(), + img_w, + img_h, + img_c, + src.data().data()) ) + { + return false; } - return false; + + if ( write_ctx->exception ) { + std::rethrow_exception(write_ctx->exception); + } + + dst.assign(write_ctx->data.data(), write_ctx->data.size()); + return true; } }