📄 qgifhandler.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 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://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** 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**** 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 "qgifhandler.h"#include <qimage.h>#include <qiodevice.h>#include <qvariant.h>#define Q_TRANSPARENT 0x00ffffff/* 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, QSize *nextSize); 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][1<< max_lzw_bits]; short stack[(1<<(max_lzw_bits))*2]; short *sp; bool needfirst; int x, y; int frame; bool out_of_bounds; bool digress; void nextY(QImage *image); 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;}/*! Destroys a QGIFFormat.*/QGIFFormat::~QGIFFormat(){ if (globalcmap) delete[] globalcmap; if (localcmap) delete[] localcmap;}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 QRgb *bits = (QRgb*)image->bits(); 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.scanLine(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, QSize *nextSize){ // 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."#define LM(l, m) (((m)<<8)|l) digress = false; 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->size() != QSize(swidth, sheight)) || image->format() != format) { (*image) = QImage(swidth, sheight, format); memset(image->bits(), 0, image->numBytes()); // ### size of the upcoming frame, should rather // be known before decoding it. *nextSize = QSize(swidth, sheight); } 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(image->bits(), 0, image->numBytes()); } for (int ln=0; ln<h; ln++) { memcpy(backingstore.scanLine(ln), image->scanLine(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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -