/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Denis M. Kishenko * @version $Revision$ */ package org.apache.harmony.awt.gl.render; import org.apache.harmony.awt.gl.MultiRectArea; public class JavaLineRasterizer { /** * LineDasher class provides dashing for particular dash style */ public static class LineDasher { int index; float pos; float phase; float dash[]; float inv[]; boolean visible; public LineDasher() { } public LineDasher(float dash[], float phase) { this.dash = dash; this.phase = phase; inv = new float[dash.length]; int j = dash.length; for (float element : dash) { inv[--j] = element; } index = 0; while (phase > dash[index]) { phase -= dash[index]; index = (index + 1) % dash.length; } visible = index % 2 == 0; } void move(float step) { // main dasher pos += step; step += phase; while(step >= dash[index]) { step -= dash[index]; index = (index + 1) % dash.length; visible = !visible; } phase = step; } float nextDash() { phase = 0.0f; index = (index + 1) % dash.length; visible = !visible; return dash[index]; } LineDasher createDiagonal(double k, float length, boolean invert) { LineDasher local = new LineDasher(); local.dash = new float[dash.length]; if (invert) { // inverted dasher move(length); local.phase = (float)((dash[index] - phase) * k); local.visible = visible; local.index = inv.length - index - 1; for(int i = 0; i < inv.length; i++) { local.dash[i] = (float)(inv[i] * k); } } else { local.phase = (float)(phase * k); local.visible = visible; local.index = index; for(int i = 0; i < dash.length; i++) { local.dash[i] = (float)(dash[i] * k); } move(length); } return local; } LineDasher createOrtogonal(float length, boolean invert) { LineDasher local = new LineDasher(); local.dash = new float[dash.length]; if (invert) { // inverted dasher move(length); local.phase = dash[index] - phase; local.visible = visible; local.index = inv.length - index - 1; local.dash = inv; } else { local.phase = phase; local.visible = visible; local.index = index; local.dash = dash; move(length); } return local; } LineDasher createChild(float start) { LineDasher child = new LineDasher(); child.phase = phase; child.visible = visible; child.index = index; child.dash = dash; child.move(start); return child; } } /** * Line class provides rasterization for different line types */ abstract static class Line { int x1, y1, x2, y2; int x, y; MultiRectArea dst; Line(int x1, int y1, int x2, int y2, MultiRectArea dst) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.dst = dst; } static abstract class Diag extends Line { int dx, dy, adx, ady, sx, sy; int eBase, ePos, eNeg; int xcount; int e; Diag(int x1, int y1, int x2, int y2, MultiRectArea dst) { super(x1, y1, x2, y2, dst); dx = x2 - x1; dy = y2 - y1; sy = 1; if (dx > 0) { adx = dx; sx = 1; } else { adx = -dx; sx = -1; } ady = dy; } float getLength() { return (float)Math.sqrt(dx * dx + dy * dy); } static class Hor extends Diag { Hor(int x1, int y1, int x2, int y2, MultiRectArea dst) { super(x1, y1, x2, y2, dst); eBase = ady + ady - adx; ePos = 2 * (ady - adx); eNeg = ady + ady; xcount = adx; } @Override void rasterize() { e = eBase; x = x1; y = y1; rasterize(xcount); } @Override void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { e = eBase + 2 * (ady * Math.abs(nx1 - x1) - adx * Math.abs(ny1 - y1)); x = nx1; y = ny1; rasterize(dx > 0 ? nx2 - nx1 : nx1 - nx2); } @Override void rasterize(int count) { int px = x; while (count-- > 0) { if (e >= 0) { if (sx > 0) { dst.addRect(px, y, x, y); } else { dst.addRect(x, y, px, y); } x += sx; y += sy; e += ePos; px = x; } else { e += eNeg; x += sx; } } if (sx > 0) { dst.addRect(px, y, x, y); } else { dst.addRect(x, y, px, y); } } @Override void skip(int count) { while (count-- > 0) { x += sx; if (e >= 0) { y += sy; e += ePos; } else { e += eNeg; } } } } static class Ver extends Diag { Ver(int x1, int y1, int x2, int y2, MultiRectArea dst) { super(x1, y1, x2, y2, dst); eBase = adx + adx - ady; ePos = 2 * (adx - ady); eNeg = adx + adx; xcount = ady; } @Override void rasterize() { e = eBase; x = x1; y = y1; rasterize(xcount); } @Override void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { e = eBase + 2 * (adx * Math.abs(ny1 - y1) - ady * Math.abs(nx1 - x1)); x = nx1; y = ny1; rasterize(ny2 - ny1); } @Override void rasterize(int count) { int py = y; while (count-- > 0) { if (e >= 0) { dst.addRect(x, py, x, y); x += sx; y += sy; e += ePos; py = y; } else { y += sy; e += eNeg; } } dst.addRect(x, py, x, y); } @Override void skip(int count) { while (count-- > 0) { y += sy; if (e >= 0) { x += sx; e += ePos; } else { e += eNeg; } } } } static class HorDashed extends Hor { LineDasher local; HorDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) { super(x1, y1, x2, y2, dst); float length = getLength(); local = dasher.createDiagonal(xcount / length, length, invert); } @Override void rasterize() { e = eBase; x = x1; y = y1; rasterizeDash(xcount, local); } @Override void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { e = eBase + 2 * (ady * Math.abs(nx1 - x1) - adx * Math.abs(ny1 - y1)); x = nx1; y = ny1; rasterizeDash(Math.abs(nx2 - nx1), local.createChild(Math.abs(nx1 - x1))); } } static class VerDashed extends Ver { LineDasher local; VerDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) { super(x1, y1, x2, y2, dst); float length = getLength(); local = dasher.createDiagonal(xcount / length, length, invert); } @Override void rasterize() { e = eBase; x = x1; y = y1; rasterizeDash(xcount, local); } @Override void rasterizeClipped(int nx1, int ny1, int nx2, int ny2) { e = eBase + 2 * (adx * Math.abs(ny1 - y1) - ady * Math.abs(nx1 - x1)); x = nx1; y = ny1; rasterizeDash(ny2 - ny1, local.createChild(ny1 - y1)); } } @Override void rasterize(int[] clip, int index) { int cx1 = clip[index + 0]; int cy1 = clip[index + 1]; int cx2 = clip[index + 2] + 1; int cy2 = clip[index + 3] + 1; int code1 = (x1 < cx1 ? 1 : 0) | (x1 >= cx2 ? 2 : 0) | (y1 < cy1 ? 8 : 0) | (y1 >= cy2 ? 4 : 0); int code2 = (x2 < cx1 ? 1 : 0) | (x2 >= cx2 ? 2 : 0) | (y2 < cy1 ? 8 : 0) | (y2 >= cy2 ? 4 : 0); // Outside if ((code1 & code2) != 0) { return; } // Inside if (code1 == 0 && code2 == 0) { rasterize(); return; } // Clip int nx1 = x1; int ny1 = y1; int nx2 = x2; int ny2 = y2; // need to clip cx1 -= x1; cx2 -= x1; cy1 -= y1; cy2 -= y1; // int d; int newx1 = 0, newy1 = 0, newx2 = 0, newy2 = 0; if (code1 != 0) { newx1 = Integer.MAX_VALUE; if ((code1 & 8) != 0) { // clip point 1 with top clip bound newy1 = cy1; newx1 = clipY(dx, dy, newy1, true); } else if ((code1 & 4) != 0) { // clip point 1 with bottom clip bound newy1 = cy2 - 1; newx1 = clipY(dx, dy, newy1, false); } if ((code1 & 1) != 0 && (cx1 > newx1 || newx1 == Integer.MAX_VALUE)) { // clip point 1 with left clip bound newx1 = cx1; newy1 = clipX(dx, dy, newx1, false); } else if ((code1 & 2) != 0 && (newx1 >= cx2 || newx1 == Integer.MAX_VALUE)) { // clip point 1 with right clip bound newx1 = cx2 - 1; newy1 = clipX(dx, dy, newx1, false); } if (newx1 < cx1 || newx1 >= cx2 || newy1 < cy1 || newy1 >= cy2) { return; } // d = 2 * (ady * Math.abs(newx1) - adx * Math.abs(newy1)) + 2 * ady - adx; } else { // d = (ady << 1) - adx; } if (code2 != 0) { newx2=Integer.MAX_VALUE; if ((code2 & 8) != 0) { // clip point 2 with top clip bound newy2 = cy1; newx2 = clipY(dx, dy, newy2, true); } else if ((code2 & 4) != 0) { // clip point 2 with bottom clip bound newy2 = cy2 - 1; newx2 = clipY(dx, dy, newy2, false); } if ((code2 & 1) != 0 && (cx1 > newx2 || newx2 == Integer.MAX_VALUE)) { // clip point 2 with left clip bound newx2 = cx1; newy2 = clipX(dx, dy, newx2, false); } else if ((code2 & 2) != 0 && (newx2 >= cx2 || newx2 == Integer.MAX_VALUE)) { // clip point 2 with right clip bound newx2 = cx2 - 1; newy2 = clipX(dx, dy, newx2, false); } if (newx2 < cx1 || newx2 >= cx2 || newy2 < cy1 || newy2 >= cy2) { return; } nx2 = x1 + newx2; ny2 = y1 + newy2; } nx1 = x1 + newx1; ny1 = y1 + newy1; rasterizeClipped(nx1, ny1, nx2, ny2); } abstract void rasterizeClipped(int nx1, int ny1, int nx2, int ny2); } static abstract class Ortog extends Line { Ortog(int x1, int y1, int x2, int y2, MultiRectArea dst) { super(x1, y1, x2, y2, dst); } static class Hor extends Ortog { int dx; Hor(int x1, int y1, int x2, int y2, MultiRectArea dst) { super(x1, y1, x2, y2, dst); dx = x2 - x1; } @Override void rasterize() { if (dx > 0) { dst.addRect(x1, y1, x2, y2); } else { dst.addRect(x2, y2, x1, y1); } } @Override void rasterize(int step) { int px = x; if (dx > 0) { x += step; dst.addRect(px, y1, x - 1, y2); } else { x -= step; dst.addRect(x + 1, y2, px, y1); } } @Override void skip(int step) { if (dx > 0) { x += step; } else { x -= step; } } void rasterizeClipped(int nx1, int nx2) { if (nx1 < nx2) { dst.addRect(nx1, y1, nx2, y1); } else { dst.addRect(nx2, y1, nx1, y1); } } @Override void rasterize(int[] clip, int index) { if (y1 >= clip[index + 1] && y1 <= clip[index + 3]) { int cx1 = clip[index + 0]; int cx2 = clip[index + 2]; if (x1 <= cx2 && x2 >= cx1) { int nx1, nx2; if (dx > 0) { nx1 = Math.max(x1, cx1); nx2 = Math.min(x2, cx2); } else { nx2 = Math.max(x2, cx1); nx1 = Math.min(x1, cx2); } rasterizeClipped(nx1, nx2); } } } } static class Ver extends Ortog { int dy; Ver(int x1, int y1, int x2, int y2, MultiRectArea dst) { super(x1, y1, x2, y2, dst); dy = y2 - y1; } @Override void rasterize() { dst.addRect(x1, y1, x2, y2); } @Override void rasterize(int step) { int py = y; y += step; dst.addRect(x1, py, x2, y - 1); } @Override void skip(int step) { y += step; } void rasterizeClipped(int ny1, int ny2) { dst.addRect(x1, ny1, x1, ny2); } @Override void rasterize(int[] clip, int index) { if (x1 >= clip[index] && x1 <= clip[index + 2]) { int cy1 = clip[index + 1]; int cy2 = clip[index + 3]; if (y1 <= cy2 && y2 >= cy1) { rasterizeClipped(Math.max(y1, cy1), Math.min(y2, cy2)); } } } } static class HorDashed extends Hor { LineDasher local; HorDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher) { super(x1, y1, x2, y2, dst); dx = x2 - x1; local = dasher.createOrtogonal(Math.abs(dx), false); } @Override void rasterize() { x = x1; y = y1; rasterizeDash(Math.abs(dx), local); } @Override void rasterizeClipped(int nx1, int nx2) { x = nx1; y = y1; rasterizeDash(Math.abs(nx2 - nx1), local.createChild(Math.abs(nx1 - x1))); } } static class VerDashed extends Ver { LineDasher local; VerDashed(int x1, int y1, int x2, int y2, MultiRectArea dst, LineDasher dasher, boolean invert) { super(x1, y1, x2, y2, dst); dy = y2 - y1; local = dasher.createOrtogonal(dy, invert); } @Override void rasterize() { x = x1; y = y1; rasterizeDash(dy, local); } @Override void rasterizeClipped(int ny1, int ny2) { x = x1; y = ny1; rasterizeDash(ny2 - ny1, local.createChild(ny1)); } } } abstract void rasterize(); abstract void rasterize(int[] clip, int index); abstract void rasterize(int count); abstract void skip(int count); void rasterizeDash(int count, LineDasher dasher) { float delta = dasher.dash[dasher.index] - dasher.phase; int step = (int)delta; delta -= step; while(count > step) { if (dasher.visible) { rasterize(step); } else { skip(step); } count -= step; delta += dasher.nextDash(); step = (int)delta; delta -= step; } if (count > 0 && dasher.visible) { rasterize(count); dasher.move(count); } } } /** * Common clipping method */ static int clip(int dX1, int dX2, int cX, boolean top) { int adX1 = dX1 < 0 ? -dX1 : dX1; int adX2 = dX2 < 0 ? -dX2 : dX2; if (adX1 <= adX2) { // obtuse intersection angle return ((dX1 << 1) * cX + (dX1 > 0 ? dX2 : -dX2)) / (dX2 << 1); } int k; if (top) { k = -dX1 + (dX2 < 0 ? 0 : dX1 > 0 ? (dX2 << 1) : -(dX2 << 1)); } else { k = dX1 + (dX2 > 0 ? 0 : dX1 > 0 ? (dX2 << 1) : -(dX2 << 1)); } k += dX1 > 0 == dX2 > 0 ? -1 : 1; return ((dX1 << 1) * cX + k) / (dX2 << 1); } /** * Clipping along X axis */ static int clipX(int dx, int dy, int cy, boolean top) { return clip(dy, dx, cy, top); } /** * Clipping along Y axis */ static int clipY(int dx, int dy, int cx, boolean top) { return clip(dx, dy, cx, top); } /** * Rasterizes line using clippind and dashing style * @param x1 - the x coordinate of the first control point * @param y1 - the y coordinate of the first control point * @param x2 - the x coordinate of the second control point * @param y2 - the y coordinate of the second control point * @param clip - the MultiRectArea object of clipping area * @param dasher - the dasher style * @param invert - the invert indicator, always false * @return a MultiRectArea of rasterizer line */ public static MultiRectArea rasterize(int x1, int y1, int x2, int y2, MultiRectArea clip, LineDasher dasher, boolean invert) { MultiRectArea dst = new MultiRectArea(false); int dx = x2 - x1; int dy = y2 - y1; // Point if (dx == 0 && dy == 0) { if ((clip == null || clip.contains(x1, y1)) && (dasher == null || dasher.visible)) { dst = new MultiRectArea(x1, y1, x1, y1); } return dst; } if (dy < 0) { return rasterize(x2, y2, x1, y1, clip, dasher, true); } Line line; if (dasher == null) { if (dx == 0) { line = new Line.Ortog.Ver(x1, y1, x2, y2, dst); } else if (dy == 0) { line = new Line.Ortog.Hor(x1, y1, x2, y2, dst); } else { if (dy < Math.abs(dx)) { line = new Line.Diag.Hor(x1, y1, x2, y2, dst); } else { line = new Line.Diag.Ver(x1, y1, x2, y2, dst); } } } else { if (dx == 0) { line = new Line.Ortog.VerDashed(x1, y1, x2, y2, dst, dasher, invert); } else if (dy == 0) { line = new Line.Ortog.HorDashed(x1, y1, x2, y2, dst, dasher); } else { if (dy < Math.abs(dx)) { line = new Line.Diag.HorDashed(x1, y1, x2, y2, dst, dasher, invert); } else { line = new Line.Diag.VerDashed(x1, y1, x2, y2, dst, dasher, invert); } } } if (clip == null || clip.isEmpty()) { line.rasterize(); } else { for(int i = 1; i < clip.rect[0]; i += 4) { line.rasterize(clip.rect, i); } } return dst; } }