/*
 * 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.

/** * 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. setOutput and setParameters must be called prior to calling any other methods.
/** * Empty constructor. <code>setOutput</code> and * <code>setParameters</code> must be called prior to calling any * other methods. */
public Dasher() {}
Constructs a Dasher.
Params:
  • output – an output LineSink.
  • dash – an array of ints containing the dash pattern in S15.16 format.
  • phase – an int containing the dash phase in S15.16 format.
  • 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.
/** * 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 LineSink of this Dasher.
Params:
  • output – an output LineSink.
/** * 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 Dasher.
Params:
  • dash – an array of ints containing the dash pattern in S15.16 format.
  • phase – an int containing the dash phase in S15.16 format.
  • 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.
/** * 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(); } }