From a9c6909a29070b3f26d58a281b8d815e9a4612cb Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Wed, 8 Sep 2021 13:20:54 -0700 Subject: [PATCH] Renamed PngImage to PngFile and put it in a Utils namespace to match the other file utilities. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Source/FrameCaptureSystemComponent.cpp | 4 +- .../Utils/Code/Include/Atom/Utils/PngFile.h | 143 +++--- Gems/Atom/Utils/Code/Source/PngFile.cpp | 469 +++++++++--------- 3 files changed, 311 insertions(+), 305 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 6201ff5e56..62113d3827 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -87,9 +87,9 @@ namespace AZ jobCompletion.StartAndWaitForCompletion(); } - PngImage image = PngImage::Create(readbackResult.m_imageDescriptor.m_size, readbackResult.m_imageDescriptor.m_format, *buffer); + Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, readbackResult.m_imageDescriptor.m_format, *buffer); - PngImage::SaveSettings saveSettings; + Utils::PngFile::SaveSettings saveSettings; saveSettings.m_compressionLevel = r_pngCompressionLevel; // We should probably strip alpha to save space, especially for automated test screenshots. Alpha is left in to maintain // prior behavior, changing this is out of scope for the current task. Note, it would have bit of a cascade effect where diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h index 1f168df02a..5a12001122 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/PngFile.h @@ -18,77 +18,80 @@ namespace AZ { - //! This is a light wrapper class for libpng, to load and save .png files. - //! Functionality is limited, feel free to add more features as needed. - class PngImage + namespace Utils { - public: - using ErrorHandler = AZStd::function; - - struct LoadSettings + //! This is a light wrapper class for libpng, to load and save .png files. + //! Functionality is limited, feel free to add more features as needed. + class PngFile { - ErrorHandler m_errorHandler = {}; //!< optional callback function describing any errors that are encountered - bool m_stripAlpha = false; //!< the alpha channel will be skipped, loading an RGBA image as RGB + public: + using ErrorHandler = AZStd::function; + + struct LoadSettings + { + ErrorHandler m_errorHandler = {}; //!< optional callback function describing any errors that are encountered + bool m_stripAlpha = false; //!< the alpha channel will be skipped, loading an RGBA image as RGB + }; + + struct SaveSettings + { + ErrorHandler m_errorHandler = {}; //!< optional callback function describing any errors that are encountered + bool m_stripAlpha = false; //!< the alpha channel will be skipped, saving an RGBA buffer as RGB + int m_compressionLevel = 6; //!< this is the zlib compression level. See png_set_compression_level in png.h + }; + + // To keep things simple for now we limit all images to RGB and RGBA, 8 bits per channel. + enum class Format + { + Unknown, + RGB, + RGBA + }; + + //! @return the loaded PngFile or an invalid PngFile if there was an error. + static PngFile Load(const char* path, LoadSettings loadSettings = {}); + + //! Create a PngFile from an RHI data buffer. + //! @param size the dimensions of the image (m_depth is not used, assumed to be 1) + //! @param format indicates the pixel format represented by @data. Only a limited set of formats are supported, see implementation. + //! @param data the buffer of image data. The size of the buffer must match the @size and @format parameters. + //! @return the created PngFile or an invalid PngFile if there was an error. + static PngFile Create(const RHI::Size& size, RHI::Format format, AZStd::array_view data); + static PngFile Create(const RHI::Size& size, RHI::Format format, AZStd::vector&& data); + + PngFile() = default; + AZ_DEFAULT_MOVE(PngFile) + + //! @return true if the save operation was successful + bool Save(const char* path, SaveSettings saveSettings = {}); + + bool IsValid() const; + operator bool() const { return IsValid(); } + + uint32_t GetWidth() const { return m_width; } + uint32_t GetHeight() const { return m_height; } + + Format GetBufferFormat() const { return m_bufferFormat; } + const AZStd::vector& GetBuffer() const { return m_buffer; } + + //! Returns a r-value reference that can be moved. This will invalidate the PngFile. + AZStd::vector&& TakeBuffer(); + + private: + AZ_DEFAULT_COPY(PngFile) + + static const int HeaderSize = 8; + + static void DefaultErrorHandler(const char* message); + + // See png_get_IHDR in http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf... + uint32_t m_width = 0; + uint32_t m_height = 0; + int32_t m_bitDepth = 0; + int32_t m_colorType = 0; + + Format m_bufferFormat = Format::Unknown; + AZStd::vector m_buffer; }; - - struct SaveSettings - { - ErrorHandler m_errorHandler = {}; //!< optional callback function describing any errors that are encountered - bool m_stripAlpha = false; //!< the alpha channel will be skipped, saving an RGBA buffer as RGB - int m_compressionLevel = 6; //!< this is the zlib compression level. See png_set_compression_level in png.h - }; - - // To keep things simple for now we limit all images to RGB and RGBA, 8 bits per channel. - enum class Format - { - Unknown, - RGB, - RGBA - }; - - //! @return the loaded PngImage or an invalid PngImage if there was an error. - static PngImage Load(const char* path, LoadSettings loadSettings = {}); - - //! Create a PngImage from an RHI data buffer. - //! @param size the dimensions of the image (m_depth is not used, assumed to be 1) - //! @param format indicates the pixel format represented by @data. Only a limited set of formats are supported, see implementation. - //! @param data the buffer of image data. The size of the buffer must match the @size and @format parameters. - //! @return the created PngImage or an invalid PngImage if there was an error. - static PngImage Create(const RHI::Size& size, RHI::Format format, AZStd::array_view data); - static PngImage Create(const RHI::Size& size, RHI::Format format, AZStd::vector&& data); - - PngImage() = default; - AZ_DEFAULT_MOVE(PngImage) - - //! @return true if the save operation was successful - bool Save(const char* path, SaveSettings saveSettings = {}); - - bool IsValid() const; - operator bool() const { return IsValid(); } - - uint32_t GetWidth() const { return m_width; } - uint32_t GetHeight() const { return m_height; } - - Format GetBufferFormat() const { return m_bufferFormat; } - const AZStd::vector& GetBuffer() const { return m_buffer; } - - //! Returns a r-value reference that can be moved. This will invalidate the PngImage. - AZStd::vector&& TakeBuffer(); - - private: - AZ_DEFAULT_COPY(PngImage) - - static const int HeaderSize = 8; - - static void DefaultErrorHandler(const char* message); - - // See png_get_IHDR in http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf... - uint32_t m_width = 0; - uint32_t m_height = 0; - int32_t m_bitDepth = 0; - int32_t m_colorType = 0; - - Format m_bufferFormat = Format::Unknown; - AZStd::vector m_buffer; - }; + } } // namespace AZ diff --git a/Gems/Atom/Utils/Code/Source/PngFile.cpp b/Gems/Atom/Utils/Code/Source/PngFile.cpp index 273ea88358..5a3fdf7fdb 100644 --- a/Gems/Atom/Utils/Code/Source/PngFile.cpp +++ b/Gems/Atom/Utils/Code/Source/PngFile.cpp @@ -11,285 +11,288 @@ namespace AZ { - namespace + namespace Utils { - void PngImage_user_error_fn(png_structp png_ptr, png_const_charp error_msg) + namespace { - PngImage::ErrorHandler* errorHandler = reinterpret_cast(png_get_error_ptr(png_ptr)); - (*errorHandler)(error_msg); - } - - void PngImage_user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg) - { - AZ_Warning("PngImage", false, "%s", warning_msg); - } - } - - PngImage PngImage::Create(const RHI::Size& size, RHI::Format format, AZStd::array_view data) - { - return Create(size, format, AZStd::vector{data.begin(), data.end()}); - } - - PngImage PngImage::Create(const RHI::Size& size, RHI::Format format, AZStd::vector&& data) - { - PngImage image; - - if (RHI::Format::R8G8B8A8_UNORM == format) - { - if (size.m_width * size.m_height * 4 == data.size()) + void PngImage_user_error_fn(png_structp png_ptr, png_const_charp error_msg) { - image.m_width = size.m_width; - image.m_height = size.m_height; - image.m_bitDepth = 8; - image.m_colorType = PNG_COLOR_TYPE_RGB_ALPHA; - image.m_bufferFormat = PngImage::Format::RGBA; - image.m_buffer = data; + PngFile::ErrorHandler* errorHandler = reinterpret_cast(png_get_error_ptr(png_ptr)); + (*errorHandler)(error_msg); } - else + + void PngImage_user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg) { - AZ_Assert(false, "Invalid arguments. Buffer size does not match the image dimensions."); + AZ_Warning("PngFile", false, "%s", warning_msg); } } - return image; - } - - PngImage PngImage::Load(const char* path, LoadSettings loadSettings) - { - if (!loadSettings.m_errorHandler) + PngFile PngFile::Create(const RHI::Size& size, RHI::Format format, AZStd::array_view data) { - loadSettings.m_errorHandler = [path](const char* message) { DefaultErrorHandler(AZStd::string::format("Could not load file '%s'. %s", path, message).c_str()); }; - } - - // For documentation of this code, see http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf chapter 3 - - FILE* fp = NULL; - if (fopen_s(&fp, path, "rb") || !fp) - { - loadSettings.m_errorHandler("Failed to open file."); - return {}; + return Create(size, format, AZStd::vector{data.begin(), data.end()}); } - png_byte header[HeaderSize] = {}; - - if (fread(header, 1, HeaderSize, fp) != HeaderSize) + PngFile PngFile::Create(const RHI::Size& size, RHI::Format format, AZStd::vector&& data) { - fclose(fp); - loadSettings.m_errorHandler("Invalid header."); - return {}; + PngFile image; + + if (RHI::Format::R8G8B8A8_UNORM == format) + { + if (size.m_width * size.m_height * 4 == data.size()) + { + image.m_width = size.m_width; + image.m_height = size.m_height; + image.m_bitDepth = 8; + image.m_colorType = PNG_COLOR_TYPE_RGB_ALPHA; + image.m_bufferFormat = PngFile::Format::RGBA; + image.m_buffer = data; + } + else + { + AZ_Assert(false, "Invalid arguments. Buffer size does not match the image dimensions."); + } + } + + return image; } - bool isPng = !png_sig_cmp(header, 0, HeaderSize); - if (!isPng) + PngFile PngFile::Load(const char* path, LoadSettings loadSettings) { - fclose(fp); - loadSettings.m_errorHandler("Invalid header."); - return {}; - } + if (!loadSettings.m_errorHandler) + { + loadSettings.m_errorHandler = [path](const char* message) { DefaultErrorHandler(AZStd::string::format("Could not load file '%s'. %s", path, message).c_str()); }; + } - png_voidp user_error_ptr = &loadSettings.m_errorHandler; - png_error_ptr user_error_fn = PngImage_user_error_fn; - png_error_ptr user_warning_fn = PngImage_user_warning_fn; + // For documentation of this code, see http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf chapter 3 - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); - if (!png_ptr) - { - fclose(fp); - loadSettings.m_errorHandler("png_create_read_struct failed."); - return {}; - } + FILE* fp = NULL; + if (fopen_s(&fp, path, "rb") || !fp) + { + loadSettings.m_errorHandler("Failed to open file."); + return {}; + } - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) - { - png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); - fclose(fp); - loadSettings.m_errorHandler("png_create_info_struct failed."); - return {}; - } + png_byte header[HeaderSize] = {}; - png_infop end_info = png_create_info_struct(png_ptr); - if (!end_info) - { - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - fclose(fp); - loadSettings.m_errorHandler("png_create_info_struct failed."); - return {}; - } + if (fread(header, 1, HeaderSize, fp) != HeaderSize) + { + fclose(fp); + loadSettings.m_errorHandler("Invalid header."); + return {}; + } + + bool isPng = !png_sig_cmp(header, 0, HeaderSize); + if (!isPng) + { + fclose(fp); + loadSettings.m_errorHandler("Invalid header."); + return {}; + } + + png_voidp user_error_ptr = &loadSettings.m_errorHandler; + png_error_ptr user_error_fn = PngImage_user_error_fn; + png_error_ptr user_warning_fn = PngImage_user_warning_fn; + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); + if (!png_ptr) + { + fclose(fp); + loadSettings.m_errorHandler("png_create_read_struct failed."); + return {}; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + fclose(fp); + loadSettings.m_errorHandler("png_create_info_struct failed."); + return {}; + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + loadSettings.m_errorHandler("png_create_info_struct failed."); + return {}; + } #pragma warning(push) #pragma warning(disable: 4611) // Disables "interaction between '_setjmp' and C++ object destruction is non-portable". See https://docs.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-160 - if (setjmp(png_jmpbuf(png_ptr))) - { + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + // We don't report an error message here because the user_error_fn should have done that already. + return {}; + } +#pragma warning(pop) + + png_init_io(png_ptr, fp); + + png_set_sig_bytes(png_ptr, HeaderSize); + + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, 0); + + // To keep things simple for now we limit all images to RGB and RGBA, 8 bits per channel + int png_transforms = PNG_TRANSFORM_PACKING | // Expand 1, 2 and 4-bit samples to bytes + PNG_TRANSFORM_STRIP_16 | // Reduce 16 bit samples to 8 bits + PNG_TRANSFORM_GRAY_TO_RGB; + + if (loadSettings.m_stripAlpha) + { + png_transforms |= PNG_TRANSFORM_STRIP_ALPHA; + } + + png_read_png(png_ptr, info_ptr, png_transforms, NULL); + + // Note that libpng will allocate row_pointers for us. If we want to manage the memory ourselves, we need to call png_set_rows. + // In that case we would have to use the low level interface: png_read_info, png_read_image, and png_read_end. + png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr); + + PngFile pngFile; + + png_get_IHDR(png_ptr, info_ptr, &pngFile.m_width, &pngFile.m_height, &pngFile.m_bitDepth, &pngFile.m_colorType, NULL, NULL, NULL); + + uint32_t bytesPerPixel = 0; + + switch (pngFile.m_colorType) + { + case PNG_COLOR_TYPE_RGB: + pngFile.m_bufferFormat = PngFile::Format::RGB; + bytesPerPixel = 3; + break; + case PNG_COLOR_TYPE_RGBA: + pngFile.m_bufferFormat = PngFile::Format::RGBA; + bytesPerPixel = 4; + break; + default: + AZ_Assert(false, "The png transforms should have ensured a pixel format of RGB or RGBA, 8 bits per channel"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + loadSettings.m_errorHandler("Unsupported pixel format."); + return {}; + } + + // In the future we could use the low-level interface to avoid copying the image (and provide progress callbacks) + pngFile.m_buffer.set_capacity(pngFile.m_width * pngFile.m_height * bytesPerPixel); + for (uint32_t rowIndex = 0; rowIndex < pngFile.m_height; ++rowIndex) + { + png_bytep row = row_pointers[rowIndex]; + pngFile.m_buffer.insert(pngFile.m_buffer.end(), row, row + (pngFile.m_width * bytesPerPixel)); + } + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); - // We don't report an error message here because the user_error_fn should have done that already. - return {}; + return pngFile; } -#pragma warning(pop) - png_init_io(png_ptr, fp); - - png_set_sig_bytes(png_ptr, HeaderSize); - - png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, 0); - - // To keep things simple for now we limit all images to RGB and RGBA, 8 bits per channel - int png_transforms = PNG_TRANSFORM_PACKING | // Expand 1, 2 and 4-bit samples to bytes - PNG_TRANSFORM_STRIP_16 | // Reduce 16 bit samples to 8 bits - PNG_TRANSFORM_GRAY_TO_RGB; - - if (loadSettings.m_stripAlpha) + bool PngFile::Save(const char* path, SaveSettings saveSettings) { - png_transforms |= PNG_TRANSFORM_STRIP_ALPHA; - } + if (!saveSettings.m_errorHandler) + { + saveSettings.m_errorHandler = [path](const char* message) { DefaultErrorHandler(AZStd::string::format("Could not save file '%s'. %s", path, message).c_str()); }; + } - png_read_png(png_ptr, info_ptr, png_transforms, NULL); - - // Note that libpng will allocate row_pointers for us. If we want to manage the memory ourselves, we need to call png_set_rows. - // In that case we would have to use the low level interface: png_read_info, png_read_image, and png_read_end. - png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr); + if (!IsValid()) + { + saveSettings.m_errorHandler("This PngFile is invalid."); + return false; + } - PngImage pngImage; + // For documentation of this code, see http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf chapter 4 - png_get_IHDR(png_ptr, info_ptr, &pngImage.m_width, &pngImage.m_height, &pngImage.m_bitDepth, &pngImage.m_colorType, NULL, NULL, NULL); - - uint32_t bytesPerPixel = 0; + FILE* fp = NULL; + if (fopen_s(&fp, path, "wb") || !fp) + { + saveSettings.m_errorHandler("Failed to open file."); + return false; + } - switch (pngImage.m_colorType) - { - case PNG_COLOR_TYPE_RGB: - pngImage.m_bufferFormat = PngImage::Format::RGB; - bytesPerPixel = 3; - break; - case PNG_COLOR_TYPE_RGBA: - pngImage.m_bufferFormat = PngImage::Format::RGBA; - bytesPerPixel = 4; - break; - default: - AZ_Assert(false, "The png transforms should have ensured a pixel format of RGB or RGBA, 8 bits per channel"); - png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); - fclose(fp); - loadSettings.m_errorHandler("Unsupported pixel format."); - return {}; - } + png_voidp user_error_ptr = &saveSettings.m_errorHandler; + png_error_ptr user_error_fn = PngImage_user_error_fn; + png_error_ptr user_warning_fn = PngImage_user_warning_fn; - // In the future we could use the low-level interface to avoid copying the image (and provide progress callbacks) - pngImage.m_buffer.set_capacity(pngImage.m_width * pngImage.m_height * bytesPerPixel); - for (uint32_t rowIndex = 0; rowIndex < pngImage.m_height; ++rowIndex) - { - png_bytep row = row_pointers[rowIndex]; - pngImage.m_buffer.insert(pngImage.m_buffer.end(), row, row + (pngImage.m_width * bytesPerPixel)); - } - - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(fp); - return pngImage; - } + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); + if (!png_ptr) + { + fclose(fp); + saveSettings.m_errorHandler("png_create_write_struct failed."); + return false; + } - bool PngImage::Save(const char* path, SaveSettings saveSettings) - { - if (!saveSettings.m_errorHandler) - { - saveSettings.m_errorHandler = [path](const char* message) { DefaultErrorHandler(AZStd::string::format("Could not save file '%s'. %s", path, message).c_str()); }; - } - - if (!IsValid()) - { - saveSettings.m_errorHandler("This PngImage is invalid."); - return false; - } - - // For documentation of this code, see http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf chapter 4 - - FILE* fp = NULL; - if (fopen_s(&fp, path, "wb") || !fp) - { - saveSettings.m_errorHandler("Failed to open file."); - return false; - } - - png_voidp user_error_ptr = &saveSettings.m_errorHandler; - png_error_ptr user_error_fn = PngImage_user_error_fn; - png_error_ptr user_warning_fn = PngImage_user_warning_fn; - - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); - if (!png_ptr) - { - fclose(fp); - saveSettings.m_errorHandler("png_create_write_struct failed."); - return false; - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) - { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - fclose(fp); - saveSettings.m_errorHandler("png_destroy_write_struct failed."); - return false; - } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + fclose(fp); + saveSettings.m_errorHandler("png_destroy_write_struct failed."); + return false; + } #pragma warning(push) #pragma warning(disable: 4611) // Disables "interaction between '_setjmp' and C++ object destruction is non-portable". See https://docs.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-160 - if (setjmp(png_jmpbuf(png_ptr))) - { - png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); - // We don't report an error message here because the user_error_fn should have done that already. - return false; - } + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + // We don't report an error message here because the user_error_fn should have done that already. + return false; + } #pragma warning(pop) - png_init_io(png_ptr, fp); + png_init_io(png_ptr, fp); - png_set_IHDR(png_ptr, info_ptr, m_width, m_height, m_bitDepth, m_colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - png_set_compression_level(png_ptr, saveSettings.m_compressionLevel); + png_set_IHDR(png_ptr, info_ptr, m_width, m_height, m_bitDepth, m_colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - const uint32_t bytesPerPixel = (m_bufferFormat == PngImage::Format::RGB) ? 3 : 4; + png_set_compression_level(png_ptr, saveSettings.m_compressionLevel); - AZStd::vector rows; - rows.reserve(m_height); - for (uint32_t i = 0; i < m_height; ++i) - { - rows.push_back(m_buffer.begin() + m_width * bytesPerPixel * i); + const uint32_t bytesPerPixel = (m_bufferFormat == PngFile::Format::RGB) ? 3 : 4; + + AZStd::vector rows; + rows.reserve(m_height); + for (uint32_t i = 0; i < m_height; ++i) + { + rows.push_back(m_buffer.begin() + m_width * bytesPerPixel * i); + } + + png_set_rows(png_ptr, info_ptr, rows.begin()); + + int transforms = PNG_TRANSFORM_IDENTITY; + if (saveSettings.m_stripAlpha && m_bufferFormat == PngFile::Format::RGBA) + { + transforms |= PNG_TRANSFORM_STRIP_FILLER_AFTER; + } + + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + + return true; } - png_set_rows(png_ptr, info_ptr, rows.begin()); - - int transforms = PNG_TRANSFORM_IDENTITY; - if (saveSettings.m_stripAlpha && m_bufferFormat == PngImage::Format::RGBA) + void PngFile::DefaultErrorHandler(const char* message) { - transforms |= PNG_TRANSFORM_STRIP_FILLER_AFTER; + AZ_Error("PngFile", false, "%s", message); } - png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + bool PngFile::IsValid() const + { + return + !m_buffer.empty() && + m_width > 0 && + m_height > 0 && + m_bitDepth > 0; + } - png_destroy_write_struct(&png_ptr, &info_ptr); - - fclose(fp); + AZStd::vector&& PngFile::TakeBuffer() + { + return AZStd::move(m_buffer); + } - return true; - } - - void PngImage::DefaultErrorHandler(const char* message) - { - AZ_Error("PngImage", false, "%s", message); - } - - bool PngImage::IsValid() const - { - return - !m_buffer.empty() && - m_width > 0 && - m_height > 0 && - m_bitDepth > 0; - } - - AZStd::vector&& PngImage::TakeBuffer() - { - return AZStd::move(m_buffer); - } - + } // namespace Utils }// namespace AZ