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