001 /* 002 * BufferedRGB24Image 003 * 004 * Copyright (c) 2002 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.gui.awt; 009 010 import java.awt.image.BufferedImage; 011 import net.sourceforge.jiu.data.PixelImage; 012 import net.sourceforge.jiu.data.RGB24Image; 013 014 /** 015 * A bridge class to use {@link java.awt.image.BufferedImage} objects (class defined 016 * in the standard runtime library, package <code>java.awt.image</code>) as 017 * {@link net.sourceforge.jiu.data.RGB24Image} objects within JIU. 018 * This class encapsulates a single {@link java.awt.image.BufferedImage} object. 019 * It enables reusing existing BufferedImage objects as input or 020 * output of JIU operations, 021 * removing the necessity for the conversion step from <code>java.awt.Image</code> 022 * to <code>net.sourceforge.jiu.data.PixelImage</code> (or vice versa) 023 * and thus reducing memory consumption. 024 * The name of this class is a combination of BufferedImage (the class of the object 025 * that is encapsulated) and RGB24Image (the JIU image data interface). 026 * <p> 027 * Internally, this class uses {@link java.awt.image.BufferedImage}'s getRGB and 028 * setRGB methods to access image data. 029 * This approach is slower than working directly on the BufferedImage's data 030 * buffers. 031 * However, using getRGB and setRGB, this class will work with all types of BufferedImage objects. 032 * <p> 033 * Note that while the abstract <code>java.awt.Image</code> class existed from the very 034 * beginning (version 1.0) of the Java runtime library, <code>java.awt.image.BufferedImage</code> 035 * has not been added until version 1.2. 036 * <h3>Usage example</h3> 037 * This code snippet demonstrates to how combine functionality from Java's runtime 038 * library with JIU by using this class. 039 * Requires Java 1.4 or higher. 040 * Obviously, BufferedRGB24Image objects can only be used with operations that 041 * work on classes implementing RGB24Image. 042 * <pre> 043 * import java.awt.image.BufferedImage; 044 * import java.io.File; 045 * import javax.imageio.ImageIO; 046 * import net.sourceforge.jiu.color.Invert; 047 * import net.sourceforge.jiu.data.PixelImage; 048 * import net.sourceforge.jiu.gui.awt.BufferedRGB24Image; 049 * ... 050 * BufferedImage bufferedImage = ImageIO.read(new File("image.jpg")); 051 * BufferedRGB24Image image = new BufferedRGB24Image(bufferedImage); 052 * Invert invert = new Invert(); 053 * invert.setInputImage(image); 054 * invert.process(); 055 * PixelImage outputImage = invert.getOutputImage(); 056 * </pre> 057 * If you can be sure that an image object can be input and output 058 * image at the same time (as is the case with some operations), you 059 * can even work with only one BufferedRGB24Image object. 060 * Invert is one of these operations, so the following would work: 061 * <pre> 062 * Invert invert = new Invert(); 063 * invert.setInputImage(image); 064 * invert.setOutputImage(image); 065 * invert.process(); 066 * // image now is inverted 067 * </pre> 068 * 069 * @author Marco Schmidt 070 * @since 0.10.0 071 */ 072 public class BufferedRGB24Image implements RGB24Image 073 { 074 private static final int RED_SHIFT = 16; 075 private static final int GREEN_SHIFT = 8; 076 private static final int BLUE_SHIFT = 0; 077 078 /** 079 * Masks for the three RGB channels. 080 * RGB_CLEAR[i] is an int with all bits on, except for those occupied by the channel i. 081 * RGB_CLEAR[i] can thus be used to bitwise AND an ARGB value so that the sample for channel i will be cleared. 082 */ 083 private static final int[] RGB_CLEAR = new int[3]; 084 private static final int[] RGB_SHIFT = new int[3]; 085 086 static 087 { 088 RGB_SHIFT[INDEX_RED] = RED_SHIFT; 089 RGB_SHIFT[INDEX_GREEN] = GREEN_SHIFT; 090 RGB_SHIFT[INDEX_BLUE] = BLUE_SHIFT; 091 RGB_CLEAR[INDEX_RED] = 0xff00ffff; 092 RGB_CLEAR[INDEX_GREEN] = 0xffff00ff; 093 RGB_CLEAR[INDEX_BLUE] = 0xffffff00; 094 } 095 096 private final int HEIGHT; 097 private final BufferedImage image; 098 private final int WIDTH; 099 100 /** 101 * Creates a new BufferedRGB24Image object, storing the argument 102 * BufferedImage object internally. 103 * All image data access will be delegated to that BufferedImage object's methods. 104 * @param bufferedImage the underlying BufferedImage object for this BufferedRGB24Image object 105 */ 106 public BufferedRGB24Image(BufferedImage bufferedImage) 107 { 108 image = bufferedImage; 109 if (image == null) 110 { 111 throw new IllegalArgumentException("Argument image object must not be null."); 112 } 113 WIDTH = image.getWidth(); 114 HEIGHT = image.getHeight(); 115 } 116 117 /** 118 * Sets all the RGB samples in this image to the argument, keeping 119 * the alpha value. 120 * @param newValue all samples in the image will be set to this value 121 */ 122 public void clear(byte newValue) 123 { 124 final int RGB = (newValue & 0xff) | (newValue & 0xff) << 8 | (newValue & 0xff) << 16; 125 for (int y = 0; y < getHeight(); y++) 126 { 127 for (int x = 0; x < getWidth(); x++) 128 { 129 int rgba = image.getRGB(x, y); 130 rgba = (rgba & 0xff000000) | RGB; 131 image.setRGB(x, y, rgba); 132 } 133 } 134 } 135 136 public void clear(int newValue) 137 { 138 clear((byte)newValue); 139 } 140 141 public void clear(int channelIndex, byte newValue) 142 { 143 final int MASK = RGB_CLEAR[channelIndex]; 144 final int SAMPLE = (newValue & 0xff) << RGB_SHIFT[channelIndex]; 145 for (int y = 0; y < getHeight(); y++) 146 { 147 for (int x = 0; x < getWidth(); x++) 148 { 149 int rgba = image.getRGB(x, y); 150 rgba = (rgba & MASK) | SAMPLE; 151 image.setRGB(x, y, rgba); 152 } 153 } 154 } 155 156 public void clear(int channelIndex, int newValue) 157 { 158 clear(channelIndex, (byte)newValue); 159 } 160 161 public PixelImage createCompatibleImage(int width, int height) 162 { 163 BufferedImage newBufferedImage = new BufferedImage(width, height, image.getType()); 164 return new BufferedRGB24Image(newBufferedImage); 165 } 166 167 public PixelImage createCopy() 168 { 169 BufferedImage newBufferedImage = new BufferedImage(getWidth(), getHeight(), image.getType()); 170 image.copyData(newBufferedImage.getRaster()); 171 return new BufferedRGB24Image(newBufferedImage); 172 } 173 174 public long getAllocatedMemory() 175 { 176 /* actually, number of pixels times 4 is just a guess, 177 BufferedImage allows for all kinds of data buffers; 178 for a more accurate approximation these data buffers 179 must be examined */ 180 return 4L * (long)getWidth() * (long)getHeight(); 181 } 182 183 public int getBitsPerPixel() 184 { 185 return 24; 186 } 187 188 public byte getByteSample(int x, int y) 189 { 190 return getByteSample(0, x, y); 191 } 192 193 public byte getByteSample(int channelIndex, int x, int y) 194 { 195 return (byte)((image.getRGB(x, y) >> RGB_SHIFT[channelIndex]) & 0xff); 196 } 197 198 public void getByteSamples(int channelIndex, int x, int y, int w, int h, byte[] dest, int destOffset) 199 { 200 final int SHIFT = RGB_SHIFT[channelIndex]; 201 int[] row = new int[w]; 202 while (h-- > 0) 203 { 204 image.getRGB(x, y++, w, 1, row, 0, w); 205 int columns = w; 206 int rowIndex = 0; 207 while (columns-- > 0) 208 { 209 dest[destOffset++] = (byte)((row[rowIndex++] >> SHIFT) & 0xff); 210 } 211 } 212 } 213 214 public void getByteSamples(int x, int y, int w, int h, byte[] dest, int destOffset) 215 { 216 getByteSamples(0, x, y, w, h, dest, destOffset); 217 } 218 219 public int getHeight() 220 { 221 return HEIGHT; 222 } 223 224 public Class getImageType() 225 { 226 return RGB24Image.class; 227 } 228 229 public int getMaxSample(int channel) 230 { 231 if (channel == INDEX_BLUE || channel == INDEX_RED || channel == INDEX_GREEN) 232 { 233 return 255; 234 } 235 else 236 { 237 throw new IllegalArgumentException("Not a valid channel index: " + channel); 238 } 239 } 240 241 public int getNumChannels() 242 { 243 return 3; 244 } 245 246 public int getSample(int x, int y) 247 { 248 return getSample(0, x, y); 249 } 250 251 public int getSample(int channelIndex, int x, int y) 252 { 253 return (image.getRGB(x, y) >> RGB_SHIFT[channelIndex]) & 0xff; 254 } 255 256 public void getSamples(int x, int y, int w, int h, int[] dest, int destOffs) 257 { 258 getSamples(0, x, y, w, h, dest, destOffs); 259 } 260 261 public void getSamples(int channelIndex, int x, int y, int w, int h, int[] dest, int destOffs) 262 { 263 final int SHIFT = RGB_SHIFT[channelIndex]; 264 int[] row = new int[w]; 265 while (h-- > 0) 266 { 267 image.getRGB(x, y++, w, 1, row, 0, w); 268 int columns = w; 269 int rowIndex = 0; 270 while (columns-- > 0) 271 { 272 dest[destOffs++] = (row[rowIndex++] >> SHIFT) & 0xff; 273 } 274 } 275 } 276 277 public int getWidth() 278 { 279 return WIDTH; 280 } 281 282 public void putByteSample(int channelIndex, int x, int y, byte newValue) 283 { 284 int argb = image.getRGB(x, y) & RGB_CLEAR[channelIndex]; 285 image.setRGB(x, y, argb | ((newValue & 0xff) << RGB_SHIFT[channelIndex])); 286 } 287 288 public void putByteSample(int x, int y, byte newValue) 289 { 290 putByteSample(0, x, y, newValue); 291 } 292 293 public void putByteSamples(int channelIndex, int x, int y, int w, int h, byte[] src, int srcOffset) 294 { 295 final int SHIFT = RGB_SHIFT[channelIndex]; 296 final int MASK = RGB_CLEAR[channelIndex]; 297 int[] row = new int[w]; 298 while (h-- > 0) 299 { 300 image.getRGB(x, y, w, 1, row, 0, w); 301 int columns = w; 302 int rowIndex = 0; 303 while (columns-- > 0) 304 { 305 int argb = row[rowIndex] & MASK; 306 row[rowIndex++] = argb | ((src[srcOffset++] & 0xff) << SHIFT); 307 } 308 image.setRGB(x, y++, w, 1, row, 0, w); 309 } 310 } 311 312 public void putByteSamples(int x, int y, int w, int h, byte[] src, int srcOffset) 313 { 314 putByteSamples(0, x, y, w, h, src, srcOffset); 315 } 316 317 public void putSample(int x, int y, int newValue) 318 { 319 putSample(0, x, y, newValue); 320 } 321 322 public void putSample(int channelIndex, int x, int y, int newValue) 323 { 324 int argb = image.getRGB(x, y) & RGB_CLEAR[channelIndex]; 325 image.setRGB(x, y, argb | (newValue << RGB_SHIFT[channelIndex])); 326 } 327 328 public void putSamples(int channelIndex, int x, int y, int w, int h, int[] src, int srcOffset) 329 { 330 final int SHIFT = RGB_SHIFT[channelIndex]; 331 final int MASK = RGB_CLEAR[channelIndex]; 332 int[] row = new int[w]; 333 while (h-- > 0) 334 { 335 image.getRGB(x, y, w, 1, row, 0, w); 336 int columns = w; 337 int rowIndex = 0; 338 while (columns-- > 0) 339 { 340 int argb = row[rowIndex] & MASK; 341 row[rowIndex++] = argb | (src[srcOffset++] << SHIFT); 342 } 343 image.setRGB(x, y++, w, 1, row, 0, w); 344 } 345 } 346 }