001    /*
002     * MemoryBilevelImage
003     *
004     * Copyright (c) 2002 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.data;
009    
010    import net.sourceforge.jiu.data.BilevelImage;
011    import net.sourceforge.jiu.util.ArrayConverter;
012    
013    /**
014     * An implementation of the {@link BilevelImage} interface that stores image
015     * data in a <code>byte</code> array in memory.
016     * An image of <code>width</code> times <code>height</code> pixels will require
017     * <code>(width + 7) / 8 * height</code> bytes of memory.
018     * @author Marco Schmidt
019     */
020    public class MemoryBilevelImage implements BilevelImage
021    {
022            private final int BYTES_PER_ROW;
023            private final byte[] data;
024            private final int HEIGHT;
025            private final int WIDTH;
026    
027            /**
028             * Create a new MemoryBilevelImage object with the specified resolution.
029             * @param width the horizontal resolution of the new image, must be larger than zero
030             * @param height the vertical resolution of the new image, must be larger than zero
031             * @throws IllegalArgumentException if any of the two parameters is smaller than one
032             */
033            public MemoryBilevelImage(int width, int height)
034            {
035                    if (width < 1)
036                    {
037                            throw new IllegalArgumentException("Width must be larger than zero; got " + width);
038                    }
039                    if (height < 1)
040                    {
041                            throw new IllegalArgumentException("Height must be larger than zero; got " + height);
042                    }
043                    BYTES_PER_ROW = (width + 7) / 8;
044                    WIDTH = width;
045                    HEIGHT = height;
046                    data = new byte[BYTES_PER_ROW * HEIGHT];
047            }
048    
049            private void checkBitOffset(int bitOffset)
050            {
051                    if (bitOffset < 0 || bitOffset > 7)
052                    {
053                            throw new IllegalArgumentException("A bit offset value must be from the interval 0..7.");
054                    }
055            }
056    
057            private void checkPositionAndNumber(int x, int y, int w, int h)
058            {
059                    if (w < 0)
060                    {
061                            throw new IllegalArgumentException("Negative number of samples " +
062                                    "to be copied: " + w);
063                    }
064                    if (h < 0)
065                    {
066                            throw new IllegalArgumentException("Negative number of rows " +
067                                    "to be copied: " + h);
068                    }
069                    if (x < 0 || x >= getWidth())
070                    {
071                            throw new IllegalArgumentException("The value for x is invalid: " + x + ".");
072                    }
073                    if (y < 0 || y >= getHeight())
074                    {
075                            throw new IllegalArgumentException("The value for y is invalid: " + y + ".");
076                    }
077                    if (x + w > getWidth())
078                    {
079                            throw new IllegalArgumentException("Cannot copy " + w + " values starting at " +
080                                    "offset " + x + " (width is only " + getWidth() + ").");
081                    }
082                    if (y + h > getHeight())
083                    {
084                            throw new IllegalArgumentException("Cannot copy " + h + " rows starting at " +
085                                    y + " (height is only " + getHeight() + ").");
086                    }
087            }
088    
089            private void checkValue(int value)
090            {
091                    if (value != WHITE && value != BLACK)
092                    {
093                            throw new IllegalArgumentException("Sample value must be either BilevelImage.BLACK or BilevelImage.WHITE.");
094                    }
095            }
096    
097            public void clear(int newValue)
098            {
099                    clear(0, newValue);
100            }
101    
102            public void clear(int channelIndex, int newValue)
103            {
104                    if (channelIndex != 0)
105                    {
106                            throw new IllegalArgumentException("Invalid channel index; " +
107                                    "bilevel images have only one channel, so 0 is the only " +
108                                    "valid argument; got " + channelIndex);
109                    }
110                    checkValue(newValue);
111                    byte value;
112                    if (newValue == BLACK)
113                    {
114                            value = 0;
115                    }
116                    else
117                    {
118                            value = (byte)0xff;
119                    }
120                    for (int i = 0; i < data.length; i++)
121                    {
122                            data[i] = value;
123                    }
124            }
125    
126            public PixelImage createCompatibleImage(int width, int height)
127            {
128                    return new MemoryBilevelImage(width, height);
129            }
130    
131            public PixelImage createCopy()
132            {
133                    PixelImage copy = createCompatibleImage(getWidth(), getHeight());
134                    MemoryBilevelImage result = (MemoryBilevelImage)copy;
135                    System.arraycopy(data, 0, result.data, 0, data.length);
136                    return result;
137            }
138    
139            public long getAllocatedMemory()
140            {
141                    return data.length;
142            }
143    
144            public int getBitsPerPixel()
145            {
146                    return 1;
147            }
148    
149            public int getHeight()
150            {
151                    return HEIGHT;
152            }
153    
154            public Class getImageType()
155            {
156                    return BilevelImage.class;
157            }
158    
159            public int getMaxSample(int channelIndex)
160            {
161                    return 1;
162            }
163    
164            public int getNumChannels()
165            {
166                    return 1;
167            }
168    
169            public void getPackedBytes(int x, int y, int numSamples, byte[] dest, int destOffset, int destBitOffset)
170            {
171                    checkPositionAndNumber(x, y, numSamples, 1);
172                    checkBitOffset(destBitOffset);
173                    int srcOffset = y * BYTES_PER_ROW + (x >> 3);
174                    int srcBitOffset = x & 7;
175                    ArrayConverter.copyPackedBytes(data, srcOffset, srcBitOffset, dest, destOffset, destBitOffset, numSamples);
176            }
177    
178            public int getSample(int x, int y)
179            {
180                    if (isBlack(x, y))
181                    {
182                            return BLACK;
183                    }
184                    else
185                    {
186                            return WHITE;
187                    }
188            }
189    
190            public int getSample(int channelIndex, int x, int y)
191            {
192                    if (channelIndex == 0)
193                    {
194                            if (isBlack(x, y))
195                            {
196                                    return BLACK;
197                            }
198                            else
199                            {
200                                    return WHITE;
201                            }
202                    }
203                    else
204                    {
205                            throw new IllegalArgumentException("The channelIndex argument must be 0 for bilevel images; got " + channelIndex);
206                    }
207            }
208    
209            public void getSamples(int channelIndex, int x, int y, int w, int h, int[] dest, int destOffset)
210            {
211                    final int INITIAL_MASK = 1 << (7 - (x % 8));
212                    int offset = y * BYTES_PER_ROW + x / 8;
213                    while (h-- > 0)
214                    {
215                            int mask = INITIAL_MASK;
216                            int srcOffset = offset;
217                            int remainingColumns = w;
218                            int srcValue = data[srcOffset++] & 0xff;
219                            while (remainingColumns-- != 0)
220                            {
221                                    if ((srcValue & mask) == 0)
222                                    {
223                                            dest[destOffset++] = BLACK;
224                                    }
225                                    else
226                                    {
227                                            dest[destOffset++] = WHITE;
228                                    }
229                                    if (mask == 1)
230                                    {
231                                            mask = 128;
232                                            srcValue = data[srcOffset++] & 0xff;
233                                    }
234                                    else
235                                    {
236                                            mask >>= 1;
237                                    }
238                            }
239                            offset += BYTES_PER_ROW;
240                    }
241            }
242    
243            public int getWidth()
244            {
245                    return WIDTH;
246            }
247    
248            public boolean isBlack(int x, int y)
249            {
250                    try
251                    {
252                            int offset = y * BYTES_PER_ROW + (x >> 3);
253                            return (data[offset] & (byte)(1 << (7 - (x & 7)))) == 0;
254                    }
255                    catch (ArrayIndexOutOfBoundsException aioobe)
256                    {
257                            checkPositionAndNumber(x, y, 1, 1);
258                    }
259                    return true;
260            }
261    
262            public boolean isWhite(int x, int y)
263            {
264                    try
265                    {
266                            int offset = y * BYTES_PER_ROW + (x >> 3);
267                            return (data[offset] & (byte)(1 << (7 - (x & 7)))) != 0;
268                    }
269                    catch (ArrayIndexOutOfBoundsException aioobe)
270                    {
271                            checkPositionAndNumber(x, y, 1, 1);
272                    }
273                    return true;
274            }
275    
276            public void putBlack(int x, int y)
277            {
278                    try
279                    {
280                            int offset = y * BYTES_PER_ROW + (x >> 3);
281                            data[offset] &= (byte)(255 - (1 << (7 - (x & 7))));
282                    }
283                    catch (ArrayIndexOutOfBoundsException aioobe)
284                    {
285                            checkPositionAndNumber(x, y, 1, 1);
286                    }
287            }
288    
289            public void putPackedBytes(int x, int y, int numSamples, byte[] src, int srcOffset, int srcBitOffset)
290            {
291                    checkPositionAndNumber(x, y, numSamples, 1);
292                    checkBitOffset(srcBitOffset);
293                    int destOffset = y * BYTES_PER_ROW + (x >> 3);
294                    int destBitOffset = x & 7;
295                    ArrayConverter.copyPackedBytes(src, srcOffset, srcBitOffset, data, destOffset, destBitOffset, numSamples);
296            }
297    
298            public void putSample(int x, int y, int newValue)
299            {
300                    putSample(0, x, y, newValue);
301            }
302    
303            public void putSample(int channelIndex, int x, int y, int newValue)
304            {
305                    checkValue(newValue);
306                    try
307                    {
308                            int offset = y * BYTES_PER_ROW + (x >> 3);
309                            if (newValue == BLACK)
310                            {
311                                    data[offset] &= (byte)(255 - (1 << (7 - (x & 7))));
312                            }
313                            else
314                            {
315                                    data[offset] |= (byte)(1 << (7 - (x & 7)));
316                            }
317                    }
318                    catch (ArrayIndexOutOfBoundsException aioobe)
319                    {
320                            checkPositionAndNumber(x, y, 1, 1);
321                    }
322            }
323    
324            public void putSamples(int channelIndex, int x, int y, int w, int h, int[] src, int srcOffset)
325            {
326                    checkPositionAndNumber(x, y, w, h);
327                    int INITIAL_ROW_MASK = 1 << (7 - (x & 7));
328                    int initialDestOffset = y * BYTES_PER_ROW + (x >> 3);
329                    while (h-- > 0)
330                    {
331                            int mask = INITIAL_ROW_MASK;
332                            int destOffset = initialDestOffset;
333                            int pixelsLeft = w;
334                            while (pixelsLeft-- > 0)
335                            {
336                                    if (src[srcOffset++] == BLACK)
337                                    {
338                                            data[destOffset] &= (byte)(255 - mask);
339                                    }
340                                    else
341                                    {
342                                            data[destOffset] |= (byte)mask;
343                                    }
344                                    if (mask == 1)
345                                    {
346                                            mask = 128;
347                                            destOffset++;
348                                    }
349                            }
350                            initialDestOffset += BYTES_PER_ROW;
351                    }
352            }
353    
354            public void putWhite(int x, int y)
355            {
356                    try
357                    {
358                            int offset = y * BYTES_PER_ROW + (x >> 3);
359                            data[offset] |= (byte)(1 << (7 - (x & 7)));
360                    }
361                    catch (ArrayIndexOutOfBoundsException aioobe)
362                    {
363                            checkPositionAndNumber(x, y, 1, 1);
364                    }
365            }
366    }