1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5 #include <cassert>
6 #include <cstdio>
7 #include <vector>
8
9 #include <OpenImageIO/filesystem.h>
10 #include <OpenImageIO/fmath.h>
11 #include <OpenImageIO/imageio.h>
12 #include <OpenImageIO/tiffutils.h>
13
14 #include "jpeg_pvt.h"
15
16 OIIO_PLUGIN_NAMESPACE_BEGIN
17
18 #define DBG if (0)
19
20
21 // References:
22 // * JPEG library documentation: /usr/share/doc/libjpeg-devel-6b
23 // * JFIF spec: https://www.w3.org/Graphics/JPEG/jfif3.pdf
24 // * ITU T.871 (aka ISO/IEC 10918-5):
25 // https://www.itu.int/rec/T-REC-T.871-201105-I/en
26
27
28
29 class JpgOutput final : public ImageOutput {
30 public:
JpgOutput()31 JpgOutput() { init(); }
~JpgOutput()32 virtual ~JpgOutput() { close(); }
format_name(void) const33 virtual const char* format_name(void) const override { return "jpeg"; }
supports(string_view feature) const34 virtual int supports(string_view feature) const override
35 {
36 return (feature == "exif" || feature == "iptc");
37 }
38 virtual bool open(const std::string& name, const ImageSpec& spec,
39 OpenMode mode = Create) override;
40 virtual bool write_scanline(int y, int z, TypeDesc format, const void* data,
41 stride_t xstride) override;
42 virtual bool write_tile(int x, int y, int z, TypeDesc format,
43 const void* data, stride_t xstride,
44 stride_t ystride, stride_t zstride) override;
45 virtual bool close() override;
46 virtual bool copy_image(ImageInput* in) override;
47
48 private:
49 FILE* m_fd;
50 std::string m_filename;
51 unsigned int m_dither;
52 int m_next_scanline; // Which scanline is the next to write?
53 std::vector<unsigned char> m_scratch;
54 struct jpeg_compress_struct m_cinfo;
55 struct jpeg_error_mgr c_jerr;
56 jvirt_barray_ptr* m_copy_coeffs;
57 struct jpeg_decompress_struct* m_copy_decompressor;
58 std::vector<unsigned char> m_tilebuffer;
59
init(void)60 void init(void)
61 {
62 m_fd = NULL;
63 m_copy_coeffs = NULL;
64 m_copy_decompressor = NULL;
65 }
66
set_subsampling(const int components[])67 void set_subsampling(const int components[])
68 {
69 jpeg_set_colorspace(&m_cinfo, JCS_YCbCr);
70 m_cinfo.comp_info[0].h_samp_factor = components[0];
71 m_cinfo.comp_info[0].v_samp_factor = components[1];
72 m_cinfo.comp_info[1].h_samp_factor = components[2];
73 m_cinfo.comp_info[1].v_samp_factor = components[3];
74 m_cinfo.comp_info[2].h_samp_factor = components[4];
75 m_cinfo.comp_info[2].v_samp_factor = components[5];
76 }
77
78 // Read the XResolution/YResolution and PixelAspectRatio metadata, store
79 // in density fields m_cinfo.X_density,Y_density.
80 void resmeta_to_density();
81 };
82
83
84
85 OIIO_PLUGIN_EXPORTS_BEGIN
86
87 OIIO_EXPORT ImageOutput*
jpeg_output_imageio_create()88 jpeg_output_imageio_create()
89 {
90 return new JpgOutput;
91 }
92
93 OIIO_EXPORT const char* jpeg_output_extensions[]
94 = { "jpg", "jpe", "jpeg", "jif", "jfif", "jfi", nullptr };
95
96 OIIO_PLUGIN_EXPORTS_END
97
98
99
100 bool
open(const std::string & name,const ImageSpec & newspec,OpenMode mode)101 JpgOutput::open(const std::string& name, const ImageSpec& newspec,
102 OpenMode mode)
103 {
104 if (mode != Create) {
105 errorf("%s does not support subimages or MIP levels", format_name());
106 return false;
107 }
108
109 // Save name and spec for later use
110 m_filename = name;
111 m_spec = newspec;
112
113 // Check for things this format doesn't support
114 if (m_spec.width < 1 || m_spec.height < 1) {
115 errorf("Image resolution must be at least 1x1, you asked for %d x %d",
116 m_spec.width, m_spec.height);
117 return false;
118 }
119 if (m_spec.depth < 1)
120 m_spec.depth = 1;
121 if (m_spec.depth > 1) {
122 errorf("%s does not support volume images (depth > 1)", format_name());
123 return false;
124 }
125
126 m_fd = Filesystem::fopen(name, "wb");
127 if (m_fd == NULL) {
128 errorf("Could not open \"%s\"", name);
129 return false;
130 }
131
132 m_cinfo.err = jpeg_std_error(&c_jerr); // set error handler
133 jpeg_create_compress(&m_cinfo); // create compressor
134 jpeg_stdio_dest(&m_cinfo, m_fd); // set output stream
135
136 // Set image and compression parameters
137 m_cinfo.image_width = m_spec.width;
138 m_cinfo.image_height = m_spec.height;
139
140 // JFIF can only handle grayscale and RGB. Do the best we can with this
141 // limited format by truncating to 3 channels if > 3 are requested,
142 // truncating to 1 channel if 2 are requested.
143 if (m_spec.nchannels >= 3) {
144 m_cinfo.input_components = 3;
145 m_cinfo.in_color_space = JCS_RGB;
146 } else {
147 m_cinfo.input_components = 1;
148 m_cinfo.in_color_space = JCS_GRAYSCALE;
149 }
150
151 resmeta_to_density();
152
153 m_cinfo.write_JFIF_header = TRUE;
154
155 if (m_copy_coeffs) {
156 // Back door for copy()
157 jpeg_copy_critical_parameters(m_copy_decompressor, &m_cinfo);
158 DBG std::cout << "out open: copy_critical_parameters\n";
159 jpeg_write_coefficients(&m_cinfo, m_copy_coeffs);
160 DBG std::cout << "out open: write_coefficients\n";
161 } else {
162 // normal write of scanlines
163 jpeg_set_defaults(&m_cinfo); // default compression
164 // Careful -- jpeg_set_defaults overwrites density
165 resmeta_to_density();
166 DBG std::cout << "out open: set_defaults\n";
167
168 auto compqual = m_spec.decode_compression_metadata("jpeg", 98);
169 if (Strutil::iequals(compqual.first, "jpeg"))
170 jpeg_set_quality(&m_cinfo, clamp(compqual.second, 1, 100), TRUE);
171 else
172 jpeg_set_quality(&m_cinfo, 98, TRUE); // not jpeg? default qual
173
174 if (m_cinfo.input_components == 3) {
175 std::string subsampling = m_spec.get_string_attribute(
176 JPEG_SUBSAMPLING_ATTR);
177 if (subsampling == JPEG_444_STR)
178 set_subsampling(JPEG_444_COMP);
179 else if (subsampling == JPEG_422_STR)
180 set_subsampling(JPEG_422_COMP);
181 else if (subsampling == JPEG_420_STR)
182 set_subsampling(JPEG_420_COMP);
183 else if (subsampling == JPEG_411_STR)
184 set_subsampling(JPEG_411_COMP);
185 }
186 DBG std::cout << "out open: set_colorspace\n";
187
188 // Save as a progressive jpeg if requested by the user
189 if (m_spec.get_int_attribute("jpeg:progressive")) {
190 jpeg_simple_progression(&m_cinfo);
191 }
192
193 jpeg_start_compress(&m_cinfo, TRUE); // start working
194 DBG std::cout << "out open: start_compress\n";
195 }
196 m_next_scanline = 0; // next scanline we'll write
197
198 // Write JPEG comment, if sent an 'ImageDescription'
199 ParamValue* comment = m_spec.find_attribute("ImageDescription",
200 TypeDesc::STRING);
201 if (comment && comment->data()) {
202 const char** c = (const char**)comment->data();
203 jpeg_write_marker(&m_cinfo, JPEG_COM, (JOCTET*)*c, strlen(*c) + 1);
204 }
205
206 if (Strutil::iequals(m_spec.get_string_attribute("oiio:ColorSpace"), "sRGB"))
207 m_spec.attribute("Exif:ColorSpace", 1);
208
209 // Write EXIF info
210 std::vector<char> exif;
211 // Start the blob with "Exif" and two nulls. That's how it
212 // always is in the JPEG files I've examined.
213 exif.push_back('E');
214 exif.push_back('x');
215 exif.push_back('i');
216 exif.push_back('f');
217 exif.push_back(0);
218 exif.push_back(0);
219 encode_exif(m_spec, exif);
220 jpeg_write_marker(&m_cinfo, JPEG_APP0 + 1, (JOCTET*)&exif[0], exif.size());
221
222 // Write IPTC IIM metadata tags, if we have anything
223 std::vector<char> iptc;
224 encode_iptc_iim(m_spec, iptc);
225 if (iptc.size()) {
226 static char photoshop[] = "Photoshop 3.0";
227 std::vector<char> head(photoshop, photoshop + strlen(photoshop) + 1);
228 static char _8BIM[] = "8BIM";
229 head.insert(head.end(), _8BIM, _8BIM + 4);
230 head.push_back(4); // 0x0404
231 head.push_back(4);
232 head.push_back(0); // four bytes of zeroes
233 head.push_back(0);
234 head.push_back(0);
235 head.push_back(0);
236 head.push_back((char)(iptc.size() >> 8)); // size of block
237 head.push_back((char)(iptc.size() & 0xff));
238 iptc.insert(iptc.begin(), head.begin(), head.end());
239 jpeg_write_marker(&m_cinfo, JPEG_APP0 + 13, (JOCTET*)&iptc[0],
240 iptc.size());
241 }
242
243 // Write XMP packet, if we have anything
244 std::string xmp = encode_xmp(m_spec, true);
245 if (!xmp.empty()) {
246 static char prefix[] = "http://ns.adobe.com/xap/1.0/";
247 std::vector<char> block(prefix, prefix + strlen(prefix) + 1);
248 block.insert(block.end(), xmp.c_str(), xmp.c_str() + xmp.length());
249 jpeg_write_marker(&m_cinfo, JPEG_APP0 + 1, (JOCTET*)&block[0],
250 block.size());
251 }
252
253 m_spec.set_format(TypeDesc::UINT8); // JPG is only 8 bit
254
255 // Write ICC profile, if we have anything
256 const ParamValue* icc_profile_parameter = m_spec.find_attribute(
257 ICC_PROFILE_ATTR);
258 if (icc_profile_parameter != NULL) {
259 unsigned char* icc_profile
260 = (unsigned char*)icc_profile_parameter->data();
261 unsigned int icc_profile_length = icc_profile_parameter->type().size();
262 if (icc_profile && icc_profile_length) {
263 /* Calculate the number of markers we'll need, rounding up of course */
264 int num_markers = icc_profile_length / MAX_DATA_BYTES_IN_MARKER;
265 if ((unsigned int)(num_markers * MAX_DATA_BYTES_IN_MARKER)
266 != icc_profile_length)
267 num_markers++;
268 int curr_marker = 1; /* per spec, count strarts at 1*/
269 size_t profile_size = MAX_DATA_BYTES_IN_MARKER + ICC_HEADER_SIZE;
270 std::vector<unsigned char> profile(profile_size);
271 while (icc_profile_length > 0) {
272 // length of profile to put in this marker
273 unsigned int length
274 = std::min(icc_profile_length,
275 (unsigned int)MAX_DATA_BYTES_IN_MARKER);
276 icc_profile_length -= length;
277 // Write the JPEG marker header (APP2 code and marker length)
278 strncpy((char*)&profile[0], "ICC_PROFILE", profile_size);
279 profile[11] = 0;
280 profile[12] = curr_marker;
281 profile[13] = (unsigned char)num_markers;
282 memcpy(&profile[0] + ICC_HEADER_SIZE,
283 icc_profile + length * (curr_marker - 1), length);
284 jpeg_write_marker(&m_cinfo, JPEG_APP0 + 2, &profile[0],
285 ICC_HEADER_SIZE + length);
286 curr_marker++;
287 }
288 }
289 }
290
291 m_dither = m_spec.get_int_attribute("oiio:dither", 0);
292
293 // If user asked for tiles -- which JPEG doesn't support, emulate it by
294 // buffering the whole image.
295 if (m_spec.tile_width && m_spec.tile_height)
296 m_tilebuffer.resize(m_spec.image_bytes());
297
298 return true;
299 }
300
301
302
303 void
resmeta_to_density()304 JpgOutput::resmeta_to_density()
305 {
306 string_view resunit = m_spec.get_string_attribute("ResolutionUnit");
307 if (Strutil::iequals(resunit, "none"))
308 m_cinfo.density_unit = 0;
309 else if (Strutil::iequals(resunit, "in"))
310 m_cinfo.density_unit = 1;
311 else if (Strutil::iequals(resunit, "cm"))
312 m_cinfo.density_unit = 2;
313 else
314 m_cinfo.density_unit = 0;
315
316 int X_density = int(m_spec.get_float_attribute("XResolution"));
317 int Y_density = int(m_spec.get_float_attribute("YResolution", X_density));
318 const float aspect = m_spec.get_float_attribute("PixelAspectRatio", 1.0f);
319 if (aspect != 1.0f && X_density <= 1 && Y_density <= 1) {
320 // No useful [XY]Resolution, but there is an aspect ratio requested.
321 // Arbitrarily pick 72 dots per undefined unit, and jigger it to
322 // honor it as best as we can.
323 //
324 // Here's where things get tricky. By logic and reason, as well as
325 // the JFIF spec and ITU T.871, the pixel aspect ratio is clearly
326 // ydensity/xdensity (because aspect is xlength/ylength, and density
327 // is 1/length). BUT... for reasons lost to history, a number of
328 // apps get this exactly backwards, and these include PhotoShop,
329 // Nuke, and RV. So, alas, we must replicate the mistake, or else
330 // all these common applications will misunderstand the JPEG files
331 // written by OIIO and vice versa.
332 Y_density = 72;
333 X_density = int(Y_density * aspect + 0.5f);
334 m_spec.attribute("XResolution", float(Y_density * aspect + 0.5f));
335 m_spec.attribute("YResolution", float(Y_density));
336 }
337 while (X_density > 65535 || Y_density > 65535) {
338 // JPEG header can store only UINT16 density values. If we
339 // overflow that limit, punt and knock it down to <= 16 bits.
340 X_density /= 2;
341 Y_density /= 2;
342 }
343 m_cinfo.X_density = X_density;
344 m_cinfo.Y_density = Y_density;
345 }
346
347
348
349 bool
write_scanline(int y,int z,TypeDesc format,const void * data,stride_t xstride)350 JpgOutput::write_scanline(int y, int z, TypeDesc format, const void* data,
351 stride_t xstride)
352 {
353 y -= m_spec.y;
354 if (y != m_next_scanline) {
355 errorf("Attempt to write scanlines out of order to %s", m_filename);
356 return false;
357 }
358 if (y >= (int)m_cinfo.image_height) {
359 errorf("Attempt to write too many scanlines to %s", m_filename);
360 return false;
361 }
362 assert(y == (int)m_cinfo.next_scanline);
363
364 // Here's where we do the dirty work of conforming to JFIF's limitation
365 // of 1 or 3 channels, by temporarily doctoring the spec so that
366 // to_native_scanline properly contiguizes the first 1 or 3 channels,
367 // then we restore it. The call to to_native_scanline below needs
368 // m_spec.nchannels to be set to the true number of channels we're
369 // writing, or it won't arrange the data properly. But if we doctored
370 // m_spec.nchannels permanently, then subsequent calls to write_scanline
371 // (including any surrounding call to write_image) with
372 // stride=AutoStride would screw up the strides since the user's stride
373 // is actually not 1 or 3 channels.
374 m_spec.auto_stride(xstride, format, m_spec.nchannels);
375 int save_nchannels = m_spec.nchannels;
376 m_spec.nchannels = m_cinfo.input_components;
377
378 data = to_native_scanline(format, data, xstride, m_scratch, m_dither, y, z);
379 m_spec.nchannels = save_nchannels;
380
381 jpeg_write_scanlines(&m_cinfo, (JSAMPLE**)&data, 1);
382 ++m_next_scanline;
383
384 return true;
385 }
386
387
388
389 bool
write_tile(int x,int y,int z,TypeDesc format,const void * data,stride_t xstride,stride_t ystride,stride_t zstride)390 JpgOutput::write_tile(int x, int y, int z, TypeDesc format, const void* data,
391 stride_t xstride, stride_t ystride, stride_t zstride)
392 {
393 // Emulate tiles by buffering the whole image
394 return copy_tile_to_image_buffer(x, y, z, format, data, xstride, ystride,
395 zstride, &m_tilebuffer[0]);
396 }
397
398
399
400 bool
close()401 JpgOutput::close()
402 {
403 if (!m_fd) { // Already closed
404 return true;
405 init();
406 }
407
408 bool ok = true;
409
410 if (m_spec.tile_width) {
411 // We've been emulating tiles; now dump as scanlines.
412 OIIO_DASSERT(m_tilebuffer.size());
413 ok &= write_scanlines(m_spec.y, m_spec.y + m_spec.height, 0,
414 m_spec.format, &m_tilebuffer[0]);
415 std::vector<unsigned char>().swap(m_tilebuffer); // free it
416 }
417
418 if (m_next_scanline < spec().height && m_copy_coeffs == NULL) {
419 // But if we've only written some scanlines, write the rest to avoid
420 // errors
421 std::vector<char> buf(spec().scanline_bytes(), 0);
422 char* data = &buf[0];
423 while (m_next_scanline < spec().height) {
424 jpeg_write_scanlines(&m_cinfo, (JSAMPLE**)&data, 1);
425 // DBG std::cout << "out close: write_scanlines\n";
426 ++m_next_scanline;
427 }
428 }
429
430 if (m_next_scanline >= spec().height || m_copy_coeffs) {
431 DBG std::cout << "out close: about to finish_compress\n";
432 jpeg_finish_compress(&m_cinfo);
433 DBG std::cout << "out close: finish_compress\n";
434 } else {
435 DBG std::cout << "out close: about to abort_compress\n";
436 jpeg_abort_compress(&m_cinfo);
437 DBG std::cout << "out close: abort_compress\n";
438 }
439 DBG std::cout << "out close: about to destroy_compress\n";
440 jpeg_destroy_compress(&m_cinfo);
441 fclose(m_fd);
442 m_fd = NULL;
443 init();
444
445 return ok;
446 }
447
448
449
450 bool
copy_image(ImageInput * in)451 JpgOutput::copy_image(ImageInput* in)
452 {
453 if (in && !strcmp(in->format_name(), "jpeg")) {
454 JpgInput* jpg_in = dynamic_cast<JpgInput*>(in);
455 std::string in_name = jpg_in->filename();
456 DBG std::cout << "JPG copy_image from " << in_name << "\n";
457
458 // Save the original input spec and close it
459 ImageSpec orig_in_spec = in->spec();
460 in->close();
461 DBG std::cout << "Closed old file\n";
462
463 // Re-open the input spec, with special request that the JpgInput
464 // will recognize as a request to merely open, but not start the
465 // decompressor.
466 ImageSpec in_spec;
467 ImageSpec config_spec;
468 config_spec.attribute("_jpeg:raw", 1);
469 in->open(in_name, in_spec, config_spec);
470
471 // Re-open the output
472 std::string out_name = m_filename;
473 ImageSpec orig_out_spec = spec();
474 close();
475 m_copy_coeffs = (jvirt_barray_ptr*)jpg_in->coeffs();
476 m_copy_decompressor = &jpg_in->m_cinfo;
477 open(out_name, orig_out_spec);
478
479 // Strangeness -- the write_coefficients somehow sets things up
480 // so that certain writes only happen in close(), which MUST
481 // happen while the input file is still open. So we go ahead
482 // and close() now, so that the caller of copy_image() doesn't
483 // close the input file first and then wonder why they crashed.
484 close();
485
486 return true;
487 }
488
489 return ImageOutput::copy_image(in);
490 }
491
492 OIIO_PLUGIN_NAMESPACE_END
493