1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 // libjpeg uses forbidden symbols in its header. Thus, we need to allow them
24 // here.
25 #define FORBIDDEN_SYMBOL_ALLOW_ALL
26
27 #include "image/jpeg.h"
28
29 #include "common/debug.h"
30 #include "common/endian.h"
31 #include "common/stream.h"
32 #include "common/textconsole.h"
33 #include "graphics/pixelformat.h"
34
35 #ifdef USE_JPEG
36 // The original release of libjpeg v6b did not contain any extern "C" in case
37 // its header files are included in a C++ environment. To avoid any linking
38 // issues we need to add it on our own.
39 extern "C" {
40 #include <jpeglib.h>
41 #include <jerror.h>
42 }
43 #endif
44
45 namespace Image {
46
JPEGDecoder()47 JPEGDecoder::JPEGDecoder() : _surface(), _colorSpace(kColorSpaceRGBA) {
48 }
49
~JPEGDecoder()50 JPEGDecoder::~JPEGDecoder() {
51 destroy();
52 }
53
getSurface() const54 const Graphics::Surface *JPEGDecoder::getSurface() const {
55 return &_surface;
56 }
57
destroy()58 void JPEGDecoder::destroy() {
59 _surface.free();
60 }
61
decodeFrame(Common::SeekableReadStream & stream)62 const Graphics::Surface *JPEGDecoder::decodeFrame(Common::SeekableReadStream &stream) {
63 if (!loadStream(stream))
64 return 0;
65
66 return getSurface();
67 }
68
getPixelFormat() const69 Graphics::PixelFormat JPEGDecoder::getPixelFormat() const {
70 return _surface.format;
71 }
72
73 #ifdef USE_JPEG
74 namespace {
75
76 #define JPEG_BUFFER_SIZE 4096
77
78 struct StreamSource : public jpeg_source_mgr {
79 Common::SeekableReadStream *stream;
80 bool startOfFile;
81 JOCTET buffer[JPEG_BUFFER_SIZE];
82 };
83
initSource(j_decompress_ptr cinfo)84 void initSource(j_decompress_ptr cinfo) {
85 StreamSource *source = (StreamSource *)cinfo->src;
86 source->startOfFile = true;
87 }
88
fillInputBuffer(j_decompress_ptr cinfo)89 boolean fillInputBuffer(j_decompress_ptr cinfo) {
90 StreamSource *source = (StreamSource *)cinfo->src;
91
92 uint32 bufferSize = source->stream->read((byte *)source->buffer, sizeof(source->buffer));
93 if (bufferSize == 0) {
94 if (source->startOfFile) {
95 // An empty file is a fatal error
96 ERREXIT(cinfo, JERR_INPUT_EMPTY);
97 } else {
98 // Otherwise we insert an EOF marker
99 WARNMS(cinfo, JWRN_JPEG_EOF);
100 source->buffer[0] = (JOCTET)0xFF;
101 source->buffer[1] = (JOCTET)JPEG_EOI;
102 bufferSize = 2;
103 }
104 }
105
106 source->next_input_byte = source->buffer;
107 source->bytes_in_buffer = bufferSize;
108 source->startOfFile = false;
109
110 return TRUE;
111 }
112
skipInputData(j_decompress_ptr cinfo,long numBytes)113 void skipInputData(j_decompress_ptr cinfo, long numBytes) {
114 StreamSource *source = (StreamSource *)cinfo->src;
115
116 if (numBytes > 0) {
117 if (numBytes > (long)source->bytes_in_buffer) {
118 // In case we need to skip more bytes than there are in the buffer
119 // we will skip the remaining data and fill the buffer again
120 numBytes -= (long)source->bytes_in_buffer;
121
122 // Skip the remaining bytes
123 source->stream->skip(numBytes);
124
125 // Fill up the buffer again
126 (*source->fill_input_buffer)(cinfo);
127 } else {
128 source->next_input_byte += (size_t)numBytes;
129 source->bytes_in_buffer -= (size_t)numBytes;
130 }
131
132 }
133 }
134
termSource(j_decompress_ptr cinfo)135 void termSource(j_decompress_ptr cinfo) {
136 }
137
jpeg_scummvm_src(j_decompress_ptr cinfo,Common::SeekableReadStream * stream)138 void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
139 StreamSource *source;
140
141 // Initialize the source in case it has not been done yet.
142 if (cinfo->src == NULL) {
143 cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(StreamSource));
144 }
145
146 source = (StreamSource *)cinfo->src;
147 source->init_source = &initSource;
148 source->fill_input_buffer = &fillInputBuffer;
149 source->skip_input_data = &skipInputData;
150 source->resync_to_restart = &jpeg_resync_to_restart;
151 source->term_source = &termSource;
152 source->bytes_in_buffer = 0;
153 source->next_input_byte = NULL;
154
155 source->stream = stream;
156 }
157
errorExit(j_common_ptr cinfo)158 void errorExit(j_common_ptr cinfo) {
159 char buffer[JMSG_LENGTH_MAX];
160 (*cinfo->err->format_message)(cinfo, buffer);
161 // This function is not allowed to return to the caller, thus we simply
162 // error out with our error handling here.
163 error("%s", buffer);
164 }
165
outputMessage(j_common_ptr cinfo)166 void outputMessage(j_common_ptr cinfo) {
167 char buffer[JMSG_LENGTH_MAX];
168 (*cinfo->err->format_message)(cinfo, buffer);
169 // Is using debug here a good idea? Or do we want to ignore all libjpeg
170 // messages?
171 debug(3, "libjpeg: %s", buffer);
172 }
173
174 } // End of anonymous namespace
175 #endif
176
loadStream(Common::SeekableReadStream & stream)177 bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
178 #ifdef USE_JPEG
179 // Reset member variables from previous decodings
180 destroy();
181
182 jpeg_decompress_struct cinfo;
183 jpeg_error_mgr jerr;
184
185 // Initialize error handling callbacks
186 cinfo.err = jpeg_std_error(&jerr);
187 cinfo.err->error_exit = &errorExit;
188 cinfo.err->output_message = &outputMessage;
189
190 // Initialize the decompression structure
191 jpeg_create_decompress(&cinfo);
192
193 // Initialize our buffer handling
194 jpeg_scummvm_src(&cinfo, &stream);
195
196 // Read the file header
197 jpeg_read_header(&cinfo, TRUE);
198
199 // We can request YUV output because Groovie requires it
200 switch (_colorSpace) {
201 case kColorSpaceRGBA:
202 cinfo.out_color_space = JCS_RGB;
203 break;
204
205 case kColorSpaceYUV:
206 cinfo.out_color_space = JCS_YCbCr;
207 break;
208 }
209
210 // Actually start decompressing the image
211 jpeg_start_decompress(&cinfo);
212
213 // Allocate buffers for the output data
214 switch (_colorSpace) {
215 case kColorSpaceRGBA:
216 // We use RGBA8888 in this scenario
217 _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
218 break;
219
220 case kColorSpaceYUV:
221 // We use YUV with 3 bytes per pixel otherwise.
222 // This is pretty ugly since our PixelFormat cannot express YUV...
223 _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
224 break;
225 }
226
227 // Allocate buffer for one scanline
228 assert(cinfo.output_components == 3);
229 JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
230 assert(_surface.pitch >= pitch);
231 JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
232
233 // Go through the image data scanline by scanline
234 while (cinfo.output_scanline < cinfo.output_height) {
235 byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
236
237 jpeg_read_scanlines(&cinfo, buffer, 1);
238
239 const byte *src = buffer[0];
240 switch (_colorSpace) {
241 case kColorSpaceRGBA: {
242 for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
243 byte r = *src++;
244 byte g = *src++;
245 byte b = *src++;
246 // We need to insert a alpha value of 255 (opaque) here.
247 #ifdef SCUMM_BIG_ENDIAN
248 *dst++ = r;
249 *dst++ = g;
250 *dst++ = b;
251 *dst++ = 0xFF;
252 #else
253 *dst++ = 0xFF;
254 *dst++ = b;
255 *dst++ = g;
256 *dst++ = r;
257 #endif
258 }
259 } break;
260
261 case kColorSpaceYUV:
262 memcpy(dst, src, pitch);
263 break;
264 }
265 }
266
267 // We are done with decompressing, thus free all the data
268 jpeg_finish_decompress(&cinfo);
269 jpeg_destroy_decompress(&cinfo);
270
271 return true;
272 #else
273 return false;
274 #endif
275 }
276
277 } // End of Graphics namespace
278