X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fimage_png.cc;fp=src%2Flib%2Fimage_png.cc;h=b97ef5aad99a4c1d6b6b6257218a297aa168c26c;hb=7ec6c86c913fba820870565ee757fdf43ae47433;hp=0000000000000000000000000000000000000000;hpb=4673b12dcf6340862f9f4f72fc6dca8553130b55;p=dcpomatic.git diff --git a/src/lib/image_png.cc b/src/lib/image_png.cc new file mode 100644 index 000000000..b97ef5aad --- /dev/null +++ b/src/lib/image_png.cc @@ -0,0 +1,132 @@ +/* + Copyright (C) 2021 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see . + +*/ + + +#include "dcpomatic_assert.h" +#include "exceptions.h" +#include "image.h" +#include + +#include "i18n.h" + + +using std::shared_ptr; + + +class Memory +{ +public: + Memory () {} + Memory (Memory const&) = delete; + Memory& operator= (Memory const&) = delete; + + ~Memory () + { + free (data); + } + + uint8_t* data = nullptr; + size_t size = 0; +}; + + +static void +png_write_data (png_structp png_ptr, png_bytep data, png_size_t length) +{ + auto mem = reinterpret_cast(png_get_io_ptr(png_ptr)); + size_t size = mem->size + length; + + if (mem->data) { + mem->data = reinterpret_cast(realloc(mem->data, size)); + } else { + mem->data = reinterpret_cast(malloc(size)); + } + + if (!mem->data) { + throw EncodeError (N_("could not allocate memory for PNG")); + } + + memcpy (mem->data + mem->size, data, length); + mem->size += length; +} + + +static void +png_flush (png_structp) +{ + +} + + +static void +png_error_fn (png_structp, char const * message) +{ + throw EncodeError (String::compose("Error during PNG write: %1", message)); +} + + +dcp::ArrayData +image_as_png (shared_ptr image) +{ + DCPOMATIC_ASSERT (image->bytes_per_pixel(0) == 4); + DCPOMATIC_ASSERT (image->planes() == 1); + if (image->pixel_format() != AV_PIX_FMT_RGBA) { + return image_as_png(image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, Image::Alignment::PADDED, false)); + } + + /* error handling? */ + auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, reinterpret_cast(const_cast(image.get())), png_error_fn, 0); + if (!png_ptr) { + throw EncodeError (N_("could not create PNG write struct")); + } + + Memory state; + + png_set_write_fn (png_ptr, &state, png_write_data, png_flush); + + auto info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct (&png_ptr, &info_ptr); + throw EncodeError (N_("could not create PNG info struct")); + } + + int const width = image->size().width; + int const height = image->size().height; + + png_set_IHDR (png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + auto row_pointers = reinterpret_cast(png_malloc(png_ptr, image->size().height * sizeof(png_byte *))); + auto const data = image->data()[0]; + auto const stride = image->stride()[0]; + for (int i = 0; i < height; ++i) { + row_pointers[i] = reinterpret_cast(data + i * stride); + } + + png_write_info (png_ptr, info_ptr); + png_write_image (png_ptr, row_pointers); + png_write_end (png_ptr, info_ptr); + + png_destroy_write_struct (&png_ptr, &info_ptr); + png_free (png_ptr, row_pointers); + + return dcp::ArrayData (state.data, state.size); +} + +