src/plugins/imageformats/gif/qgifhandler.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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.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 /*
       
    58   Incremental image decoder for GIF image format.
       
    59 
       
    60   This subclass of QImageFormat decodes GIF format images,
       
    61   including animated GIFs. Internally in
       
    62 */
       
    63 
       
    64 class QGIFFormat {
       
    65 public:
       
    66     QGIFFormat();
       
    67     ~QGIFFormat();
       
    68 
       
    69     int decode(QImage *image, const uchar* buffer, int length,
       
    70                int *nextFrameDelay, int *loopCount, QSize *nextSize);
       
    71 
       
    72     bool newFrame;
       
    73     bool partialNewFrame;
       
    74 
       
    75 private:
       
    76     void fillRect(QImage *image, int x, int y, int w, int h, QRgb col);
       
    77     inline QRgb color(uchar index) const;
       
    78 
       
    79     // GIF specific stuff
       
    80     QRgb* globalcmap;
       
    81     QRgb* localcmap;
       
    82     QImage backingstore;
       
    83     unsigned char hold[16];
       
    84     bool gif89;
       
    85     int count;
       
    86     int ccount;
       
    87     int expectcount;
       
    88     enum State {
       
    89         Header,
       
    90         LogicalScreenDescriptor,
       
    91         GlobalColorMap,
       
    92         LocalColorMap,
       
    93         Introducer,
       
    94         ImageDescriptor,
       
    95         TableImageLZWSize,
       
    96         ImageDataBlockSize,
       
    97         ImageDataBlock,
       
    98         ExtensionLabel,
       
    99         GraphicControlExtension,
       
   100         ApplicationExtension,
       
   101         NetscapeExtensionBlockSize,
       
   102         NetscapeExtensionBlock,
       
   103         SkipBlockSize,
       
   104         SkipBlock,
       
   105         Done,
       
   106         Error
       
   107     } state;
       
   108     int gncols;
       
   109     int lncols;
       
   110     int ncols;
       
   111     int lzwsize;
       
   112     bool lcmap;
       
   113     int swidth, sheight;
       
   114     int width, height;
       
   115     int left, top, right, bottom;
       
   116     enum Disposal { NoDisposal, DoNotChange, RestoreBackground, RestoreImage };
       
   117     Disposal disposal;
       
   118     bool disposed;
       
   119     int trans_index;
       
   120     bool gcmap;
       
   121     int bgcol;
       
   122     int interlace;
       
   123     int accum;
       
   124     int bitcount;
       
   125 
       
   126     enum { max_lzw_bits=12 }; // (poor-compiler's static const int)
       
   127 
       
   128     int code_size, clear_code, end_code, max_code_size, max_code;
       
   129     int firstcode, oldcode, incode;
       
   130     short table[2][1<< max_lzw_bits];
       
   131     short stack[(1<<(max_lzw_bits))*2];
       
   132     short *sp;
       
   133     bool needfirst;
       
   134     int x, y;
       
   135     int frame;
       
   136     bool out_of_bounds;
       
   137     bool digress;
       
   138     void nextY(QImage *image);
       
   139     void disposePrevious(QImage *image);
       
   140 };
       
   141 
       
   142 /*!
       
   143     Constructs a QGIFFormat.
       
   144 */
       
   145 QGIFFormat::QGIFFormat()
       
   146 {
       
   147     globalcmap = 0;
       
   148     localcmap = 0;
       
   149     lncols = 0;
       
   150     gncols = 0;
       
   151     disposal = NoDisposal;
       
   152     out_of_bounds = false;
       
   153     disposed = true;
       
   154     frame = -1;
       
   155     state = Header;
       
   156     count = 0;
       
   157     lcmap = false;
       
   158     newFrame = false;
       
   159     partialNewFrame = false;
       
   160 }
       
   161 
       
   162 /*!
       
   163     Destroys a QGIFFormat.
       
   164 */
       
   165 QGIFFormat::~QGIFFormat()
       
   166 {
       
   167     if (globalcmap) delete[] globalcmap;
       
   168     if (localcmap) delete[] localcmap;
       
   169 }
       
   170 
       
   171 void QGIFFormat::disposePrevious(QImage *image)
       
   172 {
       
   173     if (out_of_bounds) {
       
   174         // flush anything that survived
       
   175         // ### Changed: QRect(0, 0, swidth, sheight)
       
   176     }
       
   177 
       
   178     // Handle disposal of previous image before processing next one
       
   179 
       
   180     if (disposed) return;
       
   181 
       
   182     int l = qMin(swidth-1,left);
       
   183     int r = qMin(swidth-1,right);
       
   184     int t = qMin(sheight-1,top);
       
   185     int b = qMin(sheight-1,bottom);
       
   186 
       
   187     switch (disposal) {
       
   188       case NoDisposal:
       
   189         break;
       
   190       case DoNotChange:
       
   191         break;
       
   192       case RestoreBackground:
       
   193         if (trans_index>=0) {
       
   194             // Easy:  we use the transparent color
       
   195             fillRect(image, l, t, r-l+1, b-t+1, Q_TRANSPARENT);
       
   196         } else if (bgcol>=0) {
       
   197             // Easy:  we use the bgcol given
       
   198             fillRect(image, l, t, r-l+1, b-t+1, color(bgcol));
       
   199         } else {
       
   200             // Impossible:  We don't know of a bgcol - use pixel 0
       
   201             QRgb *bits = (QRgb*)image->bits();
       
   202             fillRect(image, l, t, r-l+1, b-t+1, bits[0]);
       
   203         }
       
   204         // ### Changed: QRect(l, t, r-l+1, b-t+1)
       
   205         break;
       
   206       case RestoreImage: {
       
   207         if (frame >= 0) {
       
   208             for (int ln=t; ln<=b; ln++) {
       
   209                 memcpy(image->scanLine(ln)+l,
       
   210                     backingstore.scanLine(ln-t),
       
   211                     (r-l+1)*sizeof(QRgb));
       
   212             }
       
   213             // ### Changed: QRect(l, t, r-l+1, b-t+1)
       
   214         }
       
   215       }
       
   216     }
       
   217     disposal = NoDisposal; // Until an extension says otherwise.
       
   218 
       
   219     disposed = true;
       
   220 }
       
   221 
       
   222 /*!
       
   223     This function decodes some data into image changes.
       
   224 
       
   225     Returns the number of bytes consumed.
       
   226 */
       
   227 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
       
   228                        int *nextFrameDelay, int *loopCount, QSize *nextSize)
       
   229 {
       
   230     // We are required to state that
       
   231     //    "The Graphics Interchange Format(c) is the Copyright property of
       
   232     //    CompuServe Incorporated. GIF(sm) is a Service Mark property of
       
   233     //    CompuServe Incorporated."
       
   234 
       
   235 #define LM(l, m) (((m)<<8)|l)
       
   236     digress = false;
       
   237     const int initial = length;
       
   238     while (!digress && length) {
       
   239         length--;
       
   240         unsigned char ch=*buffer++;
       
   241         switch (state) {
       
   242           case Header:
       
   243             hold[count++]=ch;
       
   244             if (count==6) {
       
   245                 // Header
       
   246                 gif89=(hold[3]!='8' || hold[4]!='7');
       
   247                 state=LogicalScreenDescriptor;
       
   248                 count=0;
       
   249             }
       
   250             break;
       
   251           case LogicalScreenDescriptor:
       
   252             hold[count++]=ch;
       
   253             if (count==7) {
       
   254                 // Logical Screen Descriptor
       
   255                 swidth=LM(hold[0], hold[1]);
       
   256                 sheight=LM(hold[2], hold[3]);
       
   257                 gcmap=!!(hold[4]&0x80);
       
   258                 //UNUSED: bpchan=(((hold[4]&0x70)>>3)+1);
       
   259                 //UNUSED: gcmsortflag=!!(hold[4]&0x08);
       
   260                 gncols=2<<(hold[4]&0x7);
       
   261                 bgcol=(gcmap) ? hold[5] : -1;
       
   262                 //aspect=hold[6] ? double(hold[6]+15)/64.0 : 1.0;
       
   263 
       
   264                 trans_index = -1;
       
   265                 count=0;
       
   266                 ncols=gncols;
       
   267                 if (gcmap) {
       
   268                     ccount=0;
       
   269                     state=GlobalColorMap;
       
   270                     globalcmap = new QRgb[gncols+1]; // +1 for trans_index
       
   271                     globalcmap[gncols] = Q_TRANSPARENT;
       
   272                 } else {
       
   273                     state=Introducer;
       
   274                 }
       
   275             }
       
   276             break;
       
   277           case GlobalColorMap: case LocalColorMap:
       
   278             hold[count++]=ch;
       
   279             if (count==3) {
       
   280                 QRgb rgb = qRgb(hold[0], hold[1], hold[2]);
       
   281                 if (state == LocalColorMap) {
       
   282                     if (ccount < lncols)
       
   283                         localcmap[ccount] =  rgb;
       
   284                 } else {
       
   285                     globalcmap[ccount] = rgb;
       
   286                 }
       
   287                 if (++ccount >= ncols) {
       
   288                     if (state == LocalColorMap)
       
   289                         state=TableImageLZWSize;
       
   290                     else
       
   291                         state=Introducer;
       
   292                 }
       
   293                 count=0;
       
   294             }
       
   295             break;
       
   296           case Introducer:
       
   297             hold[count++]=ch;
       
   298             switch (ch) {
       
   299               case ',':
       
   300                 state=ImageDescriptor;
       
   301                 break;
       
   302               case '!':
       
   303                 state=ExtensionLabel;
       
   304                 break;
       
   305               case ';':
       
   306                   // ### Changed: QRect(0, 0, swidth, sheight)
       
   307                 state=Done;
       
   308                 break;
       
   309               default:
       
   310                 digress=true;
       
   311                 // Unexpected Introducer - ignore block
       
   312                 state=Error;
       
   313             }
       
   314             break;
       
   315           case ImageDescriptor:
       
   316             hold[count++]=ch;
       
   317             if (count==10) {
       
   318                 int newleft=LM(hold[1], hold[2]);
       
   319                 int newtop=LM(hold[3], hold[4]);
       
   320                 int newwidth=LM(hold[5], hold[6]);
       
   321                 int newheight=LM(hold[7], hold[8]);
       
   322 
       
   323                 // disbelieve ridiculous logical screen sizes,
       
   324                 // unless the image frames are also large.
       
   325                 if (swidth/10 > qMax(newwidth,200))
       
   326                     swidth = -1;
       
   327                 if (sheight/10 > qMax(newheight,200))
       
   328                     sheight = -1;
       
   329 
       
   330                 if (swidth <= 0)
       
   331                     swidth = newleft + newwidth;
       
   332                 if (sheight <= 0)
       
   333                     sheight = newtop + newheight;
       
   334 
       
   335                 QImage::Format format = trans_index >= 0 ? QImage::Format_ARGB32 : QImage::Format_RGB32;
       
   336                 if (image->isNull()) {
       
   337                     (*image) = QImage(swidth, sheight, format);
       
   338                     memset(image->bits(), 0, image->numBytes());
       
   339 
       
   340                     // ### size of the upcoming frame, should rather
       
   341                     // be known before decoding it.
       
   342                     *nextSize = QSize(swidth, sheight);
       
   343                 }
       
   344 
       
   345                 disposePrevious(image);
       
   346                 disposed = false;
       
   347 
       
   348                 left = newleft;
       
   349                 top = newtop;
       
   350                 width = newwidth;
       
   351                 height = newheight;
       
   352 
       
   353                 right=qMax(0, qMin(left+width, swidth)-1);
       
   354                 bottom=qMax(0, qMin(top+height, sheight)-1);
       
   355                 lcmap=!!(hold[9]&0x80);
       
   356                 interlace=!!(hold[9]&0x40);
       
   357                 //bool lcmsortflag=!!(hold[9]&0x20);
       
   358                 lncols=lcmap ? (2<<(hold[9]&0x7)) : 0;
       
   359                 if (lncols) {
       
   360                     if (localcmap)
       
   361                         delete [] localcmap;
       
   362                     localcmap = new QRgb[lncols+1];
       
   363                     localcmap[lncols] = Q_TRANSPARENT;
       
   364                     ncols = lncols;
       
   365                 } else {
       
   366                     ncols = gncols;
       
   367                 }
       
   368                 frame++;
       
   369                 if (frame == 0) {
       
   370                     if (left || top || width<swidth || height<sheight) {
       
   371                         // Not full-size image - erase with bg or transparent
       
   372                         if (trans_index >= 0) {
       
   373                             fillRect(image, 0, 0, swidth, sheight, color(trans_index));
       
   374                             // ### Changed: QRect(0, 0, swidth, sheight)
       
   375                         } else if (bgcol>=0) {
       
   376                             fillRect(image, 0, 0, swidth, sheight, color(bgcol));
       
   377                             // ### Changed: QRect(0, 0, swidth, sheight)
       
   378                         }
       
   379                     }
       
   380                 }
       
   381 
       
   382                 if (disposal == RestoreImage) {
       
   383                     int l = qMin(swidth-1,left);
       
   384                     int r = qMin(swidth-1,right);
       
   385                     int t = qMin(sheight-1,top);
       
   386                     int b = qMin(sheight-1,bottom);
       
   387                     int w = r-l+1;
       
   388                     int h = b-t+1;
       
   389 
       
   390                     if (backingstore.width() < w
       
   391                         || backingstore.height() < h) {
       
   392                         // We just use the backing store as a byte array
       
   393                         backingstore = QImage(qMax(backingstore.width(), w),
       
   394                                               qMax(backingstore.height(), h),
       
   395                                               QImage::Format_RGB32);
       
   396                         memset(image->bits(), 0, image->numBytes());
       
   397                     }
       
   398                     for (int ln=0; ln<h; ln++) {
       
   399                         memcpy(backingstore.scanLine(ln),
       
   400                                image->scanLine(t+ln)+l, w*sizeof(QRgb));
       
   401                     }
       
   402                 }
       
   403 
       
   404                 count=0;
       
   405                 if (lcmap) {
       
   406                     ccount=0;
       
   407                     state=LocalColorMap;
       
   408                 } else {
       
   409                     state=TableImageLZWSize;
       
   410                 }
       
   411                 x = left;
       
   412                 y = top;
       
   413                 accum = 0;
       
   414                 bitcount = 0;
       
   415                 sp = stack;
       
   416                 firstcode = oldcode = 0;
       
   417                 needfirst = true;
       
   418                 out_of_bounds = left>=swidth || y>=sheight;
       
   419             }
       
   420             break;
       
   421           case TableImageLZWSize: {
       
   422             lzwsize=ch;
       
   423             if (lzwsize > max_lzw_bits) {
       
   424                 state=Error;
       
   425             } else {
       
   426                 code_size=lzwsize+1;
       
   427                 clear_code=1<<lzwsize;
       
   428                 end_code=clear_code+1;
       
   429                 max_code_size=2*clear_code;
       
   430                 max_code=clear_code+2;
       
   431                 int i;
       
   432                 for (i=0; i<clear_code; i++) {
       
   433                     table[0][i]=0;
       
   434                     table[1][i]=i;
       
   435                 }
       
   436                 state=ImageDataBlockSize;
       
   437             }
       
   438             count=0;
       
   439             break;
       
   440           } case ImageDataBlockSize:
       
   441             expectcount=ch;
       
   442             if (expectcount) {
       
   443                 state=ImageDataBlock;
       
   444             } else {
       
   445                 state=Introducer;
       
   446                 digress = true;
       
   447                 newFrame = true;
       
   448             }
       
   449             break;
       
   450           case ImageDataBlock:
       
   451             count++;
       
   452             accum|=(ch<<bitcount);
       
   453             bitcount+=8;
       
   454             while (bitcount>=code_size && state==ImageDataBlock) {
       
   455                 int code=accum&((1<<code_size)-1);
       
   456                 bitcount-=code_size;
       
   457                 accum>>=code_size;
       
   458 
       
   459                 if (code==clear_code) {
       
   460                     if (!needfirst) {
       
   461                         code_size=lzwsize+1;
       
   462                         max_code_size=2*clear_code;
       
   463                         max_code=clear_code+2;
       
   464                     }
       
   465                     needfirst=true;
       
   466                 } else if (code==end_code) {
       
   467                     bitcount = -32768;
       
   468                     // Left the block end arrive
       
   469                 } else {
       
   470                     if (needfirst) {
       
   471                         firstcode=oldcode=code;
       
   472                         if (!out_of_bounds && image->height() > y && firstcode!=trans_index)
       
   473                             ((QRgb*)image->scanLine(y))[x] = color(firstcode);
       
   474                         x++;
       
   475                         if (x>=swidth) out_of_bounds = true;
       
   476                         needfirst=false;
       
   477                         if (x>=left+width) {
       
   478                             x=left;
       
   479                             out_of_bounds = left>=swidth || y>=sheight;
       
   480                             nextY(image);
       
   481                         }
       
   482                     } else {
       
   483                         incode=code;
       
   484                         if (code>=max_code) {
       
   485                             *sp++=firstcode;
       
   486                             code=oldcode;
       
   487                         }
       
   488                         while (code>=clear_code+2) {
       
   489                             *sp++=table[1][code];
       
   490                             if (code==table[0][code]) {
       
   491                                 state=Error;
       
   492                                 break;
       
   493                             }
       
   494                             if (sp-stack>=(1<<(max_lzw_bits))*2) {
       
   495                                 state=Error;
       
   496                                 break;
       
   497                             }
       
   498                             code=table[0][code];
       
   499                         }
       
   500                         *sp++=firstcode=table[1][code];
       
   501                         code=max_code;
       
   502                         if (code<(1<<max_lzw_bits)) {
       
   503                             table[0][code]=oldcode;
       
   504                             table[1][code]=firstcode;
       
   505                             max_code++;
       
   506                             if ((max_code>=max_code_size)
       
   507                              && (max_code_size<(1<<max_lzw_bits)))
       
   508                             {
       
   509                                 max_code_size*=2;
       
   510                                 code_size++;
       
   511                             }
       
   512                         }
       
   513                         oldcode=incode;
       
   514                         const int h = image->height();
       
   515                         const QRgb *map = lcmap ? localcmap : globalcmap;
       
   516                         QRgb *line = 0;
       
   517                         if (!out_of_bounds && h > y)
       
   518                             line = (QRgb*)image->scanLine(y);
       
   519                         while (sp>stack) {
       
   520                             const uchar index = *(--sp);
       
   521                             if (!out_of_bounds && h > y && index!=trans_index) {
       
   522                                 if (index > ncols)
       
   523                                     line[x] = Q_TRANSPARENT;
       
   524                                 else
       
   525                                     line[x] = map ? map[index] : 0;
       
   526                             }
       
   527                             x++;
       
   528                             if (x>=swidth) out_of_bounds = true;
       
   529                             if (x>=left+width) {
       
   530                                 x=left;
       
   531                                 out_of_bounds = left>=swidth || y>=sheight;
       
   532                                 nextY(image);
       
   533                                 if (!out_of_bounds && h > y)
       
   534                                     line = (QRgb*)image->scanLine(y);
       
   535                             }
       
   536                         }
       
   537                     }
       
   538                 }
       
   539             }
       
   540             partialNewFrame = true;
       
   541             if (count==expectcount) {
       
   542                 count=0;
       
   543                 state=ImageDataBlockSize;
       
   544             }
       
   545             break;
       
   546           case ExtensionLabel:
       
   547             switch (ch) {
       
   548             case 0xf9:
       
   549                 state=GraphicControlExtension;
       
   550                 break;
       
   551             case 0xff:
       
   552                 state=ApplicationExtension;
       
   553                 break;
       
   554 #if 0
       
   555             case 0xfe:
       
   556                 state=CommentExtension;
       
   557                 break;
       
   558             case 0x01:
       
   559                 break;
       
   560 #endif
       
   561             default:
       
   562                 state=SkipBlockSize;
       
   563             }
       
   564             count=0;
       
   565             break;
       
   566           case ApplicationExtension:
       
   567             if (count<11) hold[count]=ch;
       
   568             count++;
       
   569             if (count==hold[0]+1) {
       
   570                 if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) {
       
   571                     // Looping extension
       
   572                     state=NetscapeExtensionBlockSize;
       
   573                 } else {
       
   574                     state=SkipBlockSize;
       
   575                 }
       
   576                 count=0;
       
   577             }
       
   578             break;
       
   579           case NetscapeExtensionBlockSize:
       
   580             expectcount=ch;
       
   581             count=0;
       
   582             if (expectcount) state=NetscapeExtensionBlock;
       
   583             else state=Introducer;
       
   584             break;
       
   585           case NetscapeExtensionBlock:
       
   586             if (count<3) hold[count]=ch;
       
   587             count++;
       
   588             if (count==expectcount) {
       
   589                 *loopCount = hold[1]+hold[2]*256;
       
   590                 state=SkipBlockSize; // Ignore further blocks
       
   591             }
       
   592             break;
       
   593           case GraphicControlExtension:
       
   594             if (count<5) hold[count]=ch;
       
   595             count++;
       
   596             if (count==hold[0]+1) {
       
   597                 disposePrevious(image);
       
   598                 disposal=Disposal((hold[1]>>2)&0x7);
       
   599                 //UNUSED: waitforuser=!!((hold[1]>>1)&0x1);
       
   600                 int delay=count>3 ? LM(hold[2], hold[3]) : 1;
       
   601                 // IE and mozilla use a minimum delay of 10. With the minimum delay of 10
       
   602                 // we are compatible to them and avoid huge loads on the app and xserver.
       
   603                 *nextFrameDelay = (delay < 2 ? 10 : delay) * 10;
       
   604 
       
   605                 bool havetrans=hold[1]&0x1;
       
   606                 trans_index = havetrans ? hold[4] : -1;
       
   607 
       
   608                 count=0;
       
   609                 state=SkipBlockSize;
       
   610             }
       
   611             break;
       
   612           case SkipBlockSize:
       
   613             expectcount=ch;
       
   614             count=0;
       
   615             if (expectcount) state=SkipBlock;
       
   616             else state=Introducer;
       
   617             break;
       
   618           case SkipBlock:
       
   619             count++;
       
   620             if (count==expectcount) state=SkipBlockSize;
       
   621             break;
       
   622           case Done:
       
   623             digress=true;
       
   624             /* Netscape ignores the junk, so we do too.
       
   625             length++; // Unget
       
   626             state=Error; // More calls to this is an error
       
   627             */
       
   628             break;
       
   629           case Error:
       
   630             return -1; // Called again after done.
       
   631         }
       
   632     }
       
   633     return initial-length;
       
   634 }
       
   635 
       
   636 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
       
   637 {
       
   638     if (w>0) {
       
   639         for (int j=0; j<h; j++) {
       
   640             QRgb *line = (QRgb*)image->scanLine(j+row);
       
   641             for (int i=0; i<w; i++)
       
   642                 *(line+col+i) = color;
       
   643         }
       
   644     }
       
   645 }
       
   646 
       
   647 void QGIFFormat::nextY(QImage *image)
       
   648 {
       
   649     int my;
       
   650     switch (interlace) {
       
   651     case 0: // Non-interlaced
       
   652         // if (!out_of_bounds) {
       
   653         //     ### Changed: QRect(left, y, right - left + 1, 1);
       
   654         // }
       
   655         y++;
       
   656         break;
       
   657     case 1: {
       
   658         int i;
       
   659         my = qMin(7, bottom-y);
       
   660         // Don't dup with transparency
       
   661         if (trans_index < 0) {
       
   662             for (i=1; i<=my; i++) {
       
   663                 memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb),
       
   664                        (right-left+1)*sizeof(QRgb));
       
   665             }
       
   666         }
       
   667 
       
   668         // if (!out_of_bounds) {
       
   669         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
       
   670         // }
       
   671 //        if (!out_of_bounds)
       
   672 //            qDebug("consumer->changed(QRect(%d, %d, %d, %d))", left, y, right-left+1, my+1);
       
   673         y+=8;
       
   674         if (y>bottom) {
       
   675             interlace++; y=top+4;
       
   676             if (y > bottom) { // for really broken GIFs with bottom < 5
       
   677                 interlace=2;
       
   678                 y = top + 2;
       
   679                 if (y > bottom) { // for really broken GIF with bottom < 3
       
   680                     interlace = 0;
       
   681                     y = top + 1;
       
   682                 }
       
   683             }
       
   684         }
       
   685     } break;
       
   686     case 2: {
       
   687         int i;
       
   688         my = qMin(3, bottom-y);
       
   689         // Don't dup with transparency
       
   690         if (trans_index < 0) {
       
   691             for (i=1; i<=my; i++) {
       
   692                 memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb),
       
   693                        (right-left+1)*sizeof(QRgb));
       
   694             }
       
   695         }
       
   696 
       
   697         // if (!out_of_bounds) {
       
   698         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
       
   699         // }
       
   700         y+=8;
       
   701         if (y>bottom) {
       
   702             interlace++; y=top+2;
       
   703             // handle broken GIF with bottom < 3
       
   704             if (y > bottom) {
       
   705                 interlace = 3;
       
   706                 y = top + 1;
       
   707             }
       
   708         }
       
   709     } break;
       
   710     case 3: {
       
   711         int i;
       
   712         my = qMin(1, bottom-y);
       
   713         // Don't dup with transparency
       
   714         if (trans_index < 0) {
       
   715             for (i=1; i<=my; i++) {
       
   716                 memcpy(image->scanLine(y+i)+left*sizeof(QRgb), image->scanLine(y)+left*sizeof(QRgb),
       
   717                        (right-left+1)*sizeof(QRgb));
       
   718             }
       
   719         }
       
   720         // if (!out_of_bounds) {
       
   721         //     ### Changed: QRect(left, y, right - left + 1, my + 1);
       
   722         // }
       
   723         y+=4;
       
   724         if (y>bottom) { interlace++; y=top+1; }
       
   725     } break;
       
   726     case 4:
       
   727         // if (!out_of_bounds) {
       
   728         //     ### Changed: QRect(left, y, right - left + 1, 1);
       
   729         // }
       
   730         y+=2;
       
   731     }
       
   732 
       
   733     // Consume bogus extra lines
       
   734     if (y >= sheight) out_of_bounds=true; //y=bottom;
       
   735 }
       
   736 
       
   737 inline QRgb QGIFFormat::color(uchar index) const
       
   738 {
       
   739     if (index == trans_index || index > ncols)
       
   740         return Q_TRANSPARENT;
       
   741 
       
   742     QRgb *map = lcmap ? localcmap : globalcmap;
       
   743     return map ? map[index] : 0;
       
   744 }
       
   745 
       
   746 //-------------------------------------------------------------------------
       
   747 //-------------------------------------------------------------------------
       
   748 //-------------------------------------------------------------------------
       
   749 
       
   750 QGifHandler::QGifHandler()
       
   751 {
       
   752     gifFormat = new QGIFFormat;
       
   753     nextDelay = 0;
       
   754     loopCnt = 0;
       
   755     frameNumber = -1;
       
   756     nextSize = QSize();
       
   757 }
       
   758 
       
   759 QGifHandler::~QGifHandler()
       
   760 {
       
   761     delete gifFormat;
       
   762 }
       
   763 
       
   764 // Does partial decode if necessary, just to see if an image is coming
       
   765 
       
   766 bool QGifHandler::imageIsComing() const
       
   767 {
       
   768     const int GifChunkSize = 4096;
       
   769 
       
   770     while (!gifFormat->partialNewFrame) {
       
   771         if (buffer.isEmpty()) {
       
   772             buffer += device()->read(GifChunkSize);
       
   773             if (buffer.isEmpty())
       
   774                 break;
       
   775         }
       
   776 
       
   777         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
       
   778                                         &nextDelay, &loopCnt, &nextSize);
       
   779         if (decoded == -1)
       
   780             break;
       
   781         buffer.remove(0, decoded);
       
   782     }
       
   783     return gifFormat->partialNewFrame;
       
   784 }
       
   785 
       
   786 bool QGifHandler::canRead() const
       
   787 {
       
   788     if (!nextDelay && canRead(device())) {
       
   789         setFormat("gif");
       
   790         return true;
       
   791     }
       
   792 
       
   793     return imageIsComing();
       
   794 }
       
   795 
       
   796 bool QGifHandler::canRead(QIODevice *device)
       
   797 {
       
   798     if (!device) {
       
   799         qWarning("QGifHandler::canRead() called with no device");
       
   800         return false;
       
   801     }
       
   802 
       
   803     char head[6];
       
   804     if (device->peek(head, sizeof(head)) == sizeof(head))
       
   805         return qstrncmp(head, "GIF87a", 6) == 0
       
   806             || qstrncmp(head, "GIF89a", 6) == 0;
       
   807     return false;
       
   808 }
       
   809 
       
   810 bool QGifHandler::read(QImage *image)
       
   811 {
       
   812     const int GifChunkSize = 4096;
       
   813 
       
   814     while (!gifFormat->newFrame) {
       
   815         if (buffer.isEmpty()) {
       
   816             buffer += device()->read(GifChunkSize);
       
   817             if (buffer.isEmpty())
       
   818                 break;
       
   819         }
       
   820 
       
   821         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
       
   822                                         &nextDelay, &loopCnt, &nextSize);
       
   823         if (decoded == -1)
       
   824             break;
       
   825         buffer.remove(0, decoded);
       
   826     }
       
   827     if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
       
   828         *image = lastImage;
       
   829         ++frameNumber;
       
   830         gifFormat->newFrame = false;
       
   831         gifFormat->partialNewFrame = false;
       
   832         return true;
       
   833     }
       
   834 
       
   835     return false;
       
   836 }
       
   837 
       
   838 bool QGifHandler::write(const QImage &image)
       
   839 {
       
   840     Q_UNUSED(image);
       
   841     return false;
       
   842 }
       
   843 
       
   844 bool QGifHandler::supportsOption(ImageOption option) const
       
   845 {
       
   846     return option == Size
       
   847         || option == Animation;
       
   848 }
       
   849 
       
   850 QVariant QGifHandler::option(ImageOption option) const
       
   851 {
       
   852     if (option == Size) {
       
   853         if (imageIsComing())
       
   854             return nextSize;
       
   855     } else if (option == Animation) {
       
   856         return true;
       
   857     }
       
   858     return QVariant();
       
   859 }
       
   860 
       
   861 void QGifHandler::setOption(ImageOption option, const QVariant &value)
       
   862 {
       
   863     Q_UNUSED(option);
       
   864     Q_UNUSED(value);
       
   865 }
       
   866 
       
   867 int QGifHandler::nextImageDelay() const
       
   868 {
       
   869     return nextDelay;
       
   870 }
       
   871 
       
   872 int QGifHandler::imageCount() const
       
   873 {
       
   874     return 0; // Don't know
       
   875 }
       
   876 
       
   877 int QGifHandler::loopCount() const
       
   878 {
       
   879     return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
       
   880 }
       
   881 
       
   882 int QGifHandler::currentImageNumber() const
       
   883 {
       
   884     return frameNumber;
       
   885 }
       
   886 
       
   887 QByteArray QGifHandler::name() const
       
   888 {
       
   889     return "gif";
       
   890 }
       
   891 
       
   892 QT_END_NAMESPACE