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}