I'm writing a program to generate procedural images, but when I run it, it skips pixels. What I mean by this is that the generated image looks like a checkerboard at the pixel level. Instead, I want it to be smooth.
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame();
BufferedImage bi = create(new BufferedImage(1024, 1024,
BufferedImage.TYPE_INT_ARGB));
f.getContentPane().add(new JLabel(new ImageIcon(bi)));
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
save(bi, "name.png");
}
//creates the procedural image
private static BufferedImage create(BufferedImage bi) {
for (int i = 0; i < bi.getWidth(); i++) {
for (int j = 0; j < bi.getHeight(); j++) {
Context c = new Context(bi, i, j);
int red = red(i, j, c) + 128;
int green = green(i, j, c) + 128;
int blue = blue(i, j, c) + 128;
bi.setRGB(i, j, new Color(red, green, blue).getRGB());
}
}
return bi;
}
//saves image to file
private static void save(BufferedImage img, String filename) {
try {
File f = new File(filename);
System.out.println(f.getAbsolutePath());
ImageIO.write(img, "png", f);
} catch (IOException e) {
e.printStackTrace();
}
}
//PIXEL FUNCTIONS - returns the color for specified pixel
private static byte red(int i, int j, Context context) {
return (byte) (Context.average(context.north, context.west).getRed()
+ (Math.random() * 5.5) - 2);
}
private static byte green(int i, int j, Context context) {
return (byte) (Context.average(context.north, context.west).getGreen()
+ (Math.random() * 3.46) - 1);
}
private static byte blue(int i, int j, Context context) {
return (byte) (Context.average(context.north, context.west).getBlue()
+ (Math.random() * 17.5) - 8);
}
//Represents the surrounding pixels of the pixel passed into the constructor
static private class Context {
public Color north;
public Color northeast;
public Color east;
public Color southeast;
public Color south;
public Color southwest;
public Color west;
public Color northwest;
private static final Color DEFAULT = Color.GRAY;
//Returns the mean color
public static Color average(Color... colors) {
int redtot = 0;
int greentot = 0;
int bluetot = 0;
for (Color c : colors) {
redtot += c.getRed();
greentot += c.getGreen();
bluetot += c.getBlue();
}
int red = redtot / colors.length;
int green = greentot / colors.length;
int blue = bluetot / colors.length;
return new Color(red, green, blue);
}
public Context(BufferedImage image, int x, int y) {
north = DEFAULT;
northeast = DEFAULT;
east = DEFAULT;
southeast = DEFAULT;
south = DEFAULT;
southwest = DEFAULT;
west = DEFAULT;
northwest = DEFAULT;
try {
north = new Color(image.getRGB(x, y - 1));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
northeast = new Color(image.getRGB(x + 1, y - 1));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
east = new Color(image.getRGB(x + 1, y));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
southeast = new Color(image.getRGB(x + 1, y + 1));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
south = new Color(image.getRGB(x, y + 1));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
southwest = new Color(image.getRGB(x - 1, y + 1));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
west = new Color(image.getRGB(x - 1, y));
} catch (ArrayIndexOutOfBoundsException e) {
}
try {
northwest = new Color(image.getRGB(x - 1, y - 1));
} catch (ArrayIndexOutOfBoundsException e) {
}
}
}
}
This is the generated image:
And this is it zoomed in:
What's causing this behavior?
I'm not familiar with whatever kind of formula you are using to calculate these colors, but the checkered pattern appears to be coming from your adding 128 to each of the color components. Possibly some overflow is causing the bouncing up and down of values...
int red = red(i, j, c) + 128;
int green = green(i, j, c) + 128;
int blue = blue(i, j, c) + 128;
At least, if I remove the + 128
, I get a correct (to me) looking image. Although I'm not sure if this is still valid for what you are trying to do.
Here's the quick fix way I came up with, but there are probably other better ways. The Math.max()
is used because you will get an exception from invalid values for at least the first pixel. (Maybe this is why you are adding 128 in the first place?)
int red = Math.max(0, red(i, j, c));
int green = Math.max(0, green(i, j, c));
int blue = Math.max(0, blue(i, j, c));
The image appears normal (to me) when magnified: