📄 qjpeghandler.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the plugins of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qjpeghandler.h"#include <qimage.h>#include <qvariant.h>#include <qvector.h>#include <stdio.h> // jpeglib needs this to be pre-included#include <setjmp.h>#ifdef FAR#undef FAR#endif// including jpeglib.h seems to be a little messyextern "C" {// mingw includes rpcndr.h but does not define boolean#if defined(Q_OS_WIN) && defined(Q_CC_GNU)# if defined(__RPCNDR_H__) && !defined(boolean) typedef unsigned char boolean;# define HAVE_BOOLEAN# endif#endif#define XMD_H // shut JPEGlib up#if defined(Q_OS_UNIXWARE)# define HAVE_BOOLEAN // libjpeg under Unixware seems to need this#endif#include <jpeglib.h>#ifdef const# undef const // remove crazy C hackery in jconfig.h#endif}struct my_error_mgr : public jpeg_error_mgr { jmp_buf setjmp_buffer;};#if defined(Q_C_CALLBACKS)extern "C" {#endifstaticvoid my_error_exit (j_common_ptr cinfo){ my_error_mgr* myerr = (my_error_mgr*) cinfo->err; char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message)(cinfo, buffer); qWarning("%s", buffer); longjmp(myerr->setjmp_buffer, 1);}#if defined(Q_C_CALLBACKS)}#endifstatic const int max_buf = 4096;struct my_jpeg_source_mgr : public jpeg_source_mgr { // Nothing dynamic - cannot rely on destruction over longjump QIODevice *device; JOCTET buffer[max_buf];public: my_jpeg_source_mgr(QIODevice *device);};#if defined(Q_C_CALLBACKS)extern "C" {#endifstaticvoid qt_init_source(j_decompress_ptr){}staticboolean qt_fill_input_buffer(j_decompress_ptr cinfo){ int num_read; my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; src->next_input_byte = src->buffer; num_read = src->device->read((char*)src->buffer, max_buf); if (num_read <= 0) { // Insert a fake EOI marker - as per jpeglib recommendation src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; src->bytes_in_buffer = 2; } else { src->bytes_in_buffer = num_read; }#if defined(Q_OS_UNIXWARE) return B_TRUE;#else return true;#endif}staticvoid qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes){ my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; // `dumb' implementation from jpeglib /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth * any trouble anyway --- large skips are infrequent. */ if (num_bytes > 0) { while (num_bytes > (long) src->bytes_in_buffer) { num_bytes -= (long) src->bytes_in_buffer; (void) qt_fill_input_buffer(cinfo); /* note we assume that qt_fill_input_buffer will never return false, * so suspension need not be handled. */ } src->next_input_byte += (size_t) num_bytes; src->bytes_in_buffer -= (size_t) num_bytes; }}staticvoid qt_term_source(j_decompress_ptr){}#if defined(Q_C_CALLBACKS)}#endifinline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device){ jpeg_source_mgr::init_source = qt_init_source; jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer; jpeg_source_mgr::skip_input_data = qt_skip_input_data; jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart; jpeg_source_mgr::term_source = qt_term_source; this->device = device; bytes_in_buffer = 0; next_input_byte = buffer;}staticvoid scaleSize(int &reqW, int &reqH, int imgW, int imgH, Qt::AspectRatioMode mode){ if (mode == Qt::IgnoreAspectRatio) return; int t1 = imgW * reqH; int t2 = reqW * imgH; if ((mode == Qt::KeepAspectRatio && (t1 > t2)) || (mode == Qt::KeepAspectRatioByExpanding && (t1 < t2))) reqH = t2 / imgW; else reqW = t1 / imgH;}static bool read_jpeg_image(QIODevice *device, QImage *outImage, const QByteArray ¶meters){ QImage image; struct jpeg_decompress_struct cinfo; struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device); struct my_error_mgr jerr; jpeg_create_decompress(&cinfo); cinfo.src = iod_src; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = my_error_exit; if (!setjmp(jerr.setjmp_buffer)) {#if defined(Q_OS_UNIXWARE) (void) jpeg_read_header(&cinfo, B_TRUE);#else (void) jpeg_read_header(&cinfo, true);#endif (void) jpeg_start_decompress(&cinfo); QString params = parameters; params.simplified(); int sWidth = 0, sHeight = 0; char sModeStr[1024] = ""; Qt::AspectRatioMode sMode; if (params.contains("GetHeaderInformation")) { // Create QImage but don't read the pixels if (cinfo.output_components == 3 || cinfo.output_components == 4) { image = QImage(cinfo.output_width, cinfo.output_height, QImage::Format_RGB32); } else if (cinfo.output_components == 1) { image = QImage(cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8); } else { // Unsupported format } } else if (params.contains("Scale")) {#if defined(_MSC_VER) && _MSC_VER >= 1400 sscanf_s(params.toLatin1().data(), "Scale(%i, %i, %1023s)", &sWidth, &sHeight, sModeStr, sizeof(sModeStr));#else sscanf(params.toLatin1().data(), "Scale(%i, %i, %1023s)", &sWidth, &sHeight, sModeStr);#endif QString sModeQStr(sModeStr); if (sModeQStr == "IgnoreAspectRatio") { sMode = Qt::IgnoreAspectRatio; } else if (sModeQStr == "KeepAspectRatio") { sMode = Qt::KeepAspectRatio; } else if (sModeQStr == "KeepAspectRatioByExpanding") { sMode = Qt::KeepAspectRatioByExpanding; } else { qDebug("read_jpeg_image: invalid aspect ratio mode \"%s\", see QImage::AspectRatioMode documentation", sModeStr); sMode = Qt::KeepAspectRatio; }// qDebug("Parameters ask to scale the image to %i x %i AspectRatioMode: %s", sWidth, sHeight, sModeStr); scaleSize(sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode);// qDebug("Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr); if (cinfo.output_components == 3 || cinfo.output_components == 4) { image = QImage(sWidth, sHeight, QImage::Format_RGB32); } else if (cinfo.output_components == 1) { image = QImage(sWidth, sHeight, QImage::Format_Indexed8); image.setNumColors(256); for (int i=0; i<256; i++) image.setColor(i, qRgb(i,i,i)); } else { // Unsupported format } if (image.isNull()) return false; if (!image.isNull()) { QImage tmpImage(cinfo.output_width, 1, QImage::Format_RGB32); uchar* inData = tmpImage.bits(); uchar* outData = image.bits(); int out_bpl = image.bytesPerLine(); while (cinfo.output_scanline < cinfo.output_height) { int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height; (void) jpeg_read_scanlines(&cinfo, &inData, 1); if (cinfo.output_components == 3) { uchar *in = inData; QRgb *out = (QRgb*)outData + outputLine * out_bpl; for (uint i=0; i<cinfo.output_width; i++) {// ### Only scaling down an image works, I don't think scaling up will work at the moment// ### An idea I have to make this a smooth scale is to progressively add the pixel values up// When scaling down, multiple values are being over drawn in to the output buffer.// Instead, a weighting based on the distance the line or pixel is from the output pixel determines// the weight of it when added to the output buffer. At present it is a non-smooth scale which is// inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive// jpegs could be made if scaling by say 50% or some other special cases out[sWidth * i / cinfo.output_width] = qRgb(in[0], in[1], in[2]); in += 3; } } else {// ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test// this code. (also only scales down and probably won't scale to a larger size) uchar *in = inData; uchar *out = outData + outputLine*out_bpl; for (uint i=0; i<cinfo.output_width; i++) { out[sWidth * i / cinfo.output_width] = in[i]; } } } (void) jpeg_finish_decompress(&cinfo); } } else { if (cinfo.output_components == 3 || cinfo.output_components == 4) { image = QImage(cinfo.output_width, cinfo.output_height, QImage::Format_RGB32); } else if (cinfo.output_components == 1) { image = QImage(cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8); image.setNumColors(256); for (int i=0; i<256; i++) image.setColor(i, qRgb(i,i,i)); } else { // Unsupported format } if (image.isNull()) return false; if (!image.isNull()) { uchar* data = image.bits(); int bpl = image.bytesPerLine(); while (cinfo.output_scanline < cinfo.output_height) { uchar *d = data + cinfo.output_scanline * bpl; (void) jpeg_read_scanlines(&cinfo, &d, 1); } (void) jpeg_finish_decompress(&cinfo); if (cinfo.output_components == 3) { // Expand 24->32 bpp.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -