1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.tools.texturepacker; 18 19 import com.badlogic.gdx.tools.texturepacker.ColorBleedEffect.Mask.MaskIterator; 20 21 import java.awt.image.BufferedImage; 22 import java.util.NoSuchElementException; 23 24 /** @author Ruben Garat 25 * @author Ariel Coppes 26 * @author Nathan Sweet */ 27 public class ColorBleedEffect { 28 static int TO_PROCESS = 0; 29 static int IN_PROCESS = 1; 30 static int REALDATA = 2; 31 static int[][] offsets = { {-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}}; 32 33 ARGBColor color = new ARGBColor(); 34 35 public BufferedImage processImage (BufferedImage image, int maxIterations) { 36 int width = image.getWidth(); 37 int height = image.getHeight(); 38 39 BufferedImage processedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 40 int[] rgb = image.getRGB(0, 0, width, height, null, 0, width); 41 Mask mask = new Mask(rgb); 42 43 int iterations = 0; 44 int lastPending = -1; 45 while (mask.pendingSize > 0 && mask.pendingSize != lastPending && iterations < maxIterations) { 46 lastPending = mask.pendingSize; 47 executeIteration(rgb, mask, width, height); 48 iterations++; 49 } 50 51 processedImage.setRGB(0, 0, width, height, rgb, 0, width); 52 return processedImage; 53 } 54 55 private void executeIteration (int[] rgb, Mask mask, int width, int height) { 56 MaskIterator iterator = mask.new MaskIterator(); 57 while (iterator.hasNext()) { 58 int pixelIndex = iterator.next(); 59 int x = pixelIndex % width; 60 int y = pixelIndex / width; 61 int r = 0, g = 0, b = 0; 62 int count = 0; 63 64 for (int i = 0, n = offsets.length; i < n; i++) { 65 int[] offset = offsets[i]; 66 int column = x + offset[0]; 67 int row = y + offset[1]; 68 69 if (column < 0 || column >= width || row < 0 || row >= height) continue; 70 71 int currentPixelIndex = getPixelIndex(width, column, row); 72 if (mask.getMask(currentPixelIndex) == REALDATA) { 73 color.argb = rgb[currentPixelIndex]; 74 r += color.red(); 75 g += color.green(); 76 b += color.blue(); 77 count++; 78 } 79 } 80 81 if (count != 0) { 82 color.setARGBA(0, r / count, g / count, b / count); 83 rgb[pixelIndex] = color.argb; 84 iterator.markAsInProgress(); 85 } 86 } 87 88 iterator.reset(); 89 } 90 91 private int getPixelIndex (int width, int x, int y) { 92 return y * width + x; 93 } 94 95 static class Mask { 96 int[] data, pending, changing; 97 int pendingSize, changingSize; 98 99 Mask (int[] rgb) { 100 data = new int[rgb.length]; 101 pending = new int[rgb.length]; 102 changing = new int[rgb.length]; 103 ARGBColor color = new ARGBColor(); 104 for (int i = 0; i < rgb.length; i++) { 105 color.argb = rgb[i]; 106 if (color.alpha() == 0) { 107 data[i] = TO_PROCESS; 108 pending[pendingSize] = i; 109 pendingSize++; 110 } else 111 data[i] = REALDATA; 112 } 113 } 114 115 int getMask (int index) { 116 return data[index]; 117 } 118 119 int removeIndex (int index) { 120 if (index >= pendingSize) throw new IndexOutOfBoundsException(String.valueOf(index)); 121 int value = pending[index]; 122 pendingSize--; 123 pending[index] = pending[pendingSize]; 124 return value; 125 } 126 127 class MaskIterator { 128 private int index; 129 130 boolean hasNext () { 131 return index < pendingSize; 132 } 133 134 int next () { 135 if (index >= pendingSize) throw new NoSuchElementException(String.valueOf(index)); 136 return pending[index++]; 137 } 138 139 void markAsInProgress () { 140 index--; 141 changing[changingSize] = removeIndex(index); 142 changingSize++; 143 } 144 145 void reset () { 146 index = 0; 147 for (int i = 0; i < changingSize; i++) { 148 int index = changing[i]; 149 data[index] = REALDATA; 150 } 151 changingSize = 0; 152 } 153 } 154 } 155 156 static class ARGBColor { 157 int argb = 0xff000000; 158 159 public int red () { 160 return (argb >> 16) & 0xFF; 161 } 162 163 public int green () { 164 return (argb >> 8) & 0xFF; 165 } 166 167 public int blue () { 168 return (argb >> 0) & 0xFF; 169 } 170 171 public int alpha () { 172 return (argb >> 24) & 0xff; 173 } 174 175 public void setARGBA (int a, int r, int g, int b) { 176 if (a < 0 || a > 255 || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) 177 throw new IllegalArgumentException("Invalid RGBA: " + r + ", " + g + "," + b + "," + a); 178 argb = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0); 179 } 180 } 181 } 182