blob: f5b5e049143a73945e84dc5e17dad3e3c571b63d [file] [log] [blame]
/*
* 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 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 <code>Dasher</code>.
*
* @param output an output <code>LineSink</code>.
* @param dash an array of <code>int</code>s containing the dash pattern
* @param phase an <code>int</code> containing the dash phase
* @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,
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();
}
}