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