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    }