#include "Draw.h" namespace Upp { DDARasterizer::DDARasterizer() { cy = 0; p0 = p1 = Point(0, 0); j1 = j2 = Null; b1 = b2 = Null; close = false; width = 1; } void DDARasterizer::AHorz(int x, int y, int cx) { if(cx) { if(cx < 0) PutHorz(x + cx + 1, y, -cx); else PutHorz(x, y, cx); } } void DDARasterizer::AVert(int x, int y, int cy) { if(cy) { if(cy < 0) PutVert(x, y + cy + 1, -cy); else PutVert(x, y, cy); } } void DDARasterizer::DoLine(Point p1, Point p2, bool last) { dirx = sgn(p2.x - p1.x); diry = sgn(p2.y - p1.y); int dx = abs(p2.x - p1.x); int dy = abs(p2.y - p1.y); int x = p1.x; int y = p1.y; int x0 = x; int y0 = y; if(dx < dy) { int dda = dy >> 1; int n = dy + last; for(;;) { if(x != x0) { AVert(x0, y0, y - y0); x0 = x; y0 = y; } if(n-- <= 0) break; y += diry; dda -= dx; if(dda < 0) { dda += dy; x += dirx; } } AVert(x0, y0, y - y0); } else { int dda = dx >> 1; int n = dx + last; for(;;) { if(y != y0) { AHorz(x0, y0, x - x0); x0 = x; y0 = y; } if(n-- <= 0) break; x += dirx; dda -= dy; if(dda < 0) { dda += dx; y += diry; } } AHorz(x0, y0, x - x0); } } struct DDARasterizer::Segments : DDARasterizer { int miny; int maxy; bool flag; Vector< Vector< Segment > > segment; virtual void PutHorz(int x, int y, int cx) { if(y >= 0 && y < cy) { Segment& m = segment.At(y).Add(); m.l = x; m.h = x + cx; m.flag = flag; miny = min(y, miny); maxy = max(y, maxy); } flag = true; } virtual void PutVert(int x, int y, int cy) { for(int i = 0; i < cy; i++) PutHorz(x, y + i, 1); } Segments() { miny = INT_MAX; maxy = 0; } }; void DDARasterizer::FatLine(Point p2) { if(p1 == p2) return; Point pp = p0; p0 = p1; // avoid Close Point v = p2 - p1; Point shift = (width - 0.5) * Orthogonal(Pointf(v) / Length((Pointf(v)))); Point p = p1 + shift / 2; Point bj2 = j2; Polygon(); if(!IsNull(j1)) { Move(j1); Line(p); } else Move(p); if(IsNull(b1)) b1 = p; Line(p += v); j1 = p; if(close && !IsNull(b1)) { Line(b1); Line(b2); } Line(p -= shift); j2 = p; Line(p -= v); if(!IsNull(bj2)) Line(bj2); if(IsNull(b2)) b2 = p; Fill(); p0 = pp; } DDARasterizer& DDARasterizer::Move(Point p) { if(pseg) Close(); p0 = p1 = p; j1 = j2 = Null; return *this; } DDARasterizer& DDARasterizer::Line(Point p) { if(pseg) { Point a = p1; Point b = p; if(a.y > b.y) Swap(a, b); pseg->flag = false; pseg->DoLine(a, b, true); p1 = p; } else { if(width <= 1) DoLine(p1, p, false); else FatLine(p); p1 = p; } return *this; } DDARasterizer& DDARasterizer::Close() { if(p1 != p0) { close = true; Line(p0); close = false; if(!pseg) b1 = b2 = Null; } return *this; } DDARasterizer& DDARasterizer::Polygon() { pseg.Create(); pseg->Cy(cy); return *this; } DDARasterizer& DDARasterizer::Fill() { ASSERT(pseg); Close(); for(int y = pseg->miny; y <= pseg->maxy; y++) { Vector& gg = pseg->segment[y]; Sort(gg); int i = 0; bool flag = false; while(i < gg.GetCount()) { int l = gg[i].l; int h = gg[i].h; flag ^= gg[i++].flag; while(i < gg.GetCount() && flag) { h = gg[i].h; flag ^= gg[i++].flag; } PutHorz(l, y, h - l); } } pseg.Clear(); return *this; } DDARasterizer& DDARasterizer::Ellipse(const Rect& rect) { Size size = rect.GetSize() / 2; int n = 16; Buffer p(n); for(int i = 0; i < n; i++) { double angle = M_PI * i / (n - 1) / 2; p[i].x = min(size.cx, int(sin(angle) * size.cx + 0.5)); p[i].y = min(size.cy, int(cos(angle) * size.cy + 0.5)); } Point center = rect.TopLeft() + size; Move(Point(center.x - p[0].x, center.y - p[0].y)); for(int i = 1; i < n; i++) Line(Point(center.x - p[i].x, center.y - p[i].y)); center.y = rect.bottom - 1 - size.cy; for(int i = n - 1; i >= 0; i--) Line(Point(center.x - p[i].x, center.y + p[i].y)); center.x = rect.right - 1 - size.cx; for(int i = 0; i < n; i++) Line(Point(center.x + p[i].x, center.y + p[i].y)); center.y = rect.top + size.cy; for(int i = n - 1; i >= 0; i--) Line(Point(center.x + p[i].x, center.y - p[i].y)); Close(); return *this; } DDARasterizer::~DDARasterizer() {} }