src/gui/image/qgifhandler.cpp
changeset 33 3e2da88830cd
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the plugins of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ** WARNING:
       
    41 **      A separate license from Unisys may be required to use the gif
       
    42 **      reader. See http://www.unisys.com/about__unisys/lzw/
       
    43 **      for information from Unisys
       
    44 **
       
    45 ****************************************************************************/
       
    46 
       
    47 #include "qgifhandler_p.h"
       
    48 
       
    49 #include <qimage.h>
       
    50 #include <qiodevice.h>
       
    51 #include <qvariant.h>
       
    52 
       
    53 QT_BEGIN_NAMESPACE
       
    54 
       
    55 #define Q_TRANSPARENT 0x00ffffff
       
    56 
       
    57 // avoid going through QImage::scanLine() which calls detach
       
    58 #define FAST_SCAN_LINE(bits, bpl, y) (bits + (y) * bpl)
       
    59 
       
    60 
       
    61 /*
       
    62   Incremental image decoder for GIF image format.
       
    63 
       
    64   This subclass of QImageFormat decodes GIF format images,
       
    65   including animated GIFs. Internally in
       
    66 */
       
    67 
       
    68 class QGIFFormat {
       
    69 public:
       
    70     QGIFFormat();
       
    71     ~QGIFFormat();
       
    72 
       
    73     int decode(QImage *image, const uchar* buffer, int length,
       
    74                int *nextFrameDelay, int *loopCount);
       
    75     static void scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount);
       
    76 
       
    77     bool newFrame;
       
    78     bool partialNewFrame;
       
    79 
       
    80 private:
       
    81     void fillRect(QImage *image, int x, int y, int w, int h, QRgb col);
       
    82     inline QRgb color(uchar index) const;
       
    83 
       
    84     // GIF specific stuff
       
    85     QRgb* globalcmap;
       
    86     QRgb* localcmap;
       
    87     QImage backingstore;
       
    88     unsigned char hold[16];
       
    89     bool gif89;
       
    90     int count;
       
    91     int ccount;
       
    92     int expectcount;
       
    93     enum State {
       
    94         Header,
       
    95         LogicalScreenDescriptor,
       
    96         GlobalColorMap,
       
    97         LocalColorMap,
       
    98         Introducer,
       
    99         ImageDescriptor,
       
   100         TableImageLZWSize,
       
   101         ImageDataBlockSize,
       
   102         ImageDataBlock,
       
   103         ExtensionLabel,
       
   104         GraphicControlExtension,
       
   105         ApplicationExtension,
       
   106         NetscapeExtensionBlockSize,
       
   107         NetscapeExtensionBlock,
       
   108         SkipBlockSize,
       
   109         SkipBlock,
       
   110         Done,
       
   111         Error
       
   112     } state;
       
   113     int gncols;
       
   114     int lncols;
       
   115     int ncols;
       
   116     int lzwsize;
       
   117     bool lcmap;
       
   118     int swidth, sheight;
       
   119     int width, height;
       
   120     int left, top, right, bottom;
       
   121     enum Disposal { NoDisposal, DoNotChange, RestoreBackground, RestoreImage };
       
   122     Disposal disposal;
       
   123     bool disposed;
       
   124     int trans_index;
       
   125     bool gcmap;
       
   126     int bgcol;
       
   127     int interlace;
       
   128     int accum;
       
   129     int bitcount;
       
   130 
       
   131     enum { max_lzw_bits=12 }; // (poor-compiler's static const int)
       
   132 
       
   133     int code_size, clear_code, end_code, max_code_size, max_code;
       
   134     int firstcode, oldcode, incode;
       
   135     short* table[2];
       
   136     short* stack;
       
   137     short *sp;
       
   138     bool needfirst;
       
   139     int x, y;
       
   140     int frame;
       
   141     bool out_of_bounds;
       
   142     bool digress;
       
   143     void nextY(unsigned char *bits, int bpl);
       
   144     void disposePrevious(QImage *image);
       
   145 };
       
   146 
       
   147 /*!
       
   148     Constructs a QGIFFormat.
       
   149 */
       
   150 QGIFFormat::QGIFFormat()
       
   151 {
       
   152     globalcmap = 0;
       
   153     localcmap = 0;
       
   154     lncols = 0;
       
   155     gncols = 0;
       
   156     disposal = NoDisposal;
       
   157     out_of_bounds = false;
       
   158     disposed = true;
       
   159     frame = -1;
       
   160     state = Header;
       
   161     count = 0;
       
   162     lcmap = false;
       
   163     newFrame = false;
       
   164     partialNewFrame = false;
       
   165     table[0] = 0;
       
   166     table[1] = 0;
       
   167     stack = 0;
       
   168 }
       
   169 
       
   170 /*!
       
   171     Destroys a QGIFFormat.
       
   172 */
       
   173 QGIFFormat::~QGIFFormat()
       
   174 {
       
   175     if (globalcmap) delete[] globalcmap;
       
   176     if (localcmap) delete[] localcmap;
       
   177     delete [] stack;
       
   178 }
       
   179 
       
   180 void QGIFFormat::disposePrevious(QImage *image)
       
   181 {
       
   182     if (out_of_bounds) {
       
   183         // flush anything that survived
       
   184         // ### Changed: QRect(0, 0, swidth, sheight)
       
   185     }
       
   186 
       
   187     // Handle disposal of previous image before processing next one
       
   188 
       
   189     if (disposed) return;
       
   190 
       
   191     int l = qMin(swidth-1,left);
       
   192     int r = qMin(swidth-1,right);
       
   193     int t = qMin(sheight-1,top);
       
   194     int b = qMin(sheight-1,bottom);
       
   195 
       
   196     switch (disposal) {
       
   197       case NoDisposal:
       
   198         break;
       
   199       case DoNotChange:
       
   200         break;
       
   201       case RestoreBackground:
       
   202         if (trans_index>=0) {
       
   203             // Easy:  we use the transparent color
       
   204             fillRect(image, l, t, r-l+1, b-t+1, Q_TRANSPARENT);
       
   205         } else if (bgcol>=0) {
       
   206             // Easy:  we use the bgcol given
       
   207             fillRect(image, l, t, r-l+1, b-t+1, color(bgcol));
       
   208         } else {
       
   209             // Impossible:  We don't know of a bgcol - use pixel 0
       
   210             QRgb *bits = (QRgb*)image->bits();
       
   211             fillRect(image, l, t, r-l+1, b-t+1, bits[0]);
       
   212         }
       
   213         // ### Changed: QRect(l, t, r-l+1, b-t+1)
       
   214         break;
       
   215       case RestoreImage: {
       
   216         if (frame >= 0) {
       
   217             for (int ln=t; ln<=b; ln++) {
       
   218                 memcpy(image->scanLine(ln)+l,
       
   219                     backingstore.scanLine(ln-t),
       
   220                     (r-l+1)*sizeof(QRgb));
       
   221             }
       
   222             // ### Changed: QRect(l, t, r-l+1, b-t+1)
       
   223         }
       
   224       }
       
   225     }
       
   226     disposal = NoDisposal; // Until an extension says otherwise.
       
   227 
       
   228     disposed = true;
       
   229 }
       
   230 
       
   231 /*!
       
   232     This function decodes some data into image changes.
       
   233 
       
   234     Returns the number of bytes consumed.
       
   235 */
       
   236 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
       
   237                        int *nextFrameDelay, int *loopCount)
       
   238 {
       
   239     // We are required to state that
       
   240     //    "The Graphics Interchange Format(c) is the Copyright property of
       
   241     //    CompuServe Incorporated. GIF(sm) is a Service Mark property of
       
   242     //    CompuServe Incorporated."
       
   243 
       
   244     if (!stack) {
       
   245         stack = new short[(1 << max_lzw_bits) * 4];
       
   246         table[0] = &stack[(1 << max_lzw_bits) * 2];
       
   247         table[1] = &stack[(1 << max_lzw_bits) * 3];
       
   248     }
       
   249 
       
   250     image->detach();
       
   251     int bpl = image->bytesPerLine();
       
   252     unsigned char *bits = image->bits();
       
   253 
       
   254 #define LM(l, m) (((m)<<8)|l)
       
   255     digress = false;
       
   256     const int initial = length;
       
   257     while (!digress && length) {
       
   258         length--;
       
   259         unsigned char ch=*buffer++;
       
   260         switch (state) {
       
   261           case Header:
       
   262             hold[count++]=ch;
       
   263             if (count==6) {
       
   264                 // Header
       
   265                 gif89=(hold[3]!='8' || hold[4]!='7');
       
   266                 state=LogicalScreenDescriptor;
       
   267                 count=0;
       
   268             }
       
   269             break;
       
   270           case LogicalScreenDescriptor:
       
   271             hold[count++]=ch;
       
   272             if (count==7) {
       
   273                 // Logical Screen Descriptor
       
   274                 swidth=LM(hold[0], hold[1]);
       
   275                 sheight=LM(hold[2], hold[3]);
       
   276                 gcmap=!!(hold[4]&0x80);
       
   277                 //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1);
       
   278                 //UNUSED: gcmsortflag=!!(hold[4]&0x08);
       
   279                 gncols=2<<(hold[4]&0x7);
       
   280                 bgcol=(gcmap) ? hold[5] : -1;
       
   281                 //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0;
       
   282 
       
   283                 trans_index = -1;
       
   284                 count=0;
       
   285                 ncols=gncols;
       
   286                 if (gcmap) {
       
   287                     ccount=0;
       
   288                     state=GlobalColorMap;
       
   289                     globalcmap = new QRgb[gncols+1]; // +1 for trans_index
       
   290                     globalcmap[gncols] = Q_TRANSPARENT;
       
   291                 } else {
       
   292                     state=Introducer;
       
   293                 }
       
   294             }
       
   295             break;
       
   296           case GlobalColorMap: case LocalColorMap:
       
   297             hold[count++]=ch;
       
   298             if (count==3) {
       
   299                 QRgb rgb = qRgb(hold[0], hold[1], hold[2]);
       
   300                 if (state == LocalColorMap) {
       
   301                     if (ccount < lncols)
       
   302                         localcmap[ccount] =  rgb;
       
   303                 } else {
       
   304                     globalcmap[ccount] = rgb;
       
   305                 }
       
   306                 if (++ccount >= ncols) {
       
   307                     if (state == LocalColorMap)
       
   308                         state=TableImageLZWSize;
       
   309                     else
       
   310                         state=Introducer;
       
   311                 }
       
   312                 count=0;
       
   313             }
       
   314             break;
       
   315           case Introducer:
       
   316             hold[count++]=ch;
       
   317             switch (ch) {
       
   318               case ',':
       
   319                 state=ImageDescriptor;
       
   320                 break;
       
   321               case '!':
       
   322                 state=ExtensionLabel;
       
   323                 break;
       
   324               case ';':
       
   325                   // ### Changed: QRect(0, 0, swidth, sheight)
       
   326                 state=Done;
       
   327                 break;
       
   328               default:
       
   329                 digress=true;
       
   330                 // Unexpected Introducer - ignore block
       
   331                 state=Error;
       
   332             }
       
   333             break;
       
   334           case ImageDescriptor:
       
   335             hold[count++]=ch;
       
   336             if (count==10) {
       
   337                 int newleft=LM(hold[1], hold[2]);
       
   338                 int newtop=LM(hold[3], hold[4]);
       
   339                 int newwidth=LM(hold[5], hold[6]);
       
   340                 int newheight=LM(hold[7], hold[8]);
       
   341 
       
   342                 // disbelieve ridiculous logical screen sizes,
       
   343                 // unless the image frames are also large.
       
   344                 if (swidth/10 > qMax(newwidth,200))
       
   345                     swidth = -1;
       
   346                 if (sheight/10 > qMax(newheight,200))
       
   347                     sheight = -1;
       
   348 
       
   349                 if (swidth <= 0)
       
   350                     swidth = newleft + newwidth;
       
   351                 if (sheight <= 0)
       
   352                     sheight = newtop + newheight;
       
   353 
       
   354                 QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32;
       
   355                 if (image->isNull()) {
       
   356                     (*image) = QImage(swidth, sheight, format);
       
   357                     bpl = image->bytesPerLine();
       
   358                     bits = image->bits();
       
   359                     memset(bits, 0, image->byteCount());
       
   360                 }
       
   361 
       
   362                 disposePrevious(image);
       
   363                 disposed = false;
       
   364 
       
   365                 left = newleft;
       
   366                 top = newtop;
       
   367                 width = newwidth;
       
   368                 height = newheight;
       
   369 
       
   370                 right=qMax(0, qMin(left+width, swidth)-1);
       
   371                 bottom=qMax(0, qMin(top+height, sheight)-1);
       
   372                 lcmap=!!(hold[9]&0x80);
       
   373                 interlace=!!(hold[9]&0x40);
       
   374                 //bool lcmsortflag=!!(hold[9]&0x20);
       
   375                 lncols=lcmap ? (2<<(hold[9]&0x7)) : 0;
       
   376                 if (lncols) {
       
   377                     if (localcmap)
       
   378                         delete [] localcmap;
       
   379                     localcmap = new QRgb[lncols+1];
       
   380                     localcmap[lncols] = Q_TRANSPARENT;
       
   381                     ncols = lncols;
       
   382                 } else {
       
   383                     ncols = gncols;
       
   384                 }
       
   385                 frame++;
       
   386                 if (frame == 0) {
       
   387                     if (left || top || width<swidth || height<sheight) {
       
   388                         // Not full-size image - erase with bg or transparent
       
   389                         if (trans_index >= 0) {
       
   390                             fillRect(image, 0, 0, swidth, sheight, color(trans_index));
       
   391                             // ### Changed: QRect(0, 0, swidth, sheight)
       
   392                         } else if (bgcol>=0) {
       
   393                             fillRect(image, 0, 0, swidth, sheight, color(bgcol));
       
   394                             // ### Changed: QRect(0, 0, swidth, sheight)
       
   395                         }
       
   396                     }
       
   397                 }
       
   398 
       
   399                 if (disposal == RestoreImage) {
       
   400                     int l = qMin(swidth-1,left);
       
   401                     int r = qMin(swidth-1,right);
       
   402                     int t = qMin(sheight-1,top);
       
   403                     int b = qMin(sheight-1,bottom);
       
   404                     int w = r-l+1;
       
   405                     int h = b-t+1;
       
   406 
       
   407                     if (backingstore.width() < w
       
   408                         || backingstore.height() < h) {
       
   409                         // We just use the backing store as a byte array
       
   410                         backingstore = QImage(qMax(backingstore.width(), w),
       
   411                                               qMax(backingstore.height(), h),
       
   412                                               QImage::Format_RGB32);
       
   413                         memset(bits, 0, image->byteCount());
       
   414                     }
       
   415                     const int dest_bpl = backingstore.bytesPerLine();
       
   416                     unsigned char *dest_data = backingstore.bits();
       
   417                     for (int ln=0; ln<h; ln++) {
       
   418                         memcpy(FAST_SCAN_LINE(dest_data, dest_bpl, ln),
       
   419                                FAST_SCAN_LINE(bits, bpl, t+ln) + l, w*sizeof(QRgb));
       
   420                     }
       
   421                 }
       
   422 
       
   423                 count=0;
       
   424                 if (lcmap) {
       
   425                     ccount=0;
       
   426                     state=LocalColorMap;
       
   427                 } else {
       
   428                     state=TableImageLZWSize;
       
   429                 }
       
   430                 x = left;
       
   431                 y = top;
       
   432                 accum = 0;
       
   433                 bitcount = 0;
       
   434                 sp = stack;
       
   435                 firstcode = oldcode = 0;
       
   436                 needfirst = true;
       
   437                 out_of_bounds = left>=swidth || y>=sheight;
       
   438             }
       
   439             break;
       
   440           case TableImageLZWSize: {
       
   441             lzwsize=ch;
       
   442             if (lzwsize > max_lzw_bits) {
       
   443                 state=Error;
       
   444             } else {
       
   445                 code_size=lzwsize+1;
       
   446                 clear_code=1<<lzwsize;
       
   447                 end_code=clear_code+1;
       
   448                 max_code_size=2*clear_code;
       
   449                 max_code=clear_code+2;
       
   450                 int i;
       
   451                 for (i=0; i<clear_code; i++) {
       
   452                     table[0][i]=0;
       
   453                     table[1][i]=i;
       
   454                 }
       
   455                 state=ImageDataBlockSize;
       
   456             }
       
   457             count=0;
       
   458             break;
       
   459           } case ImageDataBlockSize:
       
   460             expectcount=ch;
       
   461             if (expectcount) {
       
   462                 state=ImageDataBlock;
       
   463             } else {
       
   464                 state=Introducer;
       
   465                 digress = true;
       
   466                 newFrame = true;
       
   467             }
       
   468             break;
       
   469           case ImageDataBlock:
       
   470             count++;
       
   471             accum|=(ch<<bitcount);
       
   472             bitcount+=8;
       
   473             while (bitcount>=code_size && state==ImageDataBlock) {
       
   474                 int code=accum&((1<<code_size)-1);
       
   475                 bitcount-=code_size;
       
   476                 accum>>=code_size;
       
   477 
       
   478                 if (code==clear_code) {
       
   479                     if (!needfirst) {
       
   480                         code_size=lzwsize+1;
       
   481                         max_code_size=2*clear_code;
       
   482                         max_code=clear_code+2;
       
   483                     }
       
   484                     needfirst=true;
       
   485                 } else if (code==end_code) {
       
   486                     bitcount = -32768;
       
   487                     // Left the block end arrive
       
   488                 } else {
       
   489                     if (needfirst) {
       
   490                         firstcode=oldcode=code;
       
   491                         if (!out_of_bounds && image->height() > y && firstcode!=trans_index)
       
   492                             ((QRgb*)FAST_SCAN_LINE(bits, bpl, y))[x] = color(firstcode);
       
   493                         x++;
       
   494                         if (x>=swidth) out_of_bounds = true;
       
   495                         needfirst=false;
       
   496                         if (x>=left+width) {
       
   497                             x=left;
       
   498                             out_of_bounds = left>=swidth || y>=sheight;
       
   499                             nextY(bits, bpl);
       
   500                         }
       
   501                     } else {
       
   502                         incode=code;
       
   503                         if (code>=max_code) {
       
   504                             *sp++=firstcode;
       
   505                             code=oldcode;
       
   506                         }
       
   507                         while (code>=clear_code+2) {
       
   508                             *sp++=table[1][code];
       
   509                             if (code==table[0][code]) {
       
   510                                 state=Error;
       
   511                                 break;
       
   512                             }
       
   513                             if (sp-stack>=(1<<(max_lzw_bits))*2) {
       
   514                                 state=Error;
       
   515                                 break;
       
   516                             }
       
   517                             code=table[0][code];
       
   518                         }
       
   519                         *sp++=firstcode=table[1][code];
       
   520                         code=max_code;
       
   521                         if (code<(1<<max_lzw_bits)) {
       
   522                             table[0][code]=oldcode;
       
   523                             table[1][code]=firstcode;
       
   524                             max_code++;
       
   525                             if ((max_code>=max_code_size)
       
   526                              && (max_code_size<(1<<max_lzw_bits)))
       
   527                             {
       
   528                                 max_code_size*=2;
       
   529                                 code_size++;
       
   530                             }
       
   531                         }
       
   532                         oldcode=incode;
       
   533                         const int h = image->height();
       
   534                         const QRgb *map = lcmap ? localcmap : globalcmap;
       
   535                         QRgb *line = 0;
       
   536                         if (!out_of_bounds && h > y)
       
   537                             line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
       
   538                         while (sp>stack) {
       
   539                             const uchar index = *(--sp);
       
   540                             if (!out_of_bounds && h > y && index!=trans_index) {
       
   541                                 if (index > ncols)
       
   542                                     line[x] = Q_TRANSPARENT;
       
   543                                 else
       
   544                                     line[x] = map ? map[index] : 0;
       
   545                             }
       
   546                             x++;
       
   547                             if (x>=swidth) out_of_bounds = true;
       
   548                             if (x>=left+width) {
       
   549                                 x=left;
       
   550                                 out_of_bounds = left>=swidth || y>=sheight;
       
   551                                 nextY(bits, bpl);
       
   552                                 if (!out_of_bounds && h > y)
       
   553                                     line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
       
   554                             }
       
   555                         }
       
   556                     }
       
   557                 }
       
   558             }
       
   559             partialNewFrame = true;
       
   560             if (count==expectcount) {
       
   561                 count=0;
       
   562                 state=ImageDataBlockSize;
       
   563             }
       
   564             break;
       
   565           case ExtensionLabel:
       
   566             switch (ch) {
       
   567             case 0xf9:
       
   568                 state=GraphicControlExtension;
       
   569                 break;
       
   570             case 0xff:
       
   571                 state=ApplicationExtension;
       
   572                 break;
       
   573 #if 0
       
   574             case 0xfe:
       
   575                 state=CommentExtension;
       
   576                 break;
       
   577             case 0x01:
       
   578                 break;
       
   579 #endif
       
   580             default:
       
   581                 state=SkipBlockSize;
       
   582             }
       
   583             count=0;
       
   584             break;
       
   585           case ApplicationExtension:
       
   586             if (count<11) hold[count]=ch;
       
   587             count++;
       
   588             if (count==hold[0]+1) {
       
   589                 if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) {
       
   590                     // Looping extension
       
   591                     state=NetscapeExtensionBlockSize;
       
   592                 } else {
       
   593                     state=SkipBlockSize;
       
   594                 }
       
   595                 count=0;
       
   596             }
       
   597             break;
       
   598           case NetscapeExtensionBlockSize:
       
   599             expectcount=ch;
       
   600             count=0;
       
   601             if (expectcount) state=NetscapeExtensionBlock;
       
   602             else state=Introducer;
       
   603             break;
       
   604           case NetscapeExtensionBlock:
       
   605             if (count<3) hold[count]=ch;
       
   606             count++;
       
   607             if (count==expectcount) {
       
   608                 *loopCount = hold[1]+hold[2]*256;
       
   609                 state=SkipBlockSize; // Ignore further blocks
       
   610             }
       
   611             break;
       
   612           case GraphicControlExtension:
       
   613             if (count<5) hold[count]=ch;
       
   614             count++;
       
   615             if (count==hold[0]+1) {
       
   616                 disposePrevious(image);
       
   617                 disposal=Disposal((hold[1]>>2)&0x7);
       
   618                 //UNUSED: waitforuser=!!((hold[1]>>1)&0x1);
       
   619                 int delay=count>3 ? LM(hold[2], hold[3]) : 1;
       
   620                 // IE and mozilla use a minimum delay of 10. With the minimum delay of 10
       
   621                 // we are compatible to them and avoid huge loads on the app and xserver.
       
   622                 *nextFrameDelay = (delay < 2 ? 10 : delay) * 10;
       
   623 
       
   624                 bool havetrans=hold[1]&0x1;
       
   625                 trans_index = havetrans ? hold[4] : -1;
       
   626 
       
   627                 count=0;
       
   628                 state=SkipBlockSize;
       
   629             }
       
   630             break;
       
   631           case SkipBlockSize:
       
   632             expectcount=ch;
       
   633             count=0;
       
   634             if (expectcount) state=SkipBlock;
       
   635             else state=Introducer;
       
   636             break;
       
   637           case SkipBlock:
       
   638             count++;
       
   639             if (count==expectcount) state=SkipBlockSize;
       
   640             break;
       
   641           case Done:
       
   642             digress=true;
       
   643             /* Netscape ignores the junk, so we do too.
       
   644             length++; // Unget
       
   645             state=Error; // More calls to this is an error
       
   646             */
       
   647             break;
       
   648           case Error:
       
   649             return -1; // Called again after done.
       
   650         }
       
   651     }
       
   652     return initial-length;
       
   653 }
       
   654 
       
   655 /*!
       
   656    Scans through the data stream defined by \a device and returns the image
       
   657    sizes found in the stream in the \a imageSizes vector.
       
   658 */
       
   659 void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount)
       
   660 {
       
   661     if (!device)
       
   662         return;
       
   663 
       
   664     qint64 oldPos = device->pos();
       
   665     if (!device->seek(0))
       
   666         return;
       
   667 
       
   668     int colorCount = 0;
       
   669     int localColorCount = 0;
       
   670     int globalColorCount = 0;
       
   671     int colorReadCount = 0;
       
   672     bool localColormap = false;
       
   673     bool globalColormap = false;
       
   674     int count = 0;
       
   675     int blockSize = 0;
       
   676     int imageWidth = 0;
       
   677     int imageHeight = 0;
       
   678     bool done = false;
       
   679     uchar hold[16];
       
   680     State state = Header;
       
   681 
       
   682     const int readBufferSize = 40960; // 40k read buffer
       
   683     QByteArray readBuffer(device->read(readBufferSize));
       
   684 
       
   685     if (readBuffer.isEmpty()) {
       
   686         device->seek(oldPos);
       
   687         return;
       
   688     }
       
   689 
       
   690     // This is a specialized version of the state machine from decode(),
       
   691     // which doesn't do any image decoding or mallocing, and has an
       
   692     // optimized way of skipping SkipBlocks, ImageDataBlocks and
       
   693     // Global/LocalColorMaps.
       
   694 
       
   695     while (!readBuffer.isEmpty()) {
       
   696         int length = readBuffer.size();
       
   697         const uchar *buffer = (const uchar *) readBuffer.constData();
       
   698         while (!done && length) {
       
   699             length--;
       
   700             uchar ch = *buffer++;
       
   701             switch (state) {
       
   702             case Header:
       
   703                 hold[count++] = ch;
       
   704                 if (count == 6) {
       
   705                     state = LogicalScreenDescriptor;
       
   706                     count = 0;
       
   707                 }
       
   708                 break;
       
   709             case LogicalScreenDescriptor:
       
   710                 hold[count++] = ch;
       
   711                 if (count == 7) {
       
   712                     imageWidth = LM(hold[0], hold[1]);
       
   713                     imageHeight = LM(hold[2], hold[3]);
       
   714                     globalColormap = !!(hold[4] & 0x80);
       
   715                     globalColorCount = 2 << (hold[4] & 0x7);
       
   716                     count = 0;
       
   717                     colorCount = globalColorCount;
       
   718                     if (globalColormap) {
       
   719                         int colorTableSize = 3 * globalColorCount;
       
   720                         if (length >= colorTableSize) {
       
   721                             // skip the global color table in one go
       
   722                             length -= colorTableSize;
       
   723                             buffer += colorTableSize;
       
   724                             state = Introducer;
       
   725                         } else {
       
   726                             colorReadCount = 0;
       
   727                             state = GlobalColorMap;
       
   728                         }
       
   729                     } else {
       
   730                         state=Introducer;
       
   731                     }
       
   732                 }
       
   733                 break;
       
   734             case GlobalColorMap:
       
   735             case LocalColorMap:
       
   736                 hold[count++] = ch;
       
   737                 if (count == 3) {
       
   738                     if (++colorReadCount >= colorCount) {
       
   739                         if (state == LocalColorMap)
       
   740                             state = TableImageLZWSize;
       
   741                         else
       
   742                             state = Introducer;
       
   743                     }
       
   744                     count = 0;
       
   745                 }
       
   746                 break;
       
   747             case Introducer:
       
   748                 hold[count++] = ch;
       
   749                 switch (ch) {
       
   750                 case 0x2c:
       
   751                     state = ImageDescriptor;
       
   752                     break;
       
   753                 case 0x21:
       
   754                     state = ExtensionLabel;
       
   755                     break;
       
   756                 case 0x3b:
       
   757                     state = Done;
       
   758                     break;
       
   759                 default:
       
   760                     done = true;
       
   761                     state = Error;
       
   762                 }
       
   763                 break;
       
   764             case ImageDescriptor:
       
   765                 hold[count++] = ch;
       
   766                 if (count == 10) {
       
   767                     int newLeft = LM(hold[1], hold[2]);
       
   768                     int newTop = LM(hold[3], hold[4]);
       
   769                     int newWidth = LM(hold[5], hold[6]);
       
   770                     int newHeight = LM(hold[7], hold[8]);
       
   771 
       
   772                     if (imageWidth/10 > qMax(newWidth,200))
       
   773                         imageWidth = -1;
       
   774                     if (imageHeight/10 > qMax(newHeight,200))
       
   775                         imageHeight = -1;
       
   776 
       
   777                     if (imageWidth <= 0)
       
   778                         imageWidth = newLeft + newWidth;
       
   779                     if (imageHeight <= 0)
       
   780                         imageHeight = newTop + newHeight;
       
   781 
       
   782                     *imageSizes << QSize(imageWidth, imageHeight);
       
   783 
       
   784                     localColormap = !!(hold[9] & 0x80);
       
   785                     localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
       
   786                     if (localColorCount)
       
   787                         colorCount = localColorCount;
       
   788                     else
       
   789                         colorCount = globalColorCount;
       
   790 
       
   791                     count = 0;
       
   792                     if (localColormap) {
       
   793                         int colorTableSize = 3 * localColorCount;
       
   794                         if (length >= colorTableSize) {
       
   795                             // skip the local color table in one go
       
   796                             length -= colorTableSize;
       
   797                             buffer += colorTableSize;
       
   798                             state = TableImageLZWSize;
       
   799                         } else {
       
   800                             colorReadCount = 0;
       
   801                             state = LocalColorMap;
       
   802                         }
       
   803                     } else {
       
   804                         state = TableImageLZWSize;
       
   805                     }
       
   806                 }
       
   807                 break;
       
   808             case TableImageLZWSize:
       
   809                 if (ch > max_lzw_bits)
       
   810                     state = Error;
       
   811                 else
       
   812                     state = ImageDataBlockSize;
       
   813                 count = 0;
       
   814                 break;
       
   815             case ImageDataBlockSize:
       
   816                 blockSize = ch;
       
   817                 if (blockSize) {
       
   818                     if (length >= blockSize) {
       
   819                         // we can skip the block in one go
       
   820                         length -= blockSize;
       
   821                         buffer += blockSize;
       
   822                         count = 0;
       
   823                     } else {
       
   824                         state = ImageDataBlock;
       
   825                     }
       
   826                 } else {
       
   827                     state = Introducer;
       
   828                 }
       
   829                 break;
       
   830             case ImageDataBlock:
       
   831                 ++count;
       
   832                 if (count == blockSize) {
       
   833                     count = 0;
       
   834                     state = ImageDataBlockSize;
       
   835                 }
       
   836                 break;
       
   837             case ExtensionLabel:
       
   838                 switch (ch) {
       
   839                 case 0xf9:
       
   840                     state = GraphicControlExtension;
       
   841                     break;
       
   842                 case 0xff:
       
   843                     state = ApplicationExtension;
       
   844                     break;
       
   845                 default:
       
   846                     state = SkipBlockSize;
       
   847                 }
       
   848                 count = 0;
       
   849                 break;
       
   850             case ApplicationExtension:
       
   851                 if (count < 11)
       
   852                     hold[count] = ch;
       
   853                 ++count;
       
   854                 if (count == hold[0] + 1) {
       
   855                     if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0)
       
   856                         state=NetscapeExtensionBlockSize;
       
   857                     else
       
   858                         state=SkipBlockSize;
       
   859                     count = 0;
       
   860                 }
       
   861                 break;
       
   862             case GraphicControlExtension:
       
   863                 if (count < 5)
       
   864                     hold[count] = ch;
       
   865                 ++count;
       
   866                 if (count == hold[0] + 1) {
       
   867                     count = 0;
       
   868                     state = SkipBlockSize;
       
   869                 }
       
   870                 break;
       
   871             case NetscapeExtensionBlockSize:
       
   872                 blockSize = ch;
       
   873                 count = 0;
       
   874                 if (blockSize)
       
   875                     state = NetscapeExtensionBlock;
       
   876                 else
       
   877                     state = Introducer;
       
   878                 break;
       
   879             case NetscapeExtensionBlock:
       
   880                 if (count < 3)
       
   881                     hold[count] = ch;
       
   882                 count++;
       
   883                 if (count == blockSize) {
       
   884                     *loopCount = LM(hold[1], hold[2]);
       
   885                     state = SkipBlockSize;
       
   886                 }
       
   887                 break;
       
   888             case SkipBlockSize:
       
   889                 blockSize = ch;
       
   890                 count = 0;
       
   891                 if (blockSize) {
       
   892                     if (length >= blockSize) {
       
   893                         // we can skip the block in one go
       
   894                         length -= blockSize;
       
   895                         buffer += blockSize;
       
   896                     } else {
       
   897                         state = SkipBlock;
       
   898                     }
       
   899                 } else {
       
   900                     state = Introducer;
       
   901                 }
       
   902                 break;
       
   903             case SkipBlock:
       
   904                 ++count;
       
   905                 if (count == blockSize)
       
   906                     state = SkipBlockSize;
       
   907                 break;
       
   908             case Done:
       
   909                 done = true;
       
   910                 break;
       
   911             case Error:
       
   912                 device->seek(oldPos);
       
   913                 return;
       
   914             }
       
   915         }
       
   916         readBuffer = device->read(readBufferSize);
       
   917     }
       
   918     device->seek(oldPos);
       
   919     return;
       
   920 }
       
   921 
       
   922 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
       
   923 {
       
   924     if (w>0) {
       
   925         for (int j=0; j<h; j++) {
       
   926             QRgb *line = (QRgb*)image->scanLine(j+row);
       
   927             for (int i=0; i<w; i++)
       
   928                 *(line+col+i) = color;
       
   929         }
       
   930     }
       
   931 }
       
   932 
       
   933 void QGIFFormat::nextY(unsigned char *bits, int bpl)
       
   934 {
       
   935     int my;
       
   936     switch (interlace) {
       
   937     case 0: // Non-interlaced
       
   938         // if (!out_of_bounds) {
       
   939         //     ### Changed: QRect(left, y, right - left + 1, 1);
       
   940         // }
       
   941         y++;
       
   942         break;
       
   943     case 1: {
       
   944         int i;
       
   945         my = qMin(7, bottom-y);
       
   946         // Don't dup with transparency
       
   947         if (trans_index < 0) {
       
   948             for (i=1; i<=my; i++) {
       
   949                 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
       
   950                        (right-left+1)*sizeof(QRgb));
       
   951             }
       
   952         }
       
   953 
       
   954         // if (!out_of_bounds) {
       
   955         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
       
   956         // }
       
   957 //        if (!out_of_bounds)
       
   958 //            qDebug("consumer->changed(QRect(%d, %d, %d, %d))", left, y, right-left+1, my+1);
       
   959         y+=8;
       
   960         if (y>bottom) {
       
   961             interlace++; y=top+4;
       
   962             if (y > bottom) { // for really broken GIFs with bottom < 5
       
   963                 interlace=2;
       
   964                 y = top + 2;
       
   965                 if (y > bottom) { // for really broken GIF with bottom < 3
       
   966                     interlace = 0;
       
   967                     y = top + 1;
       
   968                 }
       
   969             }
       
   970         }
       
   971     } break;
       
   972     case 2: {
       
   973         int i;
       
   974         my = qMin(3, bottom-y);
       
   975         // Don't dup with transparency
       
   976         if (trans_index < 0) {
       
   977             for (i=1; i<=my; i++) {
       
   978                 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
       
   979                        (right-left+1)*sizeof(QRgb));
       
   980             }
       
   981         }
       
   982 
       
   983         // if (!out_of_bounds) {
       
   984         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
       
   985         // }
       
   986         y+=8;
       
   987         if (y>bottom) {
       
   988             interlace++; y=top+2;
       
   989             // handle broken GIF with bottom < 3
       
   990             if (y > bottom) {
       
   991                 interlace = 3;
       
   992                 y = top + 1;
       
   993             }
       
   994         }
       
   995     } break;
       
   996     case 3: {
       
   997         int i;
       
   998         my = qMin(1, bottom-y);
       
   999         // Don't dup with transparency
       
  1000         if (trans_index < 0) {
       
  1001             for (i=1; i<=my; i++) {
       
  1002                 memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
       
  1003                        (right-left+1)*sizeof(QRgb));
       
  1004             }
       
  1005         }
       
  1006         // if (!out_of_bounds) {
       
  1007         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
       
  1008         // }
       
  1009         y+=4;
       
  1010         if (y>bottom) { interlace++; y=top+1; }
       
  1011     } break;
       
  1012     case 4:
       
  1013         // if (!out_of_bounds) {
       
  1014         //     ### Changed: QRect(left, y, right - left + 1, 1);
       
  1015         // }
       
  1016         y+=2;
       
  1017     }
       
  1018 
       
  1019     // Consume bogus extra lines
       
  1020     if (y >= sheight) out_of_bounds=true; //y=bottom;
       
  1021 }
       
  1022 
       
  1023 inline QRgb QGIFFormat::color(uchar index) const
       
  1024 {
       
  1025     if (index == trans_index || index > ncols)
       
  1026         return Q_TRANSPARENT;
       
  1027 
       
  1028     QRgb *map = lcmap ? localcmap : globalcmap;
       
  1029     return map ? map[index] : 0;
       
  1030 }
       
  1031 
       
  1032 //-------------------------------------------------------------------------
       
  1033 //-------------------------------------------------------------------------
       
  1034 //-------------------------------------------------------------------------
       
  1035 
       
  1036 QGifHandler::QGifHandler()
       
  1037 {
       
  1038     gifFormat = new QGIFFormat;
       
  1039     nextDelay = 100;
       
  1040     loopCnt = 1;
       
  1041     frameNumber = -1;
       
  1042     scanIsCached = false;
       
  1043 }
       
  1044 
       
  1045 QGifHandler::~QGifHandler()
       
  1046 {
       
  1047     delete gifFormat;
       
  1048 }
       
  1049 
       
  1050 // Does partial decode if necessary, just to see if an image is coming
       
  1051 
       
  1052 bool QGifHandler::imageIsComing() const
       
  1053 {
       
  1054     const int GifChunkSize = 4096;
       
  1055 
       
  1056     while (!gifFormat->partialNewFrame) {
       
  1057         if (buffer.isEmpty()) {
       
  1058             buffer += device()->read(GifChunkSize);
       
  1059             if (buffer.isEmpty())
       
  1060                 break;
       
  1061         }
       
  1062 
       
  1063         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
       
  1064                                         &nextDelay, &loopCnt);
       
  1065         if (decoded == -1)
       
  1066             break;
       
  1067         buffer.remove(0, decoded);
       
  1068     }
       
  1069     return gifFormat->partialNewFrame;
       
  1070 }
       
  1071 
       
  1072 bool QGifHandler::canRead() const
       
  1073 {
       
  1074     if (canRead(device()) || imageIsComing()) {
       
  1075         setFormat("gif");
       
  1076         return true;
       
  1077     }
       
  1078 
       
  1079     return false;
       
  1080 }
       
  1081 
       
  1082 bool QGifHandler::canRead(QIODevice *device)
       
  1083 {
       
  1084     if (!device) {
       
  1085         qWarning("QGifHandler::canRead() called with no device");
       
  1086         return false;
       
  1087     }
       
  1088 
       
  1089     char head[6];
       
  1090     if (device->peek(head, sizeof(head)) == sizeof(head))
       
  1091         return qstrncmp(head, "GIF87a", 6) == 0
       
  1092             || qstrncmp(head, "GIF89a", 6) == 0;
       
  1093     return false;
       
  1094 }
       
  1095 
       
  1096 bool QGifHandler::read(QImage *image)
       
  1097 {
       
  1098     const int GifChunkSize = 4096;
       
  1099 
       
  1100     while (!gifFormat->newFrame) {
       
  1101         if (buffer.isEmpty()) {
       
  1102             buffer += device()->read(GifChunkSize);
       
  1103             if (buffer.isEmpty())
       
  1104                 break;
       
  1105         }
       
  1106 
       
  1107         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
       
  1108                                         &nextDelay, &loopCnt);
       
  1109         if (decoded == -1)
       
  1110             break;
       
  1111         buffer.remove(0, decoded);
       
  1112     }
       
  1113     if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
       
  1114         *image = lastImage;
       
  1115         ++frameNumber;
       
  1116         gifFormat->newFrame = false;
       
  1117         gifFormat->partialNewFrame = false;
       
  1118         return true;
       
  1119     }
       
  1120 
       
  1121     return false;
       
  1122 }
       
  1123 
       
  1124 bool QGifHandler::write(const QImage &image)
       
  1125 {
       
  1126     Q_UNUSED(image);
       
  1127     return false;
       
  1128 }
       
  1129 
       
  1130 bool QGifHandler::supportsOption(ImageOption option) const
       
  1131 {
       
  1132     if (!device() || device()->isSequential())
       
  1133         return option == Animation;
       
  1134     else
       
  1135         return option == Size
       
  1136             || option == Animation;
       
  1137 }
       
  1138 
       
  1139 QVariant QGifHandler::option(ImageOption option) const
       
  1140 {
       
  1141     if (option == Size) {
       
  1142         if (!scanIsCached) {
       
  1143             QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1144             scanIsCached = true;
       
  1145         }
       
  1146         // before the first frame is read, or we have an empty data stream
       
  1147         if (frameNumber == -1)
       
  1148             return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant();
       
  1149         // after the last frame has been read, the next size is undefined
       
  1150         if (frameNumber >= imageSizes.count() - 1)
       
  1151             return QVariant();
       
  1152         // and the last case: the size of the next frame
       
  1153         return imageSizes.at(frameNumber + 1);
       
  1154     } else if (option == Animation) {
       
  1155         return true;
       
  1156     }
       
  1157     return QVariant();
       
  1158 }
       
  1159 
       
  1160 void QGifHandler::setOption(ImageOption option, const QVariant &value)
       
  1161 {
       
  1162     Q_UNUSED(option);
       
  1163     Q_UNUSED(value);
       
  1164 }
       
  1165 
       
  1166 int QGifHandler::nextImageDelay() const
       
  1167 {
       
  1168     return nextDelay;
       
  1169 }
       
  1170 
       
  1171 int QGifHandler::imageCount() const
       
  1172 {
       
  1173     if (!scanIsCached) {
       
  1174         QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1175         scanIsCached = true;
       
  1176     }
       
  1177     return imageSizes.count();
       
  1178 }
       
  1179 
       
  1180 int QGifHandler::loopCount() const
       
  1181 {
       
  1182     if (!scanIsCached) {
       
  1183         QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1184         scanIsCached = true;
       
  1185     }
       
  1186     return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
       
  1187 }
       
  1188 
       
  1189 int QGifHandler::currentImageNumber() const
       
  1190 {
       
  1191     return frameNumber;
       
  1192 }
       
  1193 
       
  1194 QByteArray QGifHandler::name() const
       
  1195 {
       
  1196     return "gif";
       
  1197 }
       
  1198 
       
  1199 QT_END_NAMESPACE