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