1 /*
   2  * Copyright (c) 1997, 2016, 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.pipe;
  26 
  27 import java.awt.BasicStroke;
  28 import java.awt.Rectangle;
  29 import java.awt.Shape;
  30 import java.awt.geom.Rectangle2D;
  31 import java.util.concurrent.ConcurrentLinkedQueue;
  32 import sun.awt.SunHints;
  33 import sun.java2d.SunGraphics2D;
  34 
  35 /**
  36  * This class is used to convert raw geometry into 8-bit alpha tiles
  37  * using an AATileGenerator for application by the next stage of
  38  * the pipeline.
  39  * This class sets up the Generator and computes the alpha tiles
  40  * and then passes them on to a CompositePipe object for painting.
  41  */
  42 public final class AAShapePipe
  43     implements ShapeDrawPipe, ParallelogramPipe
  44 {
  45     static final RenderingEngine renderengine = RenderingEngine.getInstance();
  46 
  47     // Per-thread TileState (~1K very small so do not use any Weak Reference)
  48     private static final ReentrantThreadLocal<TileState> tileStateThreadLocal =
  49             new ReentrantThreadLocal<TileState>() {
  50         @Override
  51         protected TileState initialValue() {
  52             return new TileState();
  53         }
  54     };
  55 
  56     final CompositePipe outpipe;
  57 
  58     public AAShapePipe(CompositePipe pipe) {
  59         outpipe = pipe;
  60     }
  61 
  62     @Override
  63     public void draw(SunGraphics2D sg, Shape s) {
  64         final BasicStroke bs;
  65 
  66         if (sg.stroke instanceof BasicStroke) {
  67             bs = (BasicStroke) sg.stroke;
  68         } else {
  69             s = sg.stroke.createStrokedShape(s);
  70             bs = null;
  71         }
  72 
  73         renderPath(sg, s, bs);
  74     }
  75 
  76     @Override
  77     public void fill(SunGraphics2D sg, Shape s) {
  78         renderPath(sg, s, null);
  79     }
  80 
  81     @Override
  82     public void fillParallelogram(SunGraphics2D sg,
  83                                   double ux1, double uy1,
  84                                   double ux2, double uy2,
  85                                   double x, double y,
  86                                   double dx1, double dy1,
  87                                   double dx2, double dy2)
  88     {
  89         final TileState ts = tileStateThreadLocal.get();
  90         try {
  91             final int[] abox = ts.abox;
  92 
  93             final AATileGenerator aatg =
  94                 renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
  95                                                 sg.getCompClip(), abox);
  96             if (aatg != null) {
  97                 renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
  98                             aatg, abox, ts);
  99             }
 100         } finally {
 101             tileStateThreadLocal.restore(ts);
 102         }
 103     }
 104 
 105     @Override
 106     public void drawParallelogram(SunGraphics2D sg,
 107                                   double ux1, double uy1,
 108                                   double ux2, double uy2,
 109                                   double x, double y,
 110                                   double dx1, double dy1,
 111                                   double dx2, double dy2,
 112                                   double lw1, double lw2)
 113     {
 114         final TileState ts = tileStateThreadLocal.get();
 115         try {
 116             final int[] abox = ts.abox;
 117 
 118             final AATileGenerator aatg =
 119                 renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1,
 120                                                 lw2, sg.getCompClip(), abox);
 121             if (aatg != null) {
 122                 // Note that bbox is of the original shape, not the wide path.
 123                 // This is appropriate for handing to Paint methods...
 124                 renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
 125                             aatg, abox, ts);
 126             }
 127         } finally {
 128             tileStateThreadLocal.restore(ts);
 129         }
 130     }
 131 
 132     public void renderPath(SunGraphics2D sg, Shape s, BasicStroke bs) {
 133         final boolean adjust = (bs != null &&
 134                           sg.strokeHint != SunHints.INTVAL_STROKE_PURE);
 135         final boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
 136 
 137         final TileState ts = tileStateThreadLocal.get();
 138         try {
 139             final int[] abox = ts.abox;
 140 
 141             final AATileGenerator aatg =
 142                 renderengine.getAATileGenerator(s, sg.transform, sg.getCompClip(),
 143                                                 bs, thin, adjust, abox);
 144             if (aatg != null) {
 145                 renderTiles(sg, s, aatg, abox, ts);
 146             }
 147         } finally {
 148             tileStateThreadLocal.restore(ts);
 149         }
 150     }
 151 
 152     public void renderTiles(SunGraphics2D sg, Shape s,
 153                             final AATileGenerator aatg,
 154                             final int[] abox, final TileState ts)
 155     {
 156         Object context = null;
 157         try {
 158             // reentrance: outpipe may also use AAShapePipe:
 159             context = outpipe.startSequence(sg, s,
 160                                             ts.computeDevBox(abox),
 161                                             abox);
 162 
 163             // copy of int[] abox as local variables for performance:
 164             final int x0 = abox[0];
 165             final int y0 = abox[1];
 166             final int x1 = abox[2];
 167             final int y1 = abox[3];
 168 
 169             final int tw = aatg.getTileWidth();
 170             final int th = aatg.getTileHeight();
 171 
 172             // get tile from thread local storage:
 173             final byte[] alpha = ts.getAlphaTile(tw * th);
 174             byte[] atile;
 175 
 176             for (int y = y0; y < y1; y += th) {
 177                 final int h = Math.min(th, y1 - y);
 178 
 179                 for (int x = x0; x < x1; x += tw) {
 180                     final int w = Math.min(tw, x1 - x);
 181 
 182                     final int a = aatg.getTypicalAlpha();
 183 
 184                     if (a == 0x00 || !outpipe.needTile(context, x, y, w, h)) {
 185                         aatg.nextTile();
 186                         outpipe.skipTile(context, x, y);
 187                         continue;
 188                     }
 189                     if (a == 0xff) {
 190                         atile = null;
 191                         aatg.nextTile();
 192                     } else {
 193                         atile = alpha;
 194                         aatg.getAlpha(alpha, 0, tw);
 195                     }
 196 
 197                     outpipe.renderPathTile(context, atile, 0, tw, x, y, w, h);
 198                 }
 199             }
 200         } finally {
 201             aatg.dispose();
 202             if (context != null) {
 203                 outpipe.endSequence(context);
 204             }
 205         }
 206     }
 207 
 208     // Tile state used by AAShapePipe
 209     static final class TileState extends ReentrantContext {
 210         // cached tile (32 x 32 tile by default)
 211         private byte[] theTile = new byte[32 * 32];
 212         // dirty aabox array
 213         final int[] abox = new int[4];
 214         // dirty bbox rectangle
 215         private final Rectangle dev = new Rectangle();
 216         // dirty bbox rectangle2D.Double
 217         private final Rectangle2D.Double bbox2D = new Rectangle2D.Double();
 218 
 219         byte[] getAlphaTile(int len) {
 220             byte[] t = theTile;
 221             if (t.length < len) {
 222                 // create a larger tile and may free current theTile (too small)
 223                 theTile = t = new byte[len];
 224             }
 225             return t;
 226         }
 227 
 228         Rectangle computeDevBox(final int[] abox) {
 229             final Rectangle box = this.dev;
 230             box.x = abox[0];
 231             box.y = abox[1];
 232             box.width = abox[2] - abox[0];
 233             box.height = abox[3] - abox[1];
 234             return box;
 235         }
 236 
 237         Rectangle2D computeBBox(double ux1, double uy1,
 238                                 double ux2, double uy2)
 239         {
 240             if ((ux2 -= ux1) < 0.0) {
 241                 ux1 += ux2;
 242                 ux2 = -ux2;
 243             }
 244             if ((uy2 -= uy1) < 0.0) {
 245                 uy1 += uy2;
 246                 uy2 = -uy2;
 247             }
 248             final Rectangle2D.Double box = this.bbox2D;
 249             box.x = ux1;
 250             box.y = uy1;
 251             box.width = ux2;
 252             box.height = uy2;
 253             return box;
 254         }
 255     }
 256 
 257     static class ReentrantThreadLocal<K extends ReentrantContext>
 258         extends ThreadLocal<K>
 259     {
 260         final static int DEPTH_UNDEFINED = 0;
 261         final static int DEPTH_TL = 1;
 262         final static int DEPTH_CLQ = 2;
 263 
 264         // ReentrantContext queue for child contexts
 265         private final ConcurrentLinkedQueue<K> ctxQueue
 266             = new ConcurrentLinkedQueue<K>();
 267 
 268         /**
 269          * Give a ReentrantContext instance from thread-local or CLQ storage
 270          * @return ReentrantContext instance
 271          */
 272         @Override
 273         public final K get() {
 274             K ctx = super.get();
 275             // Check reentrance:
 276             if (ctx.depth == ReentrantThreadLocal.DEPTH_UNDEFINED) {
 277                 ctx.depth = ReentrantThreadLocal.DEPTH_TL;
 278             } else {
 279                 // get or create another ReentrantContext from queue:
 280                 ctx = ctxQueue.poll();
 281                 if (ctx == null) {
 282                     // create a new ReentrantContext if none is available
 283                     ctx = initialValue();
 284                     ctx.depth = ReentrantThreadLocal.DEPTH_CLQ;
 285                 }
 286             }
 287             return ctx;
 288         }
 289 
 290         /**
 291          * Restore the given ReentrantContext instance for reuse
 292          * @param ctx ReentrantContext instance
 293          */
 294         public final void restore(final K ctx) {
 295             if (ctx.depth == ReentrantThreadLocal.DEPTH_TL) {
 296                 ctx.depth = ReentrantThreadLocal.DEPTH_UNDEFINED;
 297             } else {
 298                 ctxQueue.offer(ctx);
 299             }
 300         }
 301     }
 302 
 303     static class ReentrantContext {
 304         int depth = ReentrantThreadLocal.DEPTH_UNDEFINED;
 305     }
 306 }