1 /*
   2  * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.imageio.plugins.common;
  27 
  28 import java.awt.Point;
  29 import java.awt.Rectangle;
  30 import java.awt.Transparency;
  31 import java.awt.color.ColorSpace;
  32 import java.awt.image.BufferedImage;
  33 import java.awt.image.ColorModel;
  34 import java.awt.image.ComponentColorModel;
  35 import java.awt.image.ComponentSampleModel;
  36 import java.awt.image.DataBuffer;
  37 import java.awt.image.DataBufferByte;
  38 import java.awt.image.DataBufferInt;
  39 import java.awt.image.DataBufferShort;
  40 import java.awt.image.DataBufferUShort;
  41 import java.awt.image.DirectColorModel;
  42 import java.awt.image.IndexColorModel;
  43 import java.awt.image.MultiPixelPackedSampleModel;
  44 import java.awt.image.Raster;
  45 import java.awt.image.RenderedImage;
  46 import java.awt.image.SampleModel;
  47 import java.awt.image.SinglePixelPackedSampleModel;
  48 import java.awt.image.WritableRaster;
  49 import java.util.Arrays;
  50 
  51 //import javax.imageio.ImageTypeSpecifier;
  52 
  53 import javax.imageio.IIOException;
  54 import javax.imageio.IIOImage;
  55 import javax.imageio.ImageTypeSpecifier;
  56 import javax.imageio.ImageWriter;
  57 import javax.imageio.spi.ImageWriterSpi;
  58 
  59 public class ImageUtil {
  60     /* XXX testing only
  61     public static void main(String[] args) {
  62         ImageTypeSpecifier bilevel =
  63             ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255},
  64                                              new byte[] {(byte)0, (byte)255},
  65                                              new byte[] {(byte)0, (byte)255},
  66                                              null, 1,
  67                                              DataBuffer.TYPE_BYTE);
  68         ImageTypeSpecifier gray =
  69             ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
  70         ImageTypeSpecifier grayAlpha =
  71             ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false,
  72                                                false);
  73         ImageTypeSpecifier rgb =
  74             ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
  75                                                  new int[] {0, 1, 2},
  76                                                  DataBuffer.TYPE_BYTE,
  77                                                  false,
  78                                                  false);
  79         ImageTypeSpecifier rgba =
  80             ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
  81                                                  new int[] {0, 1, 2, 3},
  82                                                  DataBuffer.TYPE_BYTE,
  83                                                  true,
  84                                                  false);
  85         ImageTypeSpecifier packed =
  86             ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
  87                                             0xff000000,
  88                                             0x00ff0000,
  89                                             0x0000ff00,
  90                                             0x000000ff,
  91                                             DataBuffer.TYPE_BYTE,
  92                                             false);
  93 
  94         SampleModel bandedSM =
  95             new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE,
  96                                                  1, 1, 15);
  97 
  98         System.out.println(createColorModel(bilevel.getSampleModel()));
  99         System.out.println(createColorModel(gray.getSampleModel()));
 100         System.out.println(createColorModel(grayAlpha.getSampleModel()));
 101         System.out.println(createColorModel(rgb.getSampleModel()));
 102         System.out.println(createColorModel(rgba.getSampleModel()));
 103         System.out.println(createColorModel(packed.getSampleModel()));
 104         System.out.println(createColorModel(bandedSM));
 105     }
 106     */
 107 
 108     /**
 109      * Creates a <code>ColorModel</code> that may be used with the
 110      * specified <code>SampleModel</code>.  If a suitable
 111      * <code>ColorModel</code> cannot be found, this method returns
 112      * <code>null</code>.
 113      *
 114      * <p> Suitable <code>ColorModel</code>s are guaranteed to exist
 115      * for all instances of <code>ComponentSampleModel</code>.
 116      * For 1- and 3- banded <code>SampleModel</code>s, the returned
 117      * <code>ColorModel</code> will be opaque.  For 2- and 4-banded
 118      * <code>SampleModel</code>s, the output will use alpha transparency
 119      * which is not premultiplied.  1- and 2-banded data will use a
 120      * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB
 121      * <code>ColorSpace</code>. Data with 5 or more bands will have a
 122      * <code>BogusColorSpace</code>.</p>
 123      *
 124      * <p>An instance of <code>DirectColorModel</code> will be created for
 125      * instances of <code>SinglePixelPackedSampleModel</code> with no more
 126      * than 4 bands.</p>
 127      *
 128      * <p>An instance of <code>IndexColorModel</code> will be created for
 129      * instances of <code>MultiPixelPackedSampleModel</code>. The colormap
 130      * will be a grayscale ramp with <code>1&nbsp;<<&nbsp;numberOfBits</code>
 131      * entries ranging from zero to at most 255.</p>
 132      *
 133      * @return An instance of <code>ColorModel</code> that is suitable for
 134      *         the supplied <code>SampleModel</code>, or <code>null</code>.
 135      *
 136      * @throws IllegalArgumentException  If <code>sampleModel</code> is
 137      *         <code>null</code>.
 138      */
 139     public static final ColorModel createColorModel(SampleModel sampleModel) {
 140         // Check the parameter.
 141         if(sampleModel == null) {
 142             throw new IllegalArgumentException("sampleModel == null!");
 143         }
 144 
 145         // Get the data type.
 146         int dataType = sampleModel.getDataType();
 147 
 148         // Check the data type
 149         switch(dataType) {
 150         case DataBuffer.TYPE_BYTE:
 151         case DataBuffer.TYPE_USHORT:
 152         case DataBuffer.TYPE_SHORT:
 153         case DataBuffer.TYPE_INT:
 154         case DataBuffer.TYPE_FLOAT:
 155         case DataBuffer.TYPE_DOUBLE:
 156             break;
 157         default:
 158             // Return null for other types.
 159             return null;
 160         }
 161 
 162         // The return variable.
 163         ColorModel colorModel = null;
 164 
 165         // Get the sample size.
 166         int[] sampleSize = sampleModel.getSampleSize();
 167 
 168         // Create a Component ColorModel.
 169         if(sampleModel instanceof ComponentSampleModel) {
 170             // Get the number of bands.
 171             int numBands = sampleModel.getNumBands();
 172 
 173             // Determine the color space.
 174             ColorSpace colorSpace = null;
 175             if(numBands <= 2) {
 176                 colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
 177             } else if(numBands <= 4) {
 178                 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
 179             } else {
 180                 colorSpace = new BogusColorSpace(numBands);
 181             }
 182 
 183             boolean hasAlpha = (numBands == 2) || (numBands == 4);
 184             boolean isAlphaPremultiplied = false;
 185             int transparency = hasAlpha ?
 186                 Transparency.TRANSLUCENT : Transparency.OPAQUE;
 187 
 188             colorModel = new ComponentColorModel(colorSpace,
 189                                                  sampleSize,
 190                                                  hasAlpha,
 191                                                  isAlphaPremultiplied,
 192                                                  transparency,
 193                                                  dataType);
 194         } else if (sampleModel.getNumBands() <= 4 &&
 195                    sampleModel instanceof SinglePixelPackedSampleModel) {
 196             SinglePixelPackedSampleModel sppsm =
 197                 (SinglePixelPackedSampleModel)sampleModel;
 198 
 199             int[] bitMasks = sppsm.getBitMasks();
 200             int rmask = 0;
 201             int gmask = 0;
 202             int bmask = 0;
 203             int amask = 0;
 204 
 205             int numBands = bitMasks.length;
 206             if (numBands <= 2) {
 207                 rmask = gmask = bmask = bitMasks[0];
 208                 if (numBands == 2) {
 209                     amask = bitMasks[1];
 210                 }
 211             } else {
 212                 rmask = bitMasks[0];
 213                 gmask = bitMasks[1];
 214                 bmask = bitMasks[2];
 215                 if (numBands == 4) {
 216                     amask = bitMasks[3];
 217                 }
 218             }
 219 
 220             int bits = 0;
 221             for (int i = 0; i < sampleSize.length; i++) {
 222                 bits += sampleSize[i];
 223             }
 224 
 225             return new DirectColorModel(bits, rmask, gmask, bmask, amask);
 226 
 227         } else if(sampleModel instanceof MultiPixelPackedSampleModel) {
 228             // Load the colormap with a ramp.
 229             int bitsPerSample = sampleSize[0];
 230             int numEntries = 1 << bitsPerSample;
 231             byte[] map = new byte[numEntries];
 232             for (int i = 0; i < numEntries; i++) {
 233                 map[i] = (byte)(i*255/(numEntries - 1));
 234             }
 235 
 236             colorModel = new IndexColorModel(bitsPerSample, numEntries,
 237                                              map, map, map);
 238 
 239         }
 240 
 241         return colorModel;
 242     }
 243 
 244     /**
 245      * For the case of binary data (<code>isBinary()</code> returns
 246      * <code>true</code>), return the binary data as a packed byte array.
 247      * The data will be packed as eight bits per byte with no bit offset,
 248      * i.e., the first bit in each image line will be the left-most of the
 249      * first byte of the line.  The line stride in bytes will be
 250      * <code>(int)((getWidth()+7)/8)</code>.  The length of the returned
 251      * array will be the line stride multiplied by <code>getHeight()</code>
 252      *
 253      * @return the binary data as a packed array of bytes with zero offset
 254      * of <code>null</code> if the data are not binary.
 255      * @throws IllegalArgumentException if <code>isBinary()</code> returns
 256      * <code>false</code> with the <code>SampleModel</code> of the
 257      * supplied <code>Raster</code> as argument.
 258      */
 259     public static byte[] getPackedBinaryData(Raster raster,
 260                                              Rectangle rect) {
 261         SampleModel sm = raster.getSampleModel();
 262         if(!isBinary(sm)) {
 263             throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
 264         }
 265 
 266         int rectX = rect.x;
 267         int rectY = rect.y;
 268         int rectWidth = rect.width;
 269         int rectHeight = rect.height;
 270 
 271         DataBuffer dataBuffer = raster.getDataBuffer();
 272 
 273         int dx = rectX - raster.getSampleModelTranslateX();
 274         int dy = rectY - raster.getSampleModelTranslateY();
 275 
 276         MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
 277         int lineStride = mpp.getScanlineStride();
 278         int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
 279         int bitOffset = mpp.getBitOffset(dx);
 280 
 281         int numBytesPerRow = (rectWidth + 7)/8;
 282         if(dataBuffer instanceof DataBufferByte &&
 283            eltOffset == 0 && bitOffset == 0 &&
 284            numBytesPerRow == lineStride &&
 285            ((DataBufferByte)dataBuffer).getData().length ==
 286            numBytesPerRow*rectHeight) {
 287             return ((DataBufferByte)dataBuffer).getData();
 288         }
 289 
 290         byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight];
 291 
 292         int b = 0;
 293 
 294         if(bitOffset == 0) {
 295             if(dataBuffer instanceof DataBufferByte) {
 296                 byte[] data = ((DataBufferByte)dataBuffer).getData();
 297                 int stride = numBytesPerRow;
 298                 int offset = 0;
 299                 for(int y = 0; y < rectHeight; y++) {
 300                     System.arraycopy(data, eltOffset,
 301                                      binaryDataArray, offset,
 302                                      stride);
 303                     offset += stride;
 304                     eltOffset += lineStride;
 305                 }
 306             } else if(dataBuffer instanceof DataBufferShort ||
 307                       dataBuffer instanceof DataBufferUShort) {
 308                 short[] data = dataBuffer instanceof DataBufferShort ?
 309                     ((DataBufferShort)dataBuffer).getData() :
 310                     ((DataBufferUShort)dataBuffer).getData();
 311 
 312                 for(int y = 0; y < rectHeight; y++) {
 313                     int xRemaining = rectWidth;
 314                     int i = eltOffset;
 315                     while(xRemaining > 8) {
 316                         short datum = data[i++];
 317                         binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF);
 318                         binaryDataArray[b++] = (byte)(datum & 0xFF);
 319                         xRemaining -= 16;
 320                     }
 321                     if(xRemaining > 0) {
 322                         binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF);
 323                     }
 324                     eltOffset += lineStride;
 325                 }
 326             } else if(dataBuffer instanceof DataBufferInt) {
 327                 int[] data = ((DataBufferInt)dataBuffer).getData();
 328 
 329                 for(int y = 0; y < rectHeight; y++) {
 330                     int xRemaining = rectWidth;
 331                     int i = eltOffset;
 332                     while(xRemaining > 24) {
 333                         int datum = data[i++];
 334                         binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF);
 335                         binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF);
 336                         binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF);
 337                         binaryDataArray[b++] = (byte)(datum & 0xFF);
 338                         xRemaining -= 32;
 339                     }
 340                     int shift = 24;
 341                     while(xRemaining > 0) {
 342                         binaryDataArray[b++] =
 343                             (byte)((data[i] >>> shift) & 0xFF);
 344                         shift -= 8;
 345                         xRemaining -= 8;
 346                     }
 347                     eltOffset += lineStride;
 348                 }
 349             }
 350         } else { // bitOffset != 0
 351             if(dataBuffer instanceof DataBufferByte) {
 352                 byte[] data = ((DataBufferByte)dataBuffer).getData();
 353 
 354                 if((bitOffset & 7) == 0) {
 355                     int stride = numBytesPerRow;
 356                     int offset = 0;
 357                     for(int y = 0; y < rectHeight; y++) {
 358                         System.arraycopy(data, eltOffset,
 359                                          binaryDataArray, offset,
 360                                          stride);
 361                         offset += stride;
 362                         eltOffset += lineStride;
 363                     }
 364                 } else { // bitOffset % 8 != 0
 365                     int leftShift = bitOffset & 7;
 366                     int rightShift = 8 - leftShift;
 367                     for(int y = 0; y < rectHeight; y++) {
 368                         int i = eltOffset;
 369                         int xRemaining = rectWidth;
 370                         while(xRemaining > 0) {
 371                             if(xRemaining > rightShift) {
 372                                 binaryDataArray[b++] =
 373                                     (byte)(((data[i++]&0xFF) << leftShift) |
 374                                            ((data[i]&0xFF) >>> rightShift));
 375                             } else {
 376                                 binaryDataArray[b++] =
 377                                     (byte)((data[i]&0xFF) << leftShift);
 378                             }
 379                             xRemaining -= 8;
 380                         }
 381                         eltOffset += lineStride;
 382                     }
 383                 }
 384             } else if(dataBuffer instanceof DataBufferShort ||
 385                       dataBuffer instanceof DataBufferUShort) {
 386                 short[] data = dataBuffer instanceof DataBufferShort ?
 387                     ((DataBufferShort)dataBuffer).getData() :
 388                     ((DataBufferUShort)dataBuffer).getData();
 389 
 390                 for(int y = 0; y < rectHeight; y++) {
 391                     int bOffset = bitOffset;
 392                     for(int x = 0; x < rectWidth; x += 8, bOffset += 8) {
 393                         int i = eltOffset + bOffset/16;
 394                         int mod = bOffset % 16;
 395                         int left = data[i] & 0xFFFF;
 396                         if(mod <= 8) {
 397                             binaryDataArray[b++] = (byte)(left >>> (8 - mod));
 398                         } else {
 399                             int delta = mod - 8;
 400                             int right = data[i+1] & 0xFFFF;
 401                             binaryDataArray[b++] =
 402                                 (byte)((left << delta) |
 403                                        (right >>> (16 - delta)));
 404                         }
 405                     }
 406                     eltOffset += lineStride;
 407                 }
 408             } else if(dataBuffer instanceof DataBufferInt) {
 409                 int[] data = ((DataBufferInt)dataBuffer).getData();
 410 
 411                 for(int y = 0; y < rectHeight; y++) {
 412                     int bOffset = bitOffset;
 413                     for(int x = 0; x < rectWidth; x += 8, bOffset += 8) {
 414                         int i = eltOffset + bOffset/32;
 415                         int mod = bOffset % 32;
 416                         int left = data[i];
 417                         if(mod <= 24) {
 418                             binaryDataArray[b++] =
 419                                 (byte)(left >>> (24 - mod));
 420                         } else {
 421                             int delta = mod - 24;
 422                             int right = data[i+1];
 423                             binaryDataArray[b++] =
 424                                 (byte)((left << delta) |
 425                                        (right >>> (32 - delta)));
 426                         }
 427                     }
 428                     eltOffset += lineStride;
 429                 }
 430             }
 431         }
 432 
 433         return binaryDataArray;
 434     }
 435 
 436     /**
 437      * Returns the binary data unpacked into an array of bytes.
 438      * The line stride will be the width of the <code>Raster</code>.
 439      *
 440      * @throws IllegalArgumentException if <code>isBinary()</code> returns
 441      * <code>false</code> with the <code>SampleModel</code> of the
 442      * supplied <code>Raster</code> as argument.
 443      */
 444     public static byte[] getUnpackedBinaryData(Raster raster,
 445                                                Rectangle rect) {
 446         SampleModel sm = raster.getSampleModel();
 447         if(!isBinary(sm)) {
 448             throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
 449         }
 450 
 451         int rectX = rect.x;
 452         int rectY = rect.y;
 453         int rectWidth = rect.width;
 454         int rectHeight = rect.height;
 455 
 456         DataBuffer dataBuffer = raster.getDataBuffer();
 457 
 458         int dx = rectX - raster.getSampleModelTranslateX();
 459         int dy = rectY - raster.getSampleModelTranslateY();
 460 
 461         MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
 462         int lineStride = mpp.getScanlineStride();
 463         int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
 464         int bitOffset = mpp.getBitOffset(dx);
 465 
 466         byte[] bdata = new byte[rectWidth*rectHeight];
 467         int maxY = rectY + rectHeight;
 468         int maxX = rectX + rectWidth;
 469         int k = 0;
 470 
 471         if(dataBuffer instanceof DataBufferByte) {
 472             byte[] data = ((DataBufferByte)dataBuffer).getData();
 473             for(int y = rectY; y < maxY; y++) {
 474                 int bOffset = eltOffset*8 + bitOffset;
 475                 for(int x = rectX; x < maxX; x++) {
 476                     byte b = data[bOffset/8];
 477                     bdata[k++] =
 478                         (byte)((b >>> (7 - bOffset & 7)) & 0x0000001);
 479                     bOffset++;
 480                 }
 481                 eltOffset += lineStride;
 482             }
 483         } else if(dataBuffer instanceof DataBufferShort ||
 484                   dataBuffer instanceof DataBufferUShort) {
 485             short[] data = dataBuffer instanceof DataBufferShort ?
 486                 ((DataBufferShort)dataBuffer).getData() :
 487                 ((DataBufferUShort)dataBuffer).getData();
 488             for(int y = rectY; y < maxY; y++) {
 489                 int bOffset = eltOffset*16 + bitOffset;
 490                 for(int x = rectX; x < maxX; x++) {
 491                     short s = data[bOffset/16];
 492                     bdata[k++] =
 493                         (byte)((s >>> (15 - bOffset % 16)) &
 494                                0x0000001);
 495                     bOffset++;
 496                 }
 497                 eltOffset += lineStride;
 498             }
 499         } else if(dataBuffer instanceof DataBufferInt) {
 500             int[] data = ((DataBufferInt)dataBuffer).getData();
 501             for(int y = rectY; y < maxY; y++) {
 502                 int bOffset = eltOffset*32 + bitOffset;
 503                 for(int x = rectX; x < maxX; x++) {
 504                     int i = data[bOffset/32];
 505                     bdata[k++] =
 506                         (byte)((i >>> (31 - bOffset % 32)) &
 507                                0x0000001);
 508                     bOffset++;
 509                 }
 510                 eltOffset += lineStride;
 511             }
 512         }
 513 
 514         return bdata;
 515     }
 516 
 517     /**
 518      * Sets the supplied <code>Raster</code>'s data from an array
 519      * of packed binary data of the form returned by
 520      * <code>getPackedBinaryData()</code>.
 521      *
 522      * @throws IllegalArgumentException if <code>isBinary()</code> returns
 523      * <code>false</code> with the <code>SampleModel</code> of the
 524      * supplied <code>Raster</code> as argument.
 525      */
 526     public static void setPackedBinaryData(byte[] binaryDataArray,
 527                                            WritableRaster raster,
 528                                            Rectangle rect) {
 529         SampleModel sm = raster.getSampleModel();
 530         if(!isBinary(sm)) {
 531             throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
 532         }
 533 
 534         int rectX = rect.x;
 535         int rectY = rect.y;
 536         int rectWidth = rect.width;
 537         int rectHeight = rect.height;
 538 
 539         DataBuffer dataBuffer = raster.getDataBuffer();
 540 
 541         int dx = rectX - raster.getSampleModelTranslateX();
 542         int dy = rectY - raster.getSampleModelTranslateY();
 543 
 544         MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
 545         int lineStride = mpp.getScanlineStride();
 546         int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
 547         int bitOffset = mpp.getBitOffset(dx);
 548 
 549         int b = 0;
 550 
 551         if(bitOffset == 0) {
 552             if(dataBuffer instanceof DataBufferByte) {
 553                 byte[] data = ((DataBufferByte)dataBuffer).getData();
 554                 if(data == binaryDataArray) {
 555                     // Optimal case: simply return.
 556                     return;
 557                 }
 558                 int stride = (rectWidth + 7)/8;
 559                 int offset = 0;
 560                 for(int y = 0; y < rectHeight; y++) {
 561                     System.arraycopy(binaryDataArray, offset,
 562                                      data, eltOffset,
 563                                      stride);
 564                     offset += stride;
 565                     eltOffset += lineStride;
 566                 }
 567             } else if(dataBuffer instanceof DataBufferShort ||
 568                       dataBuffer instanceof DataBufferUShort) {
 569                 short[] data = dataBuffer instanceof DataBufferShort ?
 570                     ((DataBufferShort)dataBuffer).getData() :
 571                     ((DataBufferUShort)dataBuffer).getData();
 572 
 573                 for(int y = 0; y < rectHeight; y++) {
 574                     int xRemaining = rectWidth;
 575                     int i = eltOffset;
 576                     while(xRemaining > 8) {
 577                         data[i++] =
 578                             (short)(((binaryDataArray[b++] & 0xFF) << 8) |
 579                                     (binaryDataArray[b++] & 0xFF));
 580                         xRemaining -= 16;
 581                     }
 582                     if(xRemaining > 0) {
 583                         data[i++] =
 584                             (short)((binaryDataArray[b++] & 0xFF) << 8);
 585                     }
 586                     eltOffset += lineStride;
 587                 }
 588             } else if(dataBuffer instanceof DataBufferInt) {
 589                 int[] data = ((DataBufferInt)dataBuffer).getData();
 590 
 591                 for(int y = 0; y < rectHeight; y++) {
 592                     int xRemaining = rectWidth;
 593                     int i = eltOffset;
 594                     while(xRemaining > 24) {
 595                         data[i++] =
 596                             (int)(((binaryDataArray[b++] & 0xFF) << 24) |
 597                                   ((binaryDataArray[b++] & 0xFF) << 16) |
 598                                   ((binaryDataArray[b++] & 0xFF) << 8) |
 599                                   (binaryDataArray[b++] & 0xFF));
 600                         xRemaining -= 32;
 601                     }
 602                     int shift = 24;
 603                     while(xRemaining > 0) {
 604                         data[i] |=
 605                             (int)((binaryDataArray[b++] & 0xFF) << shift);
 606                         shift -= 8;
 607                         xRemaining -= 8;
 608                     }
 609                     eltOffset += lineStride;
 610                 }
 611             }
 612         } else { // bitOffset != 0
 613             int stride = (rectWidth + 7)/8;
 614             int offset = 0;
 615             if(dataBuffer instanceof DataBufferByte) {
 616                 byte[] data = ((DataBufferByte)dataBuffer).getData();
 617 
 618                 if((bitOffset & 7) == 0) {
 619                     for(int y = 0; y < rectHeight; y++) {
 620                         System.arraycopy(binaryDataArray, offset,
 621                                          data, eltOffset,
 622                                          stride);
 623                         offset += stride;
 624                         eltOffset += lineStride;
 625                     }
 626                 } else { // bitOffset % 8 != 0
 627                     int rightShift = bitOffset & 7;
 628                     int leftShift = 8 - rightShift;
 629                     int leftShift8 = 8 + leftShift;
 630                     int mask = (byte)(255<<leftShift);
 631                     int mask1 = (byte)~mask;
 632 
 633                     for(int y = 0; y < rectHeight; y++) {
 634                         int i = eltOffset;
 635                         int xRemaining = rectWidth;
 636                         while(xRemaining > 0) {
 637                             byte datum = binaryDataArray[b++];
 638 
 639                             if (xRemaining > leftShift8) {
 640                                 // when all the bits in this BYTE will be set
 641                                 // into the data buffer.
 642                                 data[i] = (byte)((data[i] & mask ) |
 643                                     ((datum&0xFF) >>> rightShift));
 644                                 data[++i] = (byte)((datum & 0xFF) << leftShift);
 645                             } else if (xRemaining > leftShift) {
 646                                 // All the "leftShift" high bits will be set
 647                                 // into the data buffer.  But not all the
 648                                 // "rightShift" low bits will be set.
 649                                 data[i] = (byte)((data[i] & mask ) |
 650                                     ((datum&0xFF) >>> rightShift));
 651                                 i++;
 652                                 data[i] =
 653                                     (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift));
 654                             }
 655                             else {
 656                                 // Less than "leftShift" high bits will be set.
 657                                 int remainMask = (1 << leftShift - xRemaining) - 1;
 658                                 data[i] =
 659                                     (byte)((data[i] & (mask | remainMask)) |
 660                                     (datum&0xFF) >>> rightShift & ~remainMask);
 661                             }
 662                             xRemaining -= 8;
 663                         }
 664                         eltOffset += lineStride;
 665                     }
 666                 }
 667             } else if(dataBuffer instanceof DataBufferShort ||
 668                       dataBuffer instanceof DataBufferUShort) {
 669                 short[] data = dataBuffer instanceof DataBufferShort ?
 670                     ((DataBufferShort)dataBuffer).getData() :
 671                     ((DataBufferUShort)dataBuffer).getData();
 672 
 673                 int rightShift = bitOffset & 7;
 674                 int leftShift = 8 - rightShift;
 675                 int leftShift16 = 16 + leftShift;
 676                 int mask = (short)(~(255 << leftShift));
 677                 int mask1 = (short)(65535 << leftShift);
 678                 int mask2 = (short)~mask1;
 679 
 680                 for(int y = 0; y < rectHeight; y++) {
 681                     int bOffset = bitOffset;
 682                     int xRemaining = rectWidth;
 683                     for(int x = 0; x < rectWidth;
 684                         x += 8, bOffset += 8, xRemaining -= 8) {
 685                         int i = eltOffset + (bOffset >> 4);
 686                         int mod = bOffset & 15;
 687                         int datum = binaryDataArray[b++] & 0xFF;
 688                         if(mod <= 8) {
 689                             // This BYTE is set into one SHORT
 690                             if (xRemaining < 8) {
 691                                 // Mask the bits to be set.
 692                                 datum &= 255 << 8 - xRemaining;
 693                             }
 694                             data[i] = (short)((data[i] & mask) | (datum << leftShift));
 695                         } else if (xRemaining > leftShift16) {
 696                             // This BYTE will be set into two SHORTs
 697                             data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF));
 698                             data[++i] =
 699                                 (short)((datum << leftShift)&0xFFFF);
 700                         } else if (xRemaining > leftShift) {
 701                             // This BYTE will be set into two SHORTs;
 702                             // But not all the low bits will be set into SHORT
 703                             data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF));
 704                             i++;
 705                             data[i] =
 706                                 (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF));
 707                         } else {
 708                             // Only some of the high bits will be set into
 709                             // SHORTs
 710                             int remainMask = (1 << leftShift - xRemaining) - 1;
 711                             data[i] = (short)((data[i] & (mask1 | remainMask)) |
 712                                       ((datum >>> rightShift)&0xFFFF & ~remainMask));
 713                         }
 714                     }
 715                     eltOffset += lineStride;
 716                 }
 717             } else if(dataBuffer instanceof DataBufferInt) {
 718                 int[] data = ((DataBufferInt)dataBuffer).getData();
 719                 int rightShift = bitOffset & 7;
 720                 int leftShift = 8 - rightShift;
 721                 int leftShift32 = 32 + leftShift;
 722                 int mask = 0xFFFFFFFF << leftShift;
 723                 int mask1 = ~mask;
 724 
 725                 for(int y = 0; y < rectHeight; y++) {
 726                     int bOffset = bitOffset;
 727                     int xRemaining = rectWidth;
 728                     for(int x = 0; x < rectWidth;
 729                         x += 8, bOffset += 8, xRemaining -= 8) {
 730                         int i = eltOffset + (bOffset >> 5);
 731                         int mod = bOffset & 31;
 732                         int datum = binaryDataArray[b++] & 0xFF;
 733                         if(mod <= 24) {
 734                             // This BYTE is set into one INT
 735                             int shift = 24 - mod;
 736                             if (xRemaining < 8) {
 737                                 // Mask the bits to be set.
 738                                 datum &= 255 << 8 - xRemaining;
 739                             }
 740                             data[i] = (data[i] & (~(255 << shift))) | (datum << shift);
 741                         } else if (xRemaining > leftShift32) {
 742                             // All the bits of this BYTE will be set into two INTs
 743                             data[i] = (data[i] & mask) | (datum >>> rightShift);
 744                             data[++i] = datum << leftShift;
 745                         } else if (xRemaining > leftShift) {
 746                             // This BYTE will be set into two INTs;
 747                             // But not all the low bits will be set into INT
 748                             data[i] = (data[i] & mask) | (datum >>> rightShift);
 749                             i++;
 750                             data[i] = (data[i] & mask1) | (datum << leftShift);
 751                         } else {
 752                             // Only some of the high bits will be set into INT
 753                             int remainMask = (1 << leftShift - xRemaining) - 1;
 754                             data[i] = (data[i] & (mask | remainMask)) |
 755                                       (datum >>> rightShift & ~remainMask);
 756                         }
 757                     }
 758                     eltOffset += lineStride;
 759                 }
 760             }
 761         }
 762     }
 763 
 764     /**
 765      * Copies data into the packed array of the <code>Raster</code>
 766      * from an array of unpacked data of the form returned by
 767      * <code>getUnpackedBinaryData()</code>.
 768      *
 769      * <p> If the data are binary, then the target bit will be set if
 770      * and only if the corresponding byte is non-zero.
 771      *
 772      * @throws IllegalArgumentException if <code>isBinary()</code> returns
 773      * <code>false</code> with the <code>SampleModel</code> of the
 774      * supplied <code>Raster</code> as argument.
 775      */
 776     public static void setUnpackedBinaryData(byte[] bdata,
 777                                              WritableRaster raster,
 778                                              Rectangle rect) {
 779         SampleModel sm = raster.getSampleModel();
 780         if(!isBinary(sm)) {
 781             throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
 782         }
 783 
 784         int rectX = rect.x;
 785         int rectY = rect.y;
 786         int rectWidth = rect.width;
 787         int rectHeight = rect.height;
 788 
 789         DataBuffer dataBuffer = raster.getDataBuffer();
 790 
 791         int dx = rectX - raster.getSampleModelTranslateX();
 792         int dy = rectY - raster.getSampleModelTranslateY();
 793 
 794         MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
 795         int lineStride = mpp.getScanlineStride();
 796         int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
 797         int bitOffset = mpp.getBitOffset(dx);
 798 
 799         int k = 0;
 800 
 801         if(dataBuffer instanceof DataBufferByte) {
 802             byte[] data = ((DataBufferByte)dataBuffer).getData();
 803             for(int y = 0; y < rectHeight; y++) {
 804                 int bOffset = eltOffset*8 + bitOffset;
 805                 for(int x = 0; x < rectWidth; x++) {
 806                     if(bdata[k++] != (byte)0) {
 807                         data[bOffset/8] |=
 808                             (byte)(0x00000001 << (7 - bOffset & 7));
 809                     }
 810                     bOffset++;
 811                 }
 812                 eltOffset += lineStride;
 813             }
 814         } else if(dataBuffer instanceof DataBufferShort ||
 815                   dataBuffer instanceof DataBufferUShort) {
 816             short[] data = dataBuffer instanceof DataBufferShort ?
 817                 ((DataBufferShort)dataBuffer).getData() :
 818                 ((DataBufferUShort)dataBuffer).getData();
 819             for(int y = 0; y < rectHeight; y++) {
 820                 int bOffset = eltOffset*16 + bitOffset;
 821                 for(int x = 0; x < rectWidth; x++) {
 822                     if(bdata[k++] != (byte)0) {
 823                         data[bOffset/16] |=
 824                             (short)(0x00000001 <<
 825                                     (15 - bOffset % 16));
 826                     }
 827                     bOffset++;
 828                 }
 829                 eltOffset += lineStride;
 830             }
 831         } else if(dataBuffer instanceof DataBufferInt) {
 832             int[] data = ((DataBufferInt)dataBuffer).getData();
 833             for(int y = 0; y < rectHeight; y++) {
 834                 int bOffset = eltOffset*32 + bitOffset;
 835                 for(int x = 0; x < rectWidth; x++) {
 836                     if(bdata[k++] != (byte)0) {
 837                         data[bOffset/32] |=
 838                             (int)(0x00000001 <<
 839                                   (31 - bOffset % 32));
 840                     }
 841                     bOffset++;
 842                 }
 843                 eltOffset += lineStride;
 844             }
 845         }
 846     }
 847 
 848     public static boolean isBinary(SampleModel sm) {
 849         return sm instanceof MultiPixelPackedSampleModel &&
 850             ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 &&
 851             sm.getNumBands() == 1;
 852     }
 853 
 854     public static ColorModel createColorModel(ColorSpace colorSpace,
 855                                               SampleModel sampleModel) {
 856         ColorModel colorModel = null;
 857 
 858         if(sampleModel == null) {
 859             throw new IllegalArgumentException(I18N.getString("ImageUtil1"));
 860         }
 861 
 862         int numBands = sampleModel.getNumBands();
 863         if (numBands < 1 || numBands > 4) {
 864             return null;
 865         }
 866 
 867         int dataType = sampleModel.getDataType();
 868         if (sampleModel instanceof ComponentSampleModel) {
 869             if (dataType < DataBuffer.TYPE_BYTE ||
 870                 //dataType == DataBuffer.TYPE_SHORT ||
 871                 dataType > DataBuffer.TYPE_DOUBLE) {
 872                 return null;
 873             }
 874 
 875             if (colorSpace == null)
 876                 colorSpace =
 877                     numBands <= 2 ?
 878                     ColorSpace.getInstance(ColorSpace.CS_GRAY) :
 879                     ColorSpace.getInstance(ColorSpace.CS_sRGB);
 880 
 881             boolean useAlpha = (numBands == 2) || (numBands == 4);
 882             int transparency = useAlpha ?
 883                                Transparency.TRANSLUCENT : Transparency.OPAQUE;
 884 
 885             boolean premultiplied = false;
 886 
 887             int dataTypeSize = DataBuffer.getDataTypeSize(dataType);
 888             int[] bits = new int[numBands];
 889             for (int i = 0; i < numBands; i++) {
 890                 bits[i] = dataTypeSize;
 891             }
 892 
 893             colorModel = new ComponentColorModel(colorSpace,
 894                                                  bits,
 895                                                  useAlpha,
 896                                                  premultiplied,
 897                                                  transparency,
 898                                                  dataType);
 899         } else if (sampleModel instanceof SinglePixelPackedSampleModel) {
 900             SinglePixelPackedSampleModel sppsm =
 901                 (SinglePixelPackedSampleModel)sampleModel;
 902 
 903             int[] bitMasks = sppsm.getBitMasks();
 904             int rmask = 0;
 905             int gmask = 0;
 906             int bmask = 0;
 907             int amask = 0;
 908 
 909             numBands = bitMasks.length;
 910             if (numBands <= 2) {
 911                 rmask = gmask = bmask = bitMasks[0];
 912                 if (numBands == 2) {
 913                     amask = bitMasks[1];
 914                 }
 915             } else {
 916                 rmask = bitMasks[0];
 917                 gmask = bitMasks[1];
 918                 bmask = bitMasks[2];
 919                 if (numBands == 4) {
 920                     amask = bitMasks[3];
 921                 }
 922             }
 923 
 924             int[] sampleSize = sppsm.getSampleSize();
 925             int bits = 0;
 926             for (int i = 0; i < sampleSize.length; i++) {
 927                 bits += sampleSize[i];
 928             }
 929 
 930             if (colorSpace == null)
 931                 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
 932 
 933             colorModel =
 934                 new DirectColorModel(colorSpace,
 935                                      bits, rmask, gmask, bmask, amask,
 936                                      false,
 937                                      sampleModel.getDataType());
 938         } else if (sampleModel instanceof MultiPixelPackedSampleModel) {
 939             int bits =
 940                 ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride();
 941             int size = 1 << bits;
 942             byte[] comp = new byte[size];
 943 
 944             for (int i = 0; i < size; i++)
 945                 comp[i] = (byte)(255 * i / (size - 1));
 946 
 947             colorModel = new IndexColorModel(bits, size, comp, comp, comp);
 948         }
 949 
 950         return colorModel;
 951     }
 952 
 953     public static int getElementSize(SampleModel sm) {
 954         int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
 955 
 956         if (sm instanceof MultiPixelPackedSampleModel) {
 957             MultiPixelPackedSampleModel mppsm =
 958                 (MultiPixelPackedSampleModel)sm;
 959             return mppsm.getSampleSize(0) * mppsm.getNumBands();
 960         } else if (sm instanceof ComponentSampleModel) {
 961             return sm.getNumBands() * elementSize;
 962         } else if (sm instanceof SinglePixelPackedSampleModel) {
 963             return elementSize;
 964         }
 965 
 966         return elementSize * sm.getNumBands();
 967 
 968     }
 969 
 970     public static long getTileSize(SampleModel sm) {
 971         int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
 972 
 973         if (sm instanceof MultiPixelPackedSampleModel) {
 974             MultiPixelPackedSampleModel mppsm =
 975                 (MultiPixelPackedSampleModel)sm;
 976             return (mppsm.getScanlineStride() * mppsm.getHeight() +
 977                    (mppsm.getDataBitOffset() + elementSize -1) / elementSize) *
 978                    ((elementSize + 7) / 8);
 979         } else if (sm instanceof ComponentSampleModel) {
 980             ComponentSampleModel csm = (ComponentSampleModel)sm;
 981             int[] bandOffsets = csm.getBandOffsets();
 982             int maxBandOff = bandOffsets[0];
 983             for (int i=1; i<bandOffsets.length; i++)
 984                 maxBandOff = Math.max(maxBandOff, bandOffsets[i]);
 985 
 986             long size = 0;
 987             int pixelStride = csm.getPixelStride();
 988             int scanlineStride = csm.getScanlineStride();
 989             if (maxBandOff >= 0)
 990                 size += maxBandOff + 1;
 991             if (pixelStride > 0)
 992                 size += pixelStride * (sm.getWidth() - 1);
 993             if (scanlineStride > 0)
 994                 size += scanlineStride * (sm.getHeight() - 1);
 995 
 996             int[] bankIndices = csm.getBankIndices();
 997             maxBandOff = bankIndices[0];
 998             for (int i=1; i<bankIndices.length; i++)
 999                 maxBandOff = Math.max(maxBandOff, bankIndices[i]);
1000             return size * (maxBandOff + 1) * ((elementSize + 7) / 8);
1001         } else if (sm instanceof SinglePixelPackedSampleModel) {
1002             SinglePixelPackedSampleModel sppsm =
1003                 (SinglePixelPackedSampleModel)sm;
1004             long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) +
1005                         sppsm.getWidth();
1006             return size * ((elementSize + 7) / 8);
1007         }
1008 
1009         return 0;
1010     }
1011 
1012     public static long getBandSize(SampleModel sm) {
1013         int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
1014 
1015         if (sm instanceof ComponentSampleModel) {
1016             ComponentSampleModel csm = (ComponentSampleModel)sm;
1017             int pixelStride = csm.getPixelStride();
1018             int scanlineStride = csm.getScanlineStride();
1019             long size = Math.min(pixelStride, scanlineStride);
1020 
1021             if (pixelStride > 0)
1022                 size += pixelStride * (sm.getWidth() - 1);
1023             if (scanlineStride > 0)
1024                 size += scanlineStride * (sm.getHeight() - 1);
1025             return size * ((elementSize + 7) / 8);
1026         } else
1027             return getTileSize(sm);
1028     }
1029     /**
1030      * Tests whether the color indices represent a gray-scale image.
1031      *
1032      * @param r The red channel color indices.
1033      * @param g The green channel color indices.
1034      * @param b The blue channel color indices.
1035      * @return If all the indices have 256 entries, and are identical mappings,
1036      *         return <code>true</code>; otherwise, return <code>false</code>.
1037      */
1038     public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) {
1039         if (r.length != g.length || r.length != b.length)
1040             return false;
1041 
1042         int size = r.length;
1043 
1044         if (size != 256)
1045             return false;
1046 
1047         for (int i = 0; i < size; i++) {
1048             byte temp = (byte) i;
1049 
1050             if (r[i] != temp || g[i] != temp || b[i] != temp)
1051                 return false;
1052         }
1053 
1054         return true;
1055     }
1056 
1057     /** Converts the provided object to <code>String</code> */
1058     public static String convertObjectToString(Object obj) {
1059         if (obj == null)
1060             return "";
1061 
1062         String s = "";
1063         if (obj instanceof byte[]) {
1064             byte[] bArray = (byte[])obj;
1065             for (int i = 0; i < bArray.length; i++)
1066                 s += bArray[i] + " ";
1067             return s;
1068         }
1069 
1070         if (obj instanceof int[]) {
1071             int[] iArray = (int[])obj;
1072             for (int i = 0; i < iArray.length; i++)
1073                 s += iArray[i] + " " ;
1074             return s;
1075         }
1076 
1077         if (obj instanceof short[]) {
1078             short[] sArray = (short[])obj;
1079             for (int i = 0; i < sArray.length; i++)
1080                 s += sArray[i] + " " ;
1081             return s;
1082         }
1083 
1084         return obj.toString();
1085 
1086     }
1087 
1088     /** Checks that the provided <code>ImageWriter</code> can encode
1089      * the provided <code>ImageTypeSpecifier</code> or not.  If not, an
1090      * <code>IIOException</code> will be thrown.
1091      * @param writer The provided <code>ImageWriter</code>.
1092      * @param type The image to be tested.
1093      * @throws IIOException If the writer cannot encoded the provided image.
1094      */
1095     public static final void canEncodeImage(ImageWriter writer,
1096                                             ImageTypeSpecifier type)
1097         throws IIOException {
1098         ImageWriterSpi spi = writer.getOriginatingProvider();
1099 
1100         if(type != null && spi != null && !spi.canEncodeImage(type))  {
1101             throw new IIOException(I18N.getString("ImageUtil2")+" "+
1102                                    writer.getClass().getName());
1103         }
1104     }
1105 
1106     /** Checks that the provided <code>ImageWriter</code> can encode
1107      * the provided <code>ColorModel</code> and <code>SampleModel</code>.
1108      * If not, an <code>IIOException</code> will be thrown.
1109      * @param writer The provided <code>ImageWriter</code>.
1110      * @param colorModel The provided <code>ColorModel</code>.
1111      * @param sampleModel The provided <code>SampleModel</code>.
1112      * @throws IIOException If the writer cannot encoded the provided image.
1113      */
1114     public static final void canEncodeImage(ImageWriter writer,
1115                                             ColorModel colorModel,
1116                                             SampleModel sampleModel)
1117         throws IIOException {
1118         ImageTypeSpecifier type = null;
1119         if (colorModel != null && sampleModel != null)
1120             type = new ImageTypeSpecifier(colorModel, sampleModel);
1121         canEncodeImage(writer, type);
1122     }
1123 
1124     /**
1125      * Returns whether the image has contiguous data across rows.
1126      */
1127     public static final boolean imageIsContiguous(RenderedImage image) {
1128         SampleModel sm;
1129         if(image instanceof BufferedImage) {
1130             WritableRaster ras = ((BufferedImage)image).getRaster();
1131             sm = ras.getSampleModel();
1132         } else {
1133             sm = image.getSampleModel();
1134         }
1135 
1136         if (sm instanceof ComponentSampleModel) {
1137             // Ensure image rows samples are stored contiguously
1138             // in a single bank.
1139             ComponentSampleModel csm = (ComponentSampleModel)sm;
1140 
1141             if (csm.getPixelStride() != csm.getNumBands()) {
1142                 return false;
1143             }
1144 
1145             int[] bandOffsets = csm.getBandOffsets();
1146             for (int i = 0; i < bandOffsets.length; i++) {
1147                 if (bandOffsets[i] != i) {
1148                     return false;
1149                 }
1150             }
1151 
1152             int[] bankIndices = csm.getBankIndices();
1153             for (int i = 0; i < bandOffsets.length; i++) {
1154                 if (bankIndices[i] != 0) {
1155                     return false;
1156                 }
1157             }
1158 
1159             return true;
1160         }
1161 
1162         // Otherwise true if and only if it's a bilevel image with
1163         // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit
1164         // pixel stride.
1165         return ImageUtil.isBinary(sm);
1166     }
1167 }