1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4365)
30 
31 namespace jpeglibNamespace
32 {
33 #if JUCE_INCLUDE_JPEGLIB_CODE || ! defined (JUCE_INCLUDE_JPEGLIB_CODE)
34     #if JUCE_MINGW
35      typedef unsigned char boolean;
36     #endif
37 
38      JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion",
39                                           "-Wdeprecated-register",
40                                           "-Wdeprecated-declarations",
41                                           "-Wsign-conversion",
42                                           "-Wcast-align",
43                                           "-Wswitch-enum",
44                                           "-Wswitch-default",
45                                           "-Wimplicit-fallthrough",
46                                           "-Wzero-as-null-pointer-constant",
47                                           "-Wshift-negative-value",
48                                           "-Wcomma")
49 
50     #define JPEG_INTERNALS
51     #undef FAR
52     #include "jpglib/jpeglib.h"
53 
54     #include "jpglib/jcapimin.c"
55     #include "jpglib/jcapistd.c"
56     #include "jpglib/jccoefct.c"
57     #include "jpglib/jccolor.c"
58     #undef FIX
59     #include "jpglib/jcdctmgr.c"
60     #undef CONST_BITS
61     #include "jpglib/jchuff.c"
62     #undef emit_byte
63     #include "jpglib/jcinit.c"
64     #include "jpglib/jcmainct.c"
65     #include "jpglib/jcmarker.c"
66     #include "jpglib/jcmaster.c"
67     #include "jpglib/jcomapi.c"
68     #include "jpglib/jcparam.c"
69     #include "jpglib/jcphuff.c"
70     #include "jpglib/jcprepct.c"
71     #include "jpglib/jcsample.c"
72     #include "jpglib/jctrans.c"
73     #include "jpglib/jdapistd.c"
74     #include "jpglib/jdapimin.c"
75     #include "jpglib/jdatasrc.c"
76     #include "jpglib/jdcoefct.c"
77     #undef FIX
78     #include "jpglib/jdcolor.c"
79     #undef FIX
80     #include "jpglib/jddctmgr.c"
81     #undef CONST_BITS
82     #undef ASSIGN_STATE
83     #include "jpglib/jdhuff.c"
84     #include "jpglib/jdinput.c"
85     #include "jpglib/jdmainct.c"
86     #include "jpglib/jdmarker.c"
87     #include "jpglib/jdmaster.c"
88     #undef FIX
89     #include "jpglib/jdmerge.c"
90     #undef ASSIGN_STATE
91     #include "jpglib/jdphuff.c"
92     #include "jpglib/jdpostct.c"
93     #undef FIX
94     #include "jpglib/jdsample.c"
95     #include "jpglib/jdtrans.c"
96     #include "jpglib/jfdctflt.c"
97     #include "jpglib/jfdctint.c"
98     #undef CONST_BITS
99     #undef MULTIPLY
100     #undef FIX_0_541196100
101     #include "jpglib/jfdctfst.c"
102     #undef FIX_0_541196100
103     #include "jpglib/jidctflt.c"
104     #undef CONST_BITS
105     #undef FIX_1_847759065
106     #undef MULTIPLY
107     #undef DEQUANTIZE
108     #undef DESCALE
109     #include "jpglib/jidctfst.c"
110     #undef CONST_BITS
111     #undef FIX_1_847759065
112     #undef MULTIPLY
113     #undef DEQUANTIZE
114     #include "jpglib/jidctint.c"
115     #include "jpglib/jidctred.c"
116     #include "jpglib/jmemmgr.c"
117     #include "jpglib/jmemnobs.c"
118     #include "jpglib/jquant1.c"
119     #include "jpglib/jquant2.c"
120     #include "jpglib/jutils.c"
121     #include "jpglib/transupp.c"
122 
123     JUCE_END_IGNORE_WARNINGS_GCC_LIKE
124 #else
125     #define JPEG_INTERNALS
126     #undef FAR
127     #include <jpeglib.h>
128 #endif
129 }
130 
131 #undef max
132 #undef min
133 
134 JUCE_END_IGNORE_WARNINGS_MSVC
135 
136 //==============================================================================
137 namespace JPEGHelpers
138 {
139     using namespace jpeglibNamespace;
140 
141    #if ! (JUCE_WINDOWS && (JUCE_MSVC || JUCE_CLANG))
142     using jpeglibNamespace::boolean;
143    #endif
144 
fatalErrorHandler(j_common_ptr p)145     static void fatalErrorHandler (j_common_ptr p)          { *((bool*) (p->client_data)) = true; }
silentErrorCallback1(j_common_ptr)146     static void silentErrorCallback1 (j_common_ptr)         {}
silentErrorCallback2(j_common_ptr,int)147     static void silentErrorCallback2 (j_common_ptr, int)    {}
silentErrorCallback3(j_common_ptr,char *)148     static void silentErrorCallback3 (j_common_ptr, char*)  {}
149 
setupSilentErrorHandler(struct jpeg_error_mgr & err)150     static void setupSilentErrorHandler (struct jpeg_error_mgr& err)
151     {
152         zerostruct (err);
153 
154         err.error_exit      = fatalErrorHandler;
155         err.emit_message    = silentErrorCallback2;
156         err.output_message  = silentErrorCallback1;
157         err.format_message  = silentErrorCallback3;
158         err.reset_error_mgr = silentErrorCallback1;
159     }
160 
161     //==============================================================================
162    #if ! JUCE_USING_COREIMAGE_LOADER
dummyCallback1(j_decompress_ptr)163     static void dummyCallback1 (j_decompress_ptr) {}
164 
jpegSkip(j_decompress_ptr decompStruct,long num)165     static void jpegSkip (j_decompress_ptr decompStruct, long num)
166     {
167         decompStruct->src->next_input_byte += num;
168 
169         num = jmin (num, (long) decompStruct->src->bytes_in_buffer);
170         decompStruct->src->bytes_in_buffer -= (size_t) num;
171     }
172 
jpegFill(j_decompress_ptr)173     static boolean jpegFill (j_decompress_ptr)
174     {
175         return 0;
176     }
177    #endif
178 
179     //==============================================================================
180     const int jpegBufferSize = 512;
181 
182     struct JuceJpegDest  : public jpeg_destination_mgr
183     {
184         OutputStream* output;
185         char* buffer;
186     };
187 
jpegWriteInit(j_compress_ptr)188     static void jpegWriteInit (j_compress_ptr) {}
189 
jpegWriteTerminate(j_compress_ptr cinfo)190     static void jpegWriteTerminate (j_compress_ptr cinfo)
191     {
192         JuceJpegDest* const dest = static_cast<JuceJpegDest*> (cinfo->dest);
193 
194         const size_t numToWrite = jpegBufferSize - dest->free_in_buffer;
195         dest->output->write (dest->buffer, numToWrite);
196     }
197 
jpegWriteFlush(j_compress_ptr cinfo)198     static boolean jpegWriteFlush (j_compress_ptr cinfo)
199     {
200         JuceJpegDest* const dest = static_cast<JuceJpegDest*> (cinfo->dest);
201 
202         const int numToWrite = jpegBufferSize;
203 
204         dest->next_output_byte = reinterpret_cast<JOCTET*> (dest->buffer);
205         dest->free_in_buffer = jpegBufferSize;
206 
207         return (boolean) dest->output->write (dest->buffer, (size_t) numToWrite);
208     }
209 }
210 
211 //==============================================================================
JPEGImageFormat()212 JPEGImageFormat::JPEGImageFormat()
213     : quality (-1.0f)
214 {
215 }
216 
~JPEGImageFormat()217 JPEGImageFormat::~JPEGImageFormat() {}
218 
setQuality(const float newQuality)219 void JPEGImageFormat::setQuality (const float newQuality)
220 {
221     quality = newQuality;
222 }
223 
getFormatName()224 String JPEGImageFormat::getFormatName()                   { return "JPEG"; }
usesFileExtension(const File & f)225 bool JPEGImageFormat::usesFileExtension (const File& f)   { return f.hasFileExtension ("jpeg;jpg"); }
226 
canUnderstand(InputStream & in)227 bool JPEGImageFormat::canUnderstand (InputStream& in)
228 {
229     const int bytesNeeded = 24;
230     uint8 header [bytesNeeded];
231 
232     if (in.read (header, bytesNeeded) == bytesNeeded
233             && header[0] == 0xff
234             && header[1] == 0xd8
235             && header[2] == 0xff)
236         return true;
237 
238    #if JUCE_USING_COREIMAGE_LOADER
239     return header[20] == 'j'
240         && header[21] == 'p'
241         && header[22] == '2'
242         && header[23] == ' ';
243    #endif
244 
245     return false;
246 }
247 
248 #if JUCE_USING_COREIMAGE_LOADER
249  Image juce_loadWithCoreImage (InputStream& input);
250 #endif
251 
decodeImage(InputStream & in)252 Image JPEGImageFormat::decodeImage (InputStream& in)
253 {
254 #if JUCE_USING_COREIMAGE_LOADER
255     return juce_loadWithCoreImage (in);
256 #else
257     using namespace jpeglibNamespace;
258     using namespace JPEGHelpers;
259 
260     MemoryOutputStream mb;
261     mb << in;
262 
263     Image image;
264 
265     if (mb.getDataSize() > 16)
266     {
267         struct jpeg_decompress_struct jpegDecompStruct;
268 
269         struct jpeg_error_mgr jerr;
270         setupSilentErrorHandler (jerr);
271         jpegDecompStruct.err = &jerr;
272 
273         jpeg_create_decompress (&jpegDecompStruct);
274 
275         jpegDecompStruct.src = (jpeg_source_mgr*)(jpegDecompStruct.mem->alloc_small)
276             ((j_common_ptr)(&jpegDecompStruct), JPOOL_PERMANENT, sizeof (jpeg_source_mgr));
277 
278         bool hasFailed = false;
279         jpegDecompStruct.client_data = &hasFailed;
280 
281         jpegDecompStruct.src->init_source       = dummyCallback1;
282         jpegDecompStruct.src->fill_input_buffer = jpegFill;
283         jpegDecompStruct.src->skip_input_data   = jpegSkip;
284         jpegDecompStruct.src->resync_to_restart = jpeg_resync_to_restart;
285         jpegDecompStruct.src->term_source       = dummyCallback1;
286 
287         jpegDecompStruct.src->next_input_byte   = static_cast<const unsigned char*> (mb.getData());
288         jpegDecompStruct.src->bytes_in_buffer   = mb.getDataSize();
289 
290         jpeg_read_header (&jpegDecompStruct, TRUE);
291 
292         if (! hasFailed)
293         {
294             jpeg_calc_output_dimensions (&jpegDecompStruct);
295 
296             if (! hasFailed)
297             {
298                 const int width  = (int) jpegDecompStruct.output_width;
299                 const int height = (int) jpegDecompStruct.output_height;
300 
301                 jpegDecompStruct.out_color_space = JCS_RGB;
302 
303                 JSAMPARRAY buffer
304                     = (*jpegDecompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegDecompStruct,
305                                                              JPOOL_IMAGE,
306                                                              (JDIMENSION) width * 3, 1);
307 
308                 if (jpeg_start_decompress (&jpegDecompStruct) && ! hasFailed)
309                 {
310                     image = Image (Image::RGB, width, height, false);
311                     image.getProperties()->set ("originalImageHadAlpha", false);
312                     const bool hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect)
313 
314                     const Image::BitmapData destData (image, Image::BitmapData::writeOnly);
315 
316                     for (int y = 0; y < height; ++y)
317                     {
318                         jpeg_read_scanlines (&jpegDecompStruct, buffer, 1);
319 
320                         if (hasFailed)
321                             break;
322 
323                         const uint8* src = *buffer;
324                         uint8* dest = destData.getLinePointer (y);
325 
326                         if (hasAlphaChan)
327                         {
328                             for (int i = width; --i >= 0;)
329                             {
330                                 ((PixelARGB*) dest)->setARGB (0xff, src[0], src[1], src[2]);
331                                 ((PixelARGB*) dest)->premultiply();
332                                 dest += destData.pixelStride;
333                                 src += 3;
334                             }
335                         }
336                         else
337                         {
338                             for (int i = width; --i >= 0;)
339                             {
340                                 ((PixelRGB*) dest)->setARGB (0xff, src[0], src[1], src[2]);
341                                 dest += destData.pixelStride;
342                                 src += 3;
343                             }
344                         }
345                     }
346 
347                     if (! hasFailed)
348                         jpeg_finish_decompress (&jpegDecompStruct);
349 
350                     in.setPosition (((char*) jpegDecompStruct.src->next_input_byte) - (char*) mb.getData());
351                 }
352             }
353         }
354 
355         jpeg_destroy_decompress (&jpegDecompStruct);
356     }
357 
358     return image;
359 #endif
360 }
361 
writeImageToStream(const Image & image,OutputStream & out)362 bool JPEGImageFormat::writeImageToStream (const Image& image, OutputStream& out)
363 {
364     using namespace jpeglibNamespace;
365     using namespace JPEGHelpers;
366 
367     jpeg_compress_struct jpegCompStruct;
368     zerostruct (jpegCompStruct);
369     jpeg_create_compress (&jpegCompStruct);
370 
371     struct jpeg_error_mgr jerr;
372     setupSilentErrorHandler (jerr);
373     jpegCompStruct.err = &jerr;
374 
375     JuceJpegDest dest;
376     jpegCompStruct.dest = &dest;
377 
378     dest.output = &out;
379     HeapBlock<char> tempBuffer (jpegBufferSize);
380     dest.buffer = tempBuffer;
381     dest.next_output_byte = (JOCTET*) dest.buffer;
382     dest.free_in_buffer = jpegBufferSize;
383     dest.init_destination = jpegWriteInit;
384     dest.empty_output_buffer = jpegWriteFlush;
385     dest.term_destination = jpegWriteTerminate;
386 
387     jpegCompStruct.image_width  = (JDIMENSION) image.getWidth();
388     jpegCompStruct.image_height = (JDIMENSION) image.getHeight();
389     jpegCompStruct.input_components = 3;
390     jpegCompStruct.in_color_space = JCS_RGB;
391     jpegCompStruct.write_JFIF_header = 1;
392 
393     jpegCompStruct.X_density = 72;
394     jpegCompStruct.Y_density = 72;
395 
396     jpeg_set_defaults (&jpegCompStruct);
397 
398     jpegCompStruct.dct_method = JDCT_FLOAT;
399     jpegCompStruct.optimize_coding = 1;
400 
401     if (quality < 0.0f)
402         quality = 0.85f;
403 
404     jpeg_set_quality (&jpegCompStruct, jlimit (0, 100, roundToInt (quality * 100.0f)), TRUE);
405 
406     jpeg_start_compress (&jpegCompStruct, TRUE);
407 
408     const int strideBytes = (int) (jpegCompStruct.image_width * (unsigned int) jpegCompStruct.input_components);
409 
410     JSAMPARRAY buffer = (*jpegCompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegCompStruct,
411                                                              JPOOL_IMAGE, (JDIMENSION) strideBytes, 1);
412 
413     const Image::BitmapData srcData (image, Image::BitmapData::readOnly);
414 
415     while (jpegCompStruct.next_scanline < jpegCompStruct.image_height)
416     {
417         uint8* dst = *buffer;
418 
419         if (srcData.pixelFormat == Image::RGB)
420         {
421             const uint8* src = srcData.getLinePointer ((int) jpegCompStruct.next_scanline);
422 
423             for (int i = srcData.width; --i >= 0;)
424             {
425                 *dst++ = ((const PixelRGB*) src)->getRed();
426                 *dst++ = ((const PixelRGB*) src)->getGreen();
427                 *dst++ = ((const PixelRGB*) src)->getBlue();
428                 src += srcData.pixelStride;
429             }
430         }
431         else
432         {
433             for (int x = 0; x < srcData.width; ++x)
434             {
435                 const Colour pixel (srcData.getPixelColour (x, (int) jpegCompStruct.next_scanline));
436                 *dst++ = pixel.getRed();
437                 *dst++ = pixel.getGreen();
438                 *dst++ = pixel.getBlue();
439             }
440         }
441 
442         jpeg_write_scanlines (&jpegCompStruct, buffer, 1);
443     }
444 
445     jpeg_finish_compress (&jpegCompStruct);
446     jpeg_destroy_compress (&jpegCompStruct);
447 
448     return true;
449 }
450 
451 } // namespace juce
452