From d2d643e4166c8edc5298cb097ae60234cf72ac30 Mon Sep 17 00:00:00 2001 From: cxl Date: Wed, 28 Jan 2009 19:06:20 +0000 Subject: [PATCH] Refactoring Rasterizer git-svn-id: svn://ultimatepp.org/upp/trunk@820 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppdev/ScanLine/Rasterizer.cpp | 257 +++++++++++++++++++++++++++++++++ uppdev/ScanLine/ScanLine.cpp | 135 +++++++++++++++++ uppdev/ScanLine/ScanLine.h | 80 ++++++++++ uppdev/ScanLine/ScanLine.upp | 13 ++ uppdev/ScanLine/init | 5 + uppdev/ScanLine/main.cpp | 56 +++++++ 6 files changed, 546 insertions(+) create mode 100644 uppdev/ScanLine/Rasterizer.cpp create mode 100644 uppdev/ScanLine/ScanLine.cpp create mode 100644 uppdev/ScanLine/ScanLine.h create mode 100644 uppdev/ScanLine/init create mode 100644 uppdev/ScanLine/main.cpp diff --git a/uppdev/ScanLine/Rasterizer.cpp b/uppdev/ScanLine/Rasterizer.cpp new file mode 100644 index 000000000..ca344ea14 --- /dev/null +++ b/uppdev/ScanLine/Rasterizer.cpp @@ -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& 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; +} diff --git a/uppdev/ScanLine/ScanLine.cpp b/uppdev/ScanLine/ScanLine.cpp new file mode 100644 index 000000000..7006a676e --- /dev/null +++ b/uppdev/ScanLine/ScanLine.cpp @@ -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); + } +} diff --git a/uppdev/ScanLine/ScanLine.h b/uppdev/ScanLine/ScanLine.h new file mode 100644 index 000000000..a29621897 --- /dev/null +++ b/uppdev/ScanLine/ScanLine.h @@ -0,0 +1,80 @@ +#ifndef _ScanLine_ScanLine_h_ +#define _ScanLine_ScanLine_h_ + +#include + +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 { + 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; + 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 diff --git a/uppdev/ScanLine/ScanLine.upp b/uppdev/ScanLine/ScanLine.upp index e69de29bb..335c93eac 100644 --- a/uppdev/ScanLine/ScanLine.upp +++ b/uppdev/ScanLine/ScanLine.upp @@ -0,0 +1,13 @@ +uses + Core, + CtrlLib; + +file + ScanLine.h, + ScanLine.cpp, + Rasterizer.cpp, + main.cpp; + +mainconfig + "" = "GUI"; + diff --git a/uppdev/ScanLine/init b/uppdev/ScanLine/init new file mode 100644 index 000000000..99017c57c --- /dev/null +++ b/uppdev/ScanLine/init @@ -0,0 +1,5 @@ +#ifndef _ScanLine_icpp_init_stub +#define _ScanLine_icpp_init_stub +#include "Core/init" +#include "CtrlLib/init" +#endif diff --git a/uppdev/ScanLine/main.cpp b/uppdev/ScanLine/main.cpp new file mode 100644 index 000000000..bbf01a13d --- /dev/null +++ b/uppdev/ScanLine/main.cpp @@ -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 +}