/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.ui.image;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.ImageIO;
public class ICNSData {
protected ICNSType iconType;
protected byte[] image;
protected byte[] alpha;
public ICNSData(ICNSType iconType, RenderedImage image) throws IOException {
if (image.getWidth() != image.getHeight() || image.getWidth() != iconType.getSize())
throw new IllegalArgumentException(String.format("invalid image size: %dx%d", image.getWidth(), image.getHeight()));
this.iconType = iconType;
this.image = encodeImage(iconType, image);
this.alpha = iconType.hasAlpha() ? encodeAlpha(iconType, image) : null;
}
public ICNSType getIconType() {
return iconType;
}
public byte[] getImage() {
return image;
}
public byte[] getAlpha() {
return alpha;
}
protected byte[] encodeImage(ICNSType iconType, RenderedImage image) throws IOException {
switch (iconType) {
case IC08:
case IC09:
case IC10:
case IC11:
case IC12:
case IC13:
case IC14:
return convertToPng(image);
case IS32:
case IL32:
case IT32:
default:
return convertToRaw(iconType, image);
}
}
protected byte[] encodeAlpha(ICNSType iconType, RenderedImage image) {
return convertToAlpha(image);
}
protected byte[] convertToPng(RenderedImage image) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
return baos.toByteArray();
}
private static class Component {
private List<Byte> values;
private int repetitions;
public Component(byte value, int repetitions) {
this.values = new ArrayList<Byte>();
this.values.add(value);
this.repetitions = repetitions;
}
public Component(byte value) {
this(value, 0);
}
public Component add(byte value) {
if (repetitions == 0) {
int size = values.size();
if (size < 2 || values.get(size-2) != value || values.get(size-1) != value) {
if (size < 128) {
values.add(value);
return null;
} else
return new Component(value);
} else {
if (size == 2) {
values.remove(size-1);
this.repetitions = 3;
return null;
} else {
values.remove(size-1);
values.remove(size-2);
return new Component(value, 3);
}
}
} else {
if (values.get(0) != value || repetitions == 130) {
return new Component(value);
} else {
repetitions += 1;
return null;
}
}
}
public int getSize() {
return repetitions == 0 ? values.size() + 1 : 2;
}
public byte[] toByteArray() {
byte[] bytes = new byte[getSize()];
if (repetitions == 0) {
int size = values.size();
bytes[0] = (byte) (size - 1);
for (int i = 0; i < size; i++)
bytes[i+1] = values.get(i);
} else {
bytes[0] = (byte) (repetitions + 125);
bytes[1] = values.get(0);
}
return bytes;
}
}
private static class Components {
private LinkedList<Component> components = new LinkedList<Component>();
public void add(byte value) {
Component last = components.peekLast();
Component fresh = last == null ?
new Component(value) : last.add(value);
if (fresh != null)
components.add(fresh);
}
private byte[] toByteArray() {
int length = 0;
for (Component component : components)
length += component.getSize();
byte[] buffer = new byte[length];
int offset = 0;
for (Component component : components) {
byte[] array = component.toByteArray();
System.arraycopy(array, 0, buffer, offset, array.length);
offset += array.length;
}
return buffer;
}
}
protected byte[] convertToRaw(ICNSType iconType, RenderedImage image) {
int width = image.getWidth();
int height = image.getHeight();
Components reds = new Components();
Components greens = new Components();
Components blues = new Components();
Raster raster = image.getData();
ColorModel colorModel = image.getColorModel();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = colorModel.getRGB(raster.getDataElements(x, y, null));
reds.add((byte) ((rgb >> 16) & 0xFF));
greens.add((byte) ((rgb >> 8) & 0xFF));
blues.add((byte) (rgb & 0xFF));
}
}
byte[] redArray = reds.toByteArray();
byte[] greenArray = greens.toByteArray();
byte[] blueArray = blues.toByteArray();
int offset = iconType == ICNSType.IT32 ? 4 : 0;
byte[] result = new byte[offset + redArray.length + greenArray.length + blueArray.length];
System.arraycopy(redArray, 0, result, offset, redArray.length);
System.arraycopy(greenArray, 0, result, offset + redArray.length, greenArray.length);
System.arraycopy(blueArray, 0, result, offset + redArray.length + greenArray.length, blueArray.length);
return result;
}
protected byte[] convertToAlpha(RenderedImage image) {
int width = image.getWidth();
int height = image.getHeight();
Raster raster = image.getData();
ColorModel colorModel = image.getColorModel();
byte[] alpha = new byte[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int offset = (x + width * y);
alpha[offset] = (byte) colorModel.getAlpha(raster.getDataElements(x, y, null));
}
}
return alpha;
}
}