/**
* 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.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
public class XPMGenerator extends AbstractImageGenerator {
private static final int RGB_ALPHA = 0xFF000000;
public void write(OutputStream os) throws IOException {
Iterator<RenderedImage> iterator = images.values().iterator();
if (!iterator.hasNext())
throw new IllegalArgumentException("no image provided");
ImageData image = new ImageData(iterator.next());
try (OutputStreamWriter writer = new OutputStreamWriter(os, "ascii")) {
writer.append("/* XPM */\nstatic char * blarg_xpm[] = {\n");
writeGeometry(writer, image);
writePalette(writer, image);
writePixels(writer, image);
writer.append("};\n");
}
}
protected void writeGeometry(Writer writer, ImageData image) throws IOException {
writer.append(String.format("\"%d %d %d %d\",\n",
image.getWidth(), image.getHeight(),
image.getColorsNumber(), image.getPixelDepth()));
}
protected void writePalette(Writer writer, ImageData image) throws IOException {
Map<Integer, String> colorMap = image.getColorMap();
for (Entry<Integer, String> entry : colorMap.entrySet()) {
String color = entry.getKey() == RGB_ALPHA ? "None" : String.format("#%06X", entry.getKey());
writer.append(String.format("\"%s\tc %s\",\n", entry.getValue(), color));
}
}
protected void writePixels(Writer writer, ImageData image) throws IOException {
for (int y = 0; y < image.getHeight(); y++) {
writer.append("\"");
for (int x = 0; x < image.getWidth(); x++)
writer.append(image.getPixelData(x, y));
writer.append("\",\n");
}
}
public static final class ImageData {
private static final char[] DIGITS = " .+@#$%&*=-;>,')!~{]^/(_:<[}|1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`".toCharArray();
protected Map<Integer, String> colorMap;
protected String[][] pixels;
protected int pixelDepth;
public ImageData(RenderedImage image) {
Raster raster = image.getData();
ColorModel colorModel = image.getColorModel();
this.colorMap = new TreeMap<Integer, String>();
int width = image.getWidth();
int height = image.getHeight();
Set<Integer> colors = new HashSet<>();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Object dataElements = raster.getDataElements(x, y, null);
int rgb = colorModel.getRGB(dataElements) & ~RGB_ALPHA;
if (colorModel.getAlpha(dataElements) < 127)
rgb = RGB_ALPHA;
colors.add(rgb);
}
}
pixelDepth = 0;
int colorCount = colors.size();
while (colorCount > 0) {
colorCount /= DIGITS.length;
pixelDepth +=1;
}
this.pixels = new String[height][];
for (int y = 0; y < pixels.length; y++) {
this.pixels[y] = new String[width];
for (int x = 0; x < pixels[y].length; x++) {
Object dataElements = raster.getDataElements(x, y, null);
int rgb = colorModel.getRGB(raster.getDataElements(x, y, null)) & ~RGB_ALPHA;
if (colorModel.getAlpha(dataElements) < 127)
rgb = RGB_ALPHA;
this.pixels[y][x] = calculatePixelData(rgb);
}
}
}
protected String calculatePixelData(int rgb) {
String pixel = colorMap.get(rgb);
if (pixel == null)
colorMap.put(rgb, pixel = toPixel(getColorsNumber()));
return pixel;
}
protected String toPixel(int pixel) {
char buffer[] = new char[pixelDepth];
Arrays.fill(buffer, 0, pixelDepth, DIGITS[0]);
int index = 0;
while (pixel >= DIGITS.length) {
buffer[index++] = DIGITS[pixel % DIGITS.length];
pixel = pixel / DIGITS.length;
}
buffer[index] = DIGITS[pixel];
return String.valueOf(buffer, 0, index+1);
}
public String getPixelData(int x, int y) {
return this.pixels[y][x];
}
public Map<Integer, String> getColorMap() {
return Collections.unmodifiableMap(colorMap);
}
public int getColorsNumber() {
return colorMap.size();
}
public int getPixelDepth() {
return this.pixelDepth;
}
public int getWidth() {
return getHeight() > 0 ? pixels[0].length : 0;
}
public int getHeight() {
return pixels.length;
}
}
}