#include 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 > 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 > ln; int y0, ymax, lnc; void AddPoint(int x, int y) { LDUMP(Point(x, y)); if(y >= y0 && y <= ymax) { Vector& 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& 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& 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& 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 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(); }