1 // GnashImageJpeg.cpp: Jpeg reader, for Gnash.
3 // Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
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.
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.
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
26 #include "gnashconfig.h"
29 #include "GnashImageJpeg.h"
33 #include <boost/noncopyable.hpp>
37 #include "GnashImage.h"
38 #include "IOChannel.h"
40 #include "GnashException.h"
43 // jpeglib.h redefines HAVE_STDLIB_H.
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.
66 setup_jpeg_err(jpeg_error_mgr
* 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
80 jpeg_source_mgr m_pub
; /* public fields */
83 explicit rw_source_IOChannel(boost::shared_ptr
<IOChannel
> in
)
85 _ownSourceStream(false),
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."));
119 // Insert a fake EOI marker.
120 src
->m_buffer
[0] = 0xFF;
121 src
->m_buffer
[1] = JPEG_EOI
;
125 // Hack to work around SWF bug: sometimes data
126 // starts with FFD9FFD8, when it should be
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;
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
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
);
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
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
;
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
;
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
)
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
);
241 jpeg_destroy_decompress(&m_cinfo
);
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.
252 src
->discardPartialBuffer();
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
);
272 // suspended due to lack of data
273 throw ParserException(_("Lack of data during JPEG "
277 // Found valid image datastream
279 case JPEG_HEADER_TABLES_ONLY
:
280 // Found valid table-specs-only datastream
283 log_debug(_("unexpected: jpeg_read_header returned %d"), ret
);
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.
303 assert(!_compressorOpened
);
305 if (setjmp(_jmpBuf
)) {
306 std::stringstream ss
;
307 ss
<< _("Internal jpeg error: ") << _errorOccurred
;
308 throw ParserException(ss
.str());
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
);
318 // suspended due to lack of data
319 throw ParserException(_("lack of data during JPEG "
323 // Found valid image datastream
325 case JPEG_HEADER_TABLES_ONLY
:
326 // Found valid table-specs-only datastream
329 log_debug(_("unexpected: jpeg_read_header returned %d [%s:%d]"),
330 ret
, __FILE__
, __LINE__
);
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.
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.
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.
385 JpegInput::getWidth() const
387 assert(_compressorOpened
);
388 return m_cinfo
.output_width
;
393 JpegInput::getComponents() const
395 assert(_compressorOpened
);
396 return m_cinfo
.output_components
;
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
--) {
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
)
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();
465 // A jpeglib destination manager that writes to a IOChannel.
466 // Paraphrased from IJG jpeglib jdatadst.c.
467 class rw_dest_IOChannel
471 struct jpeg_destination_mgr m_pub
;
475 /// The caller is responsible for closing
476 /// the output stream after it's done using us.
479 /// The output stream, externally owned.
481 rw_dest_IOChannel(IOChannel
& 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
;
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
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
;
516 if (dest
->m_out_stream
.write(dest
->m_buffer
, IO_BUF_SIZE
)
519 log_error(_("rw_dest_IOChannel couldn't write data."));
523 dest
->m_pub
.next_output_byte
= dest
->m_buffer
;
524 dest
->m_pub
.free_in_buffer
= IO_BUF_SIZE
;
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
;
538 // Write any remaining data.
539 int datacount
= IO_BUF_SIZE
- dest
->m_pub
.free_in_buffer
;
541 if (dest
->m_out_stream
.write(dest
->m_buffer
, datacount
) != datacount
)
544 log_error(_("rw_dest_IOChannel::term_destination "
545 "couldn't write data."));
549 // Clean ourselves up.
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
);
594 JpegOutput::writeImageRGB(const unsigned char* rgbData
)
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);
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
,
627 std::auto_ptr
<Output
> outChannel(new JpegOutput(o
, width
, height
, quality
));
639 // indent-tabs-mode: t