001 /* 002 * MemoryByteChannelImage 003 * 004 * Copyright (c) 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.data; 009 010 /** 011 * An implementation of {@link ByteChannelImage} that stores image channels as 012 * <code>byte[]</code> arrays in memory. 013 * An image can have an arbitrary number of channels. 014 * <p> 015 * This class is abstract because it is merely a data container. 016 * It takes a subclass like {@link MemoryGray8Image} to give meaning to the values. 017 * 018 * @author Marco Schmidt 019 */ 020 public abstract class MemoryByteChannelImage implements ByteChannelImage 021 { 022 private final byte[][] data; 023 private final byte[] firstChannel; // == data[0] 024 private final int numChannels; // == data.length 025 private final int width; 026 private final int height; 027 private final int numPixels; // == width * height 028 029 /** 030 * Create an image of byte channels. 031 * Image data will be completely in memory, so memory requirements are 032 * <code>width * height * numChannels</code> bytes. 033 * Note that the data will not be initialized, so you should not assume 034 * anything about its content. 035 * 036 * @param numChannels the number of channels in this image, must be 037 * non-zero and positive 038 * @param width the horizontal resolution, must be non-zero and positive 039 * @param height the vertical resolution, must be non-zero and positive 040 * @throws IllegalArgumentException if any of the parameters are invalid 041 * or if width times height exceeds two GB 042 * @throws OutOfMemoryException if there is not enough free memory for 043 * the specified resolution 044 */ 045 public MemoryByteChannelImage(int numChannels, int width, int height) 046 { 047 if (width < 1) 048 { 049 throw new IllegalArgumentException("Width must be larger than " + 050 "0: " + width); 051 } 052 if (height < 1) 053 { 054 throw new IllegalArgumentException("Height must be larger than" + 055 " 0: " + height); 056 } 057 if (numChannels < 1) 058 { 059 throw new IllegalArgumentException("Number of channels must be " + 060 "larger than 0: " + numChannels); 061 } 062 this.width = width; 063 this.height = height; 064 this.numChannels = numChannels; 065 numPixels = width * height; 066 data = new byte[numChannels][]; 067 for (int i = 0; i < numChannels; i++) 068 { 069 data[i] = new byte[numPixels]; 070 } 071 firstChannel = data[0]; 072 } 073 074 /** 075 * Throws an exception if the arguments do not form a valid horizontal 076 * sequence of samples. 077 * To be valid, all of the following requirements must be met: 078 */ 079 protected void checkPositionAndNumber(int channel, int x, int y, int w, int h) 080 { 081 if (channel < 0 || channel >= numChannels) 082 { 083 throw new IllegalArgumentException("Illegal channel index value: " + channel + 084 ". Must be from 0 to " + (numChannels - 1) + "."); 085 } 086 if (x < 0 || x >= getWidth()) 087 { 088 throw new IllegalArgumentException("The value for x is invalid: " + x + "."); 089 } 090 if (w < 1) 091 { 092 throw new IllegalArgumentException("The value for w is invalid: " + w + "."); 093 } 094 if (x + w > getWidth()) 095 { 096 throw new IllegalArgumentException("The values x + w exceed the " + 097 "width of this image; x=" + x + ", w=" + w + ", width=" + 098 getWidth()); 099 } 100 if (h < 1) 101 { 102 throw new IllegalArgumentException("The value for h is invalid: " + h + "."); 103 } 104 if (y < 0 || y >= getHeight()) 105 { 106 throw new IllegalArgumentException("The value for y is invalid: " + y + "."); 107 } 108 if (y + h > getHeight()) 109 { 110 throw new IllegalArgumentException("The values y + h exceed the " + 111 "height of this image; y=" + y + ", h=" + h + ", height=" + 112 getHeight()); 113 } 114 } 115 116 public void clear(byte newValue) 117 { 118 clear(0, newValue); 119 } 120 121 public void clear(int channelIndex, byte newValue) 122 { 123 // check the validity of the channel index 124 checkPositionAndNumber(channelIndex, 0, 0, 1, 1); 125 // get the correct channel as byte[] 126 final byte[] CHANNEL = data[channelIndex]; 127 // fill channel with the argument value 128 final byte VALUE = (byte)newValue; 129 final int LENGTH = CHANNEL.length; 130 for (int i = 0; i < LENGTH; i++) 131 { 132 CHANNEL[i] = VALUE; 133 } 134 } 135 136 public void clear(int newValue) 137 { 138 clear(0, (byte)newValue); 139 } 140 141 public void clear(int channelIndex, int newValue) 142 { 143 clear(channelIndex, (byte)newValue); 144 } 145 146 public abstract PixelImage createCompatibleImage(int width, int height); 147 148 public PixelImage createCopy() 149 { 150 PixelImage copy = createCompatibleImage(getWidth(), getHeight()); 151 MemoryByteChannelImage result = (MemoryByteChannelImage)copy; 152 for (int channelIndex = 0; channelIndex < getNumChannels(); channelIndex++) 153 { 154 System.arraycopy(data[channelIndex], 0, result.data[channelIndex], 0, data[channelIndex].length); 155 } 156 return result; 157 } 158 159 public long getAllocatedMemory() 160 { 161 long result = 0; 162 if (data != null) 163 { 164 int channelIndex = 0; 165 while (channelIndex < data.length) 166 { 167 byte[] array = data[channelIndex++]; 168 if (array != null) 169 { 170 result += array.length; 171 } 172 } 173 } 174 return result; 175 } 176 177 public int getBitsPerPixel() 178 { 179 return numChannels * 8; 180 } 181 182 public byte getByteSample(int channel, int x, int y) 183 { 184 /* advantage of the following approach: we don't check arguments 185 before we access the data (too costly); instead, we have the VM 186 throw an array index out of bounds exception and then determine 187 which of the arguments was wrong; 188 that's better than checking before access all of the time => 189 the VM checks anyway 190 we then throw a meaningful IllegalArgumentException (in 191 checkPositionAndNumber) 192 disadvantage: some erroneous arguments aren't noticed, example: 193 width=100, height=20, x=100, y=0 194 will not result in an error (because only 0..99 are valid for x) 195 but in the return of sample(0/1) 196 197 */ 198 try 199 { 200 return data[channel][y * width + x]; 201 } 202 catch (ArrayIndexOutOfBoundsException aioobe) 203 { 204 checkPositionAndNumber(channel, x, y, 1, 1); 205 return -1; 206 } 207 } 208 209 public byte getByteSample(int x, int y) 210 { 211 return getByteSample(0, x, y); 212 } 213 214 public void getByteSamples(int channel, int x, int y, int w, int h, byte[] dest, int destOffset) 215 { 216 checkPositionAndNumber(channel, x, y, w, h); 217 byte[] src = data[channel]; 218 try 219 { 220 int srcOffset = y * width + x; 221 while (h-- > 0) 222 { 223 java.lang.System.arraycopy(src, srcOffset, dest, destOffset, w); 224 srcOffset += width; 225 destOffset += w; 226 } 227 } 228 catch (ArrayIndexOutOfBoundsException aioobe) 229 { 230 } 231 } 232 233 public final int getHeight() 234 { 235 return height; 236 } 237 238 public int getMaxSample(int channel) 239 { 240 return 255; 241 } 242 243 public int getNumChannels() 244 { 245 return numChannels; 246 } 247 248 public final int getSample(int x, int y) 249 { 250 try 251 { 252 return firstChannel[y * width + x] & 0xff; 253 } 254 catch (ArrayIndexOutOfBoundsException aioobe) 255 { 256 checkPositionAndNumber(0, x, y, 1, 1); 257 return -1; 258 } 259 } 260 261 public final int getSample(int channel, int x, int y) 262 { 263 try 264 { 265 return data[channel][y * width + x] & 0xff; 266 } 267 catch (ArrayIndexOutOfBoundsException aioobe) 268 { 269 checkPositionAndNumber(channel, x, y, 1, 1); 270 return -1; 271 } 272 } 273 274 public void getSamples(int channel, int x, int y, int w, int h, int[] dest, int destOffs) 275 { 276 if (w < 1 || h < 1) 277 { 278 return; 279 } 280 byte[] src = data[channel]; 281 int srcOffs = y * width + x; 282 while (h-- != 0) 283 { 284 int loop = w; 285 int from = srcOffs; 286 while (loop-- != 0) 287 { 288 dest[destOffs++] = src[from++] & 0xff; 289 } 290 srcOffs += width; 291 } 292 } 293 294 public final int getWidth() 295 { 296 return width; 297 } 298 299 public final void putByteSample(int channel, int x, int y, byte newValue) 300 { 301 checkPositionAndNumber(channel, x, y, 1, 1); 302 try 303 { 304 data[channel][y * width + x] = newValue; 305 } 306 catch (ArrayIndexOutOfBoundsException aioobe) 307 { 308 checkPositionAndNumber(channel, x, y, 1, 1); 309 } 310 } 311 312 public final void putByteSample(int x, int y, byte newValue) 313 { 314 checkPositionAndNumber(0, x, y, 1, 1); 315 try 316 { 317 firstChannel[y * width + x] = newValue; 318 } 319 catch (ArrayIndexOutOfBoundsException aioobe) 320 { 321 checkPositionAndNumber(0, x, y, 1, 1); 322 } 323 } 324 325 public void putByteSamples(int channel, int x, int y, int w, int h, byte[] src, int srcOffset) 326 { 327 checkPositionAndNumber(channel, x, y, w, h); 328 byte[] dest = data[channel]; 329 int destOffset = y * width + x; 330 while (h-- > 0) 331 { 332 java.lang.System.arraycopy(src, srcOffset, dest, destOffset, w); 333 srcOffset += w; 334 destOffset += width; 335 } 336 } 337 338 public void putSamples(int channel, int x, int y, int w, int h, int[] src, int srcOffs) 339 { 340 checkPositionAndNumber(channel, x, y, w, h); 341 byte[] dest = data[channel]; 342 int destOffs = y * width + x; 343 while (h-- != 0) 344 { 345 int loop = w; 346 int to = destOffs; 347 while (loop-- != 0) 348 { 349 dest[to++] = (byte)src[srcOffs++]; 350 } 351 destOffs += width; 352 } 353 } 354 355 public final void putSample(int x, int y, int newValue) 356 { 357 putByteSample(0, x, y, (byte)newValue); 358 } 359 360 public final void putSample(int channel, int x, int y, int newValue) 361 { 362 putByteSample(channel, x, y, (byte)newValue); 363 } 364 }