| /* |
| * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.java2d.pipe; |
| |
| import java.awt.Shape; |
| import java.awt.BasicStroke; |
| import java.awt.geom.Line2D; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.geom.AffineTransform; |
| import sun.java2d.SunGraphics2D; |
| import sun.awt.SunHints; |
| |
| /** |
| * This class converts calls to the basic pixel rendering methods |
| * into calls to the methods on a ParallelogramPipe. |
| * Most calls are transformed into calls to the fill(Shape) method |
| * by the parent PixelToShapeConverter class, but some calls are |
| * transformed into calls to fill/drawParallelogram(). |
| */ |
| public class PixelToParallelogramConverter extends PixelToShapeConverter |
| implements ShapeDrawPipe |
| { |
| ParallelogramPipe outrenderer; |
| double minPenSize; |
| double normPosition; |
| double normRoundingBias; |
| boolean adjustfill; |
| |
| /** |
| * @param shapepipe pipeline to forward shape calls to |
| * @param pgrampipe pipeline to forward parallelogram calls to |
| * (and drawLine calls if possible) |
| * @param minPenSize minimum pen size for dropout control |
| * @param normPosition sub-pixel location to normalize endpoints |
| * for STROKE_NORMALIZE cases |
| * @param adjustFill boolean to control whethere normalization |
| * constants are also applied to fill operations |
| * (normally true for non-AA, false for AA) |
| */ |
| public PixelToParallelogramConverter(ShapeDrawPipe shapepipe, |
| ParallelogramPipe pgrampipe, |
| double minPenSize, |
| double normPosition, |
| boolean adjustfill) |
| { |
| super(shapepipe); |
| outrenderer = pgrampipe; |
| this.minPenSize = minPenSize; |
| this.normPosition = normPosition; |
| this.normRoundingBias = 0.5 - normPosition; |
| this.adjustfill = adjustfill; |
| } |
| |
| public void drawLine(SunGraphics2D sg2d, |
| int x1, int y1, int x2, int y2) |
| { |
| if (!drawGeneralLine(sg2d, x1, y1, x2, y2)) { |
| super.drawLine(sg2d, x1, y1, x2, y2); |
| } |
| } |
| |
| public void drawRect(SunGraphics2D sg2d, |
| int x, int y, int w, int h) |
| { |
| if (w >= 0 && h >= 0) { |
| if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) { |
| BasicStroke bs = ((BasicStroke) sg2d.stroke); |
| if (w > 0 && h > 0) { |
| if (bs.getLineJoin() == BasicStroke.JOIN_MITER && |
| bs.getDashArray() == null) |
| { |
| double lw = bs.getLineWidth(); |
| drawRectangle(sg2d, x, y, w, h, lw); |
| return; |
| } |
| } else { |
| // Note: This calls the integer version which |
| // will verify that the local drawLine optimizations |
| // work and call super.drawLine(), if not. |
| drawLine(sg2d, x, y, x+w, y+h); |
| return; |
| } |
| } |
| super.drawRect(sg2d, x, y, w, h); |
| } |
| } |
| |
| public void fillRect(SunGraphics2D sg2d, |
| int x, int y, int w, int h) |
| { |
| if (w > 0 && h > 0) { |
| fillRectangle(sg2d, x, y, w, h); |
| } |
| } |
| |
| public void draw(SunGraphics2D sg2d, Shape s) { |
| if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) { |
| BasicStroke bs = ((BasicStroke) sg2d.stroke); |
| if (s instanceof Rectangle2D) { |
| if (bs.getLineJoin() == BasicStroke.JOIN_MITER && |
| bs.getDashArray() == null) |
| { |
| Rectangle2D r2d = (Rectangle2D) s; |
| double w = r2d.getWidth(); |
| double h = r2d.getHeight(); |
| double x = r2d.getX(); |
| double y = r2d.getY(); |
| if (w >= 0 && h >= 0) { |
| double lw = bs.getLineWidth(); |
| drawRectangle(sg2d, x, y, w, h, lw); |
| } |
| return; |
| } |
| } else if (s instanceof Line2D) { |
| Line2D l2d = (Line2D) s; |
| if (drawGeneralLine(sg2d, |
| l2d.getX1(), l2d.getY1(), |
| l2d.getX2(), l2d.getY2())) |
| { |
| return; |
| } |
| } |
| } |
| |
| outpipe.draw(sg2d, s); |
| } |
| |
| public void fill(SunGraphics2D sg2d, Shape s) { |
| if (s instanceof Rectangle2D) { |
| Rectangle2D r2d = (Rectangle2D) s; |
| double w = r2d.getWidth(); |
| double h = r2d.getHeight(); |
| if (w > 0 && h > 0) { |
| double x = r2d.getX(); |
| double y = r2d.getY(); |
| fillRectangle(sg2d, x, y, w, h); |
| } |
| return; |
| } |
| |
| outpipe.fill(sg2d, s); |
| } |
| |
| static double len(double x, double y) { |
| return ((x == 0) ? Math.abs(y) |
| : ((y == 0) ? Math.abs(x) |
| : Math.sqrt(x * x + y * y))); |
| } |
| |
| double normalize(double v) { |
| return Math.floor(v + normRoundingBias) + normPosition; |
| } |
| |
| public boolean drawGeneralLine(SunGraphics2D sg2d, |
| double x1, double y1, |
| double x2, double y2) |
| { |
| if (sg2d.strokeState == SunGraphics2D.STROKE_CUSTOM || |
| sg2d.strokeState == SunGraphics2D.STROKE_THINDASHED) |
| { |
| return false; |
| } |
| BasicStroke bs = (BasicStroke) sg2d.stroke; |
| int cap = bs.getEndCap(); |
| if (cap == BasicStroke.CAP_ROUND || bs.getDashArray() != null) { |
| // TODO: we could construct the GeneralPath directly |
| // for CAP_ROUND and save a lot of processing in that case... |
| // And again, we would need to deal with dropout control... |
| return false; |
| } |
| double lw = bs.getLineWidth(); |
| // Save the original dx, dy in case we need it to transform |
| // the linewidth as a perpendicular vector below |
| double dx = x2 - x1; |
| double dy = y2 - y1; |
| switch (sg2d.transformState) { |
| case SunGraphics2D.TRANSFORM_GENERIC: |
| case SunGraphics2D.TRANSFORM_TRANSLATESCALE: |
| { |
| double coords[] = {x1, y1, x2, y2}; |
| sg2d.transform.transform(coords, 0, coords, 0, 2); |
| x1 = coords[0]; |
| y1 = coords[1]; |
| x2 = coords[2]; |
| y2 = coords[3]; |
| } |
| break; |
| case SunGraphics2D.TRANSFORM_ANY_TRANSLATE: |
| case SunGraphics2D.TRANSFORM_INT_TRANSLATE: |
| { |
| double tx = sg2d.transform.getTranslateX(); |
| double ty = sg2d.transform.getTranslateY(); |
| x1 += tx; |
| y1 += ty; |
| x2 += tx; |
| y2 += ty; |
| } |
| break; |
| case SunGraphics2D.TRANSFORM_ISIDENT: |
| break; |
| default: |
| throw new InternalError("unknown TRANSFORM state..."); |
| } |
| if (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE) { |
| if (sg2d.strokeState == SunGraphics2D.STROKE_THIN && |
| outrenderer instanceof PixelDrawPipe) |
| { |
| // PixelDrawPipes will add sg2d.transXY so we need to factor |
| // that out... |
| int ix1 = (int) Math.floor(x1 - sg2d.transX); |
| int iy1 = (int) Math.floor(y1 - sg2d.transY); |
| int ix2 = (int) Math.floor(x2 - sg2d.transX); |
| int iy2 = (int) Math.floor(y2 - sg2d.transY); |
| ((PixelDrawPipe)outrenderer).drawLine(sg2d, ix1, iy1, ix2, iy2); |
| return true; |
| } |
| x1 = normalize(x1); |
| y1 = normalize(y1); |
| x2 = normalize(x2); |
| y2 = normalize(y2); |
| } |
| if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { |
| // Transform the linewidth... |
| // calculate the scaling factor for a unit vector |
| // perpendicular to the original user space line. |
| double len = len(dx, dy); |
| if (len == 0) { |
| dx = len = 1; |
| // dy = 0; already |
| } |
| // delta transform the transposed (90 degree rotated) unit vector |
| double unitvector[] = {dy/len, -dx/len}; |
| sg2d.transform.deltaTransform(unitvector, 0, unitvector, 0, 1); |
| lw *= len(unitvector[0], unitvector[1]); |
| } |
| lw = Math.max(lw, minPenSize); |
| dx = x2 - x1; |
| dy = y2 - y1; |
| double len = len(dx, dy); |
| double udx, udy; |
| if (len == 0) { |
| if (cap == BasicStroke.CAP_BUTT) { |
| return true; |
| } |
| udx = lw; |
| udy = 0; |
| } else { |
| udx = lw * dx / len; |
| udy = lw * dy / len; |
| } |
| double px = x1 + udy / 2.0; |
| double py = y1 - udx / 2.0; |
| if (cap == BasicStroke.CAP_SQUARE) { |
| px -= udx / 2.0; |
| py -= udy / 2.0; |
| dx += udx; |
| dy += udy; |
| } |
| outrenderer.fillParallelogram(sg2d, px, py, -udy, udx, dx, dy); |
| return true; |
| } |
| |
| public void fillRectangle(SunGraphics2D sg2d, |
| double rx, double ry, |
| double rw, double rh) |
| { |
| double px, py; |
| double dx1, dy1, dx2, dy2; |
| AffineTransform txform = sg2d.transform; |
| dx1 = txform.getScaleX(); |
| dy1 = txform.getShearY(); |
| dx2 = txform.getShearX(); |
| dy2 = txform.getScaleY(); |
| px = rx * dx1 + ry * dx2 + txform.getTranslateX(); |
| py = rx * dy1 + ry * dy2 + txform.getTranslateY(); |
| dx1 *= rw; |
| dy1 *= rw; |
| dx2 *= rh; |
| dy2 *= rh; |
| if (adjustfill && |
| sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM && |
| sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE) |
| { |
| double newx = normalize(px); |
| double newy = normalize(py); |
| dx1 = normalize(px + dx1) - newx; |
| dy1 = normalize(py + dy1) - newy; |
| dx2 = normalize(px + dx2) - newx; |
| dy2 = normalize(py + dy2) - newy; |
| px = newx; |
| py = newy; |
| } |
| outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2); |
| } |
| |
| public void drawRectangle(SunGraphics2D sg2d, |
| double rx, double ry, |
| double rw, double rh, |
| double lw) |
| { |
| double px, py; |
| double dx1, dy1, dx2, dy2; |
| double lw1, lw2; |
| AffineTransform txform = sg2d.transform; |
| dx1 = txform.getScaleX(); |
| dy1 = txform.getShearY(); |
| dx2 = txform.getShearX(); |
| dy2 = txform.getScaleY(); |
| px = rx * dx1 + ry * dx2 + txform.getTranslateX(); |
| py = rx * dy1 + ry * dy2 + txform.getTranslateY(); |
| // lw along dx1,dy1 scale by transformed length of dx2,dy2 vectors |
| // and vice versa |
| lw1 = len(dx1, dy1) * lw; |
| lw2 = len(dx2, dy2) * lw; |
| dx1 *= rw; |
| dy1 *= rw; |
| dx2 *= rh; |
| dy2 *= rh; |
| if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM && |
| sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE) |
| { |
| double newx = normalize(px); |
| double newy = normalize(py); |
| dx1 = normalize(px + dx1) - newx; |
| dy1 = normalize(py + dy1) - newy; |
| dx2 = normalize(px + dx2) - newx; |
| dy2 = normalize(py + dy2) - newy; |
| px = newx; |
| py = newy; |
| } |
| lw1 = Math.max(lw1, minPenSize); |
| lw2 = Math.max(lw2, minPenSize); |
| double len1 = len(dx1, dy1); |
| double len2 = len(dx2, dy2); |
| if (lw1 >= len1 || lw2 >= len2) { |
| // The line widths are large enough to consume the |
| // entire hole in the middle of the parallelogram |
| // so we can just fill the outer parallelogram. |
| fillOuterParallelogram(sg2d, |
| px, py, dx1, dy1, dx2, dy2, |
| len1, len2, lw1, lw2); |
| } else { |
| outrenderer.drawParallelogram(sg2d, |
| px, py, dx1, dy1, dx2, dy2, |
| lw1 / len1, lw2 / len2); |
| } |
| } |
| |
| /** |
| * This utility function handles the case where a drawRectangle |
| * operation discovered that the interior hole in the rectangle |
| * or parallelogram has been completely filled in by the stroke |
| * width. It calculates the outer parallelogram of the stroke |
| * and issues a single fillParallelogram request to fill it. |
| */ |
| public void fillOuterParallelogram(SunGraphics2D sg2d, |
| double px, double py, |
| double dx1, double dy1, |
| double dx2, double dy2, |
| double len1, double len2, |
| double lw1, double lw2) |
| { |
| double udx1 = dx1 / len1; |
| double udy1 = dy1 / len1; |
| double udx2 = dx2 / len2; |
| double udy2 = dy2 / len2; |
| if (len1 == 0) { |
| // len1 is 0, replace udxy1 with perpendicular of udxy2 |
| if (len2 == 0) { |
| // both are 0, use a unit Y vector for udxy2 |
| udx2 = 0; |
| udy2 = 1; |
| } |
| udx1 = udy2; |
| udy1 = -udx2; |
| } else if (len2 == 0) { |
| // len2 is 0, replace udxy2 with perpendicular of udxy1 |
| udx2 = udy1; |
| udy2 = -udx1; |
| } |
| udx1 *= lw1; |
| udy1 *= lw1; |
| udx2 *= lw2; |
| udy2 *= lw2; |
| px -= (udx1 + udx2) / 2; |
| py -= (udy1 + udy2) / 2; |
| dx1 += udx1; |
| dy1 += udy1; |
| dx2 += udx2; |
| dy2 += udy2; |
| |
| outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2); |
| } |
| } |