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