// fills fully 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; IntBuffer queue; // pixel indices for current round IntBuffer nextQueue; // pixel indices for next round 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 IntBuffer; new BitSet queued; assertBetween("infillOpacity", 1, 255, infillOpacity); new IntBuffer toFill; // pixel index, color, pixel index, color, ... for (pos : queue == null ? countIterator(w*h) : queue) { int rgba = pixels[pos]; if (!isFullyTransparent(rgba)) continue; int x = pos % w, y = pos/w; 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(withOpacity(infillOpacity, rgba)); } if (!surroundingColors.isEmpty()) { // Surrounding color found, fill pixel toFill.add(y*w+x); toFill.add(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++) { int pos2 = y2*w+x2; if (!queued.get(pos2) && isFullyTransparent(pixels[pos2])) { queued.set(pos2); nextQueue.add(pos2); } } } else // No surrounding colors found, reschedule this pixel nextQueue.add(pos); } int n = l(toFill); for (int i = 0; i < n; i += 2) pixels[toFill.get(i)] = toFill.get(i+1); print("Round " + rounds + " pixels filled: " + n2(filledPixels) + ", nextQueue: " + l(nextQueue)); } selfType unlimited() { maxDepth(Int.MAX_VALUE); this; } }