diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 01:02:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 01:02:50 +0000 |
commit | c66249b79aa9bfa0924494adcd5345b5b1244b0c (patch) | |
tree | 19a77c57cc41d8b522554fbde0c36d6f20d7dc7b /src/gvcore/jpegformattype.cpp | |
download | gwenview-c66249b79aa9bfa0924494adcd5345b5b1244b0c.tar.gz gwenview-c66249b79aa9bfa0924494adcd5345b5b1244b0c.zip |
Added old abandoned KDE3 version of gwenview
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/gwenview@1088034 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gvcore/jpegformattype.cpp')
-rw-r--r-- | src/gvcore/jpegformattype.cpp | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/src/gvcore/jpegformattype.cpp b/src/gvcore/jpegformattype.cpp new file mode 100644 index 0000000..ccb020e --- /dev/null +++ b/src/gvcore/jpegformattype.cpp @@ -0,0 +1,527 @@ +/* This file is based on kdelibs-3.2.0/khtml/misc/loader_jpeg.cpp. Original + * copyright follows. + */ +/* + This file is part of the KDE libraries + + Copyright (C) 2000 Dirk Mueller (mueller@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + + +// System +#include <stdio.h> +#include <setjmp.h> +extern "C" { +#define XMD_H +#include <jpeglib.h> +#undef const +} + +// Qt +#include <qdatetime.h> + +// KDE +#include <kdebug.h> +#include <kglobal.h> + +// Local +#include "jpegformattype.h" +#include "imageutils/jpegerrormanager.h" + +namespace Gwenview { + +#undef BUFFER_DEBUG +//#define BUFFER_DEBUG + +#undef JPEG_DEBUG +//#define JPEG_DEBUG + + +static const int MAX_BUFFER = 32768; +// how long it will consume data before starting outputing progressive scan +static const int MAX_CONSUMING_TIME = 100; + +//----------------------------------------------------------------------------- +// +// JPEGSourceManager +// (Does not follow HACKING file recommandation to be consistent with +// jpeg_source_mgr naming) +// +//----------------------------------------------------------------------------- +struct JPEGSourceManager : public jpeg_source_mgr { + JOCTET jpeg_buffer[MAX_BUFFER]; + + int valid_buffer_length; + size_t skip_input_bytes; + bool at_eof; + QRect change_rect; + QRect old_change_rect; + QTime decoder_timestamp; + bool final_pass; + bool decoding_done; + bool do_progressive; + + JPEGSourceManager() { + // jpeg_source_mgr fields + init_source = gvJPEGDummyDecompress; + fill_input_buffer = gvFillInputBuffer; + skip_input_data = gvSkipInputData; + resync_to_restart = jpeg_resync_to_restart; + term_source = gvJPEGDummyDecompress; + + bytes_in_buffer = 0; + next_input_byte = jpeg_buffer; + + // JPEGSourceManager fields + valid_buffer_length = 0; + skip_input_bytes = 0; + at_eof = 0; + final_pass = false; + decoding_done = false; + } + + static void gvJPEGDummyDecompress(j_decompress_ptr) {} + + /* Do not replace boolean with bool, it's the libjpeg boolean type */ + static boolean gvFillInputBuffer(j_decompress_ptr cinfo) { +#ifdef BUFFER_DEBUG + qDebug("FillInputBuffer called!"); +#endif + + JPEGSourceManager* src = (JPEGSourceManager*)cinfo->src; + + if ( src->at_eof ) { + /* Insert a fake EOI marker - as per jpeglib recommendation */ + src->jpeg_buffer[0] = (JOCTET) 0xFF; + src->jpeg_buffer[1] = (JOCTET) JPEG_EOI; + src->bytes_in_buffer = 2; + src->next_input_byte = (JOCTET *) src->jpeg_buffer; +#ifdef BUFFER_DEBUG + qDebug("...returning true!"); +#endif + return true; + } else { + return false; /* I/O suspension mode */ + } + } + + static void gvSkipInputData(j_decompress_ptr cinfo, long num_bytes) { + if(num_bytes <= 0) return; /* required noop */ + +#ifdef BUFFER_DEBUG + qDebug("SkipInputData (%d) called!", num_bytes); +#endif + + JPEGSourceManager* src = (JPEGSourceManager*)cinfo->src; + src->skip_input_bytes += num_bytes; + + unsigned int skipbytes = kMin(src->bytes_in_buffer, src->skip_input_bytes); + +#ifdef BUFFER_DEBUG + qDebug("skip_input_bytes is now %d", src->skip_input_bytes); + qDebug("skipbytes is now %d", skipbytes); + qDebug("valid_buffer_length is before %d", src->valid_buffer_length); + qDebug("bytes_in_buffer is %d", src->bytes_in_buffer); +#endif + + if(skipbytes < src->bytes_in_buffer) { + memmove(src->jpeg_buffer, + src->next_input_byte+skipbytes, + src->bytes_in_buffer - skipbytes); + } + + src->bytes_in_buffer -= skipbytes; + src->valid_buffer_length = src->bytes_in_buffer; + src->skip_input_bytes -= skipbytes; + + /* adjust data for jpeglib */ + cinfo->src->next_input_byte = (JOCTET *) src->jpeg_buffer; + cinfo->src->bytes_in_buffer = (size_t) src->valid_buffer_length; +#ifdef BUFFER_DEBUG + qDebug("valid_buffer_length is afterwards %d", src->valid_buffer_length); + qDebug("skip_input_bytes is now %d", src->skip_input_bytes); +#endif + + } +}; + + +//----------------------------------------------------------------------------- +// +// JPEGFormat +// +//----------------------------------------------------------------------------- +class JPEGFormat : public QImageFormat { +public: + JPEGFormat(); + + virtual ~JPEGFormat(); + + virtual int decode(QImage& img, QImageConsumer* consumer, + const uchar* buffer, int length); +private: + + enum { + INIT, + START_DECOMPRESS, + DECOMPRESS_STARTED, + CONSUME_INPUT, + PREPARE_OUTPUT_SCAN, + DO_OUTPUT_SCAN, + READ_DONE, + INVALID + } mState; + + // structs for the jpeglib + jpeg_decompress_struct mDecompress; + ImageUtils::JPEGErrorManager mErrorManager; + JPEGSourceManager mSourceManager; +}; + + +JPEGFormat::JPEGFormat() { + memset(&mDecompress, 0, sizeof(mDecompress)); + mDecompress.err = &mErrorManager; + jpeg_create_decompress(&mDecompress); + mDecompress.src = &mSourceManager; + mState = INIT; +} + + +JPEGFormat::~JPEGFormat() { + (void) jpeg_destroy_decompress(&mDecompress); +} + +/* + * return > 0 means "consumed x bytes, need more" + * return == 0 means "end of frame reached" + * return < 0 means "fatal error in image decoding, don't call me ever again" + */ + +int JPEGFormat::decode(QImage& image, QImageConsumer* consumer, const uchar* buffer, int length) { +#ifdef JPEG_DEBUG + qDebug("JPEGFormat::decode(%p, %p, %p, %d)", + &image, consumer, buffer, length); +#endif + + if(mSourceManager.at_eof) { +#ifdef JPEG_DEBUG + qDebug("at_eof, eating"); +#endif + return length; + } + + if(setjmp(mErrorManager.jmp_buffer)) { +#ifdef JPEG_DEBUG + qDebug("jump into state INVALID"); +#endif + if(consumer) consumer->end(); + + // this is fatal + return -1; + } + + int consumed = kMin(length, MAX_BUFFER - mSourceManager.valid_buffer_length); + +#ifdef BUFFER_DEBUG + qDebug("consuming %d bytes", consumed); +#endif + + // filling buffer with the new data + memcpy(mSourceManager.jpeg_buffer + mSourceManager.valid_buffer_length, buffer, consumed); + mSourceManager.valid_buffer_length += consumed; + + if(mSourceManager.skip_input_bytes) { +#ifdef BUFFER_DEBUG + qDebug("doing skipping"); + qDebug("valid_buffer_length %d", mSourceManager.valid_buffer_length); + qDebug("skip_input_bytes %d", mSourceManager.skip_input_bytes); +#endif + int skipbytes = kMin((size_t) mSourceManager.valid_buffer_length, mSourceManager.skip_input_bytes); + + if(skipbytes < mSourceManager.valid_buffer_length) { + memmove(mSourceManager.jpeg_buffer, + mSourceManager.jpeg_buffer+skipbytes, + mSourceManager.valid_buffer_length - skipbytes); + } + + mSourceManager.valid_buffer_length -= skipbytes; + mSourceManager.skip_input_bytes -= skipbytes; + + // still more bytes to skip + if(mSourceManager.skip_input_bytes) { + if(consumed <= 0) qDebug("ERROR!!!"); + return consumed; + } + + } + + mDecompress.src->next_input_byte = (JOCTET *) mSourceManager.jpeg_buffer; + mDecompress.src->bytes_in_buffer = (size_t) mSourceManager.valid_buffer_length; + +#ifdef BUFFER_DEBUG + qDebug("buffer contains %d bytes", mSourceManager.valid_buffer_length); +#endif + + if(mState == INIT) { + if(jpeg_read_header(&mDecompress, true) != JPEG_SUSPENDED) { + if (consumer) { + consumer->setSize( + mDecompress.image_width/mDecompress.scale_denom, + mDecompress.image_height/mDecompress.scale_denom); + } + + mState = START_DECOMPRESS; + } + } + + if(mState == START_DECOMPRESS) { + mSourceManager.do_progressive = jpeg_has_multiple_scans( &mDecompress ); + +#ifdef JPEG_DEBUG + qDebug( "**** DOPROGRESSIVE: %d", mSourceManager.do_progressive ); +#endif + mDecompress.buffered_image = mSourceManager.do_progressive; + + // setup image sizes + jpeg_calc_output_dimensions( &mDecompress ); + + if (mDecompress.jpeg_color_space == JCS_YCbCr) { + mDecompress.out_color_space = JCS_RGB; + } + + mDecompress.do_fancy_upsampling = true; + mDecompress.do_block_smoothing = false; + mDecompress.quantize_colors = false; + + // false: IO suspension + if(jpeg_start_decompress(&mDecompress)) { + if ( mDecompress.output_components == 3 || mDecompress.output_components == 4) { + image.create( mDecompress.output_width, mDecompress.output_height, 32 ); + } else if ( mDecompress.output_components == 1 ) { + image.create( mDecompress.output_width, mDecompress.output_height, 8, 256 ); + for (int i=0; i<256; i++) { + image.setColor(i, qRgb(i,i,i)); + } + } + +#ifdef JPEG_DEBUG + qDebug("will create a picture %d/%d in size", mDecompress.output_width, mDecompress.output_height); +#endif + +#ifdef JPEG_DEBUG + qDebug("ok, going to DECOMPRESS_STARTED"); +#endif + + mSourceManager.decoder_timestamp.start(); + mState = mSourceManager.do_progressive ? DECOMPRESS_STARTED : DO_OUTPUT_SCAN; + } + } + +again: + + if(mState == DECOMPRESS_STARTED) { + mState = (!mSourceManager.final_pass && mSourceManager.decoder_timestamp.elapsed() < MAX_CONSUMING_TIME) + ? CONSUME_INPUT : PREPARE_OUTPUT_SCAN; + } + + if(mState == CONSUME_INPUT) { + int retval; + + do { + retval = jpeg_consume_input(&mDecompress); + } while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_EOI + && (retval != JPEG_REACHED_SOS || mSourceManager.decoder_timestamp.elapsed() < MAX_CONSUMING_TIME)); + + if( mSourceManager.final_pass + || mSourceManager.decoder_timestamp.elapsed() >= MAX_CONSUMING_TIME + || retval == JPEG_REACHED_EOI + || retval == JPEG_REACHED_SOS) { + mState = PREPARE_OUTPUT_SCAN; + } + } + + if(mState == PREPARE_OUTPUT_SCAN) { + if ( jpeg_start_output(&mDecompress, mDecompress.input_scan_number) ) { + mState = DO_OUTPUT_SCAN; + } + } + + if(mState == DO_OUTPUT_SCAN) { + if(image.isNull() || mSourceManager.decoding_done) { +#ifdef JPEG_DEBUG + qDebug("complete in doOutputscan, eating.."); +#endif + return consumed; + } + uchar** lines = image.jumpTable(); + int oldoutput_scanline = mDecompress.output_scanline; + + while(mDecompress.output_scanline < mDecompress.output_height && + jpeg_read_scanlines(&mDecompress, lines+mDecompress.output_scanline, mDecompress.output_height)) + ; // here happens all the magic of decoding + + int completed_scanlines = mDecompress.output_scanline - oldoutput_scanline; +#ifdef JPEG_DEBUG + qDebug("completed now %d scanlines", completed_scanlines); +#endif + + if ( mDecompress.output_components == 3 ) { + // Expand 24->32 bpp. + for (int j=oldoutput_scanline; j<oldoutput_scanline+completed_scanlines; j++) { + uchar *in = image.scanLine(j) + mDecompress.output_width * 3; + QRgb *out = (QRgb*)image.scanLine(j); + + for (uint i=mDecompress.output_width; i--; ) { + in-=3; + out[i] = qRgb(in[0], in[1], in[2]); + } + } + } + + if(consumer && completed_scanlines) { + QRect r(0, oldoutput_scanline, mDecompress.output_width, completed_scanlines); +#ifdef JPEG_DEBUG + qDebug("changing %d/%d %d/%d", r.x(), r.y(), r.width(), r.height()); +#endif + mSourceManager.change_rect |= r; + + if ( mSourceManager.decoder_timestamp.elapsed() >= MAX_CONSUMING_TIME ) { + if( !mSourceManager.old_change_rect.isEmpty()) { + consumer->changed(mSourceManager.old_change_rect); + mSourceManager.old_change_rect = QRect(); + } + consumer->changed(mSourceManager.change_rect); + mSourceManager.change_rect = QRect(); + mSourceManager.decoder_timestamp.restart(); + } + } + + if(mDecompress.output_scanline >= mDecompress.output_height) { + if ( mSourceManager.do_progressive ) { + jpeg_finish_output(&mDecompress); + mSourceManager.final_pass = jpeg_input_complete(&mDecompress); + mSourceManager.decoding_done = mSourceManager.final_pass && mDecompress.input_scan_number == mDecompress.output_scan_number; + if ( !mSourceManager.decoding_done ) { + mSourceManager.old_change_rect |= mSourceManager.change_rect; + mSourceManager.change_rect = QRect(); + } + } else { + mSourceManager.decoding_done = true; + } +#ifdef JPEG_DEBUG + qDebug("one pass is completed, final_pass = %d, dec_done: %d, complete: %d", + mSourceManager.final_pass, mSourceManager.decoding_done, jpeg_input_complete(&mDecompress)); +#endif + if(!mSourceManager.decoding_done) { +#ifdef JPEG_DEBUG + qDebug("starting another one, input_scan_number is %d/%d", mDecompress.input_scan_number, + mDecompress.output_scan_number); +#endif + mSourceManager.decoder_timestamp.restart(); + mState = DECOMPRESS_STARTED; + // don't return until necessary! + goto again; + } + } + + if(mState == DO_OUTPUT_SCAN && mSourceManager.decoding_done) { +#ifdef JPEG_DEBUG + qDebug("input is complete, cleaning up, returning.."); +#endif + if ( consumer && !mSourceManager.change_rect.isEmpty() ) { + consumer->changed( mSourceManager.change_rect ); + } + + if(consumer) consumer->end(); + + // get the density X and Y info and the related units to have + // the aspect ratio of the image + // field: units -- one byte: Units for the X and Y densities + // 0 => no units, X and Y specify the pixel aspect ratio + // 1 => X and Y are dots per inch + // 2 => X and Y are dots per cm + // Xdensity -- two bytes + // Ydensity -- two bytes + const float INCHESPERMETER = (100. / 2.54); + switch (mDecompress.density_unit) + { + case 0: // no units + break; + case 1: // dots per inch + image.setDotsPerMeterX(int(mDecompress.X_density * INCHESPERMETER)); + image.setDotsPerMeterY(int(mDecompress.Y_density * INCHESPERMETER)); + break; + case 2: // dots per cm + image.setDotsPerMeterX(mDecompress.X_density * 100); + image.setDotsPerMeterY(mDecompress.Y_density * 100); + break; + } + + mSourceManager.at_eof = true; + + (void) jpeg_finish_decompress(&mDecompress); + (void) jpeg_destroy_decompress(&mDecompress); + + mState = READ_DONE; + + return 0; + } + } + +#ifdef BUFFER_DEBUG + qDebug("valid_buffer_length is now %d", mSourceManager.valid_buffer_length); + qDebug("bytes_in_buffer is now %d", mSourceManager.bytes_in_buffer); + qDebug("consumed %d bytes", consumed); +#endif + if(mSourceManager.bytes_in_buffer + && mSourceManager.jpeg_buffer != mSourceManager.next_input_byte) { + memmove(mSourceManager.jpeg_buffer, + mSourceManager.next_input_byte, + mSourceManager.bytes_in_buffer); + } + mSourceManager.valid_buffer_length = mSourceManager.bytes_in_buffer; + return consumed; +} + + +//----------------------------------------------------------------------------- +// +// JPEGFormatType +// +//----------------------------------------------------------------------------- +QImageFormat* JPEGFormatType::decoderFor(const uchar* buffer, int length) { + if(length < 3) return 0; + + if(buffer[0] == 0377 && + buffer[1] == 0330 && + buffer[2] == 0377) { + return new JPEGFormat; + } + + return 0; +} + +const char* JPEGFormatType::formatName() const { + return "JPEG"; +} + +} // namespace |