diff --git a/Docs/ChangeLog-5x.md b/Docs/ChangeLog-5x.md index 2684fd0a..eb2f5bf9 100644 --- a/Docs/ChangeLog-5x.md +++ b/Docs/ChangeLog-5x.md @@ -17,6 +17,9 @@ The 5.5.0 release is a minor maintenance release. * **Update:** Update stb_image to v1.30. * **Update:** Update Wuffs to v0.3.4. * **Update:** Update TinyEXR to v1.0.13. + * **Bug fix:** Front-end wrapper now uses C++ RAII to manage lifetime of + memory and file handles, fixing a number of resource leaks when on an error + handling path. * **Bug fix:** Throw errors in the command line wrapper for cases where `.astc`, `.dds`, and `.ktx` input image sizes would result in integer overflow in calculations of total block count or data size. diff --git a/Source/astcenc.h b/Source/astcenc.h index 0dd3443b..7d01a135 100644 --- a/Source/astcenc.h +++ b/Source/astcenc.h @@ -324,7 +324,7 @@ ASTCENC_EXTERN_C typedef void (*astcenc_progress_callback)(float); /** * @brief Enable normal map compression. * - * Input data will be treated a two component normal map, storing X and Y, and the codec will + * Input data will be treated as a two component normal map, storing X and Y, and the codec will * optimize for angular error rather than simple linear PSNR. In this mode the input swizzle should * be e.g. rrrg (the default ordering for ASTC normals on the command line) or gggr (the ordering * used by BC5n). @@ -743,7 +743,7 @@ ASTCENC_PUBLIC enum astcenc_error astcenc_config_init( * another context using the same target configuration into @c parent_context. A child will use the * read-only data tables it needs from the ancestor "root" context, rather than creating its own, * which saves a considerable amount of memory per child. You must only free the root context once - * all descendent contexts have been freed. When you pass a @c parent_context the config is taken + * all descendant contexts have been freed. When you pass a @c parent_context the config is taken * from the parent, and so @c context must be @c nullptr. * * Contexts can be allocated to support only decompression using the @c ASTCENC_FLG_DECOMPRESS_ONLY diff --git a/Source/astcenc_color_quantize.cpp b/Source/astcenc_color_quantize.cpp index df17cac3..dbe3bb7b 100644 --- a/Source/astcenc_color_quantize.cpp +++ b/Source/astcenc_color_quantize.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- -// Copyright 2011-2023 Arm Limited +// Copyright 2011-2026 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy @@ -27,7 +27,7 @@ * * Mode: LDR or HDR * * Quantization level * * Channel count: L, LA, RGB, or RGBA - * * Endpoint 2 type: Direct color endcode, or scaled from endpoint 1. + * * Endpoint 2 type: Direct color encoding, or scaled from endpoint 1. * * However, this leaves a number of decisions about exactly how to pack the endpoints open. In * particular we need to determine if blue contraction can be used, or/and if delta encoding can be diff --git a/Source/astcenc_decompress_symbolic.cpp b/Source/astcenc_decompress_symbolic.cpp index a76655e5..4402c1a0 100644 --- a/Source/astcenc_decompress_symbolic.cpp +++ b/Source/astcenc_decompress_symbolic.cpp @@ -216,7 +216,7 @@ void decompress_symbolic_block( vmask4 u8_mask = get_u8_component_mask(decode_mode, blk); // The real decoder would just use the top 8 bits, but we rescale - // in to a 16-bit value that rounds correctly. + // into a 16-bit value that rounds correctly. vint4 colori_u8 = asr<8>(colori) * 257; colori = select(colori, colori_u8, u8_mask); diff --git a/Source/astcenc_diagnostic_trace.cpp b/Source/astcenc_diagnostic_trace.cpp index ebbcb234..cffe4f07 100644 --- a/Source/astcenc_diagnostic_trace.cpp +++ b/Source/astcenc_diagnostic_trace.cpp @@ -84,7 +84,7 @@ TraceNode::TraceNode( vsnprintf (buffer, bufsz, format, args); va_end (args); - // Guarantee there is a nul terminator + // Guarantee there is a NUL terminator buffer[bufsz - 1] = 0; // Generate the node @@ -191,7 +191,7 @@ void trace_add_data( vsnprintf (buffer, bufsz, format, args); va_end (args); - // Guarantee there is a nul terminator + // Guarantee there is a NUL terminator buffer[bufsz - 1] = 0; std::string value = "\"" + std::string(buffer) + "\""; diff --git a/Source/astcenc_diagnostic_trace.h b/Source/astcenc_diagnostic_trace.h index 15f26bab..0e642124 100644 --- a/Source/astcenc_diagnostic_trace.h +++ b/Source/astcenc_diagnostic_trace.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- -// Copyright 2021-2022 Arm Limited +// Copyright 2021-2026 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy @@ -130,7 +130,7 @@ class TraceLog TraceLog(const char* file_name); /** - * @brief Detroy the trace log. + * @brief Destroy the trace log. * * Trace logs MUST be cleanly destroyed to ensure the file gets written. */ diff --git a/Source/astcenc_entry.cpp b/Source/astcenc_entry.cpp index ff928421..5afb4ac8 100644 --- a/Source/astcenc_entry.cpp +++ b/Source/astcenc_entry.cpp @@ -858,7 +858,7 @@ astcenc_error astcenc_context_alloc( return ASTCENC_SUCCESS; } -/* See header dor documentation. */ +/* See header for documentation. */ void astcenc_context_free( astcenc_context* ctxo ) { @@ -884,7 +884,7 @@ void astcenc_context_free( * * @param[out] ctxo The compressor context. * @param thread_index The thread index. - * @param image The intput image. + * @param image The input image. * @param swizzle The input swizzle. * @param[out] buffer The output array for the compressed data. */ diff --git a/Source/astcenc_integer_sequence.cpp b/Source/astcenc_integer_sequence.cpp index 41dc38b7..c2e2d740 100644 --- a/Source/astcenc_integer_sequence.cpp +++ b/Source/astcenc_integer_sequence.cpp @@ -467,7 +467,7 @@ static inline void write_bits( * @brief Read up to 16 bits from two bytes. * * This function reads a packed N-bit field from two bytes in memory. The stored value must exist - * within the two bytes, but can start at an arbitary bit offset and span the two bytes in memory. + * within the two bytes, but can start at an arbitrary bit offset and span the two bytes in memory. * * @param bitcount The number of bits to read. * @param bitoffset The bit offset to read from, between 0 and 7. @@ -565,7 +565,7 @@ void encode_ise( for (unsigned int j = 0; i < character_count; i++, j++) { - // Truncated table as this iteration is always partital + // Truncated table as this iteration is always partial static const uint8_t tbits[4] { 2, 2, 1, 2 }; static const uint8_t tshift[4] { 0, 2, 4, 5 }; @@ -624,7 +624,7 @@ void encode_ise( for (unsigned int j = 0; i < character_count; i++, j++) { - // Truncated table as this iteration is always partital + // Truncated table as this iteration is always partial static const uint8_t tbits[2] { 3, 2 }; static const uint8_t tshift[2] { 0, 3 }; diff --git a/Source/astcenc_internal.h b/Source/astcenc_internal.h index 54bdfb8a..713ebf04 100644 --- a/Source/astcenc_internal.h +++ b/Source/astcenc_internal.h @@ -78,7 +78,7 @@ static constexpr unsigned int BLOCK_MAX_COMPONENTS { 4 }; /** @brief The maximum number of partitions a block can support. */ static constexpr unsigned int BLOCK_MAX_PARTITIONS { 4 }; -/** @brief The number of partitionings, per partition count, suported by the ASTC format. */ +/** @brief The number of partitionings, per partition count, supported by the ASTC format. */ static constexpr unsigned int BLOCK_MAX_PARTITIONINGS { 1024 }; /** @brief The maximum number of texels used during partition selection for texel clustering. */ @@ -1603,7 +1603,7 @@ static inline vmask4 get_u8_component_mask( /** * @brief Setup computation of regional averages in an image. * - * This must be done by only a single thread per image, before any thread calls + * This must be done by a single thread per image, before any thread calls * @c compute_averages(). * * Results are written back into @c img->input_alpha_averages. @@ -1969,7 +1969,7 @@ unsigned int compute_ideal_endpoint_formats( * @param pi The partition info for the current trial. * @param di The weight grid decimation table. * @param dec_weights_uquant The quantized weight set. - * @param[in,out] ep The color endpoints (modifed in place). + * @param[in,out] ep The color endpoints (modified in place). * @param[out] rgbs_vectors The RGB+scale vectors for LDR blocks. * @param[out] rgbo_vectors The RGB+offset vectors for HDR blocks. */ @@ -1993,7 +1993,7 @@ void recompute_ideal_colors_1plane( * @param di The weight grid decimation table. * @param dec_weights_uquant_plane1 The quantized weight set for plane 1. * @param dec_weights_uquant_plane2 The quantized weight set for plane 2. - * @param[in,out] ep The color endpoints (modifed in place). + * @param[in,out] ep The color endpoints (modified in place). * @param[out] rgbs_vector The RGB+scale color for LDR blocks. * @param[out] rgbo_vector The RGB+offset color for HDR blocks. * @param plane2_component The component assigned to plane 2. @@ -2063,7 +2063,7 @@ void compress_block( compression_working_buffers& tmpbuf); /** - * @brief Decompress a symbolic block in to an image block. + * @brief Decompress a symbolic block into an image block. * * @param decode_mode The decode mode (LDR, HDR, etc). * @param bsd The block size information. @@ -2166,7 +2166,7 @@ void symbolic_to_physical( * flagged as an error block if the encoding is invalid. * * @param bsd The block size information. - * @param pcb The physical compresesd block input. + * @param pcb The physical compressed block input. * @param[out] scb The output symbolic representation. */ void physical_to_symbolic( diff --git a/Source/astcenc_mathlib.h b/Source/astcenc_mathlib.h index 8dd30fba..3e30f6e8 100644 --- a/Source/astcenc_mathlib.h +++ b/Source/astcenc_mathlib.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- -// Copyright 2011-2025 Arm Limited +// Copyright 2011-2026 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy @@ -545,7 +545,7 @@ void rand_init(uint64_t state[2]); /** * @brief Return the next random number from the generator. * - * This RNG is an implementation of the "xoroshoro-128+ 1.0" PRNG, based on the + * This RNG is an implementation of the "xoroshiro-128+ 1.0" PRNG, based on the * public-domain implementation given by David Blackman & Sebastiano Vigna at * http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c * diff --git a/Source/astcenc_pick_best_endpoint_format.cpp b/Source/astcenc_pick_best_endpoint_format.cpp index 7de2f2db..a899a7ff 100644 --- a/Source/astcenc_pick_best_endpoint_format.cpp +++ b/Source/astcenc_pick_best_endpoint_format.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- -// Copyright 2011-2025 Arm Limited +// Copyright 2011-2026 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy @@ -51,13 +51,13 @@ * @brief Compute the errors of the endpoint line options for one partition. * * Uncorrelated data assumes storing completely independent RGBA channels for each endpoint. Same - * chroma data assumes storing RGBA endpoints which pass though the origin (LDR only). RGBL data + * chroma data assumes storing RGBA endpoints which pass through the origin (LDR only). RGBL data * assumes storing RGB + lumashift (HDR only). Luminance error assumes storing RGB channels as a * single value. * * * @param pi The partition info data. - * @param partition_index The partition index to compule the error for. + * @param partition_index The partition index to compute the error for. * @param blk The image block. * @param uncor_pline The endpoint line assuming uncorrelated endpoints. * @param[out] uncor_err The computed error for the uncorrelated endpoint line. @@ -154,7 +154,7 @@ static void compute_error_squared_rgb_single_partition( haccumulate(uncor_errv, error, mask); - // Compute same chroma error - no "amod", its always zero + // Compute same chroma error - no "amod", it's always zero param = data_r * samec_bs0 + data_g * samec_bs1 + data_b * samec_bs2; @@ -184,7 +184,7 @@ static void compute_error_squared_rgb_single_partition( haccumulate(rgbl_errv, error, mask); - // Compute luma error - no "amod", its always zero + // Compute luma error - no "amod", it's always zero param = data_r * l_bs0 + data_g * l_bs1 + data_b * l_bs2; diff --git a/Source/astcenc_vecmathlib.h b/Source/astcenc_vecmathlib.h index 29c3cf48..5b95647d 100644 --- a/Source/astcenc_vecmathlib.h +++ b/Source/astcenc_vecmathlib.h @@ -464,7 +464,7 @@ static ASTCENC_SIMD_INLINE vint4 clz(vint4 a) // the original integer value into a 2^N encoding we can recover easily. // Convert to float without risk of rounding up by keeping only top 8 bits. - // This trick is is guaranteed to keep top 8 bits and clear the 9th. + // This trick is guaranteed to keep top 8 bits and clear the 9th. a = (~lsr<8>(a)) & a; a = float_as_int(int_to_float(a)); diff --git a/Source/astcenc_vecmathlib_sse_4.h b/Source/astcenc_vecmathlib_sse_4.h index f6991e46..7cf165b1 100644 --- a/Source/astcenc_vecmathlib_sse_4.h +++ b/Source/astcenc_vecmathlib_sse_4.h @@ -1218,7 +1218,7 @@ ASTCENC_SIMD_INLINE vint4 vtable_lookup_32bit( */ ASTCENC_SIMD_INLINE vint4 interleave_rgba8(vint4 r, vint4 g, vint4 b, vint4 a) { -// Workaround an XCode compiler internal fault; note is slower than slli_epi32 +// Workaround an Xcode compiler internal fault; note it is slower than slli_epi32 // so we should revert this when we get the opportunity #if defined(__APPLE__) __m128i value = r.m; diff --git a/Source/astcenccli_entry2.cpp b/Source/astcenccli_entry2.cpp index 2434a692..58c76a48 100644 --- a/Source/astcenccli_entry2.cpp +++ b/Source/astcenccli_entry2.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- -// Copyright 2024 Arm Limited +// Copyright 2024-2026 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy @@ -20,7 +20,7 @@ * * This module contains the second command line entry point veneer, used to * validate that Arm SVE vector width matches the tool build. When used, it is - * compiled with SVE ISA support but without any vector legnth override, so it + * compiled with SVE ISA support but without any vector length override, so it * will see the native SVE vector length exposed to the application. */ diff --git a/Source/astcenccli_image.cpp b/Source/astcenccli_image.cpp index 342276a0..6f647d5b 100644 --- a/Source/astcenccli_image.cpp +++ b/Source/astcenccli_image.cpp @@ -25,18 +25,23 @@ #include "astcenccli_internal.h" /* See header for documentation. */ -astcenc_image *alloc_image( +astcenc_image_ptr alloc_image( unsigned int bitness, unsigned int dim_x, unsigned int dim_y, unsigned int dim_z ) { - astcenc_image *img = new astcenc_image; + astcenc_image_ptr img { new astcenc_image }; + + // Initialize everything so that we can handle deletion of a partially + // constructed object if new[] throws a bad_alloc exception img->dim_x = dim_x; img->dim_y = dim_y; img->dim_z = dim_z; + img->data_type = ASTCENC_TYPE_U8; + img->data = nullptr; - void** data = new void*[dim_z]; + void** data = new void*[dim_z] {}; img->data = data; if (bitness == 8) @@ -69,19 +74,35 @@ astcenc_image *alloc_image( } /* See header for documentation. */ -void free_image(astcenc_image * img) +void astcenc_image_deleter::operator()(astcenc_image* img) const { if (img == nullptr) { return; } - for (unsigned int z = 0; z < img->dim_z; z++) + if (img->data) { - delete[] reinterpret_cast(img->data[z]); + for (unsigned int z = 0; z < img->dim_z; z++) + { + if (img->data_type == ASTCENC_TYPE_U8) + { + delete[] static_cast(img->data[z]); + } + else if (img->data_type == ASTCENC_TYPE_F16) + { + delete[] static_cast(img->data[z]); + } + else + { + assert(img->data_type == ASTCENC_TYPE_F32); + delete[] static_cast(img->data[z]); + } + } + + delete[] img->data; } - delete[] img->data; delete img; } @@ -168,13 +189,13 @@ int determine_image_components(const astcenc_image * img) } /* See header for documentation. */ -astcenc_image* astc_img_from_floatx4_array( +astcenc_image_ptr astc_img_from_floatx4_array( const float* data, unsigned int dim_x, unsigned int dim_y, bool y_flip ) { - astcenc_image* img = alloc_image(16, dim_x, dim_y, 1); + auto img = alloc_image(16, dim_x, dim_y, 1); for (unsigned int y = 0; y < dim_y; y++) { @@ -202,13 +223,13 @@ astcenc_image* astc_img_from_floatx4_array( } /* See header for documentation. */ -astcenc_image* astc_img_from_unorm8x4_array( +astcenc_image_ptr astc_img_from_unorm8x4_array( const uint8_t* data, unsigned int dim_x, unsigned int dim_y, bool y_flip ) { - astcenc_image* img = alloc_image(8, dim_x, dim_y, 1); + auto img = alloc_image(8, dim_x, dim_y, 1); for (unsigned int y = 0; y < dim_y; y++) { @@ -229,14 +250,15 @@ astcenc_image* astc_img_from_unorm8x4_array( } /* See header for documentation. */ -float* floatx4_array_from_astc_img( +std::vector floatx4_array_from_astc_img( const astcenc_image* img, bool y_flip, unsigned int z_index ) { unsigned int dim_x = img->dim_x; unsigned int dim_y = img->dim_y; - float *buf = new float[4 * dim_x * dim_y]; + std::vector buf(4 * dim_x * dim_y); + float* buf_data = buf.data(); assert(z_index < img->dim_z); @@ -246,7 +268,7 @@ float* floatx4_array_from_astc_img( for (unsigned int y = 0; y < dim_y; y++) { unsigned int ymod = y_flip ? dim_y - y - 1 : y; - float* dst = buf + y * dim_x * 4; + float* dst = buf_data + y * dim_x * 4; for (unsigned int x = 0; x < dim_x; x++) { @@ -263,7 +285,7 @@ float* floatx4_array_from_astc_img( for (unsigned int y = 0; y < dim_y; y++) { unsigned int ymod = y_flip ? dim_y - y - 1 : y; - float *dst = buf + y * dim_x * 4; + float *dst = buf_data + y * dim_x * 4; for (unsigned int x = 0; x < dim_x; x++) { @@ -286,7 +308,7 @@ float* floatx4_array_from_astc_img( for (unsigned int y = 0; y < dim_y; y++) { unsigned int ymod = y_flip ? dim_y - y - 1 : y; - float *dst = buf + y * dim_x * 4; + float *dst = buf_data + y * dim_x * 4; for (unsigned int x = 0; x < dim_x; x++) { @@ -300,76 +322,3 @@ float* floatx4_array_from_astc_img( return buf; } - -/* See header for documentation. */ -uint8_t* unorm8x4_array_from_astc_img( - const astcenc_image* img, - bool y_flip -) { - unsigned int dim_x = img->dim_x; - unsigned int dim_y = img->dim_y; - uint8_t* buf = new uint8_t[4 * dim_x * dim_y]; - - if (img->data_type == ASTCENC_TYPE_U8) - { - uint8_t* data8 = static_cast(img->data[0]); - for (unsigned int y = 0; y < dim_y; y++) - { - unsigned int ymod = y_flip ? dim_y - y - 1 : y; - uint8_t* dst = buf + y * dim_x * 4; - - for (unsigned int x = 0; x < dim_x; x++) - { - dst[4 * x ] = data8[(4 * dim_x * ymod) + (4 * x )]; - dst[4 * x + 1] = data8[(4 * dim_x * ymod) + (4 * x + 1)]; - dst[4 * x + 2] = data8[(4 * dim_x * ymod) + (4 * x + 2)]; - dst[4 * x + 3] = data8[(4 * dim_x * ymod) + (4 * x + 3)]; - } - } - } - else if (img->data_type == ASTCENC_TYPE_F16) - { - uint16_t* data16 = static_cast(img->data[0]); - for (unsigned int y = 0; y < dim_y; y++) - { - unsigned int ymod = y_flip ? dim_y - y - 1 : y; - uint8_t* dst = buf + y * dim_x * 4; - - for (unsigned int x = 0; x < dim_x; x++) - { - vint4 colori( - data16[(4 * dim_x * ymod) + (4 * x )], - data16[(4 * dim_x * ymod) + (4 * x + 1)], - data16[(4 * dim_x * ymod) + (4 * x + 2)], - data16[(4 * dim_x * ymod) + (4 * x + 3)] - ); - - vfloat4 color = float16_to_float(colori); - color = clamp(0.0f, 1.0f, color) * 255.0f; - - colori = float_to_int_rtn(color); - pack_and_store_low_bytes(colori, dst + 4 * x); - } - } - } - else // if (img->data_type == ASTCENC_TYPE_F32) - { - assert(img->data_type == ASTCENC_TYPE_F32); - float* data32 = static_cast(img->data[0]); - for (unsigned int y = 0; y < dim_y; y++) - { - unsigned int ymod = y_flip ? dim_y - y - 1 : y; - uint8_t* dst = buf + y * dim_x * 4; - - for (unsigned int x = 0; x < dim_x; x++) - { - dst[4 * x ] = static_cast(astc::flt2int_rtn(astc::clamp1f(data32[(4 * dim_x * ymod) + (4 * x )]) * 255.0f)); - dst[4 * x + 1] = static_cast(astc::flt2int_rtn(astc::clamp1f(data32[(4 * dim_x * ymod) + (4 * x + 1)]) * 255.0f)); - dst[4 * x + 2] = static_cast(astc::flt2int_rtn(astc::clamp1f(data32[(4 * dim_x * ymod) + (4 * x + 2)]) * 255.0f)); - dst[4 * x + 3] = static_cast(astc::flt2int_rtn(astc::clamp1f(data32[(4 * dim_x * ymod) + (4 * x + 3)]) * 255.0f)); - } - } - } - - return buf; -} diff --git a/Source/astcenccli_image_external.cpp b/Source/astcenccli_image_external.cpp index e723c121..fee761b1 100644 --- a/Source/astcenccli_image_external.cpp +++ b/Source/astcenccli_image_external.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "astcenccli_internal.h" @@ -70,6 +71,14 @@ static void astcenc_runtime_assert(bool condition) #include "ThirdParty/tinyexr.h" #include "ThirdParty/wuffs-v0.3.c" +struct wuffs_image_deleter +{ + void operator()(void* ptr) const + { + free(ptr); + } +}; + /** * @brief Load an image using Wuffs to provide the loader. * @@ -80,7 +89,7 @@ static void astcenc_runtime_assert(bool condition) * * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error. */ -astcenc_image* load_png_with_wuffs( +astcenc_image_ptr load_png_with_wuffs( const char* filename, bool y_flip, bool& is_hdr, @@ -102,7 +111,7 @@ astcenc_image* load_png_with_wuffs( std::vector buffer(size); file.read((char*)buffer.data(), size); - wuffs_png__decoder *dec = wuffs_png__decoder__alloc(); + std::unique_ptr dec { wuffs_png__decoder__alloc() }; if (!dec) { return nullptr; @@ -110,7 +119,7 @@ astcenc_image* load_png_with_wuffs( wuffs_base__image_config ic; wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(buffer.data(), size, true); - wuffs_base__status status = wuffs_png__decoder__decode_image_config(dec, &ic, &src); + wuffs_base__status status = wuffs_png__decoder__decode_image_config(dec.get(), &ic, &src); if (status.repr) { return nullptr; @@ -132,23 +141,17 @@ astcenc_image* load_png_with_wuffs( dim_x, dim_y); // Configure the work buffer - size_t workbuf_len = wuffs_png__decoder__workbuf_len(dec).max_incl; + uint64_t workbuf_len = wuffs_png__decoder__workbuf_len(dec.get()).max_incl; if (workbuf_len > SIZE_MAX) { return nullptr; } - wuffs_base__slice_u8 workbuf_slice = wuffs_base__make_slice_u8((uint8_t*)malloc(workbuf_len), workbuf_len); - if (!workbuf_slice.ptr) - { - return nullptr; - } + std::vector workbuf(workbuf_len); + wuffs_base__slice_u8 workbuf_slice = wuffs_base__make_slice_u8(workbuf.data(), workbuf.size()); - wuffs_base__slice_u8 pixbuf_slice = wuffs_base__make_slice_u8((uint8_t*)malloc(num_pixels * 4), num_pixels * 4); - if (!pixbuf_slice.ptr) - { - return nullptr; - } + std::vector pixbuf(num_pixels * 4); + wuffs_base__slice_u8 pixbuf_slice = wuffs_base__make_slice_u8(pixbuf.data(), pixbuf.size()); wuffs_base__pixel_buffer pb; status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, pixbuf_slice); @@ -158,17 +161,11 @@ astcenc_image* load_png_with_wuffs( } // Decode the pixels - status = wuffs_png__decoder__decode_frame(dec, &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf_slice, NULL); + status = wuffs_png__decoder__decode_frame(dec.get(), &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf_slice, NULL); if (status.repr) { return nullptr; } - astcenc_image* img = astc_img_from_unorm8x4_array(pixbuf_slice.ptr, dim_x, dim_y, y_flip); - - free(pixbuf_slice.ptr); - free(workbuf_slice.ptr); - free(dec); - - return img; + return astc_img_from_unorm8x4_array(pixbuf_slice.ptr, dim_x, dim_y, y_flip); } diff --git a/Source/astcenccli_image_load_store.cpp b/Source/astcenccli_image_load_store.cpp index 3ac375bc..2ee02ae1 100644 --- a/Source/astcenccli_image_load_store.cpp +++ b/Source/astcenccli_image_load_store.cpp @@ -26,8 +26,11 @@ #include #include #include +#include #include #include +#include +#include #include "astcenccli_internal.h" @@ -82,6 +85,30 @@ static std::string get_output_filename( Image load and store through the stb_image and tinyexr libraries ============================================================================ */ +struct tinyexr_image_deleter +{ + void operator()(void* ptr) const + { + free(ptr); + } +}; + +struct tinyexr_error_deleter +{ + void operator()(const void* ptr) const + { + free(const_cast(ptr)); + } +}; + +struct stbi_image_deleter +{ + void operator()(void* ptr) const + { + stbi_image_free(ptr); + } +}; + /** * @brief Load a .exr image using TinyExr to provide the loader. * @@ -92,26 +119,27 @@ static std::string get_output_filename( * * @return The loaded image data in a canonical 4 channel format. */ -static astcenc_image* load_image_with_tinyexr( +static astcenc_image_ptr load_image_with_tinyexr( const char* filename, bool y_flip, bool& is_hdr, unsigned int& component_count ) { int dim_x, dim_y; - float* image; - const char* err; + float* image_raw; + const char* err { nullptr }; - int load_res = LoadEXR(&image, &dim_x, &dim_y, filename, &err); + int load_res = LoadEXR(&image_raw, &dim_x, &dim_y, filename, &err); if (load_res != TINYEXR_SUCCESS) { - print_error("ERROR: Image load failed '%s' (%s)\n", filename, err); - free(reinterpret_cast(const_cast(err))); + std::unique_ptr err_ptr { err }; + print_error("ERROR: Image load failed '%s' (%s)\n", + filename, err_ptr.get() ? err_ptr.get() : "unknown error"); return nullptr; } - astcenc_image* res_img = astc_img_from_floatx4_array(image, dim_x, dim_y, y_flip); - free(image); + std::unique_ptr image { image_raw }; + auto res_img = astc_img_from_floatx4_array(image.get(), dim_x, dim_y, y_flip); is_hdr = true; component_count = 4; @@ -128,7 +156,7 @@ static astcenc_image* load_image_with_tinyexr( * * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error. */ -static astcenc_image* load_image_with_stb( +static astcenc_image_ptr load_image_with_stb( const char* filename, bool y_flip, bool& is_hdr, @@ -138,11 +166,12 @@ static astcenc_image* load_image_with_stb( if (stbi_is_hdr(filename)) { - float* data = stbi_loadf(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha); + std::unique_ptr data { + stbi_loadf(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha) + }; if (data) { - astcenc_image* img = astc_img_from_floatx4_array(data, dim_x, dim_y, y_flip); - stbi_image_free(data); + auto img = astc_img_from_floatx4_array(data.get(), dim_x, dim_y, y_flip); is_hdr = true; component_count = 4; return img; @@ -150,11 +179,12 @@ static astcenc_image* load_image_with_stb( } else { - uint8_t* data = stbi_load(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha); + std::unique_ptr data { + stbi_load(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha) + }; if (data) { - astcenc_image* img = astc_img_from_unorm8x4_array(data, dim_x, dim_y, y_flip); - stbi_image_free(data); + auto img = astc_img_from_unorm8x4_array(data.get(), dim_x, dim_y, y_flip); is_hdr = false; component_count = 4; return img; @@ -184,10 +214,9 @@ static bool store_exr_image_with_tinyexr( for (unsigned int i = 0; i < img->dim_z; i++) { std::string fnmod = get_output_filename(img, filename, i); - float* buf = floatx4_array_from_astc_img(img, y_flip, i); + std::vector buf = floatx4_array_from_astc_img(img, y_flip, i); - res = SaveEXR(buf, img->dim_x, img->dim_y, 4, 1, fnmod.c_str(), nullptr); - delete[] buf; + res = SaveEXR(buf.data(), img->dim_x, img->dim_y, 4, 1, fnmod.c_str(), nullptr); if (res < 0) { break; @@ -318,10 +347,9 @@ static bool store_hdr_image_with_stb( for (unsigned int i = 0; i < img->dim_z; i++) { std::string fnmod = get_output_filename(img, filename, i); - float* buf = floatx4_array_from_astc_img(img, y_flip, i); + std::vector buf = floatx4_array_from_astc_img(img, y_flip, i); - res = stbi_write_hdr(fnmod.c_str(), img->dim_x, img->dim_y, 4, buf); - delete[] buf; + res = stbi_write_hdr(fnmod.c_str(), img->dim_x, img->dim_y, 4, buf.data()); if (res == 0) { break; @@ -631,10 +659,10 @@ static void copy_scanline( */ static void switch_endianness2( void* dataptr, - int byte_count + size_t byte_count ) { uint8_t* data = reinterpret_cast(dataptr); - for (int i = 0; i < byte_count / 2; i++) + for (size_t i = 0; i < byte_count / 2; i++) { uint8_t d0 = data[0]; uint8_t d1 = data[1]; @@ -652,10 +680,10 @@ static void switch_endianness2( */ static void switch_endianness4( void* dataptr, - int byte_count + size_t byte_count ) { uint8_t* data = reinterpret_cast(dataptr); - for (int i = 0; i < byte_count / 4; i++) + for (size_t i = 0; i < byte_count / 4; i++) { uint8_t d0 = data[0]; uint8_t d1 = data[1]; @@ -932,33 +960,30 @@ static void ktx_header_switch_endianness(ktx_header * kt) * * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error. */ -static astcenc_image* load_ktx_uncompressed_image( +static astcenc_image_ptr load_ktx_uncompressed_image( const char* filename, bool y_flip, bool& is_hdr, unsigned int& component_count ) { - FILE *f = fopen(filename, "rb"); - if (!f) + std::ifstream file(filename, std::ios::in | std::ios::binary); + if (!file) { print_error("ERROR: File open failed '%s'\n", filename); return nullptr; } ktx_header hdr; - size_t header_bytes_read = fread(&hdr, 1, sizeof(hdr), f); - - if (header_bytes_read != sizeof(hdr)) + file.read(reinterpret_cast(&hdr), sizeof(hdr)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return nullptr; } if (memcmp(hdr.magic, ktx_magic, 12) != 0 || (hdr.endianness != 0x04030201 && hdr.endianness != 0x01020304)) { print_error("ERROR: Image header corrupt '%s'\n", filename); - fclose(f); return nullptr; } @@ -972,13 +997,12 @@ static astcenc_image* load_ktx_uncompressed_image( if (hdr.gl_type == 0 || hdr.gl_format == 0) { print_error("ERROR: Image uses unsupported KTX format '%s'\n", filename); - fclose(f); return nullptr; } - // the formats we support are: - - // Cartesian product of gl_type=(UNSIGNED_BYTE, UNSIGNED_SHORT, HALF_FLOAT, FLOAT) x gl_format=(RED, RG, RGB, RGBA, BGR, BGRA) + // Supported formats are all pairings of: + // type=(UNSIGNED_BYTE, UNSIGNED_SHORT, HALF_FLOAT, FLOAT) + // gl_format=(RED, RG, RGB, RGBA, BGR, BGRA) unsigned int components; switch (hdr.gl_format) @@ -1009,7 +1033,6 @@ static astcenc_image* load_ktx_uncompressed_image( break; default: print_error("ERROR: Image uses unsupported KTX format '%s'\n", filename); - fclose(f); return nullptr; } @@ -1154,7 +1177,6 @@ static astcenc_image* load_ktx_uncompressed_image( } default: print_error("ERROR: Image uses unsupported KTX format '%s'\n", filename); - fclose(f); return nullptr; } @@ -1178,20 +1200,24 @@ static astcenc_image* load_ktx_uncompressed_image( unsigned int dim_z = astc::max(hdr.pixel_depth, 1u); // ignore the key/value data - fseek(f, hdr.bytes_of_key_value_data, SEEK_CUR); + file.seekg(hdr.bytes_of_key_value_data, std::ios::cur); + if (file.fail()) + { + print_error("ERROR: File read failed '%s'\n", filename); + return nullptr; + } - uint32_t specified_bytes_of_surface = 0; - size_t sb_read = fread(&specified_bytes_of_surface, 1, 4, f); - if (sb_read != 4) + uint32_t specified_bytes_per_image = 0; + file.read(reinterpret_cast(&specified_bytes_per_image), sizeof(specified_bytes_per_image)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return nullptr; } if (switch_endianness) { - specified_bytes_of_surface = reverse_bytes_u32(specified_bytes_of_surface); + specified_bytes_per_image = reverse_bytes_u32(specified_bytes_per_image); } // Compute surface size, checking for overflow caused by bad user-defined sizes @@ -1209,32 +1235,28 @@ static astcenc_image* load_ktx_uncompressed_image( size_t plane_texels = astc::mul_safe(dim_x, dim_y, overflow); astc::mul_safe(plane_texels, 4, overflow); - if (overflow || bytes_per_image != specified_bytes_of_surface) + if (overflow || bytes_per_image != specified_bytes_per_image) { print_error("ERROR: Image header corrupt '%s'\n", filename); - fclose(f); return nullptr; } - uint8_t* buf { nullptr }; + std::unique_ptr buf; try { - buf = new uint8_t[bytes_per_image]; + buf = std::make_unique(bytes_per_image); } catch (const std::bad_alloc &e) { ASTCENC_UNUSED(e); print_error("ERROR: Image memory allocation failed '%s'\n", filename); - fclose(f); return nullptr; } - size_t bytes_read = fread(buf, 1, specified_bytes_of_surface, f); - fclose(f); - if (bytes_read != specified_bytes_of_surface) + file.read(reinterpret_cast(buf.get()), bytes_per_image); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - delete[] buf; return nullptr; } @@ -1243,17 +1265,17 @@ static astcenc_image* load_ktx_uncompressed_image( { if (hdr.gl_type_size == 2) { - switch_endianness2(buf, specified_bytes_of_surface); + switch_endianness2(buf.get(), bytes_per_image); } if (hdr.gl_type_size == 4) { - switch_endianness4(buf, specified_bytes_of_surface); + switch_endianness4(buf.get(), bytes_per_image); } } // Transfer data from the surface to our own image data structure - astcenc_image* astc_img { nullptr }; + astcenc_image_ptr astc_img; try { astc_img = alloc_image(bitness, dim_x, dim_y, dim_z); @@ -1262,7 +1284,6 @@ static astcenc_image* load_ktx_uncompressed_image( { ASTCENC_UNUSED(e); print_error("ERROR: Image memory allocation failed '%s'\n", filename); - delete[] buf; return nullptr; } @@ -1286,12 +1307,11 @@ static astcenc_image* load_ktx_uncompressed_image( dst = static_cast(&data16[4 * dim_x * ydst]); } - uint8_t *src = buf + (z * bytes_per_plane) + (y * bytes_per_row); + uint8_t *src = buf.get() + (z * bytes_per_plane) + (y * bytes_per_row); copy_scanline(dst, src, dim_x, copy_method); } } - delete[] buf; is_hdr = bitness >= 16; component_count = components; return astc_img; @@ -1311,19 +1331,18 @@ bool load_ktx_compressed_image( bool& is_srgb, astc_compressed_image& img ) { - FILE *f = fopen(filename, "rb"); - if (!f) + std::ifstream file(filename, std::ios::in | std::ios::binary); + if (!file) { print_error("ERROR: File open failed '%s'\n", filename); return true; } ktx_header hdr; - size_t actual = fread(&hdr, 1, sizeof(hdr), f); - if (actual != sizeof(hdr)) + file.read(reinterpret_cast(&hdr), sizeof(hdr)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return true; } @@ -1331,7 +1350,6 @@ bool load_ktx_compressed_image( (hdr.endianness != 0x04030201 && hdr.endianness != 0x01020304)) { print_error("ERROR: Image header corrupt '%s'\n", filename); - fclose(f); return true; } @@ -1346,7 +1364,6 @@ bool load_ktx_compressed_image( hdr.gl_base_internal_format != GL_RGBA) { print_error("ERROR: Image uses unsupported KTX format '%s'\n", filename); - fclose(f); return true; } @@ -1354,26 +1371,23 @@ bool load_ktx_compressed_image( if (!fmt) { print_error("ERROR: Image uses unsupported KTX format '%s'\n", filename); - fclose(f); return true; } // Skip over any key-value pairs - int seekerr = fseek(f, hdr.bytes_of_key_value_data, SEEK_CUR); - if (seekerr) + file.seekg(hdr.bytes_of_key_value_data, std::ios::cur); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return true; } - // Read the length of the data and endianess convert - unsigned int data_len; - actual = fread(&data_len, 1, sizeof(data_len), f); - if (actual != sizeof(data_len)) + // Read the length of the data and convert endianness + uint32_t data_len; + file.read(reinterpret_cast(&data_len), sizeof(data_len)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return true; } @@ -1383,13 +1397,12 @@ bool load_ktx_compressed_image( } // Read the data - unsigned char* data = new unsigned char[data_len]; - actual = fread(data, 1, data_len, f); - if (actual != data_len) + img.data.resize(data_len); + file.read(reinterpret_cast(img.data.data()), data_len); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); - delete[] data; + img.data.clear(); return true; } @@ -1401,12 +1414,8 @@ bool load_ktx_compressed_image( img.dim_y = hdr.pixel_height; img.dim_z = hdr.pixel_depth == 0 ? 1 : hdr.pixel_depth; - img.data_len = data_len; - img.data = data; - is_srgb = fmt->is_srgb; - fclose(f); return false; } @@ -1446,31 +1455,21 @@ bool store_ktx_compressed_image( ktx_header_switch_endianness(&hdr); #endif - size_t expected = sizeof(ktx_header) + 4 + img.data_len; - size_t actual = 0; - - FILE *wf = fopen(filename, "wb"); - if (!wf) + std::ofstream file(filename, std::ios::out | std::ios::binary); + if (!file) { return true; } - uint32_t data_len = static_cast(img.data_len); + uint32_t data_len = static_cast(img.data.size()); #if defined(ASTCENC_BIG_ENDIAN) data_len = reverse_bytes_u32(data_len); #endif - actual += fwrite(&hdr, 1, sizeof(ktx_header), wf); - actual += fwrite(&data_len, 1, sizeof(uint32_t), wf); - actual += fwrite(img.data, 1, img.data_len, wf); - fclose(wf); - - if (actual != expected) - { - return true; - } - - return false; + file.write(reinterpret_cast(&hdr), sizeof(ktx_header)); + file.write(reinterpret_cast(&data_len), sizeof(uint32_t)); + file.write(reinterpret_cast(img.data.data()), img.data.size()); + return file.fail(); } /** @@ -1531,13 +1530,22 @@ static bool store_ktx_uncompressed_image( hdr.bytes_of_key_value_data = 0; // Collect image data to write - uint8_t ***row_pointers8 = nullptr; - uint16_t ***row_pointers16 = nullptr; + std::vector pixel_data8; + std::vector row_data8; + std::vector row_pointers8; + + std::vector pixel_data16; + std::vector row_data16; + std::vector row_pointers16; + if (bitness == 8) { - row_pointers8 = new uint8_t **[dim_z]; - row_pointers8[0] = new uint8_t *[dim_y * dim_z]; - row_pointers8[0][0] = new uint8_t[dim_x * dim_y * dim_z * image_components + 3]; + row_pointers8.resize(dim_z); + row_data8.resize(dim_y * dim_z); + pixel_data8.resize(dim_x * dim_y * dim_z * image_components + 3); + + row_pointers8[0] = row_data8.data(); + row_pointers8[0][0] = pixel_data8.data(); for (unsigned int z = 1; z < dim_z; z++) { @@ -1597,9 +1605,12 @@ static bool store_ktx_uncompressed_image( } else // if bitness == 16 { - row_pointers16 = new uint16_t **[dim_z]; - row_pointers16[0] = new uint16_t *[dim_y * dim_z]; - row_pointers16[0][0] = new uint16_t[dim_x * dim_y * dim_z * image_components + 1]; + row_pointers16.resize(dim_z); + row_data16.resize(dim_y * dim_z); + pixel_data16.resize(dim_x * dim_y * dim_z * image_components + 1); + + row_pointers16[0] = row_data16.data(); + row_pointers16[0][0] = pixel_data16.data(); for (unsigned int z = 1; z < dim_z; z++) { @@ -1662,19 +1673,17 @@ static bool store_ktx_uncompressed_image( uint32_t image_bytes = dim_x * dim_y * dim_z * image_components * (bitness / 8); uint32_t image_write_bytes = (image_bytes + 3) & ~3; - FILE *wf = fopen(filename, "wb"); - if (wf) + std::ofstream file(filename, std::ios::out | std::ios::binary); + if (file) { void* dataptr = (bitness == 16) ? reinterpret_cast(row_pointers16[0][0]) : reinterpret_cast(row_pointers8[0][0]); - size_t expected_bytes_written = sizeof(ktx_header) + image_write_bytes + 4; - size_t hdr_bytes_written = fwrite(&hdr, 1, sizeof(ktx_header), wf); - size_t bytecount_bytes_written = fwrite(&image_bytes, 1, 4, wf); - size_t data_bytes_written = fwrite(dataptr, 1, image_write_bytes, wf); - fclose(wf); - if (hdr_bytes_written + bytecount_bytes_written + data_bytes_written != expected_bytes_written) + file.write(reinterpret_cast(&hdr), sizeof(ktx_header)); + file.write(reinterpret_cast(&image_bytes), sizeof(image_bytes)); + file.write(reinterpret_cast(dataptr), image_write_bytes); + if (file.fail()) { retval = false; } @@ -1684,20 +1693,6 @@ static bool store_ktx_uncompressed_image( retval = false; } - if (row_pointers8) - { - delete[] row_pointers8[0][0]; - delete[] row_pointers8[0]; - delete[] row_pointers8; - } - - if (row_pointers16) - { - delete[] row_pointers16[0][0]; - delete[] row_pointers16[0]; - delete[] row_pointers16; - } - return retval; } @@ -1798,14 +1793,14 @@ struct dds_header_dx10 * * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error. */ -static astcenc_image* load_dds_uncompressed_image( +static astcenc_image_ptr load_dds_uncompressed_image( const char* filename, bool y_flip, bool& is_hdr, unsigned int& component_count ) { - FILE *f = fopen(filename, "rb"); - if (!f) + std::ifstream file(filename, std::ios::in | std::ios::binary); + if (!file) { print_error("ERROR: File open failed '%s'\n", filename); return nullptr; @@ -1813,11 +1808,10 @@ static astcenc_image* load_dds_uncompressed_image( // Read and check the DDS magic number uint32_t magic; - size_t magic_bytes_read = fread(&magic, 1, sizeof(uint32_t), f); - if (magic_bytes_read != 4) + file.read(reinterpret_cast(&magic), sizeof(uint32_t)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return nullptr; } @@ -1828,17 +1822,15 @@ static astcenc_image* load_dds_uncompressed_image( if (magic != DDS_MAGIC) { print_error("ERROR: Image header corrupt '%s'\n", filename); - fclose(f); return nullptr; } // Validate that we can read the DDS header dds_header hdr; - size_t header_bytes_read = fread(&hdr, 1, sizeof(hdr), f); - if (header_bytes_read != sizeof(hdr)) + file.read(reinterpret_cast(&hdr), sizeof(hdr)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return nullptr; } @@ -1857,7 +1849,6 @@ static astcenc_image* load_dds_uncompressed_image( if (hdr.size != 124) { print_error("ERROR: Image header corrupt '%s'\n", filename); - fclose(f); return nullptr; } @@ -1871,7 +1862,6 @@ static astcenc_image* load_dds_uncompressed_image( else { print_error("ERROR: Image uses unsupported DDS format '%s'\n", filename); - fclose(f); return nullptr; } } @@ -1879,11 +1869,10 @@ static astcenc_image* load_dds_uncompressed_image( dds_header_dx10 dx10_header; if (use_dx10_header) { - size_t dx10_header_bytes_read = fread(&dx10_header, 1, sizeof(dx10_header), f); - if (dx10_header_bytes_read != sizeof(dx10_header)) + file.read(reinterpret_cast(&dx10_header), sizeof(dx10_header)); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - fclose(f); return nullptr; } } @@ -1965,7 +1954,6 @@ static astcenc_image* load_dds_uncompressed_image( if (!did_select_format) { print_error("ERROR: Image uses unsupported DDS format '%s'\n", filename); - fclose(f); return nullptr; } } @@ -2053,7 +2041,6 @@ static astcenc_image* load_dds_uncompressed_image( else { print_error("ERROR: Image uses unsupported DDS format '%s'\n", filename); - fclose(f); return nullptr; } @@ -2078,34 +2065,30 @@ static astcenc_image* load_dds_uncompressed_image( if (overflow) { print_error("ERROR: Image header corrupt '%s'\n", filename); - fclose(f); return nullptr; } - uint8_t* buf { nullptr }; + std::unique_ptr buf; try { - buf = new uint8_t[bytes_per_image]; + buf = std::make_unique(bytes_per_image); } catch (const std::bad_alloc &e) { ASTCENC_UNUSED(e); print_error("ERROR: Image memory allocation failed '%s'\n", filename); - fclose(f); return nullptr; } - size_t bytes_read = fread(buf, 1, bytes_per_image, f); - fclose(f); - if (bytes_read != bytes_per_image) + file.read(reinterpret_cast(buf.get()), bytes_per_image); + if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - delete[] buf; return nullptr; } // Transfer data from the surface to our own image data structure - astcenc_image *astc_img { nullptr }; + astcenc_image_ptr astc_img; try { astc_img = alloc_image(bitness, dim_x, dim_y, dim_z); @@ -2114,7 +2097,6 @@ static astcenc_image* load_dds_uncompressed_image( { ASTCENC_UNUSED(e); print_error("ERROR: Image memory allocation failed '%s'\n", filename); - delete[] buf; return nullptr; } @@ -2138,12 +2120,11 @@ static astcenc_image* load_dds_uncompressed_image( dst = static_cast(&data16[4 * dim_x * ydst]); } - uint8_t *src = buf + (z * bytes_per_plane) + (y * bytes_per_row); + uint8_t *src = buf.get() + (z * bytes_per_plane) + (y * bytes_per_row); copy_scanline(dst, src, dim_x, copy_method); } } - delete[] buf; is_hdr = bitness >= 16; component_count = components; return astc_img; @@ -2228,14 +2209,22 @@ static bool store_dds_uncompressed_image( dx10.reserved = 0; // Collect image data to write - uint8_t ***row_pointers8 = nullptr; - uint16_t ***row_pointers16 = nullptr; + std::vector pixel_data8; + std::vector row_data8; + std::vector row_pointers8; + + std::vector pixel_data16; + std::vector row_data16; + std::vector row_pointers16; if (bitness == 8) { - row_pointers8 = new uint8_t **[dim_z]; - row_pointers8[0] = new uint8_t *[dim_y * dim_z]; - row_pointers8[0][0] = new uint8_t[dim_x * dim_y * dim_z * image_components]; + row_pointers8.resize(dim_z); + row_data8.resize(dim_y * dim_z); + pixel_data8.resize(dim_x * dim_y * dim_z * image_components); + + row_pointers8[0] = row_data8.data(); + row_pointers8[0][0] = pixel_data8.data(); for (unsigned int z = 1; z < dim_z; z++) { @@ -2296,9 +2285,12 @@ static bool store_dds_uncompressed_image( } else // if bitness == 16 { - row_pointers16 = new uint16_t **[dim_z]; - row_pointers16[0] = new uint16_t *[dim_y * dim_z]; - row_pointers16[0][0] = new uint16_t[dim_x * dim_y * dim_z * image_components]; + row_pointers16.resize(dim_z); + row_data16.resize(dim_y * dim_z); + pixel_data16.resize(dim_x * dim_y * dim_z * image_components); + + row_pointers16[0] = row_data16.data(); + row_pointers16[0][0] = pixel_data16.data(); for (unsigned int z = 1; z < dim_z; z++) { @@ -2363,32 +2355,22 @@ static bool store_dds_uncompressed_image( uint32_t dds_magic = DDS_MAGIC; - FILE *wf = fopen(filename, "wb"); - if (wf) + std::ofstream file(filename, std::ios::out | std::ios::binary); + if (file) { void *dataptr = (bitness == 16) ? reinterpret_cast(row_pointers16[0][0]) : reinterpret_cast(row_pointers8[0][0]); - size_t expected_bytes_written = 4 + sizeof(dds_header) + (bitness > 8 ? sizeof(dds_header_dx10) : 0) + image_bytes; - - size_t magic_bytes_written = fwrite(&dds_magic, 1, 4, wf); - size_t hdr_bytes_written = fwrite(&hdr, 1, sizeof(dds_header), wf); - - size_t dx10_bytes_written; + file.write(reinterpret_cast(&dds_magic), sizeof(dds_magic)); + file.write(reinterpret_cast(&hdr), sizeof(dds_header)); if (bitness > 8) { - dx10_bytes_written = fwrite(&dx10, 1, sizeof(dx10), wf); + file.write(reinterpret_cast(&dx10), sizeof(dx10)); } - else - { - dx10_bytes_written = 0; - } - - size_t data_bytes_written = fwrite(dataptr, 1, image_bytes, wf); - fclose(wf); - if (magic_bytes_written + hdr_bytes_written + dx10_bytes_written + data_bytes_written != expected_bytes_written) + file.write(reinterpret_cast(dataptr), image_bytes); + if (file.fail()) { retval = false; } @@ -2398,27 +2380,13 @@ static bool store_dds_uncompressed_image( retval = false; } - if (row_pointers8) - { - delete[] row_pointers8[0][0]; - delete[] row_pointers8[0]; - delete[] row_pointers8; - } - - if (row_pointers16) - { - delete[] row_pointers16[0][0]; - delete[] row_pointers16[0]; - delete[] row_pointers16; - } - return retval; } /** * @brief Image loader function pointer. */ -using image_loader = astcenc_image*(*)(const char*, bool, bool&, unsigned int&); +using image_loader = astcenc_image_ptr(*)(const char*, bool, bool&, unsigned int&); /** * @brief Specs for an image loader function. @@ -2505,7 +2473,7 @@ int get_output_filename_enforced_bitness( } /* See header for documentation. */ -astcenc_image* load_ncimage( +astcenc_image_ptr load_ncimage( const char* filename, bool y_flip, bool& is_hdr, @@ -2669,10 +2637,9 @@ int load_cimage( } // Allocation may fail if image is suspiciously large - uint8_t* buffer { nullptr }; try { - buffer = new uint8_t[data_size]; + img.data.resize(data_size); } catch (const std::bad_alloc &e) { @@ -2681,17 +2648,14 @@ int load_cimage( return 1; } - file.read(reinterpret_cast(buffer), data_size); + file.read(reinterpret_cast(img.data.data()), data_size); if (file.fail()) { print_error("ERROR: File read failed '%s'\n", filename); - delete[] buffer; + img.data.clear(); return 1; } - img.data = buffer; - img.data_len = data_size; - // Casts are safe - we know individual values are small enough img.block_x = static_cast(block_x); img.block_y = static_cast(block_y); @@ -2739,6 +2703,6 @@ int store_cimage( } file.write(reinterpret_cast(&hdr), sizeof(astc_header)); - file.write(reinterpret_cast(img.data), img.data_len); + file.write(reinterpret_cast(img.data.data()), img.data.size()); return 0; } diff --git a/Source/astcenccli_internal.h b/Source/astcenccli_internal.h index 08eaa4c9..29aac686 100644 --- a/Source/astcenccli_internal.h +++ b/Source/astcenccli_internal.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "astcenc.h" #include "astcenc_mathlib.h" @@ -41,28 +43,25 @@ struct astc_compressed_image { /** @brief The block width in texels. */ - unsigned int block_x; + unsigned int block_x { 0 }; /** @brief The block height in texels. */ - unsigned int block_y; + unsigned int block_y { 0 }; /** @brief The block depth in texels. */ - unsigned int block_z; + unsigned int block_z { 0 }; /** @brief The image width in texels. */ - unsigned int dim_x; + unsigned int dim_x { 0 }; /** @brief The image height in texels. */ - unsigned int dim_y; + unsigned int dim_y { 0 }; /** @brief The image depth in texels. */ - unsigned int dim_z; + unsigned int dim_z { 0 }; /** @brief The binary data payload. */ - uint8_t* data; - - /** @brief The binary data length in bytes. */ - size_t data_len; + std::vector data; }; /** @@ -121,6 +120,19 @@ static inline void print_error( fprintf(stderr, format, args...); } +/** + * @brief A custom deleter so we can use RAII to manage astcenc_image structs. + */ +struct astcenc_image_deleter +{ + void operator()(astcenc_image* img) const; +}; + +/** + * @brief A smart pointer wrapper around an astcenc_image. + */ +using astcenc_image_ptr = std::unique_ptr; + /** * @brief Load uncompressed image. * @@ -131,7 +143,7 @@ static inline void print_error( * * @return The astc image file, or nullptr on error. */ -astcenc_image* load_ncimage( +astcenc_image_ptr load_ncimage( const char* filename, bool y_flip, bool& is_hdr, @@ -147,7 +159,7 @@ astcenc_image* load_ncimage( * * @return The astc image file, or nullptr on error. */ -astcenc_image* load_png_with_wuffs( +astcenc_image_ptr load_png_with_wuffs( const char* filename, bool y_flip, bool& is_hdr, @@ -170,7 +182,7 @@ bool store_ncimage( /** * @brief Check if the output file type requires a specific bitness. * - * @param filename The file name, containing hte extension to check. + * @param filename The file name, containing the extension to check. * * @return Valid values are: * * -1 - error - unknown file type. @@ -184,29 +196,21 @@ int get_output_filename_enforced_bitness( /** * @brief Allocate a new image in a canonical format. * - * Allocated images must be freed with a @c free_image() call. + * Allocated images are returned in a RAII-managed smart pointer. * * @param bitness The number of bits per component (8, 16, or 32). * @param dim_x The width of the image, in texels. * @param dim_y The height of the image, in texels. * @param dim_z The depth of the image, in texels. * - * @return The allocated image, or @c nullptr on error. + * @return The allocated image. */ -astcenc_image* alloc_image( +astcenc_image_ptr alloc_image( unsigned int bitness, unsigned int dim_x, unsigned int dim_y, unsigned int dim_z); -/** - * @brief Free an image. - * - * @param img The image to free. - */ -void free_image( - astcenc_image* img); - /** * @brief Determine the number of active components in an image. * @@ -279,7 +283,7 @@ bool store_ktx_compressed_image( * * @return The populated image. */ -astcenc_image* astc_img_from_floatx4_array( +astcenc_image_ptr astc_img_from_floatx4_array( const float* data, unsigned int dim_x, unsigned int dim_y, @@ -295,7 +299,7 @@ astcenc_image* astc_img_from_floatx4_array( * * @return The populated image. */ -astcenc_image* astc_img_from_unorm8x4_array( +astcenc_image_ptr astc_img_from_unorm8x4_array( const uint8_t* data, unsigned int dim_x, unsigned int dim_y, @@ -304,33 +308,17 @@ astcenc_image* astc_img_from_unorm8x4_array( /** * @brief Create a flattened RGBA FLOAT32 data array for a single slice from an image structure. * - * The returned data array is allocated with @c new[] and must be freed with a @c delete[] call. - * * @param img The input image. * @param y_flip Should the data in the array be Y flipped? * @param z_index The slice index to convert. * * @return The data array. */ -float* floatx4_array_from_astc_img( +std::vector floatx4_array_from_astc_img( const astcenc_image* img, bool y_flip, unsigned int z_index); -/** - * @brief Create a flattened RGBA UNORM8 data array from an image structure. - * - * The returned data array is allocated with @c new[] and must be freed with a @c delete[] call. - * - * @param img The input image. - * @param y_flip Should the data in the array be Y flipped? - * - * @return The data array. - */ -uint8_t* unorm8x4_array_from_astc_img( - const astcenc_image* img, - bool y_flip); - /* ============================================================================ Functions for printing build info and help messages ============================================================================ */ diff --git a/Source/astcenccli_platform_dependents.cpp b/Source/astcenccli_platform_dependents.cpp index 37867b5f..55c62037 100644 --- a/Source/astcenccli_platform_dependents.cpp +++ b/Source/astcenccli_platform_dependents.cpp @@ -32,6 +32,8 @@ #include "astcenccli_internal.h" +#include + /* ============================================================================ Platform code for Windows using the Win32 APIs. ============================================================================ */ @@ -169,7 +171,7 @@ void set_thread_name( } /* ============================================================================ - Platform code for an platform using POSIX APIs. + Platform code for a platform using POSIX APIs. ============================================================================ */ #else @@ -255,7 +257,7 @@ void launch_threads( } // Otherwise spawn worker threads - launch_desc *thread_descs = new launch_desc[thread_count]; + std::vector thread_descs(thread_count); int actual_thread_count { 0 }; for (int i = 0; i < thread_count; i++) @@ -270,7 +272,7 @@ void launch_threads( &(thread_descs[actual_thread_count].thread_handle), nullptr, launch_threads_helper, - reinterpret_cast(thread_descs + actual_thread_count)); + reinterpret_cast(&thread_descs[actual_thread_count])); // Track how many threads we actually created if (!error) @@ -308,6 +310,4 @@ void launch_threads( { func(1, 0, payload); } - - delete[] thread_descs; } diff --git a/Source/astcenccli_toplevel.cpp b/Source/astcenccli_toplevel.cpp index 0695b546..c3214f27 100644 --- a/Source/astcenccli_toplevel.cpp +++ b/Source/astcenccli_toplevel.cpp @@ -35,6 +35,7 @@ #include #include #include +#include /* ============================================================================ Data structure definitions @@ -104,7 +105,7 @@ static const astcenc_operation ASTCENC_OP_TEST = ASTCENC_STAGE_ST_NCOMP; /** - * @brief Image preprocesing tasks prior to encoding. + * @brief Image preprocessing tasks prior to encoding. */ enum astcenc_preprocess { @@ -162,6 +163,22 @@ struct decompression_workload astcenc_error error; }; +/** + * @brief A custom deleter so we can use RAII to manage codec contexts. + */ +struct astcenc_context_deleter +{ + void operator()(astcenc_context* context) const + { + astcenc_context_free(context); + } +}; + +/** + * @brief A smart pointer wrapper around an astcenc_context. + */ +using astcenc_context_ptr = std::unique_ptr; + /** * @brief Callback emitting a progress bar */ @@ -283,11 +300,11 @@ static void decompression_workload_runner( /** * @brief Utility to generate a slice file name from a pattern. * - * Convert "foo/bar.png" in to "foo/bar_.png" + * Convert "foo/bar.png" into "foo/bar_.png" * - * @param basename The base pattern; must contain a file extension. - * @param index The slice index. - * @param error Set to true on success, false on error (no extension found). + * @param basename The base pattern; must contain a file extension. + * @param index The slice index. + * @param error Set to false on success, true on error (no extension found). * * @return The slice file name. */ @@ -313,34 +330,33 @@ static std::string get_slice_filename( /** * @brief Load a non-astc image file from memory. * - * @param filename The file to load, or a pattern for array loads. - * @param dim_z The number of slices to load. - * @param y_flip Should this image be Y flipped? - * @param[out] is_hdr Is the loaded image HDR? - * @param[out] component_count The number of components in the loaded image. + * @param filename The file to load, or a pattern for array loads. + * @param dim_z The number of slices to load. + * @param y_flip Should this image be Y flipped? + * @param[out] is_hdr Is the loaded image HDR? + * @param[out] component_count The number of components in the loaded image. * * @return The astc image file, or nullptr on error. */ -static astcenc_image* load_uncomp_file( +static astcenc_image_ptr load_uncomp_file( const char* filename, unsigned int dim_z, bool y_flip, bool& is_hdr, unsigned int& component_count ) { - astcenc_image *image = nullptr; + astcenc_image_ptr image; // For a 2D image just load the image directly if (dim_z == 1) { - image = load_ncimage(filename, y_flip, is_hdr, component_count); + return load_ncimage(filename, y_flip, is_hdr, component_count); } else { bool slice_is_hdr; unsigned int slice_component_count; - astcenc_image* slice = nullptr; - std::vector slices; + std::vector slices; // For a 3D image load an array of slices for (unsigned int image_index = 0; image_index < dim_z; image_index++) @@ -353,15 +369,13 @@ static astcenc_image* load_uncomp_file( break; } - slice = load_ncimage(slice_name.c_str(), y_flip, - slice_is_hdr, slice_component_count); + auto slice = load_ncimage(slice_name.c_str(), y_flip, + slice_is_hdr, slice_component_count); if (!slice) { break; } - slices.push_back(slice); - // Check it is not a 3D image if (slice->dim_z != 1) { @@ -391,6 +405,8 @@ static astcenc_image* load_uncomp_file( is_hdr = slice_is_hdr; component_count = slice_component_count; } + + slices.push_back(std::move(slice)); } // If all slices loaded correctly then repack them into a single image @@ -430,11 +446,6 @@ static astcenc_image* load_uncomp_file( } } } - - for (auto &i : slices) - { - free_image(i); - } } return image; @@ -1539,7 +1550,7 @@ static void print_diagnostic_image( size_t block_cols = (image.dim_x + image.block_x - 1) / image.block_x; size_t block_rows = (image.dim_y + image.block_y - 1) / image.block_y; - uint8_t* data = image.data; + const uint8_t* data = image.data.data(); for (size_t block_y = 0; block_y < block_rows; block_y++) { for (size_t block_x = 0; block_x < block_cols; block_x++) @@ -1616,7 +1627,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, partition_func); std::string fname = stem + "_diag_partitioning.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Weight planes ---- ---- ---- ---- auto texel_func1 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1642,7 +1653,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func1); fname = stem + "_diag_weight_plane2.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Weight density ---- ---- ---- ---- auto texel_func2 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1663,7 +1674,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func2); fname = stem + "_diag_weight_density.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Weight quant ---- ---- ---- ---- auto texel_func3 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1681,7 +1692,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func3); fname = stem + "_diag_weight_quant.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Color quant ---- ---- ---- ---- auto texel_func4 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1699,7 +1710,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func4); fname = stem + "_diag_color_quant.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Color endpoint mode: Index ---- ---- ---- ---- auto texel_func5 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1720,7 +1731,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func5); fname = stem + "_diag_cem_index.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Color endpoint mode: Components ---- ---- ---- ---- auto texel_func6 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1773,7 +1784,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func6); fname = stem + "_diag_cem_components.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Color endpoint mode: Style ---- ---- ---- ---- auto texel_func7 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1834,7 +1845,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func7); fname = stem + "_diag_cem_style.png"; - store_ncimage(diag_image, fname.c_str(), false); + store_ncimage(diag_image.get(), fname.c_str(), false); // ---- ---- ---- ---- Color endpoint mode: Style ---- ---- ---- ---- auto texel_func8 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) { @@ -1876,9 +1887,7 @@ static void print_diagnostic_images( print_diagnostic_image(context, image, *diag_image, texel_func8); fname = stem + "_diag_cem_hdr.png"; - store_ncimage(diag_image, fname.c_str(), false); - - free_image(diag_image); + store_ncimage(diag_image.get(), fname.c_str(), false); } /** @@ -1938,7 +1947,6 @@ int astcenc_main( return 1; } - // TODO: Handle RAII resources so they get freed when out of scope // Load the compressed input file if needed // This has to come first, as the block size is in the file header @@ -2010,10 +2018,10 @@ int astcenc_main( config.progress_callback = progress_emitter; } - astcenc_image* image_uncomp_in = nullptr ; + astcenc_image_ptr image_uncomp_in; unsigned int image_uncomp_in_component_count = 0; bool image_uncomp_in_is_hdr = false; - astcenc_image* image_decomp_out = nullptr; + astcenc_image_ptr image_decomp_out; // Determine decompression output bitness, if limited by file type int out_bitness = 0; @@ -2034,9 +2042,8 @@ int astcenc_main( } } - // TODO: Handle RAII resources so they get freed when out of scope - astcenc_error codec_status; - astcenc_context* codec_context; + astcenc_error codec_status; + astcenc_context_ptr codec_context; // Preflight - check we have valid extensions for storing a file if (operation & ASTCENC_STAGE_ST_NCOMP) @@ -2068,12 +2075,14 @@ int astcenc_main( } } - codec_status = astcenc_context_alloc(&config, cli_config.thread_count, &codec_context, nullptr); + astcenc_context* codec_context_raw { nullptr }; + codec_status = astcenc_context_alloc(&config, cli_config.thread_count, &codec_context_raw, nullptr); if (codec_status != ASTCENC_SUCCESS) { print_error("ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(codec_status)); return 1; } + codec_context.reset(codec_context_raw); // Load the uncompressed input file if needed if (operation & ASTCENC_STAGE_LD_NCOMP) @@ -2092,10 +2101,10 @@ int astcenc_main( { // Allocate a float image so we can avoid additional quantization, // as e.g. premultiplication can result in fractional color values - astcenc_image* image_pp = alloc_image(32, - image_uncomp_in->dim_x, - image_uncomp_in->dim_y, - image_uncomp_in->dim_z); + auto image_pp = alloc_image(32, + image_uncomp_in->dim_x, + image_uncomp_in->dim_y, + image_uncomp_in->dim_z); if (!image_pp) { print_error("ERROR: Failed to allocate preprocessed image\n"); @@ -2113,9 +2122,7 @@ int astcenc_main( config.profile); } - // Delete the original as we no longer need it - free_image(image_uncomp_in); - image_uncomp_in = image_pp; + image_uncomp_in = std::move(image_pp); } if (!cli_config.silentmode) @@ -2163,13 +2170,13 @@ int astcenc_main( unsigned int blocks_y = (image_uncomp_in->dim_y + config.block_y - 1) / config.block_y; unsigned int blocks_z = (image_uncomp_in->dim_z + config.block_z - 1) / config.block_z; size_t buffer_size = blocks_x * blocks_y * blocks_z * 16; - uint8_t* buffer = new uint8_t[buffer_size]; + image_comp.data.resize(buffer_size); compression_workload work; - work.context = codec_context; - work.image = image_uncomp_in; + work.context = codec_context.get(); + work.image = image_uncomp_in.get(); work.swizzle = cli_config.swz_encode; - work.data_out = buffer; + work.data_out = image_comp.data.data(); work.data_len = buffer_size; work.error = ASTCENC_SUCCESS; @@ -2197,7 +2204,7 @@ int astcenc_main( work.data_out, work.data_len, 0); } - astcenc_compress_reset(codec_context); + astcenc_compress_reset(codec_context.get()); if (config.progress_callback) { @@ -2221,8 +2228,6 @@ int astcenc_main( image_comp.dim_x = image_uncomp_in->dim_x; image_comp.dim_y = image_uncomp_in->dim_y; image_comp.dim_z = image_uncomp_in->dim_z; - image_comp.data = buffer; - image_comp.data_len = buffer_size; } // Decompress an image @@ -2234,10 +2239,10 @@ int astcenc_main( out_bitness, image_comp.dim_x, image_comp.dim_y, image_comp.dim_z); decompression_workload work; - work.context = codec_context; - work.data = image_comp.data; - work.data_len = image_comp.data_len; - work.image_out = image_decomp_out; + work.context = codec_context.get(); + work.data = image_comp.data.data(); + work.data_len = image_comp.data.size(); + work.image_out = image_decomp_out.get(); work.swizzle = cli_config.swz_decode; work.error = ASTCENC_SUCCESS; @@ -2258,7 +2263,7 @@ int astcenc_main( work.image_out, &work.swizzle, 0); } - astcenc_decompress_reset(codec_context); + astcenc_decompress_reset(codec_context.get()); double iter_time = get_time() - start_iter_time; best_decompression_time = astc::min(iter_time, best_decompression_time); @@ -2286,7 +2291,8 @@ int astcenc_main( compute_error_metrics( image_uncomp_in_is_hdr, is_normal_map, image_uncomp_in_component_count, - image_uncomp_in, image_decomp_out, cli_config.low_fstop, cli_config.high_fstop); + image_uncomp_in.get(), image_decomp_out.get(), + cli_config.low_fstop, cli_config.high_fstop); } // Store compressed image @@ -2326,7 +2332,7 @@ int astcenc_main( { if (!is_null) { - bool store_result = store_ncimage(image_decomp_out, output_filename.c_str(), + bool store_result = store_ncimage(image_decomp_out.get(), output_filename.c_str(), cli_config.y_flip); if (!store_result) { @@ -2339,15 +2345,9 @@ int astcenc_main( // Store diagnostic images if (cli_config.diagnostic_images && !is_null) { - print_diagnostic_images(codec_context, image_comp, output_filename); + print_diagnostic_images(codec_context.get(), image_comp, output_filename); } - free_image(image_uncomp_in); - free_image(image_decomp_out); - astcenc_context_free(codec_context); - - delete[] image_comp.data; - if ((operation & ASTCENC_STAGE_COMPARE) || (!cli_config.silentmode)) { double end_time = get_time(); diff --git a/Source/astcenccli_toplevel_help.cpp b/Source/astcenccli_toplevel_help.cpp index f475b39c..1c201ed6 100644 --- a/Source/astcenccli_toplevel_help.cpp +++ b/Source/astcenccli_toplevel_help.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // ---------------------------------------------------------------------------- -// Copyright 2011-2024 Arm Limited +// Copyright 2011-2026 Arm Limited // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy @@ -370,7 +370,7 @@ ADVANCED COMPRESSION -fast : 1.00 | 1.00 -medium : 1.10 | 1.05 -thorough : 1.35 | 1.15 - -verythrorough : 1.60 | 1.40 + -verythorough : 1.60 | 1.40 -exhaustive : 2.00 | 2.00 -2planelimitcorrelation