/* * Copyright (c) 2007, 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.pisces; /** * The Dasher class takes a series of linear commands * (moveTo, lineTo, close and * end) and breaks them into smaller segments according to a * dash pattern array and a starting dash phase. * *

Issues: in J2Se, a zero length dash segment as drawn as a very * short dash, whereas Pisces does not draw anything. The PostScript * semantics are unclear. * */ public class Dasher implements LineSink { private final LineSink output; private final float[] dash; private final float startPhase; private final boolean startDashOn; private final int startIdx; private final float m00, m10, m01, m11; private final float det; private boolean firstDashOn; private boolean starting; private int idx; private boolean dashOn; private float phase; private float sx, sy; private float x0, y0; private float sx1, sy1; /** * Constructs a Dasher. * * @param output an output LineSink. * @param dash an array of ints containing the dash pattern * @param phase an int containing the dash phase * @param transform a Transform4 object indicating * the transform that has been previously applied to all incoming * coordinates. This is required in order to compute dash lengths * properly. */ public Dasher(LineSink output, float[] dash, float phase, float a00, float a01, float a10, float a11) { if (phase < 0) { throw new IllegalArgumentException("phase < 0 !"); } this.output = output; // Normalize so 0 <= phase < dash[0] int idx = 0; dashOn = true; float d; while (phase >= (d = dash[idx])) { phase -= d; idx = (idx + 1) % dash.length; dashOn = !dashOn; } this.dash = dash; this.startPhase = this.phase = phase; this.startDashOn = dashOn; this.startIdx = idx; m00 = a00; m01 = a01; m10 = a10; m11 = a11; det = m00 * m11 - m01 * m10; } public void moveTo(float x0, float y0) { output.moveTo(x0, y0); this.idx = startIdx; this.dashOn = this.startDashOn; this.phase = this.startPhase; this.sx = this.x0 = x0; this.sy = this.y0 = y0; this.starting = true; } public void lineJoin() { output.lineJoin(); } private void goTo(float x1, float y1) { if (dashOn) { if (starting) { this.sx1 = x1; this.sy1 = y1; firstDashOn = true; starting = false; } output.lineTo(x1, y1); } else { if (starting) { firstDashOn = false; starting = false; } output.moveTo(x1, y1); } this.x0 = x1; this.y0 = y1; } public void lineTo(float x1, float y1) { // The widened line is squished to a 0 width one, so no drawing is done if (det == 0) { goTo(x1, y1); return; } float dx = x1 - x0; float dy = y1 - y0; // Compute segment length in the untransformed // coordinate system float la = (dy*m00 - dx*m10)/det; float lb = (dy*m01 - dx*m11)/det; float origLen = (float) Math.hypot(la, lb); if (origLen == 0) { // Let the output LineSink deal with cases where dx, dy are 0. goTo(x1, y1); return; } // The scaling factors needed to get the dx and dy of the // transformed dash segments. float cx = dx / origLen; float cy = dy / origLen; while (true) { float leftInThisDashSegment = dash[idx] - phase; if (origLen < leftInThisDashSegment) { goTo(x1, y1); // Advance phase within current dash segment phase += origLen; return; } else if (origLen == leftInThisDashSegment) { goTo(x1, y1); phase = 0f; idx = (idx + 1) % dash.length; dashOn = !dashOn; return; } float dashx, dashy; float dashdx = dash[idx] * cx; float dashdy = dash[idx] * cy; if (phase == 0) { dashx = x0 + dashdx; dashy = y0 + dashdy; } else { float p = (leftInThisDashSegment) / dash[idx]; dashx = x0 + p * dashdx; dashy = y0 + p * dashdy; } goTo(dashx, dashy); origLen -= (dash[idx] - phase); // Advance to next dash segment idx = (idx + 1) % dash.length; dashOn = !dashOn; phase = 0; } } public void close() { lineTo(sx, sy); if (firstDashOn) { output.lineTo(sx1, sy1); } } public void end() { output.end(); } }