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