khtml Library API Documentation

loader_jpeg.cpp

00001 /*
00002     This file is part of the KDE libraries
00003 
00004     Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
00005 
00006     Permission is hereby granted, free of charge, to any person obtaining a copy
00007     of this software and associated documentation files (the "Software"), to deal
00008     in the Software without restriction, including without limitation the rights
00009     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010     copies of the Software, and to permit persons to whom the Software is
00011     furnished to do so, subject to the following conditions:
00012 
00013     The above copyright notice and this permission notice shall be included in
00014     all copies or substantial portions of the Software.
00015 
00016     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00019     AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00020     AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00021     CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00022 
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028 
00029 #ifdef HAVE_LIBJPEG
00030 #include "loader_jpeg.h"
00031 
00032 #include <stdio.h>
00033 #include <setjmp.h>
00034 #include <qdatetime.h>
00035 
00036 extern "C" {
00037 #define XMD_H
00038 #include <jpeglib.h>
00039 #undef const
00040 }
00041 
00042 
00043 #undef BUFFER_DEBUG
00044 //#define BUFFER_DEBUG
00045 
00046 #undef JPEG_DEBUG
00047 //#define JPEG_DEBUG
00048 
00049 // -----------------------------------------------------------------------------
00050 
00051 struct khtml_error_mgr : public jpeg_error_mgr {
00052     jmp_buf setjmp_buffer;
00053 };
00054 
00055 extern "C" {
00056 
00057     static
00058     void khtml_error_exit (j_common_ptr cinfo)
00059     {
00060         khtml_error_mgr* myerr = (khtml_error_mgr*) cinfo->err;
00061         char buffer[JMSG_LENGTH_MAX];
00062         (*cinfo->err->format_message)(cinfo, buffer);
00063         qWarning("%s", buffer);
00064         longjmp(myerr->setjmp_buffer, 1);
00065     }
00066 }
00067 
00068 static const int max_buf = 8192;
00069 static const int max_consumingtime = 500;
00070 
00071 struct khtml_jpeg_source_mgr : public jpeg_source_mgr {
00072     JOCTET buffer[max_buf];
00073 
00074     int valid_buffer_len;
00075     unsigned int skip_input_bytes;
00076     int ateof;
00077     QTime decoder_timestamp;
00078     bool final_pass;
00079     bool decoding_done;
00080 
00081 public:
00082     khtml_jpeg_source_mgr();
00083 };
00084 
00085 
00086 extern "C" {
00087 
00088     static
00089     void khtml_j_decompress_dummy(j_decompress_ptr)
00090     {
00091     }
00092 
00093     static
00094     boolean khtml_fill_input_buffer(j_decompress_ptr cinfo)
00095     {
00096 #ifdef BUFFER_DEBUG
00097         qDebug("khtml_fill_input_buffer called!");
00098 #endif
00099 
00100         khtml_jpeg_source_mgr* src = (khtml_jpeg_source_mgr*)cinfo->src;
00101 
00102         if ( src->ateof )
00103         {
00104             /* Insert a fake EOI marker - as per jpeglib recommendation */
00105             src->buffer[0] = (JOCTET) 0xFF;
00106             src->buffer[1] = (JOCTET) JPEG_EOI;
00107             src->bytes_in_buffer = 2;
00108 #ifdef BUFFER_DEBUG
00109             qDebug("...returning TRUE!");
00110 #endif
00111             return TRUE;
00112         }
00113         else
00114             return FALSE;  /* I/O suspension mode */
00115     }
00116 
00117     static
00118     void khtml_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
00119     {
00120         if(num_bytes <= 0)
00121             return; /* required noop */
00122 
00123 #ifdef BUFFER_DEBUG
00124         qDebug("khtml_skip_input_data (%d) called!", num_bytes);
00125 #endif
00126 
00127         khtml_jpeg_source_mgr* src = (khtml_jpeg_source_mgr*)cinfo->src;
00128         src->skip_input_bytes += num_bytes;
00129 
00130         unsigned int skipbytes = QMIN(src->bytes_in_buffer, src->skip_input_bytes);
00131 
00132 #ifdef BUFFER_DEBUG
00133         qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
00134         qDebug("skipbytes is now %d", skipbytes);
00135         qDebug("valid_buffer_len is before %d", src->valid_buffer_len);
00136         qDebug("bytes_in_buffer is %d", src->bytes_in_buffer);
00137 #endif
00138 
00139         if(skipbytes < src->bytes_in_buffer)
00140             memmove(src->buffer, src->next_input_byte+skipbytes, src->bytes_in_buffer - skipbytes);
00141 
00142         src->bytes_in_buffer -= skipbytes;
00143         src->valid_buffer_len = src->bytes_in_buffer;
00144         src->skip_input_bytes -= skipbytes;
00145 
00146         /* adjust data for jpeglib */
00147         cinfo->src->next_input_byte = (JOCTET *) src->buffer;
00148         cinfo->src->bytes_in_buffer = (size_t) src->valid_buffer_len;
00149 #ifdef BUFFER_DEBUG
00150         qDebug("valid_buffer_len is afterwards %d", src->valid_buffer_len);
00151         qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
00152 #endif
00153     }
00154 }
00155 
00156 
00157 khtml_jpeg_source_mgr::khtml_jpeg_source_mgr()
00158 {
00159     jpeg_source_mgr::init_source = khtml_j_decompress_dummy;
00160     jpeg_source_mgr::fill_input_buffer = khtml_fill_input_buffer;
00161     jpeg_source_mgr::skip_input_data = khtml_skip_input_data;
00162     jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
00163     jpeg_source_mgr::term_source = khtml_j_decompress_dummy;
00164     bytes_in_buffer = 0;
00165     valid_buffer_len = 0;
00166     skip_input_bytes = 0;
00167     ateof = 0;
00168     next_input_byte = buffer;
00169     final_pass = false;
00170     decoding_done = false;
00171 }
00172 
00173 
00174 
00175 // -----------------------------------------------------------------------------
00176 
00177 class KJPEGFormat : public QImageFormat
00178 {
00179 public:
00180     KJPEGFormat();
00181 
00182     virtual ~KJPEGFormat();
00183 
00184     virtual int decode(QImage& img, QImageConsumer* consumer,
00185                        const uchar* buffer, int length);
00186 private:
00187 
00188     enum {
00189         Init,
00190         readHeader,
00191         startDecompress,
00192         decompressStarted,
00193         consumeInput,
00194         prepareOutputScan,
00195         doOutputScan,
00196         readDone,
00197         invalid
00198     } state;
00199 
00200     // structs for the jpeglib
00201     struct jpeg_decompress_struct cinfo;
00202     struct khtml_error_mgr jerr;
00203     struct khtml_jpeg_source_mgr jsrc;
00204 };
00205 
00206 
00207 // -----------------------------------------------------------------------------
00208 
00209 KJPEGFormat::KJPEGFormat()
00210 {
00211     memset(&cinfo, 0, sizeof(cinfo));
00212     cinfo.err = jpeg_std_error(&jerr);
00213     jpeg_create_decompress(&cinfo);
00214     cinfo.err = jpeg_std_error(&jerr);
00215     jerr.error_exit = khtml_error_exit;
00216     cinfo.src = &jsrc;
00217     state = Init;
00218 }
00219 
00220 
00221 KJPEGFormat::~KJPEGFormat()
00222 {
00223     jpeg_destroy_decompress(&cinfo);
00224 }
00225 
00226 /*
00227  * return  > 0 means "consumed x bytes, need more"
00228  * return == 0 means "end of frame reached"
00229  * return  < 0 means "fatal error in image decoding, don't call me ever again"
00230  */
00231 
00232 int KJPEGFormat::decode(QImage& image, QImageConsumer* consumer, const uchar* buffer, int length)
00233 {
00234 #ifdef JPEG_DEBUG
00235     qDebug("KJPEGFormat::decode(%08lx, %08lx, %08lx, %d)",
00236            &image, consumer, buffer, length);
00237 #endif
00238 
00239     if(jsrc.ateof) {
00240 #ifdef JPEG_DEBUG
00241         qDebug("ateof, eating");
00242 #endif
00243         return length;
00244     }
00245 
00246 
00247     if(setjmp(jerr.setjmp_buffer))
00248     {
00249 #ifdef JPEG_DEBUG
00250         qDebug("jump into state invalid");
00251 #endif
00252         if(consumer)
00253             consumer->end();
00254 
00255         // this is fatal
00256         return -1;
00257     }
00258 
00259     int consumed = QMIN(length, max_buf - jsrc.valid_buffer_len);
00260 
00261 #ifdef BUFFER_DEBUG
00262     qDebug("consuming %d bytes", consumed);
00263 #endif
00264 
00265     // filling buffer with the new data
00266     memcpy(jsrc.buffer + jsrc.valid_buffer_len, buffer, consumed);
00267     jsrc.valid_buffer_len += consumed;
00268 
00269     if(jsrc.skip_input_bytes)
00270     {
00271 #ifdef BUFFER_DEBUG
00272         qDebug("doing skipping");
00273         qDebug("valid_buffer_len %d", jsrc.valid_buffer_len);
00274         qDebug("skip_input_bytes %d", jsrc.skip_input_bytes);
00275 #endif
00276         int skipbytes = QMIN((unsigned) jsrc.valid_buffer_len, jsrc.skip_input_bytes);
00277 
00278         if(skipbytes < jsrc.valid_buffer_len)
00279             memcpy(jsrc.buffer, jsrc.buffer+skipbytes, jsrc.valid_buffer_len - skipbytes);
00280 
00281         jsrc.valid_buffer_len -= skipbytes;
00282         jsrc.skip_input_bytes -= skipbytes;
00283 
00284         // still more bytes to skip
00285         if(jsrc.skip_input_bytes) {
00286             if(consumed <= 0) qDebug("ERROR!!!");
00287             return consumed;
00288         }
00289 
00290     }
00291 
00292     cinfo.src->next_input_byte = (JOCTET *) jsrc.buffer;
00293     cinfo.src->bytes_in_buffer = (size_t) jsrc.valid_buffer_len;
00294 
00295 #ifdef BUFFER_DEBUG
00296     qDebug("buffer contains %d bytes", jsrc.valid_buffer_len);
00297 #endif
00298 
00299     if(state == Init)
00300     {
00301         if(jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED) {
00302             if ( consumer )
00303                 consumer->setSize(cinfo.output_width, cinfo.output_height);
00304 
00305             state = startDecompress;
00306         }
00307     }
00308 
00309     if(state == startDecompress)
00310     {
00311         cinfo.buffered_image = TRUE;
00312         cinfo.do_fancy_upsampling = TRUE;
00313         cinfo.do_block_smoothing = FALSE;
00314         cinfo.dct_method = JDCT_FASTEST;
00315 
00316         // FALSE: IO suspension
00317         if(jpeg_start_decompress(&cinfo)) {
00318             if ( cinfo.output_components == 3 || cinfo.output_components == 4) {
00319                 image.create( cinfo.output_width, cinfo.output_height, 32 );
00320             } else if ( cinfo.output_components == 1 ) {
00321                 image.create( cinfo.output_width, cinfo.output_height, 8, 256 );
00322                 for (int i=0; i<256; i++)
00323                     image.setColor(i, qRgb(i,i,i));
00324             }
00325 
00326 #ifdef JPEG_DEBUG
00327             qDebug("will create a picture %d/%d in size", cinfo.output_width, cinfo.output_height);
00328 #endif
00329 
00330 #ifdef JPEG_DEBUG
00331             qDebug("ok, going to decompressStarted");
00332 #endif
00333 
00334             jsrc.decoder_timestamp.start();
00335             state = decompressStarted;
00336         }
00337     }
00338 
00339     if(state == decompressStarted) {
00340         state = (!jsrc.final_pass && jsrc.decoder_timestamp.elapsed() < max_consumingtime)
00341                 ? consumeInput : prepareOutputScan;
00342     }
00343 
00344     if(state == consumeInput)
00345     {
00346         int retval;
00347 
00348         do {
00349             retval = jpeg_consume_input(&cinfo);
00350         } while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_EOI);
00351 
00352         if(jsrc.decoder_timestamp.elapsed() > max_consumingtime || jsrc.final_pass ||
00353            retval == JPEG_REACHED_EOI || retval == JPEG_REACHED_SOS)
00354             state = prepareOutputScan;
00355         else
00356             state = consumeInput;
00357     }
00358 
00359     if(state == prepareOutputScan)
00360     {
00361         jsrc.decoder_timestamp.restart();
00362         cinfo.buffered_image = TRUE;
00363         jpeg_start_output(&cinfo, cinfo.input_scan_number);
00364         state = doOutputScan;
00365     }
00366 
00367     if(state == doOutputScan)
00368     {
00369         if(image.isNull() || jsrc.decoding_done)
00370         {
00371 #ifdef JPEG_DEBUG
00372             qDebug("complete in doOutputscan, eating..");
00373 #endif
00374             return consumed;
00375         }
00376         uchar** lines = image.jumpTable();
00377         int oldoutput_scanline = cinfo.output_scanline;
00378 
00379         while(cinfo.output_scanline < cinfo.output_height &&
00380               jpeg_read_scanlines(&cinfo, lines+cinfo.output_scanline, cinfo.output_height))
00381             ; // here happens all the magic of decoding
00382 
00383         int completed_scanlines = cinfo.output_scanline - oldoutput_scanline;
00384 #ifdef JPEG_DEBUG
00385         qDebug("completed now %d scanlines", completed_scanlines);
00386 #endif
00387 
00388         if ( cinfo.output_components == 3 ) {
00389         // Expand 24->32 bpp.
00390         for (int j=oldoutput_scanline; j<oldoutput_scanline+completed_scanlines; j++) {
00391         uchar *in = image.scanLine(j) + cinfo.output_width * 3;
00392         QRgb *out = (QRgb*)image.scanLine(j);
00393 
00394         for (uint i=cinfo.output_width; i--; ) {
00395             in-=3;
00396             out[i] = qRgb(in[0], in[1], in[2]);
00397         }
00398         }
00399     }
00400 
00401         if(consumer && completed_scanlines)
00402         {
00403             QRect r(0, oldoutput_scanline, cinfo.output_width, completed_scanlines);
00404 #ifdef JPEG_DEBUG
00405             qDebug("changing %d/%d %d/%d", r.x(), r.y(), r.width(), r.height());
00406 #endif
00407             consumer->changed(r);
00408         }
00409 
00410         if(cinfo.output_scanline >= cinfo.output_height)
00411         {
00412             jpeg_finish_output(&cinfo);
00413             jsrc.final_pass = jpeg_input_complete(&cinfo);
00414             jsrc.decoding_done = jsrc.final_pass && cinfo.input_scan_number == cinfo.output_scan_number;
00415 
00416 #ifdef JPEG_DEBUG
00417             qDebug("one pass is completed, final_pass = %d, dec_done: %d, complete: %d",
00418                    jsrc.final_pass, jsrc.decoding_done, jpeg_input_complete(&cinfo));
00419 #endif
00420             if(!jsrc.decoding_done)
00421             {
00422 #ifdef JPEG_DEBUG
00423                 qDebug("starting another one, input_scan_number is %d/%d", cinfo.input_scan_number,
00424                        cinfo.output_scan_number);
00425 #endif
00426                 // don't return until necessary!
00427                 jsrc.decoder_timestamp.restart();
00428                 state = decompressStarted;
00429             }
00430         }
00431 
00432         if(state == doOutputScan && jsrc.decoding_done) {
00433 #ifdef JPEG_DEBUG
00434             qDebug("input is complete, cleaning up, returning..");
00435 #endif
00436             (void) jpeg_finish_decompress(&cinfo);
00437             (void) jpeg_destroy_decompress(&cinfo);
00438 
00439             if(consumer)
00440                 consumer->end();
00441 
00442             jsrc.ateof = true;
00443 
00444             return 0;
00445         }
00446     }
00447 
00448 #ifdef BUFFER_DEBUG
00449     qDebug("valid_buffer_len is now %d", jsrc.valid_buffer_len);
00450     qDebug("bytes_in_buffer is now %d", jsrc.bytes_in_buffer);
00451     qDebug("consumed %d bytes", consumed);
00452 #endif
00453     if(jsrc.bytes_in_buffer && jsrc.buffer != jsrc.next_input_byte)
00454         memmove(jsrc.buffer, jsrc.next_input_byte, jsrc.bytes_in_buffer);
00455     jsrc.valid_buffer_len = jsrc.bytes_in_buffer;
00456     return consumed;
00457 }
00458 
00459 // -----------------------------------------------------------------------------
00460 // This is the factory that teaches Qt about progressive JPEG's
00461 
00462 QImageFormat* khtml::KJPEGFormatType::decoderFor(const unsigned char* buffer, int length)
00463 {
00464     if(length < 3) return 0;
00465 
00466     if(buffer[0] == 0377 &&
00467        buffer[1] == 0330 &&
00468        buffer[2] == 0377)
00469          return new KJPEGFormat;
00470 
00471     return 0;
00472 }
00473 
00474 const char* khtml::KJPEGFormatType::formatName() const
00475 {
00476     return "JPEG";
00477 }
00478 
00479 #else
00480 #ifdef __GNUC__
00481 #warning You don't seem to have libJPEG. jpeg support in khtml won't work
00482 #endif
00483 #endif // HAVE_LIBJPEG
00484 
00485 // -----------------------------------------------------------------------------
00486 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:16:39 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001