Qt 4.8
qgifhandler.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 
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;
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
88  unsigned char hold[16];
89  bool gif89;
90  int count;
91  int ccount;
93  enum State {
112  } state;
113  int gncols;
114  int lncols;
115  int ncols;
116  int lzwsize;
117  bool lcmap;
119  int width, height;
123  bool disposed;
125  bool gcmap;
126  int bgcol;
128  int accum;
129  int bitcount;
130 
131  enum { max_lzw_bits=12 }; // (poor-compiler's static const int)
132 
135  short* table[2];
136  short* stack;
137  short *sp;
138  bool needfirst;
139  int x, y;
140  int frame;
142  bool digress;
143  void nextY(unsigned char *bits, int bpl);
144  void disposePrevious(QImage *image);
145 };
146 
151 {
152  globalcmap = 0;
153  localcmap = 0;
154  lncols = 0;
155  gncols = 0;
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 
174 {
175  if (globalcmap) delete[] globalcmap;
176  if (localcmap) delete[] localcmap;
177  delete [] stack;
178 }
179 
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 
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');
267  count=0;
268  }
269  break;
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;
289  globalcmap = new QRgb[gncols+1]; // +1 for trans_index
291  } else {
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)
309  else
311  }
312  count=0;
313  }
314  break;
315  case Introducer:
316  hold[count++]=ch;
317  switch (ch) {
318  case ',':
320  break;
321  case '!':
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 
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];
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
411  qMax(backingstore.height(), h),
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;
427  } else {
429  }
430  x = left;
431  y = top;
432  accum = 0;
433  bitcount = 0;
434  sp = stack;
435  firstcode = oldcode = 0;
436  needfirst = true;
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;
450  int i;
451  for (i=0; i<clear_code; i++) {
452  table[0][i]=0;
453  table[1][i]=i;
454  }
456  }
457  count=0;
458  break;
459  } case ImageDataBlockSize:
460  expectcount=ch;
461  if (expectcount) {
463  } else {
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);
476  accum>>=code_size;
477 
478  if (code==clear_code) {
479  if (!needfirst) {
480  code_size=lzwsize+1;
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 && ((frame == 0) || (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;
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  if (code >= max_code) {
509  state = Error;
510  return -1;
511  }
512  *sp++=table[1][code];
513  if (code==table[0][code]) {
514  state=Error;
515  return -1;
516  }
517  if (sp-stack>=(1<<(max_lzw_bits))*2) {
518  state=Error;
519  return -1;
520  }
521  code=table[0][code];
522  }
523  if (code < 0) {
524  state = Error;
525  return -1;
526  }
527 
528  *sp++=firstcode=table[1][code];
529  code=max_code;
530  if (code<(1<<max_lzw_bits)) {
531  table[0][code]=oldcode;
532  table[1][code]=firstcode;
533  max_code++;
534  if ((max_code>=max_code_size)
535  && (max_code_size<(1<<max_lzw_bits)))
536  {
537  max_code_size*=2;
538  code_size++;
539  }
540  }
541  oldcode=incode;
542  const int h = image->height();
543  QRgb *line = 0;
544  if (!out_of_bounds && h > y)
545  line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
546  while (sp>stack) {
547  const uchar index = *(--sp);
548  if (!out_of_bounds && h > y && ((frame == 0) || (index != trans_index))) {
549  line[x] = color(index);
550  }
551  x++;
552  if (x>=swidth) out_of_bounds = true;
553  if (x>=left+width) {
554  x=left;
556  nextY(bits, bpl);
557  if (!out_of_bounds && h > y)
558  line = (QRgb*)FAST_SCAN_LINE(bits, bpl, y);
559  }
560  }
561  }
562  }
563  }
564  partialNewFrame = true;
565  if (count==expectcount) {
566  count=0;
568  }
569  break;
570  case ExtensionLabel:
571  switch (ch) {
572  case 0xf9:
574  break;
575  case 0xff:
577  break;
578 #if 0
579  case 0xfe:
580  state=CommentExtension;
581  break;
582  case 0x01:
583  break;
584 #endif
585  default:
587  }
588  count=0;
589  break;
591  if (count<11) hold[count]=ch;
592  count++;
593  if (count==hold[0]+1) {
594  if (qstrncmp((char*)(hold+1), "NETSCAPE", 8)==0) {
595  // Looping extension
597  } else {
599  }
600  count=0;
601  }
602  break;
604  expectcount=ch;
605  count=0;
607  else state=Introducer;
608  break;
610  if (count<3) hold[count]=ch;
611  count++;
612  if (count==expectcount) {
613  *loopCount = hold[1]+hold[2]*256;
614  state=SkipBlockSize; // Ignore further blocks
615  }
616  break;
618  if (count<5) hold[count]=ch;
619  count++;
620  if (count==hold[0]+1) {
621  disposePrevious(image);
622  disposal=Disposal((hold[1]>>2)&0x7);
623  //UNUSED: waitforuser=!!((hold[1]>>1)&0x1);
624  int delay=count>3 ? LM(hold[2], hold[3]) : 1;
625  // IE and mozilla use a minimum delay of 10. With the minimum delay of 10
626  // we are compatible to them and avoid huge loads on the app and xserver.
627  *nextFrameDelay = (delay < 2 ? 10 : delay) * 10;
628 
629  bool havetrans=hold[1]&0x1;
630  trans_index = havetrans ? hold[4] : -1;
631 
632  count=0;
634  }
635  break;
636  case SkipBlockSize:
637  expectcount=ch;
638  count=0;
640  else state=Introducer;
641  break;
642  case SkipBlock:
643  count++;
645  break;
646  case Done:
647  digress=true;
648  /* Netscape ignores the junk, so we do too.
649  length++; // Unget
650  state=Error; // More calls to this is an error
651  */
652  break;
653  case Error:
654  return -1; // Called again after done.
655  }
656  }
657  return initial-length;
658 }
659 
664 void QGIFFormat::scan(QIODevice *device, QVector<QSize> *imageSizes, int *loopCount)
665 {
666  if (!device)
667  return;
668 
669  qint64 oldPos = device->pos();
670  if (!device->seek(0))
671  return;
672 
673  int colorCount = 0;
674  int localColorCount = 0;
675  int globalColorCount = 0;
676  int colorReadCount = 0;
677  bool localColormap = false;
678  bool globalColormap = false;
679  int count = 0;
680  int blockSize = 0;
681  int imageWidth = 0;
682  int imageHeight = 0;
683  bool done = false;
684  uchar hold[16];
685  State state = Header;
686 
687  const int readBufferSize = 40960; // 40k read buffer
688  QByteArray readBuffer(device->read(readBufferSize));
689 
690  if (readBuffer.isEmpty()) {
691  device->seek(oldPos);
692  return;
693  }
694 
695  // This is a specialized version of the state machine from decode(),
696  // which doesn't do any image decoding or mallocing, and has an
697  // optimized way of skipping SkipBlocks, ImageDataBlocks and
698  // Global/LocalColorMaps.
699 
700  while (!readBuffer.isEmpty()) {
701  int length = readBuffer.size();
702  const uchar *buffer = (const uchar *) readBuffer.constData();
703  while (!done && length) {
704  length--;
705  uchar ch = *buffer++;
706  switch (state) {
707  case Header:
708  hold[count++] = ch;
709  if (count == 6) {
710  state = LogicalScreenDescriptor;
711  count = 0;
712  }
713  break;
715  hold[count++] = ch;
716  if (count == 7) {
717  imageWidth = LM(hold[0], hold[1]);
718  imageHeight = LM(hold[2], hold[3]);
719  globalColormap = !!(hold[4] & 0x80);
720  globalColorCount = 2 << (hold[4] & 0x7);
721  count = 0;
722  colorCount = globalColorCount;
723  if (globalColormap) {
724  int colorTableSize = 3 * globalColorCount;
725  if (length >= colorTableSize) {
726  // skip the global color table in one go
727  length -= colorTableSize;
728  buffer += colorTableSize;
729  state = Introducer;
730  } else {
731  colorReadCount = 0;
732  state = GlobalColorMap;
733  }
734  } else {
735  state=Introducer;
736  }
737  }
738  break;
739  case GlobalColorMap:
740  case LocalColorMap:
741  hold[count++] = ch;
742  if (count == 3) {
743  if (++colorReadCount >= colorCount) {
744  if (state == LocalColorMap)
745  state = TableImageLZWSize;
746  else
747  state = Introducer;
748  }
749  count = 0;
750  }
751  break;
752  case Introducer:
753  hold[count++] = ch;
754  switch (ch) {
755  case 0x2c:
756  state = ImageDescriptor;
757  break;
758  case 0x21:
759  state = ExtensionLabel;
760  break;
761  case 0x3b:
762  state = Done;
763  break;
764  default:
765  done = true;
766  state = Error;
767  }
768  break;
769  case ImageDescriptor:
770  hold[count++] = ch;
771  if (count == 10) {
772  int newLeft = LM(hold[1], hold[2]);
773  int newTop = LM(hold[3], hold[4]);
774  int newWidth = LM(hold[5], hold[6]);
775  int newHeight = LM(hold[7], hold[8]);
776 
777  if (imageWidth/10 > qMax(newWidth,200))
778  imageWidth = -1;
779  if (imageHeight/10 > qMax(newHeight,200))
780  imageHeight = -1;
781 
782  if (imageWidth <= 0)
783  imageWidth = newLeft + newWidth;
784  if (imageHeight <= 0)
785  imageHeight = newTop + newHeight;
786 
787  *imageSizes << QSize(imageWidth, imageHeight);
788 
789  localColormap = !!(hold[9] & 0x80);
790  localColorCount = localColormap ? (2 << (hold[9] & 0x7)) : 0;
791  if (localColorCount)
792  colorCount = localColorCount;
793  else
794  colorCount = globalColorCount;
795 
796  count = 0;
797  if (localColormap) {
798  int colorTableSize = 3 * localColorCount;
799  if (length >= colorTableSize) {
800  // skip the local color table in one go
801  length -= colorTableSize;
802  buffer += colorTableSize;
803  state = TableImageLZWSize;
804  } else {
805  colorReadCount = 0;
806  state = LocalColorMap;
807  }
808  } else {
809  state = TableImageLZWSize;
810  }
811  }
812  break;
813  case TableImageLZWSize:
814  if (ch > max_lzw_bits)
815  state = Error;
816  else
817  state = ImageDataBlockSize;
818  count = 0;
819  break;
820  case ImageDataBlockSize:
821  blockSize = ch;
822  if (blockSize) {
823  if (length >= blockSize) {
824  // we can skip the block in one go
825  length -= blockSize;
826  buffer += blockSize;
827  count = 0;
828  } else {
829  state = ImageDataBlock;
830  }
831  } else {
832  state = Introducer;
833  }
834  break;
835  case ImageDataBlock:
836  ++count;
837  if (count == blockSize) {
838  count = 0;
839  state = ImageDataBlockSize;
840  }
841  break;
842  case ExtensionLabel:
843  switch (ch) {
844  case 0xf9:
845  state = GraphicControlExtension;
846  break;
847  case 0xff:
848  state = ApplicationExtension;
849  break;
850  default:
851  state = SkipBlockSize;
852  }
853  count = 0;
854  break;
856  if (count < 11)
857  hold[count] = ch;
858  ++count;
859  if (count == hold[0] + 1) {
860  if (qstrncmp((char*)(hold+1), "NETSCAPE", 8) == 0)
862  else
863  state=SkipBlockSize;
864  count = 0;
865  }
866  break;
868  if (count < 5)
869  hold[count] = ch;
870  ++count;
871  if (count == hold[0] + 1) {
872  count = 0;
873  state = SkipBlockSize;
874  }
875  break;
877  blockSize = ch;
878  count = 0;
879  if (blockSize)
880  state = NetscapeExtensionBlock;
881  else
882  state = Introducer;
883  break;
885  if (count < 3)
886  hold[count] = ch;
887  count++;
888  if (count == blockSize) {
889  *loopCount = LM(hold[1], hold[2]);
890  state = SkipBlockSize;
891  }
892  break;
893  case SkipBlockSize:
894  blockSize = ch;
895  count = 0;
896  if (blockSize) {
897  if (length >= blockSize) {
898  // we can skip the block in one go
899  length -= blockSize;
900  buffer += blockSize;
901  } else {
902  state = SkipBlock;
903  }
904  } else {
905  state = Introducer;
906  }
907  break;
908  case SkipBlock:
909  ++count;
910  if (count == blockSize)
911  state = SkipBlockSize;
912  break;
913  case Done:
914  done = true;
915  break;
916  case Error:
917  device->seek(oldPos);
918  return;
919  }
920  }
921  readBuffer = device->read(readBufferSize);
922  }
923  device->seek(oldPos);
924  return;
925 }
926 
927 void QGIFFormat::fillRect(QImage *image, int col, int row, int w, int h, QRgb color)
928 {
929  if (w>0) {
930  for (int j=0; j<h; j++) {
931  QRgb *line = (QRgb*)image->scanLine(j+row);
932  for (int i=0; i<w; i++)
933  *(line+col+i) = color;
934  }
935  }
936 }
937 
938 void QGIFFormat::nextY(unsigned char *bits, int bpl)
939 {
940  int my;
941  switch (interlace) {
942  case 0: // Non-interlaced
943  // if (!out_of_bounds) {
944  // ### Changed: QRect(left, y, right - left + 1, 1);
945  // }
946  y++;
947  break;
948  case 1: {
949  int i;
950  my = qMin(7, bottom-y);
951  // Don't dup with transparency
952  if (trans_index < 0) {
953  for (i=1; i<=my; i++) {
954  memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
955  (right-left+1)*sizeof(QRgb));
956  }
957  }
958 
959  // if (!out_of_bounds) {
960  // ### Changed: QRect(left, y, right - left + 1, my + 1);
961  // }
962 // if (!out_of_bounds)
963 // qDebug("consumer->changed(QRect(%d, %d, %d, %d))", left, y, right-left+1, my+1);
964  y+=8;
965  if (y>bottom) {
966  interlace++; y=top+4;
967  if (y > bottom) { // for really broken GIFs with bottom < 5
968  interlace=2;
969  y = top + 2;
970  if (y > bottom) { // for really broken GIF with bottom < 3
971  interlace = 0;
972  y = top + 1;
973  }
974  }
975  }
976  } break;
977  case 2: {
978  int i;
979  my = qMin(3, bottom-y);
980  // Don't dup with transparency
981  if (trans_index < 0) {
982  for (i=1; i<=my; i++) {
983  memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
984  (right-left+1)*sizeof(QRgb));
985  }
986  }
987 
988  // if (!out_of_bounds) {
989  // ### Changed: QRect(left, y, right - left + 1, my + 1);
990  // }
991  y+=8;
992  if (y>bottom) {
993  interlace++; y=top+2;
994  // handle broken GIF with bottom < 3
995  if (y > bottom) {
996  interlace = 3;
997  y = top + 1;
998  }
999  }
1000  } break;
1001  case 3: {
1002  int i;
1003  my = qMin(1, bottom-y);
1004  // Don't dup with transparency
1005  if (trans_index < 0) {
1006  for (i=1; i<=my; i++) {
1007  memcpy(FAST_SCAN_LINE(bits, bpl, y+i)+left*sizeof(QRgb), FAST_SCAN_LINE(bits, bpl, y)+left*sizeof(QRgb),
1008  (right-left+1)*sizeof(QRgb));
1009  }
1010  }
1011  // if (!out_of_bounds) {
1012  // ### Changed: QRect(left, y, right - left + 1, my + 1);
1013  // }
1014  y+=4;
1015  if (y>bottom) { interlace++; y=top+1; }
1016  } break;
1017  case 4:
1018  // if (!out_of_bounds) {
1019  // ### Changed: QRect(left, y, right - left + 1, 1);
1020  // }
1021  y+=2;
1022  }
1023 
1024  // Consume bogus extra lines
1025  if (y >= sheight) out_of_bounds=true; //y=bottom;
1026 }
1027 
1029 {
1030  if (index > ncols)
1031  return Q_TRANSPARENT;
1032 
1034  QRgb col = map ? map[index] : 0;
1035  return index == trans_index ? col & Q_TRANSPARENT : col;
1036 }
1037 
1038 //-------------------------------------------------------------------------
1039 //-------------------------------------------------------------------------
1040 //-------------------------------------------------------------------------
1041 
1043 {
1044  gifFormat = new QGIFFormat;
1045  nextDelay = 100;
1046  loopCnt = -1;
1047  frameNumber = -1;
1048  scanIsCached = false;
1049 }
1050 
1052 {
1053  delete gifFormat;
1054 }
1055 
1056 // Does partial decode if necessary, just to see if an image is coming
1057 
1059 {
1060  const int GifChunkSize = 4096;
1061 
1062  while (!gifFormat->partialNewFrame) {
1063  if (buffer.isEmpty()) {
1064  buffer += device()->read(GifChunkSize);
1065  if (buffer.isEmpty())
1066  break;
1067  }
1068 
1069  int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1070  &nextDelay, &loopCnt);
1071  if (decoded == -1)
1072  break;
1073  buffer.remove(0, decoded);
1074  }
1075  return gifFormat->partialNewFrame;
1076 }
1077 
1079 {
1080  if (canRead(device()) || imageIsComing()) {
1081  setFormat("gif");
1082  return true;
1083  }
1084 
1085  return false;
1086 }
1087 
1089 {
1090  if (!device) {
1091  qWarning("QGifHandler::canRead() called with no device");
1092  return false;
1093  }
1094 
1095  char head[6];
1096  if (device->peek(head, sizeof(head)) == sizeof(head))
1097  return qstrncmp(head, "GIF87a", 6) == 0
1098  || qstrncmp(head, "GIF89a", 6) == 0;
1099  return false;
1100 }
1101 
1103 {
1104  const int GifChunkSize = 4096;
1105 
1106  while (!gifFormat->newFrame) {
1107  if (buffer.isEmpty()) {
1108  buffer += device()->read(GifChunkSize);
1109  if (buffer.isEmpty())
1110  break;
1111  }
1112 
1113  int decoded = gifFormat->decode(&lastImage, (const uchar *)buffer.constData(), buffer.size(),
1114  &nextDelay, &loopCnt);
1115  if (decoded == -1)
1116  break;
1117  buffer.remove(0, decoded);
1118  }
1119  if (gifFormat->newFrame || (gifFormat->partialNewFrame && device()->atEnd())) {
1120  *image = lastImage;
1121  ++frameNumber;
1122  gifFormat->newFrame = false;
1123  gifFormat->partialNewFrame = false;
1124  return true;
1125  }
1126 
1127  return false;
1128 }
1129 
1130 bool QGifHandler::write(const QImage &image)
1131 {
1132  Q_UNUSED(image);
1133  return false;
1134 }
1135 
1137 {
1138  if (!device() || device()->isSequential())
1139  return option == Animation;
1140  else
1141  return option == Size
1142  || option == Animation;
1143 }
1144 
1146 {
1147  if (option == Size) {
1148  if (!scanIsCached) {
1149  QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1150  scanIsCached = true;
1151  }
1152  // before the first frame is read, or we have an empty data stream
1153  if (frameNumber == -1)
1154  return (imageSizes.count() > 0) ? QVariant(imageSizes.at(0)) : QVariant();
1155  // after the last frame has been read, the next size is undefined
1156  if (frameNumber >= imageSizes.count() - 1)
1157  return QVariant();
1158  // and the last case: the size of the next frame
1159  return imageSizes.at(frameNumber + 1);
1160  } else if (option == Animation) {
1161  return true;
1162  }
1163  return QVariant();
1164 }
1165 
1166 void QGifHandler::setOption(ImageOption option, const QVariant &value)
1167 {
1168  Q_UNUSED(option);
1169  Q_UNUSED(value);
1170 }
1171 
1173 {
1174  return nextDelay;
1175 }
1176 
1178 {
1179  if (!scanIsCached) {
1180  QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1181  scanIsCached = true;
1182  }
1183  return imageSizes.count();
1184 }
1185 
1187 {
1188  if (!scanIsCached) {
1189  QGIFFormat::scan(device(), &imageSizes, &loopCnt);
1190  scanIsCached = true;
1191  }
1192 
1193  if (loopCnt == 0)
1194  return -1;
1195  else if (loopCnt == -1)
1196  return 0;
1197  else
1198  return loopCnt;
1199 }
1200 
1202 {
1203  return frameNumber;
1204 }
1205 
1207 {
1208  return "gif";
1209 }
1210 
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:92
const int blockSize
bool canRead() const
Returns true if an image can be read from the device (i.
Format
The following image formats are available in Qt.
Definition: qimage.h:91
short * sp
QVariant option(ImageOption option) const
Returns the value assigned to option as a QVariant.
unsigned int QRgb
Definition: qrgb.h:53
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
int currentImageNumber() const
For image formats that support animation, this function returns the sequence number of the current im...
~QGIFFormat()
Destroys a QGIFFormat.
QRgb * globalcmap
Definition: qgifhandler.cpp:85
void setOption(ImageOption option, const QVariant &value)
Sets the option option with the value value.
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition: qimage.cpp:1542
bool read(QImage *image)
Read an image from the device, and stores it in image.
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from...
Definition: qiodevice.cpp:624
#define Q_TRANSPARENT
Definition: qgifhandler.cpp:55
int byteCount() const
Returns the number of bytes occupied by the image data.
Definition: qimage.cpp:1800
int bytesPerLine() const
Returns the number of bytes per image scanline.
Definition: qimage.cpp:1812
void fillRect(QImage *image, int x, int y, int w, int h, QRgb col)
int imageCount() const
For image formats that support animation, this function returns the number of images in the animation...
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
static void scan(QIODevice *device, QVector< QSize > *imageSizes, int *loopCount)
Scans through the data stream defined by device and returns the image sizes found in the stream in th...
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read...
Definition: qiodevice.cpp:791
Disposal disposal
bool write(const QImage &image)
Writes the image image to the assigned device.
unsigned char uchar
Definition: qglobal.h:994
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QRgb color(uchar index) const
ImageOption
This enum describes the different options supported by QImageIOHandler.
bool supportsOption(ImageOption option) const
Returns true if the QImageIOHandler supports the option option; otherwise returns false...
qint64 peek(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, without side effects (i.
Definition: qiodevice.cpp:1563
void detach()
If multiple images share common data, this image makes a copy of the data and detaches itself from th...
Definition: qimage.cpp:1359
Q_CORE_EXPORT void qWarning(const char *,...)
The QImage class provides a hardware-independent image representation that allows direct access to th...
Definition: qimage.h:87
__int64 qint64
Definition: qglobal.h:942
The State element defines configurations of objects and properties.
unsigned char hold[16]
Definition: qgifhandler.cpp:88
QRgb * localcmap
Definition: qgifhandler.cpp:86
#define rgb(r, g, b)
Definition: qcolor_p.cpp:130
QByteArray name() const
Use format() instead.
uchar * bits()
Returns a pointer to the first pixel data.
Definition: qimage.cpp:1946
int decode(QImage *image, const uchar *buffer, int length, int *nextFrameDelay, int *loopCount)
This function decodes some data into image changes.
int width() const
Returns the width of the image.
Definition: qimage.cpp:1557
bool out_of_bounds
int loopCount() const
For image formats that support animation, this function returns the number of times the animation sho...
#define LM(l, m)
bool partialNewFrame
Definition: qgifhandler.cpp:78
Q_GUI_EXPORT_INLINE QRgb qRgb(int r, int g, int b)
Definition: qrgb.h:69
int qstrncmp(const char *str1, const char *str2, uint len)
Definition: qbytearray.h:101
QFactoryLoader * l
short * table[2]
int height() const
Returns the height of the image.
Definition: qimage.cpp:1572
The Animation element is the base of all QML animations.
quint16 index
QGIFFormat()
Constructs a QGIFFormat.
int nextImageDelay() const
For image formats that support animation, this function returns the number of milliseconds to wait un...
#define FAST_SCAN_LINE(bits, bpl, y)
Definition: qgifhandler.cpp:58
The QSize class defines the size of a two-dimensional object using integer point precision.
Definition: qsize.h:53
bool imageIsComing() const
enum QGIFFormat::State state
void nextY(unsigned char *bits, int bpl)
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:66
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success...
Definition: qiodevice.cpp:659
short * stack
void disposePrevious(QImage *image)
QImage backingstore
Definition: qgifhandler.cpp:87
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition: qimage.cpp:1886