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