001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.img.gif;
020
021import java.awt.AlphaComposite;
022import java.awt.Color;
023import java.awt.Dimension;
024import java.awt.Graphics2D;
025import java.awt.Rectangle;
026import java.awt.image.BufferedImage;
027import java.awt.image.DataBufferInt;
028import java.io.BufferedInputStream;
029import java.io.FileInputStream;
030import java.io.IOException;
031import java.io.InputStream;
032import java.net.URL;
033import java.util.ArrayList;
034
035public class GifDecoder {
036
037        /**
038         * File read status: No errors.
039         */
040        public static final int STATUS_OK = 0;
041
042        /**
043         * File read status: Error decoding file (may be partially decoded)
044         */
045        public static final int STATUS_FORMAT_ERROR = 1;
046
047        /**
048         * File read status: Unable to open source.
049         */
050        public static final int STATUS_OPEN_ERROR = 2;
051
052        protected BufferedInputStream in;
053        protected int status;
054
055        protected int width; // full image width
056        protected int height; // full image height
057        protected boolean gctFlag; // global color table used
058        protected int gctSize; // size of global color table
059        protected int loopCount = 1; // iterations; 0 = repeat forever
060
061        protected int[] gct; // global color table
062        protected int[] lct; // local color table
063        protected int[] act; // active color table
064
065        protected int bgIndex; // background color index
066        protected int bgColor; // background color
067        protected int lastBgColor; // previous bg color
068        protected int pixelAspect; // pixel aspect ratio
069
070        protected boolean lctFlag; // local color table flag
071        protected boolean interlace; // interlace flag
072        protected int lctSize; // local color table size
073
074        protected int ix, iy, iw, ih; // current image rectangle
075        protected Rectangle lastRect; // last image rect
076        protected BufferedImage image; // current frame
077        protected BufferedImage lastImage; // previous frame
078
079        protected byte[] block = new byte[256]; // current data block
080        protected int blockSize = 0; // block size
081
082        // last graphic control extension info
083        protected int dispose = 0;
084        // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
085        protected int lastDispose = 0;
086        protected boolean transparency = false; // use transparent color
087        protected int delay = 0; // delay in milliseconds
088        protected int transIndex; // transparent color index
089
090        protected static final int MaxStackSize = 4096;
091        // max decoder pixel stack size
092
093        // LZW decoder working arrays
094        protected short[] prefix;
095        protected byte[] suffix;
096        protected byte[] pixelStack;
097        protected byte[] pixels;
098
099        protected ArrayList frames; // frames read from current file
100        protected int frameCount;
101
102        static class GifFrame {
103                public GifFrame(BufferedImage im, int del) {
104                        image = im;
105                        delay = del;
106                }
107                public BufferedImage image;
108                public int delay;
109        }
110
111        /**
112         * Gets display duration for specified frame.
113         *
114         * @param n int index of frame
115         * @return delay in milliseconds
116         */
117        public int getDelay(int n) {
118                //
119                delay = -1;
120                if ((n >= 0) && (n < frameCount)) {
121                        delay = ((GifFrame) frames.get(n)).delay;
122                }
123                return delay;
124        }
125
126        /**
127         * Gets the number of frames read from file.
128         * @return frame count
129         */
130        public int getFrameCount() {
131                return frameCount;
132        }
133
134        /**
135         * Gets the first (or only) image read.
136         *
137         * @return BufferedImage containing first frame, or null if none.
138         */
139        public BufferedImage getImage() {
140                return getFrame(0);
141        }
142
143        /**
144         * Gets the "Netscape" iteration count, if any.
145         * A count of 0 means repeat indefinitiely.
146         *
147         * @return iteration count if one was specified, else 1.
148         */
149        public int getLoopCount() {
150                return loopCount;
151        }
152
153        /**
154         * Creates new frame image from current data (and previous
155         * frames as specified by their disposition codes).
156         */
157        protected void setPixels() {
158                // expose destination image's pixels as int array
159                int[] dest =
160                        ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
161
162                // fill in starting image contents based on last image's dispose code
163                if (lastDispose > 0) {
164                        if (lastDispose == 3) {
165                                // use image before last
166                                int n = frameCount - 2;
167                                if (n > 0) {
168                                        lastImage = getFrame(n - 1);
169                                } else {
170                                        lastImage = null;
171                                }
172                        }
173
174                        if (lastImage != null) {
175                                int[] prev =
176                                        ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
177                                System.arraycopy(prev, 0, dest, 0, width * height);
178                                // copy pixels
179
180                                if (lastDispose == 2) {
181                                        // fill last image rect area with background color
182                                        Graphics2D g = image.createGraphics();
183                                        Color c = null;
184                                        if (transparency) {
185                                                c = new Color(0, 0, 0, 0);      // assume background is transparent
186                                        } else {
187                                                c = new Color(lastBgColor); // use given background color
188                                        }
189                                        g.setColor(c);
190                                        g.setComposite(AlphaComposite.Src); // replace area
191                                        g.fill(lastRect);
192                                        g.dispose();
193                                }
194                        }
195                }
196
197                // copy each source line to the appropriate place in the destination
198                int pass = 1;
199                int inc = 8;
200                int iline = 0;
201                for (int i = 0; i < ih; i++) {
202                        int line = i;
203                        if (interlace) {
204                                if (iline >= ih) {
205                                        pass++;
206                                        switch (pass) {
207                                                case 2 :
208                                                        iline = 4;
209                                                        break;
210                                                case 3 :
211                                                        iline = 2;
212                                                        inc = 4;
213                                                        break;
214                                                case 4 :
215                                                        iline = 1;
216                                                        inc = 2;
217                                        }
218                                }
219                                line = iline;
220                                iline += inc;
221                        }
222                        line += iy;
223                        if (line < height) {
224                                int k = line * width;
225                                int dx = k + ix; // start of line in dest
226                                int dlim = dx + iw; // end of dest line
227                                if ((k + width) < dlim) {
228                                        dlim = k + width; // past dest edge
229                                }
230                                int sx = i * iw; // start of line in source
231                                while (dx < dlim) {
232                                        // map color and insert in destination
233                                        int index = pixels[sx++] & 0xff;
234                                        int c = act[index];
235                                        if (c != 0) {
236                                                dest[dx] = c;
237                                        }
238                                        dx++;
239                                }
240                        }
241                }
242        }
243
244        /**
245         * Gets the image contents of frame n.
246         *
247         * @return BufferedImage representation of frame, or null if n is invalid.
248         */
249        public BufferedImage getFrame(int n) {
250                BufferedImage im = null;
251                if ((n >= 0) && (n < frameCount)) {
252                        im = ((GifFrame) frames.get(n)).image;
253                }
254                return im;
255        }
256
257        /**
258         * Gets image size.
259         *
260         * @return GIF image dimensions
261         */
262        public Dimension getFrameSize() {
263                return new Dimension(width, height);
264        }
265
266        /**
267         * Reads GIF image from stream
268         *
269         * @param BufferedInputStream containing GIF file.
270         * @return read status code (0 = no errors)
271         */
272        public int read(BufferedInputStream is) {
273                init();
274                if (is != null) {
275                        in = is;
276                        readHeader();
277                        if (!err()) {
278                                readContents();
279                                if (frameCount < 0) {
280                                        status = STATUS_FORMAT_ERROR;
281                                }
282                        }
283                } else {
284                        status = STATUS_OPEN_ERROR;
285                }
286                try {
287                        is.close();
288                } catch (IOException e) {
289                }
290                return status;
291        }
292
293        /**
294         * Reads GIF image from stream
295         *
296         * @param InputStream containing GIF file.
297         * @return read status code (0 = no errors)
298         */
299        public int read(InputStream is) {
300                init();
301                if (is != null) {
302                        if (!(is instanceof BufferedInputStream))
303                                is = new BufferedInputStream(is);
304                        in = (BufferedInputStream) is;
305                        readHeader();
306                        if (!err()) {
307                                readContents();
308                                if (frameCount < 0) {
309                                        status = STATUS_FORMAT_ERROR;
310                                }
311                        }
312                } else {
313                        status = STATUS_OPEN_ERROR;
314                }
315                try {
316                        is.close();
317                } catch (IOException e) {
318                }
319                return status;
320        }
321
322        /**
323         * Reads GIF file from specified file/URL source  
324         * (URL assumed if name contains ":/" or "file:")
325         *
326         * @param name String containing source
327         * @return read status code (0 = no errors)
328         */
329        public int read(String name) {
330                status = STATUS_OK;
331                try {
332                        name = name.trim().toLowerCase();
333                        if ((name.indexOf("file:") >= 0) ||
334                                (name.indexOf(":/") > 0)) {
335                                URL url = new URL(name);
336                                in = new BufferedInputStream(url.openStream());
337                        } else {
338                                in = new BufferedInputStream(new FileInputStream(name));
339                        }
340                        status = read(in);
341                } catch (IOException e) {
342                        status = STATUS_OPEN_ERROR;
343                }
344
345                return status;
346        }
347
348        /**
349         * Decodes LZW image data into pixel array.
350         * Adapted from John Cristy's ImageMagick.
351         */
352        protected void decodeImageData() {
353                int NullCode = -1;
354                int npix = iw * ih;
355                int available, 
356                        clear,
357                        code_mask,
358                        code_size,
359                        end_of_information,
360                        in_code,
361                        old_code,
362                        bits,
363                        code,
364                        count,
365                        i,
366                        datum,
367                        data_size,
368                        first,
369                        top,
370                        bi,
371                        pi;
372
373                if ((pixels == null) || (pixels.length < npix)) {
374                        pixels = new byte[npix]; // allocate new pixel array
375                }
376                if (prefix == null) prefix = new short[MaxStackSize];
377                if (suffix == null) suffix = new byte[MaxStackSize];
378                if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
379
380                //  Initialize GIF data stream decoder.
381
382                data_size = read();
383                clear = 1 << data_size;
384                end_of_information = clear + 1;
385                available = clear + 2;
386                old_code = NullCode;
387                code_size = data_size + 1;
388                code_mask = (1 << code_size) - 1;
389                for (code = 0; code < clear; code++) {
390                        prefix[code] = 0;
391                        suffix[code] = (byte) code;
392                }
393
394                //  Decode GIF pixel stream.
395
396                datum = bits = count = first = top = pi = bi = 0;
397
398                for (i = 0; i < npix;) {
399                        if (top == 0) {
400                                if (bits < code_size) {
401                                        //  Load bytes until there are enough bits for a code.
402                                        if (count == 0) {
403                                                // Read a new data block.
404                                                count = readBlock();
405                                                if (count <= 0)
406                                                        break;
407                                                bi = 0;
408                                        }
409                                        datum += (block[bi] & 0xff) << bits;
410                                        bits += 8;
411                                        bi++;
412                                        count--;
413                                        continue;
414                                }
415
416                                //  Get the next code.
417
418                                code = datum & code_mask;
419                                datum >>= code_size;
420                                bits -= code_size;
421
422                                //  Interpret the code
423
424                                if ((code > available) || (code == end_of_information))
425                                        break;
426                                if (code == clear) {
427                                        //  Reset decoder.
428                                        code_size = data_size + 1;
429                                        code_mask = (1 << code_size) - 1;
430                                        available = clear + 2;
431                                        old_code = NullCode;
432                                        continue;
433                                }
434                                if (old_code == NullCode) {
435                                        pixelStack[top++] = suffix[code];
436                                        old_code = code;
437                                        first = code;
438                                        continue;
439                                }
440                                in_code = code;
441                                if (code == available) {
442                                        pixelStack[top++] = (byte) first;
443                                        code = old_code;
444                                }
445                                while (code > clear) {
446                                        pixelStack[top++] = suffix[code];
447                                        code = prefix[code];
448                                }
449                                first = suffix[code] & 0xff;
450
451                                //  Add a new string to the string table,
452
453                                if (available >= MaxStackSize)
454                                        break;
455                                pixelStack[top++] = (byte) first;
456                                prefix[available] = (short) old_code;
457                                suffix[available] = (byte) first;
458                                available++;
459                                if (((available & code_mask) == 0)
460                                        && (available < MaxStackSize)) {
461                                        code_size++;
462                                        code_mask += available;
463                                }
464                                old_code = in_code;
465                        }
466
467                        //  Pop a pixel off the pixel stack.
468
469                        top--;
470                        pixels[pi++] = pixelStack[top];
471                        i++;
472                }
473
474                for (i = pi; i < npix; i++) {
475                        pixels[i] = 0; // clear missing pixels
476                }
477
478        }
479
480        /**
481         * Returns true if an error was encountered during reading/decoding
482         */
483        protected boolean err() {
484                return status != STATUS_OK;
485        }
486
487        /**
488         * Initializes or re-initializes reader
489         */
490        protected void init() {
491                status = STATUS_OK;
492                frameCount = 0;
493                frames = new ArrayList();
494                gct = null;
495                lct = null;
496        }
497
498        /**
499         * Reads a single byte from the input stream.
500         */
501        protected int read() {
502                int curByte = 0;
503                try {
504                        curByte = in.read();
505                } catch (IOException e) {
506                        status = STATUS_FORMAT_ERROR;
507                }
508                return curByte;
509        }
510
511        /**
512         * Reads next variable length block from input.
513         *
514         * @return number of bytes stored in "buffer"
515         */
516        protected int readBlock() {
517                blockSize = read();
518                int n = 0;
519                if (blockSize > 0) {
520                        try {
521                                int count = 0;
522                                while (n < blockSize) {
523                                        count = in.read(block, n, blockSize - n);
524                                        if (count == -1) 
525                                                break;
526                                        n += count;
527                                }
528                        } catch (IOException e) {
529                        }
530
531                        if (n < blockSize) {
532                                status = STATUS_FORMAT_ERROR;
533                        }
534                }
535                return n;
536        }
537
538        /**
539         * Reads color table as 256 RGB integer values
540         *
541         * @param ncolors int number of colors to read
542         * @return int array containing 256 colors (packed ARGB with full alpha)
543         */
544        protected int[] readColorTable(int ncolors) {
545                int nbytes = 3 * ncolors;
546                int[] tab = null;
547                byte[] c = new byte[nbytes];
548                int n = 0;
549                try {
550                        n = in.read(c);
551                } catch (IOException e) {
552                }
553                if (n < nbytes) {
554                        status = STATUS_FORMAT_ERROR;
555                } else {
556                        tab = new int[256]; // max size to avoid bounds checks
557                        int i = 0;
558                        int j = 0;
559                        while (i < ncolors) {
560                                int r = c[j++] & 0xff;
561                                int g = c[j++] & 0xff;
562                                int b = c[j++] & 0xff;
563                                tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
564                        }
565                }
566                return tab;
567        }
568
569        /**
570         * Main file parser.  Reads GIF content blocks.
571         */
572        protected void readContents() {
573                // read GIF file content blocks
574                boolean done = false;
575                while (!(done || err())) {
576                        int code = read();
577                        switch (code) {
578
579                                case 0x2C : // image separator
580                                        readImage();
581                                        break;
582
583                                case 0x21 : // extension
584                                        code = read();
585                                        switch (code) {
586                                                case 0xf9 : // graphics control extension
587                                                        readGraphicControlExt();
588                                                        break;
589
590                                                case 0xff : // application extension
591                                                        readBlock();
592                                                        String app = "";
593                                                        for (int i = 0; i < 11; i++) {
594                                                                app += (char) block[i];
595                                                        }
596                                                        if (app.equals("NETSCAPE2.0")) {
597                                                                readNetscapeExt();
598                                                        }
599                                                        else
600                                                                skip(); // don't care
601                                                        break;
602
603                                                default : // uninteresting extension
604                                                        skip();
605                                        }
606                                        break;
607
608                                case 0x3b : // terminator
609                                        done = true;
610                                        break;
611
612                                case 0x00 : // bad byte, but keep going and see what happens
613                                        break;
614
615                                default :
616                                        status = STATUS_FORMAT_ERROR;
617                        }
618                }
619        }
620
621        /**
622         * Reads Graphics Control Extension values
623         */
624        protected void readGraphicControlExt() {
625                read(); // block size
626                int packed = read(); // packed fields
627                dispose = (packed & 0x1c) >> 2; // disposal method
628                if (dispose == 0) {
629                        dispose = 1; // elect to keep old image if discretionary
630                }
631                transparency = (packed & 1) != 0;
632                delay = readShort() * 10; // delay in milliseconds
633                transIndex = read(); // transparent color index
634                read(); // block terminator
635        }
636
637        /**
638         * Reads GIF file header information.
639         */
640        protected void readHeader() {
641                String id = "";
642                for (int i = 0; i < 6; i++) {
643                        id += (char) read();
644                }
645                if (!id.startsWith("GIF")) {
646                        status = STATUS_FORMAT_ERROR;
647                        return;
648                }
649
650                readLSD();
651                if (gctFlag && !err()) {
652                        gct = readColorTable(gctSize);
653                        bgColor = gct[bgIndex];
654                }
655        }
656
657        /**
658         * Reads next frame image
659         */
660        protected void readImage() {
661                ix = readShort(); // (sub)image position & size
662                iy = readShort();
663                iw = readShort();
664                ih = readShort();
665
666                int packed = read();
667                lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
668                interlace = (packed & 0x40) != 0; // 2 - interlace flag
669                // 3 - sort flag
670                // 4-5 - reserved
671                lctSize = 2 << (packed & 7); // 6-8 - local color table size
672
673                if (lctFlag) {
674                        lct = readColorTable(lctSize); // read table
675                        act = lct; // make local table active
676                } else {
677                        act = gct; // make global table active
678                        if (bgIndex == transIndex)
679                                bgColor = 0;
680                }
681                int save = 0;
682                if (transparency) {
683                        save = act[transIndex];
684                        act[transIndex] = 0; // set transparent color if specified
685                }
686
687                if (act == null) {
688                        status = STATUS_FORMAT_ERROR; // no color table defined
689                }
690
691                if (err()) return;
692
693                decodeImageData(); // decode pixel data
694                skip();
695
696                if (err()) return;
697
698                frameCount++;
699
700                // create new image to receive frame data
701                image =
702                        new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
703
704                setPixels(); // transfer pixel data to image
705
706                frames.add(new GifFrame(image, delay)); // add image to frame list
707
708                if (transparency) {
709                        act[transIndex] = save;
710                }
711                resetFrame();
712
713        }
714
715        /**
716         * Reads Logical Screen Descriptor
717         */
718        protected void readLSD() {
719
720                // logical screen size
721                width = readShort();
722                height = readShort();
723
724                // packed fields
725                int packed = read();
726                gctFlag = (packed & 0x80) != 0; // 1   : global color table flag
727                // 2-4 : color resolution
728                // 5   : gct sort flag
729                gctSize = 2 << (packed & 7); // 6-8 : gct size
730
731                bgIndex = read(); // background color index
732                pixelAspect = read(); // pixel aspect ratio
733        }
734
735        /**
736         * Reads Netscape extenstion to obtain iteration count
737         */
738        protected void readNetscapeExt() {
739                do {
740                        readBlock();
741                        if (block[0] == 1) {
742                                // loop count sub-block
743                                int b1 = block[1] & 0xff;
744                                int b2 = block[2] & 0xff;
745                                loopCount = (b2 << 8) | b1;
746                        }
747                } while ((blockSize > 0) && !err());
748        }
749
750        /**
751         * Reads next 16-bit value, LSB first
752         */
753        protected int readShort() {
754                // read 16-bit value, LSB first
755                return read() | (read() << 8);
756        }
757
758        /**
759         * Resets frame state for reading next image.
760         */
761        protected void resetFrame() {
762                lastDispose = dispose;
763                lastRect = new Rectangle(ix, iy, iw, ih);
764                lastImage = image;
765                lastBgColor = bgColor;
766                //int dispose = 0;
767                //boolean transparency = false;
768                //int delay = 0;
769                lct = null;
770        }
771
772        /**
773         * Skips variable length blocks up to and including
774         * next zero length block.
775         */
776        protected void skip() {
777                do {
778                        readBlock();
779                } while ((blockSize > 0) && !err());
780        }
781}