#include "ao_draw.h"
#include "ao_draw_int.h"
#include <stdio.h>
+#include <math.h>
+#include <float.h>
+
+const struct ao_transform ao_identity = {
+ .x_scale = 1.0f, .x_off = 0.0f,
+ .y_scale = 1.0f, .y_off = 0.0f
+};
+
+static float
+_x(const struct ao_coord *coords,
+ const struct ao_transform *transform,
+ uint16_t coord)
+{
+ return ao_t_x_c(&coords[coord], transform);
+}
+
+static float
+_y(const struct ao_coord *coords,
+ const struct ao_transform *transform,
+ uint16_t coord)
+{
+ return ao_t_y_c(&coords[coord], transform);
+}
+
+static uint16_t
+_next(uint16_t ncoords, uint16_t edge)
+{
+ return edge == ncoords - 1 ? 0 : edge + 1;
+}
/*
* Return if the given edge is 'live' at the specified y coordinate.
static bool
ao_edge_live(const struct ao_coord *coords,
uint16_t ncoords,
+ const struct ao_transform *transform,
uint16_t edge,
- int16_t y)
+ float y)
{
- int next_edge = (edge == ncoords - 1) ? 0 : edge + 1;
- int16_t y1 = coords[edge].y;
- int16_t y2 = coords[next_edge].y;
+ float y1 = _y(coords, transform, edge);
+ float y2 = _y(coords, transform, _next(ncoords, edge));
if (y1 > y2)
return y2 <= y && y < y1;
* Compute the X coordinate for a given edge at a specified y value
*/
static int16_t
-ao_edge_x(const struct ao_coord *coords,
- uint16_t ncoords,
- uint16_t edge,
- int16_t y)
+ao_edge_x(const struct ao_coord *coords,
+ uint16_t ncoords,
+ const struct ao_transform *transform,
+ uint16_t edge,
+ float y)
{
- int next_edge = (edge == ncoords - 1) ? 0 : edge + 1;
- int16_t x1 = coords[edge].x;
- int16_t x2 = coords[next_edge].x;
- int16_t y1 = coords[edge].y;
- int16_t y2 = coords[next_edge].y;
- int16_t dx = x2 - x1;
- int16_t dy = y2 - y1;
- int16_t off_y = y - y1;
-
- return x1 + (off_y * dx) / dy;
+ uint16_t next_edge = _next(ncoords, edge);
+ float x1 = _x(coords, transform, edge);
+ float x2 = _x(coords, transform, next_edge);
+ float y1 = _y(coords, transform, edge);
+ float y2 = _y(coords, transform, next_edge);
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+ float off_y = y - y1;
+
+ return (int16_t) (x1 + (off_y * dx) / dy + 0.5f);
}
struct next_x {
- int16_t x;
+ float x;
uint16_t edge;
};
* if there are no more edges.
*/
static bool
-ao_next_x(const struct ao_coord *coords,
- uint16_t ncoords,
- struct next_x *this_x,
- int16_t y)
+ao_next_x(const struct ao_coord *coords,
+ uint16_t ncoords,
+ const struct ao_transform *transform,
+ struct next_x *this_x,
+ float y)
{
uint16_t edge;
- int16_t next_x = INT16_MAX;
+ float next_x = FLT_MAX;
uint16_t next_edge = UINT16_MAX;
bool ret = false;
for (edge = 0; edge < ncoords; edge++) {
- if (ao_edge_live(coords, ncoords, edge, y)) {
- int16_t nx = ao_edge_x(coords, ncoords, edge, y);
+ if (ao_edge_live(coords, ncoords, transform, edge, y)) {
+ float nx = ao_edge_x(coords, ncoords, transform, edge, y);
if (this_x->x < nx || (this_x->x == nx && this_x->edge < edge)) {
if (nx < next_x) {
next_x = nx;
*/
static void
ao_span(const struct ao_bitmap *dst,
- int16_t x1,
- int16_t x2,
- int16_t y,
+ float x1,
+ float x2,
+ float y,
uint32_t fill,
uint8_t rop)
{
- ao_rect(dst, x1, y, x2 - x1, 1, fill, rop);
+ int16_t ix1 = (int16_t) floorf(x1 + 0.5f);
+ int16_t ix2 = (int16_t) floorf(x2 + 0.5f);
+ int16_t iy = (int16_t) y;
+ ao_rect(dst, ix1, iy, ix2 - ix1, 1, fill, rop);
}
/*
* indication of whether the edge goes upwards or downwards
*/
static int
-ao_wind(const struct ao_coord *coords,
- uint16_t ncoords,
- uint16_t edge)
+ao_wind(const struct ao_coord *coords,
+ uint16_t ncoords,
+ const struct ao_transform *transform,
+ uint16_t edge)
{
- uint16_t next_edge = (edge == ncoords - 1) ? 0 : edge + 1;
- return coords[edge].y > coords[next_edge].y ? 1 : -1;
+ uint16_t next_edge = _next(ncoords, edge);
+ return _y(coords, transform, edge) > _y(coords, transform, next_edge) ? 1 : -1;
}
/*
*/
void
ao_poly(const struct ao_bitmap *dst,
- const struct ao_coord *coords,
- uint16_t ncoords,
- uint32_t fill,
+ const struct ao_coord *coords,
+ uint16_t ncoords,
+ const struct ao_transform *transform,
+ uint32_t fill,
uint8_t rop)
{
- int16_t y_min, y_max;
+ float y_min, y_max;
uint16_t edge;
- int16_t y;
- int16_t x;
+ float y;
+ float x;
struct next_x next_x;
int wind;
+ if (!transform)
+ transform = &ao_identity;
+
/*
* Find the y limits of the polygon
*/
- y_min = y_max = coords[0].y;
+ y_min = y_max = _y(coords, transform, 0);
for (edge = 1; edge < ncoords; edge++) {
- y = coords[edge].y;
+ y = _y(coords, transform, edge);
if (y < y_min)
y_min = y;
else if (y > y_max)
y_max = y;
}
+ y_min = floorf(y_min);
+ y_max = ceilf(y_max);
+
/*
* Walk each scanline in the range and fill included spans
*/
next_x.x = INT16_MIN;
next_x.edge = 0;
wind = 0;
- while (ao_next_x(coords, ncoords, &next_x, y)) {
+ while (ao_next_x(coords, ncoords, transform, &next_x, y)) {
/*
* Fill the previous span if winding is
ao_span(dst, x, next_x.x, y, fill, rop);
/* Adjust winding for the current span */
- wind += ao_wind(coords, ncoords, next_x.edge);
+ wind += ao_wind(coords, ncoords, transform, next_x.edge);
/* Step to next span start x value */
x = next_x.x;