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 // on some systems, libjpeg installs its config.h file which causes a conflict
00031 // and makes the compiler barf with "XXX already defined".
00032 #ifdef HAVE_STDLIB_H
00033 #undef HAVE_STDLIB_H
00034 #endif
00035 #include "loader_jpeg.h"
00036 
00037 #include <stdio.h>
00038 #include <setjmp.h>
00039 #include <qdatetime.h>
00040 #include <kglobal.h>
00041 
00042 extern "C" {
00043 #define XMD_H
00044 #include <jpeglib.h>
00045 #undef const
00046 }
00047 
00048 
00049 #undef BUFFER_DEBUG
00050 //#define BUFFER_DEBUG
00051 
00052 #undef JPEG_DEBUG
00053 //#define JPEG_DEBUG
00054 
00055 // -----------------------------------------------------------------------------
00056 
00057 struct khtml_error_mgr : public jpeg_error_mgr {
00058     jmp_buf setjmp_buffer;
00059 };
00060 
00061 extern "C" {
00062 
00063     static
00064     void khtml_error_exit (j_common_ptr cinfo)
00065     {
00066         khtml_error_mgr* myerr = (khtml_error_mgr*) cinfo->err;
00067         char buffer[JMSG_LENGTH_MAX];
00068         (*cinfo->err->format_message)(cinfo, buffer);
00069         qWarning("%s", buffer);
00070         longjmp(myerr->setjmp_buffer, 1);
00071     }
00072 }
00073 
00074 static const int max_buf = 32768;
00075 static const int max_consumingtime = 2000;
00076 
00077 struct khtml_jpeg_source_mgr : public jpeg_source_mgr {
00078     JOCTET buffer[max_buf];
00079 
00080     int valid_buffer_len;
00081     size_t skip_input_bytes;
00082     int ateof;
00083     QRect change_rect;
00084     QTime decoder_timestamp;
00085     bool final_pass;
00086     bool decoding_done;
00087     bool do_progressive;
00088 
00089 public:
00090     khtml_jpeg_source_mgr();
00091 };
00092 
00093 
00094 extern "C" {
00095 
00096     static
00097     void khtml_j_decompress_dummy(j_decompress_ptr)
00098     {
00099     }
00100 
00101     static
00102     boolean khtml_fill_input_buffer(j_decompress_ptr cinfo)
00103     {
00104 #ifdef BUFFER_DEBUG
00105         qDebug("khtml_fill_input_buffer called!");
00106 #endif
00107 
00108         khtml_jpeg_source_mgr* src = (khtml_jpeg_source_mgr*)cinfo->src;
00109 
00110         if ( src->ateof )
00111         {
00112             /* Insert a fake EOI marker - as per jpeglib recommendation */
00113             src->buffer[0] = (JOCTET) 0xFF;
00114             src->buffer[1] = (JOCTET) JPEG_EOI;
00115             src->bytes_in_buffer = 2;
00116 #ifdef BUFFER_DEBUG
00117             qDebug("...returning true!");
00118 #endif
00119             return true;
00120         }
00121         else
00122             return false;  /* I/O suspension mode */
00123     }
00124 
00125     static
00126     void khtml_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
00127     {
00128         if(num_bytes <= 0)
00129             return; /* required noop */
00130 
00131 #ifdef BUFFER_DEBUG
00132         qDebug("khtml_skip_input_data (%d) called!", num_bytes);
00133 #endif
00134 
00135         khtml_jpeg_source_mgr* src = (khtml_jpeg_source_mgr*)cinfo->src;
00136         src->skip_input_bytes += num_bytes;
00137 
00138         unsigned int skipbytes = kMin(src->bytes_in_buffer, src->skip_input_bytes);
00139 
00140 #ifdef BUFFER_DEBUG
00141         qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
00142         qDebug("skipbytes is now %d", skipbytes);
00143         qDebug("valid_buffer_len is before %d", src->valid_buffer_len);
00144         qDebug("bytes_in_buffer is %d", src->bytes_in_buffer);
00145 #endif
00146 
00147         if(skipbytes < src->bytes_in_buffer)
00148             memmove(src->buffer, src->next_input_byte+skipbytes, src->bytes_in_buffer - skipbytes);
00149 
00150         src->bytes_in_buffer -= skipbytes;
00151         src->valid_buffer_len = src->bytes_in_buffer;
00152         src->skip_input_bytes -= skipbytes;
00153 
00154         /* adjust data for jpeglib */
00155         cinfo->src->next_input_byte = (JOCTET *) src->buffer;
00156         cinfo->src->bytes_in_buffer = (size_t) src->valid_buffer_len;
00157 #ifdef BUFFER_DEBUG
00158         qDebug("valid_buffer_len is afterwards %d", src->valid_buffer_len);
00159         qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
00160 #endif
00161     }
00162 }
00163 
00164 
00165 khtml_jpeg_source_mgr::khtml_jpeg_source_mgr()
00166 {
00167     jpeg_source_mgr::init_source = khtml_j_decompress_dummy;
00168     jpeg_source_mgr::fill_input_buffer = khtml_fill_input_buffer;
00169     jpeg_source_mgr::skip_input_data = khtml_skip_input_data;
00170     jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
00171     jpeg_source_mgr::term_source = khtml_j_decompress_dummy;
00172     bytes_in_buffer = 0;
00173     valid_buffer_len = 0;
00174     skip_input_bytes = 0;
00175     ateof = 0;
00176     next_input_byte = buffer;
00177     final_pass = false;
00178     decoding_done = false;
00179 }
00180 
00181 
00182 
00183 // -----------------------------------------------------------------------------
00184 
00185 class KJPEGFormat : public QImageFormat
00186 {
00187 public:
00188     KJPEGFormat();
00189 
00190     virtual ~KJPEGFormat();
00191 
00192     virtual int decode(QImage& img, QImageConsumer* consumer,
00193                        const uchar* buffer, int length);
00194 private:
00195 
00196     enum {
00197         Init,
00198         readHeader,
00199         startDecompress,
00200         decompressStarted,
00201         consumeInput,
00202         prepareOutputScan,
00203         doOutputScan,
00204         readDone,
00205         invalid
00206     } state;
00207 
00208     // structs for the jpeglib
00209     struct jpeg_decompress_struct cinfo;
00210     struct khtml_error_mgr jerr;
00211     struct khtml_jpeg_source_mgr jsrc;
00212 };
00213 
00214 
00215 // -----------------------------------------------------------------------------
00216 
00217 KJPEGFormat::KJPEGFormat()
00218 {
00219     memset(&cinfo, 0, sizeof(cinfo));
00220     cinfo.err = jpeg_std_error(&jerr);
00221     jpeg_create_decompress(&cinfo);
00222     cinfo.err = jpeg_std_error(&jerr);
00223     jerr.error_exit = khtml_error_exit;
00224     cinfo.src = &jsrc;
00225     state = Init;
00226 }
00227 
00228 
00229 KJPEGFormat::~KJPEGFormat()
00230 {
00231     (void) jpeg_destroy_decompress(&cinfo);
00232 }
00233 
00234 /*
00235  * return  > 0 means "consumed x bytes, need more"
00236  * return == 0 means "end of frame reached"
00237  * return  < 0 means "fatal error in image decoding, don't call me ever again"
00238  */
00239 
00240 int KJPEGFormat::decode(QImage& image, QImageConsumer* consumer, const uchar* buffer, int length)
00241 {
00242 #ifdef JPEG_DEBUG
00243     qDebug("KJPEGFormat::decode(%08lx, %08lx, %08lx, %d)",
00244            &image, consumer, buffer, length);
00245 #endif
00246 
00247     if(jsrc.ateof) {
00248 #ifdef JPEG_DEBUG
00249         qDebug("ateof, eating");
00250 #endif
00251         return length;
00252     }
00253 
00254 
00255     if(setjmp(jerr.setjmp_buffer))
00256     {
00257 #ifdef JPEG_DEBUG
00258         qDebug("jump into state invalid");
00259 #endif
00260         if(consumer)
00261             consumer->end();
00262 
00263         // this is fatal
00264         return -1;
00265     }
00266 
00267     int consumed = kMin(length, max_buf - jsrc.valid_buffer_len);
00268 
00269 #ifdef BUFFER_DEBUG
00270     qDebug("consuming %d bytes", consumed);
00271 #endif
00272 
00273     // filling buffer with the new data
00274     memcpy(jsrc.buffer + jsrc.valid_buffer_len, buffer, consumed);
00275     jsrc.valid_buffer_len += consumed;
00276 
00277     if(jsrc.skip_input_bytes)
00278     {
00279 #ifdef BUFFER_DEBUG
00280         qDebug("doing skipping");
00281         qDebug("valid_buffer_len %d", jsrc.valid_buffer_len);
00282         qDebug("skip_input_bytes %d", jsrc.skip_input_bytes);
00283 #endif
00284         int skipbytes = kMin((size_t) jsrc.valid_buffer_len, jsrc.skip_input_bytes);
00285 
00286         if(skipbytes < jsrc.valid_buffer_len)
00287             memcpy(jsrc.buffer, jsrc.buffer+skipbytes, jsrc.valid_buffer_len - skipbytes);
00288 
00289         jsrc.valid_buffer_len -= skipbytes;
00290         jsrc.skip_input_bytes -= skipbytes;
00291 
00292         // still more bytes to skip
00293         if(jsrc.skip_input_bytes) {
00294             if(consumed <= 0) qDebug("ERROR!!!");
00295             return consumed;
00296         }
00297 
00298     }
00299 
00300     cinfo.src->next_input_byte = (JOCTET *) jsrc.buffer;
00301     cinfo.src->bytes_in_buffer = (size_t) jsrc.valid_buffer_len;
00302 
00303 #ifdef BUFFER_DEBUG
00304     qDebug("buffer contains %d bytes", jsrc.valid_buffer_len);
00305 #endif
00306 
00307     if(state == Init)
00308     {
00309         if(jpeg_read_header(&cinfo, true) != JPEG_SUSPENDED) {
00310             // do some simple memory requirements limitations
00311             // as long as we use that stupid Qt stuff
00312             int s = cinfo.image_width * cinfo.image_height;
00313             if ( s > 16384 * 12388 )
00314                 cinfo.scale_denom = 8;
00315             else if ( s > 8192 * 6144 )
00316                 cinfo.scale_denom = 4;
00317             else if ( s > 4096 * 3072 )
00318                 cinfo.scale_denom = 2;
00319 
00320             if ( consumer )
00321                 consumer->setSize(cinfo.image_width/cinfo.scale_denom,
00322                                   cinfo.image_height/cinfo.scale_denom);
00323 
00324             state = startDecompress;
00325         }
00326     }
00327 
00328     if(state == startDecompress)
00329     {
00330         jsrc.do_progressive = jpeg_has_multiple_scans( &cinfo );
00331 
00332 #ifdef JPEG_DEBUG
00333         qDebug( "**** DOPROGRESSIVE: %d",  jsrc.do_progressive );
00334 #endif
00335         if ( jsrc.do_progressive )
00336             cinfo.buffered_image = true;
00337         else
00338             cinfo.buffered_image = false;
00339 
00340         // setup image sizes
00341         jpeg_calc_output_dimensions( &cinfo );
00342 
00343         if ( cinfo.jpeg_color_space == JCS_YCbCr )
00344             cinfo.out_color_space = JCS_RGB;
00345 
00346         cinfo.do_fancy_upsampling = true;
00347         cinfo.do_block_smoothing = false;
00348         cinfo.quantize_colors = false;
00349         cinfo.dct_method = JDCT_FASTEST;
00350 
00351         // false: IO suspension
00352         if(jpeg_start_decompress(&cinfo)) {
00353             if ( cinfo.output_components == 3 || cinfo.output_components == 4) {
00354                 image.create( cinfo.output_width, cinfo.output_height, 32 );
00355             } else if ( cinfo.output_components == 1 ) {
00356                 image.create( cinfo.output_width, cinfo.output_height, 8, 256 );
00357                 for (int i=0; i<256; i++)
00358                     image.setColor(i, qRgb(i,i,i));
00359             }
00360 
00361 #ifdef JPEG_DEBUG
00362             qDebug("will create a picture %d/%d in size", cinfo.output_width, cinfo.output_height);
00363 #endif
00364 
00365 #ifdef JPEG_DEBUG
00366             qDebug("ok, going to decompressStarted");
00367 #endif
00368 
00369             jsrc.decoder_timestamp.start();
00370             state = jsrc.do_progressive ? decompressStarted : doOutputScan;
00371         }
00372     }
00373 
00374     if(state == decompressStarted) {
00375         state =  (!jsrc.final_pass && jsrc.decoder_timestamp.elapsed() < max_consumingtime)
00376                 ? consumeInput : prepareOutputScan;
00377     }
00378 
00379     if(state == consumeInput)
00380     {
00381         int retval;
00382 
00383         do {
00384             retval = jpeg_consume_input(&cinfo);
00385         } while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_EOI);
00386 
00387         if(jsrc.decoder_timestamp.elapsed() > max_consumingtime ||
00388            jsrc.final_pass ||
00389            retval == JPEG_REACHED_EOI || retval == JPEG_REACHED_SOS)
00390             state = prepareOutputScan;
00391     }
00392 
00393     if(state == prepareOutputScan)
00394     {
00395         jsrc.decoder_timestamp.restart();
00396         if ( jpeg_start_output(&cinfo, cinfo.input_scan_number) )
00397             state = doOutputScan;
00398     }
00399 
00400     if(state == doOutputScan)
00401     {
00402         if(image.isNull() || jsrc.decoding_done)
00403         {
00404 #ifdef JPEG_DEBUG
00405             qDebug("complete in doOutputscan, eating..");
00406 #endif
00407             return consumed;
00408         }
00409         uchar** lines = image.jumpTable();
00410         int oldoutput_scanline = cinfo.output_scanline;
00411 
00412         while(cinfo.output_scanline < cinfo.output_height &&
00413               jpeg_read_scanlines(&cinfo, lines+cinfo.output_scanline, cinfo.output_height))
00414             ; // here happens all the magic of decoding
00415 
00416         int completed_scanlines = cinfo.output_scanline - oldoutput_scanline;
00417 #ifdef JPEG_DEBUG
00418         qDebug("completed now %d scanlines", completed_scanlines);
00419 #endif
00420 
00421         if ( cinfo.output_components == 3 ) {
00422             // Expand 24->32 bpp.
00423             for (int j=oldoutput_scanline; j<oldoutput_scanline+completed_scanlines; j++) {
00424                 uchar *in = image.scanLine(j) + cinfo.output_width * 3;
00425                 QRgb *out = (QRgb*)image.scanLine(j);
00426 
00427                 for (uint i=cinfo.output_width; i--; ) {
00428                     in-=3;
00429                     out[i] = qRgb(in[0], in[1], in[2]);
00430                 }
00431             }
00432         }
00433 
00434         if(consumer && completed_scanlines)
00435         {
00436             QRect r(0, oldoutput_scanline, cinfo.output_width, completed_scanlines);
00437 #ifdef JPEG_DEBUG
00438             qDebug("changing %d/%d %d/%d", r.x(), r.y(), r.width(), r.height());
00439 #endif
00440             jsrc.change_rect |= r;
00441 
00442             if ( jsrc.decoder_timestamp.elapsed() >= max_consumingtime ) {
00443                 consumer->changed(jsrc.change_rect);
00444                 jsrc.change_rect = QRect();
00445                 jsrc.decoder_timestamp.restart();
00446             }
00447         }
00448 
00449         if(cinfo.output_scanline >= cinfo.output_height)
00450         {
00451             if ( jsrc.do_progressive ) {
00452                 jpeg_finish_output(&cinfo);
00453                 jsrc.final_pass = jpeg_input_complete(&cinfo);
00454                 jsrc.decoding_done = jsrc.final_pass && cinfo.input_scan_number == cinfo.output_scan_number;
00455                 if ( !jsrc.decoding_done )
00456                     jsrc.change_rect =  QRect();
00457             }
00458             else
00459                 jsrc.decoding_done = true;
00460 
00461 #ifdef JPEG_DEBUG
00462             qDebug("one pass is completed, final_pass = %d, dec_done: %d, complete: %d",
00463                    jsrc.final_pass, jsrc.decoding_done, jpeg_input_complete(&cinfo));
00464 #endif
00465             if(!jsrc.decoding_done)
00466             {
00467 #ifdef JPEG_DEBUG
00468                 qDebug("starting another one, input_scan_number is %d/%d", cinfo.input_scan_number,
00469                        cinfo.output_scan_number);
00470 #endif
00471                 // don't return until necessary!
00472                 jsrc.decoder_timestamp.restart();
00473                 state = decompressStarted;
00474             }
00475         }
00476 
00477         if(state == doOutputScan && jsrc.decoding_done) {
00478 #ifdef JPEG_DEBUG
00479             qDebug("input is complete, cleaning up, returning..");
00480 #endif
00481             if ( consumer && !jsrc.change_rect.isEmpty() )
00482                 consumer->changed( jsrc.change_rect );
00483 
00484             if(consumer)
00485                 consumer->end();
00486 
00487             jsrc.ateof = true;
00488 
00489             (void) jpeg_finish_decompress(&cinfo);
00490             (void) jpeg_destroy_decompress(&cinfo);
00491 
00492             state = readDone;
00493 
00494             return 0;
00495         }
00496     }
00497 
00498 #ifdef BUFFER_DEBUG
00499     qDebug("valid_buffer_len is now %d", jsrc.valid_buffer_len);
00500     qDebug("bytes_in_buffer is now %d", jsrc.bytes_in_buffer);
00501     qDebug("consumed %d bytes", consumed);
00502 #endif
00503     if(jsrc.bytes_in_buffer && jsrc.buffer != jsrc.next_input_byte)
00504         memmove(jsrc.buffer, jsrc.next_input_byte, jsrc.bytes_in_buffer);
00505     jsrc.valid_buffer_len = jsrc.bytes_in_buffer;
00506     return consumed;
00507 }
00508 
00509 // -----------------------------------------------------------------------------
00510 // This is the factory that teaches Qt about progressive JPEG's
00511 
00512 QImageFormat* khtml::KJPEGFormatType::decoderFor(const unsigned char* buffer, int length)
00513 {
00514     if(length < 3) return 0;
00515 
00516     if(buffer[0] == 0377 &&
00517        buffer[1] == 0330 &&
00518        buffer[2] == 0377)
00519          return new KJPEGFormat;
00520 
00521     return 0;
00522 }
00523 
00524 const char* khtml::KJPEGFormatType::formatName() const
00525 {
00526     return "JPEG";
00527 }
00528 
00529 #else
00530 #ifdef __GNUC__
00531 #warning You don't seem to have libJPEG. jpeg support in khtml won't work
00532 #endif
00533 #endif // HAVE_LIBJPEG
00534 
00535 // -----------------------------------------------------------------------------
00536 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:29 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001