1 /*
   2  * Copyright (c) 2007, 2013, 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.java2d.pipe;
  27 
  28 import java.awt.Color;
  29 import java.awt.GradientPaint;
  30 import java.awt.LinearGradientPaint;
  31 import java.awt.MultipleGradientPaint;
  32 import java.awt.MultipleGradientPaint.ColorSpaceType;
  33 import java.awt.MultipleGradientPaint.CycleMethod;
  34 import java.awt.Paint;
  35 import java.awt.RadialGradientPaint;
  36 import java.awt.TexturePaint;
  37 import java.awt.geom.AffineTransform;
  38 import java.awt.geom.Point2D;
  39 import java.awt.geom.Rectangle2D;
  40 import java.awt.image.AffineTransformOp;
  41 import java.awt.image.BufferedImage;
  42 import sun.awt.image.PixelConverter;
  43 import sun.java2d.SunGraphics2D;
  44 import sun.java2d.SurfaceData;
  45 import sun.java2d.loops.CompositeType;
  46 import sun.java2d.loops.SurfaceType;
  47 import static sun.java2d.pipe.BufferedOpCodes.*;
  48 
  49 import java.lang.annotation.Native;
  50 
  51 public class BufferedPaints {
  52 
  53     static void setPaint(RenderQueue rq, SunGraphics2D sg2d,
  54                          Paint paint, int ctxflags)
  55     {
  56         if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
  57             setColor(rq, sg2d.pixel);
  58         } else {
  59             boolean useMask = (ctxflags & BufferedContext.USE_MASK) != 0;
  60             switch (sg2d.paintState) {
  61             case SunGraphics2D.PAINT_GRADIENT:
  62                 setGradientPaint(rq, sg2d,
  63                                  (GradientPaint)paint, useMask);
  64                 break;
  65             case SunGraphics2D.PAINT_LIN_GRADIENT:
  66                 setLinearGradientPaint(rq, sg2d,
  67                                        (LinearGradientPaint)paint, useMask);
  68                 break;
  69             case SunGraphics2D.PAINT_RAD_GRADIENT:
  70                 setRadialGradientPaint(rq, sg2d,
  71                                        (RadialGradientPaint)paint, useMask);
  72                 break;
  73             case SunGraphics2D.PAINT_TEXTURE:
  74                 setTexturePaint(rq, sg2d,
  75                                 (TexturePaint)paint, useMask);
  76                 break;
  77             default:
  78                 break;
  79             }
  80         }
  81     }
  82 
  83     static void resetPaint(RenderQueue rq) {
  84         // assert rq.lock.isHeldByCurrentThread();
  85         rq.ensureCapacity(4);
  86         RenderBuffer buf = rq.getBuffer();
  87         buf.putInt(RESET_PAINT);
  88     }
  89 
  90 /****************************** Color support *******************************/
  91 
  92     private static void setColor(RenderQueue rq, int pixel) {
  93         // assert rq.lock.isHeldByCurrentThread();
  94         rq.ensureCapacity(8);
  95         RenderBuffer buf = rq.getBuffer();
  96         buf.putInt(SET_COLOR);
  97         buf.putInt(pixel);
  98     }
  99 
 100 /************************* GradientPaint support ****************************/
 101 
 102     /**
 103      * Note: This code is factored out into a separate static method
 104      * so that it can be shared by both the Gradient and LinearGradient
 105      * implementations.  LinearGradient uses this code (for the
 106      * two-color sRGB case only) because it can be much faster than the
 107      * equivalent implementation that uses fragment shaders.
 108      *
 109      * We use OpenGL's texture coordinate generator to automatically
 110      * apply a smooth gradient (either cyclic or acyclic) to the geometry
 111      * being rendered.  This technique is almost identical to the one
 112      * described in the comments for BufferedPaints.setTexturePaint(),
 113      * except the calculations take place in one dimension instead of two.
 114      * Instead of an anchor rectangle in the TexturePaint case, we use
 115      * the vector between the two GradientPaint end points in our
 116      * calculations.  The generator uses a single plane equation that
 117      * takes the (x,y) location (in device space) of the fragment being
 118      * rendered to calculate a (u) texture coordinate for that fragment:
 119      *     u = Ax + By + Cz + Dw
 120      *
 121      * The gradient renderer uses a two-pixel 1D texture where the first
 122      * pixel contains the first GradientPaint color, and the second pixel
 123      * contains the second GradientPaint color.  (Note that we use the
 124      * GL_CLAMP_TO_EDGE wrapping mode for acyclic gradients so that we
 125      * clamp the colors properly at the extremes.)  The following diagram
 126      * attempts to show the layout of the texture containing the two
 127      * GradientPaint colors (C1 and C2):
 128      *
 129      *                        +-----------------+
 130      *                        |   C1   |   C2   |
 131      *                        |        |        |
 132      *                        +-----------------+
 133      *                      u=0  .25  .5   .75  1
 134      *
 135      * We calculate our plane equation constants (A,B,D) such that u=0.25
 136      * corresponds to the first GradientPaint end point in user space and
 137      * u=0.75 corresponds to the second end point.  This is somewhat
 138      * non-obvious, but since the gradient colors are generated by
 139      * interpolating between C1 and C2, we want the pure color at the
 140      * end points, and we will get the pure color only when u correlates
 141      * to the center of a texel.  The following chart shows the expected
 142      * color for some sample values of u (where C' is the color halfway
 143      * between C1 and C2):
 144      *
 145      *       u value      acyclic (GL_CLAMP)      cyclic (GL_REPEAT)
 146      *       -------      ------------------      ------------------
 147      *        -0.25              C1                       C2
 148      *         0.0               C1                       C'
 149      *         0.25              C1                       C1
 150      *         0.5               C'                       C'
 151      *         0.75              C2                       C2
 152      *         1.0               C2                       C'
 153      *         1.25              C2                       C1
 154      *
 155      * Original inspiration for this technique came from UMD's Agile2D
 156      * project (GradientManager.java).
 157      */
 158     private static void setGradientPaint(RenderQueue rq, AffineTransform at,
 159                                          Color c1, Color c2,
 160                                          Point2D pt1, Point2D pt2,
 161                                          boolean isCyclic, boolean useMask)
 162     {
 163         // convert gradient colors to IntArgbPre format
 164         PixelConverter pc = PixelConverter.ArgbPre.instance;
 165         int pixel1 = pc.rgbToPixel(c1.getRGB(), null);
 166         int pixel2 = pc.rgbToPixel(c2.getRGB(), null);
 167 
 168         // calculate plane equation constants
 169         double x = pt1.getX();
 170         double y = pt1.getY();
 171         at.translate(x, y);
 172         // now gradient point 1 is at the origin
 173         x = pt2.getX() - x;
 174         y = pt2.getY() - y;
 175         double len = Math.sqrt(x * x + y * y);
 176         at.rotate(x, y);
 177         // now gradient point 2 is on the positive x-axis
 178         at.scale(2*len, 1);
 179         // now gradient point 2 is at (0.5, 0)
 180         at.translate(-0.25, 0);
 181         // now gradient point 1 is at (0.25, 0), point 2 is at (0.75, 0)
 182 
 183         double p0, p1, p3;
 184         try {
 185             at.invert();
 186             p0 = at.getScaleX();
 187             p1 = at.getShearX();
 188             p3 = at.getTranslateX();
 189         } catch (java.awt.geom.NoninvertibleTransformException e) {
 190             p0 = p1 = p3 = 0.0;
 191         }
 192 
 193         // assert rq.lock.isHeldByCurrentThread();
 194         rq.ensureCapacityAndAlignment(44, 12);
 195         RenderBuffer buf = rq.getBuffer();
 196         buf.putInt(SET_GRADIENT_PAINT);
 197         buf.putInt(useMask ? 1 : 0);
 198         buf.putInt(isCyclic ? 1 : 0);
 199         buf.putDouble(p0).putDouble(p1).putDouble(p3);
 200         buf.putInt(pixel1).putInt(pixel2);
 201     }
 202 
 203     private static void setGradientPaint(RenderQueue rq,
 204                                          SunGraphics2D sg2d,
 205                                          GradientPaint paint,
 206                                          boolean useMask)
 207     {
 208         setGradientPaint(rq, (AffineTransform)sg2d.transform.clone(),
 209                          paint.getColor1(), paint.getColor2(),
 210                          paint.getPoint1(), paint.getPoint2(),
 211                          paint.isCyclic(), useMask);
 212     }
 213 
 214 /************************** TexturePaint support ****************************/
 215 
 216     /**
 217      * We use OpenGL's texture coordinate generator to automatically
 218      * map the TexturePaint image to the geometry being rendered.  The
 219      * generator uses two separate plane equations that take the (x,y)
 220      * location (in device space) of the fragment being rendered to
 221      * calculate (u,v) texture coordinates for that fragment:
 222      *     u = Ax + By + Cz + Dw
 223      *     v = Ex + Fy + Gz + Hw
 224      *
 225      * Since we use a 2D orthographic projection, we can assume that z=0
 226      * and w=1 for any fragment.  So we need to calculate appropriate
 227      * values for the plane equation constants (A,B,D) and (E,F,H) such
 228      * that {u,v}=0 for the top-left of the TexturePaint's anchor
 229      * rectangle and {u,v}=1 for the bottom-right of the anchor rectangle.
 230      * We can easily make the texture image repeat for {u,v} values
 231      * outside the range [0,1] by specifying the GL_REPEAT texture wrap
 232      * mode.
 233      *
 234      * Calculating the plane equation constants is surprisingly simple.
 235      * We can think of it as an inverse matrix operation that takes
 236      * device space coordinates and transforms them into user space
 237      * coordinates that correspond to a location relative to the anchor
 238      * rectangle.  First, we translate and scale the current user space
 239      * transform by applying the anchor rectangle bounds.  We then take
 240      * the inverse of this affine transform.  The rows of the resulting
 241      * inverse matrix correlate nicely to the plane equation constants
 242      * we were seeking.
 243      */
 244     private static void setTexturePaint(RenderQueue rq,
 245                                         SunGraphics2D sg2d,
 246                                         TexturePaint paint,
 247                                         boolean useMask)
 248     {
 249         BufferedImage bi = paint.getImage();
 250         SurfaceData dstData = sg2d.surfaceData;
 251         SurfaceData srcData =
 252             dstData.getSourceSurfaceData(bi, SunGraphics2D.TRANSFORM_ISIDENT,
 253                                          CompositeType.SrcOver, null);
 254         boolean filter =
 255             (sg2d.interpolationType !=
 256              AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
 257 
 258         // calculate plane equation constants
 259         AffineTransform at = (AffineTransform)sg2d.transform.clone();
 260         Rectangle2D anchor = paint.getAnchorRect();
 261         at.translate(anchor.getX(), anchor.getY());
 262         at.scale(anchor.getWidth(), anchor.getHeight());
 263 
 264         double xp0, xp1, xp3, yp0, yp1, yp3;
 265         try {
 266             at.invert();
 267             xp0 = at.getScaleX();
 268             xp1 = at.getShearX();
 269             xp3 = at.getTranslateX();
 270             yp0 = at.getShearY();
 271             yp1 = at.getScaleY();
 272             yp3 = at.getTranslateY();
 273         } catch (java.awt.geom.NoninvertibleTransformException e) {
 274             xp0 = xp1 = xp3 = yp0 = yp1 = yp3 = 0.0;
 275         }
 276 
 277         // assert rq.lock.isHeldByCurrentThread();
 278         rq.ensureCapacityAndAlignment(68, 12);
 279         RenderBuffer buf = rq.getBuffer();
 280         buf.putInt(SET_TEXTURE_PAINT);
 281         buf.putInt(useMask ? 1 : 0);
 282         buf.putInt(filter ? 1 : 0);
 283         buf.putLong(srcData.getNativeOps());
 284         buf.putDouble(xp0).putDouble(xp1).putDouble(xp3);
 285         buf.putDouble(yp0).putDouble(yp1).putDouble(yp3);
 286     }
 287 
 288 /****************** Shared MultipleGradientPaint support ********************/
 289 
 290     /**
 291      * The maximum number of gradient "stops" supported by our native
 292      * fragment shader implementations.
 293      *
 294      * This value has been empirically determined and capped to allow
 295      * our native shaders to run on all shader-level graphics hardware,
 296      * even on the older, more limited GPUs.  Even the oldest Nvidia
 297      * hardware could handle 16, or even 32 fractions without any problem.
 298      * But the first-generation boards from ATI would fall back into
 299      * software mode (which is unusably slow) for values larger than 12;
 300      * it appears that those boards do not have enough native registers
 301      * to support the number of array accesses required by our gradient
 302      * shaders.  So for now we will cap this value at 12, but we can
 303      * re-evaluate this in the future as hardware becomes more capable.
 304      */
 305     @Native public static final int MULTI_MAX_FRACTIONS = 12;
 306 
 307     /**
 308      * Helper function to convert a color component in sRGB space to
 309      * linear RGB space.  Copied directly from the
 310      * MultipleGradientPaintContext class.
 311      */
 312     public static int convertSRGBtoLinearRGB(int color) {
 313         float input, output;
 314 
 315         input = color / 255.0f;
 316         if (input <= 0.04045f) {
 317             output = input / 12.92f;
 318         } else {
 319             output = (float)Math.pow((input + 0.055) / 1.055, 2.4);
 320         }
 321 
 322         return Math.round(output * 255.0f);
 323     }
 324 
 325     /**
 326      * Helper function to convert a (non-premultiplied) Color in sRGB
 327      * space to an IntArgbPre pixel value, optionally in linear RGB space.
 328      * Based on the PixelConverter.ArgbPre.rgbToPixel() method.
 329      */
 330     private static int colorToIntArgbPrePixel(Color c, boolean linear) {
 331         int rgb = c.getRGB();
 332         if (!linear && ((rgb >> 24) == -1)) {
 333             return rgb;
 334         }
 335         int a = rgb >>> 24;
 336         int r = (rgb >> 16) & 0xff;
 337         int g = (rgb >>  8) & 0xff;
 338         int b = (rgb      ) & 0xff;
 339         if (linear) {
 340             r = convertSRGBtoLinearRGB(r);
 341             g = convertSRGBtoLinearRGB(g);
 342             b = convertSRGBtoLinearRGB(b);
 343         }
 344         int a2 = a + (a >> 7);
 345         r = (r * a2) >> 8;
 346         g = (g * a2) >> 8;
 347         b = (b * a2) >> 8;
 348         return ((a << 24) | (r << 16) | (g << 8) | (b));
 349     }
 350 
 351     /**
 352      * Converts the given array of Color objects into an int array
 353      * containing IntArgbPre pixel values.  If the linear parameter
 354      * is true, the Color values will be converted into a linear RGB
 355      * color space before being returned.
 356      */
 357     private static int[] convertToIntArgbPrePixels(Color[] colors,
 358                                                    boolean linear)
 359     {
 360         int[] pixels = new int[colors.length];
 361         for (int i = 0; i < colors.length; i++) {
 362             pixels[i] = colorToIntArgbPrePixel(colors[i], linear);
 363         }
 364         return pixels;
 365     }
 366 
 367 /********************** LinearGradientPaint support *************************/
 368 
 369     /**
 370      * This method uses techniques that are nearly identical to those
 371      * employed in setGradientPaint() above.  The primary difference
 372      * is that at the native level we use a fragment shader to manually
 373      * apply the plane equation constants to the current fragment position
 374      * to calculate the gradient position in the range [0,1] (the native
 375      * code for GradientPaint does the same, except that it uses OpenGL's
 376      * automatic texture coordinate generation facilities).
 377      *
 378      * One other minor difference worth mentioning is that
 379      * setGradientPaint() calculates the plane equation constants
 380      * such that the gradient end points are positioned at 0.25 and 0.75
 381      * (for reasons discussed in the comments for that method).  In
 382      * contrast, for LinearGradientPaint we setup the equation constants
 383      * such that the gradient end points fall at 0.0 and 1.0.  The
 384      * reason for this difference is that in the fragment shader we
 385      * have more control over how the gradient values are interpreted
 386      * (depending on the paint's CycleMethod).
 387      */
 388     private static void setLinearGradientPaint(RenderQueue rq,
 389                                                SunGraphics2D sg2d,
 390                                                LinearGradientPaint paint,
 391                                                boolean useMask)
 392     {
 393         boolean linear =
 394             (paint.getColorSpace() == ColorSpaceType.LINEAR_RGB);
 395         Color[] colors = paint.getColors();
 396         int numStops = colors.length;
 397         Point2D pt1 = paint.getStartPoint();
 398         Point2D pt2 = paint.getEndPoint();
 399         AffineTransform at = paint.getTransform();
 400         at.preConcatenate(sg2d.transform);
 401 
 402         if (!linear && numStops == 2 &&
 403             paint.getCycleMethod() != CycleMethod.REPEAT)
 404         {
 405             // delegate to the optimized two-color gradient codepath
 406             boolean isCyclic =
 407                 (paint.getCycleMethod() != CycleMethod.NO_CYCLE);
 408             setGradientPaint(rq, at,
 409                              colors[0], colors[1],
 410                              pt1, pt2,
 411                              isCyclic, useMask);
 412             return;
 413         }
 414 
 415         int cycleMethod = paint.getCycleMethod().ordinal();
 416         float[] fractions = paint.getFractions();
 417         int[] pixels = convertToIntArgbPrePixels(colors, linear);
 418 
 419         // calculate plane equation constants
 420         double x = pt1.getX();
 421         double y = pt1.getY();
 422         at.translate(x, y);
 423         // now gradient point 1 is at the origin
 424         x = pt2.getX() - x;
 425         y = pt2.getY() - y;
 426         double len = Math.sqrt(x * x + y * y);
 427         at.rotate(x, y);
 428         // now gradient point 2 is on the positive x-axis
 429         at.scale(len, 1);
 430         // now gradient point 1 is at (0.0, 0), point 2 is at (1.0, 0)
 431 
 432         float p0, p1, p3;
 433         try {
 434             at.invert();
 435             p0 = (float)at.getScaleX();
 436             p1 = (float)at.getShearX();
 437             p3 = (float)at.getTranslateX();
 438         } catch (java.awt.geom.NoninvertibleTransformException e) {
 439             p0 = p1 = p3 = 0.0f;
 440         }
 441 
 442         // assert rq.lock.isHeldByCurrentThread();
 443         rq.ensureCapacity(20 + 12 + (numStops*4*2));
 444         RenderBuffer buf = rq.getBuffer();
 445         buf.putInt(SET_LINEAR_GRADIENT_PAINT);
 446         buf.putInt(useMask ? 1 : 0);
 447         buf.putInt(linear  ? 1 : 0);
 448         buf.putInt(cycleMethod);
 449         buf.putInt(numStops);
 450         buf.putFloat(p0);
 451         buf.putFloat(p1);
 452         buf.putFloat(p3);
 453         buf.put(fractions);
 454         buf.put(pixels);
 455     }
 456 
 457 /********************** RadialGradientPaint support *************************/
 458 
 459     /**
 460      * This method calculates six m** values and a focusX value that
 461      * are used by the native fragment shader.  These techniques are
 462      * based on a whitepaper by Daniel Rice on radial gradient performance
 463      * (attached to the bug report for 6521533).  One can refer to that
 464      * document for the complete set of formulas and calculations, but
 465      * the basic goal is to compose a transform that will convert an
 466      * (x,y) position in device space into a "u" value that represents
 467      * the relative distance to the gradient focus point.  The resulting
 468      * value can be used to look up the appropriate color by linearly
 469      * interpolating between the two nearest colors in the gradient.
 470      */
 471     private static void setRadialGradientPaint(RenderQueue rq,
 472                                                SunGraphics2D sg2d,
 473                                                RadialGradientPaint paint,
 474                                                boolean useMask)
 475     {
 476         boolean linear =
 477             (paint.getColorSpace() == ColorSpaceType.LINEAR_RGB);
 478         int cycleMethod = paint.getCycleMethod().ordinal();
 479         float[] fractions = paint.getFractions();
 480         Color[] colors = paint.getColors();
 481         int numStops = colors.length;
 482         int[] pixels = convertToIntArgbPrePixels(colors, linear);
 483         Point2D center = paint.getCenterPoint();
 484         Point2D focus = paint.getFocusPoint();
 485         float radius = paint.getRadius();
 486 
 487         // save original (untransformed) center and focus points
 488         double cx = center.getX();
 489         double cy = center.getY();
 490         double fx = focus.getX();
 491         double fy = focus.getY();
 492 
 493         // transform from gradient coords to device coords
 494         AffineTransform at = paint.getTransform();
 495         at.preConcatenate(sg2d.transform);
 496         focus = at.transform(focus, focus);
 497 
 498         // transform unit circle to gradient coords; we start with the
 499         // unit circle (center=(0,0), focus on positive x-axis, radius=1)
 500         // and then transform into gradient space
 501         at.translate(cx, cy);
 502         at.rotate(fx - cx, fy - cy);
 503         at.scale(radius, radius);
 504 
 505         // invert to get mapping from device coords to unit circle
 506         try {
 507             at.invert();
 508         } catch (Exception e) {
 509             at.setToScale(0.0, 0.0);
 510         }
 511         focus = at.transform(focus, focus);
 512 
 513         // clamp the focus point so that it does not rest on, or outside
 514         // of, the circumference of the gradient circle
 515         fx = Math.min(focus.getX(), 0.99);
 516 
 517         // assert rq.lock.isHeldByCurrentThread();
 518         rq.ensureCapacity(20 + 28 + (numStops*4*2));
 519         RenderBuffer buf = rq.getBuffer();
 520         buf.putInt(SET_RADIAL_GRADIENT_PAINT);
 521         buf.putInt(useMask ? 1 : 0);
 522         buf.putInt(linear  ? 1 : 0);
 523         buf.putInt(numStops);
 524         buf.putInt(cycleMethod);
 525         buf.putFloat((float)at.getScaleX());
 526         buf.putFloat((float)at.getShearX());
 527         buf.putFloat((float)at.getTranslateX());
 528         buf.putFloat((float)at.getShearY());
 529         buf.putFloat((float)at.getScaleY());
 530         buf.putFloat((float)at.getTranslateY());
 531         buf.putFloat((float)fx);
 532         buf.put(fractions);
 533         buf.put(pixels);
 534     }
 535 }