最近,我开始对用Java创建.ico文件或Windows图标文件感兴趣。这是我使用的当前代码。我从这里得到了文件格式规范http://en.wikipedia.org/wiki/ICO_%28file_format%29

    BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
    Graphics g = img.getGraphics();
    g.setColor(Color.GREEN);
    g.fillRect(0, 0, 16, 16);
    byte[] imgBytes = getImgBytes(img);
    int fileSize = imgBytes.length + 22;
    ByteBuffer bytes = ByteBuffer.allocate(fileSize);
    bytes.order(ByteOrder.LITTLE_ENDIAN);
    bytes.putShort((short) 0);//Reserved must be 0
    bytes.putShort((short) 1);//Image type
    bytes.putShort((short) 1);//Number of image in file
    bytes.put((byte) img.getWidth());//image width
    bytes.put((byte) img.getHeight());//image height
    bytes.put((byte) 0);//number of colors in color palette
    bytes.put((byte) 0);//reserved must be 0
    bytes.putShort((short) 0);//color planes
    bytes.putShort((short) 0);//bits per pixel
    bytes.putInt(imgBytes.length);//image size
    bytes.putInt(22);//image offset
    bytes.put(imgBytes);
    byte[] result = bytes.array();
    FileOutputStream fos = new FileOutputStream("C://Users//Owner//Desktop//picture.ico");
    fos.write(result);
    fos.close();
    fos.flush();

private static byte[] getImgBytes(BufferedImage img) throws IOException
{
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ImageIO.write(img, "png", bos);
    return bos.toByteArray();
}

问题是Windows似乎无法打开图像,当我尝试使用Windows Photo Gallery打开图像时出现错误。但是,当我尝试使用gimp打开图像时,图像可以正常打开。我究竟做错了什么。我觉得我在弄乱文件头中的内容。编辑:即使是桌面上的陌生人,图片看起来也正确,但是当我尝试打开它时却并非如此。

在我的桌面上,图像看起来像这样

当我尝试在Windows照片库中打开它时,显示此错误

png尝试失败后,我尝试使用位图图像进行尝试,这是我的新代码
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import javax.imageio.ImageIO;

public class IconWriter
{
    public static void main(String[] args) throws HeadlessException, AWTException, IOException
    {
        BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
        Graphics g = img.getGraphics();
        g.setColor(Color.GREEN);
        g.fillRect(0, 0, 16, 16);
        byte[] imgBytes = getImgBytes(img);
        int fileSize = imgBytes.length + 22;
        ByteBuffer bytes = ByteBuffer.allocate(fileSize);
        bytes.order(ByteOrder.LITTLE_ENDIAN);
        bytes.putShort((short) 0);//Reserved must be 0
        bytes.putShort((short) 1);//Image type
        bytes.putShort((short) 1);//Number of images in file
        bytes.put((byte) img.getWidth());//image width
        bytes.put((byte) img.getHeight());//image height
        bytes.put((byte) 0);//number of colors in color palette
        bytes.put((byte) 0);//reserved must be 0
        bytes.putShort((short) 0);//color planes
        bytes.putShort((short) 0);//bits per pixel
        bytes.putInt(imgBytes.length);//image size
        bytes.putInt(22);//image offset
        bytes.put(imgBytes);
        byte[] result = bytes.array();
        FileOutputStream fos = new FileOutputStream("C://Users//Owner//Desktop//hi.ico");
        fos.write(result);
        fos.close();
        fos.flush();
    }

    private static byte[] getImgBytes(BufferedImage img) throws IOException
    {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ImageIO.write(img, "bmp", bos);
        byte[] bytes = bos.toByteArray();
        return Arrays.copyOfRange(bytes, 14, bytes.length);
    }
}

现在,当我尝试在照片库中打开图像时,图像看起来像这样,我不知道为什么它现在不起作用,尤其是为什么出现怪异的线条,尽管我怀疑它必须与ico中的color planes属性有关。图片标题。

最佳答案

实际上,规范中提到了您遇到的问题(在Wikipedia上)。
引用:



那很复杂。

创建32位图像->失败

因此,以上引用可能使您想到:“哦,我只需要使图像变为32位而不是24位”即可。不幸的是,这行不通。好吧,实际上存在一种32位BMP格式。但是最后8位没有真正使用,因为BMP文件并不真正支持透明性。

因此,您可能会想使用其他图像类型:INT_ARGB_PRE,它使用32位色深。但是,当您尝试使用ImageIO类保存它时,您会注意到没有任何 react 。 流的内容将为null

BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB_PRE);
ImageIO.write(img, "bmp", bos);

替代解决方案:image4j
ImageIO无法处理32位图像,但是还有其他库可以解决这个问题。 image4J库可以保存32位bmp文件。但是我的猜测是由于某种原因您不想使用此库。 (因为image4J具有内置的ICO创建支持,所以使用image4j会使上面的大部分代码毫无意义。)

第二种选择:创建移位的24位图像->工作

因此,让我们再来看一下维基百科关于


因此,第二种解决方案是创建两倍于原始大小的图像。实际上,您只需要用下面的那个替换getImageBytes函数即可。如上所述,在代码另一部分中指定的ICONDIRENTRY header 保留了原始图像的高度。
  private static byte[] getImgBytes(BufferedImage img) throws IOException
  {
    // create a new image, with 2x the original height.
    BufferedImage img2 = new BufferedImage(img.getWidth(), img.getHeight()*2, BufferedImage.TYPE_INT_RGB);

    // copy paste the pixels, but move them half the height.
    Raster sourceRaster = img.getRaster();
    WritableRaster destinationRaster = img2.getRaster();
    destinationRaster.setRect(0, img.getHeight(), sourceRaster);

    // save the new image to BMP format.
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ImageIO.write(img2, "bmp", bos);

    // strip the first 14 bytes (contains the bitmap-file-header)
    // the next 40 bytes contains the DIB header which we still need.
    // the pixel data follows until the end of the file.
    byte[] bytes = bos.toByteArray();
    return Arrays.copyOfRange(bytes, 14, bytes.length);
  }

我建议使用以下 header :
ByteBuffer bytes = ByteBuffer.allocate(fileSize);
bytes.order(ByteOrder.LITTLE_ENDIAN);

bytes.putShort((short) 0);
bytes.putShort((short) 1);
bytes.putShort((short) 1);
bytes.put((byte) img.getWidth());
bytes.put((byte) img.getHeight()); //no need to multiply
bytes.put((byte) img.getColorModel().getNumColorComponents()); //the pallet size
bytes.put((byte) 0);
bytes.putShort((short) 1); //should be 1
bytes.putShort((short) img.getColorModel().getPixelSize()); //bits per pixel
bytes.putInt(imgBytes.length);
bytes.putInt(22);
bytes.put(imgBytes);

09-09 22:25
查看更多