# khtml Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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

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