Skip to content

Instantly share code, notes, and snippets.

@bendem
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bendem/0c2f74f8af8eb0adf674 to your computer and use it in GitHub Desktop.
Save bendem/0c2f74f8af8eb0adf674 to your computer and use it in GitHub Desktop.
Image edition tests, main class is PictureTest (java 8!)
package be.bendem.testing.utils;
public class ColorUtils {
private ColorUtils() {}
public static int[] decompose(int color) {
int[] rgb = new int[3];
rgb[0] = color >> 16 & 0xFF;
rgb[1] = color >> 8 & 0xFF;
rgb[2] = color & 0xFF;
return rgb;
}
public static int recompose(int[] rgb) {
if(rgb.length != 3) {
throw new IllegalArgumentException("Provided color doesn't have 3 components");
}
int color = rgb[0];
color = (color << 8) | (rgb[1] & 0xFF);
color = (color << 8) | (rgb[2] & 0xFF);
return color;
}
}
package be.bendem.testing.utils;
@FunctionalInterface
public interface IntArrayBiFunction {
public int[] apply(int[] a, int[] b);
}
package be.bendem.testing.utils;
public class NumberUtils {
private NumberUtils() {}
public static int decodeHexa(String str) {
return Integer.decode((str.startsWith("0x") ? "" : "0x") + str);
}
}
package be.bendem.testing;
import be.bendem.testing.utils.ColorUtils;
import be.bendem.testing.utils.IntArrayBiFunction;
import be.bendem.testing.utils.NumberUtils;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.function.IntBinaryOperator;
import javax.imageio.ImageIO;
public class PictureTest {
private final BufferedImage image;
private final File output;
private final String extension;
private final Operation operation;
private final int[] colorMask;
public PictureTest(File file, Operation operation, int[] colorMask) throws IOException {
String[] nameParts = file.getName().split("\\.");
extension = nameParts[nameParts.length - 1];
output = new File("./output." + extension);
this.image = ImageIO.read(file);
this.operation = operation;
this.colorMask = colorMask;
System.out.println("Options are: "
+ "file=" + file.getName() + " | "
+ "operation=" + operation.name() + " | "
+ "colormask=" + Arrays.toString(colorMask) + " | "
+ "colormaskhexa=" + (colorMask == null ? "none" : Integer.toHexString(ColorUtils.recompose(colorMask)))
);
}
private void doStuff() throws IOException {
for(int i = 0; i < image.getWidth(); ++i) {
for(int j = 0; j < image.getHeight(); ++j) {
int rgbInt = image.getRGB(i, j);
int[] rgb = ColorUtils.decompose(rgbInt);
rgbInt >>= 24; // Save missed precision
// Applying provided mask
rgb = operation.apply(rgb, colorMask);
image.setRGB(i, j, ColorUtils.recompose(rgb) | rgbInt << 24); // Readds precision
}
}
}
private void save() throws IOException {
ImageIO.write(image, extension, output);
}
public static void main(String[] args) throws Exception {
if(args.length < 1) {
System.err.println("No file path provided");
return;
}
File file = new File(args[0]);
if(!file.exists() || !file.isFile()) {
System.err.println("File not found :(");
return;
}
// Operation
if(args.length < 2) {
System.err.println("No operation provided");
}
Operation operation = Operation.valueOf(args[1].toUpperCase());
int colorMask[];
if(operation == Operation.AVG) {
colorMask = null;
} else if(args.length == 3) {
colorMask = ColorUtils.decompose(NumberUtils.decodeHexa(args[2]));
} else {
System.err.println("Color should be provided as an hexacolor (like #ff3366)");
return;
}
PictureTest test = new PictureTest(file, operation, colorMask);
test.doStuff();
test.save();
}
public enum Operation {
/**
* Adds the color mask to the provided color
* (generally brightens color components)
*/
ADD((rgb, mask) -> arrayMap(new int[3], rgb, mask, (x, y) -> x + y)),
/**
* Substract the color mask from the provided color
* (generally darkens color components)
*/
SUBSTRACT((rgb, mask) -> arrayMap(new int[3], rgb, mask, (x, y) -> x - y)),
/**
* Applies an "and" operation on the provided color
* (generally removes parts of the provided colors)
*/
AND((rgb, mask) -> arrayMap(new int[3], rgb, mask, (x, y) -> x & y)),
/**
* Applies an "or" operation on the provided color
* (generally adds color components to the provided colors)
*/
OR((rgb, mask) -> arrayMap(new int[3], rgb, mask, (x, y) -> x | y)),
/**
* Computes the average out of the 3 components (used to get gray colors)
*/
AVG((rgb, unused) -> {
int[] result = new int[3];
double avg = Arrays.stream(rgb)
.average()
.getAsDouble();
int rounded = (int) (Math.round(avg) & 0xff);
// Applying provided mask
for(int k = 0; k < 3; ++k) {
result[k] = rounded;
}
return result;
})
;
private final IntArrayBiFunction function;
Operation(IntArrayBiFunction function) {
this.function = function;
}
public int[] apply(int[] a, int[] b) {
int[] result = function.apply(a, b);
for(int i = 0; i < result.length; i++) {
if(result[i] < 0) {
result[i] = 0;
} else if(result[i] > 0xff) {
result[i] = 0xff;
}
}
return result;
}
private static int[] arrayMap(int[] result, int[] a, int[] b, IntBinaryOperator function) {
for(int i = 0; i < a.length; i++) {
result[i] = function.applyAsInt(a[i], b[i]);
}
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment