最近,我开始对用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);