/*
......... 2015 Ivan Mahonin
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "swrender.h"
using namespace std;
void SwRender::fill(
Surface &target,
const Color &color )
{
for(Color *i = target.data, *end = i + target.count(); i != end; ++i)
*i = color;
}
void SwRender::fill(
Surface &target,
const Color &color,
int left,
int top,
int width,
int height )
{
int step = target.width - width;
for(Color *i = &target[top][left], *end = i + height*target.width; i < end; i += step)
for(Color *rowend = i + width; i < rowend; ++i)
*i = color;
}
void SwRender::row(
Surface &target,
const Color &color,
int left,
int top,
int length )
{
for(Color *i = &target[top][left], *end = i + length; i < end; ++i)
*i = color;
}
void SwRender::row_alpha(
Surface &target,
const Color &color,
Color::type alpha,
int left,
int top,
int length )
{
for(Color *i = &target[top][left], *end = i + length; i < end; ++i) {
i->r = i->r*(1.f - alpha) + color.r*alpha;
i->g = i->g*(1.f - alpha) + color.g*alpha;
i->b = i->b*(1.f - alpha) + color.b*alpha;
i->a = i->a*(1.f - alpha) + color.a*alpha;
}
}
void SwRender::polyspan(
Surface &target,
const Polyspan &polyspan,
const Color &color,
bool evenodd,
bool invert )
{
const ContextRect &window = polyspan.get_window();
const Polyspan::cover_array &covers = polyspan.get_covers();
Polyspan::cover_array::const_iterator cur_mark = covers.begin();
Polyspan::cover_array::const_iterator end_mark = covers.end();
Real cover = 0, area = 0, alpha = 0;
int y = 0, x = 0;
if (cur_mark == end_mark) {
// no marks at all
if (invert)
fill( target, color,
window.minx, window.miny,
window.maxx - window.minx, window.maxy - window.miny );
return;
}
// fill initial rect / line
if (invert) {
// fill all the area above the first vertex
y = window.miny;
int l = window.maxx - window.minx;
fill( target, color,
window.minx, window.miny,
l, cur_mark->y - window.miny );
// fill the area to the left of the first vertex on that line
l = cur_mark->x - window.minx;
if (l)
row(target, color, window.minx, cur_mark->y, l);
}
while(true) {
y = cur_mark->y;
x = cur_mark->x;
area = cur_mark->area;
cover += cur_mark->cover;
// accumulate for the current pixel
while(++cur_mark != covers.end()) {
if (y != cur_mark->y || x != cur_mark->x)
break;
area += cur_mark->area;
cover += cur_mark->cover;
}
// draw pixel - based on covered area
if (area) { // if we're ok, draw the current pixel
alpha = polyspan.extract_alpha(cover - area, evenodd);
if (invert) alpha = 1 - alpha;
if (alpha) {
Color::type a = (Color::type)alpha;
Color &c = target[y][x];
c.r = c.r*(1.f - a) + color.r*a;
c.g = c.g*(1.f - a) + color.g*a;
c.b = c.b*(1.f - a) + color.b*a;
c.a = c.a*(1.f - a) + color.a*a;
}
++x;
}
// if we're done, don't use iterator and exit
if (cur_mark == end_mark)
break;
// if there is no more live pixels on this line, goto next
if (y != cur_mark->y) {
if (invert) {
// fill the area at the end of the line
row(target, color, x, y, window.maxx - x);
// fill area at the beginning of the next line
row(target, color, window.minx, cur_mark->y, cur_mark->x - window.minx);
}
cover = 0;
continue;
}
// draw span to next pixel - based on total amount of pixel cover
if (x < cur_mark->x) {
alpha = polyspan.extract_alpha(cover, evenodd);
if (invert) alpha = 1.0 - alpha;
if (alpha)
row_alpha(target, color, alpha, x, y, cur_mark->x - x);
}
}
// fill the after stuff
if (invert) {
// fill the area at the end of the line
row(target, color, x, y, window.maxx - x);
// fill area at the beginning of the next line
fill( target, color,
window.minx, y+1,
window.maxx - window.minx, window.maxy - y - 1 );
}
}