1 /*
   2  * Copyright (c) 2009, 2015, 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.javafx.iio.common;
  27 
  28 import com.sun.javafx.geom.Point2D;
  29 import com.sun.javafx.geom.Rectangle;
  30 import com.sun.javafx.iio.ImageFrame;
  31 import com.sun.javafx.iio.ImageMetadata;
  32 import com.sun.javafx.iio.ImageStorage.ImageType;
  33 import java.io.EOFException;
  34 import java.io.File;
  35 import java.io.FileInputStream;
  36 import java.io.IOException;
  37 import java.io.InputStream;
  38 import java.net.URL;
  39 import java.nio.Buffer;
  40 import java.nio.ByteBuffer;
  41 
  42 /**
  43  * A set of format-independent convenience methods useful in image loading
  44  * and saving.
  45  */
  46 public class ImageTools {
  47 
  48     /**
  49      * The percentage increment between progress report updates.
  50      */
  51     public static final int PROGRESS_INTERVAL = 5;
  52 
  53     /**
  54      * See the general contract of the <code>readFully</code>
  55      * method of <code>DataInput</code>.
  56      * <p>
  57      * Bytes
  58      * for this operation are read from the specified
  59      * input stream.
  60      *
  61      * @param      stream the stream from which to read the data.
  62      * @param      b     the buffer into which the data is read.
  63      * @param      off   the start offset of the data.
  64      * @param      len   the number of bytes to read.
  65      * @exception  EOFException  if this input stream reaches the end before
  66      *               reading all the bytes.
  67      * @exception  IOException   if another I/O error occurs.
  68      */
  69     public static int readFully(InputStream stream,
  70             byte[] b, int off, int len) throws IOException {
  71         if (len < 0) {
  72             throw new IndexOutOfBoundsException();
  73         }
  74         int requestedLength = len;
  75         // Fix 4430357 - if off + len < 0, overflow occurred
  76         if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
  77             throw new IndexOutOfBoundsException("off < 0 || len < 0 || off + len > b.length!");
  78         }
  79 
  80         while (len > 0) {
  81             int nbytes = stream.read(b, off, len);
  82             if (nbytes == -1) {
  83                 throw new EOFException();
  84             }
  85             off += nbytes;
  86             len -= nbytes;
  87         }
  88 
  89         return requestedLength;
  90     }
  91 
  92     /**
  93      * See the general contract of the <code>readFully</code>
  94      * method of <code>DataInput</code>.
  95      * <p>
  96      * Bytes
  97      * for this operation are read from the contained
  98      * input stream.
  99      *
 100      * @param      stream the stream from which to read the data.
 101      * @param      b   the buffer into which the data is read.
 102      * @exception  EOFException  if this input stream reaches the end before
 103      *             reading all the bytes.
 104      * @exception  IOException   if another I/O error occurs.
 105      */
 106     public static int readFully(InputStream stream, byte[] b) throws IOException {
 107         return readFully(stream, b, 0, b.length);
 108     }
 109 
 110     /**
 111      * Skips over <code>n</code> bytes of data from the input stream.
 112      * @param      stream the stream to skip.
 113      * @param      n the number of bytes to be skipped.
 114      * @exception  EOFException if this input stream reaches the end before
 115      *             skipping all the bytes.
 116      * @exception  IOException if another I/O error occurs.
 117      */
 118     public static void skipFully(InputStream stream, long n) throws IOException {
 119         while (n > 0) {
 120             long skipped = stream.skip(n);
 121             if (skipped <= 0) {
 122                 // check if the EOF is reached
 123                 if (stream.read() == -1) {
 124                     throw new EOFException();
 125                 }
 126                 n--;
 127             } else {
 128                 n -= skipped;
 129             }
 130         }
 131     }
 132 //    public static PixelFormat getPixelFormat(ImageType type) {
 133 //        PixelFormat format;
 134 //        switch (type) {
 135 //            case GRAY:
 136 //                format = PixelFormat.BYTE_GRAY;
 137 //                break;
 138 //            case GRAY_ALPHA:
 139 //            case GRAY_ALPHA_PRE:
 140 //                format = PixelFormat.BYTE_RGBA_PRE;
 141 //                break;
 142 //            case PALETTE:
 143 //                format = PixelFormat.BYTE_RGB;
 144 //                break;
 145 //            case PALETTE_ALPHA:
 146 //            case PALETTE_ALPHA_PRE:
 147 //                format = PixelFormat.BYTE_RGBA_PRE;
 148 //                break;
 149 //            case RGB:
 150 //                format = PixelFormat.BYTE_RGB;
 151 //                break;
 152 //            case RGBA:
 153 //            case RGBA_PRE:
 154 //                format = PixelFormat.BYTE_RGBA_PRE;
 155 //                break;
 156 //            default:
 157 //                // This should not be possible ...
 158 //                throw new IllegalArgumentException("Unknown ImageType " + type);
 159 //        }
 160 //
 161 //        return format;
 162 //    }
 163 //    public static boolean isConversionACopy(ImageType type, PixelFormat format) {
 164 //        return (type == ImageType.GRAY && format == PixelFormat.BYTE_GRAY) ||
 165 //                (type == ImageType.RGB && format == PixelFormat.BYTE_RGB) ||
 166 //                (type == ImageType.RGBA_PRE && format == PixelFormat.BYTE_RGBA_PRE);
 167 //    }
 168     public static ImageType getConvertedType(ImageType type) {
 169         ImageType retType = type;
 170         switch (type) {
 171             case GRAY:
 172                 retType = ImageType.GRAY;
 173                 break;
 174             case GRAY_ALPHA:
 175             case GRAY_ALPHA_PRE:
 176             case PALETTE_ALPHA:
 177             case PALETTE_ALPHA_PRE:
 178             case PALETTE_TRANS:
 179             case RGBA:
 180                 retType = ImageType.RGBA_PRE;
 181                 break;
 182             case PALETTE:
 183             case RGB:
 184                 retType = ImageType.RGB;
 185                 break;
 186             case RGBA_PRE:
 187                 retType = ImageType.RGBA_PRE;
 188                 break;
 189             default:
 190                 throw new IllegalArgumentException("Unsupported ImageType " + type);
 191         }
 192         return retType;
 193     }
 194 
 195     public static byte[] createImageArray(ImageType type, int width, int height) {
 196         int numBands = 0;
 197         switch (type) {
 198             case GRAY:
 199             case PALETTE:
 200             case PALETTE_ALPHA:
 201             case PALETTE_ALPHA_PRE:
 202                 numBands = 1;
 203                 break;
 204             case GRAY_ALPHA:
 205             case GRAY_ALPHA_PRE:
 206                 numBands = 2;
 207                 break;
 208             case RGB:
 209                 numBands = 3;
 210                 break;
 211             case RGBA:
 212             case RGBA_PRE:
 213                 numBands = 4;
 214                 break;
 215             default:
 216                 throw new IllegalArgumentException("Unsupported ImageType " + type);
 217         }
 218         return new byte[width * height * numBands];
 219     }
 220 
 221     public static ImageFrame convertImageFrame(ImageFrame frame) {
 222         ImageFrame retFrame;
 223         ImageType type = frame.getImageType();
 224         ImageType convertedType = getConvertedType(type);
 225         if (convertedType == type) {
 226             retFrame = frame;
 227         } else {
 228             byte[] inArray = null;
 229             Buffer buf = frame.getImageData();
 230             if (!(buf instanceof ByteBuffer)) {
 231                 throw new IllegalArgumentException("!(frame.getImageData() instanceof ByteBuffer)");
 232             }
 233             ByteBuffer bbuf = (ByteBuffer) buf;
 234             if (bbuf.hasArray()) {
 235                 inArray = bbuf.array();
 236             } else {
 237                 inArray = new byte[bbuf.capacity()];
 238                 bbuf.get(inArray);
 239             }
 240             int width = frame.getWidth();
 241             int height = frame.getHeight();
 242             int inStride = frame.getStride();
 243             byte[] outArray = createImageArray(convertedType, width, height);
 244             ByteBuffer newBuf = ByteBuffer.wrap(outArray);
 245             int outStride = outArray.length / height;
 246             byte[][] palette = frame.getPalette();
 247             ImageMetadata metadata = frame.getMetadata();
 248             int transparentIndex = metadata.transparentIndex != null ? metadata.transparentIndex : 0;
 249             convert(width, height, type,
 250                     inArray, 0, inStride, outArray, 0, outStride,
 251                     palette, transparentIndex, false);
 252             ImageMetadata imd = new ImageMetadata(metadata.gamma,
 253                     metadata.blackIsZero, null,
 254                     metadata.backgroundColor, null,
 255                     metadata.delayTime, metadata.loopCount,
 256                     metadata.imageWidth, metadata.imageHeight,
 257                     metadata.imageLeftPosition, metadata.imageTopPosition,
 258                     metadata.disposalMethod);
 259             retFrame = new ImageFrame(convertedType, newBuf, width, height,
 260                     outStride, null, imd);
 261         }
 262         return retFrame;
 263     }
 264 
 265     public static byte[] convert(int width, int height, ImageType inputType,
 266             byte[] input, int inputOffset, int inRowStride,
 267             byte[] output, int outputOffset, int outRowStride,
 268             byte[][] palette, int transparentIndex, boolean skipTransparent) {
 269         //
 270         // Take care of the layouts that are a direct copy.
 271         //
 272         if (inputType == ImageType.GRAY ||
 273                 inputType == ImageType.RGB ||
 274                 inputType == ImageType.RGBA_PRE) {
 275             if (input != output) {
 276                 int bytesPerRow = width;
 277                 if (inputType == ImageType.RGB) {
 278                     bytesPerRow *= 3;
 279                 } else if (inputType == ImageType.RGBA_PRE) {
 280                     bytesPerRow *= 4;
 281                 }
 282                 if (height == 1) {
 283                     System.arraycopy(input, inputOffset, output, outputOffset, bytesPerRow);
 284                 } else {
 285                     int inRowOffset = inputOffset;
 286                     int outRowOffset = outputOffset;
 287                     for (int row = 0; row < height; row++) {
 288                         System.arraycopy(input, inRowOffset, output, outRowOffset, bytesPerRow);
 289                         inRowOffset += inRowStride;
 290                         outRowOffset += outRowStride;
 291                     }
 292                 }
 293             }
 294         } else if (inputType == ImageType.GRAY_ALPHA || inputType == ImageType.GRAY_ALPHA_PRE) {
 295             int inOffset = inputOffset;
 296             int outOffset = outputOffset;
 297             if (inputType == ImageType.GRAY_ALPHA) {
 298                 for (int y = 0; y < height; y++) {
 299                     int inOff = inOffset;
 300                     int outOff = outOffset;
 301                     for (int x = 0; x < width; x++) {
 302                         // copy input to local variables in case operating in place
 303                         byte gray = input[inOff++];
 304                         int alpha = input[inOff++] & 0xff;
 305                         float f = alpha / 255.0F;
 306                         gray = (byte) (f * (gray & 0xff));
 307                         output[outOff++] = gray;
 308                         output[outOff++] = gray;
 309                         output[outOff++] = gray;
 310                         output[outOff++] = (byte) alpha;
 311                     }
 312                     inOffset += inRowStride;
 313                     outOffset += outRowStride;
 314                 }
 315             } else {
 316                 for (int y = 0; y < height; y++) {
 317                     int inOff = inOffset;
 318                     int outOff = outOffset;
 319                     for (int x = 0; x < width; x++) {
 320                         // copy input to local variables in case operating in place
 321                         byte gray = input[inOff++];
 322                         output[outOff++] = gray;
 323                         output[outOff++] = gray;
 324                         output[outOff++] = gray;
 325                         output[outOff++] = input[inOff++];
 326                     }
 327                     inOffset += inRowStride;
 328                     outOffset += outRowStride;
 329                 }
 330             }
 331         } else if (inputType == ImageType.PALETTE) {
 332             int inOffset = inputOffset;
 333             int outOffset = outputOffset;
 334             byte[] red = palette[0];
 335             byte[] green = palette[1];
 336             byte[] blue = palette[2];
 337             int inOff = inOffset;
 338             int outOff = outOffset;
 339             //loop through the scanline and mask for the value if each byte.
 340             //the byte is an index into the palette array for that pixel.
 341             for (int x = 0; x < width; x++) {
 342                 int index = (input[inOff++] & 0xff);
 343 
 344                 output[outOff++] = red[index];
 345                 output[outOff++] = green[index];
 346                 output[outOff++] = blue[index];
 347 
 348                 outOffset += outRowStride;
 349             }
 350         } else if (inputType == ImageType.PALETTE_ALPHA) {
 351             int inOffset = inputOffset;
 352             int outOffset = outputOffset;
 353             byte[] red = palette[0];
 354             byte[] green = palette[1];
 355             byte[] blue = palette[2];
 356             byte[] alpha = palette[3];
 357                 int inOff = inOffset;
 358                 int outOff = outOffset;
 359                 for (int x = 0; x < width; x++) {
 360                     int index = input[inOff++] & 0xff;
 361                     byte r = red[index];
 362                     byte g = green[index];
 363                     byte b = blue[index];
 364                     int a = alpha[index] & 0xff;
 365                     float f = a / 255.0F;
 366                     output[outOff++] = (byte) (f * (r & 0xff));
 367                     output[outOff++] = (byte) (f * (g & 0xff));
 368                     output[outOff++] = (byte) (f * (b & 0xff));
 369                     output[outOff++] = (byte) a;
 370                 }
 371                 inOffset += inRowStride;
 372                 outOffset += outRowStride;
 373         } else if (inputType == ImageType.PALETTE_ALPHA_PRE) {
 374             int inOffset = inputOffset;
 375             int outOffset = outputOffset;
 376             byte[] red = palette[0];
 377             byte[] green = palette[1];
 378             byte[] blue = palette[2];
 379             byte[] alpha = palette[3];
 380             for (int y = 0; y < height; y++) {
 381                 int inOff = inOffset;
 382                 int outOff = outOffset;
 383                 for (int x = 0; x < width; x++) {
 384                     int index = input[inOff++] & 0xff;
 385                     output[outOff++] = red[index];
 386                     output[outOff++] = green[index];
 387                     output[outOff++] = blue[index];
 388                     output[outOff++] = alpha[index];
 389                 }
 390                 inOffset += inRowStride;
 391                 outOffset += outRowStride;
 392             }
 393         } else if (inputType == ImageType.PALETTE_TRANS) {
 394             int inOffset = inputOffset;
 395             int outOffset = outputOffset;
 396             for (int y = 0; y < height; y++) {
 397                 int inOff = inOffset;
 398                 int outOff = outOffset;
 399                 byte[] red = palette[0];
 400                 byte[] green = palette[1];
 401                 byte[] blue = palette[2];
 402                 for (int x = 0; x < width; x++) {
 403                     int index = input[inOff++] & 0xff;
 404                     if (index == transparentIndex) {
 405                         if (skipTransparent) {
 406                             outOff+=4;
 407                         } else {
 408                             output[outOff++] = (byte) 0;
 409                             output[outOff++] = (byte) 0;
 410                             output[outOff++] = (byte) 0;
 411                             output[outOff++] = (byte) 0;
 412                         }
 413                     } else {
 414                         output[outOff++] = red[index];
 415                         output[outOff++] = green[index];
 416                         output[outOff++] = blue[index];
 417                         output[outOff++] = (byte) 255;
 418                     }
 419                 }
 420                 inOffset += inRowStride;
 421                 outOffset += outRowStride;
 422             }
 423         } else if (inputType == ImageType.RGBA) {
 424             int inOffset = inputOffset;
 425             int outOffset = outputOffset;
 426             for (int y = 0; y < height; y++) {
 427                 int inOff = inOffset;
 428                 int outOff = outOffset;
 429                 for (int x = 0; x < width; x++) {
 430                     // copy input to local variables in case operating in place
 431                     byte red = input[inOff++];
 432                     byte green = input[inOff++];
 433                     byte blue = input[inOff++];
 434                     int alpha = input[inOff++] & 0xff;
 435                     float f = alpha / 255.0F;
 436                     output[outOff++] = (byte) (f * (red & 0xff));
 437                     output[outOff++] = (byte) (f * (green & 0xff));
 438                     output[outOff++] = (byte) (f * (blue & 0xff));
 439                     output[outOff++] = (byte) alpha;
 440                 }
 441 //                System.arraycopy(input, inOffset, output, outOffset, width*4);
 442                 inOffset += inRowStride;
 443                 outOffset += outRowStride;
 444             }
 445         } else {
 446             throw new UnsupportedOperationException("Unsupported ImageType " +
 447                     inputType);
 448         }
 449 
 450         return output;
 451     }
 452 
 453     public static String getScaledImageName(String path) {
 454         StringBuilder result = new StringBuilder();
 455         int slash = path.lastIndexOf('/');
 456         String name = (slash < 0) ? path : path.substring(slash + 1);
 457         int dot = name.lastIndexOf(".");
 458         if (dot < 0) {
 459             dot = name.length();
 460         }
 461         if (slash >= 0) {
 462             result.append(path.substring(0, slash + 1));
 463         }
 464         result.append(name.substring(0, dot));
 465         result.append("@2x");
 466         result.append(name.substring(dot));
 467         return result.toString();
 468     }
 469 
 470     public static InputStream createInputStream(String input) throws IOException {
 471         InputStream stream = null;
 472 
 473         // there should be a central utility  for mapping these Strings to their
 474         // inputStreams
 475         try {
 476             File file = new File(input);
 477             if (file.exists()) {
 478                 stream = new FileInputStream(file);
 479             }
 480         } catch (Exception e) {
 481             // ignore exception and try as url.
 482         }
 483         if (stream == null) {
 484             URL url = new URL(input);
 485             stream = url.openStream();
 486         }
 487         return stream;
 488     }
 489 
 490     // Helper for computeUpdatedPixels method
 491     private static void computeUpdatedPixels(int sourceOffset,
 492             int sourceExtent,
 493             int destinationOffset,
 494             int dstMin,
 495             int dstMax,
 496             int sourceSubsampling,
 497             int passStart,
 498             int passExtent,
 499             int passPeriod,
 500             int[] vals,
 501             int offset) {
 502         // We need to satisfy the congruences:
 503         // dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
 504         //
 505         // src - passStart == 0 (mod passPeriod)
 506         // src - sourceOffset == 0 (mod sourceSubsampling)
 507         //
 508         // subject to the inequalities:
 509         //
 510         // src >= passStart
 511         // src < passStart + passExtent
 512         // src >= sourceOffset
 513         // src < sourceOffset + sourceExtent
 514         // dst >= dstMin
 515         // dst <= dstmax
 516         //
 517         // where
 518         //
 519         // dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
 520         //
 521         // For now we use a brute-force approach although we could
 522         // attempt to analyze the congruences.  If passPeriod and
 523         // sourceSubsamling are relatively prime, the period will be
 524         // their product.  If they share a common factor, either the
 525         // period will be equal to the larger value, or the sequences
 526         // will be completely disjoint, depending on the relationship
 527         // between passStart and sourceOffset.  Since we only have to do this
 528         // twice per image (once each for X and Y), it seems cheap enough
 529         // to do it the straightforward way.
 530 
 531         boolean gotPixel = false;
 532         int firstDst = -1;
 533         int secondDst = -1;
 534         int lastDst = -1;
 535 
 536         for (int i = 0; i < passExtent; i++) {
 537             int src = passStart + i * passPeriod;
 538             if (src < sourceOffset) {
 539                 continue;
 540             }
 541             if ((src - sourceOffset) % sourceSubsampling != 0) {
 542                 continue;
 543             }
 544             if (src >= sourceOffset + sourceExtent) {
 545                 break;
 546             }
 547 
 548             int dst = destinationOffset +
 549                     (src - sourceOffset) / sourceSubsampling;
 550             if (dst < dstMin) {
 551                 continue;
 552             }
 553             if (dst > dstMax) {
 554                 break;
 555             }
 556 
 557             if (!gotPixel) {
 558                 firstDst = dst; // Record smallest valid pixel
 559                 gotPixel = true;
 560             } else if (secondDst == -1) {
 561                 secondDst = dst; // Record second smallest valid pixel
 562             }
 563             lastDst = dst; // Record largest valid pixel
 564         }
 565 
 566         vals[offset] = firstDst;
 567 
 568         // If we never saw a valid pixel, set width to 0
 569         if (!gotPixel) {
 570             vals[offset + 2] = 0;
 571         } else {
 572             vals[offset + 2] = lastDst - firstDst + 1;
 573         }
 574 
 575         // The period is given by the difference of any two adjacent pixels
 576         vals[offset + 4] = Math.max(secondDst - firstDst, 1);
 577     }
 578 
 579     /**
 580      * A utility method that computes the exact set of destination
 581      * pixels that will be written during a particular decoding pass.
 582      * The intent is to simplify the work done by readers in combining
 583      * the source region, source subsampling, and destination offset
 584      * information obtained from the <code>ImageReadParam</code> with
 585      * the offsets and periods of a progressive or interlaced decoding
 586      * pass.
 587      *
 588      * @param sourceRegion a <code>Rectangle</code> containing the
 589      * source region being read, offset by the source subsampling
 590      * offsets, and clipped against the source bounds, as returned by
 591      * the <code>getSourceRegion</code> method.
 592      * @param destinationOffset a <code>Point</code> containing the
 593      * coordinates of the upper-left pixel to be written in the
 594      * destination.
 595      * @param dstMinX the smallest X coordinate (inclusive) of the
 596      * destination <code>Raster</code>.
 597      * @param dstMinY the smallest Y coordinate (inclusive) of the
 598      * destination <code>Raster</code>.
 599      * @param dstMaxX the largest X coordinate (inclusive) of the destination
 600      * <code>Raster</code>.
 601      * @param dstMaxY the largest Y coordinate (inclusive) of the destination
 602      * <code>Raster</code>.
 603      * @param sourceXSubsampling the X subsampling factor.
 604      * @param sourceYSubsampling the Y subsampling factor.
 605      * @param passXStart the smallest source X coordinate (inclusive)
 606      * of the current progressive pass.
 607      * @param passYStart the smallest source Y coordinate (inclusive)
 608      * of the current progressive pass.
 609      * @param passWidth the width in pixels of the current progressive
 610      * pass.
 611      * @param passHeight the height in pixels of the current progressive
 612      * pass.
 613      * @param passPeriodX the X period (horizontal spacing between
 614      * pixels) of the current progressive pass.
 615      * @param passPeriodY the Y period (vertical spacing between
 616      * pixels) of the current progressive pass.
 617      *
 618      * @return an array of 6 <code>int</code>s containing the
 619      * destination min X, min Y, width, height, X period and Y period
 620      * of the region that will be updated.
 621      */
 622     public static int[] computeUpdatedPixels(Rectangle sourceRegion,
 623             Point2D destinationOffset,
 624             int dstMinX,
 625             int dstMinY,
 626             int dstMaxX,
 627             int dstMaxY,
 628             int sourceXSubsampling,
 629             int sourceYSubsampling,
 630             int passXStart,
 631             int passYStart,
 632             int passWidth,
 633             int passHeight,
 634             int passPeriodX,
 635             int passPeriodY) {
 636         int[] vals = new int[6];
 637         computeUpdatedPixels(sourceRegion.x, sourceRegion.width,
 638                 (int) (destinationOffset.x + 0.5F),
 639                 dstMinX, dstMaxX, sourceXSubsampling,
 640                 passXStart, passWidth, passPeriodX,
 641                 vals, 0);
 642         computeUpdatedPixels(sourceRegion.y, sourceRegion.height,
 643                 (int) (destinationOffset.y + 0.5F),
 644                 dstMinY, dstMaxY, sourceYSubsampling,
 645                 passYStart, passHeight, passPeriodY,
 646                 vals, 1);
 647         return vals;
 648     }
 649 
 650     public static int[] computeDimensions(int sourceWidth, int sourceHeight,
 651             int maxWidth, int maxHeight, boolean preserveAspectRatio) {
 652         // ensure non-negative dimensions (0 implies default)
 653         int finalWidth = maxWidth < 0 ? 0 : maxWidth;
 654         int finalHeight = maxHeight < 0 ? 0 : maxHeight;
 655 
 656         if(finalWidth == 0 && finalHeight == 0) {
 657             // default to source dimensions
 658             finalWidth = sourceWidth;
 659             finalHeight = sourceHeight;
 660         } else if (finalWidth != sourceWidth || finalHeight != sourceHeight) {
 661             if (preserveAspectRatio) {
 662                 // compute the final dimensions
 663                 if (finalWidth == 0) {
 664                     finalWidth = (int) ((float) sourceWidth * finalHeight / sourceHeight);
 665                 } else if (finalHeight == 0) {
 666                     finalHeight = (int) ((float) sourceHeight * finalWidth / sourceWidth);
 667                 } else {
 668                     float scale = Math.min((float) finalWidth / sourceWidth, (float) finalHeight / sourceHeight);
 669                     finalWidth = (int) (sourceWidth * scale);
 670                     finalHeight = (int) (sourceHeight * scale);
 671                 }
 672             } else {
 673                 // set final dimensions to default if zero
 674                 if (finalHeight == 0) {
 675                     finalHeight = sourceHeight;
 676                 }
 677                 if (finalWidth == 0) {
 678                     finalWidth = sourceWidth;
 679                 }
 680             }
 681 
 682 
 683             // clamp dimensions to positive values
 684             if (finalWidth == 0) {
 685                 finalWidth = 1;
 686             }
 687             if (finalHeight == 0) {
 688                 finalHeight = 1;
 689             }
 690         }
 691 
 692 
 693         return new int[]{finalWidth, finalHeight};
 694     }
 695 //    public static final java.awt.image.BufferedImage getAsBufferedImage(Image prismImage) {
 696 //        java.awt.image.BufferedImage image = null;
 697 //
 698 //        int width = prismImage.getWidth();
 699 //        int height = prismImage.getHeight();
 700 //        int scanlineStride = prismImage.getScanlineStride();
 701 //        byte[] pixels = ((java.nio.ByteBuffer) prismImage.getPixelBuffer()).array();
 702 //        switch (prismImage.getPixelFormat()) {
 703 //            case BYTE_GRAY: {
 704 //                image = new java.awt.image.BufferedImage(width, height,
 705 //                        java.awt.image.BufferedImage.TYPE_BYTE_GRAY);
 706 //                java.awt.image.DataBufferByte db =
 707 //                        (java.awt.image.DataBufferByte) image.getRaster().getDataBuffer();
 708 //                byte[] data = db.getData();
 709 //                System.arraycopy(pixels, 0, data, 0, width * height);
 710 //            }
 711 //            break;
 712 //            case BYTE_RGB: {
 713 //                image = new java.awt.image.BufferedImage(width, height,
 714 //                        java.awt.image.BufferedImage.TYPE_3BYTE_BGR);
 715 //                for (int y = 0; y < height; y++) {
 716 //                    int off = y * scanlineStride;
 717 //                    for (int x = 0; x < width; x++) {
 718 //                        int rgb = ((pixels[off++] & 0xff) << 16) |
 719 //                                ((pixels[off++] & 0xff) << 8) |
 720 //                                (pixels[off++] & 0xff);
 721 //                        image.setRGB(x, y, rgb);
 722 //                    }
 723 //                }
 724 //            }
 725 //            break;
 726 //            case BYTE_RGBA_PRE: {
 727 //                image = new java.awt.image.BufferedImage(width, height,
 728 //                        java.awt.image.BufferedImage.TYPE_4BYTE_ABGR_PRE);
 729 ////                for (int y = 0; y < height; y++) {
 730 ////                    int off = y * scanlineStride;
 731 ////                    for (int x = 0; x < width; x++) {
 732 ////                        int rgb = ((pixels[off++] & 0xff) << 16) |
 733 ////                                ((pixels[off++] & 0xff) << 8) |
 734 ////                                (pixels[off++] & 0xff) |
 735 ////                                ((pixels[off++] & 0xff) << 24);
 736 ////                        image.setRGB(x, y, rgb);
 737 ////                    }
 738 ////                }
 739 //                java.awt.image.DataBufferByte db =
 740 //                        (java.awt.image.DataBufferByte) image.getRaster().getDataBuffer();
 741 //                byte[] data = db.getData();
 742 //                for (int y = 0; y < height; y++) {
 743 //                    int offPrism = y * scanlineStride;
 744 //                    int offImage = y * width * 4;
 745 //                    for (int x = 0; x < width; x++) {
 746 //                        data[offImage++] = pixels[offPrism + 3]; // A
 747 //                        data[offImage++] = pixels[offPrism + 2]; // B
 748 //                        data[offImage++] = pixels[offPrism + 1]; // G
 749 //                        data[offImage++] = pixels[offPrism];     // R
 750 //                        offPrism += 4;
 751 //                    }
 752 //                }
 753 //            }
 754 //            break;
 755 //            default:
 756 //                throw new UnsupportedOperationException("Unsupported test case " +
 757 //                        prismImage.getPixelFormat());
 758 //        }
 759 //
 760 //        return image;
 761 //    }
 762 }