Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package personthecat.overlay32;
- import java.awt.Color;
- import java.awt.Graphics2D;
- import java.awt.image.BufferedImage;
- import java.io.File;
- import java.io.IOException;
- import javax.imageio.ImageIO;
- public class Main
- {
- public static void main(String[] args)
- {
- String singleColorLocation = System.getProperty("user.dir") + "/generated_color.png";
- String overlayLocation = System.getProperty("user.dir") + "/overlay.png";
- BufferedImage originalBackground = getImageFromFile(System.getProperty("user.dir") + "/background.png");
- BufferedImage oreTexture = getImageFromFile(System.getProperty("user.dir") + "/ore_texture.png");
- Color[][] backgroundColors = getColorsFromImage(originalBackground);
- Color[][] oreColors = getColorsFromImage(oreTexture);
- BufferedImage newBackground = createImageFromColors(fillColors(backgroundColors, getAverageColor(backgroundColors)));
- BufferedImage overlay = createImageFromColors(OverlayExtractor.extractBlendedOverlay(oreColors, backgroundColors));
- writeImageToFile(newBackground, singleColorLocation);
- writeImageToFile(overlay, overlayLocation);
- }
- private static BufferedImage getImageFromFile(String input)
- {
- BufferedImage bufferedImage = null;
- try
- {
- bufferedImage = ImageIO.read(new File(input));
- }
- catch (IOException e) {System.err.println("Error: Could not load image " + input);}
- return bufferedImage;
- }
- private static Color[][] getColorsFromImage(BufferedImage image)
- {
- int w = image.getWidth(), h = image.getHeight();
- Color[][] colors = new Color[w][h];
- for (int i = 0; i < w; i++)
- {
- for (int j = 0; j < h; j++)
- {
- colors[i][j] = new Color(image.getRGB(i, j), true);
- }
- }
- return colors;
- }
- private static Color getAverageColor(Color[][] image)
- {
- int r = 0, g = 0, b = 0;
- int w = image.length, h = image[0].length;
- for (int i = 0; i < w; i++)
- {
- for (int j = 0; j < h; j++)
- {
- Color pixel = image[i][j];
- r += pixel.getRed();
- g += pixel.getGreen();
- b += pixel.getBlue();
- }
- }
- r /= (w * h);
- g /= (w * h);
- b /= (w * h);
- return new Color(r, g, b);
- }
- private static Color[][] fillColors(Color[][] image, Color rgb)
- {
- int w = image.length, h = image[0].length;
- for (int i = 0; i < w; i++)
- {
- for (int j = 0; j < h; j++)
- {
- image[i][j] = rgb;
- }
- }
- return image;
- }
- private static BufferedImage createImageFromColors(Color[][] image)
- {
- int w = image.length, h = image[0].length;
- BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
- for (int i = 0; i < w; i++)
- {
- for (int j = 0; j < h; j++)
- {
- bufferedImage.setRGB(i, j, image[i][j].getRGB());
- }
- }
- return bufferedImage;
- }
- private static BufferedImage scaleBackgroundToOverlay(BufferedImage background, BufferedImage overlay)
- {
- BufferedImage scaledImage = new BufferedImage(overlay.getWidth(), overlay.getWidth(), overlay.getType());
- Graphics2D graphics = scaledImage.createGraphics();
- graphics.drawImage(background, 0, 0, overlay.getWidth(), overlay.getWidth(), null);
- graphics.dispose();
- return scaledImage;
- }
- private static void writeImageToFile(BufferedImage image, String location)
- {
- try
- {
- File png = new File(location);
- ImageIO.write(image, "png", png);
- }
- catch (IOException e) {System.err.println("Error: Could not create image file.");}
- }
- private static class OverlayExtractor
- {
- private static final double TEXTURE_SHARPEN_RATIO = 1.5; //Multiplies the alpha levels for push and pull / overlay background textures.
- private static final double TARGET_ALPHA_PERCENTAGE = 85.0; //The percentage of transparent pixels an overlay should have upon extraction.
- private static final int TRANSPARENCY_THRESHOLD = 25; //Pixels with lower alpha levels are considered transparent.
- private static final int OPACITY_THRESHOLD = 60; //Pixels with higher alpha levels are considered opaque.
- private static double threshold = 0.8; //It's also static so repeated calls to this class can continue decreasing the threshold dynamically.
- private static double getLastThreshold() {return threshold;} //Just for readability.
- private static void resetThreshold() {threshold = 0.8;} //The actual threshold becomes 0.53 > 0.35 > 0.24 when the loop in the below function iterates.
- private static Color[][] extractNormalOverlay(Color[][] image, Color[][] background)
- {
- System.out.println("someone asked me to extract an overlay");
- Color[][] overlay = extractOverlay(image, background, false);
- resetThreshold();
- return overlay;
- }
- private static Color[][] extractBlendedOverlay(Color[][] image, Color[][] background)
- {
- Color[][] orePixels = extractOverlay(image, background, false);
- Color[][] texturePixels = extractOverlay(image, background, true);
- Color[][] textureMask = getColorsFromImage(getImageFromFile(System.getProperty("user.dir") + "/mask.png"));
- resetThreshold();
- //Ore texture changes.
- orePixels = removeLonePixels(removeLonePixels(orePixels)); //Remove all of the single pixels and small lines.
- orePixels = removeOffColorPixelsFromBorders(removeOffColorPixelsFromBorders(orePixels)); //Remove pixels that look too different from everything else.
- orePixels = reAddSurroundingPixels(reAddSurroundingPixels(orePixels, image), image); //The ore textures are usually too small at this point. Expand them.
- //Texture texture changes.
- texturePixels = convertToPushAndPull(texturePixels, image, background); //Add transparency from getDifference() per-pixel using only black and white.
- texturePixels = removePixelsUsingMask(texturePixels, textureMask); //Use a vignette mask to lower the opacity from border pixels.
- return overlayImage(orePixels, texturePixels);
- }
- //Call this a second time before resetting to retrieve smaller differences.
- private static Color[][] extractOverlay(Color[][] image, Color[][] background, boolean ignoreTarget)
- {
- int w = image.length, h = image[0].length, bh = background[0].length;
- int frames = h / bh;
- if (h % bh != 0) return null; //No decimals allowed.
- Color[][] overlay = new Color[w][h];
- double percentAlpha = 100.0; //Percentage of alpha pixels.
- while (percentAlpha > TARGET_ALPHA_PERCENTAGE) //Most vanilla ores generate below ~63.145% alpha for 16x.
- { //85.0% seems fine for 32x.
- threshold /= 1.5;
- percentAlpha = 0.0;
- for (int f = 0; f < frames; f++)
- {
- for (int x = 0; x < w; x++)
- {
- for (int y = 0; y < bh; y++)
- {
- int imageY = (f * bh) + y;
- overlay[x][imageY] = getOrePixel(image[x][imageY], background[x][y], threshold);
- percentAlpha += overlay[x][imageY].getAlpha();
- }
- }
- }
- percentAlpha /= (frames * w * bh);
- percentAlpha = 100 - (percentAlpha / 255 * 100);
- if (ignoreTarget) break;
- }
- return overlay;
- }
- /**
- * Gets the difference, returns the foreground pixel if any value is outside of the accepted RGB range (0-255).
- */
- private static Color getOrePixel(Color foreground, Color background, double alphaPercent)
- {
- if (foreground.getRGB() == background.getRGB()) return new Color(0, 0, 0, 0);
- int r = (int) ((foreground.getRed() - background.getRed() * (1 - alphaPercent)) / alphaPercent);
- int g = (int) ((foreground.getGreen() - background.getGreen() * (1 - alphaPercent)) / alphaPercent);
- int b = (int) ((foreground.getBlue() - background.getBlue() * (1 - alphaPercent)) / alphaPercent);
- //if any is > 255 || < 0 -> keep foreground pixel.
- if (r > 255 || g > 255 || b > 255 || r < 0 || g < 0 || b < 0) return foreground;
- return new Color(0, 0, 0, 0); //else return nothing.
- }
- /**
- * Uses getDifference() to determine the alpha level for each pixel.
- * Uses isPixelDarker() to determine whether each pixel should be black or white (push or pull)
- */
- private static Color[][] convertToPushAndPull(Color[][] newImage, Color[][] originalImage, Color[][] background)
- {
- for (int x = 0; x < newImage.length; x++)
- {
- for (int y = 0; y < newImage[0].length; y++)
- {
- int alpha = (int) (255 * getDifference(originalImage[x][y], background[x][y]));
- if (isPixelDarker(originalImage[x][y], background[x][y])) newImage[x][y] = new Color(0, 0, 0, alpha);
- else newImage[x][y] = new Color(255, 255, 255, alpha);
- }
- }
- return newImage;
- }
- /**
- * Using the distance formula to determine the 0 - 1 "distance" between each color.
- * Imagine r, g, and b as x, y, and z on a coordinate plane; they are dimensions, not additive values.
- * e.g. Color(255, 255, 0) and Color(255, 0, 255) are totally different colors, so getting the average doesn't work.
- */
- private static double getDifference(Color foreground, Color background)
- {
- int r = foreground.getRed() - background.getRed();
- int g = foreground.getGreen() - background.getGreen();
- int b = foreground.getBlue() - background.getBlue();
- int r2 = (r * r), g2 = (g * g), b2 = (b * b);
- return Math.sqrt(r2 + g2 + b2) / 441.673; //Roughly the maximum distance.
- }
- private static boolean isPixelDarker(Color foreground, Color background)
- {
- int fgTotal = (Math.abs(foreground.getRed() + foreground.getGreen() + foreground.getBlue()));
- int bgTotal = (Math.abs(background.getRed() + background.getGreen() + background.getBlue()));
- return fgTotal < bgTotal;
- }
- private static Color[][] overlayImage(Color[][] foreground, Color[][] background)
- {
- for (int x = 0; x < foreground.length; x++)
- {
- for (int y = 0; y < foreground[0].length; y++)
- {
- foreground[x][y] = blendPixels(foreground[x][y], background[x][y]);
- }
- }
- return foreground;
- }
- /**
- * Basically just getting a weighted average of each color relative to the foreground's alpha level.
- * Foreground gets alpha level * its color, background gets the rest * its color. Final alpha = sum of both.
- */
- private static Color blendPixels(Color foreground, Color background)
- {
- int r = ((foreground.getRed() * foreground.getAlpha()) + (background.getRed() * (255 - foreground.getAlpha()))) / 255;
- int g = ((foreground.getGreen() * foreground.getAlpha()) + (background.getGreen() * (255 - foreground.getAlpha()))) / 255;
- int b = ((foreground.getBlue() * foreground.getAlpha()) + (background.getBlue() * (255 - foreground.getAlpha()))) / 255;
- if (foreground.getAlpha() > OPACITY_THRESHOLD) //I threw this in here to see if it would remove some of the harsh outlines. Wasn't originally here.
- { //Basically, just keep the overlay pixel's colors if it's strong enough. That's why its existence doesn't make sense.
- r = foreground.getRed(); g = foreground.getGreen(); b = foreground.getBlue();
- }
- int a = foreground.getAlpha() + background.getAlpha();
- if (a < TRANSPARENCY_THRESHOLD) return new Color(0, 0, 0, 0); //Don't keep pixels that are hardly there.
- a = (int) ((double) a * TEXTURE_SHARPEN_RATIO); //Sharpen the ones that aren't.
- if (a > 255) a = 255; //Don't pass the limit.
- return new Color(r, g, b, a);
- }
- /**
- * In 8 directions, if > 5 are alpha = 0, remove pixel. Does not include border pixels.
- * Can be repeated to remove textures that are unusually narrow. Probably not wise for <32x textures.
- */
- private static Color[][] removeLonePixels(Color[][] image)
- {
- for (int x = 1; x < image.length - 1; x++)
- {
- for (int y = 1; y < image[0].length - 1; y++)
- {
- int alphaCount = getSurroundingAlphaCount(image, x, y);
- if (alphaCount > 5) image[x][y] = new Color(0, 0, 0, 0);
- }
- }
- return image;
- }
- /**
- * Goes around the edge of the textures (on the borders between the pixels with full transparency and everything else)
- * and removes them if they are too different from the average color. If the pixel is brown but most of the ore is blue,
- * it gets removed. If the background and the ore are pretty similar, nothing happens anyway.
- */
- private static Color[][] removeOffColorPixelsFromBorders(Color[][] image)
- {
- Color averageColor = getAverageColorIgnoreTransparency(image);
- for (int x = 0; x < image.length; x++)
- {
- for (int y = 0; y < image[0].length; y++)
- {
- double difference = getDifference(image[x][y], averageColor);
- try
- {
- if ((getSurroundingAlphaCount(image, x, y) > 2) && difference > 0.25)
- {
- image[x][y] = new Color(0, 0, 0, 0);
- }
- }
- catch (IndexOutOfBoundsException ignored) {image[x][y] = new Color(0, 0, 0, 0);}
- }
- }
- return image;
- }
- /**
- * Using an image with clusters of non-alpha pixels, re-add the surrounding pixels at 50% opacity.
- * Makes sure the entire ore textures are both included and blended to the background.
- */
- private static Color[][] reAddSurroundingPixels(Color[][] image, Color[][] original)
- {
- for (int x = 1; x < image.length - 1; x++)
- {
- for (int y = 1; y < image[0].length - 1; y++)
- {
- if ((image[x][y].getAlpha() == 255) && (getSurroundingAlphaCount(image, x, y) < 4))
- {
- //Neighbor pixel's alpha < half ? original pixel w/ 2/3 opacity.
- image[x + 1][y] = testAlphaAndReplaceColor(image[x + 1][y], original[x + 1][y], 127, 200);
- image[x - 1][y] = testAlphaAndReplaceColor(image[x - 1][y], original[x - 1][y], 127, 200);
- image[x][y + 1] = testAlphaAndReplaceColor(image[x][y + 1], original[x][y + 1], 127, 200);
- image[x][y - 1] = testAlphaAndReplaceColor(image[x][y - 1], original[x][y - 1], 127, 200);
- //Diagonal pixel's alpha < half ? original pixel w/ quarter opacity.
- image[x + 1][y + 1] = testAlphaAndReplaceColor(image[x + 1][y + 1], original[x + 1][y + 1], 127, 63);
- image[x - 1][y - 1] = testAlphaAndReplaceColor(image[x - 1][y - 1], original[x - 1][y - 1], 127, 63);
- image[x + 1][y - 1] = testAlphaAndReplaceColor(image[x + 1][y - 1], original[x + 1][y - 1], 127, 63);
- image[x - 1][y + 1] = testAlphaAndReplaceColor(image[x - 1][y + 1], original[x - 1][y + 1], 127, 63);
- }
- }
- }
- return image;
- }
- private static int getSurroundingAlphaCount(Color[][] image, int x, int y)
- {
- int alphaCount = 0;
- if (image[x + 1][y].getAlpha() < 20) alphaCount++;
- if (image[x - 1][y].getAlpha() < 20) alphaCount++;
- if (image[x][y + 1].getAlpha() < 20) alphaCount++;
- if (image[x][y - 1].getAlpha() < 20) alphaCount++;
- if (image[x + 1][y + 1].getAlpha() < 20) alphaCount++;
- if (image[x - 1][y - 1].getAlpha() < 20) alphaCount++;
- if (image[x + 1][y - 1].getAlpha() < 20) alphaCount++;
- if (image[x - 1][y + 1].getAlpha() < 20) alphaCount++;
- return alphaCount;
- }
- private static Color testAlphaAndReplaceColor(Color targetColor, Color originalColor, int targetAlpha, int newAlpha)
- {
- if (targetColor.getAlpha() < targetAlpha) return new Color(originalColor.getRed(), originalColor.getGreen(), originalColor.getBlue(), newAlpha);
- return targetColor;
- }
- private static Color getAverageColorIgnoreTransparency(Color[][] image)
- {
- int r = 0, g = 0, b = 0;
- int pixelCount = 0;
- for (int x = 0; x < image.length; x++)
- {
- for (int y = 0; y < image[0].length; y++)
- {
- if (image[x][y].getAlpha() > 30)
- {
- r += image[x][y].getRed();
- g += image[x][y].getGreen();
- b += image[x][y].getBlue();
- pixelCount++;
- }
- }
- }
- r /= pixelCount;
- g /= pixelCount;
- b /= pixelCount;
- return new Color(r, g, b);
- }
- private static Color[][] removePixelsUsingMask(Color[][] image, Color[][] mask)
- {
- int scaleW = 1;
- if (mask.length > image.length) scaleW = mask.length / image.length;
- if (image.length > mask.length) scaleW = image.length / mask.length;
- int scaleH = 1;
- if (mask[0].length > image[0].length) scaleH = mask[0].length / image[0].length;
- if (image[0].length > mask[0].length) scaleH = image[0].length / mask[0].length;
- for (int x = 0; x < image.length; x++)
- {
- for (int y = 0; y < image[0].length; y++)
- {
- int r = image[x][y].getRed();
- int g = image[x][y].getGreen();
- int b = image[x][y].getBlue();
- int a = (int) ((double) image[x][y].getAlpha() * (1.0 - ((double) mask[x * scaleW][y * scaleH].getAlpha() / 255)));
- if (a < 0) a = 0;
- if (a > 255) a = 255;
- image[x][y] = new Color(r, g, b, a);
- }
- }
- return image;
- }
- }
- }
Add Comment
Please, Sign In to add comment