001    /*
002     * ArrayConverter
003     * 
004     * Copyright (c) 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.util;
009    
010    /**
011     * Helper class with static methods to convert between byte arrays and primitive types.
012     * Useful for serialization.
013     * @author Marco Schmidt
014     * @since 0.9.0
015     */
016    public class ArrayConverter
017    {
018            private static final int SHORT_SIZE = 2;
019            private static final int INT_SIZE = 4;
020    
021            private ArrayConverter()
022            {
023            }
024    
025            /**
026             * Makes sure that the arguments define a valid (existing) array interval.
027             * This includes:
028             * <ul>
029             * <li>array is non-null</li>
030             * <li>offset is >= 0 and smaller than array.length</li>
031             * <li>length is > 0</li>
032             * <li>offset + length is <= array.length</li>
033             * </ul>
034             */
035            private static void checkArray(byte[] array, int offset, int length) throws IllegalArgumentException
036            {
037                    if (array == null)
038                    {
039                            throw new IllegalArgumentException("Array must not be null.");
040                    }
041                    if (offset < 0)
042                    {
043                            throw new IllegalArgumentException("Array index must not be negative.");
044                    }
045                    if (length < 1)
046                    {
047                            throw new IllegalArgumentException("Length of value must not be smaller than one.");
048                    }
049                    if (offset >= array.length)
050                    {
051                            throw new IllegalArgumentException("Offset " + offset + 
052                                    " is invalid, must be smaller than array length " + 
053                                    array.length + ".");
054                    }
055                    if (offset + length > array.length)
056                    {
057                            throw new IllegalArgumentException("Value of length " + 
058                                    length + " does not fit at offset " + offset +
059                                    " into an array of length " + array.length + ".");
060                    }
061            }
062    
063            /*public static void convertPacked2BitIntensityTo8BitA(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes)
064            {
065                    final byte[] DEST_VALUES = {0, 85, (byte)170, (byte)255};
066                    while (numPackedBytes-- > 0)
067                    {
068                            int srcValue = src[srcOffset++] & 0xff;
069                            dest[destOffset++] = DEST_VALUES[(srcValue >> 6) & 3];
070                            dest[destOffset++] = DEST_VALUES[(srcValue >> 4) & 3];
071                            dest[destOffset++] = DEST_VALUES[(srcValue >> 2) & 3];
072                            dest[destOffset++] = DEST_VALUES[srcValue & 3];
073                    }
074            }*/
075    
076            /**
077             * Converts bytes with two four-bit-intensity samples to 8 byte intensity 
078             * values, each stored in one byte.
079             * Two-bit values can be 0, 1, 2 or 3.
080             * These values will be scaled to the full [0;255] range so that 0 remains 0,
081             * 1 becomes 85, 2 becomes 170 and 3 becomes 255.
082             * <p>
083             * A little discussion on how to implement this method
084             * was held in the German Java newsgroup
085             * <a href="news:de.comp.lang.java">de.comp.lang.java</a>.
086             * The message I wrote to start the thread has the ID 
087             * <code>1ef7du4vfqsd2pskb6jukut6pnhn87htt2@4ax.com</code>.
088             * Read the 
089             * <a target="_top" href="http://groups.google.com/groups?as_umsgid=1ef7du4vfqsd2pskb6jukut6pnhn87htt2@4ax.com">thread 
090             * at Google Groups</a>.
091             * @param src byte array, each byte stores four two-bit intensity values
092             * @param srcOffset index into src
093             * @param dest byte array, each byte stores an eight-bit intensity values
094             * @param destOffset index into dest
095             * @param numPackedBytes number of bytes in src to be decoded    
096             */
097            public static void convertPacked2BitIntensityTo8Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes)
098            {
099                    while (numPackedBytes-- > 0)
100                    {
101                            int srcValue = src[srcOffset++] & 0xff;
102                            dest[destOffset++] = (byte)(((srcValue >> 6) & 3) * 85);
103                            dest[destOffset++] = (byte)(((srcValue >> 4) & 3) * 85);
104                            dest[destOffset++] = (byte)(((srcValue >> 2) & 3) * 85);
105                            dest[destOffset++] = (byte)((srcValue & 3) * 85);
106                    }
107            }
108    
109            /**
110             * Converts bytes with four two-bit-intensity samples to byte-sized intensity values.
111             * Four-bit values can be from 0 to 15.
112             * These values will be scaled to the full [0;255] range so that 0 remains 0,
113             * 1 becomes 17, 2 becomes 34, ..., and 15 becomes 255.
114             * The most significant four bits in a byte become the left, the least significant
115             * four bits the right pixel.
116             * @param src byte array, each byte stores two four-bit intensity values
117             * @param srcOffset index into src
118             * @param dest byte array, each byte stores an eight-bit intensity values
119             * @param destOffset index into dest
120             * @param numPackedBytes number of bytes in src to be decoded
121             * @since 0.12.0
122             */
123            public static void convertPacked4BitIntensityTo8Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes)
124            {
125                    while (numPackedBytes-- > 0)
126                    {
127                            int srcValue = src[srcOffset++] & 0xff;
128                            dest[destOffset++] = (byte)((srcValue & 0xf0) | ((srcValue & 0xf0) >> 4));
129                            dest[destOffset++] = (byte)((srcValue & 0x0f) | ((srcValue & 0x0f) << 4));
130                    }
131            }
132    
133            /**
134             * Copies a number of bit values from one byte array to another.
135             * @param src array from which is copied
136             * @param srcOffset index into the src array of the first byte from which is copied
137             * @param srcBitOffset first bit within src[srcOffset] from which is copied (0 is left-most, 1 is second  left-most, 7 is right-most)
138             * @param dest array to which is copied
139             * @param destOffset index into the dest array of the first byte to which is copied
140             * @param destBitOffset first bit within dest[destOffset] to which is copied (0 is left-most, 1 is second  left-most, 7 is right-most)
141             * @param numSamples number of bits to be copied
142             */
143            public static void copyPackedBytes(byte[] src, int srcOffset, int srcBitOffset, byte[] dest, int destOffset, int destBitOffset, int numSamples)
144            {
145                    if (numSamples < 0)
146                    {
147                            throw new IllegalArgumentException("Number of samples to be copied must be 0 or larger.");
148                    }
149                    if (srcBitOffset == 0 && destBitOffset == 0 && numSamples > 7)
150                    {
151                            int bytes = numSamples >> 3;
152                            System.arraycopy(src, srcOffset, dest, destOffset, bytes);
153                            srcOffset += bytes;
154                            destOffset += bytes;
155                            numSamples &= 7;
156                    }
157                    int srcMask = 1 << (7 - srcBitOffset);
158                    int destMask = 1 << (7 - destBitOffset);
159                    while (numSamples-- != 0)
160                    {
161                            if ((src[srcOffset] & srcMask) == 0)
162                            {
163                                    dest[destOffset] &= (byte)(255 - destMask);
164                            }
165                            else
166                            {
167                                    dest[destOffset] |= (byte)destMask;
168                            }
169                            if (srcMask == 1)
170                            {
171                                    srcMask = 128;
172                                    srcOffset++;
173                            }
174                            else
175                            {
176                                    srcMask >>= 1;
177                            }
178                            if (destMask == 1)
179                            {
180                                    destMask = 128;
181                                    destOffset++;
182                            }
183                            else
184                            {
185                                    destMask >>= 1;
186                            }
187                    }
188            }
189            
190            public static void decodePacked1Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes)
191            {
192                    while (numPackedBytes-- != 0)
193                    {
194                            int srcValue = src[srcOffset++] & 0xff;
195                            dest[destOffset++] = (byte)((srcValue >> 7) & 0x01);
196                            dest[destOffset++] = (byte)((srcValue >> 6) & 0x01);
197                            dest[destOffset++] = (byte)((srcValue >> 5) & 0x01);
198                            dest[destOffset++] = (byte)((srcValue >> 4) & 0x01);
199                            dest[destOffset++] = (byte)((srcValue >> 3) & 0x01);
200                            dest[destOffset++] = (byte)((srcValue >> 2) & 0x01);
201                            dest[destOffset++] = (byte)((srcValue >> 1) & 0x01);
202                            dest[destOffset++] = (byte)(srcValue & 0x01);
203                    }
204            }
205    
206            /**
207             * Decodes bytes with four two-bit samples to single bytes.
208             * The two most significant bits of a source byte become the first value,
209             * the two least significant bits the fourth value.
210             * The method expects <code>numPackedBytes</code> bytes at <code>src[srcOffset]</code>
211             * (these will be read and interpreted) and
212             * <code>numPackedBytes * 4</code> at <code>dest[destOffset]</code> (where the decoded
213             * byte values will be stored.
214             * <p>
215             * @param src byte array, each byte stores four two-bit values
216             * @param srcOffset index into src
217             * @param dest byte array, each byte stores a single decoded value (from 0 to 3)
218             * @param destOffset index into dest
219             * @param numPackedBytes number of bytes in src to be decoded
220             * @since 0.10.0
221             */
222            public static void decodePacked2Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes)
223            {
224                    while (numPackedBytes-- != 0)
225                    {
226                            int srcValue = src[srcOffset++] & 0xff;
227                            dest[destOffset++] = (byte)(srcValue >> 6);
228                            dest[destOffset++] = (byte)((srcValue >> 4) & 0x03);
229                            dest[destOffset++] = (byte)((srcValue >> 2) & 0x03);
230                            dest[destOffset++] = (byte)(srcValue & 0x03);
231                    }
232            }
233    
234            /**
235             * Decodes bytes with two four-bit samples to single bytes.
236             * The four most significant bits of a source byte become the first value,
237             * the least significant four bits the second value.
238             * The method expects <code>numPackedBytes</code> bytes at <code>src[srcOffset]</code>
239             * (these will be read and interpreted) and
240             * <code>numPackedBytes * 2</code> at <code>dest[destOffset]</code> (where the decoded
241             * byte values will be stored.
242             * <p>
243             * @param src byte array, each byte stores two four-bit values
244             * @param srcOffset index into src
245             * @param dest byte array, each byte stores a single decoded value
246             * @param destOffset index into dest
247             * @param numPackedBytes number of bytes in src to be decoded
248             */
249            public static void decodePacked4Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numPackedBytes)
250            {
251                    while (numPackedBytes-- > 0)
252                    {
253                            int srcValue = src[srcOffset++] & 0xff;
254                            dest[destOffset++] = (byte)(srcValue >> 4);
255                            dest[destOffset++] = (byte)(srcValue & 0x0f);
256                    }
257            }
258    
259            /**
260             * Convert 16 bit RGB samples stored in big endian (BE) byte order
261             * with 5 bits for red and blue and 6 bits for green to 24
262             * bit RGB byte samples.
263             * @since 0.10.0
264             */
265            public static void decodePackedRGB565BigEndianToRGB24(byte[] src, int srcOffset, 
266                    byte[] red, int redOffset,
267                    byte[] green, int greenOffset,
268                    byte[] blue, int blueOffset,
269                    int numPixels)
270            {
271                    while (numPixels-- != 0)
272                    {
273                            int pixel = ((src[srcOffset] & 0xff) << 8) | (src[srcOffset + 1] & 0xff);
274                            srcOffset += 2;
275                            int r = (pixel >> 11) & 0x1f;
276                            int g = (pixel >> 5) & 0x3f;
277                            int b = pixel & 0x1f;
278                            red[redOffset++] = (byte)((r << 3) | ((r >> 2) & 0x07));
279                            green[greenOffset++] = (byte)((g << 2) | ((g >> 4) & 0x03));
280                            blue[blueOffset++] = (byte)((b << 3) | ((b >>2) & 0x07));
281                    }
282            }
283    
284            public static void encodePacked2Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numSamples)
285            {
286                    int numBytes = numSamples / 4;
287                    while (numBytes-- != 0)
288                    {
289                            int b1 = src[srcOffset++] & 3;
290                            int b2 = src[srcOffset++] & 3;
291                            int b3 = src[srcOffset++] & 3;
292                            int b4 = src[srcOffset++] & 3;
293                            dest[destOffset++] = (byte)(b1 << 6 | b2 << 4 | b3 << 2 | b4);
294                    }
295                    numSamples = numSamples % 4;
296                    if (numSamples > 0)
297                    {
298                            int value = 0;
299                            int mask = 6;
300                            while (numSamples-- != 0)
301                            {
302                                    value |= ((src[srcOffset++] & 3) << mask);
303                                    mask -= 2;
304                            }
305                            dest[destOffset] = (byte)value;
306                    }
307            }
308    
309            public static void encodePacked4Bit(byte[] src, int srcOffset, byte[] dest, int destOffset, int numSamples)
310            {
311                    int numBytes = numSamples / 2;
312                    while (numBytes-- != 0)
313                    {
314                            int b1 = src[srcOffset++] & 15;
315                            int b2 = src[srcOffset++] & 15;
316                            dest[destOffset++] = (byte)(b1 << 4 | b2);
317                    }
318                    if ((numSamples % 2) == 1)
319                    {
320                            dest[destOffset] = (byte)((src[srcOffset] & 15) << 4);
321                    }
322            }
323    
324            /**
325             * Convert 24 bit RGB pixels to 16 bit pixels stored in big endian (BE) byte order
326             * with 5 bits for red and blue and 6 bits for green.
327             * @since 0.10.0
328             */
329            public static void encodeRGB24ToPackedRGB565BigEndian(
330                    byte[] red, int redOffset,
331                    byte[] green, int greenOffset,
332                    byte[] blue, int blueOffset,
333                    byte[] dest, int destOffset, 
334                    int numPixels)
335            {
336                    while (numPixels-- != 0)
337                    {
338                            int r = (red[redOffset++] & 0xff) >> 3;
339                            int g = (green[greenOffset++] & 0xff) >> 2;
340                            int b = (blue[blueOffset++] & 0xff) >> 3;
341                            int pixel = r << 11 | g << 5 | b;
342                            dest[destOffset++] = (byte)(pixel >> 8);
343                            dest[destOffset++] = (byte)(pixel & 0xff);
344                    }
345            }
346    
347            /**
348             * Reads four consecutive bytes from the given array at the 
349             * given position in big endian order and returns them as 
350             * an <code>int</code>.
351             * @param src the array from which bytes are read
352             * @param srcOffset the index into the array from which the bytes are read
353             * @return int value taken from the array
354             */
355            public static int getIntBE(byte[] src, int srcOffset)
356            {
357                    checkArray(src, srcOffset, INT_SIZE);
358                    return 
359                            (src[srcOffset + 3] & 0xff) |
360                            ((src[srcOffset + 2] & 0xff) << 8) |
361                            ((src[srcOffset + 1] & 0xff) << 16) |
362                            ((src[srcOffset] & 0xff) << 24);
363            }
364    
365            /**
366             * Reads four consecutive bytes from the given array at the 
367             * given position in little endian order and returns them as 
368             * an <code>int</code>.
369             * @param src the array from which bytes are read
370             * @param srcOffset the index into the array from which the bytes are read
371             * @return short value taken from the array
372             */
373            public static int getIntLE(byte[] src, int srcOffset)
374            {
375                    checkArray(src, srcOffset, INT_SIZE);
376                    return 
377                            (src[srcOffset] & 0xff) |
378                            ((src[srcOffset + 1] & 0xff) << 8) |
379                            ((src[srcOffset + 2] & 0xff) << 16) |
380                            ((src[srcOffset + 3] & 0xff) << 24);
381            }
382    
383            /**
384             * Reads two consecutive bytes from the given array at the 
385             * given position in big endian order and returns them as 
386             * a <code>short</code>.
387             * @param src the array from which two bytes are read
388             * @param srcOffset the index into the array from which the two bytes are read
389             * @return short value taken from the array
390             */
391            public static short getShortBE(byte[] src, int srcOffset)
392            {
393                    checkArray(src, srcOffset, SHORT_SIZE);
394                    return (short)
395                            (((src[srcOffset++] & 0xff) << 8)  |
396                             (src[srcOffset++] & 0xff));
397            }
398    
399            /**
400             * Reads two consecutive bytes from the given array at the 
401             * given position in little endian order and returns them as 
402             * a <code>short</code>.
403             * @param src the array from which two bytes are read
404             * @param srcOffset the index into the array from which the two bytes are read
405             * @return short value taken from the array
406             */
407            public static short getShortLE(byte[] src, int srcOffset)
408            {
409                    checkArray(src, srcOffset, SHORT_SIZE);
410                    return (short)
411                            ((src[srcOffset++] & 0xff)   |
412                             ((src[srcOffset++] & 0xff) << 8));
413            }
414    
415            /**
416             * Writes an int value into four consecutive bytes of a byte array,
417             * in big endian (network) byte order.
418             * @param dest the array to which bytes are written
419             * @param destOffset index of the array to which the first byte is written
420             * @param newValue the int value to be written to the array
421             */
422            public static void setIntBE(byte[] dest, int destOffset, int newValue)
423            {
424                    checkArray(dest, destOffset, INT_SIZE);
425                    dest[destOffset] = (byte)((newValue >> 24)& 0xff);
426                    dest[destOffset + 1] = (byte)((newValue >> 16)& 0xff);
427                    dest[destOffset + 2] = (byte)((newValue >> 8)& 0xff);
428                    dest[destOffset + 3] = (byte)(newValue & 0xff);
429            }
430    
431            /**
432             * Writes an int value into four consecutive bytes of a byte array,
433             * in little endian (Intel) byte order.
434             * @param dest the array to which bytes are written
435             * @param destOffset index of the array to which the first byte is written
436             * @param newValue the int value to be written to the array
437             */
438            public static void setIntLE(byte[] dest, int destOffset, int newValue)
439            {
440                    checkArray(dest, destOffset, INT_SIZE);
441                    dest[destOffset] = (byte)(newValue & 0xff);
442                    dest[destOffset + 1] = (byte)((newValue >> 8)& 0xff);
443                    dest[destOffset + 2] = (byte)((newValue >> 16)& 0xff);
444                    dest[destOffset + 3] = (byte)((newValue >> 24)& 0xff);
445            }
446    
447            public static void setShortBE(byte[] dest, int destOffset, short newValue)
448            {
449                    checkArray(dest, destOffset, SHORT_SIZE);
450                    dest[destOffset] = (byte)((newValue >> 8) & 0xff);
451                    dest[destOffset + 1] = (byte)(newValue & 0xff);
452            }
453    
454            public static void setShortLE(byte[] dest, int destOffset, short newValue)
455            {
456                    checkArray(dest, destOffset, SHORT_SIZE);
457                    dest[destOffset + 1] = (byte)((newValue >> 8) & 0xff);
458                    dest[destOffset] = (byte)(newValue & 0xff);
459            }
460    
461    }