/* * 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 <code>Dasher</code> class takes a series of linear commands * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and * <code>end</code>) and breaks them into smaller segments according to a * dash pattern array and a starting dash phase. * * <p> 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 extends LineSink { LineSink output; int[] dash; int startPhase; boolean startDashOn; int startIdx; int idx; boolean dashOn; int phase; int sx, sy; int x0, y0; int m00, m01; int m10, m11; Transform4 transform; boolean symmetric; long ldet; boolean firstDashOn; boolean starting; int sx1, sy1; /** * Empty constructor. <code>setOutput</code> and * <code>setParameters</code> must be called prior to calling any * other methods. */ public Dasher() {} /** * Constructs a <code>Dasher</code>. * * @param output an output <code>LineSink</code>. * @param dash an array of <code>int</code>s containing the dash * pattern in S15.16 format. * @param phase an <code>int</code> containing the dash phase in * S15.16 format. * @param transform a <code>Transform4</code> 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, int[] dash, int phase, Transform4 transform) { setOutput(output); setParameters(dash, phase, transform); } /** * Sets the output <code>LineSink</code> of this * <code>Dasher</code>. * * @param output an output <code>LineSink</code>. */ public void setOutput(LineSink output) { this.output = output; } /** * Sets the parameters of this <code>Dasher</code>. * * @param dash an array of <code>int</code>s containing the dash * pattern in S15.16 format. * @param phase an <code>int</code> containing the dash phase in * S15.16 format. * @param transform a <code>Transform4</code> object indicating * the transform that has been previously applied to all incoming * coordinates. This is required in order to compute dash lengths * properly. */ public void setParameters(int[] dash, int phase, Transform4 transform) { if (phase < 0) { throw new IllegalArgumentException("phase < 0 !"); } // Normalize so 0 <= phase < dash[0] int idx = 0; dashOn = true; int d; while (phase >= (d = dash[idx])) { phase -= d; idx = (idx + 1) % dash.length; dashOn = !dashOn; } this.dash = new int[dash.length]; for (int i = 0; i < dash.length; i++) { this.dash[i] = dash[i]; } this.startPhase = this.phase = phase; this.startDashOn = dashOn; this.startIdx = idx; this.transform = transform; this.m00 = transform.m00; this.m01 = transform.m01; this.m10 = transform.m10; this.m11 = transform.m11; this.ldet = ((long)m00*m11 - (long)m01*m10) >> 16; this.symmetric = (m00 == m11 && m10 == -m01); } public void moveTo(int x0, int 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(int x1, int 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(int x1, int y1) { while (true) { int d = dash[idx] - phase; int lx = x1 - x0; int ly = y1 - y0; // Compute segment length in the untransformed // coordinate system // IMPL NOTE - use fixed point int l; if (symmetric) { l = (int)((PiscesMath.hypot(lx, ly)*65536L)/ldet); } else{ long la = ((long)ly*m00 - (long)lx*m10)/ldet; long lb = ((long)ly*m01 - (long)lx*m11)/ldet; l = (int)PiscesMath.hypot(la, lb); } if (l < d) { goTo(x1, y1); // Advance phase within current dash segment phase += l; return; } long t; int xsplit, ysplit; // // For zero length dashses, SE appears to move 1/8 unit // // in device space // if (d == 0) { // double dlx = lx/65536.0; // double dly = ly/65536.0; // len = PiscesMath.hypot(dlx, dly); // double dt = 1.0/(8*len); // double dxsplit = (x0/65536.0) + dt*dlx; // double dysplit = (y0/65536.0) + dt*dly; // xsplit = (int)(dxsplit*65536.0); // ysplit = (int)(dysplit*65536.0); // } else { t = ((long)d << 16)/l; xsplit = x0 + (int)(t*(x1 - x0) >> 16); ysplit = y0 + (int)(t*(y1 - y0) >> 16); // } goTo(xsplit, ysplit); // 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(); } }