File indexing completed on 2024-05-12 15:38:28

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     Copyright (C) 2004 Maks Orlovich (maksim@kde.org)
0005     Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
0006 
0007     Permission is hereby granted, free of charge, to any person obtaining a copy
0008     of this software and associated documentation files (the "Software"), to deal
0009     in the Software without restriction, including without limitation the rights
0010     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0011     copies of the Software, and to permit persons to whom the Software is
0012     furnished to do so, subject to the following conditions:
0013 
0014     The above copyright notice and this permission notice shall be included in
0015     all copies or substantial portions of the Software.
0016 
0017     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0018     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0019     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
0020     AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
0021     AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0022     CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0023 
0024 */
0025 #include "jpegloader.h"
0026 
0027 //### FIXME: I removed all the fancy configury stuff. it needs to be put back
0028 
0029 #include <stdio.h>
0030 #include <setjmp.h>
0031 #include <QDate>
0032 #include <QImage>
0033 #include <QElapsedTimer>
0034 
0035 #include "imageloader.h"
0036 #include "imagemanager.h"
0037 
0038 extern "C" {
0039 #define XMD_H
0040 #include <jpeglib.h>
0041 #undef const
0042 }
0043 
0044 #undef BUFFER_DEBUG
0045 //#define BUFFER_DEBUG
0046 
0047 #undef JPEG_DEBUG
0048 //#define JPEG_DEBUG
0049 
0050 namespace khtmlImLoad
0051 {
0052 
0053 class JPEGLoader: public ImageLoader
0054 {
0055     struct Private;
0056     friend struct Private;
0057     Private *d;
0058 public:
0059     JPEGLoader();
0060     ~JPEGLoader();
0061     int processData(uchar *data, int length) override;
0062 };
0063 
0064 ImageLoaderProvider::Type JPEGLoaderProvider::type()
0065 {
0066     return Efficient;
0067 }
0068 
0069 ImageLoader *JPEGLoaderProvider::loaderFor(const QByteArray &prefix)
0070 {
0071     uchar *data = (uchar *)prefix.data();
0072     if (prefix.size() < 3) {
0073         return nullptr;
0074     }
0075 
0076     if (data[0] == 0377 &&
0077             data[1] == 0330 &&
0078             data[2] == 0377) {
0079         return new JPEGLoader;
0080     }
0081 
0082     return nullptr;
0083 }
0084 
0085 // -----------------------------------------------------------------------------
0086 
0087 struct khtml_error_mgr : public jpeg_error_mgr {
0088     jmp_buf setjmp_buffer;
0089 };
0090 
0091 extern "C" {
0092 
0093     static
0094     void khtml_error_exit(j_common_ptr cinfo)
0095     {
0096         khtml_error_mgr *myerr = (khtml_error_mgr *) cinfo->err;
0097         char buffer[JMSG_LENGTH_MAX];
0098         (*cinfo->err->format_message)(cinfo, buffer);
0099         qWarning("%s", buffer);
0100         longjmp(myerr->setjmp_buffer, 1);
0101     }
0102 }
0103 
0104 static const int max_buf = 8192;
0105 static const int max_consumingtime = 500;
0106 
0107 struct khtml_jpeg_source_mgr : public jpeg_source_mgr {
0108     JOCTET buffer[max_buf];
0109 
0110     int valid_buffer_len;
0111     size_t skip_input_bytes;
0112     int ateof;
0113     QElapsedTimer decoder_timestamp;
0114     bool final_pass;
0115     bool decoding_done;
0116     bool do_progressive;
0117 public:
0118     khtml_jpeg_source_mgr();
0119 };
0120 
0121 extern "C" {
0122 
0123     static
0124     void khtml_j_decompress_dummy(j_decompress_ptr)
0125     {
0126     }
0127 
0128     static
0129     boolean khtml_fill_input_buffer(j_decompress_ptr cinfo)
0130     {
0131 #ifdef BUFFER_DEBUG
0132         qDebug("khtml_fill_input_buffer called!");
0133 #endif
0134 
0135         khtml_jpeg_source_mgr *src = (khtml_jpeg_source_mgr *)cinfo->src;
0136 
0137         if (src->ateof) {
0138             /* Insert a fake EOI marker - as per jpeglib recommendation */
0139             src->buffer[0] = (JOCTET) 0xFF;
0140             src->buffer[1] = (JOCTET) JPEG_EOI;
0141             src->bytes_in_buffer = 2;
0142             src->next_input_byte = (JOCTET *) src->buffer;
0143 #ifdef BUFFER_DEBUG
0144             qDebug("...returning true!");
0145 #endif
0146             return TRUE;
0147         } else {
0148             return FALSE;    /* I/O suspension mode */
0149         }
0150     }
0151 
0152     static
0153     void khtml_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
0154     {
0155         if (num_bytes <= 0) {
0156             return;    /* required noop */
0157         }
0158 
0159 #ifdef BUFFER_DEBUG
0160         qDebug("khtml_skip_input_data (%d) called!", num_bytes);
0161 #endif
0162 
0163         khtml_jpeg_source_mgr *src = (khtml_jpeg_source_mgr *)cinfo->src;
0164         src->skip_input_bytes += num_bytes;
0165 
0166         unsigned int skipbytes = qMin(src->bytes_in_buffer, src->skip_input_bytes);
0167 
0168 #ifdef BUFFER_DEBUG
0169         qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
0170         qDebug("skipbytes is now %d", skipbytes);
0171         qDebug("valid_buffer_len is before %d", src->valid_buffer_len);
0172         qDebug("bytes_in_buffer is %d", src->bytes_in_buffer);
0173 #endif
0174 
0175         if (skipbytes < src->bytes_in_buffer) {
0176             memmove(src->buffer, src->next_input_byte + skipbytes, src->bytes_in_buffer - skipbytes);
0177         }
0178 
0179         src->bytes_in_buffer -= skipbytes;
0180         src->valid_buffer_len = src->bytes_in_buffer;
0181         src->skip_input_bytes -= skipbytes;
0182 
0183         /* adjust data for jpeglib */
0184         cinfo->src->next_input_byte = (JOCTET *) src->buffer;
0185         cinfo->src->bytes_in_buffer = (size_t) src->valid_buffer_len;
0186 #ifdef BUFFER_DEBUG
0187         qDebug("valid_buffer_len is afterwards %d", src->valid_buffer_len);
0188         qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
0189 #endif
0190     }
0191 }
0192 
0193 khtml_jpeg_source_mgr::khtml_jpeg_source_mgr()
0194 {
0195     jpeg_source_mgr::init_source = khtml_j_decompress_dummy;
0196     jpeg_source_mgr::fill_input_buffer = khtml_fill_input_buffer;
0197     jpeg_source_mgr::skip_input_data = khtml_skip_input_data;
0198     jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
0199     jpeg_source_mgr::term_source = khtml_j_decompress_dummy;
0200     bytes_in_buffer = 0;
0201     valid_buffer_len = 0;
0202     skip_input_bytes = 0;
0203     ateof = 0;
0204     next_input_byte = buffer;
0205     final_pass = false;
0206     decoding_done = false;
0207 }
0208 
0209 struct JPEGLoader::Private {
0210     int processData(uchar *data, int length);
0211     Private();
0212     ~Private();
0213 
0214     JPEGLoader *owner;
0215 private:
0216     int    passNum;
0217     uchar *scanline;
0218 
0219     enum {
0220         Init,
0221         readHeader,
0222         startDecompress,
0223         decompressStarted,
0224         consumeInput,
0225         prepareOutputScan,
0226         doOutputScan,
0227         readDone,
0228         invalid
0229     } state;
0230 
0231     // structs for the jpeglib
0232     struct jpeg_decompress_struct cinfo;
0233     struct khtml_error_mgr jerr;
0234     struct khtml_jpeg_source_mgr jsrc;
0235 };
0236 
0237 JPEGLoader::Private::Private()
0238 {
0239     scanline = nullptr;
0240     passNum  = 0;
0241 
0242     memset(&cinfo, 0, sizeof(cinfo));
0243     cinfo.err = jpeg_std_error(&jerr);
0244     jpeg_create_decompress(&cinfo);
0245     cinfo.err = jpeg_std_error(&jerr);
0246     jerr.error_exit = khtml_error_exit;
0247     cinfo.src = &jsrc;
0248     state = Init;
0249 }
0250 
0251 JPEGLoader::Private::~Private()
0252 {
0253     delete[] scanline;
0254     (void) jpeg_destroy_decompress(&cinfo);
0255 }
0256 
0257 int JPEGLoader::Private::processData(uchar *buffer, int length)
0258 {
0259     if (jsrc.ateof) {
0260 #ifdef JPEG_DEBUG
0261         qDebug("ateof, eating");
0262 #endif
0263         return ImageLoader::Done;
0264     }
0265 
0266     if (setjmp(jerr.setjmp_buffer)) {
0267 #ifdef JPEG_DEBUG
0268         qDebug("jump into state invalid");
0269 #endif
0270 
0271         // this is fatal
0272         return ImageLoader::Error;
0273     }
0274 
0275     int consumed = qMin(length, max_buf - jsrc.valid_buffer_len);
0276 
0277 #ifdef BUFFER_DEBUG
0278     qDebug("consuming %d bytes", consumed);
0279 #endif
0280 
0281     // filling buffer with the new data
0282     memcpy(jsrc.buffer + jsrc.valid_buffer_len, buffer, consumed);
0283     jsrc.valid_buffer_len += consumed;
0284 
0285     if (jsrc.skip_input_bytes) {
0286 #ifdef BUFFER_DEBUG
0287         qDebug("doing skipping");
0288         qDebug("valid_buffer_len %d", jsrc.valid_buffer_len);
0289         qDebug("skip_input_bytes %d", jsrc.skip_input_bytes);
0290 #endif
0291         int skipbytes = qMin((size_t) jsrc.valid_buffer_len, jsrc.skip_input_bytes);
0292 
0293         if (skipbytes < jsrc.valid_buffer_len) {
0294             memmove(jsrc.buffer, jsrc.buffer + skipbytes, jsrc.valid_buffer_len - skipbytes);
0295         }
0296 
0297         jsrc.valid_buffer_len -= skipbytes;
0298         jsrc.skip_input_bytes -= skipbytes;
0299 
0300         // still more bytes to skip
0301         if (jsrc.skip_input_bytes) {
0302             if (consumed <= 0) {
0303                 qDebug("ERROR!!!");
0304             }
0305             return consumed;
0306         }
0307     }
0308 
0309     cinfo.src->next_input_byte = (JOCTET *) jsrc.buffer;
0310     cinfo.src->bytes_in_buffer = (size_t) jsrc.valid_buffer_len;
0311 
0312 #ifdef BUFFER_DEBUG
0313     qDebug("buffer contains %d bytes", jsrc.valid_buffer_len);
0314 #endif
0315 
0316     if (state == Init) {
0317         if (jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED) {
0318             state = startDecompress;
0319 
0320             // libJPEG can scale down 2x, 4x, and 8x,
0321             // so do this for oversize images.
0322             int scaleDown = 1;
0323             while (scaleDown <= 8 && !ImageManager::isAcceptableSize(
0324                         cinfo.image_width / scaleDown, cinfo.image_height / scaleDown)) {
0325                 scaleDown *= 2;
0326             }
0327 
0328             cinfo.scale_denom *= scaleDown;
0329 
0330             if (scaleDown > 8) {
0331                 // Still didn't fit... Abort.
0332                 return ImageLoader::Error;
0333             }
0334         }
0335     }
0336 
0337     if (state == startDecompress) {
0338         jsrc.do_progressive = jpeg_has_multiple_scans(&cinfo);
0339         if (jsrc.do_progressive) {
0340             cinfo.buffered_image = TRUE;
0341         } else {
0342             cinfo.buffered_image = FALSE;
0343         }
0344         // setup image sizes
0345         jpeg_calc_output_dimensions(&cinfo);
0346 
0347         if (cinfo.jpeg_color_space == JCS_YCbCr) {
0348             cinfo.out_color_space = JCS_RGB;
0349         }
0350 
0351         if (cinfo.jpeg_color_space == JCS_YCCK) {
0352             cinfo.out_color_space = JCS_CMYK;
0353         }
0354 
0355         cinfo.do_fancy_upsampling = TRUE;
0356         cinfo.do_block_smoothing = FALSE;
0357         cinfo.quantize_colors = FALSE;
0358 
0359         // false: IO suspension
0360         if (jpeg_start_decompress(&cinfo)) {
0361             ImageFormat f;
0362             if (cinfo.output_components == 3 || cinfo.output_components == 4) {
0363                 f.type = ImageFormat::Image_RGB_32;
0364                 scanline = new uchar[cinfo.output_width * 4];
0365 
0366             } else if (cinfo.output_components == 1) {
0367                 f.greyscaleSetup();
0368                 scanline = new uchar[cinfo.output_width];
0369             }
0370             // ### else return Error?
0371 
0372             owner->notifySingleFrameImage(cinfo.output_width, cinfo.output_height, f);
0373 
0374 #ifdef JPEG_DEBUG
0375             qDebug("will create a picture %d/%d in size", cinfo.output_width, cinfo.output_height);
0376 #endif
0377 
0378 #ifdef JPEG_DEBUG
0379             qDebug("ok, going to decompressStarted");
0380 #endif
0381 
0382             jsrc.decoder_timestamp.start();
0383             state = jsrc.do_progressive ? decompressStarted : doOutputScan;
0384         }
0385     }
0386 
0387     if (state == decompressStarted) {
0388         state = (!jsrc.final_pass && jsrc.decoder_timestamp.elapsed() < max_consumingtime)
0389                 ? consumeInput : prepareOutputScan;
0390     }
0391 
0392     if (state == consumeInput) {
0393         int retval;
0394 
0395         do {
0396             retval = jpeg_consume_input(&cinfo);
0397         } while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_EOI);
0398 
0399         if (jsrc.decoder_timestamp.elapsed() > max_consumingtime || jsrc.final_pass ||
0400                 retval == JPEG_REACHED_EOI || retval == JPEG_REACHED_SOS) {
0401             state = prepareOutputScan;
0402         }
0403     }
0404 
0405     if (state == prepareOutputScan) {
0406         jsrc.decoder_timestamp.restart();
0407         if (jpeg_start_output(&cinfo, cinfo.input_scan_number)) {
0408             state = doOutputScan;
0409         }
0410     }
0411 
0412     if (state == doOutputScan) {
0413         if (!scanline || jsrc.decoding_done) {
0414 #ifdef JPEG_DEBUG
0415             qDebug("complete in doOutputscan, eating..");
0416 #endif
0417             return consumed;
0418         }
0419         uchar *lines[1] = {scanline};
0420         //int oldoutput_scanline = cinfo.output_scanline;
0421 
0422         //Decode and feed line-by-line
0423         while (cinfo.output_scanline < cinfo.output_height) {
0424             if (!jpeg_read_scanlines(&cinfo, lines, 1)) {
0425                 break;
0426             }
0427 
0428             if (cinfo.output_components == 3) {
0429                 // Expand 24->32 bpp.
0430                 uchar *in = scanline + cinfo.output_width * 3;
0431                 QRgb *out = (QRgb *)scanline;
0432 
0433                 for (uint i = cinfo.output_width; i--;) {
0434                     in -= 3;
0435                     out[i] = qRgb(in[0], in[1], in[2]);
0436                 }
0437             } else if (cinfo.out_color_space == JCS_CMYK) {
0438                 uchar *in = scanline + cinfo.output_width * 4;
0439                 QRgb *out = (QRgb *) scanline;
0440 
0441                 for (uint i = cinfo.output_width; i--;) {
0442                     in -= 4;
0443                     int k = in[3];
0444                     out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
0445                 }
0446             }
0447 
0448             owner->notifyScanline(passNum + 1, scanline);
0449         } //per-line scan
0450 
0451         if (cinfo.output_scanline >= cinfo.output_height) {
0452             passNum++;
0453 
0454             if (jsrc.do_progressive) {
0455                 jpeg_finish_output(&cinfo);
0456                 jsrc.final_pass = jpeg_input_complete(&cinfo);
0457                 jsrc.decoding_done = jsrc.final_pass && cinfo.input_scan_number == cinfo.output_scan_number;
0458             } else {
0459                 jsrc.decoding_done = true;
0460             }
0461 
0462             if (passNum > ImageLoader::FinalVersionID) {
0463                 qWarning("JPEG Decoder: Too many interlacing passes needed");
0464                 jsrc.decoding_done = true; //Force exit
0465             }
0466 
0467 #ifdef JPEG_DEBUG
0468             qDebug("one pass is completed, final_pass = %d, dec_done: %d, complete: %d",
0469                    jsrc.final_pass, jsrc.decoding_done, jpeg_input_complete(&cinfo));
0470 #endif
0471             if (!jsrc.decoding_done) {
0472 #ifdef JPEG_DEBUG
0473                 qDebug("starting another one, input_scan_number is %d/%d", cinfo.input_scan_number,
0474                        cinfo.output_scan_number);
0475 #endif
0476                 jsrc.decoder_timestamp.restart();
0477                 state = decompressStarted;
0478             }
0479         }
0480     }
0481 
0482     if (state == doOutputScan && jsrc.decoding_done) {
0483 #ifdef JPEG_DEBUG
0484         qDebug("input is complete, cleaning up, returning..");
0485 #endif
0486 
0487         jsrc.ateof = true;
0488 
0489         (void) jpeg_finish_decompress(&cinfo);
0490         (void) jpeg_destroy_decompress(&cinfo);
0491 
0492         state = readDone;
0493 
0494         return Done;
0495     }
0496 
0497 #ifdef BUFFER_DEBUG
0498     qDebug("valid_buffer_len is now %d", jsrc.valid_buffer_len);
0499     qDebug("bytes_in_buffer is now %d", jsrc.bytes_in_buffer);
0500     qDebug("consumed %d bytes", consumed);
0501 #endif
0502     if (jsrc.bytes_in_buffer && jsrc.buffer != jsrc.next_input_byte) {
0503         memmove(jsrc.buffer, jsrc.next_input_byte, jsrc.bytes_in_buffer);
0504     }
0505     jsrc.valid_buffer_len = jsrc.bytes_in_buffer;
0506     return consumed;
0507 }
0508 
0509 JPEGLoader::JPEGLoader()
0510 {
0511     d = new Private;
0512     d->owner = this;
0513 }
0514 
0515 JPEGLoader::~JPEGLoader()
0516 {
0517     delete d;
0518 }
0519 
0520 int JPEGLoader::processData(uchar *data, int length)
0521 {
0522     return d->processData(data, length);
0523 }
0524 
0525 }
0526