Recently i have become interested in creating .ico file or windows icon files in java. This is the current code i use. I have gotten the file format specs from here 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();
}
The problem is that windows doesn't seem to be able to open the image, giving an error when i try to open the image using Windows Photo Gallery. However when i try to open the image using gimp the image opens fine. What am i doing wrong. I feel like i am messing up something in the file header. Edit: Even stranger on the desktop the picture looks right, just not when i try to open it.
On my desktop the image looks like this
When i try to open it in Windows Photo Gallery it displays this error
After having failed with the png attempt i tried it with bitmap image instead, here is my new code
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);
}
}
now when i try to open my image in photo gallery the image looks like this i have no idea why it isn't working now and especially why the weird lines are appearing, although i suspect it has to with the color planes attribute in the ico image header.
Actually, the problem you are having is mentioned in the specs (at wikipedia). Quote:
That's very complicated.
Creating a 32-bit image -> fails
So, the quote above might make you think: "Oh, I just have to make the image 32-bit instead of 24-bit", as a workaround. Unfortunately that won't work. Well, actually there exists a 32-bit BMP format. But the last 8 bits are not really used, because BMP files do not really support transparency.
So, you could get tempted to use a different image type:
INT_ARGB_PRE
which uses a 32-bit color depth. But as soon as you try to save it with theImageIO
class, you will notice that nothing happens. The content of the stream will benull
.Alternative solution: image4j
ImageIO
cannot handle 32-bit images, but there are other libraries that can do the trick. Theimage4J
libs can save 32-bit bmp files. But my guess is that for some reason you do not want to use this library. (Usingimage4J
would make most of your code above pointless, becauseimage4j
has built-in ICO creation support).Second option: creating a shifted 24-bit image -> works
So, let's take a second look at what wikipedia says about < 32-bit BMP data.
So, the second solution is to create an image that is twice the original size. And you actually only have to replace your
getImageBytes
function for that, with the one below. As mentioned above theICONDIRENTRY
header specified in the other part of your code keeps the original image height.I propose to use the headers as follows: