# HG changeset patch # User Jiri Vanek # Date 1447347827 -3600 # Node ID a83c72313001aa3e2283b867eb14737c3107ca9e # Parent 104317f4809608d67b8dd6c6750ff40afebb2858 Added parser to read ico images diff -r 104317f48096 -r a83c72313001 ChangeLog --- a/ChangeLog Thu Nov 12 17:19:20 2015 +0100 +++ b/ChangeLog Thu Nov 12 18:03:47 2015 +0100 @@ -1,3 +1,18 @@ +2015-11-12 Jiri Vanek + + Added parser to read ico images + * netx/net/sourceforge/jnlp/controlpanel/desktopintegrationeditor/JListUtils.java: + When reading images, also ico is attempted to parse. + * netx/net/sourceforge/jnlp/tools/ico/impl/IcoException.java: new class, + exception for special cases in ico parsing + * netx/net/sourceforge/jnlp/tools/ico/impl/IcoHeader.java: new class, parser + and holder of parsed information of header of ico file + * netx/net/sourceforge/jnlp/tools/ico/impl/IcoHeaderEntry.java: new class, + parser and holder of parsed information of headers of individual images stored + in header of ico file + * netx/net/sourceforge/jnlp/tools/ico/impl/ImageInputStreamIco.java: parser + of icon files from ImageInputStream + 2015-11-12 Jiri Vanek Added desktop integration dialog diff -r 104317f48096 -r a83c72313001 netx/net/sourceforge/jnlp/controlpanel/desktopintegrationeditor/JListUtils.java --- a/netx/net/sourceforge/jnlp/controlpanel/desktopintegrationeditor/JListUtils.java Thu Nov 12 17:19:20 2015 +0100 +++ b/netx/net/sourceforge/jnlp/controlpanel/desktopintegrationeditor/JListUtils.java Thu Nov 12 18:03:47 2015 +0100 @@ -40,11 +40,13 @@ import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileInputStream; import java.io.FilenameFilter; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import javax.imageio.ImageIO; +import javax.imageio.stream.ImageInputStream; import javax.swing.DefaultListCellRenderer; import javax.swing.Icon; import javax.swing.ImageIcon; @@ -54,6 +56,7 @@ import javax.swing.ListModel; import javax.swing.event.ListDataListener; import net.sourceforge.jnlp.config.InfrastructureFileDescriptor; +import net.sourceforge.jnlp.tools.ico.impl.ImageInputStreamIco; import net.sourceforge.jnlp.util.XDesktopEntry; public class JListUtils { @@ -357,7 +360,17 @@ */ private static ImageIcon createImageIcon(File f, String description) { try { - BufferedImage i = ImageIO.read(f); + BufferedImage i; + try(ImageInputStream is = ImageIO.createImageInputStream(new FileInputStream(f))) { + ImageInputStreamIco ico = new ImageInputStreamIco(is); + i = ico.getImage(0); + } catch (Exception eex) { + //not ico + i = null; + } + if (i == null) { + i = ImageIO.read(f); + } return new ImageIcon(i.getScaledInstance(50, 50, Image.SCALE_SMOOTH)); } catch (Exception ex) { //not worthy to log it. No image is there and so be it. diff -r 104317f48096 -r a83c72313001 netx/net/sourceforge/jnlp/tools/ico/impl/IcoException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/tools/ico/impl/IcoException.java Thu Nov 12 18:03:47 2015 +0100 @@ -0,0 +1,46 @@ +/* + Copyright (C) 2015 Red Hat, Inc. + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ +package net.sourceforge.jnlp.tools.ico.impl; + + +class IcoException extends Exception { + + public IcoException(String string) { + super(string); + } + +} diff -r 104317f48096 -r a83c72313001 netx/net/sourceforge/jnlp/tools/ico/impl/IcoHeader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/tools/ico/impl/IcoHeader.java Thu Nov 12 18:03:47 2015 +0100 @@ -0,0 +1,87 @@ +/* + Copyright (C) 2015 Red Hat, Inc. + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ +package net.sourceforge.jnlp.tools.ico.impl; + +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; +import javax.imageio.stream.ImageInputStream; + +/** + * http://www.daubnet.com/en/file-format-ico + */ +public class IcoHeader { + + private final int reserved; //0 + private final int type; //should be 1 (0 is for cusrsor?) + final int countOfIcons; + final List entries; //size 16*countOfIcons bytes + private final ByteOrder originalOrder; + + public IcoHeader(ImageInputStream src) throws IOException, IcoException { + originalOrder = src.getByteOrder(); + try { + src.setByteOrder(ByteOrder.LITTLE_ENDIAN); + reserved = src.readUnsignedShort(); + type = src.readUnsignedShort(); + isIco(); + countOfIcons = src.readUnsignedShort(); + entries = new ArrayList<>(countOfIcons); + for (int x = 0; x < countOfIcons; x++) { + entries.add(new IcoHeaderEntry(src)); + } + } finally { + src.setByteOrder(originalOrder); + } + } + + private void isIco() throws IcoException { + if (reserved != 0 || (type != 1)) { + throw new IcoException("Invalid header. Expected 0 and 1, got " + reserved + " and " + type); + } + } + + public List getEntries() { + return entries; + } + + public int getCountOfIcons() { + return countOfIcons; + } + +} diff -r 104317f48096 -r a83c72313001 netx/net/sourceforge/jnlp/tools/ico/impl/IcoHeaderEntry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/tools/ico/impl/IcoHeaderEntry.java Thu Nov 12 18:03:47 2015 +0100 @@ -0,0 +1,143 @@ +package net.sourceforge.jnlp.tools.ico.impl; + +/* + Copyright (C) 2015 Red Hat, Inc. + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ +import java.io.IOException; +import javax.imageio.stream.ImageInputStream; + +public class IcoHeaderEntry { + + private int width; + private int height; + private int colorCount; + private final int reserved; + private final int planes; //should be 1 but I met quite a lot of 0 + private final int bitCount; + private final int sizeInBytes; // InfoHeader + ANDbitmap + XORbitmap + private final int fileOffset; //FilePos, where InfoHeader starts + + public IcoHeaderEntry(ImageInputStream src) throws IOException, IcoException { + width = src.read(); + height = src.read(); + colorCount = src.read(); + // sentence "Number of Colors (2,16, 0=256) " is form doubnet + //unluckily, both 0==0 and 0==256 does exists + //going with doubnet by default + if (colorCount == 0) { + colorCount = 256; + } + reserved = src.read(); + planes = src.readUnsignedShort(); + isIcoHeader(); + bitCount = src.readUnsignedShort(); + sizeInBytes = src.readInt(); + fileOffset = src.readInt(); + } + + private IcoHeaderEntry(int width, int height, int colorCount, int planes, int bitCount, int sizeInBytes, int fileOffset) { + this.width = width; + this.height = height; + this.colorCount = colorCount; + this.reserved = 0; + this.planes = planes; + this.bitCount = bitCount; + this.sizeInBytes = sizeInBytes; + this.fileOffset = fileOffset; + } + + private IcoHeaderEntry provideMonochromeHeader() { + //each bit in byte stores 8 pixels values + return new IcoHeaderEntry(width, height, 1, planes, 1, width * height / 8, fileOffset + sizeInBytes); + } + + private void isIcoHeader() throws IcoException { + if (reserved != 0 || (planes != 1 && planes != 0)) { + throw new IcoException("Invalid header. Expected 0 and 1(0?), got " + reserved + " and " + planes); + } + } + + /** + * @return the colorCount + */ + int getColorCount() { + return colorCount; + } + + /** + * @param colorCount the colorCount to set + */ + void setColorCount(int colorCount) { + this.colorCount = colorCount; + } + + /** + * @return the width + */ + public int getWidth() { + return width; + } + + /** + * @param width the width to set + */ + void setWidth(int width) { + this.width = width; + } + + /** + * @return the height + */ + public int getHeight() { + return height; + } + + /** + * @param height the height to set + */ + void setHeight(int height) { + this.height = height; + } + + int getSizeInBytes() { + return sizeInBytes; + } + + int getFileOffset() { + return fileOffset; + } + +} diff -r 104317f48096 -r a83c72313001 netx/net/sourceforge/jnlp/tools/ico/impl/ImageInputStreamIco.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/tools/ico/impl/ImageInputStreamIco.java Thu Nov 12 18:03:47 2015 +0100 @@ -0,0 +1,170 @@ +/* + Copyright (C) 2015 Red Hat, Inc. + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ +package net.sourceforge.jnlp.tools.ico.impl; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageInputStream; + +public class ImageInputStreamIco { + + private final IcoHeader header; + private final List images; //size 16*countOfIcons bytes + + public IcoHeader getHeader() { + return header; + } + + public BufferedImage getImage(int i) { + return images.get(i); + } + + public ImageInputStreamIco(ImageInputStream src) throws IOException, IcoException { + this.header = new IcoHeader(src); + images = new ArrayList<>(header.countOfIcons); + for (IcoHeaderEntry e : header.entries) { + BufferedImage image = readImage(e, src); + images.add(image); + } + } + + private static void readMask(IcoHeaderEntry e, ImageInputStream src1) throws IOException { + //acording to spec, behind img bytes, should be another bytes, with AND bitmap.Hoewer, I had not found them. + //however, that means, that transaprency is lost... But bit offsets mathces.. so... + //IcoHeader.IcoHeaderEntry q = e.provideMonochromeHeader(); + //src1.getStreamPosition(); + //byte[] mask = new byte[q.sizeInBytes]; + //src1.readFully(mask); + } + + private static BufferedImage readImage(IcoHeaderEntry e, ImageInputStream src1) throws IOException { + BufferedImage image; + byte[] img = new byte[e.getSizeInBytes()]; + if (src1.getStreamPosition() != e.getFileOffset()) { + //I had never seen this thrown, Still, is it worthy to tempt it, or rather read and die later? + //throw new IOException("Stream position do nto match expected position. Bmp(or png) will read wrongly"); + } + src1.readFully(img); + try { + image = parse(img, e); + //readMask(e, src1); + } catch (EOFException ex) { + //some icons do not honour that 0 is 256. Retrying + if (e.getColorCount() != 0) { + e.setColorCount(0); + image = parse(img, e); + //readMask(e, src1); + } else { + throw ex; + } + } + return image; + } + + private static BufferedImage parse(byte[] img, IcoHeaderEntry e) throws IOException { + ByteArrayInputStream bis = new ByteArrayInputStream(img); + BufferedImage image = null; + try { + image = ImageIO.read(bis); + } catch (Exception ex) { + //not png + } + if (image != null) { + fixSizesInHeader(e, image); + return image; + } + //bmp + img = prefixByFakeHeader(img, e); + bis = new ByteArrayInputStream(img); + //dont try catch this one. you will break it + image = ImageIO.read(bis); + + return image; + } + + private static void fixSizesInHeader(IcoHeaderEntry e, BufferedImage image) { + //may happen for png + if (e.getWidth() == 0) { + e.setWidth(image.getWidth()); + } + if (e.getHeight() == 0) { + e.setHeight(image.getHeight()); + } + } + + private static byte[] prefixByFakeHeader(final byte[] origArray, IcoHeaderEntry e) { + int fakingArray = 14; + byte[] img = new byte[fakingArray + e.getSizeInBytes()]; + for (int i = 0; i < origArray.length; i++) { + byte p = origArray[i]; + img[i + 14] = p; + + } + //fake header + //http://www.daubnet.com/en/file-format-bmp + int size = e.getSizeInBytes() + fakingArray; + img[0] = 'B'; + img[1] = 'M'; + img[2] = (byte) (size & 0xFF); + img[3] = (byte) ((size >> 8) & 0xFF); + img[4] = (byte) ((size >> 16) & 0xFF); + img[5] = (byte) ((size >> 24) & 0xFF); + img[6] = 0; + img[7] = 0; + img[8] = 0; + img[9] = 0; + int ofset = fakingArray + 40 + 4 * e.getColorCount(); + img[10] = (byte) (ofset & 0xFF); + img[11] = (byte) ((ofset >> 8) & 0xFF); + img[12] = (byte) ((ofset >> 16) & 0xFF); + img[13] = (byte) ((ofset >> 24) & 0xFF); + //ico is storing height as height of XOR + height of AND bitmaps + //that is 2 x hight. Bitmap expects only height of single image + int tmpHeight = e.getHeight(); + img[fakingArray + 4/*size*/ + 4/*width*/] = (byte) (tmpHeight & 0xFF); + img[fakingArray + 4/*size*/ + 4/*width*/ + 1] = (byte) ((tmpHeight >> 8) & 0xFF); + img[fakingArray + 4/*size*/ + 4/*width*/ + 2] = (byte) ((tmpHeight >> 16) & 0xFF); + img[fakingArray + 4/*size*/ + 4/*width*/ + 3] = (byte) ((tmpHeight >> 24) & 0xFF); + return img; + } + +}