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    }