// fills transparent pixels in the image with // the color of the (roughly) closest non-transparent neighbor pixel sclass ImageInfiller { settable BufferedImage inputImage; // how far to infill (pixels) settable int maxDepth = 2; // whether to make the infilled pixels (semi-) transparent settable int infillOpacity = 255; int w, h; int[] pixels; PtBuffer queue; PtBuffer nextQueue; BufferedImage outputImage; gettable int rounds; gettable int filledPixels; *() {} *(BufferedImage *inputImage) {} run { prepare(); do oneRound(); while (!done()); } bool prepared() { ret pixels != null; } bool done() { ret nextQueue != null && nextQueue.isEmpty() || rounds >= maxDepth; } BufferedImage get() { if (!prepared()) run(); ret pixelsToBufferedImage(pixels, w); } void prepare { pixels = pixelsFromBufferedImage(inputImage); w = inputImage.getWidth(); h = inputImage.getHeight(); } void oneRound { ++rounds; queue = nextQueue; nextQueue = new PtBuffer; assertBetween("infillOpacity", 1, 255, infillOpacity); for (p : queue == null ? pixelsInImageIterator(w, h) : queue) { int x = p.x, y = p.y; int rgba = pixels[y*w+x]; if (!isFullyTransparent(rgba)) continue; //new IntBuffer colors; // surrounding colors new MultiSet surroundingColors; for (int y2 = max(0, y-1); y2 < min(h, y+2); y2++) for (int x2 = max(0, x-1); x2 < min(w, x+2); x2++) { rgba = pixels[y2*w+x2]; if (!isFullyTransparent(rgba)) surroundingColors.add(rgba); } if (!surroundingColors.isEmpty()) { // Surrounding color found, fill pixel pixels[y*w+x] = withOpacity(infillOpacity, surroundingColors.mostPopular()); filledPixels++; // Schedule surrounding transparent pixels for (int y2 = max(0, y-1); y2 < min(h, y+2); y2++) for (int x2 = max(0, x-1); x2 < min(w, x+2); x2++) if (isFullyTransparent(pixels[y2*w+x2])) nextQueue.add(x2, y2); } else // No surrounding colors found, reschedule this pixel nextQueue.add(p); } print("Round " + rounds + " pixels filled: " + n2(filledPixels) + ", nextQueue: " + l(nextQueue)); } selfType unlimited() { maxDepth(Int.MAX_VALUE); this; } }