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