ultimatepp/uppsrc/Painter/Rasterizer.cpp
2023-12-27 23:39:45 +01:00

388 lines
No EOL
7.8 KiB
C++

#include "Painter.h"
// This code is based in AGG rasterizer with this original information:
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
//
// The author gratefully acknowleges the support of David Turner,
// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
// libray - in producing this work. See http://www.freetype.org for details.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#define LLOG(x) // DLOG(x)
namespace Upp {
void Rasterizer::Create(int cx, int cy, bool subpixel)
{
Free();
mx = subpixel ? 3 * 256 : 256;
sz.cx = cx;
sz.cy = cy;
cell.Alloc(sz.cy + 1); // one more for overrun
// STATIC_ASSERT(sizeof(CellArray) == 256);
cliprect = Sizef(sz);
Init();
Reset(); // TODO: Can be removed (cell is just constructed, Init called above)
}
void Rasterizer::Free()
{
if(cell) {
for(int i = min_y; i <= max_y; i++) {
if(cell[i].alloc != SVO_ALLOC) {
MemoryFree(cell[i].ptr);
cell[i].alloc = SVO_ALLOC;
}
cell[i].count = 0;
}
if(cell[sz.cy].alloc != SVO_ALLOC) { // check overrun
MemoryFree(cell[sz.cy].ptr);
cell[sz.cy].alloc = SVO_ALLOC;
}
cell[sz.cy].count = 0;
}
}
void Rasterizer::Init()
{
p0 = Pointf(0, 0);
min_y = INT_MAX;
max_y = INT_MIN;
}
void Rasterizer::Reset()
{
Free();
Init();
}
void Rasterizer::SetClip(const Rectf& rect)
{
cliprect = rect & Sizef(sz);
}
force_inline
Rasterizer::Cell *Rasterizer::AddCells(int y, int n)
{
CellArray& a = cell[y];
if(a.count + n <= SVO_ALLOC) {
Cell *r = a.svo + a.count;
a.count += n;
return r;
}
if(a.count + n > a.alloc) {
size_t sz = sizeof(Cell) * (a.alloc + (a.alloc >> 1) + n);
Cell *cells = (Cell *)MemoryAllocSz(sz);
if(a.alloc == SVO_ALLOC)
memcpy_t(cells, a.svo, a.count);
else {
memcpy_t(cells, a.ptr, a.count);
MemoryFree(a.ptr);
}
a.ptr = cells;
a.alloc = int(sz / sizeof(Cell));
}
Cell *r = a.ptr + a.count;
a.count += n;
return r;
}
inline void Rasterizer::RenderHLine(int ey, int x1, int y1, int x2, int y2)
{
int ex1 = x1 >> 8;
int ex2 = x2 >> 8;
int fx1 = x1 & 255;
int fx2 = x2 & 255;
Cell *c;
if(y1 == y2)
return;
int dy = y2 - y1;
if(ex1 == ex2) {
c = AddCells(ey, 1);
c->x = ex1;
c->cover = dy;
c->area = (fx1 + fx2) * dy;
return;
}
int dx = x2 - x1;
if(dx < 0) {
int p = fx1 * dy;
dx = -dx;
int delta = p / dx;
int mod = p % dx;
if(mod < 0) {
delta--;
mod += dx;
}
c = AddCells(ey, abs(ex2 - ex1) + 1);
c->x = ex1;
c->cover = delta;
c->area = fx1 * delta;
c++;
ex1--;
y1 += delta;
if(ex1 != ex2) {
p = (y2 - y1 + delta) * 256;
int lift = p / dx;
int rem = p % dx;
if (rem < 0) {
lift--;
rem += dx;
}
mod -= dx;
while(ex1 != ex2) {
delta = lift;
mod += rem;
if(mod >= 0) {
mod -= dx;
delta++;
}
c->x = ex1;
c->cover = delta;
c->area = delta * 256;
c++;
y1 += delta;
ex1--;
}
}
c->x = ex2;
dy = y2 - y1;
c->cover = dy;
c->area = (fx2 + 256) * dy;
}
else {
int p = (256 - fx1) * dy;
int delta = p / dx;
int mod = p % dx;
if(mod < 0) {
delta--;
mod += dx;
}
c = AddCells(ey, abs(ex2 - ex1) + 1);
c->x = ex1;
c->cover = delta;
c->area = (fx1 + 256) * delta;
c++;
ex1++;
y1 += delta;
if(ex1 != ex2) {
p = (y2 - y1 + delta) * 256;
int lift = p / dx;
int rem = p % dx;
if (rem < 0) {
lift--;
rem += dx;
}
mod -= dx;
while(ex1 != ex2) {
delta = lift;
mod += rem;
if(mod >= 0) {
mod -= dx;
delta++;
}
c->x = ex1;
c->cover = delta;
c->area = delta * 256;
c++;
y1 += delta;
ex1++;
}
}
c->x = ex2;
dy = y2 - y1;
c->cover = dy;
c->area = fx2 * dy;
}
}
void Rasterizer::LineRaw(int x1, int y1, int x2, int y2)
{
PAINTER_TIMING("LineRaw");
LLOG("Rasterizer::LineRaw " << x1 / 256.0 << ':' << y1 / 256.0
<< " - " << x2 / 256.0 << ':' << y2 / 256.0);
int ex1 = x1 >> 8;
int ey1 = y1 >> 8;
int ey2 = y2 >> 8;
ASSERT(ey1 >= 0 && ey1 <= sz.cy && ey2 >= 0 && ey2 <= sz.cy);
if(ey1 < min_y) {
if(ey1 < 0) return;
min_y = ey1;
}
if(ey1 > max_y) {
if(ey1 > sz.cy) return;
max_y = min(ey1, sz.cy - 1);
}
if(ey2 < min_y) {
if(ey2 < 0) return;
min_y = ey2;
}
if(ey2 > max_y) {
if(ey2 > sz.cy) return;
max_y = min(ey2, sz.cy - 1);
}
enum dx_limit_e { dx_limit = 16384 << 8 };
int dx = x2 - x1;
if(dx >= dx_limit || dx <= -dx_limit) {
int cx = (x1 + x2) >> 1;
int cy = (y1 + y2) >> 1;
LineRaw(x1, y1, cx, cy);
LineRaw(cx, cy, x2, y2);
return;
}
int dy = y2 - y1;
int fy1 = y1 & 255;
int fy2 = y2 & 255;
Cell *c;
int x_from, x_to;
int p, rem, mod, lift, delta, first, incr;
if(ey1 == ey2) {
RenderHLine(ey1, x1, fy1, x2, fy2);
return;
}
incr = 1;
if(dx == 0) {
int two_fx = (x1 - (ex1 << 8)) << 1;
int area;
first = 256;
if(dy < 0) {
first = 0;
incr = -1;
}
x_from = x1;
delta = first - fy1;
c = AddCells(ey1, 1);
c->x = ex1;
c->cover = delta;
c->area = two_fx * delta;
ey1 += incr;
delta = first + first - 256;
area = two_fx * delta;
while(ey1 != ey2) {
c = AddCells(ey1, 1);
c->x = ex1;
c->cover = delta;
c->area = area;
ey1 += incr;
}
delta = fy2 - 256 + first;
c = AddCells(ey1, 1);
c->x = ex1;
c->cover = delta;
c->area = two_fx * delta;
return;
}
p = (256 - fy1) * dx;
first = 256;
if(dy < 0) {
p = fy1 * dx;
first = 0;
incr = -1;
dy = -dy;
}
delta = p / dy;
mod = p % dy;
if(mod < 0) {
delta--;
mod += dy;
}
x_from = x1 + delta;
RenderHLine(ey1, x1, fy1, x_from, first);
ey1 += incr;
if(ey1 != ey2) {
p = dx << 8;
lift = p / dy;
rem = p % dy;
if(rem < 0) {
lift--;
rem += dy;
}
mod -= dy;
while(ey1 != ey2) {
delta = lift;
mod += rem;
if(mod >= 0) {
mod -= dy;
delta++;
}
x_to = x_from + delta;
RenderHLine(ey1, x_from, 256 - first, x_to, first);
x_from = x_to;
ey1 += incr;
}
}
RenderHLine(ey1, x_from, 256 - first, x2, fy2);
}
void Rasterizer::Filler::End() {}
void Rasterizer::Render(int y, Rasterizer::Filler& g, bool evenodd)
{
PAINTER_TIMING("Render");
CellArray& a = cell[y];
if(a.count == 0)
return;
Cell *c = a.Get();
Cell *e = c + a.count;
Sort(SubRange(c, e));
g.Start(c->x, (e - 1)->x);
int cover = 0;
while(c < e) {
int x = c->x;
int area = c->area;
cover += c->cover;
c++;
while(c < e && c->x == x) {
area += c->area;
cover += c->cover;
c++;
}
if(evenodd) {
if(area) {
int h = abs(((cover << 9) - area) >> 9) & 511;
g.Render(h > 256 ? 512 - h : h);
x++;
}
if(c < e && c->x > x) {
int h = abs(cover) & 511;
g.Render(h > 256 ? 512 - h : h, c->x - x);
}
}
else {
if(area) {
g.Render(min(abs(((cover << 9) - area) >> 9), 256));
x++;
}
if(c < e && c->x > x)
g.Render(min(abs(cover), 256), c->x - x);
}
}
g.End();
}
}