update copyright date
[gnash.git] / libbase / GnashImageJpeg.cpp
blob3165cde6159713d36c22cccc72eef2211d5922b0
1 // GnashImageJpeg.cpp: Jpeg reader, for Gnash.
2 //
3 // Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 // Original version by Thatcher Ulrich <tu@tulrich.com> 2002
22 // Wrapper for jpeg file operations. The actual work is done by the
23 // IJG jpeg lib.
25 #ifdef HAVE_CONFIG_H
26 #include "gnashconfig.h"
27 #endif
29 #include "GnashImageJpeg.h"
31 #include <sstream>
32 #include <csetjmp>
33 #include <boost/noncopyable.hpp>
34 #include <algorithm>
36 #include "utility.h"
37 #include "GnashImage.h"
38 #include "IOChannel.h"
39 #include "log.h"
40 #include "GnashException.h"
43 // jpeglib.h redefines HAVE_STDLIB_H.
44 #undef HAVE_STDLIB_H
45 extern "C" {
46 # include <jpeglib.h>
48 #undef HAVE_STDLIB_H
50 namespace gnash {
51 namespace image {
53 namespace {
55 void
56 jpeg_error_exit(j_common_ptr cinfo)
58 // Set a flag to stop parsing
59 JpegInput* in = static_cast<JpegInput*>(cinfo->client_data);
60 in->errorOccurred(cinfo->err->jpeg_message_table[cinfo->err->msg_code]);
64 // Set up some error handlers for the jpeg lib.
65 void
66 setup_jpeg_err(jpeg_error_mgr* jerr)
68 // Set up defaults.
69 jpeg_std_error(jerr);
70 jerr->error_exit = jpeg_error_exit;
73 static const int IO_BUF_SIZE = 4096;
75 // A jpeglib source manager that reads from a IOChannel. Paraphrased
76 // from IJG jpeglib jdatasrc.c.
77 class rw_source_IOChannel : boost::noncopyable
79 public:
80 jpeg_source_mgr m_pub; /* public fields */
82 // Constructor.
83 explicit rw_source_IOChannel(boost::shared_ptr<IOChannel> in)
85 _ownSourceStream(false),
86 m_in_stream(in),
87 m_start_of_file(true)
89 init();
92 ~rw_source_IOChannel()
96 static void init_source(j_decompress_ptr cinfo)
98 rw_source_IOChannel* src = (rw_source_IOChannel*) cinfo->src;
99 src->m_start_of_file = true;
102 // Read data into our input buffer. Client calls this
103 // when it needs more data from the file.
104 static boolean fill_input_buffer(j_decompress_ptr cinfo)
106 rw_source_IOChannel* src = (rw_source_IOChannel*) cinfo->src;
108 // TODO: limit read as requested by caller
109 size_t bytes_read = src->m_in_stream->read(src->m_buffer, IO_BUF_SIZE);
111 if (bytes_read <= 0) {
112 // Is the file completely empty?
113 if (src->m_start_of_file) {
114 // Treat this as a fatal error.
115 log_error(_("JPEG: Empty jpeg source stream."));
116 return false;
119 // Insert a fake EOI marker.
120 src->m_buffer[0] = 0xFF;
121 src->m_buffer[1] = JPEG_EOI;
122 bytes_read = 2;
125 // Hack to work around SWF bug: sometimes data
126 // starts with FFD9FFD8, when it should be
127 // FFD8FFD9!
128 if (src->m_start_of_file && bytes_read >= 4) {
130 const JOCTET wrong[] = { 0xff, 0xd9, 0xff, 0xd8 };
132 if (std::equal(src->m_buffer, src->m_buffer + 4, wrong)) {
133 std::swap(src->m_buffer[1], src->m_buffer[3]);
137 // Expose buffer state to clients.
138 src->m_pub.next_input_byte = src->m_buffer;
139 src->m_pub.bytes_in_buffer = bytes_read;
140 src->m_start_of_file = false;
142 return true;
145 // Called by client when it wants to advance past some
146 // uninteresting data.
147 static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
149 rw_source_IOChannel* src = (rw_source_IOChannel*) cinfo->src;
151 // According to jpeg docs, large skips are
152 // infrequent. So let's just do it the simple
153 // way.
154 if (num_bytes > 0) {
155 while (num_bytes > static_cast<long>(src->m_pub.bytes_in_buffer)) {
156 num_bytes -= static_cast<long>(src->m_pub.bytes_in_buffer);
157 fill_input_buffer(cinfo);
159 // Handle remainder.
160 src->m_pub.next_input_byte += (size_t) num_bytes;
161 src->m_pub.bytes_in_buffer -= (size_t) num_bytes;
165 static void term_source(j_decompress_ptr /* cinfo */)
170 void discardPartialBuffer()
172 // Discard existing bytes in our buffer.
173 m_pub.bytes_in_buffer = 0;
174 m_pub.next_input_byte = NULL;
177 /// Set up the given decompress object to read from the given
178 /// stream.
180 /// @param instream
181 /// Stream to read from. Ownership always shared with caller.
183 static void setup(jpeg_decompress_struct* cinfo,
184 boost::shared_ptr<IOChannel> instream)
186 rw_source_IOChannel* source = new rw_source_IOChannel(instream);
187 cinfo->src = (jpeg_source_mgr*)source;
190 private:
192 void init()
194 // fill in function pointers...
195 m_pub.init_source = init_source;
196 m_pub.fill_input_buffer = fill_input_buffer;
197 m_pub.skip_input_data = skip_input_data;
198 // use default method
199 m_pub.resync_to_restart = jpeg_resync_to_restart;
200 m_pub.term_source = term_source;
201 m_pub.bytes_in_buffer = 0;
202 m_pub.next_input_byte = NULL;
205 const bool _ownSourceStream;
207 // Source stream
208 boost::shared_ptr<IOChannel> m_in_stream;
209 bool m_start_of_file;
210 JOCTET m_buffer[IO_BUF_SIZE];
214 } // unnamed namespace
216 JpegInput::JpegInput(boost::shared_ptr<IOChannel> in)
218 Input(in),
219 _errorOccurred(0),
220 _compressorOpened(false)
222 setup_jpeg_err(&m_jerr);
223 m_cinfo.err = &m_jerr;
224 m_cinfo.client_data = this;
226 // Initialize decompression object.
227 jpeg_create_decompress(&m_cinfo);
229 rw_source_IOChannel::setup(&m_cinfo, in);
233 JpegInput::~JpegInput()
235 rw_source_IOChannel* src =
236 reinterpret_cast<rw_source_IOChannel*>(m_cinfo.src);
238 delete src;
239 m_cinfo.src = NULL;
241 jpeg_destroy_decompress(&m_cinfo);
245 void
246 JpegInput::discardPartialBuffer()
248 rw_source_IOChannel* src = (rw_source_IOChannel*) m_cinfo.src;
250 // We only have to discard the input buffer after reading the tables.
251 if (src) {
252 src->discardPartialBuffer();
257 void
258 JpegInput::readHeader(unsigned int maxHeaderBytes)
260 if (setjmp(_jmpBuf)) {
261 std::stringstream ss;
262 ss << _("Internal jpeg error: ") << _errorOccurred;
263 throw ParserException(ss.str());
266 if (maxHeaderBytes) {
267 // Read the encoding tables.
268 // TODO: how to limit reads ?
269 const int ret = jpeg_read_header(&m_cinfo, FALSE);
270 switch (ret) {
271 case JPEG_SUSPENDED:
272 // suspended due to lack of data
273 throw ParserException(_("Lack of data during JPEG "
274 "header parsing"));
275 break;
276 case JPEG_HEADER_OK:
277 // Found valid image datastream
278 break;
279 case JPEG_HEADER_TABLES_ONLY:
280 // Found valid table-specs-only datastream
281 break;
282 default:
283 log_debug(_("unexpected: jpeg_read_header returned %d"), ret);
284 break;
287 if (_errorOccurred) {
288 std::stringstream ss;
289 ss << _("Internal jpeg error: ") << _errorOccurred;
290 throw ParserException(ss.str());
295 // Don't start reading any image data!
296 // App does that manually using start_image.
300 void
301 JpegInput::read()
303 assert(!_compressorOpened);
305 if (setjmp(_jmpBuf)) {
306 std::stringstream ss;
307 ss << _("Internal jpeg error: ") << _errorOccurred;
308 throw ParserException(ss.str());
311 // hack, FIXME
312 // found SOS, ready for start_decompress
313 static const int stateReady = 202;
314 while (m_cinfo.global_state != stateReady) {
315 const int ret = jpeg_read_header(&m_cinfo, FALSE);
316 switch (ret) {
317 case JPEG_SUSPENDED:
318 // suspended due to lack of data
319 throw ParserException(_("lack of data during JPEG "
320 "header parsing"));
321 break;
322 case JPEG_HEADER_OK:
323 // Found valid image datastream
324 break;
325 case JPEG_HEADER_TABLES_ONLY:
326 // Found valid table-specs-only datastream
327 break;
328 default:
329 log_debug(_("unexpected: jpeg_read_header returned %d [%s:%d]"),
330 ret, __FILE__, __LINE__);
331 break;
335 if (_errorOccurred) {
336 std::stringstream ss;
337 ss << _("Internal jpeg error during header parsing: ") << _errorOccurred;
338 throw ParserException(ss.str());
341 jpeg_start_decompress(&m_cinfo);
343 if (_errorOccurred) {
344 std::stringstream ss;
345 ss << _("Internal jpeg error during decompression: ") << _errorOccurred;
346 throw ParserException(ss.str());
349 _compressorOpened = true;
351 // Until this point the type should be GNASH_IMAGE_INVALID.
352 // It's possible to create transparent JPEG data by merging an
353 // alpha channel, but that is handled explicitly elsewhere.
354 _type = TYPE_RGB;
358 void
359 JpegInput::finishImage()
361 if (setjmp(_jmpBuf)) {
362 std::stringstream ss;
363 ss << _("Internal jpeg error: ") << _errorOccurred;
364 throw ParserException(ss.str());
367 if (_compressorOpened) {
368 jpeg_finish_decompress(&m_cinfo);
369 _compressorOpened = false;
374 // Return the height of the image. Take the data from our m_cinfo struct.
375 size_t
376 JpegInput::getHeight() const
378 assert(_compressorOpened);
379 return m_cinfo.output_height;
383 // Return the width of the image. Take the data from our m_cinfo struct.
384 size_t
385 JpegInput::getWidth() const
387 assert(_compressorOpened);
388 return m_cinfo.output_width;
392 size_t
393 JpegInput::getComponents() const
395 assert(_compressorOpened);
396 return m_cinfo.output_components;
400 void
401 JpegInput::readScanline(unsigned char* rgb_data)
403 assert(_compressorOpened);
404 assert(m_cinfo.output_scanline < m_cinfo.output_height);
406 // C and its unsigned variables...
407 const int toRead = 1;
408 const int lines_read = jpeg_read_scanlines(&m_cinfo, &rgb_data, toRead);
410 // This happens in real cases.
411 if (lines_read != toRead) {
412 throw ParserException("Could not read JPEG scanline");
415 // Expand grayscale to RGB
416 if (m_cinfo.out_color_space == JCS_GRAYSCALE) {
417 size_t w = getWidth();
418 unsigned char* src = rgb_data + w - 1;
419 unsigned char* dst = rgb_data + (w * 3) - 1;
420 for (; w; w--, src--) {
421 *dst-- = *src;
422 *dst-- = *src;
423 *dst-- = *src;
429 void
430 JpegInput::errorOccurred(const char* msg)
432 log_debug("Long jump: banzaaaaaai!");
433 _errorOccurred = msg;
435 // Mark the compressor as closed so we can open another image
436 // with this instance. We should throw on any errors, so there
437 // should be no further activity on the current image.
438 if (_compressorOpened) _compressorOpened = false;
439 std::longjmp(_jmpBuf, 1);
442 // Create and read a new image, using a input object that
443 // already has tables loaded. The IJG documentation describes
444 // this as "abbreviated" format.
445 std::auto_ptr<GnashImage>
446 JpegInput::readSWFJpeg2WithTables(JpegInput& loader)
449 loader.read();
451 std::auto_ptr<GnashImage> im(
452 new ImageRGB(loader.getWidth(), loader.getHeight()));
454 for (size_t y = 0, height = loader.getHeight(); y < height; y++) {
455 loader.readScanline(scanline(*im, y));
458 loader.finishImage();
460 return im;
465 // A jpeglib destination manager that writes to a IOChannel.
466 // Paraphrased from IJG jpeglib jdatadst.c.
467 class rw_dest_IOChannel
469 public:
471 struct jpeg_destination_mgr m_pub;
473 /// Constructor.
475 /// The caller is responsible for closing
476 /// the output stream after it's done using us.
478 /// @param out
479 /// The output stream, externally owned.
481 rw_dest_IOChannel(IOChannel& out)
483 m_out_stream(out)
485 // fill in function pointers...
486 m_pub.init_destination = init_destination;
487 m_pub.empty_output_buffer = empty_output_buffer;
488 m_pub.term_destination = term_destination;
490 m_pub.next_output_byte = m_buffer;
491 m_pub.free_in_buffer = IO_BUF_SIZE;
494 static void init_destination(j_compress_ptr cinfo)
496 rw_dest_IOChannel* dest = (rw_dest_IOChannel*) cinfo->dest;
497 assert(dest);
499 dest->m_pub.next_output_byte = dest->m_buffer;
500 dest->m_pub.free_in_buffer = IO_BUF_SIZE;
503 // Set up the given compress object to write to the given
504 // output stream.
505 static void setup(j_compress_ptr cinfo, IOChannel& outstream)
507 cinfo->dest = (jpeg_destination_mgr*) (new rw_dest_IOChannel(outstream));
510 /// Write the output buffer into the stream.
511 static boolean empty_output_buffer(j_compress_ptr cinfo)
513 rw_dest_IOChannel* dest = (rw_dest_IOChannel*) cinfo->dest;
514 assert(dest);
516 if (dest->m_out_stream.write(dest->m_buffer, IO_BUF_SIZE)
517 != IO_BUF_SIZE) {
518 // Error.
519 log_error(_("rw_dest_IOChannel couldn't write data."));
520 return false;
523 dest->m_pub.next_output_byte = dest->m_buffer;
524 dest->m_pub.free_in_buffer = IO_BUF_SIZE;
526 return true;
529 /// Terminate the destination.
531 /// Flush any leftover data, and make sure we get deleted.
533 static void term_destination(j_compress_ptr cinfo)
535 rw_dest_IOChannel* dest = (rw_dest_IOChannel*) cinfo->dest;
536 assert(dest);
538 // Write any remaining data.
539 int datacount = IO_BUF_SIZE - dest->m_pub.free_in_buffer;
540 if (datacount > 0) {
541 if (dest->m_out_stream.write(dest->m_buffer, datacount) != datacount)
543 // Error.
544 log_error(_("rw_dest_IOChannel::term_destination "
545 "couldn't write data."));
549 // Clean ourselves up.
550 delete dest;
551 cinfo->dest = NULL;
554 private:
556 // Source stream, owned in this context by JpegInput
557 IOChannel& m_out_stream;
559 JOCTET m_buffer[IO_BUF_SIZE]; /* start of buffer */
564 JpegOutput::JpegOutput(boost::shared_ptr<IOChannel> out, size_t width,
565 size_t height, int quality)
567 Output(out, width, height)
569 m_cinfo.err = jpeg_std_error(&m_jerr);
571 // Initialize decompression object.
572 jpeg_create_compress(&m_cinfo);
574 rw_dest_IOChannel::setup(&m_cinfo, *_outStream);
575 m_cinfo.image_width = _width;
576 m_cinfo.image_height = _height;
577 m_cinfo.input_components = 3;
578 m_cinfo.in_color_space = JCS_RGB;
579 jpeg_set_defaults(&m_cinfo);
580 jpeg_set_quality(&m_cinfo, quality, TRUE);
582 jpeg_start_compress(&m_cinfo, TRUE);
586 JpegOutput::~JpegOutput()
588 jpeg_finish_compress(&m_cinfo);
589 jpeg_destroy_compress(&m_cinfo);
593 void
594 JpegOutput::writeImageRGB(const unsigned char* rgbData)
596 // RGB...
597 const size_t components = 3;
599 for (size_t y = 0; y < _height; ++y) {
600 const unsigned char* ypos = &rgbData[y * _width * components];
601 // JPEG needs non-const data.
602 jpeg_write_scanlines(&m_cinfo, const_cast<unsigned char**>(&ypos), 1);
606 void
607 JpegOutput::writeImageRGBA(const unsigned char* rgbaData)
609 const size_t components = 3;
610 const size_t size = _width * _height;
612 boost::scoped_array<unsigned char> data(
613 new unsigned char[size * components]);
615 for (size_t pixel = 0; pixel < size; ++pixel) {
616 data[pixel * 3] = rgbaData[pixel * 4];
617 data[pixel * 3 + 1] = rgbaData[pixel * 4 + 1];
618 data[pixel * 3 + 2] = rgbaData[pixel * 4 + 2];
620 writeImageRGB(data.get());
623 std::auto_ptr<Output>
624 JpegOutput::create(boost::shared_ptr<IOChannel> o, size_t width, size_t height,
625 int quality)
627 std::auto_ptr<Output> outChannel(new JpegOutput(o, width, height, quality));
628 return outChannel;
631 } // namespace image
632 } // namespace gnash
635 // Local Variables:
636 // mode: C++
637 // c-basic-offset: 8
638 // tab-width: 8
639 // indent-tabs-mode: t
640 // End: