/* * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /*- * Reads GIF images from an InputStream and reports the * image data to an InputStreamImageSource object. * * The algorithm is copyright of CompuServe. */package sun.awt.image; import java.util.Vector;import java.util.Hashtable;import java.io.InputStream;import java.io.IOException;import java.awt.image.*; /** * Gif Image converter * * @author Arthur van Hoff * @author Jim Graham */public class GifImageDecoder extends ImageDecoder { private static final boolean verbose = false; private static final int IMAGESEP = 0x2c; private static final int EXBLOCK = 0x21; private static final int EX_GRAPHICS_CONTROL= 0xf9; private static final int EX_COMMENT = 0xfe; private static final int EX_APPLICATION = 0xff; private static final int TERMINATOR = 0x3b; private static final int TRANSPARENCYMASK = 0x01; private static final int INTERLACEMASK = 0x40; private static final int COLORMAPMASK = 0x80; int num_global_colors; byte[] global_colormap; int trans_pixel = -1; IndexColorModel global_model; Hashtable props = new Hashtable(); byte[] saved_image; IndexColorModel saved_model; int global_width; int global_height; int global_bgpixel; GifFrame curframe; public GifImageDecoder(InputStreamImageSource src, InputStream is) { super(src, is); } /** * An error has occurred. Throw an exception. */ private static void error(String s1) throws ImageFormatException { throw new ImageFormatException(s1); } /** * Read a number of bytes into a buffer. * @return number of bytes that were not read due to EOF or error */ private int readBytes(byte buf[], int off, int len) { while (len > 0) { try { int n = input.read(buf, off, len); if (n < 0) { break; } off += n; len -= n; } catch (IOException e) { break; } } return len; } private static final int ExtractByte(byte buf[], int off) { return (buf[off] & 0xFF); } private static final int ExtractWord(byte buf[], int off) { return (buf[off] & 0xFF) | ((buf[off + 1] & 0xFF) << 8); } /** * produce an image from the stream. */ public void produceImage() throws IOException, ImageFormatException { try { readHeader(); int totalframes = 0; int frameno = 0; int nloops = -1; int disposal_method = 0; int delay = -1; boolean loopsRead = false; boolean isAnimation = false; while (!aborted) { int code; switch (code = input.read()) { case EXBLOCK: switch (code = input.read()) { case EX_GRAPHICS_CONTROL: { byte buf[] = new byte[6]; if (readBytes(buf, 0, 6) != 0) { return;//error("corrupt GIF file"); } if ((buf[0] != 4) || (buf[5] != 0)) { return;//error("corrupt GIF file (GCE size)"); } // Get the index of the transparent color delay = ExtractWord(buf, 2) * 10; if (delay > 0 && !isAnimation) { isAnimation = true; ImageFetcher.startingAnimation(); } disposal_method = (buf[1] >> 2) & 7; if ((buf[1] & TRANSPARENCYMASK) != 0) { trans_pixel = ExtractByte(buf, 4); } else { trans_pixel = -1; } break; } case EX_COMMENT: case EX_APPLICATION: default: boolean loop_tag = false; String comment = ""; while (true) { int n = input.read(); if (n <= 0) { break; } byte buf[] = new byte[n]; if (readBytes(buf, 0, n) != 0) { return;//error("corrupt GIF file"); } if (code == EX_COMMENT) { comment += new String(buf, 0); } else if (code == EX_APPLICATION) { if (loop_tag) { if (n == 3 && buf[0] == 1) { if (loopsRead) { ExtractWord(buf, 1); } else { nloops = ExtractWord(buf, 1); loopsRead = true; } } else { loop_tag = false; } } if ("NETSCAPE2.0".equals(new String(buf, 0))) { loop_tag = true; } } } if (code == EX_COMMENT) { props.put("comment", comment); } if (loop_tag && !isAnimation) { isAnimation = true; ImageFetcher.startingAnimation(); } break; case -1: return; //error("corrupt GIF file"); } break; case IMAGESEP: if (!isAnimation) { input.mark(0); // we don't need the mark buffer } try { if (!readImage(totalframes == 0, disposal_method, delay)) { return; } } catch (Exception e) { if (verbose) { e.printStackTrace(); } return; } frameno++; totalframes++; break; default: case -1: if (verbose) { if (code == -1) { System.err.println("Premature EOF in GIF file," + " frame " + frameno); } else { System.err.println("corrupt GIF file (parse) [" + code + "]."); } } if (frameno == 0) { return; } // NOBREAK case TERMINATOR: if (nloops == 0 || nloops-- >= 0) { try { if (curframe != null) { curframe.dispose(); curframe = null; } input.reset(); saved_image = null; saved_model = null; frameno = 0; break; } catch (IOException e) { return; // Unable to reset input buffer } } if (verbose && frameno != 1) { System.out.println("processing GIF terminator," + " frames: " + frameno + " total: " + totalframes); } imageComplete(ImageConsumer.STATICIMAGEDONE, true); return; } } } finally { close(); } } /** * Read Image header */ private void readHeader() throws IOException, ImageFormatException { // Create a buffer byte buf[] = new byte[13]; // Read the header if (readBytes(buf, 0, 13) != 0) { throw new IOException(); } // Check header if ((buf[0] != 'G') || (buf[1] != 'I') || (buf[2] != 'F')) { error("not a GIF file."); } // Global width&height global_width = ExtractWord(buf, 6); global_height = ExtractWord(buf, 8); // colormap info int ch = ExtractByte(buf, 10); if ((ch & COLORMAPMASK) == 0) { // no global colormap so make up our own // If there is a local colormap, it will override what we // have here. If there is not a local colormap, the rules // for GIF89 say that we can use whatever colormap we want. // This means that we should probably put in a full 256 colormap // at some point. REMIND! num_global_colors = 2; global_bgpixel = 0; global_colormap = new byte[2*3]; global_colormap[0] = global_colormap[1] = global_colormap[2] = (byte)0; global_colormap[3] = global_colormap[4] = global_colormap[5] = (byte)255; } else { num_global_colors = 1 << ((ch & 0x7) + 1); global_bgpixel = ExtractByte(buf, 11); if (buf[12] != 0) { props.put("aspectratio", ""+((ExtractByte(buf, 12) + 15) / 64.0)); } // Read colors global_colormap = new byte[num_global_colors * 3]; if (readBytes(global_colormap, 0, num_global_colors * 3) != 0) { throw new IOException(); } } input.mark(Integer.MAX_VALUE); // set this mark in case this is an animated GIF } /** * The ImageConsumer hints flag for a non-interlaced GIF image. */ private static final int normalflags = ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME; /** * The ImageConsumer hints flag for an interlaced GIF image. */ private static final int interlaceflags = ImageConsumer.RANDOMPIXELORDER | ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME; private short prefix[] = new short[4096]; private byte suffix[] = new byte[4096]; private byte outCode[] = new byte[4097]; private static native void initIDs(); static { /* ensure that the necessary native libraries are loaded */ NativeLibLoader.loadLibraries(); initIDs(); } private native boolean parseImage(int x, int y, int width, int height, boolean interlace, int initCodeSize, byte block[], byte rasline[], IndexColorModel model); private int sendPixels(int x, int y, int width, int height, byte rasline[], ColorModel model) { int rasbeg, rasend, x2; if (y < 0) { height += y; y = 0; } if (y + height > global_height) { height = global_height - y; } if (height <= 0) { return 1; } // rasline[0] == pixel at coordinate (x,y) // rasline[width] == pixel at coordinate (x+width, y) if (x < 0) { rasbeg = -x; width += x; // same as (width -= rasbeg) x2 = 0; // same as (x2 = x + rasbeg) } else { rasbeg = 0; // width -= 0; // same as (width -= rasbeg) x2 = x; // same as (x2 = x + rasbeg) } // rasline[rasbeg] == pixel at coordinate (x2,y) // rasline[width] == pixel at coordinate (x+width, y) // rasline[rasbeg + width] == pixel at coordinate (x2+width, y) if (x2 + width > global_width) { width = global_width - x2; } if (width <= 0) { return 1; } rasend = rasbeg + width; // rasline[rasbeg] == pixel at coordinate (x2,y) // rasline[rasend] == pixel at coordinate (x2+width, y) int off = y * global_width + x2; boolean save = (curframe.disposal_method == GifFrame.DISPOSAL_SAVE); if (trans_pixel >= 0 && !curframe.initialframe) { if (saved_image != null && model.equals(saved_model)) { for (int i = rasbeg; i < rasend; i++, off++) { byte pixel = rasline[i]; if ((pixel & 0xff) == trans_pixel) { rasline[i] = saved_image[off]; } else if (save) { saved_image[off] = pixel; } } } else { // We have to do this the hard way - only transmit // the non-transparent sections of the line... // Fix for 6301050: the interlacing is ignored in this case // in order to avoid artefacts in case of animated images. int runstart = -1; int count = 1; for (int i = rasbeg; i < rasend; i++, off++) { byte pixel = rasline[i]; if ((pixel & 0xff) == trans_pixel) { if (runstart >= 0) { count = setPixels(x + runstart, y, i - runstart, 1, model, rasline, runstart, 0); if (count == 0) { break; } } runstart = -1; } else { if (runstart < 0) { runstart = i; } if (save) { saved_image[off] = pixel; } } } if (runstart >= 0) { count = setPixels(x + runstart, y, rasend - runstart, 1, model, rasline, runstart, 0); } return count; } } else if (save) { System.arraycopy(rasline, rasbeg, saved_image, off, width); } int count = setPixels(x2, y, width, height, model, rasline, rasbeg, 0); return count; } /** * Read Image data */ private boolean readImage(boolean first, int disposal_method, int delay) throws IOException { if (curframe != null && !curframe.dispose()) { abort(); return false; } long tm = 0; if (verbose) { tm = System.currentTimeMillis(); } // Allocate the buffer byte block[] = new byte[256 + 3]; // Read the image descriptor if (readBytes(block, 0, 10) != 0) { throw new IOException(); } int x = ExtractWord(block, 0); int y = ExtractWord(block, 2); int width = ExtractWord(block, 4); int height = ExtractWord(block, 6); /* * Majority of gif images have * same logical screen and frame dimensions. * Also, Photoshop and Mozilla seem to use the logical * screen dimension (from the global stream header) * if frame dimension is invalid. * * We use similar heuristic and trying to recover * frame width from logical screen dimension and * frame offset. */ if (width == 0 && global_width != 0) { width = global_width - x; } if (height == 0 && global_height != 0) { height = global_height - y; } boolean interlace = (block[8] & INTERLACEMASK) != 0; Lines 1–500 of 753. Load more
/* * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /*- * Reads GIF images from an InputStream and reports the * image data to an InputStreamImageSource object. * * The algorithm is copyright of CompuServe. */ package sun.awt.image; import java.util.Vector; import java.util.Hashtable; import java.io.InputStream; import java.io.IOException; import java.awt.image.*; /** * Gif Image converter * * @author Arthur van Hoff * @author Jim Graham */ public class GifImageDecoder extends ImageDecoder { private static final boolean verbose = false; private static final int IMAGESEP = 0x2c; private static final int EXBLOCK = 0x21; private static final int EX_GRAPHICS_CONTROL= 0xf9; private static final int EX_COMMENT = 0xfe; private static final int EX_APPLICATION = 0xff; private static final int TERMINATOR = 0x3b; private static final int TRANSPARENCYMASK = 0x01; private static final int INTERLACEMASK = 0x40; private static final int COLORMAPMASK = 0x80; int num_global_colors; byte[] global_colormap; int trans_pixel = -1; IndexColorModel global_model; Hashtable props = new Hashtable(); byte[] saved_image; IndexColorModel saved_model; int global_width; int global_height; int global_bgpixel; GifFrame curframe; public GifImageDecoder(InputStreamImageSource src, InputStream is) { super(src, is); } /** * An error has occurred. Throw an exception. */ private static void error(String s1) throws ImageFormatException { throw new ImageFormatException(s1); } /** * Read a number of bytes into a buffer. * @return number of bytes that were not read due to EOF or error */ private int readBytes(byte buf[], int off, int len) { while (len > 0) { try { int n = input.read(buf, off, len); if (n < 0) { break; } off += n; len -= n; } catch (IOException e) { break; } } return len; } private static final int ExtractByte(byte buf[], int off) { return (buf[off] & 0xFF); } private static final int ExtractWord(byte buf[], int off) { return (buf[off] & 0xFF) | ((buf[off + 1] & 0xFF) << 8); } /** * produce an image from the stream. */ public void produceImage() throws IOException, ImageFormatException { try { readHeader(); int totalframes = 0; int frameno = 0; int nloops = -1; int disposal_method = 0; int delay = -1; boolean loopsRead = false; boolean isAnimation = false; while (!aborted) { int code; switch (code = input.read()) { case EXBLOCK: switch (code = input.read()) { case EX_GRAPHICS_CONTROL: { byte buf[] = new byte[6]; if (readBytes(buf, 0, 6) != 0) { return;//error("corrupt GIF file"); } if ((buf[0] != 4) || (buf[5] != 0)) { return;//error("corrupt GIF file (GCE size)"); } // Get the index of the transparent color delay = ExtractWord(buf, 2) * 10; if (delay > 0 && !isAnimation) { isAnimation = true; ImageFetcher.startingAnimation(); } disposal_method = (buf[1] >> 2) & 7; if ((buf[1] & TRANSPARENCYMASK) != 0) { trans_pixel = ExtractByte(buf, 4); } else { trans_pixel = -1; } break; } case EX_COMMENT: case EX_APPLICATION: default: boolean loop_tag = false; String comment = ""; while (true) { int n = input.read(); if (n <= 0) { break; } byte buf[] = new byte[n]; if (readBytes(buf, 0, n) != 0) { return;//error("corrupt GIF file"); } if (code == EX_COMMENT) { comment += new String(buf, 0); } else if (code == EX_APPLICATION) { if (loop_tag) { if (n == 3 && buf[0] == 1) { if (loopsRead) { ExtractWord(buf, 1); } else { nloops = ExtractWord(buf, 1); loopsRead = true; } } else { loop_tag = false; } } if ("NETSCAPE2.0".equals(new String(buf, 0))) { loop_tag = true; } } } if (code == EX_COMMENT) { props.put("comment", comment); } if (loop_tag && !isAnimation) { isAnimation = true; ImageFetcher.startingAnimation(); } break; case -1: return; //error("corrupt GIF file"); } break; case IMAGESEP: if (!isAnimation) { input.mark(0); // we don't need the mark buffer } try { if (!readImage(totalframes == 0, disposal_method, delay)) { return; } } catch (Exception e) { if (verbose) { e.printStackTrace(); } return; } frameno++; totalframes++; break; default: case -1: if (verbose) { if (code == -1) { System.err.println("Premature EOF in GIF file," + " frame " + frameno); } else { System.err.println("corrupt GIF file (parse) [" + code + "]."); } } if (frameno == 0) { return; } // NOBREAK case TERMINATOR: if (nloops == 0 || nloops-- >= 0) { try { if (curframe != null) { curframe.dispose(); curframe = null; } input.reset(); saved_image = null; saved_model = null; frameno = 0; break; } catch (IOException e) { return; // Unable to reset input buffer } } if (verbose && frameno != 1) { System.out.println("processing GIF terminator," + " frames: " + frameno + " total: " + totalframes); } imageComplete(ImageConsumer.STATICIMAGEDONE, true); return; } } } finally { close(); } } /** * Read Image header */ private void readHeader() throws IOException, ImageFormatException { // Create a buffer byte buf[] = new byte[13]; // Read the header if (readBytes(buf, 0, 13) != 0) { throw new IOException(); } // Check header if ((buf[0] != 'G') || (buf[1] != 'I') || (buf[2] != 'F')) { error("not a GIF file."); } // Global width&height global_width = ExtractWord(buf, 6); global_height = ExtractWord(buf, 8); // colormap info int ch = ExtractByte(buf, 10); if ((ch & COLORMAPMASK) == 0) { // no global colormap so make up our own // If there is a local colormap, it will override what we // have here. If there is not a local colormap, the rules // for GIF89 say that we can use whatever colormap we want. // This means that we should probably put in a full 256 colormap // at some point. REMIND! num_global_colors = 2; global_bgpixel = 0; global_colormap = new byte[2*3]; global_colormap[0] = global_colormap[1] = global_colormap[2] = (byte)0; global_colormap[3] = global_colormap[4] = global_colormap[5] = (byte)255; } else { num_global_colors = 1 << ((ch & 0x7) + 1); global_bgpixel = ExtractByte(buf, 11); if (buf[12] != 0) { props.put("aspectratio", ""+((ExtractByte(buf, 12) + 15) / 64.0)); } // Read colors global_colormap = new byte[num_global_colors * 3]; if (readBytes(global_colormap, 0, num_global_colors * 3) != 0) { throw new IOException(); } } input.mark(Integer.MAX_VALUE); // set this mark in case this is an animated GIF } /** * The ImageConsumer hints flag for a non-interlaced GIF image. */ private static final int normalflags = ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME; /** * The ImageConsumer hints flag for an interlaced GIF image. */ private static final int interlaceflags = ImageConsumer.RANDOMPIXELORDER | ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME; private short prefix[] = new short[4096]; private byte suffix[] = new byte[4096]; private byte outCode[] = new byte[4097]; private static native void initIDs(); static { /* ensure that the necessary native libraries are loaded */ NativeLibLoader.loadLibraries(); initIDs(); } private native boolean parseImage(int x, int y, int width, int height, boolean interlace, int initCodeSize, byte block[], byte rasline[], IndexColorModel model); private int sendPixels(int x, int y, int width, int height, byte rasline[], ColorModel model) { int rasbeg, rasend, x2; if (y < 0) { height += y; y = 0; } if (y + height > global_height) { height = global_height - y; } if (height <= 0) { return 1; } // rasline[0] == pixel at coordinate (x,y) // rasline[width] == pixel at coordinate (x+width, y) if (x < 0) { rasbeg = -x; width += x; // same as (width -= rasbeg) x2 = 0; // same as (x2 = x + rasbeg) } else { rasbeg = 0; // width -= 0; // same as (width -= rasbeg) x2 = x; // same as (x2 = x + rasbeg) } // rasline[rasbeg] == pixel at coordinate (x2,y) // rasline[width] == pixel at coordinate (x+width, y) // rasline[rasbeg + width] == pixel at coordinate (x2+width, y) if (x2 + width > global_width) { width = global_width - x2; } if (width <= 0) { return 1; } rasend = rasbeg + width; // rasline[rasbeg] == pixel at coordinate (x2,y) // rasline[rasend] == pixel at coordinate (x2+width, y) int off = y * global_width + x2; boolean save = (curframe.disposal_method == GifFrame.DISPOSAL_SAVE); if (trans_pixel >= 0 && !curframe.initialframe) { if (saved_image != null && model.equals(saved_model)) { for (int i = rasbeg; i < rasend; i++, off++) { byte pixel = rasline[i]; if ((pixel & 0xff) == trans_pixel) { rasline[i] = saved_image[off]; } else if (save) { saved_image[off] = pixel; } } } else { // We have to do this the hard way - only transmit // the non-transparent sections of the line... // Fix for 6301050: the interlacing is ignored in this case // in order to avoid artefacts in case of animated images. int runstart = -1; int count = 1; for (int i = rasbeg; i < rasend; i++, off++) { byte pixel = rasline[i]; if ((pixel & 0xff) == trans_pixel) { if (runstart >= 0) { count = setPixels(x + runstart, y, i - runstart, 1, model, rasline, runstart, 0); if (count == 0) { break; } } runstart = -1; } else { if (runstart < 0) { runstart = i; } if (save) { saved_image[off] = pixel; } } } if (runstart >= 0) { count = setPixels(x + runstart, y, rasend - runstart, 1, model, rasline, runstart, 0); } return count; } } else if (save) { System.arraycopy(rasline, rasbeg, saved_image, off, width); } int count = setPixels(x2, y, width, height, model, rasline, rasbeg, 0); return count; } /** * Read Image data */ private boolean readImage(boolean first, int disposal_method, int delay) throws IOException { if (curframe != null && !curframe.dispose()) { abort(); return false; } long tm = 0; if (verbose) { tm = System.currentTimeMillis(); } // Allocate the buffer byte block[] = new byte[256 + 3]; // Read the image descriptor if (readBytes(block, 0, 10) != 0) { throw new IOException(); } int x = ExtractWord(block, 0); int y = ExtractWord(block, 2); int width = ExtractWord(block, 4); int height = ExtractWord(block, 6); /* * Majority of gif images have * same logical screen and frame dimensions. * Also, Photoshop and Mozilla seem to use the logical * screen dimension (from the global stream header) * if frame dimension is invalid. * * We use similar heuristic and trying to recover * frame width from logical screen dimension and * frame offset. */ if (width == 0 && global_width != 0) { width = global_width - x; } if (height == 0 && global_height != 0) { height = global_height - y; } boolean interlace = (block[8] & INTERLACEMASK) != 0;