blob: 1a1f328adc43f83f1d6be913748b04fe537d8ae1 [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 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();
}
}