2 This file is part of the KDE libraries
4 Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "loader_jpeg.h"
30 // on some systems, libjpeg installs its config.h file which causes a conflict
31 // and makes the compiler barf with "XXX already defined".
38 #include <qdatetime.h>
50 //#define BUFFER_DEBUG
55 // -----------------------------------------------------------------------------
57 struct khtml_error_mgr
: public jpeg_error_mgr
{
58 jmp_buf setjmp_buffer
;
64 void khtml_error_exit (j_common_ptr cinfo
)
66 khtml_error_mgr
* myerr
= (khtml_error_mgr
*) cinfo
->err
;
67 char buffer
[JMSG_LENGTH_MAX
];
68 (*cinfo
->err
->format_message
)(cinfo
, buffer
);
70 qWarning("%s", buffer
);
72 longjmp(myerr
->setjmp_buffer
, 1);
76 static const int max_buf
= 32768;
77 static const int max_consumingtime
= 2000;
79 struct khtml_jpeg_source_mgr
: public jpeg_source_mgr
{
80 JOCTET buffer
[max_buf
];
83 size_t skip_input_bytes
;
86 QRect old_change_rect
;
87 QTime decoder_timestamp
;
93 khtml_jpeg_source_mgr() KDE_NO_EXPORT
;
100 void khtml_j_decompress_dummy(j_decompress_ptr
)
105 boolean
khtml_fill_input_buffer(j_decompress_ptr cinfo
)
108 qDebug("khtml_fill_input_buffer called!");
111 khtml_jpeg_source_mgr
* src
= (khtml_jpeg_source_mgr
*)cinfo
->src
;
115 /* Insert a fake EOI marker - as per jpeglib recommendation */
116 src
->buffer
[0] = (JOCTET
) 0xFF;
117 src
->buffer
[1] = (JOCTET
) JPEG_EOI
;
118 src
->bytes_in_buffer
= 2;
119 src
->next_input_byte
= (JOCTET
*) src
->buffer
;
121 qDebug("...returning true!");
126 return false; /* I/O suspension mode */
130 void khtml_skip_input_data(j_decompress_ptr cinfo
, long num_bytes
)
133 return; /* required noop */
136 qDebug("khtml_skip_input_data (%d) called!", num_bytes
);
139 khtml_jpeg_source_mgr
* src
= (khtml_jpeg_source_mgr
*)cinfo
->src
;
140 src
->skip_input_bytes
+= num_bytes
;
142 unsigned int skipbytes
= qMin(src
->bytes_in_buffer
, src
->skip_input_bytes
);
145 qDebug("skip_input_bytes is now %d", src
->skip_input_bytes
);
146 qDebug("skipbytes is now %d", skipbytes
);
147 qDebug("valid_buffer_len is before %d", src
->valid_buffer_len
);
148 qDebug("bytes_in_buffer is %d", src
->bytes_in_buffer
);
151 if(skipbytes
< src
->bytes_in_buffer
)
152 memmove(src
->buffer
, src
->next_input_byte
+skipbytes
, src
->bytes_in_buffer
- skipbytes
);
154 src
->bytes_in_buffer
-= skipbytes
;
155 src
->valid_buffer_len
= src
->bytes_in_buffer
;
156 src
->skip_input_bytes
-= skipbytes
;
158 /* adjust data for jpeglib */
159 cinfo
->src
->next_input_byte
= (JOCTET
*) src
->buffer
;
160 cinfo
->src
->bytes_in_buffer
= (size_t) src
->valid_buffer_len
;
162 qDebug("valid_buffer_len is afterwards %d", src
->valid_buffer_len
);
163 qDebug("skip_input_bytes is now %d", src
->skip_input_bytes
);
169 khtml_jpeg_source_mgr::khtml_jpeg_source_mgr()
171 jpeg_source_mgr::init_source
= khtml_j_decompress_dummy
;
172 jpeg_source_mgr::fill_input_buffer
= khtml_fill_input_buffer
;
173 jpeg_source_mgr::skip_input_data
= khtml_skip_input_data
;
174 jpeg_source_mgr::resync_to_restart
= jpeg_resync_to_restart
;
175 jpeg_source_mgr::term_source
= khtml_j_decompress_dummy
;
177 valid_buffer_len
= 0;
178 skip_input_bytes
= 0;
180 next_input_byte
= buffer
;
182 decoding_done
= false;
187 // -----------------------------------------------------------------------------
190 class KJPEGFormat
: public QImageFormat
195 virtual ~KJPEGFormat();
197 virtual int decode(QImage
& img
, QImageConsumer
* consumer
,
198 const uchar
* buffer
, int length
);
213 // structs for the jpeglib
214 struct jpeg_decompress_struct cinfo
;
215 struct khtml_error_mgr jerr
;
216 struct khtml_jpeg_source_mgr jsrc
;
220 // -----------------------------------------------------------------------------
222 KJPEGFormat::KJPEGFormat()
224 memset(&cinfo
, 0, sizeof(cinfo
));
225 cinfo
.err
= jpeg_std_error(&jerr
);
226 jpeg_create_decompress(&cinfo
);
227 cinfo
.err
= jpeg_std_error(&jerr
);
228 jerr
.error_exit
= khtml_error_exit
;
234 KJPEGFormat::~KJPEGFormat()
236 (void) jpeg_destroy_decompress(&cinfo
);
240 * return > 0 means "consumed x bytes, need more"
241 * return == 0 means "end of frame reached"
242 * return < 0 means "fatal error in image decoding, don't call me ever again"
245 int KJPEGFormat::decode(QImage
& image
, QImageConsumer
* consumer
, const uchar
* buffer
, int length
)
248 qDebug("KJPEGFormat::decode(%08lx, %08lx, %08lx, %d)",
249 &image
, consumer
, buffer
, length
);
254 qDebug("ateof, eating");
260 if(setjmp(jerr
.setjmp_buffer
))
263 qDebug("jump into state invalid");
272 int consumed
= qMin(length
, max_buf
- jsrc
.valid_buffer_len
);
275 qDebug("consuming %d bytes", consumed
);
278 // filling buffer with the new data
279 memcpy(jsrc
.buffer
+ jsrc
.valid_buffer_len
, buffer
, consumed
);
280 jsrc
.valid_buffer_len
+= consumed
;
282 if(jsrc
.skip_input_bytes
)
285 qDebug("doing skipping");
286 qDebug("valid_buffer_len %d", jsrc
.valid_buffer_len
);
287 qDebug("skip_input_bytes %d", jsrc
.skip_input_bytes
);
289 int skipbytes
= qMin((size_t) jsrc
.valid_buffer_len
, jsrc
.skip_input_bytes
);
291 if(skipbytes
< jsrc
.valid_buffer_len
)
292 memmove(jsrc
.buffer
, jsrc
.buffer
+skipbytes
, jsrc
.valid_buffer_len
- skipbytes
);
294 jsrc
.valid_buffer_len
-= skipbytes
;
295 jsrc
.skip_input_bytes
-= skipbytes
;
297 // still more bytes to skip
298 if(jsrc
.skip_input_bytes
) {
299 if(consumed
<= 0) qDebug("ERROR!!!");
305 cinfo
.src
->next_input_byte
= (JOCTET
*) jsrc
.buffer
;
306 cinfo
.src
->bytes_in_buffer
= (size_t) jsrc
.valid_buffer_len
;
309 qDebug("buffer contains %d bytes", jsrc
.valid_buffer_len
);
314 if(jpeg_read_header(&cinfo
, true) != JPEG_SUSPENDED
) {
315 // do some simple memory requirements limitations
316 // as long as we use that stupid Qt stuff
317 int s
= cinfo
.image_width
* cinfo
.image_height
;
318 if ( s
> 16384 * 12388 )
319 cinfo
.scale_denom
= 8;
320 else if ( s
> 8192 * 6144 )
321 cinfo
.scale_denom
= 4;
322 else if ( s
> 4096 * 3072 )
323 cinfo
.scale_denom
= 2;
326 consumer
->setSize(cinfo
.image_width
/cinfo
.scale_denom
,
327 cinfo
.image_height
/cinfo
.scale_denom
);
329 state
= startDecompress
;
333 if(state
== startDecompress
)
335 jsrc
.do_progressive
= jpeg_has_multiple_scans( &cinfo
);
338 qDebug( "**** DOPROGRESSIVE: %d", jsrc
.do_progressive
);
340 if ( jsrc
.do_progressive
)
341 cinfo
.buffered_image
= true;
343 cinfo
.buffered_image
= false;
346 jpeg_calc_output_dimensions( &cinfo
);
348 if ( cinfo
.jpeg_color_space
== JCS_YCbCr
)
349 cinfo
.out_color_space
= JCS_RGB
;
351 cinfo
.do_fancy_upsampling
= true;
352 cinfo
.do_block_smoothing
= false;
353 cinfo
.quantize_colors
= false;
355 // false: IO suspension
356 if(jpeg_start_decompress(&cinfo
)) {
357 if ( cinfo
.output_components
== 3 || cinfo
.output_components
== 4) {
358 image
.create( cinfo
.output_width
, cinfo
.output_height
, 32 );
359 } else if ( cinfo
.output_components
== 1 ) {
360 image
.create( cinfo
.output_width
, cinfo
.output_height
, 8, 256 );
361 for (int i
=0; i
<256; i
++)
362 image
.setColor(i
, qRgb(i
,i
,i
));
366 qDebug("will create a picture %d/%d in size", cinfo
.output_width
, cinfo
.output_height
);
370 qDebug("ok, going to decompressStarted");
373 jsrc
.decoder_timestamp
.start();
374 state
= jsrc
.do_progressive
? decompressStarted
: doOutputScan
;
380 if(state
== decompressStarted
) {
381 state
= (!jsrc
.final_pass
&& jsrc
.decoder_timestamp
.elapsed() < max_consumingtime
)
382 ? consumeInput
: prepareOutputScan
;
385 if(state
== consumeInput
)
390 retval
= jpeg_consume_input(&cinfo
);
391 } while (retval
!= JPEG_SUSPENDED
&& retval
!= JPEG_REACHED_EOI
392 && (retval
!= JPEG_REACHED_SOS
|| jsrc
.decoder_timestamp
.elapsed() < max_consumingtime
));
394 if(jsrc
.decoder_timestamp
.elapsed() >= max_consumingtime
||
396 retval
== JPEG_REACHED_EOI
|| retval
== JPEG_REACHED_SOS
)
397 state
= prepareOutputScan
;
400 if(state
== prepareOutputScan
)
402 if ( jpeg_start_output(&cinfo
, cinfo
.input_scan_number
) )
403 state
= doOutputScan
;
406 if(state
== doOutputScan
)
408 if(image
.isNull() || jsrc
.decoding_done
)
411 qDebug("complete in doOutputscan, eating..");
415 uchar
** lines
= image
.jumpTable();
416 int oldoutput_scanline
= cinfo
.output_scanline
;
418 while(cinfo
.output_scanline
< cinfo
.output_height
&&
419 jpeg_read_scanlines(&cinfo
, lines
+cinfo
.output_scanline
, cinfo
.output_height
))
420 ; // here happens all the magic of decoding
422 int completed_scanlines
= cinfo
.output_scanline
- oldoutput_scanline
;
424 qDebug("completed now %d scanlines", completed_scanlines
);
427 if ( cinfo
.output_components
== 3 ) {
428 // Expand 24->32 bpp.
429 for (int j
=oldoutput_scanline
; j
<oldoutput_scanline
+completed_scanlines
; j
++) {
430 uchar
*in
= image
.scanLine(j
) + cinfo
.output_width
* 3;
431 QRgb
*out
= (QRgb
*)image
.scanLine(j
);
433 for (uint i
=cinfo
.output_width
; i
--; ) {
435 out
[i
] = qRgb(in
[0], in
[1], in
[2]);
440 if(consumer
&& completed_scanlines
)
442 QRect
r(0, oldoutput_scanline
, cinfo
.output_width
, completed_scanlines
);
444 qDebug("changing %d/%d %d/%d", r
.x(), r
.y(), r
.width(), r
.height());
446 jsrc
.change_rect
|= r
;
448 if ( jsrc
.decoder_timestamp
.elapsed() >= max_consumingtime
) {
449 if( !jsrc
.old_change_rect
.isEmpty()) {
450 consumer
->changed(jsrc
.old_change_rect
);
451 jsrc
.old_change_rect
= QRect();
453 consumer
->changed(jsrc
.change_rect
);
454 jsrc
.change_rect
= QRect();
455 jsrc
.decoder_timestamp
.restart();
459 if(cinfo
.output_scanline
>= cinfo
.output_height
)
461 if ( jsrc
.do_progressive
) {
462 jpeg_finish_output(&cinfo
);
463 jsrc
.final_pass
= jpeg_input_complete(&cinfo
);
464 jsrc
.decoding_done
= jsrc
.final_pass
&& cinfo
.input_scan_number
== cinfo
.output_scan_number
;
465 if ( !jsrc
.decoding_done
) {
466 jsrc
.old_change_rect
|= jsrc
.change_rect
;
467 jsrc
.change_rect
= QRect();
471 jsrc
.decoding_done
= true;
474 qDebug("one pass is completed, final_pass = %d, dec_done: %d, complete: %d",
475 jsrc
.final_pass
, jsrc
.decoding_done
, jpeg_input_complete(&cinfo
));
477 if(!jsrc
.decoding_done
)
480 qDebug("starting another one, input_scan_number is %d/%d", cinfo
.input_scan_number
,
481 cinfo
.output_scan_number
);
483 jsrc
.decoder_timestamp
.restart();
484 state
= decompressStarted
;
485 // don't return until necessary!
490 if(state
== doOutputScan
&& jsrc
.decoding_done
) {
492 qDebug("input is complete, cleaning up, returning..");
494 if ( consumer
&& !jsrc
.change_rect
.isEmpty() )
495 consumer
->changed( jsrc
.change_rect
);
502 (void) jpeg_finish_decompress(&cinfo
);
503 (void) jpeg_destroy_decompress(&cinfo
);
512 qDebug("valid_buffer_len is now %d", jsrc
.valid_buffer_len
);
513 qDebug("bytes_in_buffer is now %d", jsrc
.bytes_in_buffer
);
514 qDebug("consumed %d bytes", consumed
);
516 if(jsrc
.bytes_in_buffer
&& jsrc
.buffer
!= jsrc
.next_input_byte
)
517 memmove(jsrc
.buffer
, jsrc
.next_input_byte
, jsrc
.bytes_in_buffer
);
518 jsrc
.valid_buffer_len
= jsrc
.bytes_in_buffer
;
522 // -----------------------------------------------------------------------------
523 // This is the factory that teaches Qt about progressive JPEG's
525 QImageFormat
* khtml::KJPEGFormatType::decoderFor(const unsigned char* buffer
, int length
)
527 if(length
< 3) return 0;
529 if(buffer
[0] == 0377 &&
532 return new KJPEGFormat
;
537 const char* khtml::KJPEGFormatType::formatName() const
546 #warning You don't seem to have libJPEG. jpeg support in khtml won't work
548 #endif // HAVE_LIBJPEG
550 // -----------------------------------------------------------------------------