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