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