diff options
author | Ibrahn Sahir <ibrahn.sahir@gmail.com> | 2019-06-16 20:08:52 +0100 |
---|---|---|
committer | Ibrahn Sahir <ibrahn.sahir@gmail.com> | 2019-06-19 11:05:58 +0100 |
commit | 5e2461124187550bb847e29361cdb1c358006f5e (patch) | |
tree | d874182dfdc45912c5d8bd90c5fb8b68d1f6a951 /drivers | |
parent | 22afebcad71db07063fa11fd4489b29741f28009 (diff) |
Png driver reworked to use libpng 1.6 simplified API
Wrapped libpng usage in a pair of functions under PNGDriverCommon,
which convert between Godot Image and png data.
Switched to libpng 1.6 simplified API for ease of maintenance.
Implemented ImageLoaderPNG and ResourceSaverPNG in terms of
PNGDriverCommon functions.
Travis, switched to builtin libpng (thus builtin freetype and zlib also)
so we can build on Xenial.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/png/image_loader_png.cpp | 355 | ||||
-rw-r--r-- | drivers/png/image_loader_png.h | 9 | ||||
-rw-r--r-- | drivers/png/png_driver_common.cpp | 205 | ||||
-rw-r--r-- | drivers/png/png_driver_common.h | 48 | ||||
-rw-r--r-- | drivers/png/resource_saver_png.cpp | 137 |
5 files changed, 306 insertions, 448 deletions
diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index 0bf432c78a..f257fafd93 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -32,186 +32,26 @@ #include "core/os/os.h" #include "core/print_string.h" +#include "drivers/png/png_driver_common.h" #include <string.h> -void ImageLoaderPNG::_read_png_data(png_structp png_ptr, png_bytep data, png_size_t p_length) { - - FileAccess *f = (FileAccess *)png_get_io_ptr(png_ptr); - f->get_buffer((uint8_t *)data, p_length); -} - -/* -png_structp png_ptr = png_create_read_struct_2 - (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, - user_error_fn, user_warning_fn, (png_voidp) - user_mem_ptr, user_malloc_fn, user_free_fn); -*/ -static png_voidp _png_malloc_fn(png_structp png_ptr, png_size_t size) { - - return memalloc(size); -} - -static void _png_free_fn(png_structp png_ptr, png_voidp ptr) { - - memfree(ptr); -} - -static void _png_error_function(png_structp, png_const_charp text) { - - ERR_PRINT(text); -} - -static void _png_warn_function(png_structp, png_const_charp text) { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - if (String(text).begins_with("iCCP")) return; // silences annoying spam emitted to output every time the user opened assetlib - } -#endif - WARN_PRINT(text); -} - -typedef void(PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); - -Error ImageLoaderPNG::_load_image(void *rf_up, png_rw_ptr p_func, Ref<Image> p_image) { - - png_structp png; - png_infop info; - - //png = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL); - - png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, _png_error_function, _png_warn_function, (png_voidp)NULL, - _png_malloc_fn, _png_free_fn); - - ERR_FAIL_COND_V(!png, ERR_OUT_OF_MEMORY); - - info = png_create_info_struct(png); - if (!info) { - png_destroy_read_struct(&png, NULL, NULL); - ERR_PRINT("Out of Memory"); - return ERR_OUT_OF_MEMORY; - } - - if (setjmp(png_jmpbuf(png))) { - - png_destroy_read_struct(&png, NULL, NULL); - ERR_PRINT("PNG Corrupted"); - return ERR_FILE_CORRUPT; - } - - png_set_read_fn(png, (void *)rf_up, p_func); - - png_uint_32 width, height; - int depth, color; - - png_read_info(png, info); - png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL); - - //https://svn.gov.pt/projects/ccidadao/repository/middleware-offline/trunk/_src/eidmw/FreeImagePTEiD/Source/FreeImage/PluginPNG.cpp - //png_get_text(png,info,) - /* - printf("Image width:%i\n", width); - printf("Image Height:%i\n", height); - printf("Bit depth:%i\n", depth); - printf("Color type:%i\n", color); - */ - - bool update_info = false; - - if (depth < 8) { //only bit dept 8 per channel is handled - - png_set_packing(png); - update_info = true; - }; - - if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png); - update_info = true; - } - - if (depth > 8) { - png_set_strip_16(png); - update_info = true; - } - - if (png_get_valid(png, info, PNG_INFO_tRNS)) { - //png_set_expand_gray_1_2_4_to_8(png); - png_set_tRNS_to_alpha(png); - update_info = true; - } - - if (update_info) { - png_read_update_info(png, info); - png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL); - } - - int components = 0; - - Image::Format fmt; - switch (color) { - - case PNG_COLOR_TYPE_GRAY: { - - fmt = Image::FORMAT_L8; - components = 1; - } break; - case PNG_COLOR_TYPE_GRAY_ALPHA: { - - fmt = Image::FORMAT_LA8; - components = 2; - } break; - case PNG_COLOR_TYPE_RGB: { - - fmt = Image::FORMAT_RGB8; - components = 3; - } break; - case PNG_COLOR_TYPE_RGB_ALPHA: { - - fmt = Image::FORMAT_RGBA8; - components = 4; - } break; - default: { +Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { - ERR_PRINT("INVALID PNG TYPE"); - png_destroy_read_struct(&png, &info, NULL); - return ERR_UNAVAILABLE; - } break; + const size_t buffer_size = f->get_len(); + PoolVector<uint8_t> file_buffer; + Error err = file_buffer.resize(buffer_size); + if (err) { + f->close(); + return err; } - - //int rowsize = png_get_rowbytes(png, info); - int rowsize = components * width; - - PoolVector<uint8_t> dstbuff; - - dstbuff.resize(rowsize * height); - - PoolVector<uint8_t>::Write dstbuff_write = dstbuff.write(); - - uint8_t *data = dstbuff_write.ptr(); - - uint8_t **row_p = memnew_arr(uint8_t *, height); - - for (unsigned int i = 0; i < height; i++) { - row_p[i] = &data[components * width * i]; + { + PoolVector<uint8_t>::Write writer = file_buffer.write(); + f->get_buffer(writer.ptr(), buffer_size); + f->close(); } - - png_read_image(png, (png_bytep *)row_p); - - memdelete_arr(row_p); - - p_image->create(width, height, 0, fmt, dstbuff); - - png_destroy_read_struct(&png, &info, NULL); - - return OK; -} - -Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { - - Error err = _load_image(f, _read_png_data, p_image); - f->close(); - - return err; + PoolVector<uint8_t>::Read reader = file_buffer.read(); + return PNGDriverCommon::png_to_image(reader.ptr(), buffer_size, p_image); } void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const { @@ -219,178 +59,53 @@ void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const p_extensions->push_back("png"); } -struct PNGReadStatus { - - uint32_t offset; - uint32_t size; - const unsigned char *image; -}; - -static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t p_length) { - - PNGReadStatus *rstatus; - rstatus = (PNGReadStatus *)png_get_io_ptr(png_ptr); - - png_size_t to_read = MIN(p_length, rstatus->size - rstatus->offset); - memcpy(data, &rstatus->image[rstatus->offset], to_read); - rstatus->offset += to_read; - - if (to_read < p_length) { - memset(&data[to_read], 0, p_length - to_read); - } -} - -static Ref<Image> _load_mem_png(const uint8_t *p_png, int p_size) { - - PNGReadStatus prs; - prs.image = p_png; - prs.offset = 0; - prs.size = p_size; +Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) { Ref<Image> img; img.instance(); - Error err = ImageLoaderPNG::_load_image(&prs, user_read_data, img); + + Error err = PNGDriverCommon::png_to_image(p_png, p_size, img); ERR_FAIL_COND_V(err, Ref<Image>()); return img; } -static Ref<Image> _lossless_unpack_png(const PoolVector<uint8_t> &p_data) { +Ref<Image> ImageLoaderPNG::lossless_unpack_png(const PoolVector<uint8_t> &p_data) { - int len = p_data.size(); + const int len = p_data.size(); ERR_FAIL_COND_V(len < 4, Ref<Image>()); PoolVector<uint8_t>::Read r = p_data.read(); ERR_FAIL_COND_V(r[0] != 'P' || r[1] != 'N' || r[2] != 'G' || r[3] != ' ', Ref<Image>()); - return _load_mem_png(&r[4], len - 4); -} - -static void _write_png_data(png_structp png_ptr, png_bytep data, png_size_t p_length) { - - PoolVector<uint8_t> &v = *(PoolVector<uint8_t> *)png_get_io_ptr(png_ptr); - int vs = v.size(); - - v.resize(vs + p_length); - PoolVector<uint8_t>::Write w = v.write(); - copymem(&w[vs], data, p_length); + return load_mem_png(&r[4], len - 4); } -static PoolVector<uint8_t> _lossless_pack_png(const Ref<Image> &p_image) { - - Ref<Image> img = p_image->duplicate(); - - if (img->is_compressed()) - img->decompress(); - - ERR_FAIL_COND_V(img->is_compressed(), PoolVector<uint8_t>()); - - png_structp png_ptr; - png_infop info_ptr; - png_bytep *row_pointers; - - /* initialize stuff */ - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +PoolVector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { - ERR_FAIL_COND_V(!png_ptr, PoolVector<uint8_t>()); + PoolVector<uint8_t> out_buffer; - info_ptr = png_create_info_struct(png_ptr); - - ERR_FAIL_COND_V(!info_ptr, PoolVector<uint8_t>()); - - if (setjmp(png_jmpbuf(png_ptr))) { + // add Godot's own "PNG " prefix + if (out_buffer.resize(4) != OK) { ERR_FAIL_V(PoolVector<uint8_t>()); } - PoolVector<uint8_t> ret; - ret.push_back('P'); - ret.push_back('N'); - ret.push_back('G'); - ret.push_back(' '); - png_set_write_fn(png_ptr, &ret, _write_png_data, NULL); - - /* write header */ - if (setjmp(png_jmpbuf(png_ptr))) { - ERR_FAIL_V(PoolVector<uint8_t>()); + // scope for writer lifetime + { + // must be closed before call to image_to_png + PoolVector<uint8_t>::Write writer = out_buffer.write(); + copymem(writer.ptr(), "PNG ", 4); } - int pngf = 0; - int cs = 0; - - switch (img->get_format()) { - - case Image::FORMAT_L8: { - - pngf = PNG_COLOR_TYPE_GRAY; - cs = 1; - } break; - case Image::FORMAT_LA8: { - - pngf = PNG_COLOR_TYPE_GRAY_ALPHA; - cs = 2; - } break; - case Image::FORMAT_RGB8: { - - pngf = PNG_COLOR_TYPE_RGB; - cs = 3; - } break; - case Image::FORMAT_RGBA8: { - - pngf = PNG_COLOR_TYPE_RGB_ALPHA; - cs = 4; - } break; - default: { - - if (img->detect_alpha()) { - - img->convert(Image::FORMAT_RGBA8); - pngf = PNG_COLOR_TYPE_RGB_ALPHA; - cs = 4; - } else { - - img->convert(Image::FORMAT_RGB8); - pngf = PNG_COLOR_TYPE_RGB; - cs = 3; - } - } - } - - int w = img->get_width(); - int h = img->get_height(); - png_set_IHDR(png_ptr, info_ptr, w, h, - 8, pngf, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - /* write bytes */ - if (setjmp(png_jmpbuf(png_ptr))) { + Error err = PNGDriverCommon::image_to_png(p_image, out_buffer); + if (err) { ERR_FAIL_V(PoolVector<uint8_t>()); } - PoolVector<uint8_t>::Read r = img->get_data().read(); - - row_pointers = (png_bytep *)memalloc(sizeof(png_bytep) * h); - for (int i = 0; i < h; i++) { - - row_pointers[i] = (png_bytep)&r[i * w * cs]; - } - png_write_image(png_ptr, row_pointers); - - memfree(row_pointers); - - /* end write */ - if (setjmp(png_jmpbuf(png_ptr))) { - - ERR_FAIL_V(PoolVector<uint8_t>()); - } - - png_write_end(png_ptr, NULL); - - return ret; + return out_buffer; } ImageLoaderPNG::ImageLoaderPNG() { - Image::_png_mem_loader_func = _load_mem_png; - Image::lossless_unpacker = _lossless_unpack_png; - Image::lossless_packer = _lossless_pack_png; + Image::_png_mem_loader_func = load_mem_png; + Image::lossless_unpacker = lossless_unpack_png; + Image::lossless_packer = lossless_pack_png; } diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index c3951979c3..cc789f95d6 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -33,17 +33,16 @@ #include "core/io/image_loader.h" -#include <png.h> - /** @author Juan Linietsky <reduzio@gmail.com> */ class ImageLoaderPNG : public ImageFormatLoader { - - static void _read_png_data(png_structp png_ptr, png_bytep data, png_size_t p_length); +private: + static PoolVector<uint8_t> lossless_pack_png(const Ref<Image> &p_image); + static Ref<Image> lossless_unpack_png(const PoolVector<uint8_t> &p_data); + static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); public: - static Error _load_image(void *rf_up, png_rw_ptr p_func, Ref<Image> p_image); virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderPNG(); diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp new file mode 100644 index 0000000000..0e849bf2fe --- /dev/null +++ b/drivers/png/png_driver_common.cpp @@ -0,0 +1,205 @@ +/*************************************************************************/ +/* png_driver_common.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "png_driver_common.h" + +#include "core/os/os.h" + +#include <png.h> +#include <string.h> + +namespace PNGDriverCommon { + +// Print any warnings. +// On error, set explain and return true. +// Call should be wrapped in ERR_FAIL_COND +static bool check_error(const png_image &image) { + const png_uint_32 failed = PNG_IMAGE_FAILED(image); + if (failed & PNG_IMAGE_ERROR) { + ERR_EXPLAINC(image.message); + return true; + } else if (failed) { +#ifdef TOOLS_ENABLED + // suppress this warning, to avoid log spam when opening assetlib + const static char *const noisy = "iCCP: known incorrect sRGB profile"; + const Engine *const eng = Engine::get_singleton(); + if (eng && eng->is_editor_hint() && !strcmp(image.message, noisy)) { + return false; + } +#endif + WARN_PRINT(image.message); + } + return false; +} + +Error png_to_image(const uint8_t *p_source, size_t p_size, Ref<Image> p_image) { + + png_image png_img; + zeromem(&png_img, sizeof(png_img)); + png_img.version = PNG_IMAGE_VERSION; + + // fetch image properties + int success = png_image_begin_read_from_memory(&png_img, p_source, p_size); + ERR_FAIL_COND_V(check_error(png_img), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT); + + // flags to be masked out of input format to give target format + const png_uint_32 format_mask = ~( + // convert component order to RGBA + PNG_FORMAT_FLAG_BGR | PNG_FORMAT_FLAG_AFIRST + // convert 16 bit components to 8 bit + | PNG_FORMAT_FLAG_LINEAR + // convert indexed image to direct color + | PNG_FORMAT_FLAG_COLORMAP); + + png_img.format &= format_mask; + + Image::Format dest_format; + switch (png_img.format) { + case PNG_FORMAT_GRAY: + dest_format = Image::FORMAT_L8; + break; + case PNG_FORMAT_GA: + dest_format = Image::FORMAT_LA8; + break; + case PNG_FORMAT_RGB: + dest_format = Image::FORMAT_RGB8; + break; + case PNG_FORMAT_RGBA: + dest_format = Image::FORMAT_RGBA8; + break; + default: + png_image_free(&png_img); // only required when we return before finish_read + ERR_PRINT("Unsupported png format"); + return ERR_UNAVAILABLE; + } + + const png_uint_32 stride = PNG_IMAGE_ROW_STRIDE(png_img); + PoolVector<uint8_t> buffer; + Error err = buffer.resize(PNG_IMAGE_BUFFER_SIZE(png_img, stride)); + if (err) { + png_image_free(&png_img); // only required when we return before finish_read + return err; + } + PoolVector<uint8_t>::Write writer = buffer.write(); + + // read image data to buffer and release libpng resources + success = png_image_finish_read(&png_img, NULL, writer.ptr(), stride, NULL); + ERR_FAIL_COND_V(check_error(png_img), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT); + + p_image->create(png_img.width, png_img.height, 0, dest_format, buffer); + + return OK; +} + +Error image_to_png(const Ref<Image> &p_image, PoolVector<uint8_t> &p_buffer) { + + Ref<Image> source_image = p_image->duplicate(); + + if (source_image->is_compressed()) + source_image->decompress(); + + ERR_FAIL_COND_V(source_image->is_compressed(), FAILED); + + png_image png_img; + zeromem(&png_img, sizeof(png_img)); + png_img.version = PNG_IMAGE_VERSION; + png_img.width = source_image->get_width(); + png_img.height = source_image->get_height(); + + switch (source_image->get_format()) { + case Image::FORMAT_L8: + png_img.format = PNG_FORMAT_GRAY; + break; + case Image::FORMAT_LA8: + png_img.format = PNG_FORMAT_GA; + break; + case Image::FORMAT_RGB8: + png_img.format = PNG_FORMAT_RGB; + break; + case Image::FORMAT_RGBA8: + png_img.format = PNG_FORMAT_RGBA; + break; + default: + if (source_image->detect_alpha()) { + source_image->convert(Image::FORMAT_RGBA8); + png_img.format = PNG_FORMAT_RGBA; + } else { + source_image->convert(Image::FORMAT_RGB8); + png_img.format = PNG_FORMAT_RGB; + } + } + + const PoolVector<uint8_t> image_data = source_image->get_data(); + const PoolVector<uint8_t>::Read reader = image_data.read(); + + // we may be passed a buffer with existing content we're expected to append to + const int buffer_offset = p_buffer.size(); + + const size_t png_size_estimate = PNG_IMAGE_PNG_SIZE_MAX(png_img); + + // try with estimated size + size_t compressed_size = png_size_estimate; + int success = 0; + { // scope writer lifetime + Error err = p_buffer.resize(buffer_offset + png_size_estimate); + ERR_FAIL_COND_V(err, err); + + PoolVector<uint8_t>::Write writer = p_buffer.write(); + success = png_image_write_to_memory(&png_img, &writer[buffer_offset], + &compressed_size, 0, reader.ptr(), 0, NULL); + ERR_FAIL_COND_V(check_error(png_img), FAILED); + } + if (!success) { + if (compressed_size <= png_size_estimate) { + // buffer was big enough, must be some other error + ERR_FAIL_V(FAILED); + } + + // write failed due to buffer size, resize and retry + Error err = p_buffer.resize(buffer_offset + compressed_size); + ERR_FAIL_COND_V(err, err); + + PoolVector<uint8_t>::Write writer = p_buffer.write(); + success = png_image_write_to_memory(&png_img, &writer[buffer_offset], + &compressed_size, 0, reader.ptr(), 0, NULL); + ERR_FAIL_COND_V(check_error(png_img), FAILED); + ERR_FAIL_COND_V(!success, FAILED); + } + + // trim buffer size to content + Error err = p_buffer.resize(buffer_offset + compressed_size); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +} // namespace PNGDriverCommon diff --git a/drivers/png/png_driver_common.h b/drivers/png/png_driver_common.h new file mode 100644 index 0000000000..3ff87759fb --- /dev/null +++ b/drivers/png/png_driver_common.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* png_driver_common.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PNG_DRIVER_COMMON_H +#define PNG_DRIVER_COMMON_H + +#include "core/image.h" +#include "core/pool_vector.h" + +namespace PNGDriverCommon { + +// Attempt to load png from buffer (p_source, p_size) into p_image +Error png_to_image(const uint8_t *p_source, size_t p_size, Ref<Image> p_image); + +// Append p_image, as a png, to p_buffer. +// Contents of p_buffer is unspecified if error returned. +Error image_to_png(const Ref<Image> &p_image, PoolVector<uint8_t> &p_buffer); + +} // namespace PNGDriverCommon + +#endif diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp index 9e8043cbea..89e8ee32cc 100644 --- a/drivers/png/resource_saver_png.cpp +++ b/drivers/png/resource_saver_png.cpp @@ -32,17 +32,9 @@ #include "core/image.h" #include "core/os/file_access.h" -#include "core/project_settings.h" +#include "drivers/png/png_driver_common.h" #include "scene/resources/texture.h" -#include <png.h> - -static void _write_png_data(png_structp png_ptr, png_bytep data, png_size_t p_length) { - - FileAccess *f = (FileAccess *)png_get_io_ptr(png_ptr); - f->store_buffer((const uint8_t *)data, p_length); -} - Error ResourceSaverPNG::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { Ref<ImageTexture> texture = p_resource; @@ -55,129 +47,27 @@ Error ResourceSaverPNG::save(const String &p_path, const RES &p_resource, uint32 Error err = save_image(p_path, img); - if (err == OK) { - } - return err; }; Error ResourceSaverPNG::save_image(const String &p_path, const Ref<Image> &p_img) { - Ref<Image> img = p_img->duplicate(); - - if (img->is_compressed()) - img->decompress(); - - ERR_FAIL_COND_V(img->is_compressed(), ERR_INVALID_PARAMETER); - - png_structp png_ptr; - png_infop info_ptr; - png_bytep *row_pointers; - - /* initialize stuff */ - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - - ERR_FAIL_COND_V(!png_ptr, ERR_CANT_CREATE); + PoolVector<uint8_t> buffer; + Error err = PNGDriverCommon::image_to_png(p_img, buffer); + ERR_FAIL_COND_V(err, err); + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(err, err); - info_ptr = png_create_info_struct(png_ptr); + PoolVector<uint8_t>::Read reader = buffer.read(); - ERR_FAIL_COND_V(!info_ptr, ERR_CANT_CREATE); - - if (setjmp(png_jmpbuf(png_ptr))) { - ERR_FAIL_V(ERR_CANT_OPEN); - } - //change this - Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) { - ERR_FAIL_V(err); - } - - png_set_write_fn(png_ptr, f, _write_png_data, NULL); - - /* write header */ - if (setjmp(png_jmpbuf(png_ptr))) { - ERR_FAIL_V(ERR_CANT_OPEN); + file->store_buffer(reader.ptr(), buffer.size()); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + memdelete(file); + return ERR_CANT_CREATE; } - int pngf = 0; - int cs = 0; - - switch (img->get_format()) { - - case Image::FORMAT_L8: { - - pngf = PNG_COLOR_TYPE_GRAY; - cs = 1; - } break; - case Image::FORMAT_LA8: { - - pngf = PNG_COLOR_TYPE_GRAY_ALPHA; - cs = 2; - } break; - case Image::FORMAT_RGB8: { - - pngf = PNG_COLOR_TYPE_RGB; - cs = 3; - } break; - case Image::FORMAT_RGBA8: { - - pngf = PNG_COLOR_TYPE_RGB_ALPHA; - cs = 4; - } break; - default: { - - if (img->detect_alpha()) { - - img->convert(Image::FORMAT_RGBA8); - pngf = PNG_COLOR_TYPE_RGB_ALPHA; - cs = 4; - } else { - - img->convert(Image::FORMAT_RGB8); - pngf = PNG_COLOR_TYPE_RGB; - cs = 3; - } - } - } - - int w = img->get_width(); - int h = img->get_height(); - png_set_IHDR(png_ptr, info_ptr, w, h, - 8, pngf, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - /* write bytes */ - if (setjmp(png_jmpbuf(png_ptr))) { - memdelete(f); - ERR_FAIL_V(ERR_CANT_OPEN); - } - - PoolVector<uint8_t>::Read r = img->get_data().read(); - - row_pointers = (png_bytep *)memalloc(sizeof(png_bytep) * h); - for (int i = 0; i < h; i++) { - - row_pointers[i] = (png_bytep)&r[i * w * cs]; - } - png_write_image(png_ptr, row_pointers); - - memfree(row_pointers); - - /* end write */ - if (setjmp(png_jmpbuf(png_ptr))) { - - memdelete(f); - ERR_FAIL_V(ERR_CANT_OPEN); - } - - png_write_end(png_ptr, NULL); - memdelete(f); - - /* cleanup heap allocation */ - png_destroy_write_struct(&png_ptr, &info_ptr); + file->close(); + memdelete(file); return OK; } @@ -186,6 +76,7 @@ bool ResourceSaverPNG::recognize(const RES &p_resource) const { return (p_resource.is_valid() && p_resource->is_class("ImageTexture")); } + void ResourceSaverPNG::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { if (Object::cast_to<Texture>(*p_resource)) { |