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