Guest User

Untitled

a guest
May 27th, 2018
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.53 KB | None | 0 0
  1. package personthecat.overlay32;
  2.  
  3. import java.awt.Color;
  4. import java.awt.Graphics2D;
  5. import java.awt.image.BufferedImage;
  6. import java.io.File;
  7. import java.io.IOException;
  8.  
  9. import javax.imageio.ImageIO;
  10.  
  11. public class Main
  12. {
  13. public static void main(String[] args)
  14. {
  15. String singleColorLocation = System.getProperty("user.dir") + "/generated_color.png";
  16. String overlayLocation = System.getProperty("user.dir") + "/overlay.png";
  17.  
  18. BufferedImage originalBackground = getImageFromFile(System.getProperty("user.dir") + "/background.png");
  19. BufferedImage oreTexture = getImageFromFile(System.getProperty("user.dir") + "/ore_texture.png");
  20.  
  21. Color[][] backgroundColors = getColorsFromImage(originalBackground);
  22. Color[][] oreColors = getColorsFromImage(oreTexture);
  23.  
  24. BufferedImage newBackground = createImageFromColors(fillColors(backgroundColors, getAverageColor(backgroundColors)));
  25. BufferedImage overlay = createImageFromColors(OverlayExtractor.extractBlendedOverlay(oreColors, backgroundColors));
  26.  
  27. writeImageToFile(newBackground, singleColorLocation);
  28. writeImageToFile(overlay, overlayLocation);
  29. }
  30.  
  31. private static BufferedImage getImageFromFile(String input)
  32. {
  33. BufferedImage bufferedImage = null;
  34.  
  35. try
  36. {
  37. bufferedImage = ImageIO.read(new File(input));
  38. }
  39.  
  40. catch (IOException e) {System.err.println("Error: Could not load image " + input);}
  41.  
  42. return bufferedImage;
  43. }
  44.  
  45. private static Color[][] getColorsFromImage(BufferedImage image)
  46. {
  47. int w = image.getWidth(), h = image.getHeight();
  48.  
  49. Color[][] colors = new Color[w][h];
  50.  
  51. for (int i = 0; i < w; i++)
  52. {
  53. for (int j = 0; j < h; j++)
  54. {
  55. colors[i][j] = new Color(image.getRGB(i, j), true);
  56. }
  57. }
  58.  
  59. return colors;
  60. }
  61.  
  62. private static Color getAverageColor(Color[][] image)
  63. {
  64. int r = 0, g = 0, b = 0;
  65. int w = image.length, h = image[0].length;
  66.  
  67. for (int i = 0; i < w; i++)
  68. {
  69. for (int j = 0; j < h; j++)
  70. {
  71. Color pixel = image[i][j];
  72.  
  73. r += pixel.getRed();
  74. g += pixel.getGreen();
  75. b += pixel.getBlue();
  76. }
  77. }
  78.  
  79. r /= (w * h);
  80. g /= (w * h);
  81. b /= (w * h);
  82.  
  83. return new Color(r, g, b);
  84. }
  85.  
  86. private static Color[][] fillColors(Color[][] image, Color rgb)
  87. {
  88. int w = image.length, h = image[0].length;
  89.  
  90. for (int i = 0; i < w; i++)
  91. {
  92. for (int j = 0; j < h; j++)
  93. {
  94. image[i][j] = rgb;
  95. }
  96. }
  97.  
  98. return image;
  99. }
  100.  
  101. private static BufferedImage createImageFromColors(Color[][] image)
  102. {
  103. int w = image.length, h = image[0].length;
  104.  
  105. BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
  106.  
  107. for (int i = 0; i < w; i++)
  108. {
  109. for (int j = 0; j < h; j++)
  110. {
  111. bufferedImage.setRGB(i, j, image[i][j].getRGB());
  112. }
  113. }
  114.  
  115. return bufferedImage;
  116. }
  117.  
  118. private static BufferedImage scaleBackgroundToOverlay(BufferedImage background, BufferedImage overlay)
  119. {
  120. BufferedImage scaledImage = new BufferedImage(overlay.getWidth(), overlay.getWidth(), overlay.getType());
  121.  
  122. Graphics2D graphics = scaledImage.createGraphics();
  123. graphics.drawImage(background, 0, 0, overlay.getWidth(), overlay.getWidth(), null);
  124. graphics.dispose();
  125.  
  126. return scaledImage;
  127. }
  128.  
  129. private static void writeImageToFile(BufferedImage image, String location)
  130. {
  131. try
  132. {
  133. File png = new File(location);
  134.  
  135. ImageIO.write(image, "png", png);
  136. }
  137.  
  138. catch (IOException e) {System.err.println("Error: Could not create image file.");}
  139. }
  140.  
  141. private static class OverlayExtractor
  142. {
  143. private static final double TEXTURE_SHARPEN_RATIO = 1.5; //Multiplies the alpha levels for push and pull / overlay background textures.
  144. private static final double TARGET_ALPHA_PERCENTAGE = 85.0; //The percentage of transparent pixels an overlay should have upon extraction.
  145. private static final int TRANSPARENCY_THRESHOLD = 25; //Pixels with lower alpha levels are considered transparent.
  146. private static final int OPACITY_THRESHOLD = 60; //Pixels with higher alpha levels are considered opaque.
  147.  
  148. private static double threshold = 0.8; //It's also static so repeated calls to this class can continue decreasing the threshold dynamically.
  149.  
  150. private static double getLastThreshold() {return threshold;} //Just for readability.
  151.  
  152. 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.
  153.  
  154. private static Color[][] extractNormalOverlay(Color[][] image, Color[][] background)
  155. {
  156. System.out.println("someone asked me to extract an overlay");
  157.  
  158. Color[][] overlay = extractOverlay(image, background, false);
  159.  
  160. resetThreshold();
  161.  
  162. return overlay;
  163. }
  164.  
  165. private static Color[][] extractBlendedOverlay(Color[][] image, Color[][] background)
  166. {
  167. Color[][] orePixels = extractOverlay(image, background, false);
  168. Color[][] texturePixels = extractOverlay(image, background, true);
  169. Color[][] textureMask = getColorsFromImage(getImageFromFile(System.getProperty("user.dir") + "/mask.png"));
  170.  
  171. resetThreshold();
  172.  
  173. //Ore texture changes.
  174. orePixels = removeLonePixels(removeLonePixels(orePixels)); //Remove all of the single pixels and small lines.
  175. orePixels = removeOffColorPixelsFromBorders(removeOffColorPixelsFromBorders(orePixels)); //Remove pixels that look too different from everything else.
  176. orePixels = reAddSurroundingPixels(reAddSurroundingPixels(orePixels, image), image); //The ore textures are usually too small at this point. Expand them.
  177.  
  178. //Texture texture changes.
  179. texturePixels = convertToPushAndPull(texturePixels, image, background); //Add transparency from getDifference() per-pixel using only black and white.
  180. texturePixels = removePixelsUsingMask(texturePixels, textureMask); //Use a vignette mask to lower the opacity from border pixels.
  181.  
  182. return overlayImage(orePixels, texturePixels);
  183. }
  184.  
  185. //Call this a second time before resetting to retrieve smaller differences.
  186. private static Color[][] extractOverlay(Color[][] image, Color[][] background, boolean ignoreTarget)
  187. {
  188. int w = image.length, h = image[0].length, bh = background[0].length;
  189. int frames = h / bh;
  190.  
  191. if (h % bh != 0) return null; //No decimals allowed.
  192.  
  193. Color[][] overlay = new Color[w][h];
  194.  
  195. double percentAlpha = 100.0; //Percentage of alpha pixels.
  196.  
  197. while (percentAlpha > TARGET_ALPHA_PERCENTAGE) //Most vanilla ores generate below ~63.145% alpha for 16x.
  198. { //85.0% seems fine for 32x.
  199. threshold /= 1.5;
  200. percentAlpha = 0.0;
  201.  
  202. for (int f = 0; f < frames; f++)
  203. {
  204. for (int x = 0; x < w; x++)
  205. {
  206. for (int y = 0; y < bh; y++)
  207. {
  208. int imageY = (f * bh) + y;
  209.  
  210. overlay[x][imageY] = getOrePixel(image[x][imageY], background[x][y], threshold);
  211.  
  212. percentAlpha += overlay[x][imageY].getAlpha();
  213. }
  214. }
  215. }
  216.  
  217. percentAlpha /= (frames * w * bh);
  218.  
  219. percentAlpha = 100 - (percentAlpha / 255 * 100);
  220.  
  221. if (ignoreTarget) break;
  222. }
  223.  
  224. return overlay;
  225. }
  226.  
  227. /**
  228. * Gets the difference, returns the foreground pixel if any value is outside of the accepted RGB range (0-255).
  229. */
  230. private static Color getOrePixel(Color foreground, Color background, double alphaPercent)
  231. {
  232. if (foreground.getRGB() == background.getRGB()) return new Color(0, 0, 0, 0);
  233.  
  234. int r = (int) ((foreground.getRed() - background.getRed() * (1 - alphaPercent)) / alphaPercent);
  235. int g = (int) ((foreground.getGreen() - background.getGreen() * (1 - alphaPercent)) / alphaPercent);
  236. int b = (int) ((foreground.getBlue() - background.getBlue() * (1 - alphaPercent)) / alphaPercent);
  237.  
  238. //if any is > 255 || < 0 -> keep foreground pixel.
  239. if (r > 255 || g > 255 || b > 255 || r < 0 || g < 0 || b < 0) return foreground;
  240.  
  241. return new Color(0, 0, 0, 0); //else return nothing.
  242. }
  243.  
  244. /**
  245. * Uses getDifference() to determine the alpha level for each pixel.
  246. * Uses isPixelDarker() to determine whether each pixel should be black or white (push or pull)
  247. */
  248. private static Color[][] convertToPushAndPull(Color[][] newImage, Color[][] originalImage, Color[][] background)
  249. {
  250. for (int x = 0; x < newImage.length; x++)
  251. {
  252. for (int y = 0; y < newImage[0].length; y++)
  253. {
  254. int alpha = (int) (255 * getDifference(originalImage[x][y], background[x][y]));
  255.  
  256. if (isPixelDarker(originalImage[x][y], background[x][y])) newImage[x][y] = new Color(0, 0, 0, alpha);
  257.  
  258. else newImage[x][y] = new Color(255, 255, 255, alpha);
  259. }
  260. }
  261.  
  262. return newImage;
  263. }
  264.  
  265. /**
  266. * Using the distance formula to determine the 0 - 1 "distance" between each color.
  267. * Imagine r, g, and b as x, y, and z on a coordinate plane; they are dimensions, not additive values.
  268. * e.g. Color(255, 255, 0) and Color(255, 0, 255) are totally different colors, so getting the average doesn't work.
  269. */
  270. private static double getDifference(Color foreground, Color background)
  271. {
  272. int r = foreground.getRed() - background.getRed();
  273. int g = foreground.getGreen() - background.getGreen();
  274. int b = foreground.getBlue() - background.getBlue();
  275.  
  276. int r2 = (r * r), g2 = (g * g), b2 = (b * b);
  277.  
  278. return Math.sqrt(r2 + g2 + b2) / 441.673; //Roughly the maximum distance.
  279. }
  280.  
  281. private static boolean isPixelDarker(Color foreground, Color background)
  282. {
  283. int fgTotal = (Math.abs(foreground.getRed() + foreground.getGreen() + foreground.getBlue()));
  284. int bgTotal = (Math.abs(background.getRed() + background.getGreen() + background.getBlue()));
  285.  
  286. return fgTotal < bgTotal;
  287. }
  288.  
  289. private static Color[][] overlayImage(Color[][] foreground, Color[][] background)
  290. {
  291. for (int x = 0; x < foreground.length; x++)
  292. {
  293. for (int y = 0; y < foreground[0].length; y++)
  294. {
  295. foreground[x][y] = blendPixels(foreground[x][y], background[x][y]);
  296. }
  297. }
  298.  
  299. return foreground;
  300. }
  301.  
  302. /**
  303. * Basically just getting a weighted average of each color relative to the foreground's alpha level.
  304. * Foreground gets alpha level * its color, background gets the rest * its color. Final alpha = sum of both.
  305. */
  306. private static Color blendPixels(Color foreground, Color background)
  307. {
  308. int r = ((foreground.getRed() * foreground.getAlpha()) + (background.getRed() * (255 - foreground.getAlpha()))) / 255;
  309. int g = ((foreground.getGreen() * foreground.getAlpha()) + (background.getGreen() * (255 - foreground.getAlpha()))) / 255;
  310. int b = ((foreground.getBlue() * foreground.getAlpha()) + (background.getBlue() * (255 - foreground.getAlpha()))) / 255;
  311.  
  312. 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.
  313. { //Basically, just keep the overlay pixel's colors if it's strong enough. That's why its existence doesn't make sense.
  314. r = foreground.getRed(); g = foreground.getGreen(); b = foreground.getBlue();
  315. }
  316.  
  317. int a = foreground.getAlpha() + background.getAlpha();
  318.  
  319. if (a < TRANSPARENCY_THRESHOLD) return new Color(0, 0, 0, 0); //Don't keep pixels that are hardly there.
  320.  
  321. a = (int) ((double) a * TEXTURE_SHARPEN_RATIO); //Sharpen the ones that aren't.
  322.  
  323. if (a > 255) a = 255; //Don't pass the limit.
  324.  
  325. return new Color(r, g, b, a);
  326. }
  327.  
  328. /**
  329. * In 8 directions, if > 5 are alpha = 0, remove pixel. Does not include border pixels.
  330. * Can be repeated to remove textures that are unusually narrow. Probably not wise for <32x textures.
  331. */
  332. private static Color[][] removeLonePixels(Color[][] image)
  333. {
  334. for (int x = 1; x < image.length - 1; x++)
  335. {
  336. for (int y = 1; y < image[0].length - 1; y++)
  337. {
  338. int alphaCount = getSurroundingAlphaCount(image, x, y);
  339.  
  340. if (alphaCount > 5) image[x][y] = new Color(0, 0, 0, 0);
  341. }
  342. }
  343.  
  344. return image;
  345. }
  346.  
  347. /**
  348. * Goes around the edge of the textures (on the borders between the pixels with full transparency and everything else)
  349. * and removes them if they are too different from the average color. If the pixel is brown but most of the ore is blue,
  350. * it gets removed. If the background and the ore are pretty similar, nothing happens anyway.
  351. */
  352. private static Color[][] removeOffColorPixelsFromBorders(Color[][] image)
  353. {
  354. Color averageColor = getAverageColorIgnoreTransparency(image);
  355.  
  356. for (int x = 0; x < image.length; x++)
  357. {
  358. for (int y = 0; y < image[0].length; y++)
  359. {
  360. double difference = getDifference(image[x][y], averageColor);
  361.  
  362. try
  363. {
  364. if ((getSurroundingAlphaCount(image, x, y) > 2) && difference > 0.25)
  365. {
  366. image[x][y] = new Color(0, 0, 0, 0);
  367. }
  368. }
  369.  
  370. catch (IndexOutOfBoundsException ignored) {image[x][y] = new Color(0, 0, 0, 0);}
  371. }
  372. }
  373.  
  374. return image;
  375. }
  376.  
  377. /**
  378. * Using an image with clusters of non-alpha pixels, re-add the surrounding pixels at 50% opacity.
  379. * Makes sure the entire ore textures are both included and blended to the background.
  380. */
  381. private static Color[][] reAddSurroundingPixels(Color[][] image, Color[][] original)
  382. {
  383. for (int x = 1; x < image.length - 1; x++)
  384. {
  385. for (int y = 1; y < image[0].length - 1; y++)
  386. {
  387. if ((image[x][y].getAlpha() == 255) && (getSurroundingAlphaCount(image, x, y) < 4))
  388. {
  389. //Neighbor pixel's alpha < half ? original pixel w/ 2/3 opacity.
  390. image[x + 1][y] = testAlphaAndReplaceColor(image[x + 1][y], original[x + 1][y], 127, 200);
  391. image[x - 1][y] = testAlphaAndReplaceColor(image[x - 1][y], original[x - 1][y], 127, 200);
  392. image[x][y + 1] = testAlphaAndReplaceColor(image[x][y + 1], original[x][y + 1], 127, 200);
  393. image[x][y - 1] = testAlphaAndReplaceColor(image[x][y - 1], original[x][y - 1], 127, 200);
  394.  
  395. //Diagonal pixel's alpha < half ? original pixel w/ quarter opacity.
  396. image[x + 1][y + 1] = testAlphaAndReplaceColor(image[x + 1][y + 1], original[x + 1][y + 1], 127, 63);
  397. image[x - 1][y - 1] = testAlphaAndReplaceColor(image[x - 1][y - 1], original[x - 1][y - 1], 127, 63);
  398. image[x + 1][y - 1] = testAlphaAndReplaceColor(image[x + 1][y - 1], original[x + 1][y - 1], 127, 63);
  399. image[x - 1][y + 1] = testAlphaAndReplaceColor(image[x - 1][y + 1], original[x - 1][y + 1], 127, 63);
  400. }
  401. }
  402. }
  403.  
  404. return image;
  405. }
  406.  
  407. private static int getSurroundingAlphaCount(Color[][] image, int x, int y)
  408. {
  409. int alphaCount = 0;
  410.  
  411. if (image[x + 1][y].getAlpha() < 20) alphaCount++;
  412. if (image[x - 1][y].getAlpha() < 20) alphaCount++;
  413. if (image[x][y + 1].getAlpha() < 20) alphaCount++;
  414. if (image[x][y - 1].getAlpha() < 20) alphaCount++;
  415. if (image[x + 1][y + 1].getAlpha() < 20) alphaCount++;
  416. if (image[x - 1][y - 1].getAlpha() < 20) alphaCount++;
  417. if (image[x + 1][y - 1].getAlpha() < 20) alphaCount++;
  418. if (image[x - 1][y + 1].getAlpha() < 20) alphaCount++;
  419.  
  420. return alphaCount;
  421. }
  422.  
  423. private static Color testAlphaAndReplaceColor(Color targetColor, Color originalColor, int targetAlpha, int newAlpha)
  424. {
  425. if (targetColor.getAlpha() < targetAlpha) return new Color(originalColor.getRed(), originalColor.getGreen(), originalColor.getBlue(), newAlpha);
  426.  
  427. return targetColor;
  428. }
  429.  
  430. private static Color getAverageColorIgnoreTransparency(Color[][] image)
  431. {
  432. int r = 0, g = 0, b = 0;
  433. int pixelCount = 0;
  434.  
  435. for (int x = 0; x < image.length; x++)
  436. {
  437. for (int y = 0; y < image[0].length; y++)
  438. {
  439. if (image[x][y].getAlpha() > 30)
  440. {
  441. r += image[x][y].getRed();
  442. g += image[x][y].getGreen();
  443. b += image[x][y].getBlue();
  444.  
  445. pixelCount++;
  446. }
  447. }
  448. }
  449.  
  450. r /= pixelCount;
  451. g /= pixelCount;
  452. b /= pixelCount;
  453.  
  454. return new Color(r, g, b);
  455. }
  456.  
  457. private static Color[][] removePixelsUsingMask(Color[][] image, Color[][] mask)
  458. {
  459. int scaleW = 1;
  460.  
  461. if (mask.length > image.length) scaleW = mask.length / image.length;
  462. if (image.length > mask.length) scaleW = image.length / mask.length;
  463.  
  464. int scaleH = 1;
  465.  
  466. if (mask[0].length > image[0].length) scaleH = mask[0].length / image[0].length;
  467. if (image[0].length > mask[0].length) scaleH = image[0].length / mask[0].length;
  468.  
  469. for (int x = 0; x < image.length; x++)
  470. {
  471. for (int y = 0; y < image[0].length; y++)
  472. {
  473. int r = image[x][y].getRed();
  474. int g = image[x][y].getGreen();
  475. int b = image[x][y].getBlue();
  476. int a = (int) ((double) image[x][y].getAlpha() * (1.0 - ((double) mask[x * scaleW][y * scaleH].getAlpha() / 255)));
  477.  
  478. if (a < 0) a = 0;
  479. if (a > 255) a = 255;
  480.  
  481. image[x][y] = new Color(r, g, b, a);
  482. }
  483. }
  484.  
  485. return image;
  486. }
  487. }
  488. }
Add Comment
Please, Sign In to add comment