< prev index next >

src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java

Print this page


   1 /*
   2  * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  69     int global_height;
  70     int global_bgpixel;
  71 
  72     GifFrame curframe;
  73 
  74     public GifImageDecoder(InputStreamImageSource src, InputStream is) {
  75         super(src, is);
  76     }
  77 
  78     /**
  79      * An error has occurred. Throw an exception.
  80      */
  81     private static void error(String s1) throws ImageFormatException {
  82         throw new ImageFormatException(s1);
  83     }
  84 
  85     /**
  86      * Read a number of bytes into a buffer.
  87      * @return number of bytes that were not read due to EOF or error
  88      */
  89     private int readBytes(byte buf[], int off, int len) {
  90         while (len > 0) {
  91             try {
  92                 int n = input.read(buf, off, len);
  93                 if (n < 0) {
  94                     break;
  95                 }
  96                 off += n;
  97                 len -= n;
  98             } catch (IOException e) {
  99                 break;
 100             }
 101         }
 102         return len;
 103     }
 104 
 105     private static final int ExtractByte(byte buf[], int off) {
 106         return (buf[off] & 0xFF);
 107     }
 108 
 109     private static final int ExtractWord(byte buf[], int off) {
 110         return (buf[off] & 0xFF) | ((buf[off + 1] & 0xFF) << 8);
 111     }
 112 
 113     /**
 114      * produce an image from the stream.
 115      */
 116     @SuppressWarnings({"fallthrough", "deprecation"})
 117     public void produceImage() throws IOException, ImageFormatException {
 118         try {
 119             readHeader();
 120 
 121             int totalframes = 0;
 122             int frameno = 0;
 123             int nloops = -1;
 124             int disposal_method = 0;
 125             int delay = -1;
 126             boolean loopsRead = false;
 127             boolean isAnimation = false;
 128 
 129             while (!aborted) {
 130                 int code;
 131 
 132                 switch (code = input.read()) {
 133                   case EXBLOCK:
 134                     switch (code = input.read()) {
 135                       case EX_GRAPHICS_CONTROL: {
 136                         byte buf[] = new byte[6];
 137                         if (readBytes(buf, 0, 6) != 0) {
 138                             return;//error("corrupt GIF file");
 139                         }
 140                         if ((buf[0] != 4) || (buf[5] != 0)) {
 141                             return;//error("corrupt GIF file (GCE size)");
 142                         }
 143                         // Get the index of the transparent color
 144                         delay = ExtractWord(buf, 2) * 10;
 145                         if (delay > 0 && !isAnimation) {
 146                             isAnimation = true;
 147                             ImageFetcher.startingAnimation();
 148                         }
 149                         disposal_method = (buf[1] >> 2) & 7;
 150                         if ((buf[1] & TRANSPARENCYMASK) != 0) {
 151                             trans_pixel = ExtractByte(buf, 4);
 152                         } else {
 153                             trans_pixel = -1;
 154                         }
 155                         break;
 156                       }
 157 
 158                       case EX_COMMENT:
 159                       case EX_APPLICATION:
 160                       default:
 161                         boolean loop_tag = false;
 162                         String comment = "";
 163                         while (true) {
 164                             int n = input.read();
 165                             if (n <= 0) {
 166                                 break;
 167                             }
 168                             byte buf[] = new byte[n];
 169                             if (readBytes(buf, 0, n) != 0) {
 170                                 return;//error("corrupt GIF file");
 171                             }
 172                             if (code == EX_COMMENT) {
 173                                 comment += new String(buf, 0);
 174                             } else if (code == EX_APPLICATION) {
 175                                 if (loop_tag) {
 176                                     if (n == 3 && buf[0] == 1) {
 177                                         if (loopsRead) {
 178                                             ExtractWord(buf, 1);
 179                                         }
 180                                         else {
 181                                             nloops = ExtractWord(buf, 1);
 182                                             loopsRead = true;
 183                                         }
 184                                     } else {
 185                                         loop_tag = false;
 186                                     }
 187                                 }
 188                                 if ("NETSCAPE2.0".equals(new String(buf, 0))) {


 258                     }
 259                     if (verbose && frameno != 1) {
 260                         System.out.println("processing GIF terminator,"
 261                                            + " frames: " + frameno
 262                                            + " total: " + totalframes);
 263                     }
 264                     imageComplete(ImageConsumer.STATICIMAGEDONE, true);
 265                     return;
 266                 }
 267             }
 268         } finally {
 269             close();
 270         }
 271     }
 272 
 273     /**
 274      * Read Image header
 275      */
 276     private void readHeader() throws IOException, ImageFormatException {
 277         // Create a buffer
 278         byte buf[] = new byte[13];
 279 
 280         // Read the header
 281         if (readBytes(buf, 0, 13) != 0) {
 282             throw new IOException();
 283         }
 284 
 285         // Check header
 286         if ((buf[0] != 'G') || (buf[1] != 'I') || (buf[2] != 'F')) {
 287             error("not a GIF file.");
 288         }
 289 
 290         // Global width&height
 291         global_width = ExtractWord(buf, 6);
 292         global_height = ExtractWord(buf, 8);
 293 
 294         // colormap info
 295         int ch = ExtractByte(buf, 10);
 296         if ((ch & COLORMAPMASK) == 0) {
 297             // no global colormap so make up our own
 298             // If there is a local colormap, it will override what we


 322                 throw new IOException();
 323             }
 324         }
 325         input.mark(Integer.MAX_VALUE); // set this mark in case this is an animated GIF
 326     }
 327 
 328     /**
 329      * The ImageConsumer hints flag for a non-interlaced GIF image.
 330      */
 331     private static final int normalflags =
 332         ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
 333         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME;
 334 
 335     /**
 336      * The ImageConsumer hints flag for an interlaced GIF image.
 337      */
 338     private static final int interlaceflags =
 339         ImageConsumer.RANDOMPIXELORDER | ImageConsumer.COMPLETESCANLINES |
 340         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME;
 341 
 342     private short prefix[]  = new short[4096];
 343     private byte  suffix[]  = new byte[4096];
 344     private byte  outCode[] = new byte[4097];
 345 
 346     private static native void initIDs();
 347 
 348     static {
 349         /* ensure that the necessary native libraries are loaded */
 350         NativeLibLoader.loadLibraries();
 351         initIDs();
 352     }
 353 
 354     private native boolean parseImage(int x, int y, int width, int height,
 355                                       boolean interlace, int initCodeSize,
 356                                       byte block[], byte rasline[],
 357                                       IndexColorModel model);
 358 
 359     private int sendPixels(int x, int y, int width, int height,
 360                            byte rasline[], ColorModel model) {
 361         int rasbeg, rasend, x2;
 362         if (y < 0) {
 363             height += y;
 364             y = 0;
 365         }
 366         if (y + height > global_height) {
 367             height = global_height - y;
 368         }
 369         if (height <= 0) {
 370             return 1;
 371         }
 372         // rasline[0]     == pixel at coordinate (x,y)
 373         // rasline[width] == pixel at coordinate (x+width, y)
 374         if (x < 0) {
 375             rasbeg = -x;
 376             width += x;         // same as (width -= rasbeg)
 377             x2 = 0;             // same as (x2     = x + rasbeg)
 378         } else {
 379             rasbeg = 0;
 380             // width -= 0;      // same as (width -= rasbeg)


 450     }
 451 
 452     /**
 453      * Read Image data
 454      */
 455     private boolean readImage(boolean first, int disposal_method, int delay)
 456         throws IOException
 457     {
 458         if (curframe != null && !curframe.dispose()) {
 459             abort();
 460             return false;
 461         }
 462 
 463         long tm = 0;
 464 
 465         if (verbose) {
 466             tm = System.currentTimeMillis();
 467         }
 468 
 469         // Allocate the buffer
 470         byte block[] = new byte[256 + 3];
 471 
 472         // Read the image descriptor
 473         if (readBytes(block, 0, 10) != 0) {
 474             throw new IOException();
 475         }
 476         int x = ExtractWord(block, 0);
 477         int y = ExtractWord(block, 2);
 478         int width = ExtractWord(block, 4);
 479         int height = ExtractWord(block, 6);
 480 
 481         /*
 482          * Majority of gif images have
 483          * same logical screen and frame dimensions.
 484          * Also, Photoshop and Mozilla seem to use the logical
 485          * screen dimension (from the global stream header)
 486          * if frame dimension is invalid.
 487          *
 488          * We use similar heuristic and trying to recover
 489          * frame width from logical screen dimension and
 490          * frame offset.


 540         // Notify the consumers
 541         if (first) {
 542             if (global_width == 0) global_width = width;
 543             if (global_height == 0) global_height = height;
 544 
 545             setDimensions(global_width, global_height);
 546             setProperties(props);
 547             setColorModel(model);
 548             headerComplete();
 549         }
 550 
 551         if (disposal_method == GifFrame.DISPOSAL_SAVE && saved_image == null) {
 552             saved_image = new byte[global_width * global_height];
 553             /*
 554              * If height of current image is smaller than the global height,
 555              * fill the gap with transparent pixels.
 556              */
 557             if ((height < global_height) && (model != null)) {
 558                 byte tpix = (byte)model.getTransparentPixel();
 559                 if (tpix >= 0) {
 560                     byte trans_rasline[] = new byte[global_width];
 561                     for (int i=0; i<global_width;i++) {
 562                         trans_rasline[i] = tpix;
 563                     }
 564 
 565                     setPixels(0, 0, global_width, y,
 566                               model, trans_rasline, 0, 0);
 567                     setPixels(0, y+height, global_width,
 568                               global_height-height-y, model, trans_rasline,
 569                               0, 0);
 570                 }
 571             }
 572         }
 573 
 574         int hints = (interlace ? interlaceflags : normalflags);
 575         setHints(hints);
 576 
 577         curframe = new GifFrame(this, disposal_method, delay,
 578                                 (curframe == null), model,
 579                                 x, y, width, height);
 580 
 581         // allocate the raster data
 582         byte rasline[] = new byte[width];
 583 
 584         if (verbose) {
 585             System.out.print("Reading a " + width + " by " + height + " " +
 586                       (interlace ? "" : "non-") + "interlaced image...");
 587         }
 588         int initCodeSize = ExtractByte(block, 9);
 589         if (initCodeSize >= 12) {
 590             if (verbose) {
 591                 System.out.println("Invalid initial code size: " +
 592                                    initCodeSize);
 593             }
 594             return false;
 595         }
 596         boolean ret = parseImage(x, y, width, height,
 597                                  interlace, initCodeSize,
 598                                  block, rasline, model);
 599 
 600         if (!ret) {
 601             abort();
 602         }


   1 /*
   2  * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  69     int global_height;
  70     int global_bgpixel;
  71 
  72     GifFrame curframe;
  73 
  74     public GifImageDecoder(InputStreamImageSource src, InputStream is) {
  75         super(src, is);
  76     }
  77 
  78     /**
  79      * An error has occurred. Throw an exception.
  80      */
  81     private static void error(String s1) throws ImageFormatException {
  82         throw new ImageFormatException(s1);
  83     }
  84 
  85     /**
  86      * Read a number of bytes into a buffer.
  87      * @return number of bytes that were not read due to EOF or error
  88      */
  89     private int readBytes(byte[] buf, int off, int len) {
  90         while (len > 0) {
  91             try {
  92                 int n = input.read(buf, off, len);
  93                 if (n < 0) {
  94                     break;
  95                 }
  96                 off += n;
  97                 len -= n;
  98             } catch (IOException e) {
  99                 break;
 100             }
 101         }
 102         return len;
 103     }
 104 
 105     private static final int ExtractByte(byte[] buf, int off) {
 106         return (buf[off] & 0xFF);
 107     }
 108 
 109     private static final int ExtractWord(byte[] buf, int off) {
 110         return (buf[off] & 0xFF) | ((buf[off + 1] & 0xFF) << 8);
 111     }
 112 
 113     /**
 114      * produce an image from the stream.
 115      */
 116     @SuppressWarnings({"fallthrough", "deprecation"})
 117     public void produceImage() throws IOException, ImageFormatException {
 118         try {
 119             readHeader();
 120 
 121             int totalframes = 0;
 122             int frameno = 0;
 123             int nloops = -1;
 124             int disposal_method = 0;
 125             int delay = -1;
 126             boolean loopsRead = false;
 127             boolean isAnimation = false;
 128 
 129             while (!aborted) {
 130                 int code;
 131 
 132                 switch (code = input.read()) {
 133                   case EXBLOCK:
 134                     switch (code = input.read()) {
 135                       case EX_GRAPHICS_CONTROL: {
 136                         byte[] buf = new byte[6];
 137                         if (readBytes(buf, 0, 6) != 0) {
 138                             return;//error("corrupt GIF file");
 139                         }
 140                         if ((buf[0] != 4) || (buf[5] != 0)) {
 141                             return;//error("corrupt GIF file (GCE size)");
 142                         }
 143                         // Get the index of the transparent color
 144                         delay = ExtractWord(buf, 2) * 10;
 145                         if (delay > 0 && !isAnimation) {
 146                             isAnimation = true;
 147                             ImageFetcher.startingAnimation();
 148                         }
 149                         disposal_method = (buf[1] >> 2) & 7;
 150                         if ((buf[1] & TRANSPARENCYMASK) != 0) {
 151                             trans_pixel = ExtractByte(buf, 4);
 152                         } else {
 153                             trans_pixel = -1;
 154                         }
 155                         break;
 156                       }
 157 
 158                       case EX_COMMENT:
 159                       case EX_APPLICATION:
 160                       default:
 161                         boolean loop_tag = false;
 162                         String comment = "";
 163                         while (true) {
 164                             int n = input.read();
 165                             if (n <= 0) {
 166                                 break;
 167                             }
 168                             byte[] buf = new byte[n];
 169                             if (readBytes(buf, 0, n) != 0) {
 170                                 return;//error("corrupt GIF file");
 171                             }
 172                             if (code == EX_COMMENT) {
 173                                 comment += new String(buf, 0);
 174                             } else if (code == EX_APPLICATION) {
 175                                 if (loop_tag) {
 176                                     if (n == 3 && buf[0] == 1) {
 177                                         if (loopsRead) {
 178                                             ExtractWord(buf, 1);
 179                                         }
 180                                         else {
 181                                             nloops = ExtractWord(buf, 1);
 182                                             loopsRead = true;
 183                                         }
 184                                     } else {
 185                                         loop_tag = false;
 186                                     }
 187                                 }
 188                                 if ("NETSCAPE2.0".equals(new String(buf, 0))) {


 258                     }
 259                     if (verbose && frameno != 1) {
 260                         System.out.println("processing GIF terminator,"
 261                                            + " frames: " + frameno
 262                                            + " total: " + totalframes);
 263                     }
 264                     imageComplete(ImageConsumer.STATICIMAGEDONE, true);
 265                     return;
 266                 }
 267             }
 268         } finally {
 269             close();
 270         }
 271     }
 272 
 273     /**
 274      * Read Image header
 275      */
 276     private void readHeader() throws IOException, ImageFormatException {
 277         // Create a buffer
 278         byte[] buf = new byte[13];
 279 
 280         // Read the header
 281         if (readBytes(buf, 0, 13) != 0) {
 282             throw new IOException();
 283         }
 284 
 285         // Check header
 286         if ((buf[0] != 'G') || (buf[1] != 'I') || (buf[2] != 'F')) {
 287             error("not a GIF file.");
 288         }
 289 
 290         // Global width&height
 291         global_width = ExtractWord(buf, 6);
 292         global_height = ExtractWord(buf, 8);
 293 
 294         // colormap info
 295         int ch = ExtractByte(buf, 10);
 296         if ((ch & COLORMAPMASK) == 0) {
 297             // no global colormap so make up our own
 298             // If there is a local colormap, it will override what we


 322                 throw new IOException();
 323             }
 324         }
 325         input.mark(Integer.MAX_VALUE); // set this mark in case this is an animated GIF
 326     }
 327 
 328     /**
 329      * The ImageConsumer hints flag for a non-interlaced GIF image.
 330      */
 331     private static final int normalflags =
 332         ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
 333         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME;
 334 
 335     /**
 336      * The ImageConsumer hints flag for an interlaced GIF image.
 337      */
 338     private static final int interlaceflags =
 339         ImageConsumer.RANDOMPIXELORDER | ImageConsumer.COMPLETESCANLINES |
 340         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME;
 341 
 342     private short[] prefix  = new short[4096];
 343     private byte[]  suffix  = new byte[4096];
 344     private byte[]  outCode = new byte[4097];
 345 
 346     private static native void initIDs();
 347 
 348     static {
 349         /* ensure that the necessary native libraries are loaded */
 350         NativeLibLoader.loadLibraries();
 351         initIDs();
 352     }
 353 
 354     private native boolean parseImage(int x, int y, int width, int height,
 355                                       boolean interlace, int initCodeSize,
 356                                       byte[] block, byte[] rasline,
 357                                       IndexColorModel model);
 358 
 359     private int sendPixels(int x, int y, int width, int height,
 360                            byte[] rasline, ColorModel model) {
 361         int rasbeg, rasend, x2;
 362         if (y < 0) {
 363             height += y;
 364             y = 0;
 365         }
 366         if (y + height > global_height) {
 367             height = global_height - y;
 368         }
 369         if (height <= 0) {
 370             return 1;
 371         }
 372         // rasline[0]     == pixel at coordinate (x,y)
 373         // rasline[width] == pixel at coordinate (x+width, y)
 374         if (x < 0) {
 375             rasbeg = -x;
 376             width += x;         // same as (width -= rasbeg)
 377             x2 = 0;             // same as (x2     = x + rasbeg)
 378         } else {
 379             rasbeg = 0;
 380             // width -= 0;      // same as (width -= rasbeg)


 450     }
 451 
 452     /**
 453      * Read Image data
 454      */
 455     private boolean readImage(boolean first, int disposal_method, int delay)
 456         throws IOException
 457     {
 458         if (curframe != null && !curframe.dispose()) {
 459             abort();
 460             return false;
 461         }
 462 
 463         long tm = 0;
 464 
 465         if (verbose) {
 466             tm = System.currentTimeMillis();
 467         }
 468 
 469         // Allocate the buffer
 470         byte[] block = new byte[256 + 3];
 471 
 472         // Read the image descriptor
 473         if (readBytes(block, 0, 10) != 0) {
 474             throw new IOException();
 475         }
 476         int x = ExtractWord(block, 0);
 477         int y = ExtractWord(block, 2);
 478         int width = ExtractWord(block, 4);
 479         int height = ExtractWord(block, 6);
 480 
 481         /*
 482          * Majority of gif images have
 483          * same logical screen and frame dimensions.
 484          * Also, Photoshop and Mozilla seem to use the logical
 485          * screen dimension (from the global stream header)
 486          * if frame dimension is invalid.
 487          *
 488          * We use similar heuristic and trying to recover
 489          * frame width from logical screen dimension and
 490          * frame offset.


 540         // Notify the consumers
 541         if (first) {
 542             if (global_width == 0) global_width = width;
 543             if (global_height == 0) global_height = height;
 544 
 545             setDimensions(global_width, global_height);
 546             setProperties(props);
 547             setColorModel(model);
 548             headerComplete();
 549         }
 550 
 551         if (disposal_method == GifFrame.DISPOSAL_SAVE && saved_image == null) {
 552             saved_image = new byte[global_width * global_height];
 553             /*
 554              * If height of current image is smaller than the global height,
 555              * fill the gap with transparent pixels.
 556              */
 557             if ((height < global_height) && (model != null)) {
 558                 byte tpix = (byte)model.getTransparentPixel();
 559                 if (tpix >= 0) {
 560                     byte[] trans_rasline = new byte[global_width];
 561                     for (int i=0; i<global_width;i++) {
 562                         trans_rasline[i] = tpix;
 563                     }
 564 
 565                     setPixels(0, 0, global_width, y,
 566                               model, trans_rasline, 0, 0);
 567                     setPixels(0, y+height, global_width,
 568                               global_height-height-y, model, trans_rasline,
 569                               0, 0);
 570                 }
 571             }
 572         }
 573 
 574         int hints = (interlace ? interlaceflags : normalflags);
 575         setHints(hints);
 576 
 577         curframe = new GifFrame(this, disposal_method, delay,
 578                                 (curframe == null), model,
 579                                 x, y, width, height);
 580 
 581         // allocate the raster data
 582         byte[] rasline = new byte[width];
 583 
 584         if (verbose) {
 585             System.out.print("Reading a " + width + " by " + height + " " +
 586                       (interlace ? "" : "non-") + "interlaced image...");
 587         }
 588         int initCodeSize = ExtractByte(block, 9);
 589         if (initCodeSize >= 12) {
 590             if (verbose) {
 591                 System.out.println("Invalid initial code size: " +
 592                                    initCodeSize);
 593             }
 594             return false;
 595         }
 596         boolean ret = parseImage(x, y, width, height,
 597                                  interlace, initCodeSize,
 598                                  block, rasline, model);
 599 
 600         if (!ret) {
 601             abort();
 602         }


< prev index next >