1 /*
   2  * Copyright (c) 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 package sun.java2d.metal;
  26 
  27 import sun.java2d.SunGraphics2D;
  28 import sun.java2d.SurfaceData;
  29 import sun.java2d.loops.CompositeType;
  30 
  31 import java.awt.*;
  32 import java.awt.MultipleGradientPaint.ColorSpaceType;
  33 import java.awt.MultipleGradientPaint.CycleMethod;
  34 import java.awt.image.BufferedImage;
  35 import java.util.HashMap;
  36 import java.util.Map;
  37 
  38 import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_GRAD_SHADER;
  39 import static sun.java2d.pipe.BufferedPaints.MULTI_MAX_FRACTIONS;
  40 
  41 abstract class MTLPaints {
  42 
  43     /**
  44      * Holds all registered implementations, using the corresponding
  45      * SunGraphics2D.PAINT_* constant as the hash key.
  46      */
  47     private static Map<Integer, MTLPaints> impls =
  48         new HashMap<Integer, MTLPaints>(4, 1.0f);
  49 
  50     static {
  51         impls.put(SunGraphics2D.PAINT_GRADIENT, new Gradient());
  52         impls.put(SunGraphics2D.PAINT_LIN_GRADIENT, new LinearGradient());
  53         impls.put(SunGraphics2D.PAINT_RAD_GRADIENT, new RadialGradient());
  54         impls.put(SunGraphics2D.PAINT_TEXTURE, new Texture());
  55     }
  56 
  57     /**
  58      * Attempts to locate an implementation corresponding to the paint state
  59      * of the provided SunGraphics2D object.  If no implementation can be
  60      * found, or if the paint cannot be accelerated under the conditions
  61      * of the SunGraphics2D, this method returns false; otherwise, returns
  62      * true.
  63      */
  64     static boolean isValid(SunGraphics2D sg2d) {
  65         MTLPaints impl = impls.get(sg2d.paintState);
  66         return (impl != null && impl.isPaintValid(sg2d));
  67     }
  68 
  69     /**
  70      * Returns true if this implementation is able to accelerate the
  71      * Paint object associated with, and under the conditions of, the
  72      * provided SunGraphics2D instance; otherwise returns false.
  73      */
  74     abstract boolean isPaintValid(SunGraphics2D sg2d);
  75 
  76 /************************* GradientPaint support ****************************/
  77 
  78     private static class Gradient extends MTLPaints {
  79         private Gradient() {}
  80 
  81         /**
  82          * There are no restrictions for accelerating GradientPaint, so
  83          * this method always returns true.
  84          */
  85         @Override
  86         boolean isPaintValid(SunGraphics2D sg2d) {
  87             return true;
  88         }
  89     }
  90 
  91 /************************** TexturePaint support ****************************/
  92 
  93     private static class Texture extends MTLPaints {
  94         private Texture() {}
  95 
  96         /**
  97          * Returns true if the given TexturePaint instance can be used by the
  98          * accelerated MTLPaints.Texture implementation.  A TexturePaint is
  99          * considered valid if the following conditions are met:
 100          *   - the texture image dimensions are power-of-two (or the
 101          *     GL_ARB_texture_non_power_of_two extension is present)
 102          *   - the texture image can be (or is already) cached in an OpenGL
 103          *     texture object
 104          */
 105         @Override
 106         boolean isPaintValid(SunGraphics2D sg2d) {
 107             TexturePaint paint = (TexturePaint)sg2d.paint;
 108             MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
 109             BufferedImage bi = paint.getImage();
 110 
 111             // see if texture-non-pow2 extension is available
 112             if (!dstData.isTexNonPow2Available()) {
 113                 int imgw = bi.getWidth();
 114                 int imgh = bi.getHeight();
 115 
 116                 // verify that the texture image dimensions are pow2
 117                 if ((imgw & (imgw - 1)) != 0 || (imgh & (imgh - 1)) != 0) {
 118                     return false;
 119                 }
 120             }
 121 
 122             SurfaceData srcData =
 123                 dstData.getSourceSurfaceData(bi,
 124                                              SunGraphics2D.TRANSFORM_ISIDENT,
 125                                              CompositeType.SrcOver, null);
 126             if (!(srcData instanceof MTLSurfaceData)) {
 127                 // REMIND: this is a hack that attempts to cache the system
 128                 //         memory image from the TexturePaint instance into an
 129                 //         OpenGL texture...
 130                 srcData =
 131                     dstData.getSourceSurfaceData(bi,
 132                                                  SunGraphics2D.TRANSFORM_ISIDENT,
 133                                                  CompositeType.SrcOver, null);
 134                 if (!(srcData instanceof MTLSurfaceData)) {
 135                     return false;
 136                 }
 137             }
 138 
 139             // verify that the source surface is actually a texture
 140             MTLSurfaceData oglData = (MTLSurfaceData)srcData;
 141             if (oglData.getType() != MTLSurfaceData.TEXTURE) {
 142                 return false;
 143             }
 144 
 145             return true;
 146         }
 147     }
 148 
 149 /****************** Shared MultipleGradientPaint support ********************/
 150 
 151     private abstract static class MultiGradient extends MTLPaints {
 152         protected MultiGradient() {}
 153 
 154         /**
 155          * Returns true if the given MultipleGradientPaint instance can be
 156          * used by the accelerated MTLPaints.MultiGradient implementation.
 157          * A MultipleGradientPaint is considered valid if the following
 158          * conditions are met:
 159          *   - the number of gradient "stops" is <= MAX_FRACTIONS
 160          *   - the destination has support for fragment shaders
 161          */
 162         @Override
 163         boolean isPaintValid(SunGraphics2D sg2d) {
 164             MultipleGradientPaint paint = (MultipleGradientPaint)sg2d.paint;
 165             // REMIND: ugh, this creates garbage; would be nicer if
 166             // we had a MultipleGradientPaint.getNumStops() method...
 167             if (paint.getFractions().length > MULTI_MAX_FRACTIONS) {
 168                 return false;
 169             }
 170 
 171             MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
 172             MTLGraphicsConfig gc = dstData.getMTLGraphicsConfig();
 173             if (!gc.isCapPresent(CAPS_EXT_GRAD_SHADER)) {
 174                 return false;
 175             }
 176 
 177             return true;
 178         }
 179     }
 180 
 181 /********************** LinearGradientPaint support *************************/
 182 
 183     private static class LinearGradient extends MultiGradient {
 184         private LinearGradient() {}
 185 
 186         @Override
 187         boolean isPaintValid(SunGraphics2D sg2d) {
 188             LinearGradientPaint paint = (LinearGradientPaint)sg2d.paint;
 189 
 190             if (paint.getFractions().length == 2 &&
 191                 paint.getCycleMethod() != CycleMethod.REPEAT &&
 192                 paint.getColorSpace() != ColorSpaceType.LINEAR_RGB)
 193             {
 194                 // we can delegate to the optimized two-color gradient
 195                 // codepath, which does not require fragment shader support
 196                 return true;
 197             }
 198 
 199             return super.isPaintValid(sg2d);
 200         }
 201     }
 202 
 203 /********************** RadialGradientPaint support *************************/
 204 
 205     private static class RadialGradient extends MultiGradient {
 206         private RadialGradient() {}
 207     }
 208 }