/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp 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 for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.graphics.RE.software;
import static jpcsp.util.Utilities.maxInt;
import static jpcsp.util.Utilities.minInt;
/**
* @author gid15
*
*/
public class Rasterizer {
// There is no advantage of using a Rasterizer when rendering an area smaller
// than the given width and height.
// The overhead of the Rasterizer would be too high.
public static final int MINIMUM_WIDTH = 4;
public static final int MINIMUM_HEIGHT = 4;
private Edge[] edges = new Edge[3];
private int longEdge;
private int shortEdge1;
private int shortEdge2;
private int currentEdge;
float xdiff1;
float xdiff2;
float factor1;
float factorStep1;
float factor2;
float factorStep2;
float y2;
public Rasterizer(float x1, float y1, float x2, float y2, float x3, float y3, int yMin, int yMax) {
edges[0] = new Edge(x1, y1, x2, y2);
edges[1] = new Edge(x2, y2, x3, y3);
edges[2] = new Edge(x3, y3, x1, y1);
longEdge = 0;
float maxLength = edges[longEdge].getLengthY();
for (int i = 1; i < edges.length; i++) {
float length = edges[i].getLengthY();
if (length > maxLength) {
maxLength = length;
longEdge = i;
}
}
shortEdge1 = (longEdge + 1) % edges.length;
shortEdge2 = (longEdge + 2) % edges.length;
if (edges[shortEdge1].y1 > edges[shortEdge2].y1) {
// Switch short edges to start with the one with the lowest "y"
int tmp = shortEdge1;
shortEdge1 = shortEdge2;
shortEdge2 = tmp;
}
currentEdge = shortEdge1;
if (!init(longEdge, currentEdge)) {
currentEdge = shortEdge2;
init(longEdge, currentEdge);
}
}
public void setY(float y) {
if (y == y2) {
return;
}
if (currentEdge == shortEdge1 && y > edges[currentEdge].y2) {
currentEdge = shortEdge2;
init(longEdge, currentEdge);
if (y2 >= y) {
return;
}
}
float diff = y - y2;
factor1 += diff * factorStep1;
factor2 += diff * factorStep2;
y2 = y;
}
private boolean init(int edge1, int edge2) {
if (edges[edge2].getLengthY() <= 0) {
y2 = edges[edge2].y2;
return false;
}
float ydiff1 = edges[edge1].y2 - edges[edge1].y1;
float ydiff2 = edges[edge2].y2 - edges[edge2].y1;
xdiff1 = edges[edge1].x2 - edges[edge1].x1;
xdiff2 = edges[edge2].x2 - edges[edge2].x1;
factor1 = (edges[edge2].y1 - edges[edge1].y1) / ydiff1;
factorStep1 = 1.f / ydiff1;
factor2 = 0.f;
factorStep2 = 1.f / ydiff2;
y2 = edges[edge2].y1;
return true;
}
public void getNextRange(Range range) {
if (y2 >= edges[currentEdge].y2) {
if (currentEdge == shortEdge2) {
range.clear();
return;
}
currentEdge = shortEdge2;
if (!init(longEdge, currentEdge)) {
range.clear();
return;
}
}
final int edge1 = longEdge;
final int edge2 = currentEdge;
float x1 = edges[edge1].x1 + xdiff1 * factor1;
float x2 = edges[edge2].x1 + xdiff2 * factor2;
factor1 += factorStep1;
factor2 += factorStep2;
range.setRange(x1, x2);
y2++;
}
public static class Range {
public int xMin;
public int xMax;
public void clear() {
xMin = 0;
xMax = 0;
}
public void setRange(float x1, float x2) {
xMin = minInt(x1, x2); // minimum value rounded down
xMax = maxInt(x1, x2); // maximum value rounded up
}
@Override
public String toString() {
return String.format("[%d-%d]", xMin, xMax);
}
}
private static class Edge {
protected float x1, y1;
protected float x2, y2;
public Edge(float x1, float y1, float x2, float y2) {
if (y1 <= y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
} else {
this.x1 = x2;
this.y1 = y2;
this.x2 = x1;
this.y2 = y1;
}
}
public float getLengthY() {
return y2 - y1;
}
@Override
public String toString() {
return String.format("(%d,%d)-(%d,%d)", x1, y1, x2, y2);
}
}
}