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