src/plugins/imageformats/gif/qgifhandler.cpp
branchRCL_3
changeset 8 3f74d0d4af4c
parent 4 3b1da2848fc7
equal deleted inserted replaced
6:dee5afe5301f 8:3f74d0d4af4c
    69 public:
    69 public:
    70     QGIFFormat();
    70     QGIFFormat();
    71     ~QGIFFormat();
    71     ~QGIFFormat();
    72 
    72 
    73     int decode(QImage *image, const uchar* buffer, int length,
    73     int decode(QImage *image, const uchar* buffer, int length,
    74                int *nextFrameDelay, int *loopCount, QSize *nextSize);
    74                int *nextFrameDelay, int *loopCount);
       
    75     static void scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount);
    75 
    76 
    76     bool newFrame;
    77     bool newFrame;
    77     bool partialNewFrame;
    78     bool partialNewFrame;
    78 
    79 
    79 private:
    80 private:
   227     This function decodes some data into image changes.
   228     This function decodes some data into image changes.
   228 
   229 
   229     Returns the number of bytes consumed.
   230     Returns the number of bytes consumed.
   230 */
   231 */
   231 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
   232 int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
   232                        int *nextFrameDelay, int *loopCount, QSize *nextSize)
   233                        int *nextFrameDelay, int *loopCount)
   233 {
   234 {
   234     // We are required to state that
   235     // We are required to state that
   235     //    "The Graphics Interchange Format(c) is the Copyright property of
   236     //    "The Graphics Interchange Format(c) is the Copyright property of
   236     //    CompuServe Incorporated. GIF(sm) is a Service Mark property of
   237     //    CompuServe Incorporated. GIF(sm) is a Service Mark property of
   237     //    CompuServe Incorporated."
   238     //    CompuServe Incorporated."
   344                 if (image->isNull()) {
   345                 if (image->isNull()) {
   345                     (*image) = QImage(swidth, sheight, format);
   346                     (*image) = QImage(swidth, sheight, format);
   346                     bpl = image->bytesPerLine();
   347                     bpl = image->bytesPerLine();
   347                     bits = image->bits();
   348                     bits = image->bits();
   348                     memset(bits, 0, image->byteCount());
   349                     memset(bits, 0, image->byteCount());
   349 
       
   350                     // ### size of the upcoming frame, should rather
       
   351                     // be known before decoding it.
       
   352                     *nextSize = QSize(swidth, sheight);
       
   353                 }
   350                 }
   354 
   351 
   355                 disposePrevious(image);
   352                 disposePrevious(image);
   356                 disposed = false;
   353                 disposed = false;
   357 
   354 
   643         }
   640         }
   644     }
   641     }
   645     return initial-length;
   642     return initial-length;
   646 }
   643 }
   647 
   644 
       
   645 /*!
       
   646    Scans through the data stream defined by \a device and returns the image
       
   647    sizes found in the stream in the \a imageSizes vector.
       
   648 */
       
   649 void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount)
       
   650 {
       
   651     if (!device)
       
   652         return;
       
   653 
       
   654     qint64 oldPos = device->pos();
       
   655     if (!device->seek(0))
       
   656         return;
       
   657 
       
   658     int colorCount = 0;
       
   659     int localColorCount = 0;
       
   660     int globalColorCount = 0;
       
   661     int colorReadCount = 0;
       
   662     bool localColormap = false;
       
   663     bool globalColormap = false;
       
   664     int count = 0;
       
   665     int blockSize = 0;
       
   666     int imageWidth = 0;
       
   667     int imageHeight = 0;
       
   668     bool done = false;
       
   669     uchar hold[16];
       
   670     State state = Header;
       
   671 
       
   672     const int readBufferSize = 40960; // 40k read buffer
       
   673     QByteArray readBuffer(device->read(readBufferSize));
       
   674 
       
   675     if (readBuffer.isEmpty()) {
       
   676         device->seek(oldPos);
       
   677         return;
       
   678     }
       
   679 
       
   680     // This is a specialized version of the state machine from decode(),
       
   681     // which doesn't do any image decoding or mallocing, and has an
       
   682     // optimized way of skipping SkipBlocks, ImageDataBlocks and
       
   683     // Global/LocalColorMaps.
       
   684 
       
   685     while (!readBuffer.isEmpty()) {
       
   686         int length = readBuffer.size();
       
   687         const uchar *buffer = (const uchar *) readBuffer.constData();
       
   688         while (!done && length) {
       
   689             length--;
       
   690             uchar ch = *buffer++;
       
   691             switch (state) {
       
   692             case Header:
       
   693                 hold[count++] = ch;
       
   694                 if (count == 6) {
       
   695                     state = LogicalScreenDescriptor;
       
   696                     count = 0;
       
   697                 }
       
   698                 break;
       
   699             case LogicalScreenDescriptor:
       
   700                 hold[count++] = ch;
       
   701                 if (count == 7) {
       
   702                     imageWidth = LM(hold[0], hold[1]);
       
   703                     imageHeight = LM(hold[2], hold[3]);
       
   704                     globalColormap = !!(hold[4] & 0x80);
       
   705                     globalColorCount = 2 << (hold[4] & 0x7);
       
   706                     count = 0;
       
   707                     colorCount = globalColorCount;
       
   708                     if (globalColormap) {
       
   709                         int colorTableSize = 3 * globalColorCount;
       
   710                         if (length >= colorTableSize) {
       
   711                             // skip the global color table in one go
       
   712                             length -= colorTableSize;
       
   713                             buffer += colorTableSize;
       
   714                             state = Introducer;
       
   715                         } else {
       
   716                             colorReadCount = 0;
       
   717                             state = GlobalColorMap;
       
   718                         }
       
   719                     } else {
       
   720                         state=Introducer;
       
   721                     }
       
   722                 }
       
   723                 break;
       
   724             case GlobalColorMap:
       
   725             case LocalColorMap:
       
   726                 hold[count++] = ch;
       
   727                 if (count == 3) {
       
   728                     if (++colorReadCount >= colorCount) {
       
   729                         if (state == LocalColorMap)
       
   730                             state = TableImageLZWSize;
       
   731                         else
       
   732                             state = Introducer;
       
   733                     }
       
   734                     count = 0;
       
   735                 }
       
   736                 break;
       
   737             case Introducer:
       
   738                 hold[count++] = ch;
       
   739                 switch (ch) {
       
   740                 case 0x2c:
       
   741                     state = ImageDescriptor;
       
   742                     break;
       
   743                 case 0x21:
       
   744                     state = ExtensionLabel;
       
   745                     break;
       
   746                 case 0x3b:
       
   747                     state = Done;
       
   748                     break;
       
   749                 default:
       
   750                     done = true;
       
   751                     state = Error;
       
   752                 }
       
   753                 break;
       
   754             case ImageDescriptor:
       
   755                 hold[count++] = ch;
       
   756                 if (count == 10) {
       
   757                     int newLeft = LM(hold[1], hold[2]);
       
   758                     int newTop = LM(hold[3], hold[4]);
       
   759                     int newWidth = LM(hold[5], hold[6]);
       
   760                     int newHeight = LM(hold[7], hold[8]);
       
   761 
       
   762                     if (imageWidth/10 > qMax(newWidth,200))
       
   763                         imageWidth = -1;
       
   764                     if (imageHeight/10 > qMax(newHeight,200))
       
   765                         imageHeight = -1;
       
   766 
       
   767                     if (imageWidth <= 0)
       
   768                         imageWidth = newLeft + newWidth;
       
   769                     if (imageHeight <= 0)
       
   770                         imageHeight = newTop + newHeight;
       
   771 
       
   772                     *imageSizes << QSize(imageWidth, imageHeight);
       
   773 
       
   774                     localColormap = !!(hold[9] & 0x80);
       
   775                     localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
       
   776                     if (localColorCount)
       
   777                         colorCount = localColorCount;
       
   778                     else
       
   779                         colorCount = globalColorCount;
       
   780 
       
   781                     count = 0;
       
   782                     if (localColormap) {
       
   783                         int colorTableSize = 3 * localColorCount;
       
   784                         if (length >= colorTableSize) {
       
   785                             // skip the local color table in one go
       
   786                             length -= colorTableSize;
       
   787                             buffer += colorTableSize;
       
   788                             state = TableImageLZWSize;
       
   789                         } else {
       
   790                             colorReadCount = 0;
       
   791                             state = LocalColorMap;
       
   792                         }
       
   793                     } else {
       
   794                         state = TableImageLZWSize;
       
   795                     }
       
   796                 }
       
   797                 break;
       
   798             case TableImageLZWSize:
       
   799                 if (ch > max_lzw_bits)
       
   800                     state = Error;
       
   801                 else
       
   802                     state = ImageDataBlockSize;
       
   803                 count = 0;
       
   804                 break;
       
   805             case ImageDataBlockSize:
       
   806                 blockSize = ch;
       
   807                 if (blockSize) {
       
   808                     if (length >= blockSize) {
       
   809                         // we can skip the block in one go
       
   810                         length -= blockSize;
       
   811                         buffer += blockSize;
       
   812                         count = 0;
       
   813                     } else {
       
   814                         state = ImageDataBlock;
       
   815                     }
       
   816                 } else {
       
   817                     state = Introducer;
       
   818                 }
       
   819                 break;
       
   820             case ImageDataBlock:
       
   821                 ++count;
       
   822                 if (count == blockSize) {
       
   823                     count = 0;
       
   824                     state = ImageDataBlockSize;
       
   825                 }
       
   826                 break;
       
   827             case ExtensionLabel:
       
   828                 switch (ch) {
       
   829                 case 0xf9:
       
   830                     state = GraphicControlExtension;
       
   831                     break;
       
   832                 case 0xff:
       
   833                     state = ApplicationExtension;
       
   834                     break;
       
   835                 default:
       
   836                     state = SkipBlockSize;
       
   837                 }
       
   838                 count = 0;
       
   839                 break;
       
   840             case ApplicationExtension:
       
   841                 if (count < 11)
       
   842                     hold[count] = ch;
       
   843                 ++count;
       
   844                 if (count == hold[0] + 1) {
       
   845                     if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0)
       
   846                         state=NetscapeExtensionBlockSize;
       
   847                     else
       
   848                         state=SkipBlockSize;
       
   849                     count = 0;
       
   850                 }
       
   851                 break;
       
   852             case GraphicControlExtension:
       
   853                 if (count < 5)
       
   854                     hold[count] = ch;
       
   855                 ++count;
       
   856                 if (count == hold[0] + 1) {
       
   857                     count = 0;
       
   858                     state = SkipBlockSize;
       
   859                 }
       
   860                 break;
       
   861             case NetscapeExtensionBlockSize:
       
   862                 blockSize = ch;
       
   863                 count = 0;
       
   864                 if (blockSize)
       
   865                     state = NetscapeExtensionBlock;
       
   866                 else
       
   867                     state = Introducer;
       
   868                 break;
       
   869             case NetscapeExtensionBlock:
       
   870                 if (count < 3)
       
   871                     hold[count] = ch;
       
   872                 count++;
       
   873                 if (count == blockSize) {
       
   874                     *loopCount = LM(hold[1], hold[2]);
       
   875                     state = SkipBlockSize;
       
   876                 }
       
   877                 break;
       
   878             case SkipBlockSize:
       
   879                 blockSize = ch;
       
   880                 count = 0;
       
   881                 if (blockSize) {
       
   882                     if (length >= blockSize) {
       
   883                         // we can skip the block in one go
       
   884                         length -= blockSize;
       
   885                         buffer += blockSize;
       
   886                     } else {
       
   887                         state = SkipBlock;
       
   888                     }
       
   889                 } else {
       
   890                     state = Introducer;
       
   891                 }
       
   892                 break;
       
   893             case SkipBlock:
       
   894                 ++count;
       
   895                 if (count == blockSize)
       
   896                     state = SkipBlockSize;
       
   897                 break;
       
   898             case Done:
       
   899                 done = true;
       
   900                 break;
       
   901             case Error:
       
   902                 device->seek(oldPos);
       
   903                 return;
       
   904             }
       
   905         }
       
   906         readBuffer = device->read(readBufferSize);
       
   907     }
       
   908     device->seek(oldPos);
       
   909     return;
       
   910 }
       
   911 
   648 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
   912 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
   649 {
   913 {
   650     if (w>0) {
   914     if (w>0) {
   651         for (int j=0; j<h; j++) {
   915         for (int j=0; j<h; j++) {
   652             QRgb *line = (QRgb*)image->scanLine(j+row);
   916             QRgb *line = (QRgb*)image->scanLine(j+row);
   761 
  1025 
   762 QGifHandler::QGifHandler()
  1026 QGifHandler::QGifHandler()
   763 {
  1027 {
   764     gifFormat = new QGIFFormat;
  1028     gifFormat = new QGIFFormat;
   765     nextDelay = 0;
  1029     nextDelay = 0;
   766     loopCnt = 0;
  1030     loopCnt = 1;
   767     frameNumber = -1;
  1031     frameNumber = -1;
   768     nextSize = QSize();
  1032     scanIsCached = false;
   769 }
  1033 }
   770 
  1034 
   771 QGifHandler::~QGifHandler()
  1035 QGifHandler::~QGifHandler()
   772 {
  1036 {
   773     delete gifFormat;
  1037     delete gifFormat;
   785             if (buffer.isEmpty())
  1049             if (buffer.isEmpty())
   786                 break;
  1050                 break;
   787         }
  1051         }
   788 
  1052 
   789         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
  1053         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
   790                                         &nextDelay, &loopCnt, &nextSize);
  1054                                         &nextDelay, &loopCnt);
   791         if (decoded == -1)
  1055         if (decoded == -1)
   792             break;
  1056             break;
   793         buffer.remove(0, decoded);
  1057         buffer.remove(0, decoded);
   794     }
  1058     }
   795     return gifFormat->partialNewFrame;
  1059     return gifFormat->partialNewFrame;
   829             if (buffer.isEmpty())
  1093             if (buffer.isEmpty())
   830                 break;
  1094                 break;
   831         }
  1095         }
   832 
  1096 
   833         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
  1097         int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
   834                                         &nextDelay, &loopCnt, &nextSize);
  1098                                         &nextDelay, &loopCnt);
   835         if (decoded == -1)
  1099         if (decoded == -1)
   836             break;
  1100             break;
   837         buffer.remove(0, decoded);
  1101         buffer.remove(0, decoded);
   838     }
  1102     }
   839     if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
  1103     if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
   860 }
  1124 }
   861 
  1125 
   862 QVariant QGifHandler::option(ImageOption option) const
  1126 QVariant QGifHandler::option(ImageOption option) const
   863 {
  1127 {
   864     if (option == Size) {
  1128     if (option == Size) {
   865         if (imageIsComing())
  1129         if (!scanIsCached) {
   866             return nextSize;
  1130             QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1131             scanIsCached = true;
       
  1132         }
       
  1133         // before the first frame is read, or we have an empty data stream
       
  1134         if (frameNumber == -1)
       
  1135             return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant();
       
  1136         // after the last frame has been read, the next size is undefined
       
  1137         if (frameNumber >= imageSizes.count() - 1)
       
  1138             return QVariant();
       
  1139         // and the last case: the size of the next frame
       
  1140         return imageSizes.at(frameNumber + 1);
   867     } else if (option == Animation) {
  1141     } else if (option == Animation) {
   868         return true;
  1142         return true;
   869     }
  1143     }
   870     return QVariant();
  1144     return QVariant();
   871 }
  1145 }
   881     return nextDelay;
  1155     return nextDelay;
   882 }
  1156 }
   883 
  1157 
   884 int QGifHandler::imageCount() const
  1158 int QGifHandler::imageCount() const
   885 {
  1159 {
   886     return 0; // Don't know
  1160     if (!scanIsCached) {
       
  1161         QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1162         scanIsCached = true;
       
  1163     }
       
  1164     return imageSizes.count();
   887 }
  1165 }
   888 
  1166 
   889 int QGifHandler::loopCount() const
  1167 int QGifHandler::loopCount() const
   890 {
  1168 {
       
  1169     if (!scanIsCached) {
       
  1170         QGIFFormat::scan(device(), &imageSizes, &loopCnt);
       
  1171         scanIsCached = true;
       
  1172     }
   891     return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
  1173     return loopCnt-1; // In GIF, loop count is iteration count, so subtract one
   892 }
  1174 }
   893 
  1175 
   894 int QGifHandler::currentImageNumber() const
  1176 int QGifHandler::currentImageNumber() const
   895 {
  1177 {