mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
332 lines
6.7 KiB
C++
332 lines
6.7 KiB
C++
#include <CtrlLib/CtrlLib.h>
|
|
|
|
using namespace Upp;
|
|
|
|
#define LDUMP(x)
|
|
#define LLOG(x);
|
|
#define LTIMING(x) RTIMING(x)
|
|
|
|
class AABuffer {
|
|
struct RGBAi {
|
|
dword r;
|
|
dword g;
|
|
dword b;
|
|
dword a;
|
|
|
|
void Write(RGBA& t) {
|
|
t.r = byte(r >> 4);
|
|
t.g = byte(g >> 4);
|
|
t.b = byte(b >> 4);
|
|
t.a = byte(a >> 4);
|
|
}
|
|
|
|
void Put(const RGBA& s) {
|
|
r += s.r;
|
|
g += s.g;
|
|
b += s.b;
|
|
a += s.a;
|
|
}
|
|
|
|
RGBAi() { r = g = b = a = 0; }
|
|
};
|
|
|
|
#define COMPLEX *(RGBA *)(
|
|
|
|
struct P16 {
|
|
RGBA simple;
|
|
RGBA *complex;
|
|
|
|
bool IsComplex() const { return simple.b == 255 && simple.a == 0; }
|
|
};
|
|
|
|
Buffer< Buffer<P16> > line;
|
|
|
|
int cx, gx, cy;
|
|
|
|
void Free();
|
|
|
|
static bool MakeComplexIf(P16 *p, RGBA color)
|
|
{
|
|
if(!p->IsComplex()) {
|
|
if(p->simple == color)
|
|
return false;
|
|
RGBA h = p->simple;
|
|
p->complex = new RGBA[16];
|
|
p->complex[0] = h; p->complex[1] = h; p->complex[2] = h; p->complex[3] = h;
|
|
p->complex[4] = h; p->complex[5] = h; p->complex[6] = h; p->complex[7] = h;
|
|
p->complex[8] = h; p->complex[9] = h; p->complex[10] = h; p->complex[11] = h;
|
|
p->complex[12] = h; p->complex[13] = h; p->complex[14] = h; p->complex[15] = h;
|
|
p->simple.b = 255;
|
|
p->simple.a = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
void Fill(int x, int y, int n, RGBA color);
|
|
|
|
void Create(int cx, int cy);
|
|
Image Downsample();
|
|
|
|
~AABuffer();
|
|
};
|
|
|
|
void AABuffer::Fill(int x, int y, int n, RGBA color)
|
|
{
|
|
if(n <= 0)
|
|
return;
|
|
P16 *p = line[y];
|
|
int j = x >> 4;
|
|
int a = x & 15;
|
|
if(a || n < 16) {
|
|
int c = min(n, 16 - a);
|
|
if(MakeComplexIf(p, color))
|
|
UPP::Fill(p->complex + a, color, c);
|
|
n -= c;
|
|
p++;
|
|
}
|
|
while(n > 16) {
|
|
if(p->IsComplex())
|
|
delete[] p->complex;
|
|
p->simple = color;
|
|
n -= 16;
|
|
p++;
|
|
}
|
|
if(n && MakeComplexIf(p, color))
|
|
UPP::Fill(p->complex, color, n);
|
|
}
|
|
|
|
void AABuffer::Create(int _cx, int _cy)
|
|
{
|
|
cx = _cx << 2;
|
|
cy = _cy << 2;
|
|
gx = (cx + 15) >> 4;
|
|
line.Alloc(cy);
|
|
for(int i = 0; i < cy; i++) {
|
|
line[i].Alloc(gx);
|
|
memset(line[i], 0, cx * sizeof(P16));
|
|
}
|
|
}
|
|
|
|
Image AABuffer::Downsample()
|
|
{
|
|
LTIMING("Downscale");
|
|
Size tsz = Size(cx >> 2, cy >> 2);
|
|
ImageBuffer tb(tsz);
|
|
RGBA *t = tb;
|
|
for(int y = 0; y < tsz.cy; y++) {
|
|
P16 *p[4];
|
|
p[0] = line[4 * y + 0];
|
|
p[1] = line[4 * y + 1];
|
|
p[2] = line[4 * y + 2];
|
|
p[3] = line[4 * y + 3];
|
|
int x = 0;
|
|
for(int i = 0; i < gx; i++) {
|
|
RGBA c = p[1]->simple;
|
|
if(c == p[2]->simple && c == p[3]->simple && c == p[4]->simple) {
|
|
if(x + 4 < tsz.cx) {
|
|
t[0] = c;
|
|
t[1] = c;
|
|
t[2] = c;
|
|
t[3] = c;
|
|
t += 4;
|
|
}
|
|
else
|
|
while(x < tsz.cx)
|
|
*t++ = c;
|
|
}
|
|
else {
|
|
RGBAi m[4];
|
|
for(int j = 0; j < 4; j++) {
|
|
if(*p[j].IsSimple()) {
|
|
for(int k = 0; k < 4; k++)
|
|
m[k].Put4(*p[j]->simple);
|
|
}
|
|
else {
|
|
for(int k = 0; k < 4; k++) {
|
|
m[0].Put(p[j].complex[0]);
|
|
m[0].Put(p[j].complex[1]);
|
|
m[0].Put(p[j].complex[2]);
|
|
m[0].Put(p[j].complex[3]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p[0]++;
|
|
p[1]++;
|
|
p[2]++;
|
|
p[3]++;
|
|
}
|
|
return tb;
|
|
}
|
|
|
|
struct PolygonRenderer {
|
|
Buffer< Vector<int> > ln;
|
|
int y0, ymax, lnc;
|
|
|
|
void AddPoint(int x, int y)
|
|
{
|
|
LDUMP(Point(x, y));
|
|
if(y >= y0 && y <= ymax) {
|
|
Vector<int>& v = ln[y - y0];
|
|
int i;
|
|
for(i = 0; i < v.GetCount(); i++)
|
|
if(x < v[i])
|
|
break;
|
|
v.Insert(i, x);
|
|
}
|
|
}
|
|
|
|
void RenderPolygon(ImageBuffer& b, const Vector<Point>& p, RGBA color)
|
|
{
|
|
LTIMING("RenderPolygon");
|
|
if(p.GetCount() < 2)
|
|
return;
|
|
ymax = y0 = p[0].y;
|
|
for(int i = 0; i < p.GetCount(); i++) {
|
|
int y = p[i].y;
|
|
y0 = min(y0, y);
|
|
ymax = max(ymax, y);
|
|
}
|
|
y0 = minmax(y0, 0, b.GetHeight() - 1);
|
|
ymax = minmax(ymax, 0, b.GetHeight() - 1);
|
|
int lines = ymax - y0 + 1;
|
|
ln.Alloc(lines);
|
|
for(int i = 0; i < p.GetCount(); i++) {
|
|
Point p1 = p[i];
|
|
Point p2 = p[i > 0 ? i - 1 : p.GetCount() - 1];
|
|
LLOG(p1 << " - " << p2);
|
|
if(p1.y > p2.y)
|
|
Swap(p1, p2);
|
|
int delta = ((p2.x - p1.x) << 16) / (p2.y - p1.y + 1);
|
|
int x = p1.x << 16;
|
|
AddPoint(p1.x, p1.y);
|
|
for(int y = p1.y; y < p2.y; y++) {
|
|
x += delta;
|
|
AddPoint(x >> 16, y);
|
|
}
|
|
AddPoint(p2.x, p2.y);
|
|
}
|
|
RGBA *t = b[y0];
|
|
int cx = b.GetWidth();
|
|
for(int i = 0; i < lines; i++) {
|
|
LDUMP(y0 + i);
|
|
Vector<int>& v = ln[i];
|
|
int j = 0;
|
|
while(j < v.GetCount() - 1) {
|
|
LLOG(v[j] << ' ' << v[j + 1]);
|
|
if(v[j] < cx && v[j + 1] >= 0) {
|
|
int x1 = max(v[j], 0);
|
|
int x2 = min(v[j + 1], cx - 1);
|
|
Fill(t + x1, color, x2 - x1 + 1);
|
|
}
|
|
j += 2;
|
|
}
|
|
t += cx;
|
|
}
|
|
}
|
|
};
|
|
|
|
void RenderPolygon(ImageBuffer& b, const Vector<Point>& p, RGBA color)
|
|
{
|
|
PolygonRenderer r;
|
|
r.RenderPolygon(b, p, color);
|
|
}
|
|
|
|
struct RGBAi {
|
|
dword r;
|
|
dword g;
|
|
dword b;
|
|
dword a;
|
|
|
|
void Get(RGBA& t) {
|
|
t.r = r >> 4;
|
|
t.g = g >> 4;
|
|
t.b = b >> 4;
|
|
t.a = a >> 4;
|
|
}
|
|
|
|
void Put(const RGBA& s) {
|
|
r += s.r;
|
|
g += s.g;
|
|
b += s.b;
|
|
a += s.a;
|
|
}
|
|
|
|
RGBAi() { r = g = b = a = 0; }
|
|
};
|
|
|
|
Image Downscale(const ImageBuffer& b)
|
|
{
|
|
LTIMING("Downscale");
|
|
Size tsz = b.GetSize() / 4;
|
|
ImageBuffer tb(tsz);
|
|
int cx = b.GetWidth();
|
|
const RGBA *bs = ~b;
|
|
RGBA *t = tb;
|
|
for(int y = 0; y < tsz.cy; y++) {
|
|
const RGBA *q = bs;
|
|
for(int x = 0; x < tsz.cx; x++) {
|
|
RGBAi m;
|
|
const RGBA *s = q;
|
|
m.Put(s[0]); m.Put(s[1]); m.Put(s[2]); m.Put(s[3]);
|
|
s += cx;
|
|
m.Put(s[0]); m.Put(s[1]); m.Put(s[2]); m.Put(s[3]);
|
|
s += cx;
|
|
m.Put(s[0]); m.Put(s[1]); m.Put(s[2]); m.Put(s[3]);
|
|
s += cx;
|
|
m.Put(s[0]); m.Put(s[1]); m.Put(s[2]); m.Put(s[3]);
|
|
q += 4;
|
|
m.Get(*t++);
|
|
}
|
|
bs += 4 * cx;
|
|
}
|
|
return tb;
|
|
}
|
|
|
|
struct App : TopWindow {
|
|
Vector<Point> p;
|
|
|
|
virtual void LeftDown(Point, dword) {
|
|
Size sz = GetSize() * 4;
|
|
int n = Random(10) + 3;
|
|
p.SetCount(n);
|
|
for(int i = 0; i < n; i++)
|
|
p[i] = Point(Random(sz.cx), Random(sz.cy));
|
|
// p[i] = Point(Random(sz.cx + 30) - 15, Random(sz.cy + 30) - 15);
|
|
Refresh();
|
|
}
|
|
|
|
void Plot(ImageBuffer& b, int x, int y) {
|
|
if(Rect(b.GetSize()).Contains(Point(x, y)))
|
|
b[y][x] = Yellow();
|
|
}
|
|
|
|
virtual void Paint(Draw& w) {
|
|
ImageBuffer b(GetSize() * 4);
|
|
Fill(b, LtGray(), b.GetLength());
|
|
Image m;
|
|
for(int i = 0; i < 10; i++) {
|
|
RenderPolygon(b, p, Red());
|
|
for(int i = 0; i < p.GetCount(); i++) {
|
|
Point q = p[i];
|
|
Plot(b, q.x, q.y);
|
|
Plot(b, q.x - 1, q.y);
|
|
Plot(b, q.x + 1, q.y);
|
|
Plot(b, q.x, q.y + 1);
|
|
Plot(b, q.x, q.y - 1);
|
|
}
|
|
m = Downscale(b);
|
|
}
|
|
w.DrawImage(0, 0, m);
|
|
}
|
|
|
|
App() {
|
|
p << Point(20, 20) << Point(200, 20) << Point(230, 150) << Point(120, 200);
|
|
}
|
|
};
|
|
|
|
GUI_APP_MAIN
|
|
{
|
|
App().Run();
|
|
}
|