blob: b643b41acb26ff4db4f8889786346f97e6e948b6 [file] [log] [blame]
/*
* 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 JavaArcRasterizer {
/**
* Adds particular arc segment to mra
*/
static void addX0LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int x1 = 0;
for(int i = 0; i < line.length; i++) {
int x2 = line[i];
int y = cy + (b - i);
if (x1 <= finish && x2 >= start) {
mra.addRect(cx + Math.max(x1, start), y, cx + Math.min(x2, finish), y);
}
x1 = x2 + 1;
}
}
static void addX1LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int x1 = 0;
for(int i = 0; i < line.length; i++) {
int x2 = line[i];
int y = cy - (b - i);
if (x1 <= finish && x2 >= start) {
mra.addRect(cx + Math.max(x1, start), y, cx + Math.min(x2, finish), y);
}
x1 = x2 + 1;
}
}
static void addX2LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int x1 = 0;
for(int i = 0; i < line.length; i++) {
int x2 = line[i];
int y = cy - (b - i);
if (x1 <= finish && x2 >= start) {
mra.addRect(cx - Math.min(x2, finish), y, cx - Math.max(x1, start), y);
}
x1 = x2 + 1;
}
}
static void addX3LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int x1 = 0;
for(int i = 0; i < line.length; i++) {
int x2 = line[i];
int y = cy + (b - i);
if (x1 <= finish && x2 >= start) {
mra.addRect(cx - Math.min(x2, finish), y, cx - Math.max(x1, start), y);
}
x1 = x2 + 1;
}
}
static void addY0LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int y1 = 0;
for(int i = 0; i < line.length; i++) {
int x = cx + (b - i);
int y2 = line[i];
if (y1 <= finish && y2 >= start) {
mra.addRect(x, cy + Math.max(y1, start), x, cy + Math.min(y2, finish));
}
y1 = y2 + 1;
}
}
static void addY1LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int y1 = 0;
for(int i = 0; i < line.length; i++) {
int x = cx - (b - i);
int y2 = line[i];
if (y1 <= finish && y2 >= start) {
mra.addRect(x, cy + Math.max(y1, start), x, cy + Math.min(y2, finish));
}
y1 = y2 + 1;
}
}
static void addY2LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int y1 = 0;
for(int i = 0; i < line.length; i++) {
int x = cx - (b - i);
int y2 = line[i];
if (y1 <= finish && y2 >= start) {
mra.addRect(x, cy - Math.min(y2, finish), x, cy - Math.max(y1, start));
}
y1 = y2 + 1;
}
}
static void addY3LineSeg(MultiRectArea mra, int[] line, int cx, int cy, int b, int start, int finish) {
int y1 = 0;
for(int i = 0; i < line.length; i++) {
int x = cx + (b - i);
int y2 = line[i];
if (y1 <= finish && y2 >= start) {
mra.addRect(x, cy - Math.min(y2, finish), x, cy - Math.max(y1, start));
}
y1 = y2 + 1;
}
}
static void addX0Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx + prev, cy + (b - i), cx + line[i], cy + (b - i));
prev = line[i] + 1;
}
}
static void addX1Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx + prev, cy - (b - i), cx + line[i], cy - (b - i));
prev = line[i] + 1;
}
}
static void addX2Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx - line[i], cy - (b - i), cx - prev, cy - (b - i));
prev = line[i] + 1;
}
}
static void addX3Line(MultiRectArea mra, int[] line, int cx, int cy, int b) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx - line[i], cy + (b - i), cx - prev, cy + (b - i));
prev = line[i] + 1;
}
}
static void addY0Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx + (a - i), cy + prev, cx + (a - i), cy + line[i]);
prev = line[i] + 1;
}
}
static void addY1Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx - (a - i), cy + prev, cx - (a - i), cy + line[i]);
prev = line[i] + 1;
}
}
static void addY2Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx - (a - i), cy - line[i], cx - (a - i), cy - prev);
prev = line[i] + 1;
}
}
static void addY3Line(MultiRectArea mra, int[] line, int cx, int cy, int a) {
int prev = 0;
for(int i = 0; i < line.length; i++) {
mra.addRect(cx + (a - i), cy - line[i], cx + (a - i), cy - prev);
prev = line[i] + 1;
}
}
/**
* Returns normalized angle (from 0 to 360 degrees)
*/
static double getNormAngle(double angle) {
angle -= Math.floor(angle / 360) * 360;
if (angle < 0) {
angle += 360;
}
return angle;
}
/**
* Creates arc lookup table
*/
static int[] createLine(int a, int b, int xcount, int ycount) {
int[] buf = new int[b - ycount + 1];
int d = a * a + 2 * b * b - 2 * a * a * b;
int x = 0;
int y = b;
while (y >= ycount) {
if (d < 0) {
d = d + b * b * (4 * x + 6);
} else {
buf[b - y] = x;
d = d + b * b * (4 * x + 6) + 4 * a * a * (1 - y);
y--;
}
x++;
}
return buf;
}
/**
* Adds head/tail arc segment to MultiRectArea
*/
static void addSeg(MultiRectArea mra, int cx1, int cy1, int cx2, int cy2, int a, int b, int[] xline, int[] yline, int[] bounds) {
switch(bounds[0]) {
case 0:
addY3LineSeg(mra, yline, cx2, cy1, a, bounds[1], bounds[2]);
break;
case 1:
addX1LineSeg(mra, xline, cx2, cy1, b, bounds[1], bounds[2]);
break;
case 2:
addX2LineSeg(mra, xline, cx1, cy1, b, bounds[1], bounds[2]);
break;
case 3:
addY2LineSeg(mra, yline, cx1, cy1, a, bounds[1], bounds[2]);
break;
case 4:
addY1LineSeg(mra, yline, cx1, cy2, a, bounds[1], bounds[2]);
break;
case 5:
addX3LineSeg(mra, xline, cx1, cy2, b, bounds[1], bounds[2]);
break;
case 6:
addX0LineSeg(mra, xline, cx2, cy2, b, bounds[1], bounds[2]);
break;
case 7:
addY0LineSeg(mra, yline, cx2, cy2, a, bounds[1], bounds[2]);
break;
}
}
/**
* Returns bounds for non quadratic arc head
*/
static int[] getSegment1(double angle, int ax, int ay, int xcount, int ycount) {
int[] bounds = new int[3];
switch((int)(angle / 90)) {
case 0:
if (xcount < ax) {
bounds[0] = 0; // Y3
bounds[1] = -ay;
bounds[2] = ycount;
} else {
bounds[0] = 1; // X1
bounds[1] = 0;
bounds[2] = ax;
}
break;
case 1:
if (xcount > -ax) {
bounds[0] = 2; // X2
bounds[1] = -ax;
bounds[2] = xcount;
} else {
bounds[0] = 3; // Y2
bounds[1] = 0;
bounds[2] = -ay;
}
break;
case 2:
if (xcount < -ax) {
bounds[0] = 4; // Y1
bounds[1] = ay;
bounds[2] = ycount;
} else {
bounds[0] = 5; // X3
bounds[1] = 0;
bounds[2] = -ax;
}
break;
case 3:
if (xcount > ax) {
bounds[0] = 6; // X0
bounds[1] = ax;
bounds[2] = xcount;
} else {
bounds[0] = 7; // Y0
bounds[1] = 0;
bounds[2] = ay;
}
break;
}
return bounds;
}
/**
* Returns bounds for non quadratic arc tail
*/
static int[] getSegment2(double angle, int ax, int ay, int xcount, int ycount) {
int[] bounds = new int[3];
switch((int)(angle / 90)) {
case 0:
if (xcount < ax) {
bounds[0] = 0; // Y3
bounds[1] = 0;
bounds[2] = -ay;
} else {
bounds[0] = 1; // X1
bounds[1] = ax;
bounds[2] = xcount;
}
break;
case 1:
if (xcount > -ax) {
bounds[0] = 2; // X2
bounds[1] = 0;
bounds[2] = -ax;
} else {
bounds[0] = 3; // Y2
bounds[1] = -ay;
bounds[2] = ycount;
}
break;
case 2:
if (xcount < -ax) {
bounds[0] = 4; // Y1
bounds[1] = 0;
bounds[2] = ay;
} else {
bounds[0] = 5; // X3
bounds[1] = -ax;
bounds[2] = xcount;
}
break;
case 3:
if (xcount > ax) {
bounds[0] = 6; // X0
bounds[1] = 0;
bounds[2] = ax;
} else {
bounds[0] = 7; // Y0
bounds[1] = ay;
bounds[2] = ycount;
}
break;
}
return bounds;
}
/**
* Rasterizes arc using clippind and dashing style
* @param x1 - the x coordinate of the left-upper corner of the arc bounds
* @param y1 - the y coordinate of the left-upper corner of the arc bounds
* @param width - the width of the arc bounds
* @param height - the height of the arc bounds
* @param angleStart - the start angle of the arc in degrees
* @param angleExtent - the angle extent in degrees
* @param clip - the MultiRectArea object of clipping area
* @return a MultiRectArea of rasterizer arc
*/
public static MultiRectArea rasterize(int x, int y, int width, int height, double angleStart, double angleExtent, MultiRectArea clip) {
MultiRectArea mra = new MultiRectArea(false);
int cx1, cx2, cy1, cy2;
cx1 = cx2 = x + width / 2;
cy1 = cy2 = y + height / 2;
if (width % 2 == 0) {
cx2--;
}
if (height % 2 == 0) {
cy2--;
}
int a = width / 2;
int b = height / 2;
double c = Math.sqrt(a * a + b * b);
int xcount, ycount;
if (a < b) {
xcount = (int)Math.ceil(a * a / c);
ycount = (int)Math.floor(b * b / c);
} else {
xcount = (int)Math.floor(a * a / c);
ycount = (int)Math.ceil(b * b / c);
}
int[] xline = createLine(a, b, xcount, ycount);
int[] yline = createLine(b, a, ycount, xcount);
// Correct lines
int i = xline.length;
while(xline[--i] > xcount) {
xline[i] = xcount;
}
i = yline.length;
while(yline[--i] > ycount) {
yline[i] = ycount;
}
if (Math.abs(angleExtent) >= 360) {
// Rasterize CIRCLE
addX0Line(mra, xline, cx2, cy2, b);
addX1Line(mra, xline, cx2, cy1, b);
addX2Line(mra, xline, cx1, cy1, b);
addX3Line(mra, xline, cx1, cy2, b);
addY0Line(mra, yline, cx2, cy2, a);
addY1Line(mra, yline, cx1, cy2, a);
addY2Line(mra, yline, cx1, cy1, a);
addY3Line(mra, yline, cx2, cy1, a);
} else {
// Rasterize ARC
angleStart = getNormAngle(angleStart);
double angleFinish = getNormAngle(angleStart + angleExtent);
if (angleExtent < 0) {
double tmp = angleStart;
angleStart = angleFinish;
angleFinish = tmp;
}
double radStart = -Math.toRadians(angleStart);
double radFinish = -Math.toRadians(angleFinish);
int ax1 = (int)(a * Math.cos(radStart));
int ay1 = (int)(b * Math.sin(radStart));
int ax2 = (int)(a * Math.cos(radFinish));
int ay2 = (int)(b * Math.sin(radFinish));
int[] seg1 = getSegment1(angleStart, ax1, ay1, xcount, ycount);
int[] seg2 = getSegment2(angleFinish, ax2, ay2, xcount, ycount);
// Start and Finish located in the same quater
if (angleStart < angleFinish && seg1[0] == seg2[0]) {
if (seg1[0] % 2 == 0) {
seg1[2] = seg2[2];
} else {
seg1[1] = seg2[1];
}
addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg1);
return mra;
}
addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg1);
addSeg(mra, cx1, cy1, cx2, cy2, a, b, xline, yline, seg2);
int startSeg = (seg1[0] + 1) % 8;
int finishSeg = seg2[0];
while (startSeg != finishSeg) {
switch(startSeg) {
case 0:
addY3Line(mra, yline, cx2, cy1, a);
break;
case 1:
addX1Line(mra, xline, cx2, cy1, b);
break;
case 2:
addX2Line(mra, xline, cx1, cy1, b);
break;
case 3:
addY2Line(mra, yline, cx1, cy1, a);
break;
case 4:
addY1Line(mra, yline, cx1, cy2, a);
break;
case 5:
addX3Line(mra, xline, cx1, cy2, b);
break;
case 6:
addX0Line(mra, xline, cx2, cy2, b);
break;
case 7:
addY0Line(mra, yline, cx2, cy2, a);
break;
}
startSeg = (startSeg + 1) % 8;
}
}
if (clip != null) {
mra.intersect(clip);
}
return mra;
}
}