ultimatepp/uppsrc/Geom/Draw/plotter.cpp
rylek 7ae5221acb Fixed arithmetic overflow in palette generator; some basic fixes related to recent update of the Draw package.
git-svn-id: svn://ultimatepp.org/upp/trunk@1371 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2009-07-06 20:09:12 +00:00

2637 lines
67 KiB
C++

#include "GeomDraw.h"
#pragma hdrstop
NAMESPACE_UPP
#define LLOG(x) // LOG(x)
#define DEBUG_AREA 0
#define CHECK_DIST 0 // 0 = off, 1 = on (purging very short line segments)
#define STAT_LINES 0 // 0 = off, 1 = on, 2 = log individual vertices
#if defined(CPU_IA32) && defined(COMPILER_MSC)
#define MSC_ASSEMBLY
#endif
static void DrawFatPolyPolygonOutline(Draw& draw,
const Point *vertices, int vertex_counts,
const int *polygon_counts, int polygon_count_counts,
Color color, int width)
{
if(IsNull(color))
return;
if(width <= 1)
{
DrawPolyPolygon(draw, vertices, vertex_counts, polygon_counts, polygon_count_counts, Null, 0, color);
return;
}
enum { BATCH = 4000 };
int nsegs = min<int>(BATCH, (vertex_counts + polygon_count_counts + 1) * width);
int batch = 2 * nsegs;
Buffer<Point> buffer(batch);
Buffer<int> counts(nsegs);
Fill(counts + 0, counts + nsegs, 2);
int half = width >> 1;
Point *gen = buffer, *genend = buffer + batch - 2 * width;
for(; --polygon_count_counts >= 0; vertices += *polygon_counts++)
{
int vert = *polygon_counts;
int vw = vert * width;
int pc = 2 * vw;
Point prev = *vertices;
for(const Point *next = vertices + vert; next-- > vertices; prev = *next)
{
int dx = next->x - prev.x, dy = next->y - prev.y;
bool vert = (tabs(dx) < tabs(dy));
if(vert)
{
// if(dy >= 0) { dy += width; prev.y -= half; }
// else { dy -= width; prev.y += half; }
prev.x -= half;
for(int i = 0; i < width; i++, gen += 2)
{
gen[1].x = dx + (gen[0].x = prev.x + i);
gen[1].y = dy + (gen[0].y = prev.y);
}
}
else
{
// if(dx >= 0) { dx += width; prev.x -= half; }
// else { dx -= width; prev.x += half; }
prev.y -= half;
for(int i = 0; i < width; i++, gen += 2)
{
gen[1].x = dx + (gen[0].x = prev.x);
gen[1].y = dy + (gen[0].y = prev.y + i);
}
}
if(gen >= genend)
{
int pc = gen - buffer;
ASSERT(pc <= batch);
ASSERT(!(pc & 1));
draw.DrawPolyPolyline(buffer, pc, counts, pc >> 1, 0, color);
gen = buffer;
}
}
}
if(int pc = gen - buffer)
{
ASSERT(pc <= batch);
ASSERT(!(pc & 1));
draw.DrawPolyPolyline(buffer, pc, counts, pc >> 1, 0, color);
gen = buffer;
}
}
static void DrawFatPolyPolygonOutline(Draw& draw,
const Vector<Point>& vertices, const Vector<int>& counts,
Color color, int width)
{
DrawFatPolyPolygonOutline(draw, vertices.Begin(), vertices.GetCount(),
counts.Begin(), counts.GetCount(), color, width);
}
LineStyle SolidLine = { 0 };
LineStyle EmptyLine = { -1 };
LineStyle DashLine = { 8, -4, 0 };
LineStyle DotLine = { 2, -4, 0 };
LineStyle DashDotLine = { 8, -4, 2, -4, 0 };
LineStyle DashDotDotLine = { 8, -4, 2, -4, 2, -4, 0 };
enum { MAX_COUNT = 2048, MAX_MANUAL = 50 };
LineDraw::LineDraw()
: draw(0)
, MoveToRaw(0)
, LineToRaw(0)
, AddVector(0)
{
}
LineDraw::LineDraw(Draw& draw, LineStyle pattern, Color color, int width, double dash)
{
Set(draw, pattern, color, width, dash);
}
void LineDraw::Set(Draw& _draw, LineStyle pattern, Color color, int width, double dash)
{
draw = &_draw;
empty = (*pattern < 0 || IsNull(color));
if(empty)
{
active = 0;
ClearExtent();
return;
}
pen_width = (width >= 0 ? width : DotsToPixels(*draw, -width));
if(dash < 0)
dash *= -GetAvgPixelsPerDot(*draw);
else if(dash == 0)
dash = max(pen_width, 1);
dash = max<double>(dash, 1);
pen_color = color;
if(pen_width <= 1)
AddVector = &LineDraw::AddVectorThin;
else if(draw->Pixels() && pen_width <= 5)
{
AddVector = &LineDraw::AddVectorThick;
half_width = pen_width >> 1;
}
else
{
AddVector = &LineDraw::AddVectorArea;
factor = pen_width / 2.0;
}
if(*pattern > 0)
{
if(dash == 0)
dash = width;
const signed char *p = pattern;
while(*p++)
;
p--;
while(p > pattern)
segments.Add(*--p * dash);
active = segments.End();
remains = *--active;
}
else
{
remains = 1e100;
active = NULL;
}
first = last = Null;
Size size = draw->GetPageSize();
clip = draw->GetPageSize(); // GetClip()
max_rad = max(size.cx, size.cy);
vertices.Clear();
indices.Clear();
ClearExtent();
}
bool LineDraw::SetExtent(const Rect& rc)
{
if(!(rc && clip) || empty)
return false;
bool in = clip.Contains(rc);
MoveToRaw = active ? &LineDraw::MoveToDashed : &LineDraw::MoveToSolid;
if(active)
LineToRaw = in ? &LineDraw::LineToDashedSimple : &LineDraw::LineToDashedClip;
else
LineToRaw = in ? &LineDraw::LineToSolidSimple : &LineDraw::LineToSolidClip;
return true;
}
void LineDraw::ClearExtent()
{
MoveToRaw = empty ? &LineDraw::MoveToEmpty : active ? &LineDraw::MoveToDashed : &LineDraw::MoveToSolid;
LineToRaw = empty ? &LineDraw::LineToEmpty : active ? &LineDraw::LineToDashedClip : &LineDraw::LineToSolidClip;
}
void LineDraw::Clear()
{
indices.Clear();
vertices.Clear();
}
void LineDraw::Flush()
{
if(indices.IsEmpty())
return;
draw->DrawPolyPolyline(vertices, indices, 0, pen_color);
vertices.Clear();
indices.Clear();
}
void LineDraw::AddVectorThin(Point a, Point b)
{
if(a == b)
return;
if(!vertices.IsEmpty())
if(vertices.Top() == a)
{
vertices.Add(b);
indices.Top()++;
return;
}
else if(vertices.Top() == b)
{
vertices.Add(a);
indices.Top()++;
return;
}
indices.Add(2);
vertices.Add(a);
vertices.Add(b);
if(vertices.GetCount() >= MAX_COUNT)
Flush();
}
void LineDraw::AddVectorThick(Point a, Point b)
{
Size vector = b - a;
if((vector.cx | vector.cy) == 0)
return;
Size sh(sgn(vector.cx), sgn(vector.cy));
Size mv(0, 0);
if(tabs(vector.cx) > tabs(vector.cy))
{
mv.cy = 1;
a.y += half_width;
b.y += half_width;
}
else
{
mv.cx = 1;
a.x += half_width;
b.x += half_width;
}
indices.Add(2 * pen_width);
int i = pen_width;
while(i >= 2)
{
vertices.Add(a);
vertices.Add(b);
a -= mv;
b -= mv;
vertices.Add(b);
vertices.Add(a);
a -= mv;
b -= mv;
i -= 2;
}
if(i)
{
vertices.Add(a);
vertices.Add(b);
}
if(vertices.GetCount() >= MAX_COUNT)
Flush();
}
void LineDraw::AddVectorArea(Point a, Point b)
{
// static Point last(0, 0);
// LOG(a << " - " << b << ": " << abs(a - b) << " (" << abs(a - last) << ")");
// last = b;
Size vector = b - a;
if((vector.cx | vector.cy) == 0)
vector.cx = 1;
double f = factor / sqrt(double(vector.cx * vector.cx + vector.cy * vector.cy));
vector.cx = fround(f * vector.cx);
vector.cy = fround(f * vector.cy);
Size larger((vector.cy - vector.cx) * 3 >> 2, (vector.cy + vector.cx) * 3 >> 2);
Point polygon[10];
polygon[0].x = a.x + vector.cy; polygon[0].y = a.y - vector.cx;
polygon[1].x = a.x + larger.cx; polygon[1].y = a.y - larger.cy;
polygon[2].x = a.x - vector.cx; polygon[2].y = a.y - vector.cy;
polygon[3].x = a.x - larger.cy; polygon[3].y = a.y - larger.cx;
polygon[4].x = a.x - vector.cy; polygon[4].y = a.y + vector.cx;
polygon[5].x = b.x - vector.cy; polygon[5].y = b.y + vector.cx;
polygon[6].x = b.x - larger.cx; polygon[6].y = b.y + larger.cy;
polygon[7].x = b.x + vector.cx; polygon[7].y = b.y + vector.cy;
polygon[8].x = b.x + larger.cy; polygon[8].y = b.y + larger.cx;
polygon[9].x = b.x + vector.cy; polygon[9].y = b.y - vector.cx;
DrawPolygon(*draw, polygon, 10, pen_color, PEN_NULL, Black, 0);
// draw->SetColor(pen_color);
// draw->SetDrawPen(PEN_NULL, Black);
// Polygon(*draw, polygon, 10);
}
void LineDraw::MoveToEmpty(Point pt) {}
void LineDraw::MoveToSolid(Point pt)
{
first = last = pt;
}
void LineDraw::MoveToDashed(Point pt)
{
first = last = pt;
active = segments.End();
remains = *--active;
}
void LineDraw::LineToEmpty(Point pt)
{
}
void LineDraw::LineToSolidSimple(Point pt)
{
if(!IsNull(last) && pt != last)
(this ->* AddVector)(last, pt);
last = pt;
}
void LineDraw::LineToSolidClip(Point pt)
{
if(!IsNull(last))
{
if(pt == last)
return;
Point next = pt;
if(!clip.Contains(next) || !clip.Contains(last))
{
Pointf A = last, B = next;
if(!ClipLine(A, B, clip))
{
last = pt;
return;
}
last = PointfToPoint(A);
next = PointfToPoint(B);
}
(this ->* AddVector)(last, next);
}
last = pt;
}
void LineDraw::LineToDashedClip(Point pt)
{
if(pt == last)
return;
if(!IsNull(last))
{
Point next = pt;
if(!clip.Contains(next) || !clip.Contains(last))
{
Pointf A = last, B = next;
if(!ClipLine(A, B, clip))
{
last = pt;
return;
}
last = PointfToPoint(A);
next = PointfToPoint(B);
}
LineToDashedSimple(next);
}
last = pt;
}
void LineDraw::LineToDashedSimple(Point pt)
{
if(IsNull(last))
{
last = pt;
return;
}
Size vector = pt - last;
if((vector.cx | vector.cy) == 0)
return;
double segment = hypot(vector.cx, vector.cy), left = segment;
for(;;)
{
if(remains > 0)
{
if(left < segment)
{
double r = left / segment;
last.x = pt.x - fround(vector.cx * r);
last.y = pt.y - fround(vector.cy * r);
}
if(remains >= left)
{
remains -= left;
(this ->* AddVector)(last, pt);
break;
}
else
{
double r = (left -= remains) / segment;
Point end(pt.x - fround(vector.cx * r), pt.y - fround(vector.cy * r));
(this ->* AddVector)(last, end);
last = end;
}
}
else
{
if(remains + left <= 0)
{
remains += left;
break;
}
else
left += remains;
}
if(active == segments.Begin())
active = segments.End();
remains = *--active;
}
last = pt;
}
void LineDraw::Close()
{
if(empty)
return;
if(!IsNull(first.x) && first != last)
LineTo(first);
first = Null;
}
void LineDraw::Rectangle(const Rect& rc)
{
if(empty)
return;
MoveTo(rc.TopLeft());
LineTo(Point(rc.right, rc.top));
LineTo(rc.BottomRight());
LineTo(Point(rc.left, rc.bottom));
Close();
}
void LineDraw::RecurseArc(Point next, int length, int bulge, int levels)
{
if(--levels < 0 || tabs(bulge) <= 1 || length <= 2)
LineTo(next); // degenerate next line
else
{ // bisect arc
Size normal = iscale(Size(next.y - last.y, last.x - next.x), bulge, length);
Point centre = ((last + next) >> 1) + normal;
int ll = (int)Length(centre - next);
int hh = iscale(bulge, ll, 2 * ll + length);
RecurseArc(centre, ll, hh, levels);
RecurseArc(next, ll, hh, levels);
}
}
void LineDraw::ArcTo(Point p, int bulge, int levels)
{
if(empty)
return;
if(p == last)
return;
if(bulge >= -1 && bulge <= 1)
{
LineTo(p);
return;
}
VecArcInfo info(last, Pointf(p), -bulge);
if(!info.IsCurved())
{
LineTo(p);
return;
}
if(info.radius >= max_rad)
{
RecurseArc(p, (int)Length(p - last), bulge, levels);
return;
}
Point c = PointfToPoint(info.C);
int rad = fround(info.radius);
Rect rc(c.x - rad, c.y - rad, c.x + rad, c.y + rad);
if(!active)
{ // draw simple arc
if(bulge >= 0)
draw->DrawArc(rc, last, p, pen_width, pen_color);
else
draw->DrawArc(rc, p, last, pen_width, pen_color);
last = p;
return;
}
double left = info.Length();
double end_angle = info.reversed ? info.alpha : info.beta;
double len_ang = (info.reversed ? info.beta - info.alpha : info.alpha - info.beta) / left;
int part = 1000;
for(;;)
{
if(--part < 0)
break;
if(remains > 0)
{
Point start = PointfToPoint(PolarPointf(info.C, info.radius, end_angle + len_ang * left));
Point end;
if(remains >= left)
{
remains -= left;
left = 0;
end = p;
}
else
end = PointfToPoint(PolarPointf(info.C, info.radius, end_angle + len_ang * (left -= remains)));
if(bulge >= 0)
Swap(start, end);
if(max(start.x - end.x, start.y - end.y) <= 2)
draw->DrawLine(start, end, pen_width, pen_color);
else
{
if(bulge >= 0)
draw->DrawArc(rc, start, end, pen_width, pen_color);
else
draw->DrawArc(rc, end, start, pen_width, pen_color);
}
if(left == 0)
break;
}
else if(remains < 0)
{
if(remains + left <= 0)
{
remains += left;
break;
}
else
left += remains;
}
if(active == segments.Begin())
active = segments.End();
remains = *--active;
}
last = p;
}
void LineDraw::Circle(Point centre, int radius)
{
if(empty)
return;
Point east(centre.x + radius, centre.y), west(centre.x - radius, centre.y);
MoveTo(east);
ArcTo(west, radius);
ArcTo(east, radius);
}
void LineDraw::Ellipse(const Rect& rc)
{
enum { SEGMENTS = 16 };
Point centre = rc.CenterPoint();
Size radius = rc.Size() >> 1;
Point beg = Point(centre.x + radius.cx, centre.y);
MoveTo(beg);
for(int i = 1; i <= SEGMENTS; i++)
{
double a = i * (2 * M_PI / SEGMENTS) - M_PI / SEGMENTS;
Point mid(centre.x + fround(radius.cx * cos(a)), centre.y + fround(radius.cy * sin(a)));
a += M_PI / SEGMENTS;
Point end(centre.x + fround(radius.cx * cos(a)), centre.y + fround(radius.cy * sin(a)));
int bulge = 0;
int dist = Squared(end - beg);
if(dist)
bulge = fround(tabs(VectorProduct(mid - beg, end - beg)) / sqrt(double(dist)));
ArcTo(end, bulge);
beg = end;
}
}
void Plotter::Set(Draw& _draw, const Rectf& _src, const Rectf& _dest, int flags, int reserve, double meter)
{
Rectf src = _src;
if(src.right <= src.left) src.right = src.left + 1;
if(src.bottom <= src.top) src.bottom = src.top + 1;
Rectf dest = _dest;
if(flags & MIRROR_X) Swap(dest.left, dest.right);
if(flags & MIRROR_Y) Swap(dest.top, dest.bottom);
Pointf scale = dest.Size() / src.Size();
if(flags & ISOTROPIC)
{
scale.x = sgn(scale.x) * AbsMin(scale);
scale.y = sgn(scale.y) * fabs(scale.x);
}
Pointf delta = dest.CenterPoint() - src.CenterPoint() * scale;
Set(_draw, scale, delta, reserve, meter);
}
void Plotter::Set(Draw& _draw, Sizef _scale, Pointf _delta, int reserve, double meter)
{
Set(_draw, Matrixf(Pointf(_scale.cx, 0), Pointf(0, _scale.cy), _delta), reserve, meter);
}
void Plotter::Set(Draw& _draw, const Matrixf& matrix, int reserve, double meter)
{
Rect rc = _draw.GetPageSize(); //GetClip();
if(reserve < 0)
reserve = DotsToPixels(_draw, -reserve);
rc.Inflate(reserve);
Set(_draw, matrix, rc, meter);
}
void Plotter::Set(Draw& _draw, const Matrixf& _transform, const Rect& _clip, double meter)
{
physical = _transform;
logical = MatrixfInverse(_transform);
measure = fabs(MatrixfMeasure(physical));
logprec = 1.0 / measure;
ortho = (physical.x.y == 0 && physical.y.x == 0);
ltop = (ortho ? &Plotter::LtoPOrtho : &Plotter::LtoPFull);
ltopoint = (ortho ? &Plotter::LtoPointOrtho : &Plotter::LtoPointFull);
SetDraw(_draw, _clip, meter);
}
void Plotter::SetDraw(Draw& _draw, const Rect& _clip, double meter)
{
draw = &_draw;
SetPixelsPerDot(GetAvgPixelsPerDot(*draw));
clip = _clip;
logclip = PtoL(clip);
logdiag = Diagonal(logclip);
path_map = PATH_MAP_NULL;
SetMapMeters(meter);
}
void Plotter::Set(const Plotter& info, const Rect& clip)
{
ASSERT(info.draw);
Set(*info.draw, info.physical, clip, info.map_meters);
path_map = info.path_map;
pixels_per_dot = info.pixels_per_dot;
SetMapMeters(info.map_meters);
}
void Plotter::Set(const Plotter& info, int reserve)
{
ASSERT(info.draw);
Set(*info.draw, info.physical, reserve);
path_map = info.path_map;
pixels_per_dot = info.pixels_per_dot;
SetMapMeters(info.map_meters);
}
void Plotter::SetMapMeters(double mm)
{
map_meters = mm;
physical_scale = fabs(measure) / (map_meters * GetPixelsPerMeter());
LLOG("Plotter::SetMapMeters(" << mm << "): pixel per dot = " << pixels_per_dot << ", physical scale = " << physical_scale);
}
void Plotter::SetXorMode(bool xm)
{
if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(draw)) {
#ifdef PLATFORM_WIN32
SetROP2(*sdraw, xm ? R2_NOTXORPEN : R2_COPYPEN);
#endif
#ifdef PLATFORM_X11
XSetFunction(Xdisplay, sdraw->GetGC(), xm ? X11_ROP2_NOT_XOR : X11_ROP2_COPY);
#endif
}
}
static inline void AddRectMatrix(Rectf& out, const Rectf& rc, const Matrixf& mx)
{
out.left += mx.x.x * (mx.x.x >= 0 ? rc.left : rc.right) + mx.y.x * (mx.y.x >= 0 ? rc.top : rc.bottom);
out.top += mx.x.y * (mx.x.y >= 0 ? rc.left : rc.right) + mx.y.y * (mx.y.y >= 0 ? rc.top : rc.bottom);
out.right += mx.x.x * (mx.x.x <= 0 ? rc.left : rc.right) + mx.y.x * (mx.y.x <= 0 ? rc.top : rc.bottom);
out.bottom += mx.x.y * (mx.x.y <= 0 ? rc.left : rc.right) + mx.y.y * (mx.y.y <= 0 ? rc.top : rc.bottom);
}
Rectf Plotter::LtoPrel(const Rectf& rc) const
{
Rectf result(0, 0, 0, 0);
AddRectMatrix(result, rc, physical);
return result;
}
Rectf Plotter::LtoP(const Rectf& rc) const
{
Rectf result = PointfRectf(physical.a);
AddRectMatrix(result, rc, physical);
return result;
}
Rectf Plotter::PtoLrel(const Rectf& rc) const
{
Rectf result = Rectf(0, 0, 0, 0);
AddRectMatrix(result, rc, logical);
return result;
}
Rectf Plotter::PtoL(const Rectf& rc) const
{
Rectf result = PointfRectf(logical.a);
AddRectMatrix(result, rc, logical);
return result;
}
Point Plotter::LtoPointFull(Pointf pt) const
{
#ifdef MSC_ASSEMBLY
Point result;
const Matrixf& mx = physical;
__asm mov ebx, [mx]
__asm fld qword ptr [ebx]Matrixf.a.y
__asm fld qword ptr [ebx]Matrixf.a.x
__asm fld qword ptr [pt]Pointf.x
__asm fld st(0)
__asm fmul qword ptr [ebx]Matrixf.x.x
__asm faddp st(2), st
__asm fmul qword ptr [ebx]Matrixf.x.y
__asm faddp st(2), st
__asm fld qword ptr [pt]Pointf.y
__asm fld st(0)
__asm fmul qword ptr [ebx]Matrixf.y.x
__asm faddp st(2), st
__asm fmul qword ptr [ebx]Matrixf.y.y
__asm faddp st(2), st
__asm fistp dword ptr [result + 0]
__asm fistp dword ptr [result + 4]
return result;
#else
return PointfToPoint(pt * physical);
#endif
}
Point Plotter::LtoPointOrtho(Pointf pt) const
{
#ifdef MSC_ASSEMBLY
Point result;
const Matrixf& mx = physical;
__asm mov ebx, [mx]
__asm fld qword ptr [ebx]Matrixf.a.y
__asm fld qword ptr [ebx]Matrixf.a.x
__asm fld qword ptr [pt]Pointf.y
__asm fmul qword ptr [ebx]Matrixf.y.y
__asm fld qword ptr [pt]Pointf.x
__asm fmul qword ptr [ebx]Matrixf.x.x
__asm faddp st(2), st
__asm faddp st(2), st
__asm fistp dword ptr [result + 0]
__asm fistp dword ptr [result + 4]
return result;
#else
return Point((int)(pt.x * physical.x.x + physical.a.x), (int)(pt.y * physical.y.y + physical.a.y));
#endif
}
static void PaintRectPart(Draw& draw, int x, int y, int w, int h)
{
if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(&draw)) {
#if defined(PLATFORM_WIN32)
::PatBlt(*sdraw, x, y, w, h, PATINVERT);
#elif defined(PLATFORM_X11)
Point offset = sdraw->GetOffset();
XFillRectangle(Xdisplay, sdraw->GetDrawable(), sdraw->GetGC(), x + offset.x, y + offset.y, w, h);
#endif
}
}
static void PaintRectPart(Draw& draw, const Rect& rc) { PaintRectPart(draw, rc.left, rc.top, rc.Width(), rc.Height()); }
enum { DRAG_STEP = 4 };
void PaintDragHorzLine(Draw& draw, const Rect& rc, Color c1, Color c2, Color bgnd, int mingap)
{
Size sz = rc.Size();
if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(&draw)) {
#if defined(PLATFORM_WIN32)
sdraw->BeginGdi();
static word bmp_bits[8];
HBITMAP hbmp = CreateBitmap(8, 8, 1, 1, bmp_bits);
HBRUSH brush = CreatePatternBrush(hbmp);
DeleteObject(hbmp);
COLORREF cc1 = (COLORREF)c1 ^ (COLORREF)bgnd, cc2 = (COLORREF)c2 ^ (COLORREF)bgnd;
COLORREF old_bk = SetTextColor(*sdraw, cc1);
HGDIOBJ old_brush = SelectObject(*sdraw, brush);
#elif defined(PLATFORM_X11)
XGCValues gcv_old, gcv_new;
XGetGCValues(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
gcv_new.function = X11_ROP2_XOR;
gcv_new.foreground = GetXPixel(c1) ^ GetXPixel(bgnd);
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_new);
#endif
int whites = (sz.cx - 2 * mingap + DRAG_STEP) / (2 * DRAG_STEP);
if(rc.Width() <= 2 * mingap)
PaintRectPart(*sdraw, rc);
else if(whites <= 0) {
PaintRectPart(*sdraw, rc.left, rc.top, mingap, sz.cy);
PaintRectPart(*sdraw, rc.right - mingap, rc.top, mingap, sz.cy);
#if defined(PLATFORM_WIN32)
SetTextColor(*sdraw, cc2);
#elif defined(PLATFORM_X11)
gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
#endif
PaintRectPart(*sdraw, rc.left + mingap, rc.top, sz.cx - 2 * mingap, sz.cy);
}
else
{
int rem = sz.cx - whites * 2 * DRAG_STEP + DRAG_STEP;
int lrem = rem >> 1, rrem = (rem + 1) >> 1;
PaintRectPart(*sdraw, rc.left, rc.top, lrem, sz.cy);
PaintRectPart(*sdraw, rc.right - rrem, rc.top, rrem, sz.cy);
int i;
int start = rc.left + lrem - DRAG_STEP;
for(i = 1; i < whites; i++)
PaintRectPart(*sdraw, start + 2 * DRAG_STEP * i, rc.top, DRAG_STEP, sz.cy);
#if defined(PLATFORM_WIN32)
SetTextColor(*sdraw, cc2);
#elif defined(PLATFORM_X11)
gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
#endif
start = rc.left + lrem;
for(i = 0; i < whites; i++)
PaintRectPart(*sdraw, start + 2 * DRAG_STEP * i, rc.top, DRAG_STEP, sz.cy);
}
#if defined(PLATFORM_WIN32)
SelectObject(*sdraw, old_brush);
DeleteObject(brush);
SetTextColor(*sdraw, old_bk);
sdraw->EndGdi();
#elif defined(PLATFORM_X11)
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
#endif
}
}
void PaintDragVertLine(Draw& draw, const Rect& rc, Color c1, Color c2, Color bgnd, int mingap)
{
Size sz = rc.Size();
if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(&draw)) {
#if defined(PLATFORM_WIN32)
sdraw->BeginGdi();
static word bmp_bits[8];
HBITMAP hbmp = CreateBitmap(8, 8, 1, 1, bmp_bits);
HBRUSH brush = CreatePatternBrush(hbmp);
DeleteObject(hbmp);
COLORREF cc1 = (COLORREF)c1 ^ (COLORREF)bgnd, cc2 = (COLORREF)c2 ^ (COLORREF)bgnd;
COLORREF old_bk = SetTextColor(*sdraw, cc1);
HGDIOBJ old_brush = SelectObject(*sdraw, brush);
#elif defined(PLATFORM_X11)
XGCValues gcv_old, gcv_new;
XGetGCValues(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
gcv_new.function = X11_ROP2_XOR;
gcv_new.foreground = GetXPixel(c1) ^ GetXPixel(bgnd);
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_new);
#endif
int whites = (sz.cy - 2 * mingap + DRAG_STEP) / (2 * DRAG_STEP);
if(rc.Height() <= 2 * mingap)
PaintRectPart(*sdraw, rc);
else if(whites <= 0)
{
PaintRectPart(*sdraw, rc.left, rc.top, sz.cx, mingap);
PaintRectPart(*sdraw, rc.left, rc.bottom - mingap, sz.cx, mingap);
#if defined(PLATFORM_WIN32)
SetTextColor(*sdraw, cc2);
#elif defined(PLATFORM_X11)
gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
#endif
PaintRectPart(*sdraw, rc.left, rc.top + mingap, sz.cx, sz.cy - 2 * mingap);
}
else
{
int rem = sz.cy - whites * 2 * DRAG_STEP + DRAG_STEP;
int lrem = rem >> 1, rrem = (rem + 1) >> 1;
PaintRectPart(*sdraw, rc.left, rc.top, sz.cx, lrem);
PaintRectPart(*sdraw, rc.left, rc.bottom - rrem, sz.cx, rrem);
int i;
int start = rc.top + lrem - DRAG_STEP;
for(i = 1; i < whites; i++)
PaintRectPart(*sdraw, rc.left, start + 2 * DRAG_STEP * i, sz.cx, DRAG_STEP);
#if defined(PLATFORM_WIN32)
SetTextColor(*sdraw, cc2);
#elif defined(PLATFORM_X11)
gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
#endif
start = rc.left + lrem;
for(i = 0; i < whites; i++)
PaintRectPart(*sdraw, rc.left, start + 2 * DRAG_STEP * i, sz.cx, DRAG_STEP);
}
#if defined(PLATFORM_WIN32)
SelectObject(*sdraw, old_brush);
DeleteObject(brush);
SetTextColor(*sdraw, old_bk);
sdraw->EndGdi();
#elif defined(PLATFORM_X11)
XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
#endif
}
}
void PaintDragRect(Draw& draw, const Rect& rc, Color c1, Color c2, Color bgnd, int width)
{
if(rc.IsEmpty())
return;
if(rc.Height() <= 2 * width)
PaintDragHorzLine(draw, rc, LtRed, White, SWhite, width);
else if(rc.Width() <= 2 * width)
PaintDragVertLine(draw, rc, LtRed, White, SWhite, width);
else {
PaintDragHorzLine(draw, Rect(rc.left, rc.top, rc.right, rc.top + width), LtRed, White, SWhite, width);
PaintDragHorzLine(draw, Rect(rc.left, rc.bottom - width, rc.right, rc.bottom), LtRed, White, SWhite, width);
PaintDragVertLine(draw, Rect(rc.left, rc.top + width, rc.left + width, rc.bottom - width), LtRed, White, SWhite, 0);
PaintDragVertLine(draw, Rect(rc.right - width, rc.top + width, rc.right, rc.bottom - width), LtRed, White, SWhite, 0);
}
}
void PlotDragRect(Plotter& plotter, const Rectf& r)
{
if(!IsNull(r))
{
Rect rc = RectfToRect(plotter.LtoP(r).Inflated(1, 1, 2, 2)) & plotter.clip.Inflated(10);
PaintDragRect(plotter.GetDraw(), rc, LtRed, White, SWhite, 3);
}
}
PlotterTool::PlotterTool()
: last_point(0, 0)
, clip_arcs(true)
, LineToRaw(0) // acts like a pure virtual function: throws exception when called
, MoveToRaw(0)
{
}
PlotterTool::~PlotterTool() {}
void PlotterTool::Paint()
{
}
void PlotterTool::Clear()
{
}
void PlotterTool::Line(const Array<Pointf>& points)
{
if(!points.IsEmpty()) {
bool line = false;
for(int i = 0; i < points.GetCount(); i++)
if(IsNull(points[i]))
line = false;
else {
DrawTo(points[i], line);
line = true;
}
}
}
bool PlotterTool::ArcToRaw(Pointf pt, double bulge)
{
return false;
}
void PlotterTool::ArcTo(Pointf pt, double bulge, int levels)
{
if(fabs(bulge) <= plotter.logprec)
LineTo(pt);
else
{
VecArcIterator arc(last_point, pt, bulge, LineToRaw);
arc.Precision(plotter.logprec).ArcTo(THISBACK(ArcToRaw));
if(clip_arcs)
arc.Clip(plotter.logclip, MoveToRaw);
arc.Go();
}
}
void PlotterTool::Rectangle(double x1, double y1, double x2, double y2, bool r)
{
MoveTo(x1, y1);
LineTo(x2, y1);
LineTo(x2, y2);
if(!r || min(fabs(x2 - x1), fabs(y2 - y1)) * plotter.measure > 1)
{
LineTo(x1, y2);
LineTo(x1, y1);
}
}
void PlotterTool::Ellipse(const Rectf& rc)
{
enum { SEGMENTS = 16 };
Pointf centre = rc.CenterPoint();
Pointf radius = Sizef(rc.Size()) / 2.0;
Pointf beg(centre.x + radius.x, centre.y);
MoveTo(beg);
// int sign = sgn(plotter.measure);
for(int i = 1; i <= SEGMENTS; i++)
{
double a = i * (2 * M_PI / SEGMENTS) - M_PI / SEGMENTS;
Pointf mid = centre + radius * PolarPointf(a);
Pointf end = centre + radius * PolarPointf(a + M_PI / SEGMENTS);
double bulge = 0, dist = end | beg;
if(dist)
bulge = fabs((mid - beg) % (end - beg)) / dist;
ArcTo(end, bulge);
beg = end;
}
}
void PlotterTool::ArrowHead(Pointf P, Pointf Q, double length, double angle)
{
double base = Bearing(P, Q);
Line(PolarPointf(Q, length, base + angle), Q);
Line(PolarPointf(Q, length, base - angle), Q);
}
void PlotterTool::Arrow(Pointf P, Pointf Q, double length, double angle)
{
Line(P, Q);
ArrowHead(P, Q, min(length, P | Q), angle);
}
void PlotterTool::BiArrow(Pointf P, Pointf Q, double length, double angle)
{
Line(P, Q);
length = min(length, (P | Q) / 2);
ArrowHead(P, Q, length, angle);
ArrowHead(Q, P, length, angle);
}
void PlotterTool::Circle(double x, double y, double radius)
{
Pointf right(x + radius, y);
Pointf left(x - radius, y);
MoveTo(right);
ArcTo(left, radius);
ArcTo(right, radius);
}
#ifdef PLOTTER_TIMING
int PathTool::segments = 0;
int PathTool::drawn = 0;
long PathTool::ticks = 0;
#endif
PathTool::PathTool()
: use_last(false)
, dummy(true)
{
ClearExtent();
}
PathTool::~PathTool() {}
void PathTool::Reset()
{
dummy = true;
ClearExtent();
}
bool PathTool::Set(const Plotter& _info, const String& pattern, Color color, int width, double dash)
{
if(dummy = IsNull(color)) {
Reset();
return false;
}
Clear();
plotter = _info;
pathdraw.Set(*plotter.draw, plotter.GetPath(pattern), color, width, dash, false);
use_last = false;
return true;
}
bool PathTool::SetExtent(const Rectf& rc)
{
if(!dummy && rc.Intersects(plotter.logclip)) {
Rectf rcc = plotter.LtoP(rc);
simple = rcc.left >= plotter.clip.left && rcc.top >= plotter.clip.top
&& rcc.right <= plotter.clip.right && rcc.bottom <= plotter.clip.bottom;
bool within = true;
if(simple)
within = pathdraw.SetExtent(RectfToRect(rcc));
else
pathdraw.ClearExtent();
MoveToRaw = (simple ? THISBACK(MoveToRawSimple) : THISBACK(MoveToRawClip));
LineToRaw = (simple ? THISBACK(LineToRawSimple) : THISBACK(LineToRawClip));
return within;
}
else {
MoveToRaw = THISBACK(MoveToRawDummy);
LineToRaw = THISBACK(LineToRawDummy);
return false;
}
}
void PathTool::ClearExtent()
{
if(dummy) {
MoveToRaw = THISBACK(MoveToRawDummy);
LineToRaw = THISBACK(LineToRawDummy);
}
else {
MoveToRaw = THISBACK(MoveToRawClip);
LineToRaw = THISBACK(LineToRawClip);
simple = false;
pathdraw.ClearExtent();
}
}
void PathTool::Paint()
{
if(!dummy && plotter.draw)
pathdraw.Paint();
ClearExtent();
}
void PathTool::Clear()
{
pathdraw.Clear();
ClearExtent();
}
void PathTool::MoveToRawDummy(Pointf pt) {}
void PathTool::LineToRawDummy(Pointf pt) {}
void PathTool::MoveToRawSimple(Pointf pt)
{
pathdraw.MoveTo(plotter.LtoPoint(last_point = pt));
}
void PathTool::MoveToRawClip(Pointf pt)
{
last_phys = plotter.LtoP(last_point = pt);
use_last = false;
}
void PathTool::LineToRawSimple(Pointf pt)
{
if(IsNull(pt.x) || IsNull(pt.y))
{
use_last = false;
return;
}
#ifdef PLOTTER_TIMING
ticks -= GetTickCount();
segments++;
drawn++;
#endif
pathdraw.LineTo(plotter.LtoPoint(pt));
}
void PathTool::LineToRawClip(Pointf pt)
{
if(IsNull(pt.x) || IsNull(pt.y))
{
use_last = false;
return;
}
ASSERT(pt.x <= 1e100);
#ifdef PLOTTER_TIMING
ticks -= GetTickCount();
segments++;
#endif
Pointf B = plotter.LtoP(pt);
#if CHECK_DIST
if(use_last && fabs(B.x - last_phys.x) < 1 && fabs(B.y - last_phys.y) < 1)
{
#ifdef PLOTTER_TIMING
ticks += GetTickCount();
#endif
return;
}
#endif//CHECK_DIST
Pointf A = last_phys, end = B;
last_point = pt;
last_phys = B;
if(ClipLine(A, B, plotter.clip))
{ // draw line
#ifdef PLOTTER_TIMING
drawn++;
#endif
if(!use_last)
pathdraw.MoveTo(PointfToPoint(A));
pathdraw.LineTo(PointfToPoint(B));
use_last = (B | end) <= 1;
}
else
use_last = false;
#ifdef PLOTTER_TIMING
ticks += GetTickCount();
#endif
}
bool PathTool::ArcToRaw(Pointf pt, double bulge)
{
if(bulge >= plotter.logdiag)
return false;
if(bulge <= plotter.logprec)
{
LineTo(pt);
return true;
}
Pointf pt_phys = plotter.LtoP(pt);
pathdraw.Arc(PointfToPoint(last_phys), PointfToPoint(pt_phys), fround(bulge * fabs(plotter.measure)));
last_point = pt;
last_phys = pt_phys;
return true;
}
AreaTool::AreaTool()
{
prev_ghost = false;
clip_arcs = false;
fill_color = Black;
dummy = true;
Clear();
}
AreaTool::~AreaTool() {}
bool AreaTool::SetExtent(const Rectf& rc)
{
if(!dummy && rc.Intersects(plotter.logclip)) {
Rectf rcc = plotter.LtoP(rc);
simple = rcc.left >= plotter.clip.left && rcc.top >= plotter.clip.top
&& rcc.right <= plotter.clip.right && rcc.bottom <= plotter.clip.bottom;
MoveToRaw = (simple ? THISBACK(MoveToRawSimple) : THISBACK(MoveToRawClip));
LineToRaw = (simple ? THISBACK(LineToRawSimple) : THISBACK(LineToRawClip));
return true;
}
else {
MoveToRaw = THISBACK(MoveToRawDummy);
LineToRaw = THISBACK(LineToRawDummy);
return false;
}
}
void AreaTool::ClearExtent()
{
if(dummy) {
MoveToRaw = THISBACK(MoveToRawDummy);
LineToRaw = THISBACK(LineToRawDummy);
}
else {
MoveToRaw = THISBACK(MoveToRawClip);
LineToRaw = THISBACK(LineToRawClip);
}
simple = false;
}
void AreaTool::Clear()
{
begin_index = 0;
disjunct_begin_index = 0;
last_start = Null;
last_in = true;
vertices.Clear();
counts.Clear();
disjunct_counts.Clear();
ClearExtent();
}
void AreaTool::MoveToRawDummy(Pointf pt) {}
void AreaTool::LineToRawDummy(Pointf pt) {}
void AreaTool::MoveToRawSimple(Pointf pt)
{
Flush();
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(plotter.LtoPoint(last_point = pt));
}
void AreaTool::MoveToRawClip(Pointf pt)
{
Flush();
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(ClipBind(last_phys = plotter.LtoP(last_point = last_start = pt)));
last_in = (last_phys.x >= plotter.clip.left && last_phys.x <= plotter.clip.right
&& last_phys.y >= plotter.clip.top && last_phys.y <= plotter.clip.bottom);
}
void AreaTool::LineToRawSimple(Pointf pt)
{
// RTIMING("AreaTool::LineToRawSimple");
Point ipt = plotter.LtoPoint(last_point = pt);
if(ipt != vertices.Top())
{
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(ipt);
}
}
void AreaTool::LineToRawClip(Pointf pt)
{
if(IsNull(pt.x) || IsNull(pt.y))
return;
// RTIMING("AreaTool::LineToRawClip");
#if DEBUG_AREA
Pointf anchor(-747102, -1043537);
bool show = (pt | anchor) <= 500 || (last_point | anchor) <= 500;
#endif
Pointf next_phys = plotter.LtoP(pt);
#if CHECK_DIST
if(fabs(next_phys.x - last_phys.x) < 1 && fabs(next_phys.y - last_phys.y) < 1)
return;
#endif//CHECK_DIST
last_point = pt;
bool next_in = (next_phys.x >= plotter.clip.left && next_phys.x <= plotter.clip.right
&& next_phys.y >= plotter.clip.top && next_phys.y <= plotter.clip.bottom);
#if DEBUG_AREA
int vc = vertices.GetCount();
if(show) {
LOG("[" << vc << "/" << begin_index << "] AreaTool::LineToRawClip " << pt << ", next_phys " << next_phys
<< ", top = " << vertices.Top() << ", last_in " << last_in << ", next_in " << next_in);
}
#endif
if(next_in && last_in) {
Point ppt = PointfToPoint(next_phys);
if(ppt != vertices.Top())
{
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(ppt);
}
last_phys = next_phys;
last_in = next_in;
}
else {
Pointf A = last_phys, B = next_phys;
bool clipped = ClipLine(A, B, plotter.clip);
Point cb = ClipBind(clipped ? B : next_phys);
Point ca = clipped ? ClipBind(A) : cb;
if(vertices.GetCount() > begin_index || clipped)
SkipTo(ca, (next_phys - last_phys) % (clip_center - last_phys) >= 0);
if(clipped && cb != vertices.Top()) {
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(cb);
}
last_phys = next_phys;
last_in = (clipped && B == next_phys);
}
#if DEBUG_AREA
Point check = plotter.LtoP(anchor);
for(int t = vc; t < vertices.GetCount(); t++)
if(tabs(vertices[t].x - check.x) <= 50 && tabs(vertices[t].y - check.y) <= 50)
{
LOG("AreaTool::LineToRawClip / orphan [" << t << "] = " << vertices[t] << " for " << pt);
}
if(show)
{
String plist;
while(vc < vertices.GetCount())
plist << " " << vertices[vc++];
LOG("//AreaTool::LineToRawClip " << plist);
}
#endif
}
void AreaTool::SkipTo(Point pt, bool clockwise)
{
/*
if(vertices.GetCount() == begin_index)
{
vertices.Add(pt);
return;
}
*/
Point la = vertices.Top();
if(la == pt)
return;
// Size diff(pt.x + la.x - plotter.clip.left - plotter.clip.right, pt.y + la.y - plotter.clip.top - plotter.clip.bottom);
if(la.y <= plotter.clip.top) {
if(pt.y <= plotter.clip.top)
Horz(pt.x);
else if(pt.y >= plotter.clip.bottom)
Horz(clockwise ? plotter.clip.right : plotter.clip.left).Vert(plotter.clip.bottom).Horz(pt.x);
else if(pt.x <= plotter.clip.left)
Horz(plotter.clip.left).Vert(pt.y);
else if(pt.x >= plotter.clip.right)
Horz(plotter.clip.right).Vert(pt.y);
else
NEVER();
}
else if(la.y >= plotter.clip.bottom) {
if(pt.y <= plotter.clip.top)
Horz(clockwise ? plotter.clip.left : plotter.clip.right).Vert(plotter.clip.top).Horz(pt.x);
else if(pt.y >= plotter.clip.bottom)
Horz(pt.x);
else if(pt.x <= plotter.clip.left)
Horz(plotter.clip.left).Vert(pt.y);
else if(pt.x >= plotter.clip.right)
Horz(plotter.clip.right).Vert(pt.y);
else
NEVER();
}
else if(la.x <= plotter.clip.left) {
if(pt.y <= plotter.clip.top)
Vert(plotter.clip.top).Horz(pt.x);
else if(pt.y >= plotter.clip.bottom)
Vert(plotter.clip.bottom).Horz(pt.x);
else if(pt.x <= plotter.clip.left)
Vert(pt.y);
else if(pt.x >= plotter.clip.right)
Vert(clockwise ? plotter.clip.top : plotter.clip.bottom).Horz(plotter.clip.right).Vert(pt.y);
else
NEVER();
}
else if(la.x >= plotter.clip.right) {
if(pt.y <= plotter.clip.top)
Vert(plotter.clip.top).Horz(pt.x);
else if(pt.y >= plotter.clip.bottom)
Vert(plotter.clip.bottom).Horz(pt.x);
else if(pt.x <= plotter.clip.left)
Vert(clockwise ? plotter.clip.bottom : plotter.clip.top).Horz(plotter.clip.left).Vert(pt.y);
else if(pt.x >= plotter.clip.right)
Vert(pt.y);
else
NEVER();
}
else
NEVER(); // invalid case
}
void AreaTool::Flush()
{
if((vertices.GetCount() - begin_index >= 3) && !simple) {
ASSERT(!IsNull(last_start));
LineTo(last_start);
}
int add = vertices.GetCount() - begin_index;
ASSERT(add >= 0);
if(add >= 2 && vertices.Top() == vertices[begin_index]) {
vertices.Drop(); // remove superfluous end point
if(!ghost_lines.IsEmpty() && ghost_lines.Top() == vertices.GetCount())
ghost_lines.Drop();
add--;
}
if(add <= 2) {
vertices.SetCountR(begin_index);
while(!ghost_lines.IsEmpty() && ghost_lines.Top() >= begin_index)
ghost_lines.Drop();
}
else {
counts.Add(add);
begin_index = vertices.GetCount();
}
}
AreaTool& AreaTool::Horz(int x)
{
int count = vertices.GetCount() - begin_index;
ASSERT(count > 0);
Point *p = vertices.End();
int y = p[-1].y;
if(count >= 2 && y == p[-2].y) {
while(count > 2 && y == p[-3].y) {
count--;
p--;
}
p[-1].x = x;
int end = begin_index + count;
vertices.SetCountR(end);
while(!ghost_lines.IsEmpty() && ghost_lines.Top() >= end)
ghost_lines.Drop();
}
else {
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(Point(x, y));
}
return *this;
}
AreaTool& AreaTool::Vert(int y)
{
int count = vertices.GetCount() - begin_index;
ASSERT(count > 0);
Point *p = vertices.End();
int x = p[-1].x;
if(count >= 2 && x == p[-2].x) {
while(count > 2 && x == p[-3].x) {
count--;
p--;
}
p[-1].y = y;
int end = begin_index + count;
vertices.SetCountR(end);
while(!ghost_lines.IsEmpty() && ghost_lines.Top() >= end)
ghost_lines.Drop();
}
else {
if(prev_ghost)
ghost_lines.Add(vertices.GetCount());
vertices.Add(Point(x, y));
}
return *this;
}
void AreaTool::Reset()
{
dummy = true;
Clear();
}
bool AreaTool::Set(const Plotter& _info, Color _fill_color, uint64 _fill_pattern,
const String& outline_pattern, Color outline_color, int outline_width, double outline_dash)
{
dummy = IsNull(_fill_color) && IsNull(outline_color);
if(dummy) {
Reset();
return false;
}
if(outline_width < 0)
outline_width = DotsToPixels(*_info.draw, -outline_width);
const PathStyle& path = _info.GetPath(outline_pattern);
plotter = Plotter(_info, int(outline_width * path.width) + 2);
clip_center = Pointf(plotter.clip.CenterPoint());
fill_color = _fill_color;
fill_pattern = _fill_pattern;
double pw;
Color pc;
bool is_solid = path.IsSolid(pw, pc);
outline_pixels = (outline_width >= 0 ? outline_width : fround(_info.GetPixelsPerDot() * -outline_width));
pw *= outline_width;
is_line = !path.IsEmpty();
std_pen = !is_line || IsNull(outline_color)
|| (ghost_lines.IsEmpty() && is_solid && pw < (_info.draw->Dots() ? 20 : 2.5) /*&& !IsNull(fill_color)*/ && !fill_pattern);
if(std_pen) {
fill_outline_color = is_solid ? Nvl(pc, outline_color) : Color(Null);
fill_outline_style = is_solid ? fround(pw) : 0;
if(_info.draw->Pixels() && outline_width > 1)
{
thick_outline_color = fill_outline_color;
fill_outline_color = Null;
fill_outline_style = 0;
}
}
else {
raw_outline.Set(*plotter.draw, path, outline_color, outline_width, outline_dash, true);
fill_outline_color = thick_outline_color = Null;
fill_outline_style = 0;
}
point_pixels = outline_pixels;
point_color = Nvl(Nvl(pc, outline_color), fill_color);
Clear();
#if DEBUG_AREA
LOG("AreaTool::Set, phys clip = " << plotter.clip << ", log clip = " << plotter.logclip
<< ", a = " << plotter.physical.a);
#endif
return true;
}
void AreaTool::FlushFill()
{
Flush();
if(IsFill() && disjunct_begin_index < vertices.GetCount())
{
disjunct_counts.Add(vertices.GetCount() - disjunct_begin_index);
disjunct_begin_index = vertices.GetCount();
}
if(vertices.GetCount() >= FLUSH_BATCH)
Paint();
}
void AreaTool::Paint()
{
if(!plotter.draw)
return;
if(!(plotter.clip.Intersects(plotter.draw->GetPageSize /*GetClip*/()))) {
Clear();
return;
}
Flush();
if(vertices.IsEmpty() || counts.IsEmpty())
{
Clear();
return;
}
if(disjunct_begin_index < vertices.GetCount())
disjunct_counts.Add(vertices.GetCount() - disjunct_begin_index);
if(!IsNull(fill_color) || !IsNull(fill_outline_color))
DrawPolyPolyPolygon(*plotter.draw, vertices, counts, disjunct_counts,
fill_color, fill_outline_style, fill_outline_color, fill_pattern);
if(!IsNull(thick_outline_color))
DrawFatPolyPolygonOutline(*plotter.draw, vertices, counts,
thick_outline_color, outline_pixels);
/*
else if(fill_pattern)
{
// RTIMING("AreaTool::Fill(pattern)");
HDC hdc = *plotter.draw;
int old_rop = SetROP2(hdc, R2_MASKPEN);
HGDIOBJ old_brush = SelectObject(hdc, fill_pattern.GetBrush(Null, Null));
int old_color = SetTextColor(hdc, Black);
int old_bk = SetBkColor(hdc, White);
plotter.draw->SetDrawPen(PEN_NULL, Black);
if(counts.GetCount() == 1)
Polygon(*plotter.draw, (const POINT *)vertices.Begin(), vertices.GetCount());
else
PolyPolygon(*plotter.draw, (const POINT *)vertices.Begin(), counts.Begin(), counts.GetCount());
SetROP2(hdc, R2_MERGEPEN);
if(IsNull(fill_color)) // use color fill brush
SelectObject(hdc, fill_pattern.GetBrush(Null, Black));
else
{ // just change text color
SetTextColor(hdc, fill_color);
SetBkColor(hdc, Black);
}
if(counts.GetCount() == 1)
Polygon(*plotter.draw, (const POINT *)vertices.Begin(), vertices.GetCount());
else
PolyPolygon(*plotter.draw, (const POINT *)vertices.Begin(), counts.Begin(), counts.GetCount());
SetTextColor(hdc, old_color);
SetBkColor(hdc, old_bk);
SetROP2(hdc, old_rop);
SelectObject(hdc, old_brush);
}
else if(!IsNull(fill_color) || (std_pen && raw_outline_style != PEN_NULL))
{ // simple fill
// RTIMING("AreaTool::Fill(solid color)");
plotter.draw->SetDrawPen(raw_outline_style, raw_outline_color);
HGDIOBJ old_brush = 0;
if(IsNull(fill_color))
{
static HGDIOBJ null_brush = GetStockObject(NULL_BRUSH);
old_brush = SelectObject(*plotter.draw, null_brush);
}
else
plotter.draw->SetColor(fill_color);
if(counts.GetCount() == 1)
Polygon(*plotter.draw, (const POINT *)vertices.Begin(), vertices.GetCount());
else
PolyPolygon(*plotter.draw, (const POINT *)vertices.Begin(), counts.Begin(), counts.GetCount());
if(old_brush)
SelectObject(*plotter.draw, old_brush);
}
*/
if(!std_pen)
{ // manual outline
// RTIMING("AreaTool::Outline()");
if(ghost_lines.IsEmpty())
{
const Point *p = vertices.Begin();
for(int i = 0; i < counts.GetCount(); i++)
{
const Point *b = p, *n = p + counts[i];
raw_outline.MoveTo(*p);
while(++p < n)
raw_outline.LineTo(*p);
/*
if((p[0].x >= plotter.clip.right || p[0].x <= plotter.clip.left) && p[-1].x == p[0].x
|| (p[0].y >= plotter.clip.bottom || p[0].y <= plotter.clip.top) && p[-1].y == p[0].y)
outline.MoveTo(*p);
else
outline.LineTo(*p);
*/
// if((b[0].x >= plotter.clip.right || b[0].x <= plotter.clip.left) && p[-1].x == b[0].x
// || (b[0].y >= plotter.clip.bottom || b[0].y <= plotter.clip.top) && p[-1].y == b[0].y)
// ;
// else
// outline.LineTo(*b);
}
}
else
{
int gl = 0;
int ngl = (gl >= ghost_lines.GetCount() ? vertices.GetCount() : ghost_lines[gl]);
int v = 0;
for(int i = 0; i < counts.GetCount(); i++)
{
int nv = v + counts[i];
if(nv >= v + 2)
{
for(raw_outline.MoveTo(vertices[nv - 1]); v < nv; v++)
{
if(ngl == v)
{
ngl = (++gl >= ghost_lines.GetCount() ? vertices.GetCount() : ghost_lines[gl]);
raw_outline.MoveTo(vertices[v]);
}
else
raw_outline.LineTo(vertices[v]);
}
}
else if(ngl < nv)
ngl = (++gl >= ghost_lines.GetCount() ? vertices.GetCount() : ghost_lines[gl]);
v = nv;
}
}
raw_outline.Paint();
}
Clear();
}
class EmptyMarker : public MarkTool::Marker
{
public:
virtual void Paint(Draw& draw, const Vector<Point>& pt) {}
virtual int GetSize() const { return 0; }
};
One<MarkTool::Marker> MarkTool::Empty()
{
return new EmptyMarker;
}
class ImageMarker : public MarkTool::Marker
{
public:
ImageMarker(const Image& img, int size, Color color, bool antialias, bool ignore_hotspot);
virtual void Paint(Draw& draw, const Vector<Point>& pt);
virtual int GetSize() const { return linear_size; }
private:
Image img;
Size imgsize;
Size outsize;
Point hotspot;
int linear_size;
Color color;
// bool body;
bool mask;
bool antialias;
bool ignore_hotspot;
};
ImageMarker::ImageMarker(const Image& srcimg, int size_, Color color_, bool antialias_, bool ignore_hotspot_)
: imgsize(srcimg.GetSize())
, color(color_)
, antialias(antialias_)
, ignore_hotspot(ignore_hotspot_)
, outsize(0, 0)
{
hotspot = (ignore_hotspot ? (Point)(srcimg.GetSize() >> 1) : srcimg.GetHotSpot());
if(imgsize.cx > 0 && imgsize.cy > 0) {
hotspot = iscale(hotspot, size_, max(imgsize.cx, imgsize.cy));
if(imgsize.cx > imgsize.cy)
outsize = Size(size_, iscale(size_, imgsize.cy, imgsize.cx));
else
outsize = Size(iscale(size_, imgsize.cx, imgsize.cy), size_);
if(imgsize == outsize || outsize.cx >= imgsize.cx && outsize.cy >= imgsize.cy && !antialias)
img = srcimg;
else {
Size xsize = 2 * imgsize;
while(2 * xsize.cx <= outsize.cx && 2 * xsize.cy <= outsize.cy && xsize.cx <= 256 && xsize.cy <= 256)
xsize <<= 2;
Size tmpsize = min(outsize, xsize);
// if(antialias)
img = Rescale(srcimg, tmpsize);
// else RescaleNoAA !! todo
}
imgsize = img.GetSize();
}
linear_size = max(
max(hotspot.x, outsize.cx - hotspot.x),
max(hotspot.y, outsize.cy - hotspot.y));
mask = (srcimg.GetKind() == IMAGE_OPAQUE);
}
void ImageMarker::Paint(Draw& draw, const Vector<Point>& pts)
{
if(img.IsEmpty())
return;
for(int t = 0; t < pts.GetCount(); t++) {
Point pt = pts[t] - hotspot;
draw.DrawImage(Rect(pt, outsize), img);
}
}
One<MarkTool::Marker> MarkTool::Picture(Image pic, int size, Color color, bool antialias, bool ignore_hotspot)
{
return new ImageMarker(pic, size, color, antialias, ignore_hotspot);
}
class AreaMarker : public MarkTool::Marker
{
public:
AreaMarker(int size, Color color, Color outline, int outline_width)
: size(size), color(color), outline(outline), outline_width(outline_width)
{ out_size = (size + outline_width) >> 1; }
virtual int GetSize() const { return out_size; }
public:
int size;
Color color;
Color outline;
int outline_width;
int out_size;
};
class SquareMarker : public AreaMarker
{
public:
SquareMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width) {}
virtual void Paint(Draw& draw, const Vector<Point>& pt);
};
void SquareMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
Vector<Point> outpt;
int obj = pt.GetCount();
outpt.SetCount(4 * obj);
Point *op = outpt.Begin();
int half = size >> 1;
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 4)
{
op[0].x = op[3].x = ip->x - half;
op[0].y = op[1].y = ip->y - half;
op[1].x = op[2].x = ip->x + half;
op[2].y = op[3].y = ip->y + half;
}
Vector<int> outix;
outix.SetCount(obj, 4);
DrawPolygons(draw, outpt, outix, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Square(int size, Color color, Color outline, int outline_width)
{
return new SquareMarker(size, color, outline, outline_width);
}
class Square45Marker : public AreaMarker
{
public:
Square45Marker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width) {}
virtual void Paint(Draw& draw, const Vector<Point>& pt);
};
void Square45Marker::Paint(Draw& draw, const Vector<Point>& pt)
{
Vector<Point> outpt;
int obj = pt.GetCount();
outpt.SetCount(4 * obj);
Point *op = outpt.Begin();
int half = size >> 1;
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 4)
{
op[0].x = ip->x - half;
op[0].y = op[2].y = ip->y;
op[1].x = op[3].x = ip->x;
op[1].y = ip->y + half;
op[2].x = ip->x + half;
op[3].y = ip->y - half;
}
Vector<int> outix;
outix.SetCount(obj, 4);
DrawPolygons(draw, outpt, outix, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Square45(int size, Color color, Color outline, int outline_width)
{
return new Square45Marker(size, color, outline, outline_width);
}
class TriangleMarker : public AreaMarker
{
public:
TriangleMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width) {}
virtual void Paint(Draw& draw, const Vector<Point>& pt);
};
void TriangleMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
int half = size >> 1;
Vector<Point> outpt;
int obj = pt.GetCount();
outpt.SetCount(3 * obj);
Point *op = outpt.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 3)
{
op[0].x = ip->x - half;
op[1].x = ip->x + half;
op[0].y = op[1].y = ip->y + half + 1;
op[2].x = ip->x;
op[2].y = ip->y - half;
}
Vector<int> outix;
outix.SetCount(obj, 3);
DrawPolygons(draw, outpt, outix, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Triangle(int size, Color color, Color outline, int outline_width)
{
return new TriangleMarker(size, color, outline, outline_width);
}
class NablaMarker : public AreaMarker
{
public:
NablaMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width) {}
virtual void Paint(Draw& draw, const Vector<Point>& pt);
};
void NablaMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
int half = size >> 1;
Vector<Point> outpt;
int obj = pt.GetCount();
outpt.SetCount(3 * obj);
Point *op = outpt.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 3)
{
op[0].x = ip->x - half;
op[1].x = ip->x + half;
op[0].y = op[1].y = ip->y - half;
op[2].x = ip->x;
op[2].y = ip->y + half + 1;
}
Vector<int> outix;
outix.SetCount(obj, 3);
DrawPolygons(draw, outpt, outix, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Nabla(int size, Color color, Color outline, int outline_width)
{
return new NablaMarker(size, color, outline, outline_width);
}
class CircleMarker : public AreaMarker
{
public:
CircleMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width) {}
virtual void Paint(Draw& draw, const Vector<Point>& pt);
};
void CircleMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
int half = size >> 1;
for(int t = 0; t < pt.GetCount(); t++)
draw.DrawEllipse(RectC(pt[t].x - half, pt[t].y - half, size, size),
color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Circle(int size, Color color, Color outline, int outline_width)
{
return new CircleMarker(size, color, outline, outline_width);
}
class CrossMarker : public AreaMarker
{
public:
CrossMarker(int size, Color outline, int outline_width)
: AreaMarker(size, Null, outline, outline_width) {}
virtual void Paint(Draw& draw, const Vector<Point>& pt);
};
void CrossMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
int half = size >> 1;
Vector<Point> out;
out.SetCount(pt.GetCount() * 4);
Point *op = out.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 4)
{
op[0].x = ip->x - half;
op[1].x = ip->x + half;
op[0].y = op[1].y = ip->y;
op[2].x = op[3].x = ip->x;
op[2].y = ip->y - half;
op[3].y = ip->y + half;
}
Vector<int> seg;
seg.SetCount(2 * pt.GetCount(), 2);
draw.DrawPolyPolyline(out, seg, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Cross(int size, Color outline, int outline_width)
{
return new CrossMarker(size, outline, outline_width);
}
class DiamondMarker : public AreaMarker
{
public:
DiamondMarker(int size, Color color, Color outline, int outline_width);
virtual void Paint(Draw& draw, const Vector<Point>& pt);
private:
int x2, y2, x4;
};
DiamondMarker::DiamondMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width)
{
y2 = size >> 1;
x2 = size * 17 >> 5;
x4 = x2 >> 1;
}
void DiamondMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
Vector<Point> out;
out.SetCount(pt.GetCount() * 5);
Point *op = out.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 5)
{
op[0].x = ip->x - x4; op[0].y = ip->y + y2;
op[1].x = ip->x + x4; op[1].y = ip->y + y2;
op[2].x = ip->x + x2; op[2].y = ip->y;
op[3].x = ip->x; op[3].y = ip->y - y2;
op[4].x = ip->x - x2; op[4].y = ip->y;
}
Vector<int> seg;
seg.SetCount(pt.GetCount(), 5);
DrawPolygons(draw, out, seg, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Diamond(int size, Color color, Color outline, int outline_width)
{
return new DiamondMarker(size, color, outline, outline_width);
}
class HexagonMarker : public AreaMarker
{
public:
HexagonMarker(int size, Color color, Color outline, int outline_width);
virtual void Paint(Draw& draw, const Vector<Point>& pt);
private:
int x2, y2, x4;
};
HexagonMarker::HexagonMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width)
{
y2 = size >> 1;
x2 = size * 19 >> 5;
x4 = x2 >> 1;
}
void HexagonMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
Vector<Point> out;
out.SetCount(pt.GetCount() * 6);
Point *op = out.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 6)
{
op[0].x = ip->x - x4; op[0].y = ip->y + y2;
op[1].x = ip->x + x4; op[1].y = ip->y + y2;
op[2].x = ip->x + x2; op[2].y = ip->y;
op[3].x = ip->x + x4; op[3].y = ip->y - y2;
op[4].x = ip->x - x4; op[4].y = ip->y - y2;
op[5].x = ip->x - x2; op[5].y = ip->y;
}
Vector<int> seg;
seg.SetCount(pt.GetCount(), 6);
DrawPolygons(draw, out, seg, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Hexagon(int size, Color color, Color outline, int outline_width)
{
return new HexagonMarker(size, color, outline, outline_width);
}
class StarMarker : public AreaMarker
{
public:
StarMarker(int size, Color color, Color outline, int outline_width);
virtual void Paint(Draw& draw, const Vector<Point>& pt);
private:
int x2, x4, X2, X4, y2, y4, Y2, Y4, dy;
};
StarMarker::StarMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width)
{
y2 = size >> 1;
x2 = size * 18 >> 5;
x4 = size * 11 >> 5;
Y2 = y2 * 5 >> 4;
Y4 = Y2 * 5 >> 4;
X2 = x2 * 5 >> 4;
X4 = x4 * 5 >> 4;
dy = size / 18;
}
void StarMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
Vector<Point> out;
out.SetCount(pt.GetCount() * 10);
Point *op = out.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 10)
{
int sy = ip->y + dy;
op[0].x = ip->x - x4; op[0].y = ip->y + y2;
op[1].x = ip->x; op[1].y = sy + Y2;
op[2].x = ip->x + x4; op[2].y = ip->y + y2;
op[3].x = ip->x + X2; op[3].y = sy + Y4;
op[4].x = ip->x + x2; op[4].y = ip->y - Y4;
op[5].x = ip->x + X4; op[5].y = sy - Y2;
op[6].x = ip->x; op[6].y = ip->y - y2;
op[7].x = ip->x - X4; op[7].y = sy - Y2;
op[8].x = ip->x - x2; op[8].y = ip->y - Y4;
op[9].x = ip->x - X2; op[9].y = sy + Y4;
}
Vector<int> seg;
seg.SetCount(pt.GetCount(), 10);
DrawPolygons(draw, out, seg, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::Star(int size, Color color, Color outline, int outline_width)
{
return new StarMarker(size, color, outline, outline_width);
}
class ThickCrossMarker : public AreaMarker
{
public:
ThickCrossMarker(int size, Color color, Color outline, int outline_width);
virtual void Paint(Draw& draw, const Vector<Point>& pt);
private:
int s2, s6;
};
ThickCrossMarker::ThickCrossMarker(int size, Color color, Color outline, int outline_width)
: AreaMarker(size, color, outline, outline_width)
{
s2 = size >> 1;
s6 = size / 6;
}
void ThickCrossMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
Vector<Point> out;
out.SetCount(pt.GetCount() * 12);
Point *op = out.Begin();
for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 12) {
int x = ip->x, y = ip->y;
op[0].x = op[11].x = x + s2;
op[1].x = op[2].x = op[9].x = op[10].x = x + s6;
op[3].x = op[4].x = op[7].x = op[8].x = x - s6;
op[5].x = op[6].x = x - s2;
op[2].y = op[3].y = y + s2;
op[0].y = op[1].y = op[4].y = op[5].y = y + s6;
op[6].y = op[7].y = op[10].y = op[11].y = y - s6;
op[8].y = op[9].y = y - s2;
}
Vector<int> seg;
seg.SetCount(pt.GetCount(), 12);
DrawPolygons(draw, out, seg, color, outline_width, outline);
}
One<MarkTool::Marker> MarkTool::ThickCross(int size, Color color, Color outline, int outline_width)
{
return new ThickCrossMarker(size, color, outline, outline_width);
}
class LetterMarker : public MarkTool::Marker
{
public:
LetterMarker(char ascii, int angle, Font font, Color color, Color outline);
virtual void Paint(Draw& draw, const Vector<Point>& pt);
virtual int GetSize() const;
private:
void PaintSimple(Point pt);
void PaintOutline(Point pt);
void PaintImage(Point pt);
void PaintEmpty(Point pt);
private:
Draw *draw;
char ascii;
Font font;
Font raw_font;
Color color;
Color outline;
// Image image;
// ImageDraw data;
// ImageMaskDraw mask;
int angle;
int img_size;
int raw_angle;
Size offset;
// HFONT new_font, old_font;
// int old_align;
};
LetterMarker::LetterMarker(char ascii, int angle, Font font, Color color, Color outline)
: ascii(ascii)
, angle(angle)
, font(font)
, color(color)
, outline(outline)
{
}
int LetterMarker::GetSize() const
{
Font raw = font;
if(raw.GetHeight() >= 0)
raw.Height(-raw.GetHeight());
else
raw.Height(-DotsToPixels(ScreenInfo(), -raw.GetHeight()));
FontInfo info = raw.Info();
return fceil(hypot(info[(byte)ascii], info.GetHeight())) >> 1;
}
static void DrawTextWithMask(Draw& data, Draw& mask, int x, int y, char letter, int angle, Font font, Color cdata, Color cmask)
{
data.DrawText(x, y, angle, &letter, font, cdata, 1);
mask.DrawText(x, y, angle, &letter, font, cmask, 1);
}
void LetterMarker::Paint(Draw& draw, const Vector<Point>& pt)
{
double ang = angle * (M_PI / 180), c = cos(ang), s = sin(ang);
raw_font = font;
if(raw_font.GetHeight() >= 0)
raw_font.Height(-raw_font.GetHeight());
else
raw_font.Height(-DotsToPixels(draw, -raw_font.GetHeight()));
Size size = GetTextSize(&ascii, raw_font, 1);
// offset = Pointf(size).Rotated(-angle * M_PI / 1800.0).AsSize() >> 1;
/*
new_font = CreateFont(raw_font.GetHeight(),
0, 10 * angle, 10 * angle, raw_font.IsBold() ? FW_BOLD : FW_NORMAL,
raw_font.IsItalic(), raw_font.IsUnderline(), raw_font.IsStrikeout(),
raw_font.GetCharSet() ? raw_font.GetCharSet() : DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE,
raw_font.GetFaceName());
draw->BeginGdi();
old_font = (HFONT)SelectObject(*draw, new_font);
*/
// Size size;
// GetTextExtentPoint32(*draw, &ascii, 1, &size);
raw_angle = 10 * angle;
offset.cx = fround((size.cx * c + size.cy * s) * 0.5);
offset.cy = fround((size.cx * s - size.cy * c) * -0.5);
if(IsNull(color))
;
else if(IsNull(outline))
{
for(int i = 0; i < pt.GetCount(); i++)
draw.DrawText(pt[i].x - offset.cx, pt[i].y - offset.cy, raw_angle, &ascii, raw_font, color, 1);
}
else
{
for(int i = 0; i < pt.GetCount(); i++)
{
Point pos = pt[i] - offset;
draw.DrawText(pos.x + 1, pos.y + 0, raw_angle, &ascii, raw_font, outline, 1);
draw.DrawText(pos.x + 0, pos.y - 1, raw_angle, &ascii, raw_font, outline, 1);
draw.DrawText(pos.x - 1, pos.y + 0, raw_angle, &ascii, raw_font, outline, 1);
draw.DrawText(pos.x + 0, pos.y + 1, raw_angle, &ascii, raw_font, outline, 1);
draw.DrawText(pos.x + 0, pos.y + 0, raw_angle, &ascii, raw_font, color, 1);
}
}
/*
if(!IsNull(outline))
{
if(IsNull(color))
{ // generate image with outline
img_size = abs(size);
image = Image(*draw, img_size, img_size);
data.Open(*draw, image);
mask.Open(image);
Point pos((img_size >> 1) - offset.cx, (img_size >> 1) - offset.cy);
data.DrawRect(0, 0, img_size, img_size, Black);
mask.DrawRect(0, 0, img_size, img_size, White);
DrawTextWithMask(data, mask, pos.x + 1, pos.y + 0, ascii, raw_angle, raw_font, outline, Black);
DrawTextWithMask(data, mask, pos.x + 0, pos.y - 1, ascii, raw_angle, raw_font, outline, Black);
DrawTextWithMask(data, mask, pos.x - 1, pos.y + 0, ascii, raw_angle, raw_font, outline, Black);
DrawTextWithMask(data, mask, pos.x + 0, pos.y + 1, ascii, raw_angle, raw_font, outline, Black);
DrawTextWithMask(data, mask, pos.x + 0, pos.y + 0, ascii, raw_angle, raw_font, Black, White);
offset.cx = offset.cy = img_size >> 1;
PaintRaw = &LetterMarker::PaintImage;
}
else
PaintRaw = &LetterMarker::PaintOutline;
}
*/
// old_align = SetTextAlign(*draw, TA_LEFT | TA_TOP);
// SetTextColor(*draw, color);
}
One<MarkTool::Marker> MarkTool::Letter(char ascii, int angle, Font font, Color color, Color outline)
{
return new LetterMarker(ascii, angle, font, color, outline);
}
One<MarkTool::Marker> MarkTool::StandardMarker(int type, int size, Color color, Color outline, int outline_width)
{
switch(type)
{
case CIRCLE: return Circle(size, color, outline, outline_width);
case SQUARE: return Square(size, color, outline, outline_width);
case TRIANGLE: return Triangle(size, color, outline, outline_width);
case CROSS: return Cross(size, outline, outline_width);
case DIAMOND: return Diamond(size, color, outline, outline_width);
case HEXAGON: return Hexagon(size, color, outline, outline_width);
case STAR: return Star(size, color, outline, outline_width);
case SQUARE45: return Square45(size, color, outline, outline_width);
case NABLA: return Nabla(size, color, outline, outline_width);
default: return Empty();
}
}
MarkTool::MarkTool()
{
PutRaw = &MarkTool::PutDummy;
}
MarkTool::~MarkTool() {}
void MarkTool::Reset()
{
marker.Clear();
Clear();
}
bool MarkTool::Set(const Plotter& info, One<Marker> _marker)
{
if(!_marker) {
Reset();
return false;
}
marker = _marker;
size = marker->GetSize();
plotter.Set(info, size);
Clear();
return true;
}
bool MarkTool::SetExtent(const Rectf& rc)
{
if(marker && plotter.IntersectsLClip(rc)) {
Rectf crc = plotter.LtoP(rc);
if(plotter.InPClip(crc))
PutRaw = &MarkTool::PutSimple;
else
PutRaw = &MarkTool::PutClip;
return true;
}
else {
PutRaw = &MarkTool::PutDummy;
return false;
}
}
void MarkTool::ClearExtent()
{
if(marker)
PutRaw = &MarkTool::PutClip;
else
PutRaw = &MarkTool::PutDummy;
}
void MarkTool::Put(const Array<Pointf>& pt)
{
for(int i = 0; i < pt.GetCount(); i++)
Put(pt[i]);
}
void MarkTool::Paint()
{
if(!plotter.draw)
return;
Flush();
ClearExtent();
}
void MarkTool::Flush()
{
if(!vertices.IsEmpty()) {
marker->Paint(*plotter.draw, vertices);
vertices.Clear();
}
}
void MarkTool::Clear()
{
vertices.Clear();
ClearExtent();
}
void MarkTool::PutDummy(Pointf pt) {}
void MarkTool::PutSimple(Pointf pt)
{
if(IsNull(pt.x) || IsNull(pt.y))
return;
vertices.Add(plotter.LtoPoint(pt));
if(vertices.GetCount() >= BUF_COUNT)
Flush();
}
void MarkTool::PutClip(Pointf pt)
{
if(IsNull(pt.x) || IsNull(pt.y))
return;
Pointf cpt = plotter.LtoP(pt);
if(plotter.InPClip(cpt))
{
vertices.Add(PointfToPoint(cpt));
if(vertices.GetCount() >= BUF_COUNT)
Flush();
}
}
Size TextTool::Type::GetTextSize(const String& s) const
{
int wd = 0;
const byte *b = s;
unsigned len = s.GetLength();
for(unsigned rep = len >> 2; rep; rep--)
{
wd += widths[b[0]] + widths[b[1]] + widths[b[2]] + widths[b[3]];
b += 4;
}
switch(len & 3)
{
case 3: wd += widths[b[0]] + widths[b[1]] + widths[b[2]]; break;
case 2: wd += widths[b[0]] + widths[b[1]]; break;
case 1: wd += widths[b[0]]; break;
}
return Size(wd, height);
}
TextTool::TextTool()
{
}
TextTool::~TextTool() {}
void TextTool::Set(const Plotter& info, Font _f, Color _c, Color _o, double _a, Alignment _x, Alignment _y, bool _flip, int _aprec)
{
cache_size = 0;
font_index = 0;
plotter = info;
color = _c;
outline = _o;
angle = _a;
x_align = _x;
y_align = _y;
flip = _flip;
angle_prec = _aprec;
ASSERT(angle_prec > 0 && angle_prec <= 36000);
SetFont(_f);
}
void TextTool::SetFont(Font _font)
{
if((font_index = font_map.Find(font = _font)) < 0)
{
if(font_map.GetCount() >= FONT_LIMIT)
Paint();
font_index = font_map.GetCount();
Type& type = font_map.Add(_font);
FontInfo fi = _font.Info();
short *out = type.widths;
for(int c = 256; --c >= 0; out[c] = fi.GetWidth(c))
;
type.height = fi.GetHeight();
}
}
void TextTool::PutFlip(const String& text, Pointf pt, double angle)
{
if(flip && (angle >= 0.5 * M_PI && angle <= 1.5 * M_PI || angle <= -0.5 * M_PI))
angle -= M_PI;
Put(text, pt, angle);
}
void TextTool::Put(const String& text, Pointf pt, double angle)
{
if(IsNull(pt.x) || IsNull(pt.y))
return;
Size size = font_map[font_index].GetTextSize(text);
int delta = size.cx + size.cy;
Pointf pos = plotter.LtoP(pt);
if(pos.x >= plotter.clip.right + delta || pos.x <= plotter.clip.left - delta
|| pos.y >= plotter.clip.bottom + delta || pos.y <= plotter.clip.top - delta)
return;
Pointf offset(0, 0);
switch(x_align)
{
case ALIGN_LEFT: break;
case ALIGN_RIGHT: offset.x -= size.cx; break;
default: offset.x -= size.cx >> 1; break;
}
switch(y_align)
{
case ALIGN_TOP: break;
case ALIGN_BOTTOM: offset.y -= size.cy; break;
default: offset.y -= size.cy >> 1; break;
}
int apart = fround(angle * angle_prec / (2 * M_PI)) % angle_prec;
if(apart < 0) apart += angle_prec;
if(apart)
offset = Rotated(offset, (-2 * M_PI * apart) / angle_prec);
One<Item> item = new Item(PointfToPoint(pos + offset), text, color, outline);
int sz = item->GetSize();
if(cache_size + sz > CACHE_LIMIT) // flush
Paint();
cache_size += sz;
cache.GetAdd(font_index * angle_prec + apart).Add(item.Detach());
}
void TextTool::Paint()
{
if(plotter.draw)
Flush();
Clear();
}
void TextTool::Clear()
{
ClearCache();
SetFont(font);
}
void TextTool::Flush()
{
Draw& draw = *plotter.draw;
for(int i = 0; i < cache.GetCount(); i++)
{
int angle = cache.GetKey(i);
Font font = font_map.GetKey(angle / angle_prec);
Font font_naa = Font(font).NonAntiAliased();
angle = (angle % angle_prec) * 3600 / angle_prec;
const Array<Item>& items = cache[i];
for(int t = 0; t < items.GetCount(); t++)
{
const Item& ii = items[t];
if(!IsNull(ii.outline))
{
draw.DrawText(ii.point.x - 1, ii.point.y, angle, ii.text, font_naa, ii.outline);
draw.DrawText(ii.point.x + 1, ii.point.y, angle, ii.text, font_naa, ii.outline);
draw.DrawText(ii.point.x, ii.point.y - 1, angle, ii.text, font_naa, ii.outline);
draw.DrawText(ii.point.x, ii.point.y + 1, angle, ii.text, font_naa, ii.outline);
draw.DrawText(ii.point.x, ii.point.y, angle, ii.text, font_naa, ii.color);
}
else
draw.DrawText(ii.point.x, ii.point.y, angle, ii.text, font, ii.color);
}
}
ClearCache();
}
void TextTool::ClearCache()
{
font_map.Clear();
cache.Clear();
cache_size = 0;
}
#if 0
class TestWindow : public TopWindow
{
public:
virtual void Paint(Draw& draw);
};
void TestWindow::Paint(Draw& draw)
{
draw.DrawRect(draw.GetClip(), LtCyan);
Plotter plotter(draw);
PathTool path;
path.Set(plotter, SolidLine);
for(int i = 0; i < 320; i += 20)
{
Rectf rc = Pointf(300, 200);
rc.Inflate(i);
path.Rectangle(rc);
path.Circle(Pointf(300, 200), i);
}
Rect rc = RectC(GetSize());
rc.DeflateRect(20, 20);
AreaTool area;
area.Set(Plotter(plotter, rc), LtBlue, Image(), DashDotDotLine, Black, -50, 0);
area.Rectangle(100, 100, 400, 400);
area.Circle(400, 250, 100);
// area.MoveTo(100, 0);
// area.LineTo(0, 400);
// area.LineTo(300, 500);
// area.Circle(300, 200, 200);
area.Paint();
}
void PlotterTest()
{
Size size(600, 400);
TestWindow window;
window.SetRect(CalcWindowRect(window, size));
window.Sizeable().Zoomable();
window.Run();
}
#endif
END_UPP_NAMESPACE