package org.gba.spritely.processing;
import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;
import org.gba.spritely.color.ColorPalette;
public class ImageProcessing {
static int pixelSimilarityLimit = 40;
public static int trans_limit = 100;
public static BufferedImage reduceBackground(BufferedImage image,
boolean andCrop) {
Color[] corners = {
new Color(image.getRGB(0, 0)),
new Color(image.getRGB(image.getWidth() - 1, 0)),
new Color(image.getRGB(0, image.getHeight() - 1)),
new Color(image.getRGB(image.getWidth() - 1,
image.getHeight() - 1)) };
int avr = 0;
int avb = 0;
int avg = 0;
int ava = 0;
int count = 0;
for (Color c : corners) {
if ((c.getRed() != 0) || (c.getBlue() != 0) || (c.getGreen() != 0)) {
avr += c.getRed();
avb += c.getBlue();
avg += c.getGreen();
ava += c.getAlpha();
count++;
}
}
if (count == 0) {
return image;
}
for (Color c : corners) {
if ((c.getRed() != 0) || (c.getBlue() != 0) || (c.getGreen() != 0)) {
if ((Math.abs(c.getRed() - avr / count) >= pixelSimilarityLimit)
|| (Math.abs(c.getBlue() - avb / count) >= pixelSimilarityLimit)
|| (Math.abs(c.getGreen() - avg / count) >= pixelSimilarityLimit)
|| (Math.abs(c.getAlpha() - ava / count) >= pixelSimilarityLimit)) {
System.out.println("Reduction failed.");
return image;
}
}
}
Color master = new Color(avr / count, avg / count, avb / count, ava
/ count);
// Ok, trying to figure out what I'm doing here.
// Open seems to be a list of corners initially. This is probably the
// border removal code.
LinkedList<Pixel> open = new LinkedList<Pixel>();
LinkedList<Pixel> closed = new LinkedList<Pixel>();
open.add(new Pixel(0, 0));
open.add(new Pixel(0, image.getHeight() - 1));
open.add(new Pixel(image.getWidth() - 1, 0));
open.add(new Pixel(image.getWidth() - 1, image.getHeight() - 1));
// Ah I remember this bit. These variables are used to crop the image as
// tightly as possible
int bottom = 0;
int right = 0;
int left = image.getWidth() - 1;
int top = image.getHeight() - 1;
// Which makes this code the code to remove the transparent stuff from
// outside the image.
while (open.size() > 0) {
Pixel p = (Pixel) open.removeFirst();
closed.add(p);
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++)
if ((i != 0) || (j != 0)) {
if ((p.x + i >= 0) && (p.x + i < image.getWidth())
&& (p.y + j >= 0)
&& (p.y + j < image.getHeight())) {
Pixel thisPoint = new Pixel(p.x + i, p.y + j);
boolean add = true;
for (Pixel pp : open) {
if ((thisPoint.x == pp.x)
&& (thisPoint.y == pp.y)) {
add = false;
break;
}
}
if (add) {
for (Pixel pp : closed) {
if ((thisPoint.x == pp.x)
&& (thisPoint.y == pp.y)) {
add = false;
break;
}
}
}
if ((add)
&& (areSimilar(
master,
new Color(image.getRGB(p.x + i, p.y
+ j), true)))) {
open.add(thisPoint);
} else if (add) {
if (p.x < left)
left = p.x;
else if (p.x > right)
right = p.x;
if (p.y > bottom)
bottom = p.y;
else if (p.y < top)
top = p.y;
}
}
}
}
}
if (andCrop) {
image = image.getSubimage(left, top, right - left, bottom - top);
}
for (Pixel p : closed) {
int mx = p.x - left;
int my = p.y - top;
if ((mx >= 0) && (mx < right - left) && (my >= 0)
&& (my < bottom - top)) {
image.setRGB(mx, my, 0);
}
}
return image;
}
public static boolean areSimilar(Color c, Color d) {
if ((Math.abs(c.getRed() - d.getRed()) < pixelSimilarityLimit)
&& (Math.abs(c.getBlue() - d.getBlue()) < pixelSimilarityLimit)
&& (Math.abs(c.getGreen() - d.getGreen()) < pixelSimilarityLimit)
&& (Math.abs(c.getAlpha() - d.getAlpha()) < pixelSimilarityLimit)) {
return true;
}
return false;
}
public static BufferedImage shrink(BufferedImage b, int w, int h) {
int[][][] array = new int[w][h][4];
int[][][] count = new int[w][h][1];
BufferedImage res = new BufferedImage(w, h, 2);
float w_chunk = ((float)b.getWidth() / (float)w) ;
float h_chunk = ((float)b.getHeight() / (float)h) ;
for (int i = 0; i < b.getWidth(); i++) {
for (int j = 0; j < b.getHeight(); j++) {
Color c = new Color(b.getRGB(i, j), true);
int x = (int) Math.floor(i / w_chunk);
int y = (int) Math.floor(j / h_chunk);
array[x][y][0] += c.getRed();
array[x][y][1] += c.getGreen();
array[x][y][2] += c.getBlue();
array[x][y][3] += c.getAlpha();
count[x][y][0] += 1;
}
}
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
if (count[i][j][0] != 0) {
Color c = new Color(array[i][j][0] / count[i][j][0],
array[i][j][1] / count[i][j][0], array[i][j][2]
/ count[i][j][0], array[i][j][3]
/ count[i][j][0]);
if (array[i][j][3] / count[i][j][0] < trans_limit)
res.setRGB(i, j, new Color(0, 0, 0, 0).getRGB());
else
res.setRGB(i, j, c.getRGB());
}
}
}
return res;
}
public static List<BufferedImage> filterForBorderless(List<String> urls) {
return filterForBorderless(urls, true);
}
public static List<BufferedImage> filterForBorderless(List<String> urls,
boolean strict) {
LinkedList<BufferedImage> res = new LinkedList<BufferedImage>();
for (String s : urls) {
if (!s.endsWith("svg")) {
BufferedImage image = ImageUtil.getFromURL(s);
if (image != null) {
Color[] corners = {
new Color(image.getRGB(0, 0), true),
new Color(image.getRGB(image.getWidth() - 1, 0),
true),
new Color(image.getRGB(0, image.getHeight() - 1),
true),
new Color(image.getRGB(image.getWidth() - 1,
image.getHeight() - 1), true) };
int avr = 0;
int avb = 0;
int avg = 0;
int ava = 0;
for (Color c : corners) {
avr += c.getRed();
avb += c.getBlue();
avg += c.getGreen();
ava += c.getAlpha();
}
List<Color> colors = new LinkedList<Color>();
if (strict) {
for (int i = 0; i < image.getWidth(); i++) {
colors.add(new Color(image.getRGB(i, 0), true));
}
for (int i = 0; i < image.getHeight(); i++)
colors.add(new Color(image.getRGB(0, i), true));
} else {
colors.add(new Color(image.getRGB(0, 0), true));
colors.add(new Color(image.getRGB(image.getWidth() - 1,
0), true));
colors.add(new Color(image.getRGB(0,
image.getHeight() - 1), true));
colors.add(new Color(image.getRGB(image.getWidth() - 1,
image.getHeight() - 1), true));
}
boolean add = true;
for (Color c : colors) {
if ((Math.abs(c.getRed() - avr / 4) >= pixelSimilarityLimit)
|| (Math.abs(c.getBlue() - avb / 4) >= pixelSimilarityLimit)
|| (Math.abs(c.getGreen() - avg / 4) >= pixelSimilarityLimit)
|| (Math.abs(c.getAlpha() - ava / 4) >= pixelSimilarityLimit)) {
if (add) {
add = false;
}
}
}
if (add)
res.add(image);
}
}
}
return res;
}
public static BufferedImage isBorderless(String s, boolean strict) {
if (!s.endsWith("svg")) {
BufferedImage image = ImageUtil.getFromURL(s);
if (image != null) {
Color[] corners = {
new Color(image.getRGB(0, 0), true),
new Color(image.getRGB(image.getWidth() - 1, 0), true),
new Color(image.getRGB(0, image.getHeight() - 1), true),
new Color(image.getRGB(image.getWidth() - 1,
image.getHeight() - 1), true) };
int avr = 0;
int avb = 0;
int avg = 0;
int ava = 0;
for (Color c : corners) {
avr += c.getRed();
avb += c.getBlue();
avg += c.getGreen();
ava += c.getAlpha();
}
List<Color> colors = new LinkedList<Color>();
if (strict) {
for (int i = 0; i < image.getWidth(); i++) {
colors.add(new Color(image.getRGB(i, 0), true));
}
for (int i = 0; i < image.getHeight(); i++)
colors.add(new Color(image.getRGB(0, i), true));
} else {
colors.add(new Color(image.getRGB(0, 0), true));
colors.add(new Color(image.getRGB(image.getWidth() - 1, 0),
true));
colors.add(new Color(
image.getRGB(0, image.getHeight() - 1), true));
colors.add(new Color(image.getRGB(image.getWidth() - 1,
image.getHeight() - 1), true));
}
boolean add = true;
for (Color c : colors) {
if ((Math.abs(c.getRed() - avr / 4) >= pixelSimilarityLimit)
|| (Math.abs(c.getBlue() - avb / 4) >= pixelSimilarityLimit)
|| (Math.abs(c.getGreen() - avg / 4) >= pixelSimilarityLimit)
|| (Math.abs(c.getAlpha() - ava / 4) >= pixelSimilarityLimit)) {
if (add) {
add = false;
}
}
}
if(add)
return image;
else
return null;
}
}
return null;
}
public static List<BufferedImage> filterForSingular(List<BufferedImage> imgs) {
LinkedList<BufferedImage> res = new LinkedList<BufferedImage>();
LinkedList<Point> locations = new LinkedList<Point>();
for (BufferedImage img : imgs) {
locations = new LinkedList<Point>();
boolean add = true;
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
if ((locations.size() == 0)
&& (new Color(img.getRGB(i, j), true).getAlpha() > trans_limit)) {
locations.add(new Point(i, j));
locations = fill(locations, img);
} else if ((!locations.contains(new Point(i, j)))
&& (new Color(img.getRGB(i, j), true).getAlpha() > trans_limit)) {
i = img.getWidth();
j = img.getHeight();
add = false;
}
}
}
if (add)
res.add(img);
}
return res;
}
public static BufferedImage isSingular(BufferedImage img) {
LinkedList<Point> locations = new LinkedList<Point>();
locations = new LinkedList<Point>();
boolean add = true;
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
if ((locations.size() == 0)
&& (new Color(img.getRGB(i, j), true).getAlpha() > trans_limit)) {
locations.add(new Point(i, j));
locations = fill(locations, img);
} else if ((!locations.contains(new Point(i, j)))
&& (new Color(img.getRGB(i, j), true).getAlpha() > trans_limit)) {
i = img.getWidth();
j = img.getHeight();
add = false;
}
}
}
if (add) {
return img;
}
return null;
}
public static LinkedList<Point> fill(LinkedList<Point> openset,
BufferedImage img) {
LinkedList<Point> closedset = new LinkedList<Point>();
while (openset.size() > 0) {
Point p = (Point) openset.removeFirst();
closedset.add(p);
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++) {
Point q = new Point(p.x + i, p.y + j);
if ((q.x >= 0)
&& (q.x < img.getWidth())
&& (q.y >= 0)
&& (q.y < img.getHeight())
&& (!closedset.contains(q))
&& (!openset.contains(q))
&& (new Color(img.getRGB(q.x, q.y), true)
.getAlpha() > trans_limit))
openset.add(q);
}
}
}
return closedset;
}
public static void recolorImage(BufferedImage img, ColorPalette cp, int type) {
Color c = null;
Color d = null;
for (int i = 0; i < img.getWidth(); i++)
for (int j = 0; j < img.getHeight(); j++) {
c = new Color(img.getRGB(i, j), true);
if (c.getAlpha() > 100) {
switch (type) {
case 0:
d = getClosestColorRGB(c, cp);
break;
case 1:
d = getClosestColorHSV(c, cp);
break;
case 2:
d = getClosestColorColton(c, cp);
}
img.setRGB(i, j, d.getRGB());
}
}
}
private static Color getClosestColorColton(Color c, ColorPalette p) {
float delta = 765.0F;
Color closest = c;
float[] cvals = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(),
null);
float[] rvals = new float[3];
Color pc = null;
float[] vals = new float[3];
for (int i = 0; i < p.colors.size(); i++) {
pc = (Color) p.colors.get(i);
vals = Color.RGBtoHSB(pc.getRed(), pc.getGreen(), pc.getBlue(),
vals);
float diff = Math.abs(cvals[0] - vals[0]);
diff += Math.abs(cvals[1] - vals[1]);
diff += Math.abs(cvals[2] - vals[2]);
if (diff < delta) {
delta = diff;
closest = pc;
rvals = Color.RGBtoHSB(pc.getRed(), pc.getGreen(),
pc.getBlue(), null);
}
}
Color res = new Color(Color.HSBtoRGB(rvals[0], rvals[1], cvals[2]));
return res;
}
private static Color getClosestColorHSV(Color c, ColorPalette p) {
float delta = 765.0F;
Color closest = c;
float[] cvals = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(),
null);
Color pc = null;
float[] vals = new float[3];
for (int i = 0; i < p.colors.size(); i++) {
pc = (Color) p.colors.get(i);
vals = Color.RGBtoHSB(pc.getRed(), pc.getGreen(), pc.getBlue(),
vals);
float diff = Math.abs(cvals[0] - vals[0]);
diff += Math.abs(cvals[1] - vals[1]);
diff += Math.abs(cvals[2] - vals[2]);
if (diff < delta) {
delta = diff;
closest = pc;
}
}
return closest;
}
private static Color getClosestColorRGB(Color c, ColorPalette p) {
int delta = 765;
Color closest = c;
for (int i = 0; i < p.colors.size(); i++) {
Color pc = (Color) p.colors.get(i);
int diff = Math.abs(c.getRed() - pc.getRed());
diff += Math.abs(c.getGreen() - pc.getGreen());
diff += Math.abs(c.getBlue() - pc.getBlue());
if (diff < delta) {
delta = diff;
closest = pc;
}
}
return closest;
}
}