1 /* 2 * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt.image; 27 28 import java.awt.image.ImageConsumer; 29 import java.awt.image.ColorModel; 30 31 /** 32 * This class provides an easy way to create an ImageFilter which modifies 33 * the pixels of an image in the default RGB ColorModel. It is meant to 34 * be used in conjunction with a FilteredImageSource object to produce 35 * filtered versions of existing images. It is an abstract class that 36 * provides the calls needed to channel all of the pixel data through a 37 * single method which converts pixels one at a time in the default RGB 38 * ColorModel regardless of the ColorModel being used by the ImageProducer. 39 * The only method which needs to be defined to create a useable image 40 * filter is the filterRGB method. Here is an example of a definition 41 * of a filter which swaps the red and blue components of an image: 42 * <pre>{@code 43 * 44 * class RedBlueSwapFilter extends RGBImageFilter { 45 * public RedBlueSwapFilter() { 46 * // The filter's operation does not depend on the 47 * // pixel's location, so IndexColorModels can be 48 * // filtered directly. 49 * canFilterIndexColorModel = true; 50 * } 51 * 52 * public int filterRGB(int x, int y, int rgb) { 53 * return ((rgb & 0xff00ff00) 54 * | ((rgb & 0xff0000) >> 16) 55 * | ((rgb & 0xff) << 16)); 56 * } 57 * } 58 * 59 * }</pre> 60 * 61 * @see FilteredImageSource 62 * @see ImageFilter 63 * @see ColorModel#getRGBdefault 64 * 65 * @author Jim Graham 66 */ 67 public abstract class RGBImageFilter extends ImageFilter { 68 69 /** 70 * The {@code ColorModel} to be replaced by 71 * {@code newmodel} when the user calls 72 * {@link #substituteColorModel(ColorModel, ColorModel) substituteColorModel}. 73 */ 74 protected ColorModel origmodel; 75 76 /** 77 * The {@code ColorModel} with which to 78 * replace {@code origmodel} when the user calls 79 * {@code substituteColorModel}. 80 */ 81 protected ColorModel newmodel; 82 83 /** 84 * This boolean indicates whether or not it is acceptable to apply 85 * the color filtering of the filterRGB method to the color table 86 * entries of an IndexColorModel object in lieu of pixel by pixel 87 * filtering. Subclasses should set this variable to true in their 88 * constructor if their filterRGB method does not depend on the 89 * coordinate of the pixel being filtered. 90 * @see #substituteColorModel 91 * @see #filterRGB 92 * @see IndexColorModel 93 */ 94 protected boolean canFilterIndexColorModel; 95 96 /** 97 * If the ColorModel is an IndexColorModel and the subclass has 98 * set the canFilterIndexColorModel flag to true, we substitute 99 * a filtered version of the color model here and wherever 100 * that original ColorModel object appears in the setPixels methods. 101 * If the ColorModel is not an IndexColorModel or is null, this method 102 * overrides the default ColorModel used by the ImageProducer and 103 * specifies the default RGB ColorModel instead. 104 * <p> 105 * Note: This method is intended to be called by the 106 * {@code ImageProducer} of the {@code Image} whose pixels 107 * are being filtered. Developers using 108 * this class to filter pixels from an image should avoid calling 109 * this method directly since that operation could interfere 110 * with the filtering operation. 111 * @see ImageConsumer 112 * @see ColorModel#getRGBdefault 113 */ 114 public void setColorModel(ColorModel model) { 115 if (canFilterIndexColorModel && (model instanceof IndexColorModel)) { 116 ColorModel newcm = filterIndexColorModel((IndexColorModel)model); 117 substituteColorModel(model, newcm); 118 consumer.setColorModel(newcm); 119 } else { 120 consumer.setColorModel(ColorModel.getRGBdefault()); 121 } 122 } 123 124 /** 125 * Registers two ColorModel objects for substitution. If the oldcm 126 * is encountered during any of the setPixels methods, the newcm 127 * is substituted and the pixels passed through 128 * untouched (but with the new ColorModel object). 129 * @param oldcm the ColorModel object to be replaced on the fly 130 * @param newcm the ColorModel object to replace oldcm on the fly 131 */ 132 public void substituteColorModel(ColorModel oldcm, ColorModel newcm) { 133 origmodel = oldcm; 134 newmodel = newcm; 135 } 136 137 /** 138 * Filters an IndexColorModel object by running each entry in its 139 * color tables through the filterRGB function that RGBImageFilter 140 * subclasses must provide. Uses coordinates of -1 to indicate that 141 * a color table entry is being filtered rather than an actual 142 * pixel value. 143 * @param icm the IndexColorModel object to be filtered 144 * @exception NullPointerException if {@code icm} is null 145 * @return a new IndexColorModel representing the filtered colors 146 */ 147 public IndexColorModel filterIndexColorModel(IndexColorModel icm) { 148 int mapsize = icm.getMapSize(); 149 byte r[] = new byte[mapsize]; 150 byte g[] = new byte[mapsize]; 151 byte b[] = new byte[mapsize]; 152 byte a[] = new byte[mapsize]; 153 icm.getReds(r); 154 icm.getGreens(g); 155 icm.getBlues(b); 156 icm.getAlphas(a); 157 int trans = icm.getTransparentPixel(); 158 boolean needalpha = false; 159 for (int i = 0; i < mapsize; i++) { 160 int rgb = filterRGB(-1, -1, icm.getRGB(i)); 161 a[i] = (byte) (rgb >> 24); 162 if (a[i] != ((byte)0xff) && i != trans) { 163 needalpha = true; 164 } 165 r[i] = (byte) (rgb >> 16); 166 g[i] = (byte) (rgb >> 8); 167 b[i] = (byte) (rgb >> 0); 168 } 169 if (needalpha) { 170 return new IndexColorModel(icm.getPixelSize(), mapsize, 171 r, g, b, a); 172 } else { 173 return new IndexColorModel(icm.getPixelSize(), mapsize, 174 r, g, b, trans); 175 } 176 } 177 178 /** 179 * Filters a buffer of pixels in the default RGB ColorModel by passing 180 * them one by one through the filterRGB method. 181 * @param x the X coordinate of the upper-left corner of the region 182 * of pixels 183 * @param y the Y coordinate of the upper-left corner of the region 184 * of pixels 185 * @param w the width of the region of pixels 186 * @param h the height of the region of pixels 187 * @param pixels the array of pixels 188 * @param off the offset into the {@code pixels} array 189 * @param scansize the distance from one row of pixels to the next 190 * in the array 191 * @see ColorModel#getRGBdefault 192 * @see #filterRGB 193 */ 194 public void filterRGBPixels(int x, int y, int w, int h, 195 int pixels[], int off, int scansize) { 196 int index = off; 197 for (int cy = 0; cy < h; cy++) { 198 for (int cx = 0; cx < w; cx++) { 199 pixels[index] = filterRGB(x + cx, y + cy, pixels[index]); 200 index++; 201 } 202 index += scansize - w; 203 } 204 consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), 205 pixels, off, scansize); 206 } 207 208 /** 209 * If the ColorModel object is the same one that has already 210 * been converted, then simply passes the pixels through with the 211 * converted ColorModel. Otherwise converts the buffer of byte 212 * pixels to the default RGB ColorModel and passes the converted 213 * buffer to the filterRGBPixels method to be converted one by one. 214 * <p> 215 * Note: This method is intended to be called by the 216 * {@code ImageProducer} of the {@code Image} whose pixels 217 * are being filtered. Developers using 218 * this class to filter pixels from an image should avoid calling 219 * this method directly since that operation could interfere 220 * with the filtering operation. 221 * @see ColorModel#getRGBdefault 222 * @see #filterRGBPixels 223 */ 224 public void setPixels(int x, int y, int w, int h, 225 ColorModel model, byte pixels[], int off, 226 int scansize) { 227 if (model == origmodel) { 228 consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize); 229 } else { 230 int filteredpixels[] = new int[w]; 231 int index = off; 232 for (int cy = 0; cy < h; cy++) { 233 for (int cx = 0; cx < w; cx++) { 234 filteredpixels[cx] = model.getRGB((pixels[index] & 0xff)); 235 index++; 236 } 237 index += scansize - w; 238 filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w); 239 } 240 } 241 } 242 243 /** 244 * If the ColorModel object is the same one that has already 245 * been converted, then simply passes the pixels through with the 246 * converted ColorModel, otherwise converts the buffer of integer 247 * pixels to the default RGB ColorModel and passes the converted 248 * buffer to the filterRGBPixels method to be converted one by one. 249 * Converts a buffer of integer pixels to the default RGB ColorModel 250 * and passes the converted buffer to the filterRGBPixels method. 251 * <p> 252 * Note: This method is intended to be called by the 253 * {@code ImageProducer} of the {@code Image} whose pixels 254 * are being filtered. Developers using 255 * this class to filter pixels from an image should avoid calling 256 * this method directly since that operation could interfere 257 * with the filtering operation. 258 * @see ColorModel#getRGBdefault 259 * @see #filterRGBPixels 260 */ 261 public void setPixels(int x, int y, int w, int h, 262 ColorModel model, int pixels[], int off, 263 int scansize) { 264 if (model == origmodel) { 265 consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize); 266 } else { 267 int filteredpixels[] = new int[w]; 268 int index = off; 269 for (int cy = 0; cy < h; cy++) { 270 for (int cx = 0; cx < w; cx++) { 271 filteredpixels[cx] = model.getRGB(pixels[index]); 272 index++; 273 } 274 index += scansize - w; 275 filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w); 276 } 277 } 278 } 279 280 /** 281 * Subclasses must specify a method to convert a single input pixel 282 * in the default RGB ColorModel to a single output pixel. 283 * @param x the X coordinate of the pixel 284 * @param y the Y coordinate of the pixel 285 * @param rgb the integer pixel representation in the default RGB 286 * color model 287 * @return a filtered pixel in the default RGB color model. 288 * @see ColorModel#getRGBdefault 289 * @see #filterRGBPixels 290 */ 291 public abstract int filterRGB(int x, int y, int rgb); 292 }