1 /* 2 * Copyright (c) 1995, 2020, 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 * Creates an {@code RGBImageFilter}. 71 */ 72 protected RGBImageFilter() {} 73 74 /** 75 * The {@code ColorModel} to be replaced by 76 * {@code newmodel} when the user calls 77 * {@link #substituteColorModel(ColorModel, ColorModel) substituteColorModel}. 78 */ 79 protected ColorModel origmodel; 80 81 /** 82 * The {@code ColorModel} with which to 83 * replace {@code origmodel} when the user calls 84 * {@code substituteColorModel}. 85 */ 86 protected ColorModel newmodel; 87 88 /** 89 * This boolean indicates whether or not it is acceptable to apply 90 * the color filtering of the filterRGB method to the color table 91 * entries of an IndexColorModel object in lieu of pixel by pixel 92 * filtering. Subclasses should set this variable to true in their 93 * constructor if their filterRGB method does not depend on the 94 * coordinate of the pixel being filtered. 95 * @see #substituteColorModel 96 * @see #filterRGB 97 * @see IndexColorModel 98 */ 99 protected boolean canFilterIndexColorModel; 100 101 /** 102 * If the ColorModel is an IndexColorModel and the subclass has 103 * set the canFilterIndexColorModel flag to true, we substitute 104 * a filtered version of the color model here and wherever 105 * that original ColorModel object appears in the setPixels methods. 106 * If the ColorModel is not an IndexColorModel or is null, this method 107 * overrides the default ColorModel used by the ImageProducer and 108 * specifies the default RGB ColorModel instead. 109 * <p> 110 * Note: This method is intended to be called by the 111 * {@code ImageProducer} of the {@code Image} whose pixels 112 * are being filtered. Developers using 113 * this class to filter pixels from an image should avoid calling 114 * this method directly since that operation could interfere 115 * with the filtering operation. 116 * @see ImageConsumer 117 * @see ColorModel#getRGBdefault 118 */ 119 public void setColorModel(ColorModel model) { 120 if (canFilterIndexColorModel && (model instanceof IndexColorModel)) { 121 ColorModel newcm = filterIndexColorModel((IndexColorModel)model); 122 substituteColorModel(model, newcm); 123 consumer.setColorModel(newcm); 124 } else { 125 consumer.setColorModel(ColorModel.getRGBdefault()); 126 } 127 } 128 129 /** 130 * Registers two ColorModel objects for substitution. If the oldcm 131 * is encountered during any of the setPixels methods, the newcm 132 * is substituted and the pixels passed through 133 * untouched (but with the new ColorModel object). 134 * @param oldcm the ColorModel object to be replaced on the fly 135 * @param newcm the ColorModel object to replace oldcm on the fly 136 */ 137 public void substituteColorModel(ColorModel oldcm, ColorModel newcm) { 138 origmodel = oldcm; 139 newmodel = newcm; 140 } 141 142 /** 143 * Filters an IndexColorModel object by running each entry in its 144 * color tables through the filterRGB function that RGBImageFilter 145 * subclasses must provide. Uses coordinates of -1 to indicate that 146 * a color table entry is being filtered rather than an actual 147 * pixel value. 148 * @param icm the IndexColorModel object to be filtered 149 * @exception NullPointerException if {@code icm} is null 150 * @return a new IndexColorModel representing the filtered colors 151 */ 152 public IndexColorModel filterIndexColorModel(IndexColorModel icm) { 153 int mapsize = icm.getMapSize(); 154 byte[] r = new byte[mapsize]; 155 byte[] g = new byte[mapsize]; 156 byte[] b = new byte[mapsize]; 157 byte[] a = new byte[mapsize]; 158 icm.getReds(r); 159 icm.getGreens(g); 160 icm.getBlues(b); 161 icm.getAlphas(a); 162 int trans = icm.getTransparentPixel(); 163 boolean needalpha = false; 164 for (int i = 0; i < mapsize; i++) { 165 int rgb = filterRGB(-1, -1, icm.getRGB(i)); 166 a[i] = (byte) (rgb >> 24); 167 if (a[i] != ((byte)0xff) && i != trans) { 168 needalpha = true; 169 } 170 r[i] = (byte) (rgb >> 16); 171 g[i] = (byte) (rgb >> 8); 172 b[i] = (byte) (rgb >> 0); 173 } 174 if (needalpha) { 175 return new IndexColorModel(icm.getPixelSize(), mapsize, 176 r, g, b, a); 177 } else { 178 return new IndexColorModel(icm.getPixelSize(), mapsize, 179 r, g, b, trans); 180 } 181 } 182 183 /** 184 * Filters a buffer of pixels in the default RGB ColorModel by passing 185 * them one by one through the filterRGB method. 186 * @param x the X coordinate of the upper-left corner of the region 187 * of pixels 188 * @param y the Y coordinate of the upper-left corner of the region 189 * of pixels 190 * @param w the width of the region of pixels 191 * @param h the height of the region of pixels 192 * @param pixels the array of pixels 193 * @param off the offset into the {@code pixels} array 194 * @param scansize the distance from one row of pixels to the next 195 * in the array 196 * @see ColorModel#getRGBdefault 197 * @see #filterRGB 198 */ 199 public void filterRGBPixels(int x, int y, int w, int h, 200 int[] pixels, int off, int scansize) { 201 int index = off; 202 for (int cy = 0; cy < h; cy++) { 203 for (int cx = 0; cx < w; cx++) { 204 pixels[index] = filterRGB(x + cx, y + cy, pixels[index]); 205 index++; 206 } 207 index += scansize - w; 208 } 209 consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), 210 pixels, off, scansize); 211 } 212 213 /** 214 * If the ColorModel object is the same one that has already 215 * been converted, then simply passes the pixels through with the 216 * converted ColorModel. Otherwise converts the buffer of byte 217 * pixels to the default RGB ColorModel and passes the converted 218 * buffer to the filterRGBPixels method to be converted one by one. 219 * <p> 220 * Note: This method is intended to be called by the 221 * {@code ImageProducer} of the {@code Image} whose pixels 222 * are being filtered. Developers using 223 * this class to filter pixels from an image should avoid calling 224 * this method directly since that operation could interfere 225 * with the filtering operation. 226 * @see ColorModel#getRGBdefault 227 * @see #filterRGBPixels 228 */ 229 public void setPixels(int x, int y, int w, int h, 230 ColorModel model, byte[] pixels, int off, 231 int scansize) { 232 if (model == origmodel) { 233 consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize); 234 } else { 235 int[] filteredpixels = new int[w]; 236 int index = off; 237 for (int cy = 0; cy < h; cy++) { 238 for (int cx = 0; cx < w; cx++) { 239 filteredpixels[cx] = model.getRGB((pixels[index] & 0xff)); 240 index++; 241 } 242 index += scansize - w; 243 filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w); 244 } 245 } 246 } 247 248 /** 249 * If the ColorModel object is the same one that has already 250 * been converted, then simply passes the pixels through with the 251 * converted ColorModel, otherwise converts the buffer of integer 252 * pixels to the default RGB ColorModel and passes the converted 253 * buffer to the filterRGBPixels method to be converted one by one. 254 * Converts a buffer of integer pixels to the default RGB ColorModel 255 * and passes the converted buffer to the filterRGBPixels method. 256 * <p> 257 * Note: This method is intended to be called by the 258 * {@code ImageProducer} of the {@code Image} whose pixels 259 * are being filtered. Developers using 260 * this class to filter pixels from an image should avoid calling 261 * this method directly since that operation could interfere 262 * with the filtering operation. 263 * @see ColorModel#getRGBdefault 264 * @see #filterRGBPixels 265 */ 266 public void setPixels(int x, int y, int w, int h, 267 ColorModel model, int[] pixels, int off, 268 int scansize) { 269 if (model == origmodel) { 270 consumer.setPixels(x, y, w, h, newmodel, pixels, off, scansize); 271 } else { 272 int[] filteredpixels = new int[w]; 273 int index = off; 274 for (int cy = 0; cy < h; cy++) { 275 for (int cx = 0; cx < w; cx++) { 276 filteredpixels[cx] = model.getRGB(pixels[index]); 277 index++; 278 } 279 index += scansize - w; 280 filterRGBPixels(x, y + cy, w, 1, filteredpixels, 0, w); 281 } 282 } 283 } 284 285 /** 286 * Subclasses must specify a method to convert a single input pixel 287 * in the default RGB ColorModel to a single output pixel. 288 * @param x the X coordinate of the pixel 289 * @param y the Y coordinate of the pixel 290 * @param rgb the integer pixel representation in the default RGB 291 * color model 292 * @return a filtered pixel in the default RGB color model. 293 * @see ColorModel#getRGBdefault 294 * @see #filterRGBPixels 295 */ 296 public abstract int filterRGB(int x, int y, int rgb); 297 }