1 /*
2  * Copyright (c) 1997, 2019, 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;
27 
28 import java.awt.AWTException;
29 import java.awt.BufferCapabilities;
30 import java.awt.Component;
31 import java.awt.GraphicsConfiguration;
32 import java.awt.GraphicsDevice;
33 import java.awt.Image;
34 import java.awt.ImageCapabilities;
35 import java.awt.Rectangle;
36 import java.awt.Toolkit;
37 import java.awt.Transparency;
38 import java.awt.color.ColorSpace;
39 import java.awt.geom.AffineTransform;
40 import java.awt.image.ColorModel;
41 import java.awt.image.ComponentColorModel;
42 import java.awt.image.DataBuffer;
43 import java.awt.image.DirectColorModel;
44 import java.awt.image.VolatileImage;
45 import java.awt.image.WritableRaster;
46 
47 import sun.awt.image.OffScreenImage;
48 import sun.awt.image.SunVolatileImage;
49 import sun.awt.image.SurfaceManager;
50 import sun.java2d.Disposer;
51 import sun.java2d.DisposerRecord;
52 import sun.java2d.SurfaceData;
53 import sun.java2d.loops.CompositeType;
54 import sun.java2d.loops.RenderLoops;
55 import sun.java2d.loops.SurfaceType;
56 import sun.java2d.pipe.Region;
57 import sun.java2d.x11.X11SurfaceData;
58 
59 /**
60  * This is an implementation of a GraphicsConfiguration object for a
61  * single X11 visual.
62  *
63  * @see java.awt.GraphicsEnvironment
64  * @see GraphicsDevice
65  */
66 public class X11GraphicsConfig extends GraphicsConfiguration
67     implements SurfaceManager.ProxiedGraphicsConfig
68 {
69     private final X11GraphicsDevice device;
70     protected int visual;
71     int depth;
72     int colormap;
73     ColorModel colorModel;
74     long aData;
75     boolean doubleBuffer;
76     private Object disposerReferent = new Object();
77     private BufferCapabilities bufferCaps;
78     private static ImageCapabilities imageCaps =
79         new ImageCapabilities(X11SurfaceData.isAccelerationEnabled());
80 
81     // will be set on native level from init()
82     protected int bitsPerPixel;
83 
84     protected SurfaceType surfaceType;
85 
86     public RenderLoops solidloops;
87 
getConfig(X11GraphicsDevice device, int visualnum, int depth, int colormap, boolean doubleBuffer)88     public static X11GraphicsConfig getConfig(X11GraphicsDevice device,
89                                               int visualnum, int depth,
90                                               int colormap,
91                                               boolean doubleBuffer)
92     {
93         return new X11GraphicsConfig(device, visualnum, depth, colormap, doubleBuffer);
94     }
95 
96     /*
97      * Note this method is currently here for backward compatibility
98      * as this was the method used in jdk 1.2 beta4 to create the
99      * X11GraphicsConfig objects. Java3D code had called this method
100      * explicitly so without this, if a user tries to use JDK1.2 fcs
101      * with Java3D beta1, a NoSuchMethod execption is thrown and
102      * the program exits. REMOVE this method after Java3D fcs is
103      * released!
104      */
getConfig(X11GraphicsDevice device, int visualnum, int depth, int colormap, int type)105     public static X11GraphicsConfig getConfig(X11GraphicsDevice device,
106                                               int visualnum, int depth,
107                                               int colormap, int type)
108     {
109         return new X11GraphicsConfig(device, visualnum, depth, colormap, false);
110     }
111 
getNumColors()112     private native int getNumColors();
init(int visualNum, int screen)113     private native void init(int visualNum, int screen);
makeColorModel()114     private native ColorModel makeColorModel();
115 
X11GraphicsConfig(X11GraphicsDevice device, int visualnum, int depth, int colormap, boolean doubleBuffer)116     protected X11GraphicsConfig(X11GraphicsDevice device,
117                                 int visualnum, int depth,
118                                 int colormap, boolean doubleBuffer)
119     {
120         this.device = device;
121         this.visual = visualnum;
122         this.doubleBuffer = doubleBuffer;
123         this.depth = depth;
124         this.colormap = colormap;
125         init (visualnum, device.getScreen());
126 
127         // add a record to the Disposer so that we destroy the native
128         // AwtGraphicsConfigData when this object goes away (i.e. after a
129         // display change event)
130         long x11CfgData = getAData();
131         Disposer.addRecord(disposerReferent,
132                            new X11GCDisposerRecord(x11CfgData));
133     }
134 
135     /**
136      * Return the graphics device associated with this configuration.
137      */
138     @Override
getDevice()139     public X11GraphicsDevice getDevice() {
140         return device;
141     }
142 
143     /**
144      * Returns the visual id associated with this configuration.
145      */
getVisual()146     public int getVisual () {
147         return visual;
148     }
149 
150 
151     /**
152      * Returns the depth associated with this configuration.
153      */
getDepth()154     public int getDepth () {
155         return depth;
156     }
157 
158     /**
159      * Returns the colormap associated with this configuration.
160      */
getColormap()161     public int getColormap () {
162         return colormap;
163     }
164 
165     /**
166      * Returns a number of bits allocated per pixel
167      * (might be different from depth)
168      */
getBitsPerPixel()169     public int getBitsPerPixel() {
170         return bitsPerPixel;
171     }
172 
getSurfaceType()173     public synchronized SurfaceType getSurfaceType() {
174         if (surfaceType != null) {
175             return surfaceType;
176         }
177 
178         surfaceType = X11SurfaceData.getSurfaceType(this, Transparency.OPAQUE);
179         return surfaceType;
180     }
181 
182     @Override
getProxyKey()183     public Object getProxyKey() {
184         return device.getProxyKeyFor(getSurfaceType());
185     }
186 
187     /**
188      * Return the RenderLoops this type of destination uses for
189      * solid fills and strokes.
190      */
getSolidLoops(SurfaceType stype)191     public synchronized RenderLoops getSolidLoops(SurfaceType stype) {
192         if (solidloops == null) {
193             solidloops = SurfaceData.makeRenderLoops(SurfaceType.OpaqueColor,
194                                                      CompositeType.SrcNoEa,
195                                                      stype);
196         }
197         return solidloops;
198     }
199 
200     /**
201      * Returns the color model associated with this configuration.
202      */
203     @Override
getColorModel()204     public synchronized ColorModel getColorModel() {
205         if (colorModel == null)  {
206             // Force SystemColors to be resolved before we create the CM
207             java.awt.SystemColor.window.getRGB();
208             // This method, makeColorModel(), can return null if the
209             // toolkit is not initialized yet.
210             // The toolkit will then call back to this routine after it
211             // is initialized and makeColorModel() should return a non-null
212             // colorModel.
213             colorModel = makeColorModel();
214             if (colorModel == null)
215                 colorModel = Toolkit.getDefaultToolkit ().getColorModel ();
216         }
217 
218         return colorModel;
219     }
220 
221     /**
222      * Returns the color model associated with this configuration that
223      * supports the specified transparency.
224      */
225     @Override
getColorModel(int transparency)226     public ColorModel getColorModel(int transparency) {
227         switch (transparency) {
228         case Transparency.OPAQUE:
229             return getColorModel();
230         case Transparency.BITMASK:
231             return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
232         case Transparency.TRANSLUCENT:
233             return ColorModel.getRGBdefault();
234         default:
235             return null;
236         }
237     }
238 
createDCM32(int rMask, int gMask, int bMask, int aMask, boolean aPre)239     public static DirectColorModel createDCM32(int rMask, int gMask, int bMask,
240                                                int aMask, boolean aPre) {
241         return new DirectColorModel(
242             ColorSpace.getInstance(ColorSpace.CS_sRGB),
243             32, rMask, gMask, bMask, aMask, aPre, DataBuffer.TYPE_INT);
244     }
245 
createABGRCCM()246     public static ComponentColorModel createABGRCCM() {
247         ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
248         int[] nBits = {8, 8, 8, 8};
249         int[] bOffs = {3, 2, 1, 0};
250         return new ComponentColorModel(cs, nBits, true, true,
251                                        Transparency.TRANSLUCENT,
252                                        DataBuffer.TYPE_BYTE);
253     }
254 
255     /**
256      * Returns the default Transform for this configuration.  This
257      * Transform is typically the Identity transform for most normal
258      * screens.  Device coordinates for screen and printer devices will
259      * have the origin in the upper left-hand corner of the target region of
260      * the device, with X coordinates
261      * increasing to the right and Y coordinates increasing downwards.
262      * For image buffers, this Transform will be the Identity transform.
263      */
264     @Override
getDefaultTransform()265     public AffineTransform getDefaultTransform() {
266         double scale = getScale();
267         return AffineTransform.getScaleInstance(scale, scale);
268     }
269 
getScale()270     public int getScale() {
271         return getDevice().getScaleFactor();
272     }
273 
scaleUp(int x)274     public int scaleUp(int x) {
275         return Region.clipRound(x * (double)getScale());
276     }
277 
scaleDown(int x)278     public int scaleDown(int x) {
279         return Region.clipRound(x / (double)getScale());
280     }
281 
282     /**
283      *
284      * Returns a Transform that can be composed with the default Transform
285      * of a Graphics2D so that 72 units in user space will equal 1 inch
286      * in device space.
287      * Given a Graphics2D, g, one can reset the transformation to create
288      * such a mapping by using the following pseudocode:
289      * <pre>
290      *      GraphicsConfiguration gc = g.getGraphicsConfiguration();
291      *
292      *      g.setTransform(gc.getDefaultTransform());
293      *      g.transform(gc.getNormalizingTransform());
294      * </pre>
295      * Note that sometimes this Transform will be identity (e.g. for
296      * printers or metafile output) and that this Transform is only
297      * as accurate as the information supplied by the underlying system.
298      * For image buffers, this Transform will be the Identity transform,
299      * since there is no valid distance measurement.
300      */
301     @Override
getNormalizingTransform()302     public AffineTransform getNormalizingTransform() {
303         double xscale = getXResolution(device.getScreen()) / 72.0;
304         double yscale = getYResolution(device.getScreen()) / 72.0;
305         return new AffineTransform(xscale, 0.0, 0.0, yscale, 0.0, 0.0);
306     }
307 
getXResolution(int screen)308     private native double getXResolution(int screen);
getYResolution(int screen)309     private native double getYResolution(int screen);
310 
getAData()311     public long getAData() {
312         return aData;
313     }
314 
toString()315     public String toString() {
316         return ("X11GraphicsConfig[dev="+device+
317                 ",vis=0x"+Integer.toHexString(visual)+
318                 "]");
319     }
320 
321     /*
322      * Initialize JNI field and method IDs for fields that may be
323      *  accessed from C.
324      */
initIDs()325     private static native void initIDs();
326 
327     static {
initIDs()328         initIDs ();
329     }
330 
331     @Override
getBounds()332     public Rectangle getBounds() {
333         Rectangle rect = pGetBounds(device.getScreen());
334         if (getScale() != 1) {
335             rect.x = scaleDown(rect.x);
336             rect.y = scaleDown(rect.y);
337             rect.width = scaleDown(rect.width);
338             rect.height = scaleDown(rect.height);
339         }
340         return rect;
341     }
342 
pGetBounds(int screenNum)343     private native Rectangle pGetBounds(int screenNum);
344 
345     private static class XDBECapabilities extends BufferCapabilities {
XDBECapabilities()346         public XDBECapabilities() {
347             super(imageCaps, imageCaps, FlipContents.UNDEFINED);
348         }
349     }
350 
351     @Override
getBufferCapabilities()352     public BufferCapabilities getBufferCapabilities() {
353         if (bufferCaps == null) {
354             if (doubleBuffer) {
355                 bufferCaps = new XDBECapabilities();
356             } else {
357                 bufferCaps = super.getBufferCapabilities();
358             }
359         }
360         return bufferCaps;
361     }
362 
363     @Override
getImageCapabilities()364     public ImageCapabilities getImageCapabilities() {
365         return imageCaps;
366     }
367 
isDoubleBuffered()368     public boolean isDoubleBuffered() {
369         return doubleBuffer;
370     }
371 
dispose(long x11ConfigData)372     private static native void dispose(long x11ConfigData);
373 
374     private static class X11GCDisposerRecord implements DisposerRecord {
375         private long x11ConfigData;
X11GCDisposerRecord(long x11CfgData)376         public X11GCDisposerRecord(long x11CfgData) {
377             this.x11ConfigData = x11CfgData;
378         }
379         @Override
dispose()380         public synchronized void dispose() {
381             if (x11ConfigData != 0L) {
382                 X11GraphicsConfig.dispose(x11ConfigData);
383                 x11ConfigData = 0L;
384             }
385         }
386     }
387 
388     /**
389      * The following methods are invoked from {M,X}Toolkit.java and
390      * X11ComponentPeer.java rather than having the X11-dependent
391      * implementations hardcoded in those classes.  This way the appropriate
392      * actions are taken based on the peer's GraphicsConfig, whether it is
393      * an X11GraphicsConfig or a GLXGraphicsConfig.
394      */
395 
396     /**
397      * Creates a new SurfaceData that will be associated with the given
398      * X11ComponentPeer.
399      */
createSurfaceData(X11ComponentPeer peer)400     public SurfaceData createSurfaceData(X11ComponentPeer peer) {
401         return X11SurfaceData.createData(peer);
402     }
403 
404     /**
405      * Creates a new hidden-acceleration image of the given width and height
406      * that is associated with the target Component.
407      */
createAcceleratedImage(Component target, int width, int height)408     public Image createAcceleratedImage(Component target,
409                                         int width, int height)
410     {
411         // As of 1.7 we no longer create pmoffscreens here...
412         ColorModel model = getColorModel(Transparency.OPAQUE);
413         WritableRaster wr =
414             model.createCompatibleWritableRaster(width, height);
415         return new OffScreenImage(target, model, wr,
416                                   model.isAlphaPremultiplied());
417     }
418 
419     /**
420      * The following methods correspond to the multibuffering methods in
421      * X11ComponentPeer.java...
422      */
423 
createBackBuffer(long window, int swapAction)424     private native long createBackBuffer(long window, int swapAction);
swapBuffers(long window, int swapAction)425     private native void swapBuffers(long window, int swapAction);
426 
427     /**
428      * Attempts to create an XDBE-based backbuffer for the given peer.  If
429      * the requested configuration is not natively supported, an AWTException
430      * is thrown.  Otherwise, if the backbuffer creation is successful, a
431      * handle to the native backbuffer is returned.
432      */
createBackBuffer(X11ComponentPeer peer, int numBuffers, BufferCapabilities caps)433     public long createBackBuffer(X11ComponentPeer peer,
434                                  int numBuffers, BufferCapabilities caps)
435         throws AWTException
436     {
437         if (!X11GraphicsDevice.isDBESupported()) {
438             throw new AWTException("Page flipping is not supported");
439         }
440         if (numBuffers > 2) {
441             throw new AWTException(
442                 "Only double or single buffering is supported");
443         }
444         BufferCapabilities configCaps = getBufferCapabilities();
445         if (!configCaps.isPageFlipping()) {
446             throw new AWTException("Page flipping is not supported");
447         }
448 
449         long window = peer.getContentWindow();
450         int swapAction = getSwapAction(caps.getFlipContents());
451 
452         return createBackBuffer(window, swapAction);
453     }
454 
455     /**
456      * Destroys the backbuffer object represented by the given handle value.
457      */
destroyBackBuffer(long backBuffer)458     public native void destroyBackBuffer(long backBuffer);
459 
460     /**
461      * Creates a VolatileImage that essentially wraps the target Component's
462      * backbuffer, using the provided backbuffer handle.
463      */
createBackBufferImage(Component target, long backBuffer)464     public VolatileImage createBackBufferImage(Component target,
465                                                long backBuffer)
466     {
467         // it is possible for the component to have size 0x0, adjust it to
468         // be at least 1x1 to avoid IAE
469         int w = Math.max(1, target.getWidth());
470         int h = Math.max(1, target.getHeight());
471         return new SunVolatileImage(target,
472                                     w, h,
473                                     Long.valueOf(backBuffer));
474     }
475 
476     /**
477      * Performs the native XDBE flip operation for the given target Component.
478      */
flip(X11ComponentPeer peer, Component target, VolatileImage xBackBuffer, int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction)479     public void flip(X11ComponentPeer peer,
480                      Component target, VolatileImage xBackBuffer,
481                      int x1, int y1, int x2, int y2,
482                      BufferCapabilities.FlipContents flipAction)
483     {
484         long window = peer.getContentWindow();
485         int swapAction = getSwapAction(flipAction);
486         swapBuffers(window, swapAction);
487     }
488 
489     /**
490      * Maps the given FlipContents constant to the associated XDBE swap
491      * action constant.
492      */
getSwapAction( BufferCapabilities.FlipContents flipAction)493     private static int getSwapAction(
494         BufferCapabilities.FlipContents flipAction) {
495         if (flipAction == BufferCapabilities.FlipContents.BACKGROUND) {
496             return 0x01;
497         } else if (flipAction == BufferCapabilities.FlipContents.PRIOR) {
498             return 0x02;
499         } else if (flipAction == BufferCapabilities.FlipContents.COPIED) {
500             return 0x03;
501         } else {
502             return 0x00; // UNDEFINED
503         }
504     }
505 
506     @Override
isTranslucencyCapable()507     public boolean isTranslucencyCapable() {
508         return isTranslucencyCapable(getAData());
509     }
510 
isTranslucencyCapable(long x11ConfigData)511     private native boolean isTranslucencyCapable(long x11ConfigData);
512 }
513