diff options
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/image/image.pri | 10 | ||||
-rw-r--r-- | src/gui/image/qgifhandler.cpp | 1218 | ||||
-rw-r--r-- | src/gui/image/qgifhandler.pri | 3 | ||||
-rw-r--r-- | src/gui/image/qgifhandler_p.h | 105 | ||||
-rw-r--r-- | src/gui/image/qimagereader.cpp | 41 | ||||
-rw-r--r-- | src/gui/image/qimagewriter.cpp | 20 | ||||
-rw-r--r-- | src/gui/image/qjpeghandler.cpp | 1144 | ||||
-rw-r--r-- | src/gui/image/qjpeghandler.pri | 11 | ||||
-rw-r--r-- | src/gui/image/qjpeghandler_p.h | 85 | ||||
-rw-r--r-- | src/gui/image/qpnghandler.pri | 4 |
10 files changed, 5 insertions, 2636 deletions
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 8db944e5e3..657b57c0cd 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -71,11 +71,11 @@ SOURCES += \ image/qxbmhandler.cpp \ image/qxpmhandler.cpp -!contains(QT_CONFIG, no-png):include($$PWD/qpnghandler.pri) -else:DEFINES *= QT_NO_IMAGEFORMAT_PNG - -contains(QT_CONFIG, jpeg):include($$PWD/qjpeghandler.pri) -contains(QT_CONFIG, gif):include($$PWD/qgifhandler.pri) +contains(QT_CONFIG, png) { + HEADERS += image/qpnghandler_p.h + SOURCES += image/qpnghandler.cpp + include($$PWD/../../3rdparty/png_dependency.pri) +} # SIMD SSE2_SOURCES += image/qimage_sse2.cpp diff --git a/src/gui/image/qgifhandler.cpp b/src/gui/image/qgifhandler.cpp deleted file mode 100644 index 476b456563..0000000000 --- a/src/gui/image/qgifhandler.cpp +++ /dev/null @@ -1,1218 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** WARNING: -** A separate license from Unisys may be required to use the gif -** reader. See http://www.unisys.com/about__unisys/lzw/ -** for information from Unisys -** -****************************************************************************/ - -#include "qgifhandler_p.h" - -#include <qimage.h> -#include <qiodevice.h> -#include <qvariant.h> - -QT_BEGIN_NAMESPACE - -#define Q_TRANSPARENT 0x00ffffff - -// avoid going through QImage::scanLine() which calls detach -#define FAST_SCAN_LINE(bits, bpl, y) (bits + (y) * bpl) - - -/* - Incremental image decoder for GIF image format. - - This subclass of QImageFormat decodes GIF format images, - including animated GIFs. Internally in -*/ - -class QGIFFormat { -public: - QGIFFormat(); - ~QGIFFormat(); - - int decode(QImage *image, const uchar* buffer, int length, - int *nextFrameDelay, int *loopCount); - static void scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount); - - bool newFrame; - bool partialNewFrame; - -private: - void fillRect(QImage *image, int x, int y, int w, int h, QRgb col); - inline QRgb color(uchar index) const; - - // GIF specific stuff - QRgb* globalcmap; - QRgb* localcmap; - QImage backingstore; - unsigned char hold[16]; - bool gif89; - int count; - int ccount; - int expectcount; - enum State { - Header, - LogicalScreenDescriptor, - GlobalColorMap, - LocalColorMap, - Introducer, - ImageDescriptor, - TableImageLZWSize, - ImageDataBlockSize, - ImageDataBlock, - ExtensionLabel, - GraphicControlExtension, - ApplicationExtension, - NetscapeExtensionBlockSize, - NetscapeExtensionBlock, - SkipBlockSize, - SkipBlock, - Done, - Error - } state; - int gncols; - int lncols; - int ncols; - int lzwsize; - bool lcmap; - int swidth, sheight; - int width, height; - int left, top, right, bottom; - enum Disposal { NoDisposal, DoNotChange, RestoreBackground, RestoreImage }; - Disposal disposal; - bool disposed; - int trans_index; - bool gcmap; - int bgcol; - int interlace; - int accum; - int bitcount; - - enum { max_lzw_bits=12 }; // (poor-compiler's static const int) - - int code_size, clear_code, end_code, max_code_size, max_code; - int firstcode, oldcode, incode; - short* table[2]; - short* stack; - short *sp; - bool needfirst; - int x, y; - int frame; - bool out_of_bounds; - bool digress; - void nextY(unsigned char *bits, int bpl); - void disposePrevious(QImage *image); -}; - -/*! - Constructs a QGIFFormat. -*/ -QGIFFormat::QGIFFormat() -{ - globalcmap = 0; - localcmap = 0; - lncols = 0; - gncols = 0; - disposal = NoDisposal; - out_of_bounds = false; - disposed = true; - frame = -1; - state = Header; - count = 0; - lcmap = false; - newFrame = false; - partialNewFrame = false; - table[0] = 0; - table[1] = 0; - stack = 0; -} - -/*! - Destroys a QGIFFormat. -*/ -QGIFFormat::~QGIFFormat() -{ - if (globalcmap) delete[] globalcmap; - if (localcmap) delete[] localcmap; - delete [] stack; -} - -void QGIFFormat::disposePrevious(QImage *image) -{ - if (out_of_bounds) { - // flush anything that survived - // ### Changed: QRect(0, 0, swidth, sheight) - } - - // Handle disposal of previous image before processing next one - - if (disposed) return; - - int l = qMin(swidth-1,left); - int r = qMin(swidth-1,right); - int t = qMin(sheight-1,top); - int b = qMin(sheight-1,bottom); - - switch (disposal) { - case NoDisposal: - break; - case DoNotChange: - break; - case RestoreBackground: - if (trans_index>=0) { - // Easy: we use the transparent color - fillRect(image, l, t, r-l+1, b-t+1, Q_TRANSPARENT); - } else if (bgcol>=0) { - // Easy: we use the bgcol given - fillRect(image, l, t, r-l+1, b-t+1, color(bgcol)); - } else { - // Impossible: We don't know of a bgcol - use pixel 0 - const QRgb *bits = reinterpret_cast<const QRgb *>(image->constBits()); - fillRect(image, l, t, r-l+1, b-t+1, bits[0]); - } - // ### Changed: QRect(l, t, r-l+1, b-t+1) - break; - case RestoreImage: { - if (frame >= 0) { - for (int ln=t; ln<=b; ln++) { - memcpy(image->scanLine(ln)+l, - backingstore.constScanLine(ln-t), - (r-l+1)*sizeof(QRgb)); - } - // ### Changed: QRect(l, t, r-l+1, b-t+1) - } - } - } - disposal = NoDisposal; // Until an extension says otherwise. - - disposed = true; -} - -/*! - This function decodes some data into image changes. - - Returns the number of bytes consumed. -*/ -int QGIFFormat::decode(QImage *image, const uchar *buffer, int length, - int *nextFrameDelay, int *loopCount) -{ - // We are required to state that - // "The Graphics Interchange Format(c) is the Copyright property of - // CompuServe Incorporated. GIF(sm) is a Service Mark property of - // CompuServe Incorporated." - - if (!stack) { - stack = new short[(1 << max_lzw_bits) * 4]; - table[0] = &stack[(1 << max_lzw_bits) * 2]; - table[1] = &stack[(1 << max_lzw_bits) * 3]; - } - - image->detach(); - int bpl = image->bytesPerLine(); - unsigned char *bits = image->bits(); - -#define LM(l, m) (((m)<<8)|l) - digress = false; - const int initial = length; - while (!digress && length) { - length--; - unsigned char ch=*buffer++; - switch (state) { - case Header: - hold[count++]=ch; - if (count==6) { - // Header - gif89=(hold[3]!='8' || hold[4]!='7'); - state=LogicalScreenDescriptor; - count=0; - } - break; - case LogicalScreenDescriptor: - hold[count++]=ch; - if (count==7) { - // Logical Screen Descriptor - swidth=LM(hold[0], hold[1]); - sheight=LM(hold[2], hold[3]); - gcmap=!!(hold[4]&0x80); - //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1); - //UNUSED: gcmsortflag=!!(hold[4]&0x08); - gncols=2<<(hold[4]&0x7); - bgcol=(gcmap) ? hold[5] : -1; - //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0; - - trans_index = -1; - count=0; - ncols=gncols; - if (gcmap) { - ccount=0; - state=GlobalColorMap; - globalcmap = new QRgb[gncols+1]; // +1 for trans_index - globalcmap[gncols] = Q_TRANSPARENT; - } else { - state=Introducer; - } - } - break; - case GlobalColorMap: case LocalColorMap: - hold[count++]=ch; - if (count==3) { - QRgb rgb = qRgb(hold[0], hold[1], hold[2]); - if (state == LocalColorMap) { - if (ccount < lncols) - localcmap[ccount] = rgb; - } else { - globalcmap[ccount] = rgb; - } - if (++ccount >= ncols) { - if (state == LocalColorMap) - state=TableImageLZWSize; - else - state=Introducer; - } - count=0; - } - break; - case Introducer: - hold[count++]=ch; - switch (ch) { - case ',': - state=ImageDescriptor; - break; - case '!': - state=ExtensionLabel; - break; - case ';': - // ### Changed: QRect(0, 0, swidth, sheight) - state=Done; - break; - default: - digress=true; - // Unexpected Introducer - ignore block - state=Error; - } - break; - case ImageDescriptor: - hold[count++]=ch; - if (count==10) { - int newleft=LM(hold[1], hold[2]); - int newtop=LM(hold[3], hold[4]); - int newwidth=LM(hold[5], hold[6]); - int newheight=LM(hold[7], hold[8]); - - // disbelieve ridiculous logical screen sizes, - // unless the image frames are also large. - if (swidth/10 > qMax(newwidth,200)) - swidth = -1; - if (sheight/10 > qMax(newheight,200)) - sheight = -1; - - if (swidth <= 0) - swidth = newleft + newwidth; - if (sheight <= 0) - sheight = newtop + newheight; - - QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32; - if (image->isNull()) { - (*image) = QImage(swidth, sheight, format); - bpl = image->bytesPerLine(); - bits = image->bits(); - memset(bits, 0, image->byteCount()); - } - - // Check if the previous attempt to create the image failed. If it - // did then the image is broken and we should give up. - if (image->isNull()) { - state = Error; - return -1; - } - - disposePrevious(image); - disposed = false; - - left = newleft; - top = newtop; - width = newwidth; - height = newheight; - - right=qMax(0, qMin(left+width, swidth)-1); - bottom=qMax(0, qMin(top+height, sheight)-1); - lcmap=!!(hold[9]&0x80); - interlace=!!(hold[9]&0x40); - //bool lcmsortflag=!!(hold[9]&0x20); - lncols=lcmap ? (2<<(hold[9]&0x7)) : 0; - if (lncols) { - if (localcmap) - delete [] localcmap; - localcmap = new QRgb[lncols+1]; - localcmap[lncols] = Q_TRANSPARENT; - ncols = lncols; - } else { - ncols = gncols; - } - frame++; - if (frame == 0) { - if (left || top || width<swidth || height<sheight) { - // Not full-size image - erase with bg or transparent - if (trans_index >= 0) { - fillRect(image, 0, 0, swidth, sheight, color(trans_index)); - // ### Changed: QRect(0, 0, swidth, sheight) - } else if (bgcol>=0) { - fillRect(image, 0, 0, swidth, sheight, color(bgcol)); - // ### Changed: QRect(0, 0, swidth, sheight) - } - } - } - - if (disposal == RestoreImage) { - int l = qMin(swidth-1,left); - int r = qMin(swidth-1,right); - int t = qMin(sheight-1,top); - int b = qMin(sheight-1,bottom); - int w = r-l+1; - int h = b-t+1; - - if (backingstore.width() < w - || backingstore.height() < h) { - // We just use the backing store as a byte array - backingstore = QImage(qMax(backingstore.width(), w), - qMax(backingstore.height(), h), - QImage::Format_RGB32); - memset(bits, 0, image->byteCount()); - } - const int dest_bpl = backingstore.bytesPerLine(); - unsigned char *dest_data = backingstore.bits(); - for (int ln=0; ln<h; ln++) { - memcpy(FAST_SCAN_LINE(dest_data, dest_bpl, ln), - FAST_SCAN_LINE(bits, bpl, t+ln) + l, w*sizeof(QRgb)); - } - } - - count=0; - if (lcmap) { - ccount=0; - state=LocalColorMap; - } else { - state=TableImageLZWSize; - } - x = left; - y = top; - accum = 0; - bitcount = 0; - sp = stack; - firstcode = oldcode = 0; - needfirst = true; - out_of_bounds = left>=swidth || y>=sheight; - } - break; - case TableImageLZWSize: { - lzwsize=ch; - if (lzwsize > max_lzw_bits) { - state=Error; - } else { - code_size=lzwsize+1; - clear_code=1<<lzwsize; - end_code=clear_code+1; - max_code_size=2*clear_code; - max_code=clear_code+2; - int i; - for (i=0; i<clear_code; i++) { - table[0][i]=0; - table[1][i]=i; - } - state=ImageDataBlockSize; - } - count=0; - break; - } case ImageDataBlockSize: - expectcount=ch; - if (expectcount) { - state=ImageDataBlock; - } else { - state=Introducer; - digress = true; - newFrame = true; - } - break; - case ImageDataBlock: - count++; - accum|=(ch<<bitcount); - bitcount+=8; - while (bitcount>=code_size && state==ImageDataBlock) { - int code=accum&((1<<code_size)-1); - bitcount-=code_size; - accum>>=code_size; - - if (code==clear_code) { - if (!needfirst) { - code_size=lzwsize+1; - max_code_size=2*clear_code; - max_code=clear_code+2; - } - needfirst=true; - } else if (code==end_code) { - bitcount = -32768; - // Left the block end arrive - } else { - if (needfirst) { - firstcode=oldcode=code; - if (!out_of_bounds && image->height() > y && ((frame == 0) || (firstcode != trans_index))) - ((QRgb*)FAST_SCAN_LINE(bits, bpl, y))[x] = color(firstcode); - x++; - if (x>=swidth) out_of_bounds = true; - needfirst=false; - if (x>=left+width) { - x=left; - out_of_bounds = left>=swidth || y>=sheight; - nextY(bits, bpl); - } - } else { - incode=code; - if (code>=max_code) { - *sp++=firstcode; - code=oldcode; - } - while (code>=clear_code+2) { - if (code >= max_code) { - state = Error; - return -1; - } - *sp++=table[1][code]; - if (code==table[0][code]) { - state=Error; - return -1; - } - if (sp-stack>=(1<<(max_lzw_bits))*2) { - state=Error; - return -1; - } - code=table[0][code]; - } - if (code < 0) { - state = Error; - return -1; - } - - *sp++=firstcode=table[1][code]; - code=max_code; - if (code<(1<<max_lzw_bits)) { - table[0][code]=oldcode; - table[1][code]=firstcode; - max_code++; - if ((max_code>=max_code_size) - && (max_code_size<(1<<max_lzw_bits))) - { - max_code_size*=2; - code_size++; - } - } - oldcode=incode; - const int h = image->height(); - QRgb *line = 0; - if (!out_of_bounds && h > y) - line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y); - while (sp>stack) { - const uchar index = *(--sp); - if (!out_of_bounds && h > y && ((frame == 0) || (index != trans_index))) { - line[x] = color(index); - } - x++; - if (x>=swidth) out_of_bounds = true; - if (x>=left+width) { - x=left; - out_of_bounds = left>=swidth || y>=sheight; - nextY(bits, bpl); - if (!out_of_bounds && h > y) - line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y); - } - } - } - } - } - partialNewFrame = true; - if (count==expectcount) { - count=0; - state=ImageDataBlockSize; - } - break; - case ExtensionLabel: - switch (ch) { - case 0xf9: - state=GraphicControlExtension; - break; - case 0xff: - state=ApplicationExtension; - break; -#if 0 - case 0xfe: - state=CommentExtension; - break; - case 0x01: - break; -#endif - default: - state=SkipBlockSize; - } - count=0; - break; - case ApplicationExtension: - if (count<11) hold[count]=ch; - count++; - if (count==hold[0]+1) { - if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) { - // Looping extension - state=NetscapeExtensionBlockSize; - } else { - state=SkipBlockSize; - } - count=0; - } - break; - case NetscapeExtensionBlockSize: - expectcount=ch; - count=0; - if (expectcount) state=NetscapeExtensionBlock; - else state=Introducer; - break; - case NetscapeExtensionBlock: - if (count<3) hold[count]=ch; - count++; - if (count==expectcount) { - *loopCount = hold[1]+hold[2]*256; - state=SkipBlockSize; // Ignore further blocks - } - break; - case GraphicControlExtension: - if (count<5) hold[count]=ch; - count++; - if (count==hold[0]+1) { - disposePrevious(image); - disposal=Disposal((hold[1]>>2)&0x7); - //UNUSED: waitforuser=!!((hold[1]>>1)&0x1); - int delay=count>3 ? LM(hold[2], hold[3]) : 1; - // IE and mozilla use a minimum delay of 10. With the minimum delay of 10 - // we are compatible to them and avoid huge loads on the app and xserver. - *nextFrameDelay = (delay < 2 ? 10 : delay) * 10; - - bool havetrans=hold[1]&0x1; - trans_index = havetrans ? hold[4] : -1; - - count=0; - state=SkipBlockSize; - } - break; - case SkipBlockSize: - expectcount=ch; - count=0; - if (expectcount) state=SkipBlock; - else state=Introducer; - break; - case SkipBlock: - count++; - if (count==expectcount) state=SkipBlockSize; - break; - case Done: - digress=true; - /* Netscape ignores the junk, so we do too. - length++; // Unget - state=Error; // More calls to this is an error - */ - break; - case Error: - return -1; // Called again after done. - } - } - return initial-length; -} - -/*! - Scans through the data stream defined by \a device and returns the image - sizes found in the stream in the \a imageSizes vector. -*/ -void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount) -{ - if (!device) - return; - - qint64 oldPos = device->pos(); - if (device->isSequential() || !device->seek(0)) - return; - - int colorCount = 0; - int localColorCount = 0; - int globalColorCount = 0; - int colorReadCount = 0; - bool localColormap = false; - bool globalColormap = false; - int count = 0; - int blockSize = 0; - int imageWidth = 0; - int imageHeight = 0; - bool done = false; - uchar hold[16]; - State state = Header; - - const int readBufferSize = 40960; // 40k read buffer - QByteArray readBuffer(device->read(readBufferSize)); - - if (readBuffer.isEmpty()) { - device->seek(oldPos); - return; - } - - // This is a specialized version of the state machine from decode(), - // which doesn't do any image decoding or mallocing, and has an - // optimized way of skipping SkipBlocks, ImageDataBlocks and - // Global/LocalColorMaps. - - while (!readBuffer.isEmpty()) { - int length = readBuffer.size(); - const uchar *buffer = (const uchar *) readBuffer.constData(); - while (!done && length) { - length--; - uchar ch = *buffer++; - switch (state) { - case Header: - hold[count++] = ch; - if (count == 6) { - state = LogicalScreenDescriptor; - count = 0; - } - break; - case LogicalScreenDescriptor: - hold[count++] = ch; - if (count == 7) { - imageWidth = LM(hold[0], hold[1]); - imageHeight = LM(hold[2], hold[3]); - globalColormap = !!(hold[4] & 0x80); - globalColorCount = 2 << (hold[4] & 0x7); - count = 0; - colorCount = globalColorCount; - if (globalColormap) { - int colorTableSize = 3 * globalColorCount; - if (length >= colorTableSize) { - // skip the global color table in one go - length -= colorTableSize; - buffer += colorTableSize; - state = Introducer; - } else { - colorReadCount = 0; - state = GlobalColorMap; - } - } else { - state=Introducer; - } - } - break; - case GlobalColorMap: - case LocalColorMap: - hold[count++] = ch; - if (count == 3) { - if (++colorReadCount >= colorCount) { - if (state == LocalColorMap) - state = TableImageLZWSize; - else - state = Introducer; - } - count = 0; - } - break; - case Introducer: - hold[count++] = ch; - switch (ch) { - case 0x2c: - state = ImageDescriptor; - break; - case 0x21: - state = ExtensionLabel; - break; - case 0x3b: - state = Done; - break; - default: - done = true; - state = Error; - } - break; - case ImageDescriptor: - hold[count++] = ch; - if (count == 10) { - int newLeft = LM(hold[1], hold[2]); - int newTop = LM(hold[3], hold[4]); - int newWidth = LM(hold[5], hold[6]); - int newHeight = LM(hold[7], hold[8]); - - if (imageWidth/10 > qMax(newWidth,200)) - imageWidth = -1; - if (imageHeight/10 > qMax(newHeight,200)) - imageHeight = -1; - - if (imageWidth <= 0) - imageWidth = newLeft + newWidth; - if (imageHeight <= 0) - imageHeight = newTop + newHeight; - - *imageSizes << QSize(imageWidth, imageHeight); - - localColormap = !!(hold[9] & 0x80); - localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0; - if (localColorCount) - colorCount = localColorCount; - else - colorCount = globalColorCount; - - count = 0; - if (localColormap) { - int colorTableSize = 3 * localColorCount; - if (length >= colorTableSize) { - // skip the local color table in one go - length -= colorTableSize; - buffer += colorTableSize; - state = TableImageLZWSize; - } else { - colorReadCount = 0; - state = LocalColorMap; - } - } else { - state = TableImageLZWSize; - } - } - break; - case TableImageLZWSize: - if (ch > max_lzw_bits) - state = Error; - else - state = ImageDataBlockSize; - count = 0; - break; - case ImageDataBlockSize: - blockSize = ch; - if (blockSize) { - if (length >= blockSize) { - // we can skip the block in one go - length -= blockSize; - buffer += blockSize; - count = 0; - } else { - state = ImageDataBlock; - } - } else { - state = Introducer; - } - break; - case ImageDataBlock: - ++count; - if (count == blockSize) { - count = 0; - state = ImageDataBlockSize; - } - break; - case ExtensionLabel: - switch (ch) { - case 0xf9: - state = GraphicControlExtension; - break; - case 0xff: - state = ApplicationExtension; - break; - default: - state = SkipBlockSize; - } - count = 0; - break; - case ApplicationExtension: - if (count < 11) - hold[count] = ch; - ++count; - if (count == hold[0] + 1) { - if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0) - state=NetscapeExtensionBlockSize; - else - state=SkipBlockSize; - count = 0; - } - break; - case GraphicControlExtension: - if (count < 5) - hold[count] = ch; - ++count; - if (count == hold[0] + 1) { - count = 0; - state = SkipBlockSize; - } - break; - case NetscapeExtensionBlockSize: - blockSize = ch; - count = 0; - if (blockSize) - state = NetscapeExtensionBlock; - else - state = Introducer; - break; - case NetscapeExtensionBlock: - if (count < 3) - hold[count] = ch; - count++; - if (count == blockSize) { - *loopCount = LM(hold[1], hold[2]); - state = SkipBlockSize; - } - break; - case SkipBlockSize: - blockSize = ch; - count = 0; - if (blockSize) { - if (length >= blockSize) { - // we can skip the block in one go - length -= blockSize; - buffer += blockSize; - } else { - state = SkipBlock; - } - } else { - state = Introducer; - } - break; - case SkipBlock: - ++count; - if (count == blockSize) - state = SkipBlockSize; - break; - case Done: - done = true; - break; - case Error: - device->seek(oldPos); - return; - } - } - readBuffer = device->read(readBufferSize); - } - device->seek(oldPos); - return; -} - -void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color) -{ - if (w>0) { - for (int j=0; j<h; j++) { - QRgb *line = (QRgb*)image->scanLine(j+row); - for (int i=0; i<w; i++) - *(line+col+i) = color; - } - } -} - -void QGIFFormat::nextY(unsigned char *bits, int bpl) -{ - if (out_of_bounds) - return; - int my; - switch (interlace) { - case 0: // Non-interlaced - // if (!out_of_bounds) { - // ### Changed: QRect(left, y, right - left + 1, 1); - // } - y++; - break; - case 1: { - int i; - my = qMin(7, bottom-y); - // Don't dup with transparency - if (trans_index < 0) { - for (i=1; i<=my; i++) { - memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb), - (right-left+1)*sizeof(QRgb)); - } - } - - // if (!out_of_bounds) { - // ### Changed: QRect(left, y, right - left + 1, my + 1); - // } -// if (!out_of_bounds) -// qDebug("consumer->changed(QRect(%d, %d, %d, %d))", left, y, right-left+1, my+1); - y+=8; - if (y>bottom) { - interlace++; y=top+4; - if (y > bottom) { // for really broken GIFs with bottom < 5 - interlace=2; - y = top + 2; - if (y > bottom) { // for really broken GIF with bottom < 3 - interlace = 0; - y = top + 1; - } - } - } - } break; - case 2: { - int i; - my = qMin(3, bottom-y); - // Don't dup with transparency - if (trans_index < 0) { - for (i=1; i<=my; i++) { - memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb), - (right-left+1)*sizeof(QRgb)); - } - } - - // if (!out_of_bounds) { - // ### Changed: QRect(left, y, right - left + 1, my + 1); - // } - y+=8; - if (y>bottom) { - interlace++; y=top+2; - // handle broken GIF with bottom < 3 - if (y > bottom) { - interlace = 3; - y = top + 1; - } - } - } break; - case 3: { - int i; - my = qMin(1, bottom-y); - // Don't dup with transparency - if (trans_index < 0) { - for (i=1; i<=my; i++) { - memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb), - (right-left+1)*sizeof(QRgb)); - } - } - // if (!out_of_bounds) { - // ### Changed: QRect(left, y, right - left + 1, my + 1); - // } - y+=4; - if (y>bottom) { interlace++; y=top+1; } - } break; - case 4: - // if (!out_of_bounds) { - // ### Changed: QRect(left, y, right - left + 1, 1); - // } - y+=2; - } - - // Consume bogus extra lines - if (y >= sheight) out_of_bounds=true; //y=bottom; -} - -inline QRgb QGIFFormat::color(uchar index) const -{ - if (index > ncols) - return Q_TRANSPARENT; - - QRgb *map = lcmap ? localcmap : globalcmap; - QRgb col = map ? map[index] : 0; - return index == trans_index ? col & Q_TRANSPARENT : col; -} - -//------------------------------------------------------------------------- -//------------------------------------------------------------------------- -//------------------------------------------------------------------------- - -QGifHandler::QGifHandler() -{ - gifFormat = new QGIFFormat; - nextDelay = 100; - loopCnt = -1; - frameNumber = -1; - scanIsCached = false; -} - -QGifHandler::~QGifHandler() -{ - delete gifFormat; -} - -// Does partial decode if necessary, just to see if an image is coming - -bool QGifHandler::imageIsComing() const -{ - const int GifChunkSize = 4096; - - while (!gifFormat->partialNewFrame) { - if (buffer.isEmpty()) { - buffer += device()->read(GifChunkSize); - if (buffer.isEmpty()) - break; - } - - int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(), - &nextDelay, &loopCnt); - if (decoded == -1) - break; - buffer.remove(0, decoded); - } - return gifFormat->partialNewFrame; -} - -bool QGifHandler::canRead() const -{ - if (canRead(device()) || imageIsComing()) { - setFormat("gif"); - return true; - } - - return false; -} - -bool QGifHandler::canRead(QIODevice *device) -{ - if (!device) { - qWarning("QGifHandler::canRead() called with no device"); - return false; - } - - char head[6]; - if (device->peek(head, sizeof(head)) == sizeof(head)) - return qstrncmp(head, "GIF87a", 6) == 0 - || qstrncmp(head, "GIF89a", 6) == 0; - return false; -} - -bool QGifHandler::read(QImage *image) -{ - const int GifChunkSize = 4096; - - while (!gifFormat->newFrame) { - if (buffer.isEmpty()) { - buffer += device()->read(GifChunkSize); - if (buffer.isEmpty()) - break; - } - - int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(), - &nextDelay, &loopCnt); - if (decoded == -1) - break; - buffer.remove(0, decoded); - } - if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) { - *image = lastImage; - ++frameNumber; - gifFormat->newFrame = false; - gifFormat->partialNewFrame = false; - return true; - } - - return false; -} - -bool QGifHandler::write(const QImage &image) -{ - Q_UNUSED(image); - return false; -} - -bool QGifHandler::supportsOption(ImageOption option) const -{ - if (!device() || device()->isSequential()) - return option == Animation; - else - return option == Size - || option == Animation; -} - -QVariant QGifHandler::option(ImageOption option) const -{ - if (option == Size) { - if (!scanIsCached) { - QGIFFormat::scan(device(), &imageSizes, &loopCnt); - scanIsCached = true; - } - // before the first frame is read, or we have an empty data stream - if (frameNumber == -1) - return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant(); - // after the last frame has been read, the next size is undefined - if (frameNumber >= imageSizes.count() - 1) - return QVariant(); - // and the last case: the size of the next frame - return imageSizes.at(frameNumber + 1); - } else if (option == Animation) { - return true; - } - return QVariant(); -} - -void QGifHandler::setOption(ImageOption option, const QVariant &value) -{ - Q_UNUSED(option); - Q_UNUSED(value); -} - -int QGifHandler::nextImageDelay() const -{ - return nextDelay; -} - -int QGifHandler::imageCount() const -{ - if (!scanIsCached) { - QGIFFormat::scan(device(), &imageSizes, &loopCnt); - scanIsCached = true; - } - return imageSizes.count(); -} - -int QGifHandler::loopCount() const -{ - if (!scanIsCached) { - QGIFFormat::scan(device(), &imageSizes, &loopCnt); - scanIsCached = true; - } - - if (loopCnt == 0) - return -1; - else if (loopCnt == -1) - return 0; - else - return loopCnt; -} - -int QGifHandler::currentImageNumber() const -{ - return frameNumber; -} - -QByteArray QGifHandler::name() const -{ - return "gif"; -} - -QT_END_NAMESPACE diff --git a/src/gui/image/qgifhandler.pri b/src/gui/image/qgifhandler.pri deleted file mode 100644 index ec33101451..0000000000 --- a/src/gui/image/qgifhandler.pri +++ /dev/null @@ -1,3 +0,0 @@ -# common to plugin and built-in forms -HEADERS += $$PWD/qgifhandler_p.h -SOURCES += $$PWD/qgifhandler.cpp diff --git a/src/gui/image/qgifhandler_p.h b/src/gui/image/qgifhandler_p.h deleted file mode 100644 index bc3debe83c..0000000000 --- a/src/gui/image/qgifhandler_p.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** WARNING: -** A separate license from Unisys may be required to use the gif -** reader. See http://www.unisys.com/about__unisys/lzw/ -** for information from Unisys -** -****************************************************************************/ - -#ifndef QGIFHANDLER_P_H -#define QGIFHANDLER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtGui/qimageiohandler.h> -#include <QtGui/qimage.h> -#include <QtCore/qbytearray.h> - -QT_BEGIN_NAMESPACE - -class QGIFFormat; -class QGifHandler : public QImageIOHandler -{ -public: - QGifHandler(); - ~QGifHandler(); - - bool canRead() const Q_DECL_OVERRIDE; - bool read(QImage *image) Q_DECL_OVERRIDE; - bool write(const QImage &image) Q_DECL_OVERRIDE; - - QByteArray name() const Q_DECL_OVERRIDE; - - static bool canRead(QIODevice *device); - - QVariant option(ImageOption option) const Q_DECL_OVERRIDE; - void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE; - bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE; - - int imageCount() const Q_DECL_OVERRIDE; - int loopCount() const Q_DECL_OVERRIDE; - int nextImageDelay() const Q_DECL_OVERRIDE; - int currentImageNumber() const Q_DECL_OVERRIDE; - -private: - bool imageIsComing() const; - QGIFFormat *gifFormat; - QString fileName; - mutable QByteArray buffer; - mutable QImage lastImage; - - mutable int nextDelay; - mutable int loopCnt; - int frameNumber; - mutable QVector<QSize> imageSizes; - mutable bool scanIsCached; -}; - -QT_END_NAMESPACE - -#endif // QGIFHANDLER_P_H diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 4dada4ca82..8bf0ce3523 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -155,12 +155,6 @@ #ifndef QT_NO_IMAGEFORMAT_PNG #include <private/qpnghandler_p.h> #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG -#include <private/qjpeghandler_p.h> -#endif -#ifdef QT_BUILTIN_GIF_READER -#include <private/qgifhandler_p.h> -#endif #include <algorithm> @@ -175,13 +169,6 @@ enum _qt_BuiltInFormatType { #ifndef QT_NO_IMAGEFORMAT_PNG _qt_PngFormat, #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - _qt_JpgFormat, - _qt_JpegFormat, -#endif -#ifdef QT_BUILTIN_GIF_READER - _qt_GifFormat, -#endif #ifndef QT_NO_IMAGEFORMAT_BMP _qt_BmpFormat, #endif @@ -210,13 +197,6 @@ static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = { #ifndef QT_NO_IMAGEFORMAT_PNG {"png", "image/png"}, #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - {"jpg", "image/jpeg"}, - {"jpeg", "image/jpeg"}, -#endif -#ifdef QT_BUILTIN_GIF_READER - {"gif", "image/gif"}, -#endif #ifndef QT_NO_IMAGEFORMAT_BMP {"bmp", "image/bmp"}, #endif @@ -357,14 +337,6 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device, } else if (testFormat == "png") { handler = new QPngHandler; #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - } else if (testFormat == "jpg" || testFormat == "jpeg") { - handler = new QJpegHandler; -#endif -#ifdef QT_BUILTIN_GIF_READER - } else if (testFormat == "gif") { - handler = new QGifHandler; -#endif #ifndef QT_NO_IMAGEFORMAT_BMP } else if (testFormat == "bmp") { handler = new QBmpHandler; @@ -442,19 +414,6 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device, handler = new QPngHandler; break; #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - case _qt_JpgFormat: - case _qt_JpegFormat: - if (QJpegHandler::canRead(device)) - handler = new QJpegHandler; - break; -#endif -#ifdef QT_BUILTIN_GIF_READER - case _qt_GifFormat: - if (QGifHandler::canRead(device)) - handler = new QGifHandler; - break; -#endif #ifndef QT_NO_IMAGEFORMAT_BMP case _qt_BmpFormat: if (QBmpHandler::canRead(device)) diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index b60cf59864..ddf9f2dda9 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -115,12 +115,6 @@ #ifndef QT_NO_IMAGEFORMAT_PNG #include <private/qpnghandler_p.h> #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG -#include <private/qjpeghandler_p.h> -#endif -#ifdef QT_BUILTIN_GIF_READER -#include <private/qgifhandler_p.h> -#endif #include <algorithm> @@ -184,14 +178,6 @@ static QImageIOHandler *createWriteHandlerHelper(QIODevice *device, } else if (testFormat == "png") { handler = new QPngHandler; #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - } else if (testFormat == "jpg" || testFormat == "jpeg") { - handler = new QJpegHandler; -#endif -#ifdef QT_BUILTIN_GIF_READER - } else if (testFormat == "gif") { - handler = new QGifHandler; -#endif #ifndef QT_NO_IMAGEFORMAT_BMP } else if (testFormat == "bmp") { handler = new QBmpHandler; @@ -910,9 +896,6 @@ QList<QByteArray> QImageWriter::supportedImageFormats() #ifndef QT_NO_IMAGEFORMAT_PNG formats << "png"; #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - formats << "jpg" << "jpeg"; -#endif #ifndef QT_NO_IMAGEFORMATPLUGIN supportedImageHandlerFormats(loader(), QImageIOPlugin::CanWrite, &formats); @@ -951,9 +934,6 @@ QList<QByteArray> QImageWriter::supportedMimeTypes() #ifndef QT_NO_IMAGEFORMAT_PNG mimeTypes << "image/png"; #endif -#ifndef QT_NO_IMAGEFORMAT_JPEG - mimeTypes << "image/jpeg"; -#endif #ifndef QT_NO_IMAGEFORMATPLUGIN supportedImageHandlerMimeTypes(loader(), QImageIOPlugin::CanWrite, &mimeTypes); diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp deleted file mode 100644 index 52e8b39f11..0000000000 --- a/src/gui/image/qjpeghandler.cpp +++ /dev/null @@ -1,1144 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qjpeghandler_p.h" - -#include <qimage.h> -#include <qvariant.h> -#include <qvector.h> -#include <qbuffer.h> -#include <qmath.h> -#include <private/qsimd_p.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 messy -extern "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 -} - -QT_BEGIN_NAMESPACE -QT_WARNING_DISABLE_GCC("-Wclobbered") - -Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32(quint32 *dst, const uchar *src, int len); -typedef void (QT_FASTCALL *Rgb888ToRgb32Converter)(quint32 *dst, const uchar *src, int len); - -struct my_error_mgr : public jpeg_error_mgr { - jmp_buf setjmp_buffer; -}; - -extern "C" { - -static void 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); -} - -static void my_output_message(j_common_ptr cinfo) -{ - char buffer[JMSG_LENGTH_MAX]; - (*cinfo->err->format_message)(cinfo, buffer); - qWarning("%s", buffer); -} - -} - - -static 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]; - const QBuffer *memDevice; - -public: - my_jpeg_source_mgr(QIODevice *device); -}; - -extern "C" { - -static void qt_init_source(j_decompress_ptr) -{ -} - -static boolean qt_fill_input_buffer(j_decompress_ptr cinfo) -{ - my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; - qint64 num_read = 0; - if (src->memDevice) { - src->next_input_byte = (const JOCTET *)(src->memDevice->data().constData() + src->memDevice->pos()); - num_read = src->memDevice->data().size() - src->memDevice->pos(); - src->device->seek(src->memDevice->data().size()); - } else { - 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->next_input_byte = src->buffer; - src->buffer[0] = (JOCTET) 0xFF; - src->buffer[1] = (JOCTET) JPEG_EOI; - src->bytes_in_buffer = 2; - } else { - src->bytes_in_buffer = num_read; - } - return TRUE; -} - -static void 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) { // Should not happen in case of memDevice - 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; - } -} - -static void qt_term_source(j_decompress_ptr cinfo) -{ - my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; - if (!src->device->isSequential()) - src->device->seek(src->device->pos() - src->bytes_in_buffer); -} - -} - -inline 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; - memDevice = qobject_cast<QBuffer *>(device); - bytes_in_buffer = 0; - next_input_byte = buffer; -} - - -inline static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo) -{ - (void) jpeg_calc_output_dimensions(cinfo); - - w = cinfo->output_width; - h = cinfo->output_height; - return true; -} - -#define HIGH_QUALITY_THRESHOLD 50 - -inline static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo) -{ - - bool result = true; - switch (cinfo->output_components) { - case 1: - format = QImage::Format_Grayscale8; - break; - case 3: - case 4: - format = QImage::Format_RGB32; - break; - default: - result = false; - break; - } - cinfo->output_scanline = cinfo->output_height; - return result; -} - -static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info, - const QSize& size) -{ - QImage::Format format; - switch (info->output_components) { - case 1: - format = QImage::Format_Grayscale8; - break; - case 3: - case 4: - format = QImage::Format_RGB32; - break; - default: - return false; // unsupported format - } - - if (dest->size() != size || dest->format() != format) - *dest = QImage(size, format); - - return !dest->isNull(); -} - -static bool read_jpeg_image(QImage *outImage, - QSize scaledSize, QRect scaledClipRect, - QRect clipRect, volatile int inQuality, - Rgb888ToRgb32Converter converter, - j_decompress_ptr info, struct my_error_mgr* err ) -{ - if (!setjmp(err->setjmp_buffer)) { - // -1 means default quality. - int quality = inQuality; - if (quality < 0) - quality = 75; - - // If possible, merge the scaledClipRect into either scaledSize - // or clipRect to avoid doing a separate scaled clipping pass. - // Best results are achieved by clipping before scaling, not after. - if (!scaledClipRect.isEmpty()) { - if (scaledSize.isEmpty() && clipRect.isEmpty()) { - // No clipping or scaling before final clip. - clipRect = scaledClipRect; - scaledClipRect = QRect(); - } else if (scaledSize.isEmpty()) { - // Clipping, but no scaling: combine the clip regions. - scaledClipRect.translate(clipRect.topLeft()); - clipRect = scaledClipRect.intersected(clipRect); - scaledClipRect = QRect(); - } else if (clipRect.isEmpty()) { - // No clipping, but scaling: if we can map back to an - // integer pixel boundary, then clip before scaling. - if ((info->image_width % scaledSize.width()) == 0 && - (info->image_height % scaledSize.height()) == 0) { - int x = scaledClipRect.x() * info->image_width / - scaledSize.width(); - int y = scaledClipRect.y() * info->image_height / - scaledSize.height(); - int width = (scaledClipRect.right() + 1) * - info->image_width / scaledSize.width() - x; - int height = (scaledClipRect.bottom() + 1) * - info->image_height / scaledSize.height() - y; - clipRect = QRect(x, y, width, height); - scaledSize = scaledClipRect.size(); - scaledClipRect = QRect(); - } - } else { - // Clipping and scaling: too difficult to figure out, - // and not a likely use case, so do it the long way. - } - } - - // Determine the scale factor to pass to libjpeg for quick downscaling. - if (!scaledSize.isEmpty() && info->image_width && info->image_height) { - if (clipRect.isEmpty()) { - double f = qMin(double(info->image_width) / scaledSize.width(), - double(info->image_height) / scaledSize.height()); - - // libjpeg supports M/8 scaling with M=[1,16]. All downscaling factors - // are a speed improvement, but upscaling during decode is slower. - info->scale_num = qBound(1, qCeil(8/f), 8); - info->scale_denom = 8; - } else { - info->scale_denom = qMin(clipRect.width() / scaledSize.width(), - clipRect.height() / scaledSize.height()); - - // Only scale by powers of two when clipping so we can - // keep the exact pixel boundaries - if (info->scale_denom < 2) - info->scale_denom = 1; - else if (info->scale_denom < 4) - info->scale_denom = 2; - else if (info->scale_denom < 8) - info->scale_denom = 4; - else - info->scale_denom = 8; - info->scale_num = 1; - - // Correct the scale factor so that we clip accurately. - // It is recommended that the clip rectangle be aligned - // on an 8-pixel boundary for best performance. - while (info->scale_denom > 1 && - ((clipRect.x() % info->scale_denom) != 0 || - (clipRect.y() % info->scale_denom) != 0 || - (clipRect.width() % info->scale_denom) != 0 || - (clipRect.height() % info->scale_denom) != 0)) { - info->scale_denom /= 2; - } - } - } - - // If high quality not required, use fast decompression - if( quality < HIGH_QUALITY_THRESHOLD ) { - info->dct_method = JDCT_IFAST; - info->do_fancy_upsampling = FALSE; - } - - (void) jpeg_calc_output_dimensions(info); - - // Determine the clip region to extract. - QRect imageRect(0, 0, info->output_width, info->output_height); - QRect clip; - if (clipRect.isEmpty()) { - clip = imageRect; - } else if (info->scale_denom == info->scale_num) { - clip = clipRect.intersected(imageRect); - } else { - // The scale factor was corrected above to ensure that - // we don't miss pixels when we scale the clip rectangle. - clip = QRect(clipRect.x() / int(info->scale_denom), - clipRect.y() / int(info->scale_denom), - clipRect.width() / int(info->scale_denom), - clipRect.height() / int(info->scale_denom)); - clip = clip.intersected(imageRect); - } - - // Allocate memory for the clipped QImage. - if (!ensureValidImage(outImage, info, clip.size())) - longjmp(err->setjmp_buffer, 1); - - // Avoid memcpy() overhead if grayscale with no clipping. - bool quickGray = (info->output_components == 1 && - clip == imageRect); - if (!quickGray) { - // Ask the jpeg library to allocate a temporary row. - // The library will automatically delete it for us later. - // The libjpeg docs say we should do this before calling - // jpeg_start_decompress(). We can't use "new" here - // because we are inside the setjmp() block and an error - // in the jpeg input stream would cause a memory leak. - JSAMPARRAY rows = (info->mem->alloc_sarray) - ((j_common_ptr)info, JPOOL_IMAGE, - info->output_width * info->output_components, 1); - - (void) jpeg_start_decompress(info); - - while (info->output_scanline < info->output_height) { - int y = int(info->output_scanline) - clip.y(); - if (y >= clip.height()) - break; // We've read the entire clip region, so abort. - - (void) jpeg_read_scanlines(info, rows, 1); - - if (y < 0) - continue; // Haven't reached the starting line yet. - - if (info->output_components == 3) { - uchar *in = rows[0] + clip.x() * 3; - QRgb *out = (QRgb*)outImage->scanLine(y); - converter(out, in, clip.width()); - } else if (info->out_color_space == JCS_CMYK) { - // Convert CMYK->RGB. - uchar *in = rows[0] + clip.x() * 4; - QRgb *out = (QRgb*)outImage->scanLine(y); - for (int i = 0; i < clip.width(); ++i) { - int k = in[3]; - *out++ = qRgb(k * in[0] / 255, k * in[1] / 255, - k * in[2] / 255); - in += 4; - } - } else if (info->output_components == 1) { - // Grayscale. - memcpy(outImage->scanLine(y), - rows[0] + clip.x(), clip.width()); - } - } - } else { - // Load unclipped grayscale data directly into the QImage. - (void) jpeg_start_decompress(info); - while (info->output_scanline < info->output_height) { - uchar *row = outImage->scanLine(info->output_scanline); - (void) jpeg_read_scanlines(info, &row, 1); - } - } - - if (info->output_scanline == info->output_height) - (void) jpeg_finish_decompress(info); - - if (info->density_unit == 1) { - outImage->setDotsPerMeterX(int(100. * info->X_density / 2.54)); - outImage->setDotsPerMeterY(int(100. * info->Y_density / 2.54)); - } else if (info->density_unit == 2) { - outImage->setDotsPerMeterX(int(100. * info->X_density)); - outImage->setDotsPerMeterY(int(100. * info->Y_density)); - } - - if (scaledSize.isValid() && scaledSize != clip.size()) { - *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >= HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation); - } - - if (!scaledClipRect.isEmpty()) - *outImage = outImage->copy(scaledClipRect); - return !outImage->isNull(); - } - else - return false; -} - -struct my_jpeg_destination_mgr : public jpeg_destination_mgr { - // Nothing dynamic - cannot rely on destruction over longjump - QIODevice *device; - JOCTET buffer[max_buf]; - -public: - my_jpeg_destination_mgr(QIODevice *); -}; - - -extern "C" { - -static void qt_init_destination(j_compress_ptr) -{ -} - -static boolean qt_empty_output_buffer(j_compress_ptr cinfo) -{ - my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest; - - int written = dest->device->write((char*)dest->buffer, max_buf); - if (written == -1) - (*cinfo->err->error_exit)((j_common_ptr)cinfo); - - dest->next_output_byte = dest->buffer; - dest->free_in_buffer = max_buf; - - return TRUE; -} - -static void qt_term_destination(j_compress_ptr cinfo) -{ - my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest; - qint64 n = max_buf - dest->free_in_buffer; - - qint64 written = dest->device->write((char*)dest->buffer, n); - if (written == -1) - (*cinfo->err->error_exit)((j_common_ptr)cinfo); -} - -} - -inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device) -{ - jpeg_destination_mgr::init_destination = qt_init_destination; - jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer; - jpeg_destination_mgr::term_destination = qt_term_destination; - this->device = device; - next_output_byte = buffer; - free_in_buffer = max_buf; -} - - -static inline void set_text(const QImage &image, j_compress_ptr cinfo, const QString &description) -{ - QMap<QString, QString> text; - foreach (const QString &key, image.textKeys()) { - if (!key.isEmpty()) - text.insert(key, image.text(key)); - } - foreach (const QString &pair, description.split(QLatin1String("\n\n"))) { - int index = pair.indexOf(QLatin1Char(':')); - if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) { - QString s = pair.simplified(); - if (!s.isEmpty()) - text.insert(QLatin1String("Description"), s); - } else { - QString key = pair.left(index); - if (!key.simplified().isEmpty()) - text.insert(key, pair.mid(index + 2).simplified()); - } - } - if (text.isEmpty()) - return; - - for (QMap<QString, QString>::ConstIterator it = text.constBegin(); it != text.constEnd(); ++it) { - QByteArray comment = it.key().toLatin1(); - if (!comment.isEmpty()) - comment += ": "; - comment += it.value().toLatin1(); - if (comment.length() > 65530) - comment.truncate(65530); - jpeg_write_marker(cinfo, JPEG_COM, (const JOCTET *)comment.constData(), comment.size()); - } -} - -static bool write_jpeg_image(const QImage &image, QIODevice *device, volatile int sourceQuality, const QString &description, bool optimize, bool progressive) -{ - bool success = false; - const QVector<QRgb> cmap = image.colorTable(); - - if (image.format() == QImage::Format_Invalid || image.format() == QImage::Format_Alpha8) - return false; - - struct jpeg_compress_struct cinfo; - JSAMPROW row_pointer[1]; - row_pointer[0] = 0; - - struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device); - struct my_error_mgr jerr; - - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = my_error_exit; - jerr.output_message = my_output_message; - - if (!setjmp(jerr.setjmp_buffer)) { - // WARNING: - // this if loop is inside a setjmp/longjmp branch - // do not create C++ temporaries here because the destructor may never be called - // if you allocate memory, make sure that you can free it (row_pointer[0]) - jpeg_create_compress(&cinfo); - - cinfo.dest = iod_dest; - - cinfo.image_width = image.width(); - cinfo.image_height = image.height(); - - bool gray = false; - switch (image.format()) { - case QImage::Format_Mono: - case QImage::Format_MonoLSB: - case QImage::Format_Indexed8: - gray = true; - for (int i = image.colorCount(); gray && i; i--) { - gray = gray & qIsGray(cmap[i-1]); - } - cinfo.input_components = gray ? 1 : 3; - cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB; - break; - case QImage::Format_Grayscale8: - gray = true; - cinfo.input_components = 1; - cinfo.in_color_space = JCS_GRAYSCALE; - break; - default: - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - } - - jpeg_set_defaults(&cinfo); - - qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.)) - + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.)); - qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.)) - + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54; - if (diffInch < diffCm) { - cinfo.density_unit = 1; // dots/inch - cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.); - cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.); - } else { - cinfo.density_unit = 2; // dots/cm - cinfo.X_density = (image.dotsPerMeterX()+50) / 100; - cinfo.Y_density = (image.dotsPerMeterY()+50) / 100; - } - - if (optimize) - cinfo.optimize_coding = true; - - if (progressive) - jpeg_simple_progression(&cinfo); - - int quality = sourceQuality >= 0 ? qMin(int(sourceQuality),100) : 75; - jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); - jpeg_start_compress(&cinfo, TRUE); - - set_text(image, &cinfo, description); - - row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components]; - int w = cinfo.image_width; - while (cinfo.next_scanline < cinfo.image_height) { - uchar *row = row_pointer[0]; - switch (image.format()) { - case QImage::Format_Mono: - case QImage::Format_MonoLSB: - if (gray) { - const uchar* data = image.constScanLine(cinfo.next_scanline); - if (image.format() == QImage::Format_MonoLSB) { - for (int i=0; i<w; i++) { - bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7))); - row[i] = qRed(cmap[bit]); - } - } else { - for (int i=0; i<w; i++) { - bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7)))); - row[i] = qRed(cmap[bit]); - } - } - } else { - const uchar* data = image.constScanLine(cinfo.next_scanline); - if (image.format() == QImage::Format_MonoLSB) { - for (int i=0; i<w; i++) { - bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7))); - *row++ = qRed(cmap[bit]); - *row++ = qGreen(cmap[bit]); - *row++ = qBlue(cmap[bit]); - } - } else { - for (int i=0; i<w; i++) { - bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7)))); - *row++ = qRed(cmap[bit]); - *row++ = qGreen(cmap[bit]); - *row++ = qBlue(cmap[bit]); - } - } - } - break; - case QImage::Format_Indexed8: - if (gray) { - const uchar* pix = image.constScanLine(cinfo.next_scanline); - for (int i=0; i<w; i++) { - *row = qRed(cmap[*pix]); - ++row; ++pix; - } - } else { - const uchar* pix = image.constScanLine(cinfo.next_scanline); - for (int i=0; i<w; i++) { - *row++ = qRed(cmap[*pix]); - *row++ = qGreen(cmap[*pix]); - *row++ = qBlue(cmap[*pix]); - ++pix; - } - } - break; - case QImage::Format_Grayscale8: - memcpy(row, image.constScanLine(cinfo.next_scanline), w); - break; - case QImage::Format_RGB888: - memcpy(row, image.constScanLine(cinfo.next_scanline), w * 3); - break; - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - case QImage::Format_ARGB32_Premultiplied: - { - const QRgb* rgb = (const QRgb*)image.constScanLine(cinfo.next_scanline); - for (int i=0; i<w; i++) { - *row++ = qRed(*rgb); - *row++ = qGreen(*rgb); - *row++ = qBlue(*rgb); - ++rgb; - } - } - break; - default: - { - // (Testing shows that this way is actually faster than converting to RGB888 + memcpy) - QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32); - const QRgb* rgb = (const QRgb*)rowImg.constScanLine(0); - for (int i=0; i<w; i++) { - *row++ = qRed(*rgb); - *row++ = qGreen(*rgb); - *row++ = qBlue(*rgb); - ++rgb; - } - } - break; - } - jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - success = true; - } else { - jpeg_destroy_compress(&cinfo); - success = false; - } - - delete iod_dest; - delete [] row_pointer[0]; - return success; -} - -class QJpegHandlerPrivate -{ -public: - enum State { - Ready, - ReadHeader, - ReadingEnd, - Error - }; - - QJpegHandlerPrivate(QJpegHandler *qq) - : quality(75), transformation(QImageIOHandler::TransformationNone), iod_src(0), - rgb888ToRgb32ConverterPtr(qt_convert_rgb888_to_rgb32), state(Ready), optimize(false), progressive(false), q(qq) - {} - - ~QJpegHandlerPrivate() - { - if(iod_src) - { - jpeg_destroy_decompress(&info); - delete iod_src; - iod_src = 0; - } - } - - bool readJpegHeader(QIODevice*); - bool read(QImage *image); - - int quality; - QImageIOHandler::Transformations transformation; - QVariant size; - QImage::Format format; - QSize scaledSize; - QRect scaledClipRect; - QRect clipRect; - QString description; - QStringList readTexts; - - struct jpeg_decompress_struct info; - struct my_jpeg_source_mgr * iod_src; - struct my_error_mgr err; - - Rgb888ToRgb32Converter rgb888ToRgb32ConverterPtr; - - State state; - - bool optimize; - bool progressive; - - QJpegHandler *q; -}; - -static bool readExifHeader(QDataStream &stream) -{ - char prefix[6]; - if (stream.readRawData(prefix, sizeof(prefix)) != sizeof(prefix)) - return false; - static const char exifMagic[6] = {'E', 'x', 'i', 'f', 0, 0}; - return memcmp(prefix, exifMagic, 6) == 0; -} - -/* - * Returns -1 on error - * Returns 0 if no Exif orientation was found - * Returns 1 orientation is horizontal (normal) - * Returns 2 mirror horizontal - * Returns 3 rotate 180 - * Returns 4 mirror vertical - * Returns 5 mirror horizontal and rotate 270 CCW - * Returns 6 rotate 90 CW - * Returns 7 mirror horizontal and rotate 90 CW - * Returns 8 rotate 270 CW - */ -static int getExifOrientation(QByteArray &exifData) -{ - QDataStream stream(&exifData, QIODevice::ReadOnly); - - if (!readExifHeader(stream)) - return -1; - - quint16 val; - quint32 offset; - const qint64 headerStart = stream.device()->pos(); - - // read byte order marker - stream >> val; - if (val == 0x4949) // 'II' == Intel - stream.setByteOrder(QDataStream::LittleEndian); - else if (val == 0x4d4d) // 'MM' == Motorola - stream.setByteOrder(QDataStream::BigEndian); - else - return -1; // unknown byte order - - // read size - stream >> val; - if (val != 0x2a) - return -1; - - stream >> offset; - - // read IFD - while (!stream.atEnd()) { - quint16 numEntries; - - // skip offset bytes to get the next IFD - const qint64 bytesToSkip = offset - (stream.device()->pos() - headerStart); - - if (stream.skipRawData(bytesToSkip) != bytesToSkip) - return -1; - - stream >> numEntries; - - for (; numEntries > 0; --numEntries) { - quint16 tag; - quint16 type; - quint32 components; - quint16 value; - quint16 dummy; - - stream >> tag >> type >> components >> value >> dummy; - if (tag == 0x0112) { // Tag Exif.Image.Orientation - if (components != 1) - return -1; - if (type != 3) // we are expecting it to be an unsigned short - return -1; - if (value < 1 || value > 8) // check for valid range - return -1; - - // It is possible to include the orientation multiple times. - // Right now the first value is returned. - return value; - } - } - - // read offset to next IFD - stream >> offset; - if (offset == 0) // this is the last IFD - break; - } - - // No Exif orientation was found - return 0; -} - -static QImageIOHandler::Transformations exif2Qt(int exifOrientation) -{ - switch (exifOrientation) { - case 1: // normal - return QImageIOHandler::TransformationNone; - case 2: // mirror horizontal - return QImageIOHandler::TransformationMirror; - case 3: // rotate 180 - return QImageIOHandler::TransformationRotate180; - case 4: // mirror vertical - return QImageIOHandler::TransformationFlip; - case 5: // mirror horizontal and rotate 270 CW - return QImageIOHandler::TransformationFlipAndRotate90; - case 6: // rotate 90 CW - return QImageIOHandler::TransformationRotate90; - case 7: // mirror horizontal and rotate 90 CW - return QImageIOHandler::TransformationMirrorAndRotate90; - case 8: // rotate 270 CW - return QImageIOHandler::TransformationRotate270; - } - qWarning("Invalid EXIF orientation"); - return QImageIOHandler::TransformationNone; -} - -/*! - \internal -*/ -bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) -{ - if(state == Ready) - { - state = Error; - iod_src = new my_jpeg_source_mgr(device); - - info.err = jpeg_std_error(&err); - err.error_exit = my_error_exit; - err.output_message = my_output_message; - - jpeg_create_decompress(&info); - info.src = iod_src; - - if (!setjmp(err.setjmp_buffer)) { - jpeg_save_markers(&info, JPEG_COM, 0xFFFF); - jpeg_save_markers(&info, JPEG_APP0 + 1, 0xFFFF); // Exif uses APP1 marker - - (void) jpeg_read_header(&info, TRUE); - - int width = 0; - int height = 0; - read_jpeg_size(width, height, &info); - size = QSize(width, height); - - format = QImage::Format_Invalid; - read_jpeg_format(format, &info); - - QByteArray exifData; - - for (jpeg_saved_marker_ptr marker = info.marker_list; marker != NULL; marker = marker->next) { - if (marker->marker == JPEG_COM) { - QString key, value; - QString s = QString::fromLatin1((const char *)marker->data, marker->data_length); - int index = s.indexOf(QLatin1String(": ")); - if (index == -1 || s.indexOf(QLatin1Char(' ')) < index) { - key = QLatin1String("Description"); - value = s; - } else { - key = s.left(index); - value = s.mid(index + 2); - } - if (!description.isEmpty()) - description += QLatin1String("\n\n"); - description += key + QLatin1String(": ") + value.simplified(); - readTexts.append(key); - readTexts.append(value); - } else if (marker->marker == JPEG_APP0 + 1) { - exifData.append((const char*)marker->data, marker->data_length); - } - } - - if (!exifData.isEmpty()) { - // Exif data present - int exifOrientation = getExifOrientation(exifData); - if (exifOrientation > 0) - transformation = exif2Qt(exifOrientation); - } - - state = ReadHeader; - return true; - } - else - { - return false; - } - } - else if(state == Error) - return false; - return true; -} - -bool QJpegHandlerPrivate::read(QImage *image) -{ - if(state == Ready) - readJpegHeader(q->device()); - - if(state == ReadHeader) - { - bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality, rgb888ToRgb32ConverterPtr, &info, &err); - if (success) { - for (int i = 0; i < readTexts.size()-1; i+=2) - image->setText(readTexts.at(i), readTexts.at(i+1)); - - state = ReadingEnd; - return true; - } - - state = Error; - } - - return false; - -} - -Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, const uchar *src, int len); -Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len); -extern "C" void qt_convert_rgb888_to_rgb32_mips_dspr2_asm(quint32 *dst, const uchar *src, int len); - -QJpegHandler::QJpegHandler() - : d(new QJpegHandlerPrivate(this)) -{ -#if defined(__ARM_NEON__) - // from qimage_neon.cpp - if (qCpuHasFeature(NEON)) - d->rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_neon; -#endif - -#if defined(QT_COMPILER_SUPPORTS_SSSE3) - // from qimage_ssse3.cpps - if (qCpuHasFeature(SSSE3)) { - d->rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_ssse3; - } -#endif // QT_COMPILER_SUPPORTS_SSSE3 -#if defined(QT_COMPILER_SUPPORTS_MIPS_DSPR2) - if (qCpuHasFeature(DSPR2)) { - d->rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_mips_dspr2_asm; - } -#endif // QT_COMPILER_SUPPORTS_DSPR2 -} - -QJpegHandler::~QJpegHandler() -{ - delete d; -} - -bool QJpegHandler::canRead() const -{ - if(d->state == QJpegHandlerPrivate::Ready && !canRead(device())) - return false; - - if (d->state != QJpegHandlerPrivate::Error && d->state != QJpegHandlerPrivate::ReadingEnd) { - setFormat("jpeg"); - return true; - } - - return false; -} - -bool QJpegHandler::canRead(QIODevice *device) -{ - if (!device) { - qWarning("QJpegHandler::canRead() called with no device"); - return false; - } - - char buffer[2]; - if (device->peek(buffer, 2) != 2) - return false; - return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8; -} - -bool QJpegHandler::read(QImage *image) -{ - if (!canRead()) - return false; - return d->read(image); -} - -extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient); - -bool QJpegHandler::write(const QImage &image) -{ - if (d->transformation != QImageIOHandler::TransformationNone) { - // We don't support writing EXIF headers so apply the transform to the data. - QImage img = image; - qt_imageTransform(img, d->transformation); - return write_jpeg_image(img, device(), d->quality, d->description, d->optimize, d->progressive); - } - return write_jpeg_image(image, device(), d->quality, d->description, d->optimize, d->progressive); -} - -bool QJpegHandler::supportsOption(ImageOption option) const -{ - return option == Quality - || option == ScaledSize - || option == ScaledClipRect - || option == ClipRect - || option == Description - || option == Size - || option == ImageFormat - || option == OptimizedWrite - || option == ProgressiveScanWrite - || option == ImageTransformation; -} - -QVariant QJpegHandler::option(ImageOption option) const -{ - switch(option) { - case Quality: - return d->quality; - case ScaledSize: - return d->scaledSize; - case ScaledClipRect: - return d->scaledClipRect; - case ClipRect: - return d->clipRect; - case Description: - d->readJpegHeader(device()); - return d->description; - case Size: - d->readJpegHeader(device()); - return d->size; - case ImageFormat: - d->readJpegHeader(device()); - return d->format; - case OptimizedWrite: - return d->optimize; - case ProgressiveScanWrite: - return d->progressive; - case ImageTransformation: - d->readJpegHeader(device()); - return int(d->transformation); - default: - break; - } - - return QVariant(); -} - -void QJpegHandler::setOption(ImageOption option, const QVariant &value) -{ - switch(option) { - case Quality: - d->quality = value.toInt(); - break; - case ScaledSize: - d->scaledSize = value.toSize(); - break; - case ScaledClipRect: - d->scaledClipRect = value.toRect(); - break; - case ClipRect: - d->clipRect = value.toRect(); - break; - case Description: - d->description = value.toString(); - break; - case OptimizedWrite: - d->optimize = value.toBool(); - break; - case ProgressiveScanWrite: - d->progressive = value.toBool(); - break; - case ImageTransformation: { - int transformation = value.toInt(); - if (transformation > 0 && transformation < 8) - d->transformation = QImageIOHandler::Transformations(transformation); - } - default: - break; - } -} - -QByteArray QJpegHandler::name() const -{ - return "jpeg"; -} - -QT_END_NAMESPACE diff --git a/src/gui/image/qjpeghandler.pri b/src/gui/image/qjpeghandler.pri deleted file mode 100644 index de40c6742e..0000000000 --- a/src/gui/image/qjpeghandler.pri +++ /dev/null @@ -1,11 +0,0 @@ -# common to plugin and built-in forms -HEADERS += $$PWD/qjpeghandler_p.h -SOURCES += $$PWD/qjpeghandler.cpp -contains(QT_CONFIG, system-jpeg) { - msvc: \ - LIBS += libjpeg.lib - else: \ - LIBS += -ljpeg -} else { - include($$PWD/../../3rdparty/libjpeg.pri) -} diff --git a/src/gui/image/qjpeghandler_p.h b/src/gui/image/qjpeghandler_p.h deleted file mode 100644 index 534ce12115..0000000000 --- a/src/gui/image/qjpeghandler_p.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QJPEGHANDLER_P_H -#define QJPEGHANDLER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtGui/qimageiohandler.h> -#include <QtCore/QSize> -#include <QtCore/QRect> - -QT_BEGIN_NAMESPACE - -class QJpegHandlerPrivate; -class QJpegHandler : public QImageIOHandler -{ -public: - QJpegHandler(); - ~QJpegHandler(); - - bool canRead() const Q_DECL_OVERRIDE; - bool read(QImage *image) Q_DECL_OVERRIDE; - bool write(const QImage &image) Q_DECL_OVERRIDE; - - QByteArray name() const Q_DECL_OVERRIDE; - - static bool canRead(QIODevice *device); - - QVariant option(ImageOption option) const Q_DECL_OVERRIDE; - void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE; - bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE; - -private: - QJpegHandlerPrivate *d; -}; - -QT_END_NAMESPACE - -#endif // QJPEGHANDLER_P_H diff --git a/src/gui/image/qpnghandler.pri b/src/gui/image/qpnghandler.pri index 505d214130..e69de29bb2 100644 --- a/src/gui/image/qpnghandler.pri +++ b/src/gui/image/qpnghandler.pri @@ -1,4 +0,0 @@ -HEADERS += $$PWD/qpnghandler_p.h -SOURCES += $$PWD/qpnghandler.cpp - -include($$PWD/../../3rdparty/png_dependency.pri) |