001/*
002 * $RCSfile: ImageUtil.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.7 $
042 * $Date: 2007/08/28 18:45:06 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.impl.common;
046
047import java.awt.Point;
048import java.awt.Rectangle;
049import java.awt.Transparency;
050import java.awt.color.ColorSpace;
051import java.awt.color.ICC_ColorSpace;
052import java.awt.image.BufferedImage;
053import java.awt.image.ColorModel;
054import java.awt.image.ComponentColorModel;
055import java.awt.image.ComponentSampleModel;
056import java.awt.image.DataBuffer;
057import java.awt.image.DataBufferByte;
058import java.awt.image.DataBufferInt;
059import java.awt.image.DataBufferShort;
060import java.awt.image.DataBufferUShort;
061import java.awt.image.DirectColorModel;
062import java.awt.image.IndexColorModel;
063import java.awt.image.MultiPixelPackedSampleModel;
064import java.awt.image.Raster;
065import java.awt.image.RenderedImage;
066import java.awt.image.SampleModel;
067import java.awt.image.SinglePixelPackedSampleModel;
068import java.awt.image.WritableRaster;
069import java.io.IOException;
070import java.util.Arrays;
071import java.util.ArrayList;
072import java.util.Iterator;
073import java.util.List;
074import java.util.Locale;
075
076//import javax.imageio.ImageTypeSpecifier;
077
078import javax.imageio.IIOException;
079import javax.imageio.IIOImage;
080import javax.imageio.ImageReadParam;
081import javax.imageio.ImageTypeSpecifier;
082import javax.imageio.ImageWriter;
083import javax.imageio.spi.IIORegistry;
084import javax.imageio.spi.ImageReaderSpi;
085import javax.imageio.spi.ImageReaderWriterSpi;
086import javax.imageio.spi.ImageWriterSpi;
087import javax.imageio.spi.ServiceRegistry;
088import javax.imageio.stream.ImageInputStream;
089
090public class ImageUtil {
091    /* XXX testing only
092    public static void main(String[] args) {
093        ImageTypeSpecifier bilevel =
094            ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255},
095                                             new byte[] {(byte)0, (byte)255},
096                                             new byte[] {(byte)0, (byte)255},
097                                             null, 1,
098                                             DataBuffer.TYPE_BYTE);
099        ImageTypeSpecifier gray =
100            ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
101        ImageTypeSpecifier grayAlpha =
102            ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false,
103                                               false);
104        ImageTypeSpecifier rgb =
105            ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
106                                                 new int[] {0, 1, 2},
107                                                 DataBuffer.TYPE_BYTE,
108                                                 false,
109                                                 false);
110        ImageTypeSpecifier rgba =
111            ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
112                                                 new int[] {0, 1, 2, 3},
113                                                 DataBuffer.TYPE_BYTE,
114                                                 true,
115                                                 false);
116        ImageTypeSpecifier packed =
117            ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
118                                            0xff000000,
119                                            0x00ff0000,
120                                            0x0000ff00,
121                                            0x000000ff,
122                                            DataBuffer.TYPE_BYTE,
123                                            false);
124
125        SampleModel bandedSM =
126            new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE,
127                                                 1, 1, 15);
128
129        System.out.println(createColorModel(bilevel.getSampleModel()));
130        System.out.println(createColorModel(gray.getSampleModel()));
131        System.out.println(createColorModel(grayAlpha.getSampleModel()));
132        System.out.println(createColorModel(rgb.getSampleModel()));
133        System.out.println(createColorModel(rgba.getSampleModel()));
134        System.out.println(createColorModel(packed.getSampleModel()));
135        System.out.println(createColorModel(bandedSM));
136    }
137    */
138
139    /**
140     * Creates a <code>ColorModel</code> that may be used with the
141     * specified <code>SampleModel</code>.  If a suitable
142     * <code>ColorModel</code> cannot be found, this method returns
143     * <code>null</code>.
144     *
145     * <p> Suitable <code>ColorModel</code>s are guaranteed to exist
146     * for all instances of <code>ComponentSampleModel</code>.
147     * For 1- and 3- banded <code>SampleModel</code>s, the returned
148     * <code>ColorModel</code> will be opaque.  For 2- and 4-banded
149     * <code>SampleModel</code>s, the output will use alpha transparency
150     * which is not premultiplied.  1- and 2-banded data will use a
151     * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB
152     * <code>ColorSpace</code>. Data with 5 or more bands will have a
153     * <code>BogusColorSpace</code>.</p>
154     *
155     * <p>An instance of <code>DirectColorModel</code> will be created for
156     * instances of <code>SinglePixelPackedSampleModel</code> with no more
157     * than 4 bands.</p>
158     *
159     * <p>An instance of <code>IndexColorModel</code> will be created for
160     * instances of <code>MultiPixelPackedSampleModel</code>. The colormap
161     * will be a grayscale ramp with <code>1&nbsp;<<&nbsp;numberOfBits</code>
162     * entries ranging from zero to at most 255.</p>
163     *
164     * @return An instance of <code>ColorModel</code> that is suitable for
165     *         the supplied <code>SampleModel</code>, or <code>null</code>.
166     *
167     * @throws IllegalArgumentException  If <code>sampleModel</code> is
168     *         <code>null</code>.
169     */
170    public static final ColorModel createColorModel(SampleModel sampleModel) {
171        // Check the parameter.
172        if(sampleModel == null) {
173            throw new IllegalArgumentException("sampleModel == null!");
174        }
175
176        // Get the data type.
177        int dataType = sampleModel.getDataType();
178
179        // Check the data type
180        switch(dataType) {
181        case DataBuffer.TYPE_BYTE:
182        case DataBuffer.TYPE_USHORT:
183        case DataBuffer.TYPE_SHORT:
184        case DataBuffer.TYPE_INT:
185        case DataBuffer.TYPE_FLOAT:
186        case DataBuffer.TYPE_DOUBLE:
187            break;
188        default:
189            // Return null for other types.
190            return null;
191        }
192
193        // The return variable.
194        ColorModel colorModel = null;
195
196        // Get the sample size.
197        int[] sampleSize = sampleModel.getSampleSize();
198
199        // Create a Component ColorModel.
200        if(sampleModel instanceof ComponentSampleModel) {
201            // Get the number of bands.
202            int numBands = sampleModel.getNumBands();
203
204            // Determine the color space.
205            ColorSpace colorSpace = null;
206            if(numBands <= 2) {
207                colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
208            } else if(numBands <= 4) {
209                colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
210            } else {
211                colorSpace = new BogusColorSpace(numBands);
212            }
213
214            boolean hasAlpha = (numBands == 2) || (numBands == 4);
215            boolean isAlphaPremultiplied = false;
216            int transparency = hasAlpha ?
217                Transparency.TRANSLUCENT : Transparency.OPAQUE;
218
219            colorModel = new ComponentColorModel(colorSpace,
220                                                 sampleSize,
221                                                 hasAlpha,
222                                                 isAlphaPremultiplied,
223                                                 transparency,
224                                                 dataType);
225        } else if (sampleModel.getNumBands() <= 4 &&
226                   sampleModel instanceof SinglePixelPackedSampleModel) {
227            SinglePixelPackedSampleModel sppsm =
228                (SinglePixelPackedSampleModel)sampleModel;
229
230            int[] bitMasks = sppsm.getBitMasks();
231            int rmask = 0;
232            int gmask = 0;
233            int bmask = 0;
234            int amask = 0;
235
236            int numBands = bitMasks.length;
237            if (numBands <= 2) {
238                rmask = gmask = bmask = bitMasks[0];
239                if (numBands == 2) {
240                    amask = bitMasks[1];
241                }
242            } else {
243                rmask = bitMasks[0];
244                gmask = bitMasks[1];
245                bmask = bitMasks[2];
246                if (numBands == 4) {
247                    amask = bitMasks[3];
248                }
249            }
250
251            int bits = 0;
252            for (int i = 0; i < sampleSize.length; i++) {
253                bits += sampleSize[i];
254            }
255
256            return new DirectColorModel(bits, rmask, gmask, bmask, amask);
257
258        } else if(sampleModel instanceof MultiPixelPackedSampleModel) {
259            // Load the colormap with a ramp.
260            int bitsPerSample = sampleSize[0];
261            int numEntries = 1 << bitsPerSample;
262            byte[] map = new byte[numEntries];
263            for (int i = 0; i < numEntries; i++) {
264                map[i] = (byte)(i*255/(numEntries - 1));
265            }
266
267            colorModel = new IndexColorModel(bitsPerSample, numEntries,
268                                             map, map, map);
269
270        }
271
272        return colorModel;
273    }
274
275    /**
276     * For the case of binary data (<code>isBinary()</code> returns
277     * <code>true</code>), return the binary data as a packed byte array.
278     * The data will be packed as eight bits per byte with no bit offset,
279     * i.e., the first bit in each image line will be the left-most of the
280     * first byte of the line.  The line stride in bytes will be
281     * <code>(int)((getWidth()+7)/8)</code>.  The length of the returned
282     * array will be the line stride multiplied by <code>getHeight()</code>
283     *
284     * @return the binary data as a packed array of bytes with zero offset
285     * of <code>null</code> if the data are not binary.
286     * @throws IllegalArgumentException if <code>isBinary()</code> returns
287     * <code>false</code> with the <code>SampleModel</code> of the
288     * supplied <code>Raster</code> as argument.
289     */
290    public static byte[] getPackedBinaryData(Raster raster,
291                                             Rectangle rect) {
292        SampleModel sm = raster.getSampleModel();
293        if(!isBinary(sm)) {
294            throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
295        }
296
297        int rectX = rect.x;
298        int rectY = rect.y;
299        int rectWidth = rect.width;
300        int rectHeight = rect.height;
301
302        DataBuffer dataBuffer = raster.getDataBuffer();
303
304        int dx = rectX - raster.getSampleModelTranslateX();
305        int dy = rectY - raster.getSampleModelTranslateY();
306
307        MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
308        int lineStride = mpp.getScanlineStride();
309        int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
310        int bitOffset = mpp.getBitOffset(dx);
311
312        int numBytesPerRow = (rectWidth + 7)/8;
313        if(dataBuffer instanceof DataBufferByte &&
314           eltOffset == 0 && bitOffset == 0 &&
315           numBytesPerRow == lineStride &&
316           ((DataBufferByte)dataBuffer).getData().length ==
317           numBytesPerRow*rectHeight) {
318            return ((DataBufferByte)dataBuffer).getData();
319        }
320
321        byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight];
322
323        int b = 0;
324
325        if(bitOffset == 0) {
326            if(dataBuffer instanceof DataBufferByte) {
327                byte[] data = ((DataBufferByte)dataBuffer).getData();
328                int stride = numBytesPerRow;
329                int offset = 0;
330                for(int y = 0; y < rectHeight; y++) {
331                    System.arraycopy(data, eltOffset,
332                                     binaryDataArray, offset,
333                                     stride);
334                    offset += stride;
335                    eltOffset += lineStride;
336                }
337            } else if(dataBuffer instanceof DataBufferShort ||
338                      dataBuffer instanceof DataBufferUShort) {
339                short[] data = dataBuffer instanceof DataBufferShort ?
340                    ((DataBufferShort)dataBuffer).getData() :
341                    ((DataBufferUShort)dataBuffer).getData();
342
343                for(int y = 0; y < rectHeight; y++) {
344                    int xRemaining = rectWidth;
345                    int i = eltOffset;
346                    while(xRemaining > 8) {
347                        short datum = data[i++];
348                        binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF);
349                        binaryDataArray[b++] = (byte)(datum & 0xFF);
350                        xRemaining -= 16;
351                    }
352                    if(xRemaining > 0) {
353                        binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF);
354                    }
355                    eltOffset += lineStride;
356                }
357            } else if(dataBuffer instanceof DataBufferInt) {
358                int[] data = ((DataBufferInt)dataBuffer).getData();
359
360                for(int y = 0; y < rectHeight; y++) {
361                    int xRemaining = rectWidth;
362                    int i = eltOffset;
363                    while(xRemaining > 24) {
364                        int datum = data[i++];
365                        binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF);
366                        binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF);
367                        binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF);
368                        binaryDataArray[b++] = (byte)(datum & 0xFF);
369                        xRemaining -= 32;
370                    }
371                    int shift = 24;
372                    while(xRemaining > 0) {
373                        binaryDataArray[b++] =
374                            (byte)((data[i] >>> shift) & 0xFF);
375                        shift -= 8;
376                        xRemaining -= 8;
377                    }
378                    eltOffset += lineStride;
379                }
380            }
381        } else { // bitOffset != 0
382            if(dataBuffer instanceof DataBufferByte) {
383                byte[] data = ((DataBufferByte)dataBuffer).getData();
384
385                if((bitOffset & 7) == 0) {
386                    int stride = numBytesPerRow;
387                    int offset = 0;
388                    for(int y = 0; y < rectHeight; y++) {
389                        System.arraycopy(data, eltOffset,
390                                         binaryDataArray, offset,
391                                         stride);
392                        offset += stride;
393                        eltOffset += lineStride;
394                    }
395                } else { // bitOffset % 8 != 0
396                    int leftShift = bitOffset & 7;
397                    int rightShift = 8 - leftShift;
398                    for(int y = 0; y < rectHeight; y++) {
399                        int i = eltOffset;
400                        int xRemaining = rectWidth;
401                        while(xRemaining > 0) {
402                            if(xRemaining > rightShift) {
403                                binaryDataArray[b++] =
404                                    (byte)(((data[i++]&0xFF) << leftShift) |
405                                           ((data[i]&0xFF) >>> rightShift));
406                            } else {
407                                binaryDataArray[b++] =
408                                    (byte)((data[i]&0xFF) << leftShift);
409                            }
410                            xRemaining -= 8;
411                        }
412                        eltOffset += lineStride;
413                    }
414                }
415            } else if(dataBuffer instanceof DataBufferShort ||
416                      dataBuffer instanceof DataBufferUShort) {
417                short[] data = dataBuffer instanceof DataBufferShort ?
418                    ((DataBufferShort)dataBuffer).getData() :
419                    ((DataBufferUShort)dataBuffer).getData();
420
421                for(int y = 0; y < rectHeight; y++) {
422                    int bOffset = bitOffset;
423                    for(int x = 0; x < rectWidth; x += 8, bOffset += 8) {
424                        int i = eltOffset + bOffset/16;
425                        int mod = bOffset % 16;
426                        int left = data[i] & 0xFFFF;
427                        if(mod <= 8) {
428                            binaryDataArray[b++] = (byte)(left >>> (8 - mod));
429                        } else {
430                            int delta = mod - 8;
431                            int right = data[i+1] & 0xFFFF;
432                            binaryDataArray[b++] =
433                                (byte)((left << delta) |
434                                       (right >>> (16 - delta)));
435                        }
436                    }
437                    eltOffset += lineStride;
438                }
439            } else if(dataBuffer instanceof DataBufferInt) {
440                int[] data = ((DataBufferInt)dataBuffer).getData();
441
442                for(int y = 0; y < rectHeight; y++) {
443                    int bOffset = bitOffset;
444                    for(int x = 0; x < rectWidth; x += 8, bOffset += 8) {
445                        int i = eltOffset + bOffset/32;
446                        int mod = bOffset % 32;
447                        int left = data[i];
448                        if(mod <= 24) {
449                            binaryDataArray[b++] =
450                                (byte)(left >>> (24 - mod));
451                        } else {
452                            int delta = mod - 24;
453                            int right = data[i+1];
454                            binaryDataArray[b++] =
455                                (byte)((left << delta) |
456                                       (right >>> (32 - delta)));
457                        }
458                    }
459                    eltOffset += lineStride;
460                }
461            }
462        }
463
464        return binaryDataArray;
465    }
466
467    /**
468     * Returns the binary data unpacked into an array of bytes.
469     * The line stride will be the width of the <code>Raster</code>.
470     *
471     * @throws IllegalArgumentException if <code>isBinary()</code> returns
472     * <code>false</code> with the <code>SampleModel</code> of the
473     * supplied <code>Raster</code> as argument.
474     */
475    public static byte[] getUnpackedBinaryData(Raster raster,
476                                               Rectangle rect) {
477        SampleModel sm = raster.getSampleModel();
478        if(!isBinary(sm)) {
479            throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
480        }
481
482        int rectX = rect.x;
483        int rectY = rect.y;
484        int rectWidth = rect.width;
485        int rectHeight = rect.height;
486
487        DataBuffer dataBuffer = raster.getDataBuffer();
488
489        int dx = rectX - raster.getSampleModelTranslateX();
490        int dy = rectY - raster.getSampleModelTranslateY();
491
492        MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
493        int lineStride = mpp.getScanlineStride();
494        int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
495        int bitOffset = mpp.getBitOffset(dx);
496
497        byte[] bdata = new byte[rectWidth*rectHeight];
498        int maxY = rectY + rectHeight;
499        int maxX = rectX + rectWidth;
500        int k = 0;
501
502        if(dataBuffer instanceof DataBufferByte) {
503            byte[] data = ((DataBufferByte)dataBuffer).getData();
504            for(int y = rectY; y < maxY; y++) {
505                int bOffset = eltOffset*8 + bitOffset;
506                for(int x = rectX; x < maxX; x++) {
507                    byte b = data[bOffset/8];
508                    bdata[k++] =
509                        (byte)((b >>> (7 - bOffset & 7)) & 0x0000001);
510                    bOffset++;
511                }
512                eltOffset += lineStride;
513            }
514        } else if(dataBuffer instanceof DataBufferShort ||
515                  dataBuffer instanceof DataBufferUShort) {
516            short[] data = dataBuffer instanceof DataBufferShort ?
517                ((DataBufferShort)dataBuffer).getData() :
518                ((DataBufferUShort)dataBuffer).getData();
519            for(int y = rectY; y < maxY; y++) {
520                int bOffset = eltOffset*16 + bitOffset;
521                for(int x = rectX; x < maxX; x++) {
522                    short s = data[bOffset/16];
523                    bdata[k++] =
524                        (byte)((s >>> (15 - bOffset % 16)) &
525                               0x0000001);
526                    bOffset++;
527                }
528                eltOffset += lineStride;
529            }
530        } else if(dataBuffer instanceof DataBufferInt) {
531            int[] data = ((DataBufferInt)dataBuffer).getData();
532            for(int y = rectY; y < maxY; y++) {
533                int bOffset = eltOffset*32 + bitOffset;
534                for(int x = rectX; x < maxX; x++) {
535                    int i = data[bOffset/32];
536                    bdata[k++] =
537                        (byte)((i >>> (31 - bOffset % 32)) &
538                               0x0000001);
539                    bOffset++;
540                }
541                eltOffset += lineStride;
542            }
543        }
544
545        return bdata;
546    }
547
548    /**
549     * Sets the supplied <code>Raster</code>'s data from an array
550     * of packed binary data of the form returned by
551     * <code>getPackedBinaryData()</code>.
552     *
553     * @throws IllegalArgumentException if <code>isBinary()</code> returns
554     * <code>false</code> with the <code>SampleModel</code> of the
555     * supplied <code>Raster</code> as argument.
556     */
557    public static void setPackedBinaryData(byte[] binaryDataArray,
558                                           WritableRaster raster,
559                                           Rectangle rect) {
560        SampleModel sm = raster.getSampleModel();
561        if(!isBinary(sm)) {
562            throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
563        }
564
565        int rectX = rect.x;
566        int rectY = rect.y;
567        int rectWidth = rect.width;
568        int rectHeight = rect.height;
569
570        DataBuffer dataBuffer = raster.getDataBuffer();
571
572        int dx = rectX - raster.getSampleModelTranslateX();
573        int dy = rectY - raster.getSampleModelTranslateY();
574
575        MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
576        int lineStride = mpp.getScanlineStride();
577        int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
578        int bitOffset = mpp.getBitOffset(dx);
579
580        int b = 0;
581
582        if(bitOffset == 0) {
583            if(dataBuffer instanceof DataBufferByte) {
584                byte[] data = ((DataBufferByte)dataBuffer).getData();
585                if(data == binaryDataArray) {
586                    // Optimal case: simply return.
587                    return;
588                }
589                int stride = (rectWidth + 7)/8;
590                int offset = 0;
591                for(int y = 0; y < rectHeight; y++) {
592                    System.arraycopy(binaryDataArray, offset,
593                                     data, eltOffset,
594                                     stride);
595                    offset += stride;
596                    eltOffset += lineStride;
597                }
598            } else if(dataBuffer instanceof DataBufferShort ||
599                      dataBuffer instanceof DataBufferUShort) {
600                short[] data = dataBuffer instanceof DataBufferShort ?
601                    ((DataBufferShort)dataBuffer).getData() :
602                    ((DataBufferUShort)dataBuffer).getData();
603
604                for(int y = 0; y < rectHeight; y++) {
605                    int xRemaining = rectWidth;
606                    int i = eltOffset;
607                    while(xRemaining > 8) {
608                        data[i++] =
609                            (short)(((binaryDataArray[b++] & 0xFF) << 8) |
610                                    (binaryDataArray[b++] & 0xFF));
611                        xRemaining -= 16;
612                    }
613                    if(xRemaining > 0) {
614                        data[i++] =
615                            (short)((binaryDataArray[b++] & 0xFF) << 8);
616                    }
617                    eltOffset += lineStride;
618                }
619            } else if(dataBuffer instanceof DataBufferInt) {
620                int[] data = ((DataBufferInt)dataBuffer).getData();
621
622                for(int y = 0; y < rectHeight; y++) {
623                    int xRemaining = rectWidth;
624                    int i = eltOffset;
625                    while(xRemaining > 24) {
626                        data[i++] =
627                            (int)(((binaryDataArray[b++] & 0xFF) << 24) |
628                                  ((binaryDataArray[b++] & 0xFF) << 16) |
629                                  ((binaryDataArray[b++] & 0xFF) << 8) |
630                                  (binaryDataArray[b++] & 0xFF));
631                        xRemaining -= 32;
632                    }
633                    int shift = 24;
634                    while(xRemaining > 0) {
635                        data[i] |=
636                            (int)((binaryDataArray[b++] & 0xFF) << shift);
637                        shift -= 8;
638                        xRemaining -= 8;
639                    }
640                    eltOffset += lineStride;
641                }
642            }
643        } else { // bitOffset != 0
644            int stride = (rectWidth + 7)/8;
645            int offset = 0;
646            if(dataBuffer instanceof DataBufferByte) {
647                byte[] data = ((DataBufferByte)dataBuffer).getData();
648
649                if((bitOffset & 7) == 0) {
650                    for(int y = 0; y < rectHeight; y++) {
651                        System.arraycopy(binaryDataArray, offset,
652                                         data, eltOffset,
653                                         stride);
654                        offset += stride;
655                        eltOffset += lineStride;
656                    }
657                } else { // bitOffset % 8 != 0
658                    int rightShift = bitOffset & 7;
659                    int leftShift = 8 - rightShift;
660                    int leftShift8 = 8 + leftShift;
661                    int mask = (byte)(255<<leftShift);
662                    int mask1 = (byte)~mask;
663
664                    for(int y = 0; y < rectHeight; y++) {
665                        int i = eltOffset;
666                        int xRemaining = rectWidth;
667                        while(xRemaining > 0) {
668                            byte datum = binaryDataArray[b++];
669
670                            if (xRemaining > leftShift8) {
671                                // when all the bits in this BYTE will be set
672                                // into the data buffer.
673                                data[i] = (byte)((data[i] & mask ) |
674                                    ((datum&0xFF) >>> rightShift));
675                                data[++i] = (byte)((datum & 0xFF) << leftShift);
676                            } else if (xRemaining > leftShift) {
677                                // All the "leftShift" high bits will be set
678                                // into the data buffer.  But not all the
679                                // "rightShift" low bits will be set.
680                                data[i] = (byte)((data[i] & mask ) |
681                                    ((datum&0xFF) >>> rightShift));
682                                i++;
683                                data[i] =
684                                    (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift));
685                            }
686                            else {
687                                // Less than "leftShift" high bits will be set.
688                                int remainMask = (1 << leftShift - xRemaining) - 1;
689                                data[i] =
690                                    (byte)((data[i] & (mask | remainMask)) |
691                                    (datum&0xFF) >>> rightShift & ~remainMask);
692                            }
693                            xRemaining -= 8;
694                        }
695                        eltOffset += lineStride;
696                    }
697                }
698            } else if(dataBuffer instanceof DataBufferShort ||
699                      dataBuffer instanceof DataBufferUShort) {
700                short[] data = dataBuffer instanceof DataBufferShort ?
701                    ((DataBufferShort)dataBuffer).getData() :
702                    ((DataBufferUShort)dataBuffer).getData();
703
704                int rightShift = bitOffset & 7;
705                int leftShift = 8 - rightShift;
706                int leftShift16 = 16 + leftShift;
707                int mask = (short)(~(255 << leftShift));
708                int mask1 = (short)(65535 << leftShift);
709                int mask2 = (short)~mask1;
710
711                for(int y = 0; y < rectHeight; y++) {
712                    int bOffset = bitOffset;
713                    int xRemaining = rectWidth;
714                    for(int x = 0; x < rectWidth;
715                        x += 8, bOffset += 8, xRemaining -= 8) {
716                        int i = eltOffset + (bOffset >> 4);
717                        int mod = bOffset & 15;
718                        int datum = binaryDataArray[b++] & 0xFF;
719                        if(mod <= 8) {
720                            // This BYTE is set into one SHORT
721                            if (xRemaining < 8) {
722                                // Mask the bits to be set.
723                                datum &= 255 << 8 - xRemaining;
724                            }
725                            data[i] = (short)((data[i] & mask) | (datum << leftShift));
726                        } else if (xRemaining > leftShift16) {
727                            // This BYTE will be set into two SHORTs
728                            data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF));
729                            data[++i] =
730                                (short)((datum << leftShift)&0xFFFF);
731                        } else if (xRemaining > leftShift) {
732                            // This BYTE will be set into two SHORTs;
733                            // But not all the low bits will be set into SHORT
734                            data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF));
735                            i++;
736                            data[i] =
737                                (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF));
738                        } else {
739                            // Only some of the high bits will be set into
740                            // SHORTs
741                            int remainMask = (1 << leftShift - xRemaining) - 1;
742                            data[i] = (short)((data[i] & (mask1 | remainMask)) |
743                                      ((datum >>> rightShift)&0xFFFF & ~remainMask));
744                        }
745                    }
746                    eltOffset += lineStride;
747                }
748            } else if(dataBuffer instanceof DataBufferInt) {
749                int[] data = ((DataBufferInt)dataBuffer).getData();
750                int rightShift = bitOffset & 7;
751                int leftShift = 8 - rightShift;
752                int leftShift32 = 32 + leftShift;
753                int mask = 0xFFFFFFFF << leftShift;
754                int mask1 = ~mask;
755
756                for(int y = 0; y < rectHeight; y++) {
757                    int bOffset = bitOffset;
758                    int xRemaining = rectWidth;
759                    for(int x = 0; x < rectWidth;
760                        x += 8, bOffset += 8, xRemaining -= 8) {
761                        int i = eltOffset + (bOffset >> 5);
762                        int mod = bOffset & 31;
763                        int datum = binaryDataArray[b++] & 0xFF;
764                        if(mod <= 24) {
765                            // This BYTE is set into one INT
766                            int shift = 24 - mod;
767                            if (xRemaining < 8) {
768                                // Mask the bits to be set.
769                                datum &= 255 << 8 - xRemaining;
770                            }
771                            data[i] = (data[i] & (~(255 << shift))) | (datum << shift);
772                        } else if (xRemaining > leftShift32) {
773                            // All the bits of this BYTE will be set into two INTs
774                            data[i] = (data[i] & mask) | (datum >>> rightShift);
775                            data[++i] = datum << leftShift;
776                        } else if (xRemaining > leftShift) {
777                            // This BYTE will be set into two INTs;
778                            // But not all the low bits will be set into INT
779                            data[i] = (data[i] & mask) | (datum >>> rightShift);
780                            i++;
781                            data[i] = (data[i] & mask1) | (datum << leftShift);
782                        } else {
783                            // Only some of the high bits will be set into INT
784                            int remainMask = (1 << leftShift - xRemaining) - 1;
785                            data[i] = (data[i] & (mask | remainMask)) |
786                                      (datum >>> rightShift & ~remainMask);
787                        }
788                    }
789                    eltOffset += lineStride;
790                }
791            }
792        }
793    }
794
795    /**
796     * Copies data into the packed array of the <code>Raster</code>
797     * from an array of unpacked data of the form returned by
798     * <code>getUnpackedBinaryData()</code>.
799     *
800     * <p> If the data are binary, then the target bit will be set if
801     * and only if the corresponding byte is non-zero.
802     *
803     * @throws IllegalArgumentException if <code>isBinary()</code> returns
804     * <code>false</code> with the <code>SampleModel</code> of the
805     * supplied <code>Raster</code> as argument.
806     */
807    public static void setUnpackedBinaryData(byte[] bdata,
808                                             WritableRaster raster,
809                                             Rectangle rect) {
810        SampleModel sm = raster.getSampleModel();
811        if(!isBinary(sm)) {
812            throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
813        }
814
815        int rectX = rect.x;
816        int rectY = rect.y;
817        int rectWidth = rect.width;
818        int rectHeight = rect.height;
819
820        DataBuffer dataBuffer = raster.getDataBuffer();
821
822        int dx = rectX - raster.getSampleModelTranslateX();
823        int dy = rectY - raster.getSampleModelTranslateY();
824
825        MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
826        int lineStride = mpp.getScanlineStride();
827        int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
828        int bitOffset = mpp.getBitOffset(dx);
829
830        int k = 0;
831
832        if(dataBuffer instanceof DataBufferByte) {
833            byte[] data = ((DataBufferByte)dataBuffer).getData();
834            for(int y = 0; y < rectHeight; y++) {
835                int bOffset = eltOffset*8 + bitOffset;
836                for(int x = 0; x < rectWidth; x++) {
837                    if(bdata[k++] != (byte)0) {
838                        data[bOffset/8] |=
839                            (byte)(0x00000001 << (7 - bOffset & 7));
840                    }
841                    bOffset++;
842                }
843                eltOffset += lineStride;
844            }
845        } else if(dataBuffer instanceof DataBufferShort ||
846                  dataBuffer instanceof DataBufferUShort) {
847            short[] data = dataBuffer instanceof DataBufferShort ?
848                ((DataBufferShort)dataBuffer).getData() :
849                ((DataBufferUShort)dataBuffer).getData();
850            for(int y = 0; y < rectHeight; y++) {
851                int bOffset = eltOffset*16 + bitOffset;
852                for(int x = 0; x < rectWidth; x++) {
853                    if(bdata[k++] != (byte)0) {
854                        data[bOffset/16] |=
855                            (short)(0x00000001 <<
856                                    (15 - bOffset % 16));
857                    }
858                    bOffset++;
859                }
860                eltOffset += lineStride;
861            }
862        } else if(dataBuffer instanceof DataBufferInt) {
863            int[] data = ((DataBufferInt)dataBuffer).getData();
864            for(int y = 0; y < rectHeight; y++) {
865                int bOffset = eltOffset*32 + bitOffset;
866                for(int x = 0; x < rectWidth; x++) {
867                    if(bdata[k++] != (byte)0) {
868                        data[bOffset/32] |=
869                            (int)(0x00000001 <<
870                                  (31 - bOffset % 32));
871                    }
872                    bOffset++;
873                }
874                eltOffset += lineStride;
875            }
876        }
877    }
878
879    public static boolean isBinary(SampleModel sm) {
880        return sm instanceof MultiPixelPackedSampleModel &&
881            ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 &&
882            sm.getNumBands() == 1;
883    }
884
885    public static ColorModel createColorModel(ColorSpace colorSpace,
886                                              SampleModel sampleModel) {
887        ColorModel colorModel = null;
888
889        if(sampleModel == null) {
890            throw new IllegalArgumentException(I18N.getString("ImageUtil1"));
891        }
892
893        int numBands = sampleModel.getNumBands();
894        if (numBands < 1 || numBands > 4) {
895            return null;
896        }
897
898        int dataType = sampleModel.getDataType();
899        if (sampleModel instanceof ComponentSampleModel) {
900            if (dataType < DataBuffer.TYPE_BYTE ||
901                //dataType == DataBuffer.TYPE_SHORT ||
902                dataType > DataBuffer.TYPE_DOUBLE) {
903                return null;
904            }
905
906            if (colorSpace == null)
907                colorSpace =
908                    numBands <= 2 ?
909                    ColorSpace.getInstance(ColorSpace.CS_GRAY) :
910                    ColorSpace.getInstance(ColorSpace.CS_sRGB);
911
912            boolean useAlpha = (numBands == 2) || (numBands == 4);
913            int transparency = useAlpha ?
914                               Transparency.TRANSLUCENT : Transparency.OPAQUE;
915
916            boolean premultiplied = false;
917
918            int dataTypeSize = DataBuffer.getDataTypeSize(dataType);
919            int[] bits = new int[numBands];
920            for (int i = 0; i < numBands; i++) {
921                bits[i] = dataTypeSize;
922            }
923
924            colorModel = new ComponentColorModel(colorSpace,
925                                                 bits,
926                                                 useAlpha,
927                                                 premultiplied,
928                                                 transparency,
929                                                 dataType);
930        } else if (sampleModel instanceof SinglePixelPackedSampleModel) {
931            SinglePixelPackedSampleModel sppsm =
932                (SinglePixelPackedSampleModel)sampleModel;
933
934            int[] bitMasks = sppsm.getBitMasks();
935            int rmask = 0;
936            int gmask = 0;
937            int bmask = 0;
938            int amask = 0;
939
940            numBands = bitMasks.length;
941            if (numBands <= 2) {
942                rmask = gmask = bmask = bitMasks[0];
943                if (numBands == 2) {
944                    amask = bitMasks[1];
945                }
946            } else {
947                rmask = bitMasks[0];
948                gmask = bitMasks[1];
949                bmask = bitMasks[2];
950                if (numBands == 4) {
951                    amask = bitMasks[3];
952                }
953            }
954
955            int[] sampleSize = sppsm.getSampleSize();
956            int bits = 0;
957            for (int i = 0; i < sampleSize.length; i++) {
958                bits += sampleSize[i];
959            }
960
961            if (colorSpace == null)
962                colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
963
964            colorModel =
965                new DirectColorModel(colorSpace,
966                                     bits, rmask, gmask, bmask, amask,
967                                     false,
968                                     sampleModel.getDataType());
969        } else if (sampleModel instanceof MultiPixelPackedSampleModel) {
970            int bits =
971                ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride();
972            int size = 1 << bits;
973            byte[] comp = new byte[size];
974
975            for (int i = 0; i < size; i++)
976                comp[i] = (byte)(255 * i / (size - 1));
977
978            colorModel = new IndexColorModel(bits, size, comp, comp, comp);
979        }
980
981        return colorModel;
982    }
983
984    public static int getElementSize(SampleModel sm) {
985        int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
986
987        if (sm instanceof MultiPixelPackedSampleModel) {
988            MultiPixelPackedSampleModel mppsm =
989                (MultiPixelPackedSampleModel)sm;
990            return mppsm.getSampleSize(0) * mppsm.getNumBands();
991        } else if (sm instanceof ComponentSampleModel) {
992            return sm.getNumBands() * elementSize;
993        } else if (sm instanceof SinglePixelPackedSampleModel) {
994            return elementSize;
995        }
996
997        return elementSize * sm.getNumBands();
998
999    }
1000
1001    public static long getTileSize(SampleModel sm) {
1002        int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
1003
1004        if (sm instanceof MultiPixelPackedSampleModel) {
1005            MultiPixelPackedSampleModel mppsm =
1006                (MultiPixelPackedSampleModel)sm;
1007            return (mppsm.getScanlineStride() * mppsm.getHeight() +
1008                   (mppsm.getDataBitOffset() + elementSize -1) / elementSize) *
1009                   ((elementSize + 7) / 8);
1010        } else if (sm instanceof ComponentSampleModel) {
1011            ComponentSampleModel csm = (ComponentSampleModel)sm;
1012            int[] bandOffsets = csm.getBandOffsets();
1013            int maxBandOff = bandOffsets[0];
1014            for (int i=1; i<bandOffsets.length; i++)
1015                maxBandOff = Math.max(maxBandOff, bandOffsets[i]);
1016
1017            long size = 0;
1018            int pixelStride = csm.getPixelStride();
1019            int scanlineStride = csm.getScanlineStride();
1020            if (maxBandOff >= 0)
1021                size += maxBandOff + 1;
1022            if (pixelStride > 0)
1023                size += pixelStride * (sm.getWidth() - 1);
1024            if (scanlineStride > 0)
1025                size += scanlineStride * (sm.getHeight() - 1);
1026
1027            int[] bankIndices = csm.getBankIndices();
1028            maxBandOff = bankIndices[0];
1029            for (int i=1; i<bankIndices.length; i++)
1030                maxBandOff = Math.max(maxBandOff, bankIndices[i]);
1031            return size * (maxBandOff + 1) * ((elementSize + 7) / 8);
1032        } else if (sm instanceof SinglePixelPackedSampleModel) {
1033            SinglePixelPackedSampleModel sppsm =
1034                (SinglePixelPackedSampleModel)sm;
1035            long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) +
1036                        sppsm.getWidth();
1037            return size * ((elementSize + 7) / 8);
1038        }
1039
1040        return 0;
1041    }
1042
1043    public static long getBandSize(SampleModel sm) {
1044        int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
1045
1046        if (sm instanceof ComponentSampleModel) {
1047            ComponentSampleModel csm = (ComponentSampleModel)sm;
1048            int pixelStride = csm.getPixelStride();
1049            int scanlineStride = csm.getScanlineStride();
1050            long size = Math.min(pixelStride, scanlineStride);
1051
1052            if (pixelStride > 0)
1053                size += pixelStride * (sm.getWidth() - 1);
1054            if (scanlineStride > 0)
1055                size += scanlineStride * (sm.getHeight() - 1);
1056            return size * ((elementSize + 7) / 8);
1057        } else
1058            return getTileSize(sm);
1059    }
1060
1061    /**
1062     * Tests whether the color indices represent a gray-scale image with
1063     * the indicated number of bits over the color component range [0,255].
1064     * The grayscale mapping may be inverted, i.e., 0 -> 255 and
1065     * mapSize -> 0.
1066     *
1067     * @param icm The gray-to-color mapping.
1068     * @return Whether the <code>IndexColorModel</code> maps index
1069     *         <code>i</code> to <code>((255*i)/icm.getMapSize()-1)</code>.
1070     * @throws IllegalArgumentException if <code>icm</code> is
1071     *         <code>null</code>.
1072     */
1073    public static boolean isGrayscaleMapping(IndexColorModel icm) {
1074        if(icm == null) {
1075            throw new IllegalArgumentException("icm == null!");
1076        }
1077
1078        // Get colormap size and contents.
1079        int mapSize = icm.getMapSize();
1080
1081        byte[] r = new byte[mapSize];
1082        byte[] g = new byte[mapSize];
1083        byte[] b = new byte[mapSize];
1084
1085        icm.getReds(r);
1086        icm.getGreens(g);
1087        icm.getBlues(b);
1088
1089        boolean isGrayToColor = true;
1090
1091        // Check ascending ramp.
1092        for (int i = 0; i < mapSize; i++) {
1093            byte temp = (byte)(i*255/(mapSize - 1));
1094
1095            if (r[i] != temp || g[i] != temp || b[i] != temp) {
1096                isGrayToColor = false;
1097                break;
1098            }
1099        }
1100
1101        if(!isGrayToColor) {
1102            isGrayToColor = true;
1103
1104            // Check descending ramp.
1105            for (int i = 0, j = mapSize - 1; i < mapSize; i++, j--) {
1106                byte temp = (byte)(j*255/(mapSize - 1));
1107
1108                if (r[i] != temp || g[i] != temp || b[i] != temp) {
1109                    isGrayToColor = false;
1110                    break;
1111                }
1112            }
1113        }
1114
1115        return isGrayToColor;
1116    }
1117
1118    /**
1119     * Tests whether the color indices represent a gray-scale image.
1120     *
1121     * @param r The red channel color indices.
1122     * @param g The green channel color indices.
1123     * @param b The blue channel color indices.
1124     * @return If all the indices have 256 entries, and are identical mappings,
1125     *         return <code>true</code>; otherwise, return <code>false</code>.
1126     */
1127    public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) {
1128        if (r.length != g.length || r.length != b.length)
1129            return false;
1130
1131        int size = r.length;
1132
1133        if (size != 256)
1134            return false;
1135
1136        for (int i = 0; i < size; i++) {
1137            byte temp = (byte) i;
1138
1139            if (r[i] != temp || g[i] != temp || b[i] != temp)
1140                return false;
1141        }
1142
1143        return true;
1144    }
1145
1146    /** Converts the provided object to <code>String</code> */
1147    public static String convertObjectToString(Object obj) {
1148        if (obj == null)
1149            return "";
1150
1151        String s = "";
1152        if (obj instanceof byte[]) {
1153            byte[] bArray = (byte[])obj;
1154            for (int i = 0; i < bArray.length; i++)
1155                s += bArray[i] + " ";
1156            return s;
1157        }
1158
1159        if (obj instanceof int[]) {
1160            int[] iArray = (int[])obj;
1161            for (int i = 0; i < iArray.length; i++)
1162                s += iArray[i] + " " ;
1163            return s;
1164        }
1165
1166        if (obj instanceof short[]) {
1167            short[] sArray = (short[])obj;
1168            for (int i = 0; i < sArray.length; i++)
1169                s += sArray[i] + " " ;
1170            return s;
1171        }
1172
1173        return obj.toString();
1174
1175    }
1176    
1177    /** Checks that the provided <code>ImageWriter</code> can encode
1178     * the provided <code>ImageTypeSpecifier</code> or not.  If not, an
1179     * <code>IIOException</code> will be thrown.
1180     * @param writer The provided <code>ImageWriter</code>.
1181     * @param type The image to be tested.
1182     * @throws IIOException If the writer cannot encoded the provided image.
1183     */
1184    public static final void canEncodeImage(ImageWriter writer,
1185                                            ImageTypeSpecifier type)
1186        throws IIOException {
1187        ImageWriterSpi spi = writer.getOriginatingProvider();
1188
1189        if(type != null && spi != null && !spi.canEncodeImage(type))  {
1190            throw new IIOException(I18N.getString("ImageUtil2")+" "+
1191                                   writer.getClass().getName());
1192        }
1193    }
1194
1195    /** Checks that the provided <code>ImageWriter</code> can encode
1196     * the provided <code>ColorModel</code> and <code>SampleModel</code>.
1197     * If not, an <code>IIOException</code> will be thrown.
1198     * @param writer The provided <code>ImageWriter</code>.
1199     * @param colorModel The provided <code>ColorModel</code>.
1200     * @param sampleModel The provided <code>SampleModel</code>.
1201     * @throws IIOException If the writer cannot encoded the provided image.
1202     */
1203    public static final void canEncodeImage(ImageWriter writer, 
1204                                            ColorModel colorModel,
1205                                            SampleModel sampleModel)
1206        throws IIOException {
1207        ImageTypeSpecifier type = null;
1208        if (colorModel != null && sampleModel != null)
1209            type = new ImageTypeSpecifier(colorModel, sampleModel);
1210        canEncodeImage(writer, type);
1211    }
1212
1213    /**
1214     * Returns whether the image has contiguous data across rows.
1215     */
1216    public static final boolean imageIsContiguous(RenderedImage image) {
1217        SampleModel sm;
1218        if(image instanceof BufferedImage) {
1219            WritableRaster ras = ((BufferedImage)image).getRaster();
1220            sm = ras.getSampleModel();
1221        } else {
1222            sm = image.getSampleModel();
1223        }
1224        
1225        if (sm instanceof ComponentSampleModel) {
1226            // Ensure image rows samples are stored contiguously
1227            // in a single bank.
1228            ComponentSampleModel csm = (ComponentSampleModel)sm;
1229
1230            if (csm.getPixelStride() != csm.getNumBands()) {
1231                return false;
1232            }
1233
1234            int[] bandOffsets = csm.getBandOffsets();
1235            for (int i = 0; i < bandOffsets.length; i++) {
1236                if (bandOffsets[i] != i) {
1237                    return false;
1238                }
1239            }
1240
1241            int[] bankIndices = csm.getBankIndices();
1242            for (int i = 0; i < bandOffsets.length; i++) {
1243                if (bankIndices[i] != 0) {
1244                    return false;
1245                }
1246            }
1247
1248            return true;
1249        }
1250
1251        // Otherwise true if and only if it's a bilevel image with
1252        // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit
1253        // pixel stride.
1254        return ImageUtil.isBinary(sm);
1255    }
1256
1257    /**
1258     * Gets the destination image type.
1259     */
1260    // NOTE: Code shamelessly copied from ImageReader.getDestination().
1261    public static final ImageTypeSpecifier
1262        getDestinationType(ImageReadParam param,
1263                           Iterator imageTypes) throws IIOException {
1264
1265        if (imageTypes == null || !imageTypes.hasNext()) {
1266            throw new IllegalArgumentException("imageTypes null or empty!");
1267        }
1268
1269        ImageTypeSpecifier imageType = null;
1270
1271        // If param is non-null, use it
1272        if (param != null) {
1273            imageType = param.getDestinationType();
1274        }
1275
1276        // No info from param, use fallback image type
1277        if (imageType == null) {
1278            Object o = imageTypes.next();
1279            if (!(o instanceof ImageTypeSpecifier)) {
1280                throw new IllegalArgumentException
1281                    ("Non-ImageTypeSpecifier retrieved from imageTypes!");
1282            }
1283            imageType = (ImageTypeSpecifier)o;
1284        } else {
1285            boolean foundIt = false;
1286            while (imageTypes.hasNext()) {
1287                ImageTypeSpecifier type =
1288                    (ImageTypeSpecifier)imageTypes.next();
1289                if (type.equals(imageType)) {
1290                    foundIt = true;
1291                    break;
1292                }
1293            }
1294
1295            if (!foundIt) {
1296                throw new IIOException
1297                    ("Destination type from ImageReadParam does not match!");
1298            }
1299        }
1300
1301        return imageType;
1302    }
1303
1304    /**
1305     * Returns <code>true</code> if the given <code>ColorSpace</code> object
1306     * is an instance of <code>ICC_ColorSpace</code> but is not one of the
1307     * standard <code>ColorSpace</code>s returned by
1308     * <code>ColorSpace.getInstance()</code>.
1309     *
1310     * @param cs The <code>ColorSpace</code> to test.
1311     */
1312    public static boolean isNonStandardICCColorSpace(ColorSpace cs) {
1313        boolean retval = false;
1314
1315        try {
1316            // Check the standard ColorSpaces in decreasing order of
1317            // likelihood except check CS_PYCC last as in some JREs
1318            // PYCC.pf used not to be installed.
1319            retval =
1320                (cs instanceof ICC_ColorSpace) &&
1321                !(cs.isCS_sRGB() ||
1322                  cs.equals(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)) ||
1323                  cs.equals(ColorSpace.getInstance(ColorSpace.CS_GRAY)) ||
1324                  cs.equals(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ)) ||
1325                  cs.equals(ColorSpace.getInstance(ColorSpace.CS_PYCC)));
1326        } catch(IllegalArgumentException e) {
1327            // PYCC.pf not installed: ignore it - 'retval' is still 'false'.
1328        }
1329
1330        return retval;
1331    }
1332
1333    // Method to return JDK core ImageReaderSPI/ImageWriterSPI for a 
1334    // given formatName.
1335    public static List getJDKImageReaderWriterSPI(ServiceRegistry registry, 
1336                                                  String formatName,
1337                                                  boolean isReader) {
1338
1339        IIORegistry iioRegistry = (IIORegistry)registry;
1340
1341        Class spiClass;
1342        String descPart;
1343        if (isReader) {
1344            spiClass = ImageReaderSpi.class;
1345            descPart = " image reader";
1346        } else {
1347            spiClass = ImageWriterSpi.class;
1348            descPart = " image writer";
1349        }
1350
1351        Iterator iter = iioRegistry.getServiceProviders(spiClass, 
1352                                                        true); // useOrdering
1353
1354        String formatNames[];
1355        ImageReaderWriterSpi provider;
1356        String desc = "standard " + formatName + descPart;
1357        String jiioPath = "com.github.jaiimageio.impl";
1358        Locale locale = Locale.getDefault();
1359        ArrayList list = new ArrayList();
1360        while (iter.hasNext()) {
1361            provider = (ImageReaderWriterSpi)iter.next();
1362
1363            // Look for JDK core ImageWriterSpi's
1364            if (provider.getVendorName().startsWith("Sun Microsystems") &&
1365                desc.equalsIgnoreCase(provider.getDescription(locale)) &&
1366                // not JAI Image I/O plugins
1367                !provider.getPluginClassName().startsWith(jiioPath)) {
1368
1369                // Get the formatNames supported by this Spi
1370                formatNames = provider.getFormatNames();
1371                for (int i=0; i<formatNames.length; i++) {
1372                    if (formatNames[i].equalsIgnoreCase(formatName)) {
1373                        // Must be a JDK provided ImageReader/ImageWriter
1374                        list.add(provider);
1375                        break;
1376                    }
1377                }
1378            }
1379        }
1380        
1381        return list;
1382    }
1383
1384    public static void processOnRegistration(ServiceRegistry registry,
1385                                             Class category,
1386                                             String formatName,
1387                                             ImageReaderWriterSpi spi,
1388                                             int deregisterJvmVersion, 
1389                                             int priorityJvmVersion) {
1390
1391        // Check which JVM we are running on
1392        String jvmVendor = System.getProperty("java.vendor");
1393        String jvmVersionString = 
1394            System.getProperty("java.specification.version");
1395        int verIndex = jvmVersionString.indexOf("1.");
1396        // Skip the "1." part to get to the part of the version number that
1397        // actually changes from version to version
1398        // The assumption here is that "java.specification.version" is 
1399        // always of the format "x.y" and not "x.y.z" since that is what has
1400        // been practically observed in all JDKs to-date, an examination of
1401        // the code returning this property bears this out. However this does
1402        // not guarantee that the format won't change in the future, 
1403        // though that seems unlikely.
1404        jvmVersionString = jvmVersionString.substring(verIndex+2);
1405        
1406        int jvmVersion = Integer.parseInt(jvmVersionString);
1407        
1408        if (jvmVendor.equals("Sun Microsystems Inc.")) {
1409            
1410            List list;
1411            if (spi instanceof ImageReaderSpi)
1412                list = getJDKImageReaderWriterSPI(registry, formatName, true);
1413            else 
1414                list = getJDKImageReaderWriterSPI(registry, formatName, false);
1415            
1416            if (jvmVersion >= deregisterJvmVersion && list.size() != 0) {
1417                // De-register JIIO's plug-in
1418                registry.deregisterServiceProvider(spi, category);
1419            } else {
1420                for (int i=0; i<list.size(); i++) {
1421                    if (jvmVersion >= priorityJvmVersion) {
1422                        // Set JIIO plug-in to lower priority
1423                        registry.setOrdering(category, 
1424                                             list.get(i),
1425                                             spi);
1426                    } else {
1427                        // Set JIIO plug-in to higher priority
1428                        registry.setOrdering(category,
1429                                             spi,
1430                                             list.get(i));
1431                    }
1432                }
1433            }
1434        }
1435    }
1436    
1437    public static int readMultiByteInteger(ImageInputStream iis) throws IOException {
1438        int value = iis.readByte();
1439        int result = value & 0x7f;
1440        while((value & 0x80) == 0x80) {
1441            result <<= 7;
1442            value = iis.readByte();
1443            result |= (value & 0x7f);
1444        }
1445        return result;
1446    }
1447}