1 /*
   2  * Copyright (c) 1999, 2013, 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 sun.awt.image;
  27 
  28 import java.awt.Color;
  29 import java.awt.Rectangle;
  30 import java.awt.GraphicsConfiguration;
  31 import java.awt.image.ColorModel;
  32 import java.awt.image.SampleModel;
  33 import java.awt.image.DirectColorModel;
  34 import java.awt.image.IndexColorModel;
  35 import java.awt.image.Raster;
  36 import java.awt.image.BufferedImage;
  37 import java.awt.image.DataBuffer;
  38 
  39 import sun.java2d.SurfaceData;
  40 import sun.java2d.SunGraphics2D;
  41 import sun.java2d.StateTrackable;
  42 import sun.java2d.StateTrackable.*;
  43 import sun.java2d.StateTracker;
  44 import sun.java2d.loops.SurfaceType;
  45 import sun.java2d.loops.CompositeType;
  46 import sun.java2d.loops.RenderLoops;
  47 
  48 
  49 public class BufImgSurfaceData extends SurfaceData {
  50     BufferedImage bufImg;
  51     private BufferedImageGraphicsConfig graphicsConfig;
  52     RenderLoops solidloops;
  53     private final double scaleX;
  54     private final double scaleY;
  55 
  56     private static native void initIDs(Class<?> ICM, Class<?> ICMColorData);
  57 
  58     private static final int DCM_RGBX_RED_MASK   = 0xff000000;
  59     private static final int DCM_RGBX_GREEN_MASK = 0x00ff0000;
  60     private static final int DCM_RGBX_BLUE_MASK  = 0x0000ff00;
  61     private static final int DCM_555X_RED_MASK = 0xF800;
  62     private static final int DCM_555X_GREEN_MASK = 0x07C0;
  63     private static final int DCM_555X_BLUE_MASK = 0x003E;
  64     private static final int DCM_4444_RED_MASK   = 0x0f00;
  65     private static final int DCM_4444_GREEN_MASK = 0x00f0;
  66     private static final int DCM_4444_BLUE_MASK  = 0x000f;
  67     private static final int DCM_4444_ALPHA_MASK = 0xf000;
  68     private static final int DCM_ARGBBM_ALPHA_MASK = 0x01000000;
  69     private static final int DCM_ARGBBM_RED_MASK   = 0x00ff0000;
  70     private static final int DCM_ARGBBM_GREEN_MASK = 0x0000ff00;
  71     private static final int DCM_ARGBBM_BLUE_MASK  = 0x000000ff;
  72 
  73     static {
  74         initIDs(IndexColorModel.class, ICMColorData.class);
  75     }
  76 
  77     public static SurfaceData createData(BufferedImage bufImg) {
  78         return createData(bufImg, 1, 1);
  79     }
  80 
  81     public static SurfaceData createData(BufferedImage bufImg,
  82                                          double scaleX, double scaleY)
  83     {
  84         if (bufImg == null) {
  85             throw new NullPointerException("BufferedImage cannot be null");
  86         }
  87         SurfaceData sData;
  88         ColorModel cm = bufImg.getColorModel();
  89         int type = bufImg.getType();
  90         // REMIND: Check the image type and pick an appropriate subclass
  91         switch (type) {
  92         case BufferedImage.TYPE_INT_BGR:
  93             sData = createDataIC(bufImg, SurfaceType.IntBgr, scaleX, scaleY);
  94             break;
  95         case BufferedImage.TYPE_INT_RGB:
  96             sData = createDataIC(bufImg, SurfaceType.IntRgb, scaleX, scaleY);
  97             break;
  98         case BufferedImage.TYPE_INT_ARGB:
  99             sData = createDataIC(bufImg, SurfaceType.IntArgb, scaleX, scaleY);
 100             break;
 101         case BufferedImage.TYPE_INT_ARGB_PRE:
 102             sData = createDataIC(bufImg, SurfaceType.IntArgbPre, scaleX, scaleY);
 103             break;
 104         case BufferedImage.TYPE_3BYTE_BGR:
 105             sData = createDataBC(bufImg, SurfaceType.ThreeByteBgr, 2,
 106                                  scaleX, scaleY);
 107             break;
 108         case BufferedImage.TYPE_4BYTE_ABGR:
 109             sData = createDataBC(bufImg, SurfaceType.FourByteAbgr, 3,
 110                                  scaleX, scaleY);
 111             break;
 112         case BufferedImage.TYPE_4BYTE_ABGR_PRE:
 113             sData = createDataBC(bufImg, SurfaceType.FourByteAbgrPre, 3,
 114                                  scaleX, scaleY);
 115             break;
 116         case BufferedImage.TYPE_USHORT_565_RGB:
 117             sData = createDataSC(bufImg, SurfaceType.Ushort565Rgb, null,
 118                                  scaleX, scaleY);
 119             break;
 120         case BufferedImage.TYPE_USHORT_555_RGB:
 121             sData = createDataSC(bufImg, SurfaceType.Ushort555Rgb, null,
 122                                  scaleX, scaleY);
 123             break;
 124         case BufferedImage.TYPE_BYTE_INDEXED:
 125             {
 126                 SurfaceType sType;
 127                 switch (cm.getTransparency()) {
 128                 case OPAQUE:
 129                     if (isOpaqueGray((IndexColorModel)cm)) {
 130                         sType = SurfaceType.Index8Gray;
 131                     } else {
 132                         sType = SurfaceType.ByteIndexedOpaque;
 133                     }
 134                     break;
 135                 case BITMASK:
 136                     sType = SurfaceType.ByteIndexedBm;
 137                     break;
 138                 case TRANSLUCENT:
 139                     sType = SurfaceType.ByteIndexed;
 140                     break;
 141                 default:
 142                     throw new InternalError("Unrecognized transparency");
 143                 }
 144                 sData = createDataBC(bufImg, sType, 0, scaleX, scaleY);
 145             }
 146             break;
 147         case BufferedImage.TYPE_BYTE_GRAY:
 148             sData = createDataBC(bufImg, SurfaceType.ByteGray, 0,
 149                                  scaleX, scaleY);
 150             break;
 151         case BufferedImage.TYPE_USHORT_GRAY:
 152             sData = createDataSC(bufImg, SurfaceType.UshortGray, null,
 153                                  scaleX, scaleY);
 154             break;
 155         case BufferedImage.TYPE_BYTE_BINARY:
 156             {
 157                 SurfaceType sType;
 158                 SampleModel sm = bufImg.getRaster().getSampleModel();
 159                 switch (sm.getSampleSize(0)) {
 160                 case 1:
 161                     sType = SurfaceType.ByteBinary1Bit;
 162                     break;
 163                 case 2:
 164                     sType = SurfaceType.ByteBinary2Bit;
 165                     break;
 166                 case 4:
 167                     sType = SurfaceType.ByteBinary4Bit;
 168                     break;
 169                 default:
 170                     throw new InternalError("Unrecognized pixel size");
 171                 }
 172                 sData = createDataBP(bufImg, sType, scaleX, scaleY);
 173             }
 174             break;
 175         case BufferedImage.TYPE_CUSTOM:
 176         default:
 177             {
 178                 Raster raster = bufImg.getRaster();
 179                 int numBands = raster.getNumBands();
 180                 if (raster instanceof IntegerComponentRaster &&
 181                     raster.getNumDataElements() == 1 &&
 182                     ((IntegerComponentRaster)raster).getPixelStride() == 1)
 183                 {
 184                     SurfaceType sType = SurfaceType.AnyInt;
 185                     if (cm instanceof DirectColorModel) {
 186                         DirectColorModel dcm = (DirectColorModel) cm;
 187                         int aMask = dcm.getAlphaMask();
 188                         int rMask = dcm.getRedMask();
 189                         int gMask = dcm.getGreenMask();
 190                         int bMask = dcm.getBlueMask();
 191                         if (numBands == 3 &&
 192                             aMask == 0 &&
 193                             rMask == DCM_RGBX_RED_MASK &&
 194                             gMask == DCM_RGBX_GREEN_MASK &&
 195                             bMask == DCM_RGBX_BLUE_MASK)
 196                         {
 197                             sType = SurfaceType.IntRgbx;
 198                         } else if (numBands == 4 &&
 199                                    aMask == DCM_ARGBBM_ALPHA_MASK &&
 200                                    rMask == DCM_ARGBBM_RED_MASK &&
 201                                    gMask == DCM_ARGBBM_GREEN_MASK &&
 202                                    bMask == DCM_ARGBBM_BLUE_MASK)
 203                         {
 204                             sType = SurfaceType.IntArgbBm;
 205                         } else {
 206                             sType = SurfaceType.AnyDcm;
 207                         }
 208                     }
 209                     sData = createDataIC(bufImg, sType, scaleX, scaleY);
 210                     break;
 211                 } else if (raster instanceof ShortComponentRaster &&
 212                            raster.getNumDataElements() == 1 &&
 213                            ((ShortComponentRaster)raster).getPixelStride() == 1)
 214                 {
 215                     SurfaceType sType = SurfaceType.AnyShort;
 216                     IndexColorModel icm = null;
 217                     if (cm instanceof DirectColorModel) {
 218                         DirectColorModel dcm = (DirectColorModel) cm;
 219                         int aMask = dcm.getAlphaMask();
 220                         int rMask = dcm.getRedMask();
 221                         int gMask = dcm.getGreenMask();
 222                         int bMask = dcm.getBlueMask();
 223                         if (numBands == 3 &&
 224                             aMask == 0 &&
 225                             rMask == DCM_555X_RED_MASK &&
 226                             gMask == DCM_555X_GREEN_MASK &&
 227                             bMask == DCM_555X_BLUE_MASK)
 228                         {
 229                             sType = SurfaceType.Ushort555Rgbx;
 230                         } else
 231                         if (numBands == 4 &&
 232                             aMask == DCM_4444_ALPHA_MASK &&
 233                             rMask == DCM_4444_RED_MASK &&
 234                             gMask == DCM_4444_GREEN_MASK &&
 235                             bMask == DCM_4444_BLUE_MASK)
 236                         {
 237                             sType = SurfaceType.Ushort4444Argb;
 238                         }
 239                     } else if (cm instanceof IndexColorModel) {
 240                         icm = (IndexColorModel)cm;
 241                         if (icm.getPixelSize() == 12) {
 242                             if (isOpaqueGray(icm)) {
 243                                 sType = SurfaceType.Index12Gray;
 244                             } else {
 245                                 sType = SurfaceType.UshortIndexed;
 246                             }
 247                         } else {
 248                             icm = null;
 249                         }
 250                     }
 251                     sData = createDataSC(bufImg, sType, icm, scaleX, scaleY);
 252                     break;
 253                 }
 254                 sData = new BufImgSurfaceData(raster.getDataBuffer(), bufImg,
 255                                               SurfaceType.Custom,
 256                                               scaleX, scaleY);
 257             }
 258             break;
 259         }
 260         ((BufImgSurfaceData) sData).initSolidLoops();
 261         return sData;
 262     }
 263 
 264     public static SurfaceData createData(Raster ras, ColorModel cm) {
 265         throw new InternalError("SurfaceData not implemented for Raster/CM");
 266     }
 267 
 268     public static SurfaceData createDataIC(BufferedImage bImg,
 269                                            SurfaceType sType,
 270                                            double scaleX,
 271                                            double scaleY)
 272     {
 273         IntegerComponentRaster icRaster =
 274             (IntegerComponentRaster)bImg.getRaster();
 275         BufImgSurfaceData bisd =
 276             new BufImgSurfaceData(icRaster.getDataBuffer(), bImg, sType,
 277                                   scaleX, scaleY);
 278         bisd.initRaster(icRaster.getDataStorage(),
 279                         icRaster.getDataOffset(0) * 4, 0,
 280                         icRaster.getWidth(),
 281                         icRaster.getHeight(),
 282                         icRaster.getPixelStride() * 4,
 283                         icRaster.getScanlineStride() * 4,
 284                         null);
 285         return bisd;
 286     }
 287 
 288     public static SurfaceData createDataSC(BufferedImage bImg,
 289                                            SurfaceType sType,
 290                                            IndexColorModel icm,
 291                                            double scaleX, double scaleY)
 292     {
 293         ShortComponentRaster scRaster =
 294             (ShortComponentRaster)bImg.getRaster();
 295         BufImgSurfaceData bisd =
 296             new BufImgSurfaceData(scRaster.getDataBuffer(), bImg, sType,
 297                                   scaleX, scaleY);
 298         bisd.initRaster(scRaster.getDataStorage(),
 299                         scRaster.getDataOffset(0) * 2, 0,
 300                         scRaster.getWidth(),
 301                         scRaster.getHeight(),
 302                         scRaster.getPixelStride() * 2,
 303                         scRaster.getScanlineStride() * 2,
 304                         icm);
 305         return bisd;
 306     }
 307 
 308     public static SurfaceData createDataBC(BufferedImage bImg,
 309                                            SurfaceType sType,
 310                                            int primaryBank,
 311                                            double scaleX, double scaleY)
 312     {
 313         ByteComponentRaster bcRaster =
 314             (ByteComponentRaster)bImg.getRaster();
 315         BufImgSurfaceData bisd =
 316             new BufImgSurfaceData(bcRaster.getDataBuffer(), bImg, sType,
 317                                   scaleX, scaleY);
 318         ColorModel cm = bImg.getColorModel();
 319         IndexColorModel icm = ((cm instanceof IndexColorModel)
 320                                ? (IndexColorModel) cm
 321                                : null);
 322         bisd.initRaster(bcRaster.getDataStorage(),
 323                         bcRaster.getDataOffset(primaryBank), 0,
 324                         bcRaster.getWidth(),
 325                         bcRaster.getHeight(),
 326                         bcRaster.getPixelStride(),
 327                         bcRaster.getScanlineStride(),
 328                         icm);
 329         return bisd;
 330     }
 331 
 332     public static SurfaceData createDataBP(BufferedImage bImg,
 333                                            SurfaceType sType,
 334                                            double scaleX, double scaleY)
 335     {
 336         BytePackedRaster bpRaster =
 337             (BytePackedRaster)bImg.getRaster();
 338         BufImgSurfaceData bisd =
 339             new BufImgSurfaceData(bpRaster.getDataBuffer(), bImg, sType,
 340                                   scaleX, scaleY);
 341         ColorModel cm = bImg.getColorModel();
 342         IndexColorModel icm = ((cm instanceof IndexColorModel)
 343                                ? (IndexColorModel) cm
 344                                : null);
 345         bisd.initRaster(bpRaster.getDataStorage(),
 346                         bpRaster.getDataBitOffset() / 8,
 347                         bpRaster.getDataBitOffset() & 7,
 348                         bpRaster.getWidth(),
 349                         bpRaster.getHeight(),
 350                         0,
 351                         bpRaster.getScanlineStride(),
 352                         icm);
 353         return bisd;
 354     }
 355 
 356     public RenderLoops getRenderLoops(SunGraphics2D sg2d) {
 357         if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
 358             sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY)
 359         {
 360             return solidloops;
 361         }
 362         return super.getRenderLoops(sg2d);
 363     }
 364 
 365     public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
 366         return bufImg.getRaster();
 367     }
 368 
 369     /**
 370      * Initializes the native Ops pointer.
 371      */
 372     protected native void initRaster(Object theArray,
 373                                      int offset,
 374                                      int bitoffset,
 375                                      int width,
 376                                      int height,
 377                                      int pixStr,
 378                                      int scanStr,
 379                                      IndexColorModel icm);
 380 
 381     public BufImgSurfaceData(DataBuffer db,
 382                              BufferedImage bufImg,
 383                              SurfaceType sType,
 384                              double scaleX,
 385                              double scaleY)
 386     {
 387         super(SunWritableRaster.stealTrackable(db),
 388               sType, bufImg.getColorModel());
 389         this.bufImg = bufImg;
 390         this.scaleX = scaleX;
 391         this.scaleY = scaleY;
 392     }
 393 
 394     protected BufImgSurfaceData(SurfaceType surfaceType, ColorModel cm) {
 395         super(surfaceType, cm);
 396         this.scaleX = 1;
 397         this.scaleY = 1;
 398     }
 399 
 400     public void initSolidLoops() {
 401         this.solidloops = getSolidLoops(getSurfaceType());
 402     }
 403 
 404     private static final int CACHE_SIZE = 5;
 405     private static RenderLoops loopcache[] = new RenderLoops[CACHE_SIZE];
 406     private static SurfaceType typecache[] = new SurfaceType[CACHE_SIZE];
 407     public static synchronized RenderLoops getSolidLoops(SurfaceType type) {
 408         for (int i = CACHE_SIZE - 1; i >= 0; i--) {
 409             SurfaceType t = typecache[i];
 410             if (t == type) {
 411                 return loopcache[i];
 412             } else if (t == null) {
 413                 break;
 414             }
 415         }
 416         RenderLoops l = makeRenderLoops(SurfaceType.OpaqueColor,
 417                                         CompositeType.SrcNoEa,
 418                                         type);
 419         System.arraycopy(loopcache, 1, loopcache, 0, CACHE_SIZE-1);
 420         System.arraycopy(typecache, 1, typecache, 0, CACHE_SIZE-1);
 421         loopcache[CACHE_SIZE - 1] = l;
 422         typecache[CACHE_SIZE - 1] = type;
 423         return l;
 424     }
 425 
 426     public SurfaceData getReplacement() {
 427         // BufImgSurfaceData objects should never lose their contents,
 428         // so this method should never be called.
 429         return restoreContents(bufImg);
 430     }
 431 
 432     public synchronized GraphicsConfiguration getDeviceConfiguration() {
 433         if (graphicsConfig == null) {
 434             graphicsConfig = BufferedImageGraphicsConfig
 435                     .getConfig(bufImg, scaleX, scaleY);
 436         }
 437         return graphicsConfig;
 438     }
 439 
 440     public java.awt.Rectangle getBounds() {
 441         return new Rectangle(bufImg.getWidth(), bufImg.getHeight());
 442     }
 443 
 444     protected void checkCustomComposite() {
 445         // BufferedImages always allow Custom Composite objects since
 446         // their pixels are immediately retrievable anyway.
 447     }
 448 
 449     private static native void freeNativeICMData(long pData);
 450 
 451     /**
 452      * Returns destination Image associated with this SurfaceData.
 453      */
 454     public Object getDestination() {
 455         return bufImg;
 456     }
 457 
 458     @Override
 459     public double getDefaultScaleX() {
 460         return scaleX;
 461     }
 462 
 463     @Override
 464     public double getDefaultScaleY() {
 465         return scaleY;
 466     }
 467 
 468     public static final class ICMColorData {
 469         private long pData = 0L;
 470 
 471         private ICMColorData(long pData) {
 472             this.pData = pData;
 473         }
 474 
 475         public void finalize() {
 476             if (pData != 0L) {
 477                 BufImgSurfaceData.freeNativeICMData(pData);
 478                 pData = 0L;
 479             }
 480         }
 481     }
 482 }