Missed update to private test repo version.
[dcpomatic.git] / src / lib / image_jpeg.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 <jpeglib.h>
26
27 #include "i18n.h"
28
29
30 using std::shared_ptr;
31
32
33 struct destination_mgr
34 {
35         jpeg_destination_mgr pub;
36         std::vector<uint8_t>* data;
37 };
38
39
40 static void
41 init_destination (j_compress_ptr)
42 {
43
44 }
45
46
47 /* Empty the output buffer (i.e. make more space for data); we'll make
48  * the output buffer bigger instead.
49  */
50 static boolean
51 empty_output_buffer (j_compress_ptr state)
52 {
53         auto dest = reinterpret_cast<destination_mgr*> (state->dest);
54
55         auto const old_size = dest->data->size();
56         dest->data->resize (old_size * 2);
57
58         dest->pub.next_output_byte = dest->data->data() + old_size;
59         dest->pub.free_in_buffer = old_size;
60
61         return TRUE;
62 }
63
64
65 static void
66 term_destination (j_compress_ptr state)
67 {
68         auto dest = reinterpret_cast<destination_mgr*> (state->dest);
69
70         dest->data->resize (dest->data->size() - dest->pub.free_in_buffer);
71 }
72
73
74 void
75 error_exit (j_common_ptr)
76 {
77         throw EncodeError (N_("JPEG encoding error"));
78 }
79
80
81 dcp::ArrayData
82 image_as_jpeg (shared_ptr<const Image> image, int quality)
83 {
84         if (image->pixel_format() != AV_PIX_FMT_RGB24) {
85                 return image_as_jpeg(
86                         Image::ensure_alignment(image, Image::Alignment::PADDED)->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::PADDED, false),
87                         quality
88                         );
89         }
90
91         struct jpeg_compress_struct compress;
92         struct jpeg_error_mgr error;
93
94         compress.err = jpeg_std_error (&error);
95         error.error_exit = error_exit;
96         jpeg_create_compress (&compress);
97
98         auto mgr = new destination_mgr;
99         compress.dest = reinterpret_cast<jpeg_destination_mgr*>(mgr);
100
101         mgr->pub.init_destination = init_destination;
102         mgr->pub.empty_output_buffer = empty_output_buffer;
103         mgr->pub.term_destination = term_destination;
104
105         std::vector<uint8_t> data(4096);
106         mgr->data = &data;
107         mgr->pub.next_output_byte = data.data();
108         mgr->pub.free_in_buffer = data.size();
109
110         compress.image_width = image->size().width;
111         compress.image_height = image->size().height;
112         compress.input_components = 3;
113         compress.in_color_space = JCS_RGB;
114
115         jpeg_set_defaults (&compress);
116         jpeg_set_quality (&compress, quality, TRUE);
117
118         jpeg_start_compress (&compress, TRUE);
119
120         JSAMPROW row[1];
121         auto const source_data = image->data()[0];
122         auto const source_stride = image->stride()[0];
123         for (auto y = 0U; y < compress.image_height; ++y) {
124                 row[0] = source_data + y * source_stride;
125                 jpeg_write_scanlines (&compress, row, 1);
126         }
127
128         jpeg_finish_compress (&compress);
129         delete mgr;
130         jpeg_destroy_compress (&compress);
131
132         return dcp::ArrayData (data.data(), data.size());
133 }
134
135