Fix the build for older macOS.
[dcpomatic.git] / src / lib / j2k_image_proxy.cc
1 /*
2     Copyright (C) 2014-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 "j2k_image_proxy.h"
23 #include "dcpomatic_socket.h"
24 #include "image.h"
25 #include "dcpomatic_assert.h"
26 #include "warnings.h"
27 #include <dcp/raw_convert.h>
28 #include <dcp/openjpeg_image.h>
29 #include <dcp/mono_picture_frame.h>
30 #include <dcp/stereo_picture_frame.h>
31 #include <dcp/colour_conversion.h>
32 #include <dcp/rgb_xyz.h>
33 #include <dcp/j2k_transcode.h>
34 #include <libcxml/cxml.h>
35 DCPOMATIC_DISABLE_WARNINGS
36 #include <libxml++/libxml++.h>
37 DCPOMATIC_ENABLE_WARNINGS
38 #include <iostream>
39
40 #include "i18n.h"
41
42
43 using std::cout;
44 using std::dynamic_pointer_cast;
45 using std::make_shared;
46 using std::max;
47 using std::shared_ptr;
48 using std::string;
49 using boost::optional;
50 using dcp::ArrayData;
51 using dcp::raw_convert;
52
53
54 /** Construct a J2KImageProxy from a JPEG2000 file */
55 J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPixelFormat pixel_format)
56         : _data (new dcp::ArrayData(path))
57         , _size (size)
58         , _pixel_format (pixel_format)
59         , _error (false)
60 {
61         /* ::image assumes 16bpp */
62         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
63 }
64
65
66 J2KImageProxy::J2KImageProxy (
67         shared_ptr<const dcp::MonoPictureFrame> frame,
68         dcp::Size size,
69         AVPixelFormat pixel_format,
70         optional<int> forced_reduction
71         )
72         : _data (frame)
73         , _size (size)
74         , _pixel_format (pixel_format)
75         , _forced_reduction (forced_reduction)
76         , _error (false)
77 {
78         /* ::image assumes 16bpp */
79         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
80 }
81
82
83 J2KImageProxy::J2KImageProxy (
84         shared_ptr<const dcp::StereoPictureFrame> frame,
85         dcp::Size size,
86         dcp::Eye eye,
87         AVPixelFormat pixel_format,
88         optional<int> forced_reduction
89         )
90         : _data (eye == dcp::Eye::LEFT ? frame->left() : frame->right())
91         , _size (size)
92         , _eye (eye)
93         , _pixel_format (pixel_format)
94         , _forced_reduction (forced_reduction)
95         , _error (false)
96 {
97         /* ::image assumes 16bpp */
98         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
99 }
100
101
102 J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
103         : _error (false)
104 {
105         _size = dcp::Size (xml->number_child<int>("Width"), xml->number_child<int>("Height"));
106         if (xml->optional_number_child<int>("Eye")) {
107                 _eye = static_cast<dcp::Eye>(xml->number_child<int>("Eye"));
108         }
109         auto data = make_shared<ArrayData>(xml->number_child<int>("Size"));
110         /* This only matters when we are using J2KImageProxy for the preview, which
111            will never use this constructor (which is only used for passing data to
112            encode servers).  So we can put anything in here.  It's a bit of a hack.
113         */
114         _pixel_format = AV_PIX_FMT_XYZ12LE;
115         socket->read (data->data(), data->size());
116         _data = data;
117 }
118
119
120 int
121 J2KImageProxy::prepare (Image::Alignment alignment, optional<dcp::Size> target_size) const
122 {
123         boost::mutex::scoped_lock lm (_mutex);
124
125         if (_image && target_size == _target_size) {
126                 DCPOMATIC_ASSERT (_reduce);
127                 return *_reduce;
128         }
129
130         int reduce = 0;
131
132         if (_forced_reduction) {
133                 reduce = *_forced_reduction;
134         } else {
135                 while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
136                         ++reduce;
137                 }
138
139                 --reduce;
140                 reduce = max (0, reduce);
141         }
142
143         try {
144                 /* XXX: should check that potentially trashing _data here doesn't matter */
145                 auto decompressed = dcp::decompress_j2k (const_cast<uint8_t*>(_data->data()), _data->size(), reduce);
146                 _image = make_shared<Image>(_pixel_format, decompressed->size(), alignment);
147
148                 int const shift = 16 - decompressed->precision (0);
149
150                 /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
151                    the data is 12-bit either way.
152                    */
153
154                 int const width = decompressed->size().width;
155
156                 int p = 0;
157                 int* decomp_0 = decompressed->data (0);
158                 int* decomp_1 = decompressed->data (1);
159                 int* decomp_2 = decompressed->data (2);
160                 for (int y = 0; y < decompressed->size().height; ++y) {
161                         auto q = reinterpret_cast<uint16_t *>(_image->data()[0] + y * _image->stride()[0]);
162                         for (int x = 0; x < width; ++x) {
163                                 *q++ = decomp_0[p] << shift;
164                                 *q++ = decomp_1[p] << shift;
165                                 *q++ = decomp_2[p] << shift;
166                                 ++p;
167                         }
168                 }
169         } catch (dcp::J2KDecompressionError& e) {
170                 _image = make_shared<Image>(_pixel_format, _size, alignment);
171                 _image->make_black ();
172                 _error = true;
173         }
174
175         _target_size = target_size;
176         _reduce = reduce;
177
178         return reduce;
179 }
180
181
182 ImageProxy::Result
183 J2KImageProxy::image (Image::Alignment alignment, optional<dcp::Size> target_size) const
184 {
185         int const r = prepare (alignment, target_size);
186
187         /* I think this is safe without a lock on mutex.  _image is guaranteed to be
188            set up when prepare() has happened.
189         */
190         return Result (_image, r, _error);
191 }
192
193
194 void
195 J2KImageProxy::add_metadata (xmlpp::Node* node) const
196 {
197         node->add_child("Type")->add_child_text(N_("J2K"));
198         node->add_child("Width")->add_child_text(raw_convert<string>(_size.width));
199         node->add_child("Height")->add_child_text(raw_convert<string>(_size.height));
200         if (_eye) {
201                 node->add_child("Eye")->add_child_text(raw_convert<string>(static_cast<int>(_eye.get())));
202         }
203         node->add_child("Size")->add_child_text(raw_convert<string>(_data->size()));
204 }
205
206
207 void
208 J2KImageProxy::write_to_socket (shared_ptr<Socket> socket) const
209 {
210         socket->write (_data->data(), _data->size());
211 }
212
213
214 bool
215 J2KImageProxy::same (shared_ptr<const ImageProxy> other) const
216 {
217         auto jp = dynamic_pointer_cast<const J2KImageProxy>(other);
218         if (!jp) {
219                 return false;
220         }
221
222         return *_data == *jp->_data;
223 }
224
225
226 J2KImageProxy::J2KImageProxy (ArrayData data, dcp::Size size, AVPixelFormat pixel_format)
227         : _data (new ArrayData(data))
228         , _size (size)
229         , _pixel_format (pixel_format)
230         , _error (false)
231 {
232         /* ::image assumes 16bpp */
233         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
234 }
235
236
237 size_t
238 J2KImageProxy::memory_used () const
239 {
240         size_t m = _data->size();
241         if (_image) {
242                 /* 3 components, 16-bits per pixel */
243                 m += 3 * 2 * _image->size().width * _image->size().height;
244         }
245         return m;
246 }