Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMAGING-106: support for JPEG2000 and larger icons up to 1024px #24

Open
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
760 changes: 382 additions & 378 deletions pom.xml

Large diffs are not rendered by default.

184 changes: 88 additions & 96 deletions src/main/java/org/apache/commons/imaging/formats/icns/IcnsDecoder.java
100644 → 100755

Large diffs are not rendered by default.

Large diffs are not rendered by default.

136 changes: 60 additions & 76 deletions src/main/java/org/apache/commons/imaging/formats/icns/IcnsType.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -20,104 +20,88 @@

enum IcnsType {

ICNS_16x12_1BIT_IMAGE_AND_MASK("icm#", 16, 12, 1, true),
ICNS_16x12_4BIT_IMAGE("icm4", 16, 12, 4, false),
ICNS_16x12_8BIT_IMAGE("icm8", 16, 12, 8, false),

ICNS_16x16_8BIT_MASK("s8mk", 16, 16, 8, true),
ICNS_16x16_1BIT_IMAGE_AND_MASK("ics#", 16, 16, 1, true),
ICNS_16x16_4BIT_IMAGE("ics4", 16, 16, 4, false),
ICNS_16x16_8BIT_IMAGE("ics8", 16, 16, 8, false),
ICNS_16x16_32BIT_IMAGE("is32", 16, 16, 32, false),

ICNS_32x32_8BIT_MASK("l8mk", 32, 32, 8, true),
ICNS_32x32_1BIT_IMAGE_AND_MASK("ICN#", 32, 32, 1, true),
ICNS_32x32_4BIT_IMAGE("icl4", 32, 32, 4, false),
ICNS_32x32_8BIT_IMAGE("icl8", 32, 32, 8, false),
ICNS_32x32_32BIT_IMAGE("il32", 32, 32, 32, false),

ICNS_48x48_8BIT_MASK("h8mk", 48, 48, 8, true),
ICNS_48x48_1BIT_IMAGE_AND_MASK("ich#", 48, 48, 1, true),
ICNS_48x48_4BIT_IMAGE("ich4", 48, 48, 4, false),
ICNS_48x48_8BIT_IMAGE("ich8", 48, 48, 8, false),
ICNS_48x48_32BIT_IMAGE("ih32", 48, 48, 32, false),

ICNS_128x128_8BIT_MASK("t8mk", 128, 128, 8, true),
ICNS_128x128_32BIT_IMAGE("it32", 128, 128, 32, false),

ICNS_256x256_32BIT_ARGB_IMAGE("ic08", 256, 256, 32, false),

ICNS_512x512_32BIT_ARGB_IMAGE("ic09", 512, 512, 32, false);

private static final IcnsType[] ALL_IMAGE_TYPES = {
ICNS_16x12_1BIT_IMAGE_AND_MASK,
ICNS_16x12_4BIT_IMAGE,
ICNS_16x12_8BIT_IMAGE,
ICNS_16x16_1BIT_IMAGE_AND_MASK,
ICNS_16x16_4BIT_IMAGE,
ICNS_16x16_8BIT_IMAGE,
ICNS_16x16_32BIT_IMAGE,
ICNS_32x32_1BIT_IMAGE_AND_MASK,
ICNS_32x32_4BIT_IMAGE,
ICNS_32x32_8BIT_IMAGE,
ICNS_32x32_32BIT_IMAGE,
ICNS_48x48_1BIT_IMAGE_AND_MASK,
ICNS_48x48_4BIT_IMAGE,
ICNS_48x48_8BIT_IMAGE,
ICNS_48x48_32BIT_IMAGE,
ICNS_128x128_32BIT_IMAGE,
ICNS_256x256_32BIT_ARGB_IMAGE,
ICNS_512x512_32BIT_ARGB_IMAGE};

private static final IcnsType[] ALL_MASK_TYPES = {
ICNS_16x12_1BIT_IMAGE_AND_MASK,
ICNS_16x16_1BIT_IMAGE_AND_MASK,
ICNS_16x16_8BIT_MASK,
ICNS_32x32_1BIT_IMAGE_AND_MASK,
ICNS_32x32_8BIT_MASK,
ICNS_48x48_1BIT_IMAGE_AND_MASK,
ICNS_48x48_8BIT_MASK,
ICNS_128x128_8BIT_MASK};
ICNS_16x12_1BIT_IMAGE_AND_MASK("icm#", 16, 12, 1, true, false), ICNS_16x12_4BIT_IMAGE("icm4", 16, 12, 4, false, false), ICNS_16x12_8BIT_IMAGE("icm8", 16, 12, 8, false, false), ICNS_16x16_8BIT_MASK("s8mk", 16, 16, 8, true, false), ICNS_16x16_1BIT_IMAGE_AND_MASK("ics#", 16, 16, 1, true, false), ICNS_16x16_4BIT_IMAGE("ics4", 16, 16, 4, false, false), ICNS_16x16_8BIT_IMAGE("ics8", 16, 16, 8, false, false), ICNS_16x16_32BIT_IMAGE("is32", 16, 16, 32, false, false), ICNS_16x16_32BIT_ARGB_IMAGE("icp4", 16, 16, 32, false, true),

ICNS_32x32_1BIT_MONO_IMAGE("ICON", 32, 32, 1, false, false), ICNS_32x32_1BIT_IMAGE_AND_MASK("ICN#", 32, 32, 1, true, false), ICNS_32x32_4BIT_IMAGE("icl4", 32, 32, 4, false, false), ICNS_32x32_8BIT_IMAGE("icl8", 32, 32, 8, false, false), ICNS_32x32_8BIT_MASK("l8mk", 32, 32, 8, true, false), ICNS_32x32_32BIT_IMAGE("il32", 32, 32, 32, false, false), ICNS_32x32_32BIT_ARGB_IMAGE("icp5", 32, 32, 32, false, true), ICNS_32x32_32BIT_RETINA_IMAGE("ic11", 32, 32, 32, false, true),

ICNS_48x48_1BIT_IMAGE_AND_MASK("ich#", 48, 48, 1, true, false), ICNS_48x48_4BIT_IMAGE("ich4", 48, 48, 4, false, false), ICNS_48x48_8BIT_IMAGE("ich8", 48, 48, 8, false, false), ICNS_48x48_8BIT_MASK("h8mk", 48, 48, 8, true, false), ICNS_48x48_32BIT_IMAGE("ih32", 48, 48, 32, false, false),

ICNS_64x64_32BIT_ARGB_IMAGE("icp6", 64, 64, 32, false, true), ICNS_64x64_32BIT_RETINA_IMAGE("ic12", 64, 64, 32, false, true),

ICNS_128x128_8BIT_MASK("t8mk", 128, 128, 8, true, false), ICNS_128x128_32BIT_IMAGE("it32", 128, 128, 32, false, false), ICNS_128x128_32BIT_ARGB_IMAGE("ic07", 128, 128, 32, false, true),

ICNS_256x256_32BIT_ARGB_IMAGE("ic08", 256, 256, 32, false, true), ICNS_256x256_32BIT_RETINA_IMAGE("ic13", 256, 256, 32, false, true),

ICNS_512x512_32BIT_ARGB_IMAGE("ic09", 512, 512, 32, false, true), ICNS_512x512_32BIT_RETINA_IMAGE("ic14", 512, 512, 32, false, true),

ICNS_1024x1024_32BIT_ARGB_IMAGE("ic10", 1024, 1024, 32, false, true);

private static final IcnsType[] ALL_IMAGE_TYPES = { ICNS_16x12_1BIT_IMAGE_AND_MASK, ICNS_16x12_4BIT_IMAGE, ICNS_16x12_8BIT_IMAGE, ICNS_16x16_1BIT_IMAGE_AND_MASK, ICNS_16x16_4BIT_IMAGE, ICNS_16x16_8BIT_IMAGE, ICNS_16x16_32BIT_IMAGE, ICNS_16x16_32BIT_ARGB_IMAGE, ICNS_32x32_1BIT_MONO_IMAGE, ICNS_32x32_1BIT_IMAGE_AND_MASK, ICNS_32x32_4BIT_IMAGE, ICNS_32x32_8BIT_IMAGE, ICNS_32x32_32BIT_IMAGE, ICNS_32x32_32BIT_ARGB_IMAGE, ICNS_32x32_32BIT_RETINA_IMAGE, ICNS_48x48_1BIT_IMAGE_AND_MASK, ICNS_48x48_4BIT_IMAGE, ICNS_48x48_8BIT_IMAGE, ICNS_48x48_32BIT_IMAGE, ICNS_64x64_32BIT_ARGB_IMAGE, ICNS_64x64_32BIT_RETINA_IMAGE, ICNS_128x128_32BIT_IMAGE, ICNS_128x128_32BIT_ARGB_IMAGE, ICNS_256x256_32BIT_ARGB_IMAGE, ICNS_256x256_32BIT_RETINA_IMAGE, ICNS_512x512_32BIT_ARGB_IMAGE, ICNS_512x512_32BIT_RETINA_IMAGE, ICNS_1024x1024_32BIT_ARGB_IMAGE };

private static final IcnsType[] ALL_MASK_TYPES = { ICNS_16x12_1BIT_IMAGE_AND_MASK, ICNS_16x16_1BIT_IMAGE_AND_MASK, ICNS_16x16_8BIT_MASK, ICNS_32x32_1BIT_IMAGE_AND_MASK, ICNS_32x32_8BIT_MASK, ICNS_48x48_1BIT_IMAGE_AND_MASK, ICNS_48x48_8BIT_MASK, ICNS_128x128_8BIT_MASK };

private final int type;
private final String typeName;
private final int width;
private final int height;
private final int bitsPerPixel;
private final boolean hasMask;
private final boolean isPngOrJpeg2000;

private IcnsType(final String type, final int width, final int height, final int bitsPerPixel, final boolean hasMask) {
private IcnsType(final String type, final int width, final int height, final int bitsPerPixel, final boolean hasMask, final boolean isPngOrJpeg2000) {
this.type = typeAsInt(type);
this.typeName = type;
this.width = width;
this.height = height;
this.bitsPerPixel = bitsPerPixel;
this.hasMask = hasMask;
this.isPngOrJpeg2000 = isPngOrJpeg2000;
}

public int getType() {
return type;
return this.type;
}

public String getTypeName() {
return this.typeName;
}

public int getWidth() {
return width;
return this.width;
}

public int getHeight() {
return height;
return this.height;
}

public int getBitsPerPixel() {
return bitsPerPixel;
return this.bitsPerPixel;
}

public boolean hasMask() {
return hasMask;
return this.hasMask;
}

public boolean isPngOrJpeg2000() {
return this.isPngOrJpeg2000;
}

@Override
public String toString() {
return getClass().getName() + "[" + "width=" + width + "," + "height="
+ height + "," + "bpp=" + bitsPerPixel + "," + "hasMask="
+ hasMask + "]";
return getClass().getName()
+ "["
+ "width="
+ this.width
+ ","
+ "height="
+ this.height
+ ","
+ "bpp="
+ this.bitsPerPixel
+ ","
+ "hasMask="
+ this.hasMask
+ "]";
}

public static IcnsType findAnyType(final int type) {
Expand Down Expand Up @@ -146,8 +130,8 @@ public static IcnsType findImageType(final int type) {
public static IcnsType find8BPPMaskType(final IcnsType imageType) {
for (final IcnsType allMaskType : ALL_MASK_TYPES) {
if (allMaskType.getBitsPerPixel() == 8
&& allMaskType.getWidth() == imageType.getWidth()
&& allMaskType.getHeight() == imageType.getHeight()) {
&& allMaskType.getWidth() == imageType.getWidth()
&& allMaskType.getHeight() == imageType.getHeight()) {
return allMaskType;
}
}
Expand All @@ -157,16 +141,16 @@ public static IcnsType find8BPPMaskType(final IcnsType imageType) {
public static IcnsType find1BPPMaskType(final IcnsType imageType) {
for (final IcnsType allMaskType : ALL_MASK_TYPES) {
if (allMaskType.getBitsPerPixel() == 1
&& allMaskType.getWidth() == imageType.getWidth()
&& allMaskType.getHeight() == imageType.getHeight()) {
&& allMaskType.getWidth() == imageType.getWidth()
&& allMaskType.getHeight() == imageType.getHeight()) {
return allMaskType;
}
}
return null;
}

public static int typeAsInt(final String type) {
byte[] bytes;
final byte[] bytes;
try {
bytes = type.getBytes("US-ASCII");
} catch (final UnsupportedEncodingException unsupportedEncodingException) {
Expand All @@ -175,10 +159,10 @@ public static int typeAsInt(final String type) {
if (bytes.length != 4) {
throw new IllegalArgumentException("Invalid ICNS type");
}
return ((0xff & bytes[0]) << 24)
| ((0xff & bytes[1]) << 16)
| ((0xff & bytes[2]) << 8)
| (0xff & bytes[3]);
return ((0xff & bytes[0]) << 24)
| ((0xff & bytes[1]) << 16)
| ((0xff & bytes[2]) << 8)
| (0xff & bytes[3]);
}

public static String describeType(final int type) {
Expand Down
Binary file added src/test/data/images/icns/2/AppIcon.icns
Binary file not shown.
Binary file added src/test/data/images/icns/2/Google Chrome.icns
Binary file not shown.
Binary file added src/test/data/images/icns/2/MSWD.icns
Binary file not shown.
Binary file added src/test/data/images/icns/2/Wireshark.icns
Binary file not shown.
Binary file added src/test/data/images/icns/2/XCEL.icns
Binary file not shown.
Binary file added src/test/data/images/icns/2/compass.icns
Binary file not shown.
Binary file added src/test/data/images/icns/2/sourcetree.icns
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.apache.commons.imaging.formats.icns;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

import org.apache.commons.imaging.ImageFormat;
import org.apache.commons.imaging.ImageFormats;
import org.apache.commons.imaging.Imaging;
import org.junit.Assert;
import org.junit.Test;

/**
* Created by mw on 15.09.16.
*/
public class IcnsGetAllBufferedImagesTest {

private static final String ICNS_FILES = "src/test/data/images/icns/2/";
private static final String DIR_ICONS_CACHE = "src/test/data/images/icns/extractedICNS/";

@Test
public void testGetAllBufferedImagesWithType_aFewIcnsFiles_containsJpegFormat() {

if (!new File(DIR_ICONS_CACHE).exists()) {
new File(DIR_ICONS_CACHE).mkdir();
}

final Set<String> imageTypeProofList = new HashSet<>();
//As of Wikipedia: JPEG 2000 or PNG format
final List<String> jpegImageTypes = new ArrayList<>(Arrays.asList("icp4", "icp5", "icp6", "ic07", "ic08", "ic09", "ic10", "ic11", "ic12", "ic13", "ic14"));
final ImageFormat format = ImageFormats.PNG;
final Map<String, Object> params = new HashMap<>();

final File icnsTestFiles = new File(ICNS_FILES);
final File[] files = icnsTestFiles.listFiles();
final IcnsImageParser icnsImageParser = new IcnsImageParser();
for (final File icnsFile : files) {
try {
final List<Map<BufferedImage, IcnsType>> bufferedImages = icnsImageParser.getAllBufferedImagesWithType(icnsFile);

int counterExtracted = 0;
for (final Map<BufferedImage, IcnsType> map : bufferedImages) {
final Iterator<BufferedImage> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
final BufferedImage image = iterator.next();
final String name = icnsFile.getName();
final String type = map.get(image).getTypeName();
final int width = image.getWidth();
final int height = image.getHeight();
final int pixelSize = image.getColorModel().getPixelSize();

//Add imageType to proof list if image type could be of JPEG2000 format
for (final String jpegImageType : jpegImageTypes) {
if (jpegImageType.equals(type)) {
imageTypeProofList.add(type);
}
}

System.out.println(name + " -> [" + type + "] " + pixelSize + " bit | " + width + "x" + height);
final File cachedImageFile = new File(DIR_ICONS_CACHE + name + "_[" + type + "]_" + width + "x" + height + "_" + pixelSize + "bit_" + counterExtracted + "." + format.getExtension());
Imaging.writeImage(image, cachedImageFile, format, params);
counterExtracted++;
}
}

//Check if JPEG is supported
Assert.assertEquals(true, imageTypeProofList.size() > 0);
System.out.println("--------------- " + imageTypeProofList.size() + " potential JPEGs and " + counterExtracted + " images extracted ---------------");

} catch (final Exception e) {
continue;
}
}
}
}