1 #include <mbgl/util/image.hpp>
2 #include <mbgl/util/char_array_buffer.hpp>
3 
4 #include <istream>
5 #include <sstream>
6 #include <array>
7 
8 extern "C"
9 {
10 #include <jpeglib.h>
11 }
12 
13 namespace mbgl {
14 
15 const static unsigned BUF_SIZE = 4096;
16 
17 struct jpeg_stream_wrapper {
18     jpeg_source_mgr manager;
19     std::istream* stream;
20     std::array<JOCTET, BUF_SIZE> buffer;
21 };
22 
init_source(j_decompress_ptr cinfo)23 static void init_source(j_decompress_ptr cinfo) {
24     auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
25     wrap->stream->seekg(0, std::ios_base::beg);
26 }
27 
fill_input_buffer(j_decompress_ptr cinfo)28 static boolean fill_input_buffer(j_decompress_ptr cinfo) {
29     auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
30     wrap->stream->read(reinterpret_cast<char*>(&wrap->buffer[0]), BUF_SIZE);
31     std::streamsize size = wrap->stream->gcount();
32     wrap->manager.next_input_byte = wrap->buffer.data();
33     wrap->manager.bytes_in_buffer = BUF_SIZE;
34     return (size > 0) ? TRUE : FALSE;
35 }
36 
skip(j_decompress_ptr cinfo,long count)37 static void skip(j_decompress_ptr cinfo, long count) {
38     if (count <= 0) return; // A zero or negative skip count should be treated as a no-op.
39     auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
40 
41     if (wrap->manager.bytes_in_buffer > 0 && count < static_cast<long>(wrap->manager.bytes_in_buffer))
42     {
43         wrap->manager.bytes_in_buffer -= count;
44         wrap->manager.next_input_byte = &wrap->buffer[BUF_SIZE - wrap->manager.bytes_in_buffer];
45     }
46     else
47     {
48         wrap->stream->seekg(count - wrap->manager.bytes_in_buffer, std::ios_base::cur);
49         // trigger buffer fill
50         wrap->manager.next_input_byte = nullptr;
51         wrap->manager.bytes_in_buffer = 0; // bytes_in_buffer may be zero on return.
52     }
53 }
54 
term(j_decompress_ptr)55 static void term(j_decompress_ptr) {}
56 
attach_stream(j_decompress_ptr cinfo,std::istream * in)57 static void attach_stream(j_decompress_ptr cinfo, std::istream* in) {
58     if (cinfo->src == nullptr) {
59         cinfo->src = (struct jpeg_source_mgr *)
60             (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper));
61     }
62     auto * src = reinterpret_cast<jpeg_stream_wrapper*> (cinfo->src);
63     src->manager.init_source = init_source;
64     src->manager.fill_input_buffer = fill_input_buffer;
65     src->manager.skip_input_data = skip;
66     src->manager.resync_to_restart = jpeg_resync_to_restart;
67     src->manager.term_source = term;
68     src->manager.bytes_in_buffer = 0;
69     src->manager.next_input_byte = nullptr;
70     src->stream = in;
71 }
72 
on_error(j_common_ptr)73 static void on_error(j_common_ptr) {}
74 
on_error_message(j_common_ptr cinfo)75 static void on_error_message(j_common_ptr cinfo) {
76     char buffer[JMSG_LENGTH_MAX];
77     (*cinfo->err->format_message)(cinfo, buffer);
78     throw std::runtime_error(std::string("JPEG Reader: libjpeg could not read image: ") + buffer);
79 }
80 
81 struct jpeg_info_guard {
jpeg_info_guardmbgl::jpeg_info_guard82     jpeg_info_guard(jpeg_decompress_struct* cinfo)
83         : i_(cinfo) {}
84 
~jpeg_info_guardmbgl::jpeg_info_guard85     ~jpeg_info_guard() {
86         jpeg_destroy_decompress(i_);
87     }
88 
89     jpeg_decompress_struct* i_;
90 };
91 
decodeJPEG(const uint8_t * data,size_t size)92 PremultipliedImage decodeJPEG(const uint8_t* data, size_t size) {
93     util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size };
94     std::istream stream(&dataBuffer);
95 
96     jpeg_decompress_struct cinfo;
97     jpeg_info_guard iguard(&cinfo);
98     jpeg_error_mgr jerr;
99     cinfo.err = jpeg_std_error(&jerr);
100     jerr.error_exit = on_error;
101     jerr.output_message = on_error_message;
102     jpeg_create_decompress(&cinfo);
103     attach_stream(&cinfo, &stream);
104 
105     int ret = jpeg_read_header(&cinfo, TRUE);
106     if (ret != JPEG_HEADER_OK)
107         throw std::runtime_error("JPEG Reader: failed to read header");
108 
109     jpeg_start_decompress(&cinfo);
110 
111     if (cinfo.out_color_space == JCS_UNKNOWN)
112         throw std::runtime_error("JPEG Reader: failed to read unknown color space");
113 
114     if (cinfo.output_width == 0 || cinfo.output_height == 0)
115         throw std::runtime_error("JPEG Reader: failed to read image size");
116 
117     size_t width = cinfo.output_width;
118     size_t height = cinfo.output_height;
119     size_t components = cinfo.output_components;
120     size_t rowStride = components * width;
121 
122     PremultipliedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
123     uint8_t* dst = image.data.get();
124 
125     JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowStride, 1);
126 
127     while (cinfo.output_scanline < cinfo.output_height) {
128         jpeg_read_scanlines(&cinfo, buffer, 1);
129 
130         for (size_t i = 0; i < width; ++i) {
131             dst[0] = buffer[0][components * i];
132             dst[3] = 0xFF;
133 
134             if (components > 2) {
135                 dst[1] = buffer[0][components * i + 1];
136                 dst[2] = buffer[0][components * i + 2];
137             } else {
138                 dst[1] = dst[0];
139                 dst[2] = dst[0];
140             }
141 
142             dst += 4;
143         }
144     }
145 
146     jpeg_finish_decompress(&cinfo);
147 
148     return image;
149 }
150 
151 } // namespace mbgl
152