由于宽度和高度为0

时间:2018-05-25 11:11:33

标签: java java-8 icons ico

我正在尝试阅读包含bitcount = 8的两个ico图像的ico文件。我知道ICONDIRENTRY格式(https://msdn.microsoft.com/en-us/library/ms997538.aspx),这段代码主要与几个特定的​​ico文件不同.Below是我的代码 -

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.io.FileInputStream;
import java.nio.ByteOrder;
import java.util.ArrayList;

/**
 * Created by dsomesh8 on 5/25/2018.
 */
public class Program {
    private static ArrayList<IconDirEntry> iconDirEntries;


    private static final byte SEED = -67;
    private static final byte SEED2 = 107;
    private static final String HEADER = "@OB@";
    private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static ImageInputStream in;


    public  static void main(String[] args) {
        FileInputStream fis = null;
        try {
            //C:\Users\dsomesh8\Downloads\Logs\test\Tool.ico
            //C:\Users\dsomesh8\Downloads\icons\zen.ico
            String filePath = "C:\\Users\\dsomesh8\\Downloads\\Logs\\test\\Tool.ico";
            //String filePath="C:\\Users\\tdivya\\Downloads\\test.ico";
            fis = new FileInputStream(filePath);
            in = ImageIO.createImageInputStream(fis);
            ArrayList<IconDirEntry> list=decodeIcon(in);
            IconImage nweIcon=new IconImage(list.get(0));
            //iconDirEntries = new ArrayList<IconDirEntry>();
            //boolean res = ;
        } catch (java.io.FileNotFoundException fnfe) {
            //WebLogger.debug("Input icon file " + filePath + " is missing");
        } catch (java.io.IOException ioe) {
            //WebLogger.debug("IO Exception reading the icon file " + filePath);
        }
    }

    private static ArrayList<IconDirEntry> decodeIcon(ImageInputStream in)
    {
        try
        {
            in.setByteOrder(ByteOrder.LITTLE_ENDIAN);

            in.readShort();             // idReserved field

            if(in.readShort() != 1)    // idType field
                return null;

            int imgCount = in.readShort();  //No of icon entries

            iconDirEntries = new ArrayList<IconDirEntry>();
            System.out.println(imgCount);
            for(int i = 0; i < imgCount; i++)
            {

                IconDirEntry dirEntry = new IconDirEntry(in);
                System.out.println(dirEntry.toString());
                iconDirEntries.add(dirEntry);
            }



        }
        catch(java.io.IOException ioe)
        {
            // WebLogger.debug("IOException reading the reserved field of the icon");
            return null;
        }
        return  iconDirEntries;
    }
}


import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;


/*
typdef struct
{
   BITMAPINFOHEADER   icHeader;   // DIB header
   RGBQUAD         icColors[1];   // Color table
   BYTE            icXOR[1];      // DIB bits for XOR mask
   BYTE            icAND[1];      // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;

typedef struct tagBITMAPINFOHEADER{
  DWORD  biSize;
  LONG   biWidth;
  LONG   biHeight;
  WORD   biPlanes;
  WORD   biBitCount;
  DWORD  biCompression;
  DWORD  biSizeImage;
  LONG   biXPelsPerMeter;
  LONG   biYPelsPerMeter;
  DWORD  biClrUsed;
  DWORD  biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

typedef struct tagRGBQUAD {
  BYTE    rgbBlue;
  BYTE    rgbGreen;
  BYTE    rgbRed;
  BYTE    rgbReserved;
} RGBQUAD;
*/


public class IconImage
{
    private int biSize;
    private int biWidth;
    private int biHeight;
    private int biPlanes;
    private int biBitCount;
    private int biCompression;
    private int biSizeImage;
    private int biXPelsPerMeter;
    private int biYPelsPerMeter;
    private int biClrUsed;
    private int biClrImportant;

    private byte[] rgbQuad;
    private byte[] icXOR;
    private byte[] icAND;

    private RGBQuad[] colors;
    private byte[] andMask;
    private byte[] xorMask;

    private IconDirEntry entry;
    private ImageInputStream iis;

    public IconImage(IconDirEntry entry)
    {
        this.entry = entry;

        try
        {
            iis = ImageIO.createImageInputStream(new ByteArrayInputStream(entry.getImageData()));
            iis.setByteOrder(java.nio.ByteOrder.LITTLE_ENDIAN);

            biSize = iis.readInt();
            biWidth = iis.readInt();
            biHeight = iis.readInt();
            biPlanes = iis.readShort();
            biBitCount = iis.readShort();

            biCompression = iis.readInt();
            biSizeImage = iis.readInt();
            biXPelsPerMeter = iis.readInt();
            biYPelsPerMeter = iis.readInt();
            biClrUsed = iis.readInt();
            biClrImportant = iis.readInt();

            if(entry.getBitCount() <= 8)
            {
                int nColors = (int)(Math.pow(2, biBitCount));
                colors = new RGBQuad[nColors]; //color table specifying colors uses in the image

                for(int i = 0; i < colors.length; i++)
                {
                    colors[i] = new RGBQuad(iis);
                }

                int bitsPerPixel = biBitCount;
                int pixelsPerByte = 8/bitsPerPixel;
                int nPixels = biWidth*biHeight/2; //biHeight is twice of actual height
                int nBytes = nPixels/pixelsPerByte;

                xorMask = new byte[nBytes];
                for(int i = 0; i < nBytes; i++)
                {
                    xorMask[i] = (byte)iis.readUnsignedByte();
                }

                int paddedWidth = 0;
                if(biWidth <= 32)
                    paddedWidth = 32;
                else
                {
                    int rem = biWidth%32;
                    if(rem == 0)
                        paddedWidth = biWidth;
                    else
                        paddedWidth = (biWidth/32 + 1)*32; //Round off to the next multiple of 32
                }

                int len = paddedWidth*(biHeight/2)/8;
                //AND mask is a monochrome DIB, with a color depth of 1 bpp
                andMask = new byte[len];
                for(int i = 0; i < len; i++)
                {
                    andMask[i] = (byte)iis.readUnsignedByte();
                }
            }

        }
        catch(Exception ioe)
        {
            System.out.println("Exception while reading image details for icon entry");
        }

    }

    public int[] getPixelValues()
    {
        int nRows = entry.getHeight();
        int nCols = entry.getWidth();
        int bpp = entry.getBitCount()/8; //Bytes per pixel
        int[] pixelValues = new int[nRows*nCols];

        for(int row = 0; row < nRows; row++)
        {

            byte[] rowData = new byte[nCols*bpp];
            try
            {
                iis.readFully(rowData);
            }
            catch(Exception e)
            {
                System.out.println("Exception reading the image data for this entry!!!");
            }

            int curRow = nRows - row; //Moving upwards starting from the last row
            int pos = (curRow - 1)*nCols; //Index of first pixel at current row

            int iByte = 0; //Iterator for each byte

            for(int col = 0; col < nCols; col++)
            {
                int pixelValue = 0;

                pixelValue = (rowData[iByte++] & 0xFF);

                if(bpp > 1)
                    pixelValue += ((rowData[iByte++] & 0xFF) << 8);

                if(bpp > 2)
                    pixelValue += ((rowData[iByte++] & 0xFF) << 16);

                if(bpp > 3)
                    pixelValue += ((rowData[iByte++] & 0xFF) << 24);
                else
                {
                    //if (pixelValue == 0)
                    pixelValue += ((255 & 0xFF) << 24);
                }

                pixelValues[pos] = pixelValue;
                pos++;
            }
        }

        return pixelValues;
    }

    public BufferedImage getIconGraphics()
    {
        BufferedImage buffImg = new BufferedImage(entry.getWidth(), entry.getHeight(), BufferedImage.TYPE_INT_ARGB);
        final Color TRANSPARENT = new Color(0, 0, 0, 0);

        Graphics2D g = buffImg.createGraphics();
        for(int y = biHeight/2 - 1; y >= 0; y--)
        {
            for(int x = 0; x < biWidth; x++)
            {
                if(isTransparent(x, y))
                    g.setColor(TRANSPARENT);
                else
                    g.setColor(getRGB(x, y));

                g.fillRect(x, entry.getHeight() - y - 1, 1, 1);
            }

        }

        return buffImg;
    }

    private boolean isTransparent(int x, int y)
    {
        int paddedWidth = 0;
        if(biWidth <= 32)
            paddedWidth = 32;
        else
        {
            int rem = biWidth%32;
            if(rem == 0)
                paddedWidth = biWidth;
            else
                paddedWidth = (biWidth/32 + 1)*32; //Round off to the next multiple of 32
        }

        int pixelIndex = (paddedWidth*y) + x;
        int andByteIndex = pixelIndex/8;
        int andByte = andMask[andByteIndex];
        int pos = x%8; //position of bit in the byte, for pixel x,y
        int nRightShift = 8 - (pos + 1); //Right shift needed to get the bit to LSB; increment of 1 since x starts from 0
        int pixelBit = andByte >> nRightShift;
        int andMask = pixelBit & 1;
        return (andMask == 1);
    }

    private Color getRGB(int x, int y)
    {
        int pixelIndex = (biWidth*y) + x;
        int bitsPerPixel = biBitCount;
        int pixelsPerByte = 8/bitsPerPixel;
        int xorByteIndex = pixelIndex/pixelsPerByte;

        int shift = ((pixelsPerByte - (x%pixelsPerByte) - 1)*biBitCount);
        int colIdx = (xorMask[xorByteIndex] >> shift) & ((1 << biBitCount) - 1);

        int b = colors[colIdx].getBlue();
        int g = colors[colIdx].getGreen();
        int r = colors[colIdx].getRed();

        return new Color(r, g, b);
    }

}


import com.sun.imageio.plugins.common.ReaderUtil;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;



/*
typedef struct
{
    BYTE        bWidth;          // Width, in pixels, of the image
    BYTE        bHeight;         // Height, in pixels, of the image
    BYTE        bColorCount;     // Number of colors in image (0 if >=8bpp)
    BYTE        bReserved;       // Reserved ( must be 0)
    WORD        wPlanes;         // Color Planes
    WORD        wBitCount;       // Bits per pixel
    DWORD       dwBytesInRes;    // How many bytes in this resource?
    DWORD       dwImageOffset;   // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
*/

public class IconDirEntry
{
    private short width;
    private short height;
    private short colorCount;
    private short reserved;

    private int planes;
    private int bitCount;
    private int bytesInResource;
    private int imageOffset;

    private byte[] imgData;

    public IconDirEntry(ImageInputStream in)
    {
        try
        {
            //System.out.println("canDecodeInput-"+canDecodeInput(in));
           // bitCount = readBitCountFromImageData(imgData);
            width = (short)in.readUnsignedByte();

            height = (short)in.readUnsignedByte();
            colorCount = new Byte(in.readByte()).shortValue();
            reserved = new Byte(in.readByte()).shortValue();

            planes = in.readShort();
            bitCount = in.readShort();
            bytesInResource = in.readInt();
            imageOffset = in.readInt();

            /*
            System.out.println("val : " + width);
            System.out.println("val : " + height);
            System.out.println("val : " + colorCount);
            System.out.println("val : " + reserved);

            System.out.println("val : " + planes);
            System.out.println("val : " + bitCount);
            System.out.println("val : " + bytesInResource);
            System.out.println("val : " + imageOffset);
            System.out.println("\n");
            */

            in.mark();

            long curPos = in.getStreamPosition();
            int nBytesToSkip = imageOffset - (int)curPos;
            in.skipBytes(nBytesToSkip);

            imgData = new byte[bytesInResource];
            try
            {
                in.read(imgData);
            }
            finally
            {
                in.reset();
            }

            // Certain icons will not specify the bitCount at the icon entry level.
            // For such cases, read the bitCount from the image data
            if(bitCount == 0 && imageOffset > 0)
                bitCount = readBitCountFromImageData(imgData);

        }
        catch(Exception e)
        {
            System.out.println("Exception reading icon entry");
        }
    }


    /*
     * Image data structure:
            typdef struct
            {
               BITMAPINFOHEADER   icHeader;   // DIB header
               RGBQUAD         icColors[1];   // Color table
               BYTE            icXOR[1];      // DIB bits for XOR mask
               BYTE            icAND[1];      // DIB bits for AND mask
            } ICONIMAGE, *LPICONIMAGE;

            typedef struct tagBITMAPINFOHEADER{
              DWORD  biSize;
              LONG   biWidth;
              LONG   biHeight;
              WORD   biPlanes;
              WORD   biBitCount;
              DWORD  biCompression;
              DWORD  biSizeImage;
              LONG   biXPelsPerMeter;
              LONG   biYPelsPerMeter;
              DWORD  biClrUsed;
              DWORD  biClrImportant;
            } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
    *
    * Read biBitCount
    */
    private int readBitCountFromImageData(byte[] imgData) throws IOException
    {
        ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(imgData));
        iis.setByteOrder(java.nio.ByteOrder.LITTLE_ENDIAN);

        // These many number of bytes can actually be skipped. Reading for code clarity.
        iis.readInt();   // biSize
        iis.readInt();   // biWidth
        iis.readInt();   // biHeight
        iis.readShort(); // biPlanes

        int biBitCount = iis.readShort();

        return biBitCount;
    }


    public short getWidth()
    {
        return width;
    }

    public short getHeight()
    {
        return height;
    }

    public int getBitCount()
    {
        return bitCount;
    }

    public byte[] getImageData()
    {
        return imgData;
    }

}

有问题的区域是
iis = ImageIO.createImageInputStream(new ByteArrayInputStream(entry.getImageData()));

发布这个我得到的位数是一个大整数,虽然实际的位数是8.所以当创建一个大尺寸的数组会抛出以下异常 -

&#34; java.lang.OutOfMemoryError:请求的数组大小超过VM限制&#34;

失败的ico文件是https://www.dropbox.com/s/euh52s0vc2s2ryf/Tool.ico?dl=0

1 个答案:

答案 0 :(得分:1)

对于此图标,图像数据不符合tagBITMAPINFOHEADER结构。相反,它们是嵌入的PNG图像,您可以在第一个单词中识别,这个单词不是常规大小(跟随tagBITMAPINFOHEADER结构时会出现),而是PNG图像的神奇单词。

您可以通过将IconImage的构造函数的开头更改为

来验证这一点
public IconImage(IconDirEntry entry)
{
    this.entry = entry;
    try
    {
        final ByteArrayInputStream bais = new ByteArrayInputStream(entry.getImageData());
        bais.mark(4);
        iis = ImageIO.createImageInputStream(bais);
        iis.setByteOrder(java.nio.ByteOrder.LITTLE_ENDIAN);
        biSize = iis.readInt();
        if(biSize == 0x474e5089) { //PNG instead of tagBITMAPINFOHEADER)
            bais.reset();
            BufferedImage bi = ImageIO.read(bais);
            System.out.println("read embedded PNG "+bi.getWidth()+" x "+bi.getHeight());
            return;
        }
…

神奇的单词是…PNG,第一个字节是0x89,但是当你将它作为一个小的int值读取时,顺序已被反转,所以它的(('G'<<24)|('N'<<16)|('P'<<8)|0x89)

我让你重新构建代码,以便使用通用接口处理这两种情况......

相关问题