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 
initIDs(Class<?> ICM, Class<?> ICMColorData)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 {
initIDs(IndexColorModel.class, ICMColorData.class)70         initIDs(IndexColorModel.class, ICMColorData.class);
71     }
72 
createData(BufferedImage bufImg)73     public static SurfaceData createData(BufferedImage bufImg) {
74         return createData(bufImg, 1, 1);
75     }
76 
createData(BufferedImage bufImg, double scaleX, double scaleY)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 
createData(Raster ras, ColorModel cm)260     public static SurfaceData createData(Raster ras, ColorModel cm) {
261         throw new InternalError("SurfaceData not implemented for Raster/CM");
262     }
263 
createDataIC(BufferedImage bImg, SurfaceType sType, double scaleX, double scaleY)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 
createDataSC(BufferedImage bImg, SurfaceType sType, IndexColorModel icm, double scaleX, double scaleY)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 
createDataBC(BufferedImage bImg, SurfaceType sType, int primaryBank, double scaleX, double scaleY)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 
createDataBP(BufferedImage bImg, SurfaceType sType, double scaleX, double scaleY)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 
getRenderLoops(SunGraphics2D sg2d)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 
getRaster(int x, int y, int w, int h)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      */
initRaster(Object theArray, int offset, int bitoffset, int width, int height, int pixStr, int scanStr, IndexColorModel icm)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 
BufImgSurfaceData(DataBuffer db, BufferedImage bufImg, SurfaceType sType, double scaleX, double scaleY)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 
BufImgSurfaceData(SurfaceType surfaceType, ColorModel cm)390     protected BufImgSurfaceData(SurfaceType surfaceType, ColorModel cm) {
391         super(surfaceType, cm);
392         this.scaleX = 1;
393         this.scaleY = 1;
394     }
395 
initSolidLoops()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];
getSolidLoops(SurfaceType type)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 
getReplacement()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 
getDeviceConfiguration()428     public synchronized GraphicsConfiguration getDeviceConfiguration() {
429         if (graphicsConfig == null) {
430             graphicsConfig = BufferedImageGraphicsConfig
431                     .getConfig(bufImg, scaleX, scaleY);
432         }
433         return graphicsConfig;
434     }
435 
getBounds()436     public java.awt.Rectangle getBounds() {
437         return new Rectangle(bufImg.getWidth(), bufImg.getHeight());
438     }
439 
checkCustomComposite()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      */
getDestination()448     public Object getDestination() {
449         return bufImg;
450     }
451 
452     @Override
getDefaultScaleX()453     public double getDefaultScaleX() {
454         return scaleX;
455     }
456 
457     @Override
getDefaultScaleY()458     public double getDefaultScaleY() {
459         return scaleY;
460     }
461 
462     public static final class ICMColorData {
463         private long pData = 0L;
464 
ICMColorData(long pData)465         private ICMColorData(long pData) {
466             this.pData = pData;
467         }
468     }
469 }
470