source: trunk/src/gui/image/qgifhandler.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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