首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >编写ico文件

编写ico文件
EN

Stack Overflow用户
提问于 2013-08-29 21:44:14
回答 5查看 4.1K关注 0票数 10

最近,我开始对在java中创建.ico文件或windows图标文件感兴趣。这是我使用的当前代码。我从这里得到了文件格式规范,format%29

代码语言:javascript
复制
    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图片库打开图像时出现了错误。但是,当我尝试使用gimp打开图像时,图像会很好地打开。我做错什么了。我觉得我搞砸了文件头上的东西。编辑:更奇怪的是,在桌面上,图片看上去是对的,只是当我试图打开它的时候,情况就不一样了。

在我的桌面上,图像如下所示

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

在png尝试失败之后,我用位图图像代替了它,下面是我的新代码

代码语言:javascript
复制
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图像标题中的颜色平面属性有关。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-09-07 13:39:20

实际上,你遇到的问题在规范中提到了(在维基百科)。引用:

小于32位的彩色 depth6的图像遵循一种特定的格式:图像被编码为由一个颜色掩码( "XOR掩码“)和一个不透明掩码(”和掩码“)组成的单个图像。

这很复杂。

创建32位映像->失败

所以,上面的引语可能会让你想:“哦,我只需要做32位而不是24位的图像”,作为一种解决办法。不幸的是这行不通。实际上有32位BMP格式。但是最后的8位并没有被真正使用,因为BMP文件并不真正支持透明性。

因此,您可能会尝试使用不同的图像类型:INT_ARGB_PRE,它使用32位的颜色深度。但是,当您尝试用ImageIO类保存它时,您会注意到什么都没有发生。流的内容将是null

代码语言:javascript
复制
BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB_PRE);
ImageIO.write(img, "bmp", bos);

替代解决方案: image4j

ImageIO不能处理32位图像,但是还有其他库可以做到这一点。image4J库可以保存32位bmp文件.但我的猜测是,出于某种原因,你不想使用这个库。(使用image4J将使您的大部分代码没有意义,因为image4j已经内置了ICO创建支持)。

第二个选项:创建一个移位的24位图像->工作。

那么,让我们再看一下维基百科对<32位BMP数据的看法。

ICO/CUR文件的ICONDIRENTRY结构中的图像的高度与预期的图像尺寸(在掩码合成之后)的高度相同,而BMP头中的高度则与两个掩码图像的高度相结合(在组合之前)。因此,掩码必须具有相同的尺寸,头中指定的高度必须是ICONDIRENTRY结构中指定高度的两倍。

因此,第二个解决方案是创建一个两倍于原始大小的图像。实际上,您只需要用下面的一个替换您的getImageBytes函数。如前所述,代码的另一部分中指定的ICONDIRENTRY标头保持原始图像高度。

代码语言:javascript
复制
  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);
  }

我建议按以下方式使用标题:

代码语言:javascript
复制
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);
票数 3
EN

Stack Overflow用户

发布于 2013-09-04 16:28:14

奇怪的…但是:使BMP图片比所需的图标高两倍。保持已声明的图标大小在ICO标题中,与以前一样,只有图片应该更高。然后保持区域(0,0)-(16,16)黑色(它定义透明度,但我不知道它是如何编码的,所有不透明的作品都是黑色的)。在区域(0,16)-(16,32)中的BufferedImage中绘制所需的内容。换句话说,将高度的一半添加到所有像素坐标中。

请注意,Windows桌面可能会缓存图标并拒绝在桌面上更新它们。如果有疑问,请通过另一个资源管理器窗口打开桌面文件夹并在那里执行“更新”。

代码语言:javascript
复制
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
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 IOException
    {
      // note the double height
        BufferedImage img = new BufferedImage(16, 32, BufferedImage.TYPE_INT_RGB);
        Graphics g = img.getGraphics();
        g.setColor(Color.GREEN);
        g.fillRect(0, 16, 16, 16);// added 16 to y coordinate
        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()>>1));//image height, half the BMP 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(System.getProperty("user.home")+"\\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);
    }
}
票数 5
EN

Stack Overflow用户

发布于 2013-09-01 01:49:54

我认为bytes.putShort((short) 0);//bits per pixel应该改为32,而不是0。

如果你在把这个值改为32后得到你编辑的那张小照片,那么我要说的是,再想一想,它可能是16。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18521470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档