840 lines · 34.6 KB · Java
RawDownload
/* * Copyright (c) 1999, 2010, 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. */ package sun.awt.image; import java.io.*;import java.util.*;import java.util.zip.*;import java.awt.image.*;import java.awt.Color; /** PNG - Portable Network Graphics - image file reader.    See <a href=http://www.ietf.org/rfc/rfc2083.txt>RFC2083</a> for details. */ /* this is changedpublic class PNGImageDecoder extends FilterInputStream implements Runnable{ */ public class PNGImageDecoder extends ImageDecoder{    private static final int GRAY=0;    private static final int PALETTE=1;    private static final int COLOR=2;    private static final int ALPHA=4;     private static final int bKGDChunk = 0x624B4744;    private static final int cHRMChunk = 0x6348524D;    private static final int gAMAChunk = 0x67414D41;    private static final int hISTChunk = 0x68495354;    private static final int IDATChunk = 0x49444154;    private static final int IENDChunk = 0x49454E44;    private static final int IHDRChunk = 0x49484452;    private static final int PLTEChunk = 0x504C5445;    private static final int pHYsChunk = 0x70485973;    private static final int sBITChunk = 0x73424954;    private static final int tEXtChunk = 0x74455874;    private static final int tIMEChunk = 0x74494D45;    private static final int tRNSChunk = 0x74524E53;    private static final int zTXtChunk = 0x7A545874;     private int width;    private int height;    private int bitDepth;    private int colorType;    private int compressionMethod;    private int filterMethod;    private int interlaceMethod;    private int gamma = 100000;    private java.util.Hashtable properties;  /* this is not needed    ImageConsumer target;    */    private ColorModel cm;    private byte[] red_map, green_map, blue_map, alpha_map;    private int transparentPixel = -1;    private byte[]  transparentPixel_16 = null; // we need 6 bytes to store 16bpp value    private static ColorModel greyModels[] = new ColorModel[4];  /* this is not needed     PNGImageDecoder next;     */     private void property(String key,Object value) {        if(value==null) return;        if(properties==null) properties=new java.util.Hashtable();        properties.put(key,value);    }    private void property(String key,float value) {        property(key,new Float(value));    }    private final void pngassert(boolean b) throws IOException {        if(!b) {            PNGException e = new PNGException("Broken file");            e.printStackTrace();            throw e;        }    }    protected boolean handleChunk(int key, byte[] buf, int st, int len)        throws IOException {        switch(key) {            case bKGDChunk:                Color c = null;                switch(colorType) {                    case COLOR:                    case COLOR|ALPHA:                        pngassert(len==6);                        c = new Color(buf[st]&0xff,buf[st+2]&0xff,buf[st+4]&0xff);                        break;                    case COLOR|PALETTE:                    case COLOR|PALETTE|ALPHA:                        pngassert(len==1);                        int ix = buf[st]&0xFF;                        pngassert(red_map!=null && ix<red_map.length);                        c = new Color(red_map[ix]&0xff,green_map[ix]&0xff,blue_map[ix]&0xff);                        break;                    case GRAY:                    case GRAY|ALPHA:                        pngassert(len==2);                        int t = buf[st]&0xFF;                        c = new Color(t,t,t);                        break;                }                if(c!=null) property("background",c);                break;            case cHRMChunk:                property("chromaticities",                    new Chromaticities(                        getInt(st),                        getInt(st+4),                        getInt(st+8),                        getInt(st+12),                        getInt(st+16),                        getInt(st+20),                        getInt(st+24),                        getInt(st+28)));                break;            case gAMAChunk:                if(len!=4) throw new PNGException("bogus gAMA");                gamma = getInt(st);                if(gamma!=100000) property("gamma",gamma/100000.0f);                break;            case hISTChunk: break;            case IDATChunk: return false;            case IENDChunk: break;            case IHDRChunk:                if(len!=13                    ||(width = getInt(st))==0                    ||(height = getInt(st+4))==0                    ) throw new PNGException("bogus IHDR");                bitDepth = getByte(st+8);                colorType = getByte(st+9);                compressionMethod = getByte(st+10);                filterMethod = getByte(st+11);                interlaceMethod = getByte(st+12);                /* this is not needed                  if(target!=null) target.setDimensions(width,height);                  */                break;            case PLTEChunk:                {   int tsize = len/3;                    red_map = new byte[tsize];                    green_map = new byte[tsize];                    blue_map = new byte[tsize];                    for(int i=0,j=st; i<tsize; i++, j+=3) {                        red_map[i] = buf[j];                        green_map[i] = buf[j+1];                        blue_map[i] = buf[j+2];                    }                }                break;            case pHYsChunk: break;            case sBITChunk: break;            case tEXtChunk:                int klen = 0;                while(klen<len && buf[st+klen]!=0) klen++;                if(klen<len) {                    String tkey = new String(buf,st,klen);                    String tvalue = new String(buf,st+klen+1,len-klen-1);                    property(tkey,tvalue);                }                break;            case tIMEChunk:                property("modtime",new GregorianCalendar(                    getShort(st+0),                    getByte(st+2)-1,                    getByte(st+3),                    getByte(st+4),                    getByte(st+5),                    getByte(st+6)).getTime());                break;            case tRNSChunk:                switch(colorType) {                    case PALETTE|COLOR:                    case PALETTE|COLOR|ALPHA:                        int alen = len;                        if(red_map!=null) alen = red_map.length;                        alpha_map = new byte[alen];                        System.arraycopy(buf,st,alpha_map,0,len<alen ? len : alen);                        while (--alen>=len) alpha_map[alen] = (byte)0xFF;                        break;                    case COLOR: // doesn't deal with 16 bit colors properly                    case COLOR|ALPHA: // doesn't deal with 16 bit colors properly                        pngassert(len==6);                        if (bitDepth == 16) {                            transparentPixel_16 = new byte[6];                            for (int i = 0; i < 6; i++) {                                transparentPixel_16[i] = (byte)getByte(st + i);                            }                        } else {                            transparentPixel =                                      ((getShort(st + 0)&0xFF)<<16)                                    | ((getShort(st + 2)&0xFF)<< 8)                                    | ((getShort(st + 4)&0xFF)    );                        }                        break;                    case GRAY:  // doesn't deal with 16 bit colors properly                    case GRAY|ALPHA:  // doesn't deal with 16 bit colors properly                        pngassert(len==2);                        /* REMIND: Discarding the LSB for 16 bit depth here                         * means that the all pixels which match the MSB                         * will be treated as transparent.                         */                        int t = getShort(st);                        t = 0xFF & ((bitDepth == 16) ? (t >> 8) : t);                        transparentPixel = (t<<16) | (t<< 8) | t;                        break;                }                break;            case zTXtChunk: break;        }        return true;    }    public class PNGException extends IOException {        PNGException(String s) { super(s); }    }  /* this is changed     public void run() {     */  public void produceImage() throws IOException, ImageFormatException {    /* this is not needed       ImageConsumer t = target;       if(t!=null) try {       */    try {            for(int i=0; i<signature.length; i++)              if((signature[i]&0xFF)!=underlyingInputStream.read())                throw new PNGException("Chunk signature mismatch");             InputStream is = new BufferedInputStream(new InflaterInputStream(inputStream,new Inflater()));             getData();             byte[] bPixels = null;            int[] wPixels = null;            int pixSize = width;            int rowStride;            int logDepth = 0;            switch(bitDepth) {                case  1: logDepth = 0; break;                case  2: logDepth = 1; break;                case  4: logDepth = 2; break;                case  8: logDepth = 3; break;                case 16: logDepth = 4; break;                default: throw new PNGException("invalid depth");            }            if(interlaceMethod!=0) {pixSize *= height;rowStride=width;}            else rowStride = 0;            int combinedType = colorType|(bitDepth<<3);            int bitMask = (1<<(bitDepth>=8?8:bitDepth))-1;            //Figure out the color model            switch(colorType) {                case COLOR|PALETTE:                case COLOR|PALETTE|ALPHA:                    if(red_map==null) throw new PNGException("palette expected");                    if(alpha_map==null)                        cm = new IndexColorModel(bitDepth,red_map.length,                            red_map,green_map,blue_map);                    else                        cm = new IndexColorModel(bitDepth,red_map.length,                            red_map,green_map,blue_map,alpha_map);                    bPixels = new byte[pixSize];                    break;                case GRAY:                    {   int llog = logDepth>=4 ? 3 : logDepth;                        if((cm=greyModels[llog]) == null) {                            int size = 1<<(1<<llog);                             byte ramp[] = new byte[size];                            for(int i = 0; i<size; i++) ramp[i] = (byte)(255*i/(size-1));                             if (transparentPixel == -1) {                                cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp);                            } else {                                cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp,                                                         (transparentPixel & 0xFF));                            }                            greyModels[llog] = cm;                        }                    }                    bPixels = new byte[pixSize];                    break;                case COLOR:                case COLOR|ALPHA:                case GRAY|ALPHA:                    cm = ColorModel.getRGBdefault();                    wPixels = new int[pixSize];                    break;                default:                    throw new PNGException("invalid color type");            }            /* this is going to be set in the pixel store              t.setColorModel(cm);            t.setHints(interlaceMethod !=0                       ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES                       : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |                         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);                         */            // code added to make it work with ImageDecoder architecture            setDimensions(width, height);            setColorModel(cm);            int flags = (interlaceMethod !=0                       ? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES                       : ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |                         ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME);            setHints(flags);            headerComplete();            // end of adding             int samplesPerPixel = ((colorType&PALETTE)!=0 ? 1                                 : ((colorType&COLOR)!=0 ? 3 : 1)+((colorType&ALPHA)!=0?1:0));            int bitsPerPixel = samplesPerPixel*bitDepth;            int bytesPerPixel = (bitsPerPixel+7)>>3;            int pass, passLimit;            if(interlaceMethod==0) { pass = -1; passLimit = 0; }            else { pass = 0; passLimit = 7; }            // These loops are far from being tuned.  They're this way to make them easy to            // debug.  Tuning comes later.            /* code changed. target not needed here               while(++pass<=passLimit && (t=target)!=null) {               */            while(++pass<=passLimit) {                int row = startingRow[pass];                int rowInc = rowIncrement[pass];                int colInc = colIncrement[pass];                int bWidth = blockWidth[pass];                int bHeight = blockHeight[pass];                int sCol = startingCol[pass];                int rowPixelWidth = (width-sCol+(colInc-1))/colInc;                int rowByteWidth = ((rowPixelWidth*bitsPerPixel)+7)>>3;                if(rowByteWidth==0) continue;                int pixelBufferInc = interlaceMethod==0 ? rowInc*width : 0;                int rowOffset = rowStride*row;                boolean firstRow = true;                 byte[] rowByteBuffer = new byte[rowByteWidth];                byte[] prevRowByteBuffer = new byte[rowByteWidth];                /* code changed. target not needed here                   while (row < height && (t=target)!=null) {                   */                while (row < height) {                    int rowFilter = is.read();                    for (int rowFillPos=0;rowFillPos<rowByteWidth; ) {                        int n = is.read(rowByteBuffer,rowFillPos,rowByteWidth-rowFillPos);                        if(n<=0) throw new PNGException("missing data");                        rowFillPos+=n;                    }                    filterRow(rowByteBuffer,                              firstRow ? null : prevRowByteBuffer,                              rowFilter, rowByteWidth, bytesPerPixel);                    int col = sCol;                    int spos=0;                    int pixel = 0;                    while (col < width) {                        if(wPixels !=null) {                            switch(combinedType) {                                case COLOR|ALPHA|(8<<3):                                    wPixels[col+rowOffset] =                                          ((rowByteBuffer[spos  ]&0xFF)<<16)                                        | ((rowByteBuffer[spos+1]&0xFF)<< 8)                                        | ((rowByteBuffer[spos+2]&0xFF)    )                                        | ((rowByteBuffer[spos+3]&0xFF)<<24);                                    spos+=4;                                    break;                                case COLOR|ALPHA|(16<<3):                                    wPixels[col+rowOffset] =                                          ((rowByteBuffer[spos  ]&0xFF)<<16)                                        | ((rowByteBuffer[spos+2]&0xFF)<< 8)                                        | ((rowByteBuffer[spos+4]&0xFF)    )                                        | ((rowByteBuffer[spos+6]&0xFF)<<24);                                    spos+=8;                                    break;                                case COLOR|(8<<3):                                    pixel =                                          ((rowByteBuffer[spos  ]&0xFF)<<16)                                        | ((rowByteBuffer[spos+1]&0xFF)<< 8)                                        | ((rowByteBuffer[spos+2]&0xFF)    );                                    if (pixel != transparentPixel) {                                        pixel |= 0xff000000;                                    }                                    wPixels[col+rowOffset] = pixel;                                    spos+=3;                                    break;                                case COLOR|(16<<3):                                    pixel =                                              ((rowByteBuffer[spos  ]&0xFF)<<16)                                            | ((rowByteBuffer[spos+2]&0xFF)<< 8)                                            | ((rowByteBuffer[spos+4]&0xFF)    );                                     boolean isTransparent = (transparentPixel_16 != null);                                    for (int i = 0; isTransparent && (i < 6); i++) {                                        isTransparent &=                                                (rowByteBuffer[spos + i] & 0xFF) == (transparentPixel_16[i] & 0xFF);                                    }                                    if (!isTransparent)  {                                        pixel |= 0xff000000;                                    }                                    wPixels[col+rowOffset] = pixel;                                    spos+=6;                                    break;                                case GRAY|ALPHA|(8<<3):                                    { int tx = rowByteBuffer[spos]&0xFF;                                      wPixels[col+rowOffset] =                                          (tx<<16)|(tx<<8)|tx                                        |((rowByteBuffer[spos+1]&0xFF)<<24); }                                    spos+=2;                                    break;                                case GRAY|ALPHA|(16<<3):                                    { int tx = rowByteBuffer[spos]&0xFF;                                      wPixels[col+rowOffset] =                                          (tx<<16)|(tx<<8)|tx                                        |((rowByteBuffer[spos+2]&0xFF)<<24); }                                    spos+=4;                                    break;                                default: throw new PNGException("illegal type/depth");                            }                        } else switch(bitDepth) {                            case 1:                                bPixels[col+rowOffset] =                                    (byte)((rowByteBuffer[spos>>3]>>(7-(spos&7)))&1);                                spos++;                                break;                            case 2:                                bPixels[col+rowOffset] =                                    (byte)((rowByteBuffer[spos>>2]>>((3-(spos&3))*2))&3);                                spos++;                                break;                            case 4:                                bPixels[col+rowOffset] =                                    (byte)((rowByteBuffer[spos>>1]>>((1-(spos&1))*4))&15);                                spos++;                                break;                            case 8: bPixels[col+rowOffset] = rowByteBuffer[spos++];                                break;                            case 16: bPixels[col+rowOffset] = rowByteBuffer[spos]; spos+=2;                                break;                            default: throw new PNGException("illegal type/depth");                        }                        /*visit (row, col,                            min (bHeight, height - row),                            min (bWidth, width - col)); */                        col += colInc;                    }                    if(interlaceMethod==0)                      if(wPixels!=null) {                        /* code changed. target not needed here                          t.setPixels(0,row,width,1,cm,wPixels,0,width);                          */                       // code added to make it work with ImageDecoder arch                        sendPixels(0,row,width,1,wPixels,0,width);                        // end of adding                      }                      else {                        /* code changed. target not needed here                           t.setPixels(0,row,width,1,cm,bPixels,0,width);                           */                        // code added to make it work with ImageDecoder arch                        sendPixels(0,row,width,1,bPixels,0,width);                        //end of adding                      }                    row += rowInc;                    rowOffset += rowInc*rowStride;                    byte[] T = rowByteBuffer;                    rowByteBuffer = prevRowByteBuffer;                    prevRowByteBuffer = T;                    firstRow = false;                }                if(interlaceMethod!=0)                  if(wPixels!=null) {                    /* code changed. target not needed here                       t.setPixels(0,0,width,height,cm,wPixels,0,width);                       */                    // code added to make it work with ImageDecoder arch                      sendPixels(0,0,width,height,wPixels,0,width);                      //end of adding                  }                  else {                     /* code changed. target not needed here                        t.setPixels(0,0,width,height,cm,bPixels,0,width);                        */

Lines 1–500 of 840.