mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Refactoring Rasterizer
git-svn-id: svn://ultimatepp.org/upp/trunk@820 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
3cf347c47e
commit
d2d643e416
6 changed files with 546 additions and 0 deletions
257
uppdev/ScanLine/Rasterizer.cpp
Normal file
257
uppdev/ScanLine/Rasterizer.cpp
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#include "ScanLine.h"
|
||||
|
||||
Rasterizer::Rasterizer(int cy)
|
||||
{
|
||||
min_y = INT_MAX;
|
||||
max_y = INT_MIN;
|
||||
finish = true;
|
||||
current.Init();
|
||||
cell.Alloc(cy);
|
||||
}
|
||||
|
||||
inline void Rasterizer::AddCurrent()
|
||||
{
|
||||
if(current.area | current.cover)
|
||||
cell[current_y].Add(current);
|
||||
}
|
||||
|
||||
inline void Rasterizer::SetCurrent(int x, int y)
|
||||
{
|
||||
if(current.x != x || current_y != y) {
|
||||
AddCurrent();
|
||||
current.x = x;
|
||||
current_y = y;
|
||||
current.cover = current.area = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if(y1 == y2) {
|
||||
SetCurrent(ex2, ey);
|
||||
return;
|
||||
}
|
||||
if(ex1 == ex2) {
|
||||
int delta = y2 - y1;
|
||||
current.cover += delta;
|
||||
current.area += (fx1 + fx2) * delta;
|
||||
return;
|
||||
}
|
||||
int p = (256 - fx1) * (y2 - y1);
|
||||
int first = 256;
|
||||
int incr = 1;
|
||||
int dx = x2 - x1;
|
||||
if(dx < 0) {
|
||||
p = fx1 * (y2 - y1);
|
||||
first = 0;
|
||||
incr = -1;
|
||||
dx = -dx;
|
||||
}
|
||||
int delta = p / dx;
|
||||
int mod = p % dx;
|
||||
if(mod < 0) {
|
||||
delta--;
|
||||
mod += dx;
|
||||
}
|
||||
current.cover += delta;
|
||||
current.area += (fx1 + first) * delta;
|
||||
ex1 += incr;
|
||||
SetCurrent(ex1, ey);
|
||||
y1 += delta;
|
||||
if(ex1 != ex2) {
|
||||
p = (y2 - y1 + delta) << 8;
|
||||
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++;
|
||||
}
|
||||
current.cover += delta;
|
||||
current.area += delta << 8;
|
||||
y1 += delta;
|
||||
ex1 += incr;
|
||||
SetCurrent(ex1, ey);
|
||||
}
|
||||
}
|
||||
delta = y2 - y1;
|
||||
current.cover += delta;
|
||||
current.area += (fx2 + 256 - first) * delta;
|
||||
}
|
||||
|
||||
void Rasterizer::Line(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
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;
|
||||
Line(x1, y1, cx, cy);
|
||||
Line(cx, cy, x2, y2);
|
||||
return;
|
||||
}
|
||||
int dy = y2 - y1;
|
||||
int ex1 = x1 >> 8;
|
||||
int ex2 = x2 >> 8;
|
||||
int ey1 = y1 >> 8;
|
||||
int ey2 = y2 >> 8;
|
||||
int fy1 = y1 & 255;
|
||||
int fy2 = y2 & 255;
|
||||
|
||||
int x_from, x_to;
|
||||
int p, rem, mod, lift, delta, first, incr;
|
||||
|
||||
if(ey1 < min_y) min_y = ey1;
|
||||
if(ey1 > max_y) max_y = ey1;
|
||||
if(ey2 < min_y) min_y = ey2;
|
||||
if(ey2 > max_y) max_y = ey2;
|
||||
|
||||
SetCurrent(ex1, ey1);
|
||||
|
||||
if(ey1 == ey2) {
|
||||
RenderHLine(ey1, x1, fy1, x2, fy2);
|
||||
return;
|
||||
}
|
||||
incr = 1;
|
||||
if(dx == 0) {
|
||||
int ex = x1 >> 8;
|
||||
int two_fx = (x1 - (ex << 8)) << 1;
|
||||
int area;
|
||||
first = 256;
|
||||
if(dy < 0) {
|
||||
first = 0;
|
||||
incr = -1;
|
||||
}
|
||||
x_from = x1;
|
||||
delta = first - fy1;
|
||||
current.cover += delta;
|
||||
current.area += two_fx * delta;
|
||||
ey1 += incr;
|
||||
SetCurrent(ex, ey1);
|
||||
delta = first + first - 256;
|
||||
area = two_fx * delta;
|
||||
while(ey1 != ey2) {
|
||||
current.cover = delta;
|
||||
current.area = area;
|
||||
ey1 += incr;
|
||||
SetCurrent(ex, ey1);
|
||||
}
|
||||
delta = fy2 - 256 + first;
|
||||
current.cover += delta;
|
||||
current.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;
|
||||
SetCurrent(x_from >> 8, ey1);
|
||||
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;
|
||||
SetCurrent(x_from >> 8, ey1);
|
||||
}
|
||||
}
|
||||
RenderHLine(ey1, x_from, 256 - first, x2, fy2);
|
||||
}
|
||||
|
||||
inline unsigned Alpha(int area)
|
||||
{
|
||||
int cover = area >> (2 * 8 + 1 - 8);
|
||||
if(cover < 0) cover = -cover;
|
||||
/* if(evenodd) {
|
||||
cover &= 511;
|
||||
if(cover > 256)
|
||||
cover = 512 - cover;
|
||||
}*/
|
||||
if(cover > 255) cover = 255;
|
||||
return (cover + 1) / 2;
|
||||
}
|
||||
|
||||
ScanLine Rasterizer::Get(int y)
|
||||
{
|
||||
if(finish) {
|
||||
AddCurrent();
|
||||
finish = false;
|
||||
}
|
||||
ScanLine r;
|
||||
r.len = 0;
|
||||
r.x = 0;
|
||||
Vector<Cell>& cl = cell[y];
|
||||
Sort(cl);
|
||||
if(y < min_y || y > max_y || cl.GetCount() == 0) return r;
|
||||
const Cell *c = cl;
|
||||
const Cell *e = cl.End();
|
||||
r.x = c->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(area)
|
||||
r.data.Cat(Alpha((cover << (8 + 1)) - area));
|
||||
else
|
||||
r.data.Cat(0);
|
||||
r.len++;
|
||||
x++;
|
||||
if(c < e && c->x > x) {
|
||||
byte val = Alpha(cover << (8 + 1));
|
||||
int n = c->x - x;
|
||||
r.len += n;
|
||||
while(n > 0) {
|
||||
int q = min(n, 127);
|
||||
r.data.Cat(q + 128);
|
||||
r.data.Cat(val);
|
||||
n -= q;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
135
uppdev/ScanLine/ScanLine.cpp
Normal file
135
uppdev/ScanLine/ScanLine.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#include "ScanLine.h"
|
||||
|
||||
ScanLine And0(const ScanLine& a, const ScanLine& b)
|
||||
{
|
||||
ScanLine r;
|
||||
r.len = 0;
|
||||
r.x = b.x;
|
||||
byte aval = 0;
|
||||
int acount = 0;
|
||||
byte bval = 0;
|
||||
int ai = 0;
|
||||
int bi = 0;
|
||||
int bcount = b.x - a.x;
|
||||
while(bcount) {
|
||||
if(ai >= a.data.GetCount())
|
||||
return r;
|
||||
aval = a.data[ai++];
|
||||
if(aval > 128) {
|
||||
acount = aval - 128;
|
||||
aval = a.data[ai++];
|
||||
int n = min(acount, bcount);
|
||||
acount -= n;
|
||||
bcount -= n;
|
||||
}
|
||||
else
|
||||
bcount--;
|
||||
}
|
||||
while(ai < a.data.GetCount() && bi < b.data.GetCount()) {
|
||||
if(acount == 0) {
|
||||
aval = a.data[ai++];
|
||||
if(aval > 128) {
|
||||
acount = aval - 128;
|
||||
aval = a.data[ai++];
|
||||
}
|
||||
}
|
||||
if(bcount == 0) {
|
||||
bval = b.data[bi++];
|
||||
if(bval > 128) {
|
||||
bcount = bval - 128;
|
||||
bval = b.data[bi++];
|
||||
}
|
||||
}
|
||||
if(acount && bcount) {
|
||||
int l = min(acount, bcount);
|
||||
acount -= l;
|
||||
bcount -= l;
|
||||
r.len += l;
|
||||
while(l) {
|
||||
int n = min(l, 127);
|
||||
r.data.Cat(n + 128);
|
||||
r.data.Cat((aval * bval) >> 7);
|
||||
l -= n;
|
||||
}
|
||||
}
|
||||
else {
|
||||
r.data.Cat((aval * bval) >> 7);
|
||||
r.len++;
|
||||
if(acount) acount--;
|
||||
if(bcount) bcount--;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ScanLine And(const ScanLine& a, const ScanLine& b)
|
||||
{
|
||||
return a.x < b.x ? And0(a, b) : And0(b, a);
|
||||
}
|
||||
|
||||
ScanLine Pack(int x, const byte *data, int len)
|
||||
{
|
||||
ScanLine r;
|
||||
int i = 0;
|
||||
r.x = x;
|
||||
r.len = 0;
|
||||
while(i < len) {
|
||||
int n = i;
|
||||
byte val = data[i++];
|
||||
while(i < len && data[i] == val)
|
||||
i++;
|
||||
n = i - n;
|
||||
if(n > 1) {
|
||||
r.len += n;
|
||||
while(n) {
|
||||
int q = min(n, 127);
|
||||
r.data.Cat(q + 128);
|
||||
r.data.Cat(val);
|
||||
n -= q;
|
||||
}
|
||||
}
|
||||
else {
|
||||
r.data.Cat(val);
|
||||
r.len++;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
String ScanLine::ToString() const
|
||||
{
|
||||
String s;
|
||||
s << "x = " << x << ", len = " << len << ": ";
|
||||
for(int i = 0; i < data.GetCount(); i++) {
|
||||
byte val = data[i];
|
||||
if(val > 128) {
|
||||
int n = val - 128;
|
||||
val = data[++i];
|
||||
while(n--)
|
||||
s << (int)val << ", ";
|
||||
}
|
||||
else
|
||||
s << (int)val << ", ";
|
||||
}
|
||||
s << "datalen: " << data.GetCount();
|
||||
return s;
|
||||
}
|
||||
|
||||
void Apply(RGBA *t, int len, const RGBA& color, const ScanLine& s)
|
||||
{
|
||||
t += s.x;
|
||||
len -= s.x;
|
||||
int si = 0;
|
||||
while(len >= 0 && si < s.data.GetLength()) {
|
||||
byte val = s.data[si++];
|
||||
if(val > 128) {
|
||||
int n = min(len, val - 128);
|
||||
val = s.data[si++];
|
||||
while(n--)
|
||||
AlphaBlendCover7(*t++, color, val);
|
||||
len -= n;
|
||||
}
|
||||
else
|
||||
AlphaBlendCover7(*t++, color, val);
|
||||
}
|
||||
}
|
||||
80
uppdev/ScanLine/ScanLine.h
Normal file
80
uppdev/ScanLine/ScanLine.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#ifndef _ScanLine_ScanLine_h_
|
||||
#define _ScanLine_ScanLine_h_
|
||||
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
inline RGBA Mul7(const RGBA& s, int mul)
|
||||
{
|
||||
RGBA t;
|
||||
t.r = (mul * s.r) >> 7;
|
||||
t.g = (mul * s.g) >> 7;
|
||||
t.b = (mul * s.b) >> 7;
|
||||
t.a = (mul * s.a) >> 7;
|
||||
return t;
|
||||
}
|
||||
|
||||
inline void AlphaBlend(RGBA& t, const RGBA& c)
|
||||
{
|
||||
int alpha = 256 - (c.a + (c.a >> 7));
|
||||
t.r = c.r + (alpha * t.r >> 8);
|
||||
t.g = c.g + (alpha * t.g >> 8);
|
||||
t.b = c.b + (alpha * t.b >> 8);
|
||||
t.a = c.a + (alpha * t.a >> 8);
|
||||
}
|
||||
|
||||
inline void AlphaBlendCover7(RGBA& t, const RGBA& c, byte cover)
|
||||
{
|
||||
AlphaBlend(t, Mul7(c, cover));
|
||||
}
|
||||
|
||||
struct ScanLine {
|
||||
int x, len;
|
||||
String data;
|
||||
|
||||
String ToString() const;
|
||||
};
|
||||
|
||||
void Apply(RGBA *t, int len, const RGBA& color, const ScanLine& s);
|
||||
ScanLine And(const ScanLine& a, const ScanLine& b);
|
||||
ScanLine Pack(int x, const byte *data, int len);
|
||||
|
||||
class Rasterizer {
|
||||
struct Cell : Moveable<Cell> {
|
||||
int x;
|
||||
int cover;
|
||||
int area;
|
||||
|
||||
void Init() { x = 0x7FFFFFFF; cover = area = 0; }
|
||||
bool operator<(const Cell& b) const { return x < b.x; }
|
||||
};
|
||||
|
||||
struct SortedY {
|
||||
unsigned start;
|
||||
unsigned num;
|
||||
};
|
||||
|
||||
Buffer< Vector<Cell> > cell;
|
||||
int current_y;
|
||||
Cell current;
|
||||
int min_y;
|
||||
int max_y;
|
||||
bool finish;
|
||||
|
||||
void AddCurrent();
|
||||
void SetCurrent(int x, int y);
|
||||
void RenderHLine(int ey, int x1, int y1, int x2, int y2);
|
||||
|
||||
public:
|
||||
void Line(int x1, int y1, int x2, int y2);
|
||||
|
||||
int MinY() const { return min_y; }
|
||||
int MaxY() const { return max_y; }
|
||||
|
||||
ScanLine Get(int y);
|
||||
|
||||
Rasterizer(int cy);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
uses
|
||||
Core,
|
||||
CtrlLib;
|
||||
|
||||
file
|
||||
ScanLine.h,
|
||||
ScanLine.cpp,
|
||||
Rasterizer.cpp,
|
||||
main.cpp;
|
||||
|
||||
mainconfig
|
||||
"" = "GUI";
|
||||
|
||||
5
uppdev/ScanLine/init
Normal file
5
uppdev/ScanLine/init
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _ScanLine_icpp_init_stub
|
||||
#define _ScanLine_icpp_init_stub
|
||||
#include "Core/init"
|
||||
#include "CtrlLib/init"
|
||||
#endif
|
||||
56
uppdev/ScanLine/main.cpp
Normal file
56
uppdev/ScanLine/main.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "ScanLine.h"
|
||||
|
||||
struct App : TopWindow {
|
||||
virtual void Paint(Draw& w) {
|
||||
ScanLine a, b;
|
||||
static byte line1[] = { 1, 50, 100, 128, 128, 128, 128, 128, 100, 50, 1 };
|
||||
a = Pack(0, line1, __countof(line1));
|
||||
ImageBuffer ib(400, 400);
|
||||
Fill(~ib, White(), ib.GetLength());
|
||||
Apply(ib[20], 100, Black(), a);
|
||||
a.x = 10;
|
||||
Apply(ib[30], 100, Blue(), a);
|
||||
for(int i = 0; i < 20; i++) {
|
||||
b = Pack(i, line1, __countof(line1));
|
||||
Apply(ib[50 + 2 * i], 100, Black(), And(a, b));
|
||||
}
|
||||
|
||||
Rasterizer r(400);
|
||||
r.Line(100 * 256, 100 * 256, 300 * 256, 100 * 256);
|
||||
r.Line(300 * 256, 100 * 256, 200 * 256, 300 * 256);
|
||||
r.Line(200 * 256, 300 * 256, 100 * 256, 100 * 256);
|
||||
for(int y = r.MinY(); y <= r.MaxY(); y++) {
|
||||
ScanLine sl = r.Get(y);
|
||||
DUMP(sl);
|
||||
Apply(ib[y], 400, Black(), sl);
|
||||
}
|
||||
|
||||
w.DrawRect(GetSize(), White());
|
||||
w.DrawImage(0, 0, ib);
|
||||
}
|
||||
};
|
||||
|
||||
GUI_APP_MAIN {
|
||||
App().Run();
|
||||
|
||||
ScanLine a, b, c;
|
||||
static byte line1[] = { 1, 50, 100, 128, 128, 128, 128, 128, 100, 50, 1 };
|
||||
a = Pack(0, line1, __countof(line1));
|
||||
DUMP(a);
|
||||
for(int i = 0; i < 16; i++) {
|
||||
b = Pack(i, line1, __countof(line1));
|
||||
DUMP(b);
|
||||
DUMP(And(a, b));
|
||||
DUMP(And(b, a));
|
||||
}
|
||||
|
||||
|
||||
#ifndef _DEBUG
|
||||
int x = 0;
|
||||
b = Pack(3, line1, __countof(line1));
|
||||
for(int i = 0; i < 10000000; i++) {
|
||||
RTIMING("And");
|
||||
x += And(a, b).x;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue