Missed update to private test repo version.
[dcpomatic.git] / src / lib / image_png.cc
1 /*
2     Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "dcpomatic_assert.h"
23 #include "exceptions.h"
24 #include "image.h"
25 #include <png.h>
26
27 #include "i18n.h"
28
29
30 using std::shared_ptr;
31
32
33 class Memory
34 {
35 public:
36         Memory () {}
37         Memory (Memory const&) = delete;
38         Memory& operator= (Memory const&) = delete;
39
40         ~Memory ()
41         {
42                 free (data);
43         }
44
45         uint8_t* data = nullptr;
46         size_t size = 0;
47 };
48
49
50 static void
51 png_write_data (png_structp png_ptr, png_bytep data, png_size_t length)
52 {
53         auto mem = reinterpret_cast<Memory*>(png_get_io_ptr(png_ptr));
54         size_t size = mem->size + length;
55
56         if (mem->data) {
57                 mem->data = reinterpret_cast<uint8_t*>(realloc(mem->data, size));
58         } else {
59                 mem->data = reinterpret_cast<uint8_t*>(malloc(size));
60         }
61
62         if (!mem->data) {
63                 throw EncodeError (N_("could not allocate memory for PNG"));
64         }
65
66         memcpy (mem->data + mem->size, data, length);
67         mem->size += length;
68 }
69
70
71 static void
72 png_flush (png_structp)
73 {
74
75 }
76
77
78 static void
79 png_error_fn (png_structp, char const * message)
80 {
81         throw EncodeError (String::compose("Error during PNG write: %1", message));
82 }
83
84
85 dcp::ArrayData
86 image_as_png (shared_ptr<const Image> image)
87 {
88         DCPOMATIC_ASSERT (image->bytes_per_pixel(0) == 4);
89         DCPOMATIC_ASSERT (image->planes() == 1);
90         if (image->pixel_format() != AV_PIX_FMT_RGBA) {
91                 return image_as_png(image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, Image::Alignment::PADDED, false));
92         }
93
94         /* error handling? */
95         auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, reinterpret_cast<void*>(const_cast<Image*>(image.get())), png_error_fn, 0);
96         if (!png_ptr) {
97                 throw EncodeError (N_("could not create PNG write struct"));
98         }
99
100         Memory state;
101
102         png_set_write_fn (png_ptr, &state, png_write_data, png_flush);
103
104         auto info_ptr = png_create_info_struct(png_ptr);
105         if (!info_ptr) {
106                 png_destroy_write_struct (&png_ptr, &info_ptr);
107                 throw EncodeError (N_("could not create PNG info struct"));
108         }
109
110         int const width = image->size().width;
111         int const height = image->size().height;
112
113         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);
114
115         auto row_pointers = reinterpret_cast<png_byte **>(png_malloc(png_ptr, image->size().height * sizeof(png_byte *)));
116         auto const data = image->data()[0];
117         auto const stride = image->stride()[0];
118         for (int i = 0; i < height; ++i) {
119                 row_pointers[i] = reinterpret_cast<png_byte *>(data + i * stride);
120         }
121
122         png_write_info (png_ptr, info_ptr);
123         png_write_image (png_ptr, row_pointers);
124         png_write_end (png_ptr, info_ptr);
125
126         png_destroy_write_struct (&png_ptr, &info_ptr);
127         png_free (png_ptr, row_pointers);
128
129         return dcp::ArrayData (state.data, state.size);
130 }
131
132