diff --git a/uppsrc/Painter/Approximate.cpp b/uppsrc/Painter/Approximate.cpp index 19aef6fcc..efa5cbccb 100644 --- a/uppsrc/Painter/Approximate.cpp +++ b/uppsrc/Painter/Approximate.cpp @@ -1,89 +1,72 @@ -#include "Painter.h" - -NAMESPACE_UPP - -static void sQuadratic(LinearPathConsumer& t, const Pointf& p1, const Pointf& p2, const Pointf& p3, - double qt, int lvl) -{ - if(lvl < 16) { - Pointf d = p3 - p1; - double q = Squared(d); - if(q > 1e-30) { - Pointf pd = p2 - p1; - double u = (pd.x * d.x + pd.y * d.y) / q; - if(u <= 0 || u >= 1 || SquaredDistance(u * d, pd) > qt) { - Pointf p12 = Mid(p1, p2); - Pointf p23 = Mid(p2, p3); - Pointf div = Mid(p12, p23); - sQuadratic(t, p1, p12, div, qt, lvl + 1); - sQuadratic(t, div, p23, p3, qt, lvl + 1); - return; - } - } - } - t.Line(p3); -} - -void ApproximateQuadratic(LinearPathConsumer& t, const Pointf& p1, const Pointf& p2, const Pointf& p3, - double tolerance) -{ - sQuadratic(t, p1, p2, p3, tolerance * tolerance, 0); - t.Line(p3); -} - -static void sCubic(LinearPathConsumer& t, - const Pointf& p1, const Pointf& p2, const Pointf& p3, const Pointf& p4, - double qt, int lvl) -{ - if(lvl < 16) { - Pointf d = p4 - p1; - double q = d.x * d.x + d.y * d.y; - if(q >= 1e-30) { - Pointf d2 = p2 - p1; - Pointf d3 = p3 - p1; - double u1 = (d2.x * d.x + d2.y * d.y) / q; - double u2 = (d3.x * d.x + d3.y * d.y) / q; - if(u1 <= 0 || u1 >= 1 || u2 <= 0 || u2 >= 1 || - SquaredDistance(u1 * d, d2) > qt || SquaredDistance(u2 * d, d3) > qt) { - Pointf p12 = Mid(p1, p2); - Pointf p23 = Mid(p2, p3); - Pointf p34 = Mid(p3, p4); - Pointf p123 = Mid(p12, p23); - Pointf p234 = Mid(p23, p34); - Pointf div = Mid(p123, p234); - Pointf p14 = Mid(p1, p4); - sCubic(t, p1, p12, p123, div, qt, lvl + 1); - sCubic(t, div, p234, p34, p4, qt, lvl + 1); - return; - } - } - } - t.Line(p4); -} - -void ApproximateCubic(LinearPathConsumer& t, - const Pointf& p1, const Pointf& p2, const Pointf& p3, const Pointf& p4, - double tolerance) -{ - sCubic(t, p1, p2, p3, p4, tolerance * tolerance, 0); - t.Line(p4); -} - -void ApproximateArc(LinearPathConsumer& t, const Pointf& c, const Pointf& r, - double angle, double sweep, double tolerance) -{ - while(angle + sweep < 0) - angle += 2000 * M_PI; - double fid = acos(1 - tolerance / max(r.x, r.y)); - if(fabs(sweep / fid) > 1000) - fid = sweep / 1000; - double a = angle; - double e = angle + sweep; - while(fid > 0 ? a < e : a > e) { - t.Line(Polar(a) * r + c); - a += fid; - } - t.Line(Polar(angle + sweep) * r + c); -} - -END_UPP_NAMESPACE +#include "Painter.h" + +NAMESPACE_UPP + +static void sQuadratic(LinearPathConsumer& t, const Pointf& p1, const Pointf& p2, const Pointf& p3, + double qt, int lvl) +{ + if(lvl < 16) { + Pointf d = p3 - p1; + double q = Squared(d); + if(q > 1e-30) { + Pointf pd = p2 - p1; + double u = (pd.x * d.x + pd.y * d.y) / q; + if(u <= 0 || u >= 1 || SquaredDistance(u * d, pd) > qt) { + Pointf p12 = Mid(p1, p2); + Pointf p23 = Mid(p2, p3); + Pointf div = Mid(p12, p23); + sQuadratic(t, p1, p12, div, qt, lvl + 1); + sQuadratic(t, div, p23, p3, qt, lvl + 1); + return; + } + } + } + t.Line(p3); +} + +void ApproximateQuadratic(LinearPathConsumer& t, const Pointf& p1, const Pointf& p2, const Pointf& p3, + double tolerance) +{ + sQuadratic(t, p1, p2, p3, tolerance * tolerance, 0); + t.Line(p3); +} + +static void sCubic(LinearPathConsumer& t, + const Pointf& p1, const Pointf& p2, const Pointf& p3, const Pointf& p4, + double qt, int lvl) +{ + if(lvl < 16) { + Pointf d = p4 - p1; + double q = d.x * d.x + d.y * d.y; + if(q >= 1e-30) { + Pointf d2 = p2 - p1; + Pointf d3 = p3 - p1; + double u1 = (d2.x * d.x + d2.y * d.y) / q; + double u2 = (d3.x * d.x + d3.y * d.y) / q; + if(u1 <= 0 || u1 >= 1 || u2 <= 0 || u2 >= 1 || + SquaredDistance(u1 * d, d2) > qt || SquaredDistance(u2 * d, d3) > qt) { + Pointf p12 = Mid(p1, p2); + Pointf p23 = Mid(p2, p3); + Pointf p34 = Mid(p3, p4); + Pointf p123 = Mid(p12, p23); + Pointf p234 = Mid(p23, p34); + Pointf div = Mid(p123, p234); + Pointf p14 = Mid(p1, p4); + sCubic(t, p1, p12, p123, div, qt, lvl + 1); + sCubic(t, div, p234, p34, p4, qt, lvl + 1); + return; + } + } + } + t.Line(p4); +} + +void ApproximateCubic(LinearPathConsumer& t, + const Pointf& p1, const Pointf& p2, const Pointf& p3, const Pointf& p4, + double tolerance) +{ + sCubic(t, p1, p2, p3, p4, tolerance * tolerance, 0); + t.Line(p4); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Painter/BufferPainter.h b/uppsrc/Painter/BufferPainter.h index c02b67643..d41b99042 100644 --- a/uppsrc/Painter/BufferPainter.h +++ b/uppsrc/Painter/BufferPainter.h @@ -1,352 +1,347 @@ -Pointf Mid(const Pointf& a, const Pointf& b); -Pointf Orthogonal(const Pointf& p); -double Squared(const Pointf& p); -double Length(const Pointf& p); -double Bearing(const Pointf& p); -double Distance(const Pointf& p1, const Pointf& p2); -double SquaredDistance(const Pointf& p1, const Pointf& p2); -Pointf Polar(double a); -Pointf Polar(const Pointf& p, double r, double a); - -struct LinearPathConsumer { - virtual void Move(const Pointf& p) = 0; - virtual void Line(const Pointf& p) = 0; - virtual void End(); -}; - -void ApproximateQuadratic(LinearPathConsumer& t, - const Pointf& p1, const Pointf& p2, const Pointf& p3, - double tolerance); -void ApproximateCubic(LinearPathConsumer& t, - const Pointf& x0, const Pointf& x1, const Pointf& x2, const Pointf& x, - double tolerance); -void ApproximateArc(LinearPathConsumer& t, const Pointf& p, const Pointf& r, - double angle, double sweep, double tolerance); - -struct LinearPathFilter : LinearPathConsumer { - virtual void End(); - - LinearPathConsumer *target; - - void PutMove(const Pointf& p) { target->Move(p); } - void PutLine(const Pointf& p) { target->Line(p); } - void PutEnd() { target->End(); } -}; - -class Stroker : public LinearPathFilter { -public: - virtual void Move(const Pointf& p); - virtual void Line(const Pointf& p); - virtual void End(); - -private: - double w2; - double qmiter; - double fid; - - Pointf p0, v0, o0, a0, b0; - Pointf p1, v1, o1, a1, b1; - Pointf p2; - int linecap; - int linejoin; - - void Finish(); - void Round(const Pointf& p, const Pointf& v1, const Pointf& v2, double r); - void Cap(const Pointf& p0, const Pointf& v0, const Pointf& o0, - const Pointf& a0, const Pointf& b0); - -public: - void Init(double width, double miterlimit, double tolerance, int linecap, int linejoin); -}; - -class Dasher : public LinearPathFilter { -public: - virtual void Move(const Pointf& p); - virtual void Line(const Pointf& p); - -private: - const Vector *pattern; - int patterni; - double sum, rem; - bool flag; - Pointf p0; - - void Put(const Pointf& p); - -public: - void Init(const Vector& pattern, double distance); -}; - -struct Transformer : public LinearPathFilter { -public: - virtual void Move(const Pointf& p); - virtual void Line(const Pointf& p); - -private: - const Xform2D& xform; - -public: - Transformer(const Xform2D& xform) : xform(xform) {} -}; - -inline RGBA Mul8(const RGBA& s, int mul) -{ - RGBA t; - t.r = (mul * s.r) >> 8; - t.g = (mul * s.g) >> 8; - t.b = (mul * s.b) >> 8; - t.a = (mul * s.a) >> 8; - 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 AlphaBlendCover8(RGBA& t, const RGBA& c, int cover) -{ - int a = c.a * cover >> 8; - int alpha = 256 - (a + (a >> 7)); - t.r = (c.r * cover >> 8) + (alpha * t.r >> 8); - t.g = (c.g * cover >> 8) + (alpha * t.g >> 8); - t.b = (c.b * cover >> 8) + (alpha * t.b >> 8); - t.a = a + (alpha * t.a >> 8); -} - -inline int Q8(double x) -{ - return int(x * 256 + 0.5); -} - -class Rasterizer : public LinearPathConsumer { -public: - virtual void Move(const Pointf& p); - virtual void Line(const Pointf& p); - -private: - struct Cell : Moveable { - int16 x; - int16 cover; - int area; - - bool operator<(const Cell& b) const { return x < b.x; } - }; - - Rectf cliprect; - Pointf p0; - Buffer< Vector > cell; - int min_y; - int max_y; - Size sz; - - void Init(); - Cell *AddCells(int y, int n); - void RenderHLine(int ey, int x1, int y1, int x2, int y2); - void LineClip(double x1, double y1, double x2, double y2); - int CvX(double x); - int CvY(double y); - void CvLine(double x1, double y1, double x2, double y2); - bool BeginRender(int y, const Cell *&c, const Cell *&e); - -public: - struct Filler { - virtual void Start(int x, int len) = 0; - virtual void Render(int val) = 0; - virtual void Render(int val, int len) = 0; - }; - - void LineRaw(int x1, int y1, int x2, int y2); - - void SetClip(const Rectf& rect); - - int MinY() const { return min_y; } - int MaxY() const { return max_y; } - void Render(int y, Filler& g, bool evenodd); - - void Reset(); - - Rasterizer(int cx, int cy); -}; - -struct SpanSource { - virtual void Get(RGBA *span, int x, int y, unsigned len) = 0; -}; - -class ClipLine : NoCopy { - byte *data; - -public: - void Clear() { if(!IsFull()) delete[] data; data = NULL; } - void Set(const byte *s, int len) { data = new byte[len]; memcpy(data, s, len); } - void SetFull() { ASSERT(!data); data = (byte *)1; } - - bool IsEmpty() const { return !data; } - bool IsFull() const { return data == (byte *)1; } - operator const byte*() const { return data; } - - ClipLine() { data = NULL; } - ~ClipLine() { Clear(); } -}; - -Image MipMap(const Image& img); -Image MakeMipMap(const Image& img, int level); - -class LinearInterpolator { - struct Dda2 { - int count, lift, rem, mod, p; - - void Set(int a, int b, int len); - int Get(); - }; - - Xform2D xform; - Dda2 ddax, dday; - -public: - void Set(const Xform2D& m) { xform = m; } - - void Begin(int x, int y, int len); - Point Get(); -}; - -class BufferPainter : public Painter { -protected: - virtual void ClearOp(const RGBA& color); - - virtual void MoveOp(const Pointf& p, bool rel); - virtual void LineOp(const Pointf& p, bool rel); - virtual void QuadraticOp(const Pointf& p1, const Pointf& p, bool rel); - virtual void QuadraticOp(const Pointf& p, bool rel); - virtual void CubicOp(const Pointf& p1, const Pointf& p2, const Pointf& p, bool rel); - virtual void CubicOp(const Pointf& p2, const Pointf& p, bool rel); - virtual void ArcOp(const Pointf& c, const Pointf& r, double angle, double sweep, bool rel); - virtual void SvgArcOp(const Pointf& r, double xangle, bool large, bool sweep, - const Pointf& p, bool rel); - virtual void CloseOp(); - virtual void DivOp(); - - virtual void FillOp(const RGBA& color); - virtual void FillOp(const Image& image, const Xform2D& transsrc, dword flags); - virtual void FillOp(const Pointf& p1, const RGBA& color1, - const Pointf& p2, const RGBA& color2, - int style); - virtual void FillOp(const Pointf& f, const RGBA& color1, - const Pointf& c, double r, const RGBA& color2, - int style); - - virtual void StrokeOp(double width, const RGBA& rgba); - virtual void StrokeOp(double width, const Image& image, const Xform2D& transsrc, - dword flags); - virtual void StrokeOp(double width, const Pointf& p1, const RGBA& color1, - const Pointf& p2, const RGBA& color2, - int style); - virtual void StrokeOp(double width, const Pointf& f, const RGBA& color1, - const Pointf& c, double r, const RGBA& color2, - int style); - - virtual void ClipOp(); - - virtual void ColorStopOp(double pos, const RGBA& color); - virtual void ClearStopsOp(); - - virtual void OpacityOp(double o); - virtual void LineCapOp(int linecap); - virtual void LineJoinOp(int linejoin); - virtual void MiterLimitOp(double l); - virtual void EvenOddOp(bool evenodd); - virtual void DashOp(const Vector& dash, double start); - virtual void NoAAOp(bool noaa); - - virtual void TransformOp(const Xform2D& m); - - virtual void BeginOp(); - virtual void EndOp(); - - virtual void BeginMaskOp(); - -public: - enum { - MOVE, LINE, QUADRATIC, CUBIC, ARC, DIV - }; - struct LinearData { - Pointf p; - }; - struct QuadraticData : LinearData { - Pointf p1; - }; - struct CubicData : QuadraticData { - Pointf p2; - }; - struct ArcData : LinearData { - Pointf r; - double angle, sweep; - - Pointf EndPoint() const; - }; - struct Path { - Vector type; - Vector data; - }; - struct Attr : Moveable { - Xform2D mtx; - double tolerance; - bool evenodd; - byte join; - byte cap; - double miter_limit; - WithDeepCopy< Vector > dash; - WithDeepCopy< Vector > stop; - WithDeepCopy< Vector > stop_color; - double dash_start; - double opacity; - int cliplevel; - bool hasclip; - bool mask; - bool noaa; - }; - - ImageBuffer& ib; - - Attr attr; - Attr pathattr; - Array attrstack; - Vector< Buffer > clip; - Array< ImageBuffer > mask; - - - Image gradient; - RGBA gradient1, gradient2; - int gradientn; - - Path path; - Pointf current, ccontrol, qcontrol, move; - Rectf pathrect; - - Rasterizer rasterizer; - Buffer span; - - void *PathAddRaw(int type, int size); - template T& PathAdd(int type) { return *(T *)PathAddRaw(type, sizeof(T)); } - - Pointf PathPoint(const Pointf& p, bool rel); - Pointf EndPoint(const Pointf& p, bool rel); - void DoMove0(); - void ClearPath(); - Buffer RenderPath(double width, SpanSource *ss, const RGBA& color); - void RenderImage(double width, const Image& image, const Xform2D& transsrc, - dword flags); - void RenderRadial(double width, const Pointf& f, const RGBA& color1, - const Pointf& c, double r, const RGBA& color2, int style); - void MakeGradient(RGBA color1, RGBA color2, int cx); - void Gradient(const RGBA& color1, const RGBA& color2, const Pointf& p1, const Pointf& p2); - void ColorStop0(Attr& a, double pos, const RGBA& color); - void FinishMask(); - -public: - BufferPainter(ImageBuffer& ib); -}; +Pointf Mid(const Pointf& a, const Pointf& b); +Pointf Orthogonal(const Pointf& p); +double Squared(const Pointf& p); +double Length(const Pointf& p); +double Bearing(const Pointf& p); +double Distance(const Pointf& p1, const Pointf& p2); +double SquaredDistance(const Pointf& p1, const Pointf& p2); +Pointf Polar(double a); +Pointf Polar(const Pointf& p, double r, double a); + +struct LinearPathConsumer { + virtual void Move(const Pointf& p) = 0; + virtual void Line(const Pointf& p) = 0; + virtual void End(); +}; + +void ApproximateQuadratic(LinearPathConsumer& t, + const Pointf& p1, const Pointf& p2, const Pointf& p3, + double tolerance); +void ApproximateCubic(LinearPathConsumer& t, + const Pointf& x0, const Pointf& x1, const Pointf& x2, const Pointf& x, + double tolerance); + +struct LinearPathFilter : LinearPathConsumer { + virtual void End(); + + LinearPathConsumer *target; + + void PutMove(const Pointf& p) { target->Move(p); } + void PutLine(const Pointf& p) { target->Line(p); } + void PutEnd() { target->End(); } +}; + +class Stroker : public LinearPathFilter { +public: + virtual void Move(const Pointf& p); + virtual void Line(const Pointf& p); + virtual void End(); + +private: + double w2; + double qmiter; + double fid; + + Pointf p0, v0, o0, a0, b0; + Pointf p1, v1, o1, a1, b1; + Pointf p2; + int linecap; + int linejoin; + + void Finish(); + void Round(const Pointf& p, const Pointf& v1, const Pointf& v2, double r); + void Cap(const Pointf& p0, const Pointf& v0, const Pointf& o0, + const Pointf& a0, const Pointf& b0); + +public: + void Init(double width, double miterlimit, double tolerance, int linecap, int linejoin); +}; + +class Dasher : public LinearPathFilter { +public: + virtual void Move(const Pointf& p); + virtual void Line(const Pointf& p); + +private: + const Vector *pattern; + int patterni; + double sum, rem; + bool flag; + Pointf p0; + + void Put(const Pointf& p); + +public: + void Init(const Vector& pattern, double distance); +}; + +struct Transformer : public LinearPathFilter { +public: + virtual void Move(const Pointf& p); + virtual void Line(const Pointf& p); + +private: + const Xform2D& xform; + +public: + Transformer(const Xform2D& xform) : xform(xform) {} +}; + +inline RGBA Mul8(const RGBA& s, int mul) +{ + RGBA t; + t.r = (mul * s.r) >> 8; + t.g = (mul * s.g) >> 8; + t.b = (mul * s.b) >> 8; + t.a = (mul * s.a) >> 8; + 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 AlphaBlendCover8(RGBA& t, const RGBA& c, int cover) +{ + int a = c.a * cover >> 8; + int alpha = 256 - (a + (a >> 7)); + t.r = (c.r * cover >> 8) + (alpha * t.r >> 8); + t.g = (c.g * cover >> 8) + (alpha * t.g >> 8); + t.b = (c.b * cover >> 8) + (alpha * t.b >> 8); + t.a = a + (alpha * t.a >> 8); +} + +inline int Q8(double x) +{ + return int(x * 256 + 0.5); +} + +class Rasterizer : public LinearPathConsumer { +public: + virtual void Move(const Pointf& p); + virtual void Line(const Pointf& p); + +private: + struct Cell : Moveable { + int16 x; + int16 cover; + int area; + + bool operator<(const Cell& b) const { return x < b.x; } + }; + + Rectf cliprect; + Pointf p0; + Buffer< Vector > cell; + int min_y; + int max_y; + Size sz; + + void Init(); + Cell *AddCells(int y, int n); + void RenderHLine(int ey, int x1, int y1, int x2, int y2); + void LineClip(double x1, double y1, double x2, double y2); + int CvX(double x); + int CvY(double y); + void CvLine(double x1, double y1, double x2, double y2); + bool BeginRender(int y, const Cell *&c, const Cell *&e); + +public: + struct Filler { + virtual void Start(int x, int len) = 0; + virtual void Render(int val) = 0; + virtual void Render(int val, int len) = 0; + virtual void End(); + }; + + void LineRaw(int x1, int y1, int x2, int y2); + + void SetClip(const Rectf& rect); + + int MinY() const { return min_y; } + int MaxY() const { return max_y; } + void Render(int y, Filler& g, bool evenodd); + + void Reset(); + + Rasterizer(int cx, int cy); +}; + +struct SpanSource { + virtual void Get(RGBA *span, int x, int y, unsigned len) = 0; +}; + +class ClipLine : NoCopy { + byte *data; + +public: + void Clear() { if(!IsFull()) delete[] data; data = NULL; } + void Set(const byte *s, int len) { data = new byte[len]; memcpy(data, s, len); } + void SetFull() { ASSERT(!data); data = (byte *)1; } + + bool IsEmpty() const { return !data; } + bool IsFull() const { return data == (byte *)1; } + operator const byte*() const { return data; } + + ClipLine() { data = NULL; } + ~ClipLine() { Clear(); } +}; + +Image MipMap(const Image& img); +Image MakeMipMap(const Image& img, int level); + +class LinearInterpolator { + struct Dda2 { + int count, lift, rem, mod, p; + + void Set(int a, int b, int len); + int Get(); + }; + + Xform2D xform; + Dda2 ddax, dday; + +public: + void Set(const Xform2D& m) { xform = m; } + + void Begin(int x, int y, int len); + Point Get(); +}; + +class BufferPainter : public Painter { +protected: + virtual void ClearOp(const RGBA& color); + + virtual void MoveOp(const Pointf& p, bool rel); + virtual void LineOp(const Pointf& p, bool rel); + virtual void QuadraticOp(const Pointf& p1, const Pointf& p, bool rel); + virtual void QuadraticOp(const Pointf& p, bool rel); + virtual void CubicOp(const Pointf& p1, const Pointf& p2, const Pointf& p, bool rel); + virtual void CubicOp(const Pointf& p2, const Pointf& p, bool rel); + virtual void ArcOp(const Pointf& c, const Pointf& r, double angle, double sweep, bool rel); + virtual void SvgArcOp(const Pointf& r, double xangle, bool large, bool sweep, + const Pointf& p, bool rel); + virtual void CloseOp(); + virtual void DivOp(); + + virtual void FillOp(const RGBA& color); + virtual void FillOp(const Image& image, const Xform2D& transsrc, dword flags); + virtual void FillOp(const Pointf& p1, const RGBA& color1, + const Pointf& p2, const RGBA& color2, + int style); + virtual void FillOp(const Pointf& f, const RGBA& color1, + const Pointf& c, double r, const RGBA& color2, + int style); + + virtual void StrokeOp(double width, const RGBA& rgba); + virtual void StrokeOp(double width, const Image& image, const Xform2D& transsrc, + dword flags); + virtual void StrokeOp(double width, const Pointf& p1, const RGBA& color1, + const Pointf& p2, const RGBA& color2, + int style); + virtual void StrokeOp(double width, const Pointf& f, const RGBA& color1, + const Pointf& c, double r, const RGBA& color2, + int style); + + virtual void ClipOp(); + + virtual void ColorStopOp(double pos, const RGBA& color); + virtual void ClearStopsOp(); + + virtual void OpacityOp(double o); + virtual void LineCapOp(int linecap); + virtual void LineJoinOp(int linejoin); + virtual void MiterLimitOp(double l); + virtual void EvenOddOp(bool evenodd); + virtual void DashOp(const Vector& dash, double start); + virtual void NoAAOp(bool noaa); + + virtual void TransformOp(const Xform2D& m); + + virtual void BeginOp(); + virtual void EndOp(); + + virtual void BeginMaskOp(); + +public: + enum { + MOVE, LINE, QUADRATIC, CUBIC, DIV + }; + struct LinearData { + Pointf p; + }; + struct QuadraticData : LinearData { + Pointf p1; + }; + struct CubicData : QuadraticData { + Pointf p2; + }; + struct Path { + Vector type; + Vector data; + }; + struct Attr : Moveable { + Xform2D mtx; + double tolerance; + bool evenodd; + byte join; + byte cap; + double miter_limit; + WithDeepCopy< Vector > dash; + WithDeepCopy< Vector > stop; + WithDeepCopy< Vector > stop_color; + double dash_start; + double opacity; + int cliplevel; + bool hasclip; + bool mask; + bool noaa; + }; + + ImageBuffer& ib; + Buffer subpixel; + int render_cx; + + Attr attr; + Attr pathattr; + Array attrstack; + Vector< Buffer > clip; + Array< ImageBuffer > mask; + + + Image gradient; + RGBA gradient1, gradient2; + int gradientn; + + Path path; + Pointf current, ccontrol, qcontrol, move; + Rectf pathrect; + + Rasterizer rasterizer; + Buffer span; + + void *PathAddRaw(int type, int size); + template T& PathAdd(int type) { return *(T *)PathAddRaw(type, sizeof(T)); } + + Pointf PathPoint(const Pointf& p, bool rel); + Pointf EndPoint(const Pointf& p, bool rel); + void DoMove0(); + void ClearPath(); + Buffer RenderPath(double width, SpanSource *ss, const RGBA& color); + void RenderImage(double width, const Image& image, const Xform2D& transsrc, + dword flags); + void RenderRadial(double width, const Pointf& f, const RGBA& color1, + const Pointf& c, double r, const RGBA& color2, int style); + void MakeGradient(RGBA color1, RGBA color2, int cx); + void Gradient(const RGBA& color1, const RGBA& color2, const Pointf& p1, const Pointf& p2); + void ColorStop0(Attr& a, double pos, const RGBA& color); + void FinishMask(); + +public: + BufferPainter(ImageBuffer& ib, bool subpixel = false); +}; diff --git a/uppsrc/Painter/Context.cpp b/uppsrc/Painter/Context.cpp index 66af03b35..65e943e27 100644 --- a/uppsrc/Painter/Context.cpp +++ b/uppsrc/Painter/Context.cpp @@ -105,13 +105,18 @@ void BufferPainter::ClearStopsOp() } } -BufferPainter::BufferPainter(ImageBuffer& ib) -: ib(ib), - rasterizer(ib.GetWidth(), ib.GetHeight()) +BufferPainter::BufferPainter(ImageBuffer& ib, bool subpixel_) +: ib(ib), + rasterizer(subpixel_ ? 3 * ib.GetWidth() : ib.GetWidth(), ib.GetHeight()) { ClearPath(); - attr.mtx = Xform2D::Identity(); + render_cx = ib.GetWidth(); + if(subpixel_) { + render_cx *= 3; + subpixel.Alloc(render_cx + 30); + } + attr.mtx = Xform2D::Scale(subpixel ? 3 : 1, 1); attr.tolerance = 0.3; attr.cap = LINECAP_BUTT; attr.join = LINEJOIN_MITER; diff --git a/uppsrc/Painter/Fillers.cpp b/uppsrc/Painter/Fillers.cpp index fe548224f..bb8625c39 100644 --- a/uppsrc/Painter/Fillers.cpp +++ b/uppsrc/Painter/Fillers.cpp @@ -1,272 +1,331 @@ -#include "Painter.h" -#include "Fillers.h" - -NAMESPACE_UPP - -void SolidFiller::Start(int minx, int maxx) -{ - t += minx; -} - -void SolidFiller::Render(int val) -{ - AlphaBlendCover8(*t++, c, val); -} - -void SolidFiller::Render(int val, int len) -{ - if(val == 0) { - t += len; - return; - } - if(((val - 256) | (c.a - 255)) == 0) { - while(len >= 16) { - t[0] = c; t[1] = c; t[2] = c; t[3] = c; - t[4] = c; t[5] = c; t[6] = c; t[7] = c; - t[8] = c; t[9] = c; t[10] = c; t[11] = c; - t[12] = c; t[13] = c; t[14] = c; t[15] = c; - t += 16; - len -= 16; - } - switch(len) { - case 15: t[14] = c; - case 14: t[13] = c; - case 13: t[12] = c; - case 12: t[11] = c; - case 11: t[10] = c; - case 10: t[9] = c; - case 9: t[8] = c; - case 8: t[7] = c; - case 7: t[6] = c; - case 6: t[5] = c; - case 5: t[4] = c; - case 4: t[3] = c; - case 3: t[2] = c; - case 2: t[1] = c; - case 1: t[0] = c; - } - t += len; - } - else { - RGBA c1; - if(val != 256) - c1 = Mul8(c, val); - else - c1 = c; - RGBA *e = t + len; - while(t < e) - AlphaBlend(*t++, c1); - } -} - -void SpanFiller::Start(int minx, int maxx) -{ - t += minx; - ss->Get(buffer, minx, y, maxx - minx + 1); - s = buffer; -} - -void SpanFiller::Render(int val) -{ - if(alpha != 256) - val = alpha * val >> 8; - AlphaBlendCover8(*t++, *s++, val); -} - -void SpanFiller::Render(int val, int len) -{ - if(val == 0) { - t += len; - s += len; - return; - } - const RGBA *e = t + len; - if(alpha != 256) - val = alpha * val >> 8; - if(val == 256) - while(t < e) { - if(s->a == 255) - *t++ = *s++; - else - AlphaBlend(*t++, *s++); - } - else - while(t < e) - AlphaBlendCover8(*t++, *s++, val); -} - -ClipFiller::ClipFiller(int _cx) -{ - cx = _cx; - buffer.Alloc(2 * cx); -} - -void ClipFiller::Clear() -{ - t = ~buffer; - x = 0; - empty = true; - full = true; - last = -1; -} - -void ClipFiller::Start(int xmin, int xmax) -{ - Render(0, xmin); -} - -void ClipFiller::Span(int val, int len) -{ - int v = val >> 1; - if(last == val) { - int n = min(v + 128 - *lastn - 1, len); - *lastn += n; - len -= n; - } - last = -1; - while(len > 128) { - int n = min(len, 128); - *t++ = 0; - *t++ = v + n - 1; - len -= n; - } - if(len) { - *t++ = 0; - last = val; - lastn = t; - *t++ = v + len - 1; - } -} - -void ClipFiller::Render(int val, int len) -{ - if(val == 256) { - Span(256, len); - empty = false; - } - else { - full = false; - if(val == 0) - Span(0, len); - else { - memset(t, val, len); - t += len; - empty = false; - last = -1; - } - } - x += len; -} - -void ClipFiller::Render(int val) -{ - Render(val, 1); -} - -void ClipFiller::Finish(ClipLine& cl) -{ - if(empty) - return; - while(x < cx) { - int n = min(cx - x, 128); - *t++ = 0; - *t++ = n - 1; - x += n; - full = false; - } - if(full) - cl.SetFull(); - else - cl.Set(~buffer, t - ~buffer); -} - -void MaskFillerFilter::Render(int val) -{ - for(;;) { - if(empty) { - t->Render(0); - empty--; - return; - } - if(full) { - t->Render(val); - full--; - return; - } - byte m = *mask++; - if(m) { - t->Render(val * m >> 8); - return; - } - m = *mask++; - if(m < 128) - empty = m + 1; - else - full = m - 128 + 1; - } -} - -void MaskFillerFilter::Render(int val, int len) -{ - while(len) - if(empty) { - int n = min(len, empty); - t->Render(0, n); - empty -= n; - len -= n; - } - else - if(full) { - int n = min(len, full); - t->Render(val, n); - full -= n; - len -= n; - } - else { - byte m = *mask++; - if(m) { - t->Render(val * m >> 8); - len--; - } - else { - m = *mask++; - if(m < 128) - empty = m + 1; - else - full = m - 128 + 1; - } - } -} - -struct NilFiller : Rasterizer::Filler { - void Start(int minx, int maxx) {} - void Render(int val, int len) {} - void Render(int val) {} -}; - -void MaskFillerFilter::Start(int minx, int maxx) -{ - t->Start(minx, maxx); - Rasterizer::Filler *h = t; - NilFiller nil; - t = &nil; - Render(0, minx); - t = h; -} - -void NoAAFillerFilter::Start(int minx, int maxx) -{ - t->Start(minx, maxx); -} - -void NoAAFillerFilter::Render(int val, int len) -{ - t->Render(val < 128 ? 0 : 256, len); -} - -void NoAAFillerFilter::Render(int val) -{ - t->Render(val < 128 ? 0 : 256); -} - -END_UPP_NAMESPACE +#include "Painter.h" +#include "Fillers.h" + +NAMESPACE_UPP + +void SolidFiller::Start(int minx, int maxx) +{ + t += minx; +} + +void SolidFiller::Render(int val) +{ + AlphaBlendCover8(*t++, c, val); +} + +void SolidFiller::Render(int val, int len) +{ + if(val == 0) { + t += len; + return; + } + if(((val - 256) | (c.a - 255)) == 0) { + while(len >= 16) { + t[0] = c; t[1] = c; t[2] = c; t[3] = c; + t[4] = c; t[5] = c; t[6] = c; t[7] = c; + t[8] = c; t[9] = c; t[10] = c; t[11] = c; + t[12] = c; t[13] = c; t[14] = c; t[15] = c; + t += 16; + len -= 16; + } + switch(len) { + case 15: t[14] = c; + case 14: t[13] = c; + case 13: t[12] = c; + case 12: t[11] = c; + case 11: t[10] = c; + case 10: t[9] = c; + case 9: t[8] = c; + case 8: t[7] = c; + case 7: t[6] = c; + case 6: t[5] = c; + case 5: t[4] = c; + case 4: t[3] = c; + case 3: t[2] = c; + case 2: t[1] = c; + case 1: t[0] = c; + } + t += len; + } + else { + RGBA c1; + if(val != 256) + c1 = Mul8(c, val); + else + c1 = c; + RGBA *e = t + len; + while(t < e) + AlphaBlend(*t++, c1); + } +} + +void SubpixelFiller::Start(int minx, int maxx) +{ + x = minx / 3; + t += x; + v = sbuffer; + int n = minx % 3 + 3; + while(n--) + *v++ = 0; + v[0] = v[1] = v[2] = v[3] = v[4] = 0; + if(ss) { + int xx = maxx / 3; + ss->Get(buffer, x, y, xx - x + 2); + s = buffer; + } +} + +void SubpixelFiller::Render(int val) +{ + int h = (7282 * val) >> 16; + int h2 = h + h; + v[-2] += h; + v[-1] += h2; + v[0] += val - h2 - h2 - h2; + v[1] += h2; + v[2] += h; + v[3] = 0; + v++; + x++; +} + +void SubpixelFiller::Render(int val, int len) +{ + while(len--) + Render(val); +} + +void SubpixelFiller::End() +{ + int16 *q = sbuffer + 3; + while(q < v) { + RGBA c = ss ? Mul8(*s++, alpha) : color; + int a, alpha; + a = c.a * q[0] >> 8; + alpha = 256 - (a + (a >> 7)); + t->r = (c.r * q[0] >> 8) + (alpha * t->r >> 8); + a = c.a * q[1] >> 8; + alpha = 256 - (a + (a >> 7)); + t->g = (c.g * q[1] >> 8) + (alpha * t->g >> 8); + a = c.a * q[2] >> 8; + alpha = 256 - (a + (a >> 7)); + t->b = (c.b * q[2] >> 8) + (alpha * t->b >> 8); + a = c.a * (q[0] + q[1] + q[2]) / 3 >> 8; + alpha = 256 - (a + (a >> 7)); + t->a = a + (alpha * t->a >> 8); + t++; + q += 3; + } +} + +void SpanFiller::Start(int minx, int maxx) +{ + t += minx; + ss->Get(buffer, minx, y, maxx - minx + 1); + s = buffer; +} + +void SpanFiller::Render(int val) +{ + if(alpha != 256) + val = alpha * val >> 8; + AlphaBlendCover8(*t++, *s++, val); +} + +void SpanFiller::Render(int val, int len) +{ + if(val == 0) { + t += len; + s += len; + return; + } + const RGBA *e = t + len; + if(alpha != 256) + val = alpha * val >> 8; + if(val == 256) + while(t < e) { + if(s->a == 255) + *t++ = *s++; + else + AlphaBlend(*t++, *s++); + } + else + while(t < e) + AlphaBlendCover8(*t++, *s++, val); +} + +ClipFiller::ClipFiller(int _cx) +{ + cx = _cx; + buffer.Alloc(2 * cx); +} + +void ClipFiller::Clear() +{ + t = ~buffer; + x = 0; + empty = true; + full = true; + last = -1; +} + +void ClipFiller::Start(int xmin, int xmax) +{ + Render(0, xmin); +} + +void ClipFiller::Span(int val, int len) +{ + int v = val >> 1; + if(last == val) { + int n = min(v + 128 - *lastn - 1, len); + *lastn += n; + len -= n; + } + last = -1; + while(len > 128) { + int n = min(len, 128); + *t++ = 0; + *t++ = v + n - 1; + len -= n; + } + if(len) { + *t++ = 0; + last = val; + lastn = t; + *t++ = v + len - 1; + } +} + +void ClipFiller::Render(int val, int len) +{ + if(val == 256) { + Span(256, len); + empty = false; + } + else { + full = false; + if(val == 0) + Span(0, len); + else { + memset(t, val, len); + t += len; + empty = false; + last = -1; + } + } + x += len; +} + +void ClipFiller::Render(int val) +{ + Render(val, 1); +} + +void ClipFiller::Finish(ClipLine& cl) +{ + if(empty) + return; + while(x < cx) { + int n = min(cx - x, 128); + *t++ = 0; + *t++ = n - 1; + x += n; + full = false; + } + if(full) + cl.SetFull(); + else + cl.Set(~buffer, t - ~buffer); +} + +void MaskFillerFilter::Render(int val) +{ + for(;;) { + if(empty) { + t->Render(0); + empty--; + return; + } + if(full) { + t->Render(val); + full--; + return; + } + byte m = *mask++; + if(m) { + t->Render(val * m >> 8); + return; + } + m = *mask++; + if(m < 128) + empty = m + 1; + else + full = m - 128 + 1; + } +} + +void MaskFillerFilter::Render(int val, int len) +{ + while(len) + if(empty) { + int n = min(len, empty); + t->Render(0, n); + empty -= n; + len -= n; + } + else + if(full) { + int n = min(len, full); + t->Render(val, n); + full -= n; + len -= n; + } + else { + byte m = *mask++; + if(m) { + t->Render(val * m >> 8); + len--; + } + else { + m = *mask++; + if(m < 128) + empty = m + 1; + else + full = m - 128 + 1; + } + } +} + +struct NilFiller : Rasterizer::Filler { + void Start(int minx, int maxx) {} + void Render(int val, int len) {} + void Render(int val) {} +}; + +void MaskFillerFilter::Start(int minx, int maxx) +{ + t->Start(minx, maxx); + Rasterizer::Filler *h = t; + NilFiller nil; + t = &nil; + Render(0, minx); + t = h; +} + +void NoAAFillerFilter::Start(int minx, int maxx) +{ + t->Start(minx, maxx); +} + +void NoAAFillerFilter::Render(int val, int len) +{ + t->Render(val < 128 ? 0 : 256, len); +} + +void NoAAFillerFilter::Render(int val) +{ + t->Render(val < 128 ? 0 : 256); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Painter/Fillers.h b/uppsrc/Painter/Fillers.h index f1dc16062..d83b07c3b 100644 --- a/uppsrc/Painter/Fillers.h +++ b/uppsrc/Painter/Fillers.h @@ -22,6 +22,23 @@ struct SpanFiller : Rasterizer::Filler { void Render(int val, int len); }; +struct SubpixelFiller : Rasterizer::Filler { + int16 *sbuffer; + RGBA *t; + int16 *v; + RGBA *s; + RGBA color; + SpanSource *ss; + int alpha; + RGBA *buffer; + int x, y; + + void Start(int minx, int maxx); + void Render(int val); + void Render(int val, int len); + void End(); +}; + struct ClipFiller : Rasterizer::Filler { Buffer buffer; byte *t; @@ -53,6 +70,7 @@ struct MaskFillerFilter : Rasterizer::Filler { void Start(int minx, int maxx); void Render(int val, int len); void Render(int val); + void End() { t->End(); } void Set(Rasterizer::Filler *f, const byte *m) { t = f; mask = m; empty = full = 0; } }; @@ -63,6 +81,7 @@ struct NoAAFillerFilter : Rasterizer::Filler { void Start(int minx, int maxx); void Render(int val, int len); void Render(int val); + void End() { t->End(); } void Set(Rasterizer::Filler *f) { t = f; } }; diff --git a/uppsrc/Painter/Image.cpp b/uppsrc/Painter/Image.cpp index 1313fb660..07eb22072 100644 --- a/uppsrc/Painter/Image.cpp +++ b/uppsrc/Painter/Image.cpp @@ -220,7 +220,10 @@ void BufferPainter::RenderImage(double width, const Image& image, const Xform2D& ss.hstyle = byte(flags & 3); ss.vstyle = byte(flags & 12); ss.fast = flags & FILL_FAST; - ss.Set(transsrc * pathattr.mtx, image); + Xform2D m = transsrc * pathattr.mtx; + if(subpixel) + m = m * Xform2D::Scale(1 / 3.0, 1); + ss.Set(m, image); RenderPath(width, &ss, RGBAZero()); } diff --git a/uppsrc/Painter/Painter.h b/uppsrc/Painter/Painter.h index 259c325c2..e27b82d74 100644 --- a/uppsrc/Painter/Painter.h +++ b/uppsrc/Painter/Painter.h @@ -146,13 +146,10 @@ protected: static bool ReadBool(CParser& p); static double ReadDouble(CParser& p); static Pointf ReadPoint(CParser& p); - void ArcSegment(const Pointf& c, double th0, double th1, const Pointf& r, double xAxisRotation); + void DoArc0(double theta, double th_sweep, const Xform2D& m); void DoArc(const Pointf& c, const Pointf& r, double angle, double sweep, double xangle); void DoSvgArc(const Pointf& rr, double xangle, int large, int sweep, const Pointf& p, const Pointf& p0); -// void DoSvgArc(double rx, double ry, double x_axis_rotation, -// int large_arc_flag, int sweep_flag, -// double x, double y, double curx, double cury); public: void Clear(const RGBA& color); diff --git a/uppsrc/Painter/Path.cpp b/uppsrc/Painter/Path.cpp index 599df59df..ce56f9d4d 100644 --- a/uppsrc/Painter/Path.cpp +++ b/uppsrc/Painter/Path.cpp @@ -1,131 +1,119 @@ -#include "Painter.h" - -NAMESPACE_UPP - -void BufferPainter::ClearPath() -{ - path.type.Clear(); - path.data.Clear(); - current = move = Null; - ccontrol = qcontrol = Pointf(0, 0); -} - -Pointf BufferPainter::PathPoint(const Pointf& p, bool rel) -{ - Pointf r; - r.x = IsNull(p.x) ? current.x : rel ? p.x + current.x : p.x; - r.y = IsNull(p.y) ? current.y : rel ? p.y + current.y : p.y; - if(IsNull(current)) { - ClearPath(); - pathrect.left = pathrect.right = r.x; - pathrect.top = pathrect.bottom = r.y; - pathattr = attr; - } - else { - pathrect.left = min(pathrect.left, r.x); - pathrect.top = min(pathrect.top, r.y); - pathrect.right = max(pathrect.right, r.x); - pathrect.bottom = max(pathrect.bottom, r.y); - } - return r; -} - -Pointf BufferPainter::EndPoint(const Pointf& p, bool rel) -{ - return current = PathPoint(p, rel); -} - -void *BufferPainter::PathAddRaw(int type, int size) -{ - int q = path.data.GetCount(); - path.type.Add(type); - path.data.SetCount(q + size); - return &path.data[q]; -} - -void BufferPainter::MoveOp(const Pointf& p, bool rel) -{ - move = ccontrol = qcontrol = EndPoint(p, rel); - PathAdd(MOVE).p = move; -} - -void BufferPainter::DoMove0() -{ - if(IsNull(move)) - MoveOp(Pointf(0, 0), false); -} - -void BufferPainter::LineOp(const Pointf& p, bool rel) -{ - DoMove0(); - PathAdd(LINE).p = ccontrol = qcontrol = EndPoint(p, rel); -} - -void BufferPainter::QuadraticOp(const Pointf& p1, const Pointf& p, bool rel) -{ - DoMove0(); - QuadraticData& m = PathAdd(QUADRATIC); - qcontrol = m.p1 = PathPoint(p1, rel); - m.p = EndPoint(p, rel); -} - -void BufferPainter::QuadraticOp(const Pointf& p, bool rel) -{ - QuadraticOp(2.0 * current - qcontrol, p, rel); -} - -void BufferPainter::CubicOp(const Pointf& p1, const Pointf& p2, const Pointf& p, bool rel) -{ - DoMove0(); - CubicData& m = PathAdd(CUBIC); - m.p1 = PathPoint(p1, rel); - ccontrol = m.p2 = PathPoint(p2, rel); - m.p = EndPoint(p, rel); -} - -void BufferPainter::CubicOp(const Pointf& p2, const Pointf& p, bool rel) -{ - CubicOp(2.0 * current - ccontrol, p2, p, rel); -} - -Pointf BufferPainter::ArcData::EndPoint() const -{ - return r * Polar(angle + sweep) + p; -} - -void BufferPainter::ArcOp(const Pointf& c, const Pointf& r, double angle, double sweep, bool rel) -{ - DoMove0(); - ArcData& m = PathAdd(ARC); - m.p = PathPoint(c, rel); - m.r = r; - m.angle = angle; - m.sweep = sweep; - PathPoint(c + r, rel); - PathPoint(c - r, rel); - current = m.EndPoint(); -} - -void BufferPainter::SvgArcOp(const Pointf& r, double xangle, bool large, bool sweep, - const Pointf& p, bool rel) -{ - DoMove0(); - Pointf c = current; - DoSvgArc(r, xangle, large, sweep, EndPoint(p, rel), c); -} - -void BufferPainter::CloseOp() -{ - if(!IsNull(move) && !IsNull(current) && current != move) { - Line(move); - move = Null; - } -} - -void BufferPainter::DivOp() -{ - CloseOp(); - path.type.Add(DIV); -} - -END_UPP_NAMESPACE +#include "Painter.h" + +NAMESPACE_UPP + +void BufferPainter::ClearPath() +{ + path.type.Clear(); + path.data.Clear(); + current = move = Null; + ccontrol = qcontrol = Pointf(0, 0); +} + +Pointf BufferPainter::PathPoint(const Pointf& p, bool rel) +{ + Pointf r; + r.x = IsNull(p.x) ? current.x : rel ? p.x + current.x : p.x; + r.y = IsNull(p.y) ? current.y : rel ? p.y + current.y : p.y; + if(IsNull(current)) { + ClearPath(); + pathrect.left = pathrect.right = r.x; + pathrect.top = pathrect.bottom = r.y; + pathattr = attr; + } + else { + pathrect.left = min(pathrect.left, r.x); + pathrect.top = min(pathrect.top, r.y); + pathrect.right = max(pathrect.right, r.x); + pathrect.bottom = max(pathrect.bottom, r.y); + } + return r; +} + +Pointf BufferPainter::EndPoint(const Pointf& p, bool rel) +{ + return current = PathPoint(p, rel); +} + +void *BufferPainter::PathAddRaw(int type, int size) +{ + int q = path.data.GetCount(); + path.type.Add(type); + path.data.SetCount(q + size); + return &path.data[q]; +} + +void BufferPainter::MoveOp(const Pointf& p, bool rel) +{ + move = ccontrol = qcontrol = EndPoint(p, rel); + PathAdd(MOVE).p = move; +} + +void BufferPainter::DoMove0() +{ + if(IsNull(move)) + MoveOp(Pointf(0, 0), false); +} + +void BufferPainter::LineOp(const Pointf& p, bool rel) +{ + DoMove0(); + PathAdd(LINE).p = ccontrol = qcontrol = EndPoint(p, rel); +} + +void BufferPainter::QuadraticOp(const Pointf& p1, const Pointf& p, bool rel) +{ + DoMove0(); + QuadraticData& m = PathAdd(QUADRATIC); + qcontrol = m.p1 = PathPoint(p1, rel); + m.p = EndPoint(p, rel); +} + +void BufferPainter::QuadraticOp(const Pointf& p, bool rel) +{ + QuadraticOp(2.0 * current - qcontrol, p, rel); +} + +void BufferPainter::CubicOp(const Pointf& p1, const Pointf& p2, const Pointf& p, bool rel) +{ + DoMove0(); + CubicData& m = PathAdd(CUBIC); + m.p1 = PathPoint(p1, rel); + ccontrol = m.p2 = PathPoint(p2, rel); + m.p = EndPoint(p, rel); +} + +void BufferPainter::CubicOp(const Pointf& p2, const Pointf& p, bool rel) +{ + CubicOp(2.0 * current - ccontrol, p2, p, rel); +} + +void BufferPainter::ArcOp(const Pointf& c, const Pointf& r, double angle, double sweep, bool rel) +{ + DoMove0(); + DoArc(PathPoint(c, rel), r, angle, sweep, 0); +} + +void BufferPainter::SvgArcOp(const Pointf& r, double xangle, bool large, bool sweep, + const Pointf& p, bool rel) +{ + DoMove0(); + Pointf c = current; + DoSvgArc(r, xangle, large, sweep, EndPoint(p, rel), c); +} + +void BufferPainter::CloseOp() +{ + if(!IsNull(move) && !IsNull(current) && current != move) { + Line(move); + move = Null; + } +} + +void BufferPainter::DivOp() +{ + CloseOp(); + path.type.Add(DIV); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Painter/RadialGradient.cpp b/uppsrc/Painter/RadialGradient.cpp index 37809defa..933e78bef 100644 --- a/uppsrc/Painter/RadialGradient.cpp +++ b/uppsrc/Painter/RadialGradient.cpp @@ -198,7 +198,10 @@ void BufferPainter::RenderRadial(double width, const Pointf& f, const RGBA& colo const Pointf& c, double r, const RGBA& color2, int style) { PainterRadialSpan sg; - sg.interpolator.Set(Inverse(pathattr.mtx)); + Xform2D m = pathattr.mtx; + if(subpixel) + m = m * Xform2D::Scale(1.0 / 3, 1); + sg.interpolator.Set(Inverse(m)); sg.style = style; sg.Set((int)c.x, (int)c.y, (int)r, (int)f.x, (int)f.y); MakeGradient(color1, color2, 2048); diff --git a/uppsrc/Painter/Rasterizer.cpp b/uppsrc/Painter/Rasterizer.cpp index 8763eda70..0f44af929 100644 --- a/uppsrc/Painter/Rasterizer.cpp +++ b/uppsrc/Painter/Rasterizer.cpp @@ -1,352 +1,355 @@ -#include "Painter.h" - -// This code is based in AGG rasterizer with this original information: - -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.4 -// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// -// The author gratefully acknowleges the support of David Turner, -// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType -// libray - in producing this work. See http://www.freetype.org for details. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://www.antigrain.com -//---------------------------------------------------------------------------- - -#define LLOG(x) // LOG(x) - -NAMESPACE_UPP - -void Rasterizer::Init() -{ - p0 = Pointf(0, 0); - min_y = INT_MAX; - max_y = INT_MIN; -} - -void Rasterizer::Reset() -{ - PAINTER_TIMING("Rasterizer::Reset"); - for(int i = min_y; i <= max_y; i++) - cell[i].SetCount(0); - Init(); -} - -void Rasterizer::SetClip(const Rectf& rect) -{ - cliprect = rect & Sizef(sz); -} - -Rasterizer::Rasterizer(int cx, int cy) -{ - sz.cx = cx; - sz.cy = cy; - cell.Alloc(sz.cy + 1); - cliprect = Sizef(sz); - Init(); - Reset(); -} - -inline Rasterizer::Cell *Rasterizer::AddCells(int y, int n) -{ - Vector& v = cell[y]; - if(v.GetAlloc() == 0) { - v.Reserve(16); - v.SetCount(n); - return &v[0]; - } - y = v.GetCount(); - v.SetCount(y + n); - return &v[y]; -} - -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; - Cell *c; - if(y1 == y2) - return; - int dy = y2 - y1; - if(ex1 == ex2) { - c = AddCells(ey, 1); - c->x = ex1; - c->cover = dy; - c->area = (fx1 + fx2) * dy; - return; - } - int dx = x2 - x1; - if(dx < 0) { - int p = fx1 * dy; - dx = -dx; - int delta = p / dx; - int mod = p % dx; - if(mod < 0) { - delta--; - mod += dx; - } - c = AddCells(ey, abs(ex2 - ex1) + 1); - c->x = ex1; - c->cover = delta; - c->area = fx1 * delta; - c++; - ex1--; - y1 += delta; - if(ex1 != ex2) { - p = (y2 - y1 + delta) * 256; - 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++; - } - c->x = ex1; - c->cover = delta; - c->area = delta * 256; - c++; - y1 += delta; - ex1--; - } - } - c->x = ex2; - dy = y2 - y1; - c->cover = dy; - c->area = (fx2 + 256) * dy; - } - else { - int p = (256 - fx1) * dy; - int delta = p / dx; - int mod = p % dx; - if(mod < 0) { - delta--; - mod += dx; - } - c = AddCells(ey, abs(ex2 - ex1) + 1); - c->x = ex1; - c->cover = delta; - c->area = (fx1 + 256) * delta; - c++; - ex1++; - y1 += delta; - if(ex1 != ex2) { - p = (y2 - y1 + delta) * 256; - 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++; - } - c->x = ex1; - c->cover = delta; - c->area = delta * 256; - c++; - y1 += delta; - ex1++; - } - } - c->x = ex2; - dy = y2 - y1; - c->cover = dy; - c->area = fx2 * dy; - } -} - -void Rasterizer::LineRaw(int x1, int y1, int x2, int y2) -{ - PAINTER_TIMING("LineRaw"); - LLOG("Rasterizer::LineRaw " << x1 / 256.0 << ':' << y1 / 256.0 - << " - " << x2 / 256.0 << ':' << y2 / 256.0); - int ex1 = x1 >> 8; - int ex2 = x2 >> 8; - int ey1 = y1 >> 8; - int ey2 = y2 >> 8; - - ASSERT(ey1 >= 0 && ey1 <= sz.cy && ey2 >= 0 && ey2 <= sz.cy); - - if(ey1 < min_y) { - if(ey1 < 0) return; - min_y = ey1; - } - if(ey1 > max_y) { - if(ey1 > sz.cy) return; - max_y = min(ey1, sz.cy - 1); - } - if(ey2 < min_y) { - if(ey2 < 0) return; - min_y = ey2; - } - if(ey2 > max_y) { - if(ey2 > sz.cy) return; - max_y = min(ey2, sz.cy - 1); - } - - 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; - LineRaw(x1, y1, cx, cy); - LineRaw(cx, cy, x2, y2); - return; - } - int dy = y2 - y1; - int fy1 = y1 & 255; - int fy2 = y2 & 255; - - - Cell *c; - int x_from, x_to; - int p, rem, mod, lift, delta, first, incr; - - 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; - c = AddCells(ey1, 1); - c->x = ex1; - c->cover = delta; - c->area = two_fx * delta; - ey1 += incr; - delta = first + first - 256; - area = two_fx * delta; - while(ey1 != ey2) { - c = AddCells(ey1, 1); - c->x = ex1; - c->cover = delta; - c->area = area; - ey1 += incr; - } - delta = fy2 - 256 + first; - c = AddCells(ey1, 1); - c->x = ex1; - c->cover = delta; - c->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; - 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; - } - } - RenderHLine(ey1, x_from, 256 - first, x2, fy2); -} - -void Rasterizer::Render(int y, Rasterizer::Filler& g, bool evenodd) -{ - PAINTER_TIMING("Render"); - const Cell *c, *e; - if(y < min_y || y > max_y) return; - Vector& cl = cell[y]; - if(cl.GetCount() == 0) return; - Sort(cl); - c = cl; - e = cl.End(); - g.Start(c->x, (e - 1)->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(evenodd) { - if(area) { - int h = abs(((cover << 9) - area) >> 9) & 511; - g.Render(h > 256 ? 512 - h : h); - x++; - } - if(c < e && c->x > x) { - int h = abs(cover) & 511; - g.Render(h > 256 ? 512 - h : h, c->x - x); - } - } - else { - if(area) { - g.Render(min(abs(((cover << 9) - area) >> 9), 256)); - x++; - } - if(c < e && c->x > x) - g.Render(min(abs(cover), 256), c->x - x); - } - } -} - -END_UPP_NAMESPACE +#include "Painter.h" + +// This code is based in AGG rasterizer with this original information: + +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#define LLOG(x) // LOG(x) + +NAMESPACE_UPP + +void Rasterizer::Init() +{ + p0 = Pointf(0, 0); + min_y = INT_MAX; + max_y = INT_MIN; +} + +void Rasterizer::Reset() +{ + PAINTER_TIMING("Rasterizer::Reset"); + for(int i = min_y; i <= max_y; i++) + cell[i].SetCount(0); + Init(); +} + +void Rasterizer::SetClip(const Rectf& rect) +{ + cliprect = rect & Sizef(sz); +} + +Rasterizer::Rasterizer(int cx, int cy) +{ + sz.cx = cx; + sz.cy = cy; + cell.Alloc(sz.cy + 1); + cliprect = Sizef(sz); + Init(); + Reset(); +} + +inline Rasterizer::Cell *Rasterizer::AddCells(int y, int n) +{ + Vector& v = cell[y]; + if(v.GetAlloc() == 0) { + v.Reserve(16); + v.SetCount(n); + return &v[0]; + } + y = v.GetCount(); + v.SetCount(y + n); + return &v[y]; +} + +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; + Cell *c; + if(y1 == y2) + return; + int dy = y2 - y1; + if(ex1 == ex2) { + c = AddCells(ey, 1); + c->x = ex1; + c->cover = dy; + c->area = (fx1 + fx2) * dy; + return; + } + int dx = x2 - x1; + if(dx < 0) { + int p = fx1 * dy; + dx = -dx; + int delta = p / dx; + int mod = p % dx; + if(mod < 0) { + delta--; + mod += dx; + } + c = AddCells(ey, abs(ex2 - ex1) + 1); + c->x = ex1; + c->cover = delta; + c->area = fx1 * delta; + c++; + ex1--; + y1 += delta; + if(ex1 != ex2) { + p = (y2 - y1 + delta) * 256; + 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++; + } + c->x = ex1; + c->cover = delta; + c->area = delta * 256; + c++; + y1 += delta; + ex1--; + } + } + c->x = ex2; + dy = y2 - y1; + c->cover = dy; + c->area = (fx2 + 256) * dy; + } + else { + int p = (256 - fx1) * dy; + int delta = p / dx; + int mod = p % dx; + if(mod < 0) { + delta--; + mod += dx; + } + c = AddCells(ey, abs(ex2 - ex1) + 1); + c->x = ex1; + c->cover = delta; + c->area = (fx1 + 256) * delta; + c++; + ex1++; + y1 += delta; + if(ex1 != ex2) { + p = (y2 - y1 + delta) * 256; + 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++; + } + c->x = ex1; + c->cover = delta; + c->area = delta * 256; + c++; + y1 += delta; + ex1++; + } + } + c->x = ex2; + dy = y2 - y1; + c->cover = dy; + c->area = fx2 * dy; + } +} + +void Rasterizer::LineRaw(int x1, int y1, int x2, int y2) +{ + PAINTER_TIMING("LineRaw"); + LLOG("Rasterizer::LineRaw " << x1 / 256.0 << ':' << y1 / 256.0 + << " - " << x2 / 256.0 << ':' << y2 / 256.0); + int ex1 = x1 >> 8; + int ex2 = x2 >> 8; + int ey1 = y1 >> 8; + int ey2 = y2 >> 8; + + ASSERT(ey1 >= 0 && ey1 <= sz.cy && ey2 >= 0 && ey2 <= sz.cy); + + if(ey1 < min_y) { + if(ey1 < 0) return; + min_y = ey1; + } + if(ey1 > max_y) { + if(ey1 > sz.cy) return; + max_y = min(ey1, sz.cy - 1); + } + if(ey2 < min_y) { + if(ey2 < 0) return; + min_y = ey2; + } + if(ey2 > max_y) { + if(ey2 > sz.cy) return; + max_y = min(ey2, sz.cy - 1); + } + + 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; + LineRaw(x1, y1, cx, cy); + LineRaw(cx, cy, x2, y2); + return; + } + int dy = y2 - y1; + int fy1 = y1 & 255; + int fy2 = y2 & 255; + + + Cell *c; + int x_from, x_to; + int p, rem, mod, lift, delta, first, incr; + + 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; + c = AddCells(ey1, 1); + c->x = ex1; + c->cover = delta; + c->area = two_fx * delta; + ey1 += incr; + delta = first + first - 256; + area = two_fx * delta; + while(ey1 != ey2) { + c = AddCells(ey1, 1); + c->x = ex1; + c->cover = delta; + c->area = area; + ey1 += incr; + } + delta = fy2 - 256 + first; + c = AddCells(ey1, 1); + c->x = ex1; + c->cover = delta; + c->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; + 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; + } + } + RenderHLine(ey1, x_from, 256 - first, x2, fy2); +} + +void Rasterizer::Filler::End() {} + +void Rasterizer::Render(int y, Rasterizer::Filler& g, bool evenodd) +{ + PAINTER_TIMING("Render"); + const Cell *c, *e; + if(y < min_y || y > max_y) return; + Vector& cl = cell[y]; + if(cl.GetCount() == 0) return; + Sort(cl); + c = cl; + e = cl.End(); + g.Start(c->x, (e - 1)->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(evenodd) { + if(area) { + int h = abs(((cover << 9) - area) >> 9) & 511; + g.Render(h > 256 ? 512 - h : h); + x++; + } + if(c < e && c->x > x) { + int h = abs(cover) & 511; + g.Render(h > 256 ? 512 - h : h, c->x - x); + } + } + else { + if(area) { + g.Render(min(abs(((cover << 9) - area) >> 9), 256)); + x++; + } + if(c < e && c->x > x) + g.Render(min(abs(cover), 256), c->x - x); + } + } + g.End(); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Painter/Render.cpp b/uppsrc/Painter/Render.cpp index 882f41d71..98aa2a809 100644 --- a/uppsrc/Painter/Render.cpp +++ b/uppsrc/Painter/Render.cpp @@ -1,163 +1,173 @@ -#include "Painter.h" -#include "Fillers.h" - -NAMESPACE_UPP - -void BufferPainter::ClearOp(const RGBA& color) -{ - UPP::Fill(~ib, color, ib.GetLength()); -} - -Buffer BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) -{ - Buffer newclip; - if(width == 0) - return newclip; - Transformer trans(pathattr.mtx); - trans.target = &rasterizer; - LinearPathConsumer *g; - Stroker stroker; - Dasher dasher; - bool evenodd = pathattr.evenodd; - if(width > 0) { - stroker.Init(width, pathattr.miter_limit, pathattr.tolerance, pathattr.cap, pathattr.join); - stroker.target = &trans; - if(pathattr.dash.GetCount()) { - dasher.Init(pathattr.dash, pathattr.dash_start); - dasher.target = &stroker; - g = &dasher; - } - else - g = &stroker; - evenodd = false; - } - else { - Close(); - g = &trans; - } - byte *data = path.data; - int opacity = int(256 * pathattr.opacity); - Pointf pos = Pointf(0, 0); - int i = 0; - Rasterizer::Filler *rg; - SpanFiller span_filler; - SolidFiller solid_filler; - ClipFiller clip_filler(ib.GetWidth()); - NoAAFillerFilter noaa_filler; - bool doclip = width == -2; - if(doclip) { - rg = &clip_filler; - newclip.Alloc(ib.GetHeight()); - } - else - if(ss) { - if(!span) - span.Alloc(ib.GetWidth() + 1); - span_filler.ss = ss; - span_filler.buffer = span; - span_filler.alpha = opacity; - rg = &span_filler; - } - else { - solid_filler.c = Mul8(color, opacity); - rg = &solid_filler; - } - if(pathattr.noaa) { - noaa_filler.Set(rg); - rg = &noaa_filler; - } - for(;;) { - if(i >= path.type.GetCount() || path.type[i] == DIV) { - g->End(); - for(int y = rasterizer.MinY(); y <= rasterizer.MaxY(); y++) { - solid_filler.t = span_filler.t = ib[y]; - span_filler.y = y; - Rasterizer::Filler *rf = rg; - MaskFillerFilter mf; - if(clip.GetCount()) { - const ClipLine& s = clip.Top()[y]; - if(s.IsEmpty()) goto empty; - if(!s.IsFull()) { - mf.Set(rg, s); - rf = &mf; - } - } - if(doclip) - clip_filler.Clear(); - rasterizer.Render(y, *rf, evenodd); - if(doclip) - clip_filler.Finish(newclip[y]); - empty:; - } - rasterizer.Reset(); - if(i >= path.type.GetCount()) - break; - } - else - switch(path.type[i]) { - case MOVE: { - const LinearData *d = (LinearData *)data; - data += sizeof(LinearData); - g->Move(pos = d->p); - break; - } - case LINE: { - const LinearData *d = (LinearData *)data; - data += sizeof(LinearData); - g->Line(pos = d->p); - break; - } - case QUADRATIC: { - const QuadraticData *d = (QuadraticData *)data; - data += sizeof(QuadraticData); - ApproximateQuadratic(*g, pos, d->p1, d->p, pathattr.tolerance); - pos = d->p; - break; - } - case CUBIC: { - const CubicData *d = (CubicData *)data; - data += sizeof(CubicData); - ApproximateCubic(*g, pos, d->p1, d->p2, d->p, pathattr.tolerance); - pos = d->p; - break; - } - case ARC: { - const ArcData *d = (ArcData *)data; - data += sizeof(ArcData); - ApproximateArc(*g, d->p, d->r, d->angle, d->sweep, pathattr.tolerance); - pos = d->EndPoint(); - break; - } - default: - NEVER(); - return newclip; - } - i++; - } - current = Null; - return newclip; -} - -void BufferPainter::FillOp(const RGBA& color) -{ - RenderPath(-1, NULL, color); -} - -void BufferPainter::StrokeOp(double width, const RGBA& color) -{ - RenderPath(width, NULL, color); -} - -void BufferPainter::ClipOp() -{ - Buffer newclip = RenderPath(-2, NULL, RGBAZero()); - if(attr.hasclip) - clip.Top() = newclip; - else { - clip.Add() = newclip; - attr.hasclip = true; - attr.cliplevel = clip.GetCount(); - } -} - -END_UPP_NAMESPACE +#include "Painter.h" +#include "Fillers.h" + +NAMESPACE_UPP + +void BufferPainter::ClearOp(const RGBA& color) +{ + UPP::Fill(~ib, color, ib.GetLength()); +} + +Buffer BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) +{ + Buffer newclip; + if(width == 0) + return newclip; + Transformer trans(pathattr.mtx); + trans.target = &rasterizer; + LinearPathConsumer *g; + Stroker stroker; + Dasher dasher; + bool evenodd = pathattr.evenodd; + if(width > 0) { + stroker.Init(width, pathattr.miter_limit, pathattr.tolerance, pathattr.cap, pathattr.join); + stroker.target = &trans; + if(pathattr.dash.GetCount()) { + dasher.Init(pathattr.dash, pathattr.dash_start); + dasher.target = &stroker; + g = &dasher; + } + else + g = &stroker; + evenodd = false; + } + else { + Close(); + g = &trans; + } + byte *data = path.data; + int opacity = int(256 * pathattr.opacity); + Pointf pos = Pointf(0, 0); + int i = 0; + Rasterizer::Filler *rg; + SpanFiller span_filler; + SolidFiller solid_filler; + SubpixelFiller subpixel_filler; + ClipFiller clip_filler(render_cx); + NoAAFillerFilter noaa_filler; + MaskFillerFilter mf; + bool doclip = width == -2; + subpixel_filler.sbuffer = subpixel; + if(doclip) { + rg = &clip_filler; + newclip.Alloc(ib.GetHeight()); + } + else + if(ss) { + if(!span) + span.Alloc(ib.GetWidth() + 3); + if(subpixel) { + subpixel_filler.ss = ss; + subpixel_filler.buffer = span; + subpixel_filler.alpha = opacity; + rg = &subpixel_filler; + } + else { + span_filler.ss = ss; + span_filler.buffer = span; + span_filler.alpha = opacity; + rg = &span_filler; + } + } + else { + if(subpixel) { + subpixel_filler.color = Mul8(color, opacity); + subpixel_filler.ss = NULL; + rg = &subpixel_filler; + } + else { + solid_filler.c = Mul8(color, opacity); + rg = &solid_filler; + } + } + if(pathattr.noaa) { + noaa_filler.Set(rg); + rg = &noaa_filler; + } + for(;;) { + if(i >= path.type.GetCount() || path.type[i] == DIV) { + g->End(); + for(int y = rasterizer.MinY(); y <= rasterizer.MaxY(); y++) { + solid_filler.t = subpixel_filler.t = span_filler.t = ib[y]; + span_filler.y = subpixel_filler.y = y; + Rasterizer::Filler *rf = rg; + if(clip.GetCount()) { + const ClipLine& s = clip.Top()[y]; + if(s.IsEmpty()) goto empty; + if(!s.IsFull()) { + mf.Set(rg, s); + rf = &mf; + } + } + if(doclip) + clip_filler.Clear(); + rasterizer.Render(y, *rf, evenodd); + if(doclip) + clip_filler.Finish(newclip[y]); + empty:; + } + rasterizer.Reset(); + if(i >= path.type.GetCount()) + break; + } + else + switch(path.type[i]) { + case MOVE: { + const LinearData *d = (LinearData *)data; + data += sizeof(LinearData); + g->Move(pos = d->p); + break; + } + case LINE: { + const LinearData *d = (LinearData *)data; + data += sizeof(LinearData); + g->Line(pos = d->p); + break; + } + case QUADRATIC: { + const QuadraticData *d = (QuadraticData *)data; + data += sizeof(QuadraticData); + ApproximateQuadratic(*g, pos, d->p1, d->p, pathattr.tolerance); + pos = d->p; + break; + } + case CUBIC: { + const CubicData *d = (CubicData *)data; + data += sizeof(CubicData); + ApproximateCubic(*g, pos, d->p1, d->p2, d->p, pathattr.tolerance); + pos = d->p; + break; + } + default: + NEVER(); + return newclip; + } + i++; + } + current = Null; + return newclip; +} + +void BufferPainter::FillOp(const RGBA& color) +{ + RenderPath(-1, NULL, color); +} + +void BufferPainter::StrokeOp(double width, const RGBA& color) +{ + RenderPath(width, NULL, color); +} + +void BufferPainter::ClipOp() +{ + Buffer newclip = RenderPath(-2, NULL, RGBAZero()); + if(attr.hasclip) + clip.Top() = newclip; + else { + clip.Add() = newclip; + attr.hasclip = true; + attr.cliplevel = clip.GetCount(); + } +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Painter/SvgArc.cpp b/uppsrc/Painter/SvgArc.cpp index 8f4a7025e..970d0ffd9 100644 --- a/uppsrc/Painter/SvgArc.cpp +++ b/uppsrc/Painter/SvgArc.cpp @@ -2,6 +2,30 @@ NAMESPACE_UPP +void Painter::DoArc0(double theta, double th_sweep, const Xform2D& m) +{ + int nsegs = int(ceil(fabs(th_sweep / (M_PI * 0.5 + 0.001)))); + for(int i = 0; i < nsegs; i++) { + double th0 = theta + i * th_sweep / nsegs; + double th1 = theta + (i + 1) * th_sweep / nsegs; + double thHalf = 0.5 * (th1 - th0); + double t = (8.0 / 3.0) * sin(thHalf * 0.5) * sin(thHalf * 0.5) / sin(thHalf); + double x3 = cos(th1); + double y3 = sin(th1); + Cubic(m.Transform(cos(th0) - t * sin(th0), sin(th0) + t * cos(th0)), + m.Transform(x3 + t * sin(th1), y3 - t * cos(th1)), + m.Transform(x3, y3)); + } +} + +void Painter::DoArc(const Pointf& c, const Pointf& r, double angle, double sweep, double xangle) +{ + Xform2D m = Xform2D::Scale(r.x, r.y); + m = m * Xform2D::Translation(c.x, c.y); + Line(m.Transform(cos(angle), sin(angle))); + DoArc0(angle, sweep, m); +} + void Painter::DoSvgArc(const Pointf& rr, double xangle, int large, int sweep, const Pointf& p1, const Pointf& p0) { @@ -33,21 +57,11 @@ void Painter::DoSvgArc(const Pointf& rr, double xangle, int large, int sweep, else if(th_sweep > 0 && !sweep) th_sweep -= 2 * M_PI; - int nsegs = int(ceil(fabs(th_sweep / (M_PI * 0.5 + 0.001)))); m = Xform2D::Rotation(xangle); m.x *= r; m.y *= r; - for(int i = 0; i < nsegs; i++) { - double th0 = theta + i * th_sweep / nsegs; - double th1 = theta + (i + 1) * th_sweep / nsegs; - double thHalf = 0.5 * (th1 - th0); - double t = (8.0 / 3.0) * sin(thHalf * 0.5) * sin(thHalf * 0.5) / sin(thHalf); - double x3 = c.x + cos(th1); - double y3 = c.y + sin(th1); - Cubic(m.Transform(c.x + cos(th0) - t * sin(th0), c.y + sin(th0) + t * cos(th0)), - m.Transform(x3 + t * sin(th1), y3 - t * cos(th1)), - m.Transform(x3, y3)); - } + m = Xform2D::Translation(c.x, c.y) * m; + DoArc0(theta, th_sweep, m); } END_UPP_NAMESPACE diff --git a/uppsrc/Painter/Xform2D.cpp b/uppsrc/Painter/Xform2D.cpp index 18fbf769c..4a6e52708 100644 --- a/uppsrc/Painter/Xform2D.cpp +++ b/uppsrc/Painter/Xform2D.cpp @@ -1,115 +1,114 @@ -#include "Painter.h" - -NAMESPACE_UPP - -Pointf Xform2D::Transform(const Pointf& f) const -{ - return Pointf(f.x * x.x + f.y * x.y + t.x, f.x * y.x + f.y * y.y + t.y); -} - -Pointf Xform2D::Transform(double x, double y) const -{ - return Transform(Pointf(x, y)); -} - -Pointf Xform2D::GetScaleXY() const -{ - return Pointf(sqrt(x.x * x.x + y.x * y.x), sqrt(x.y * x.y + y.y * y.y)); -} - -double Xform2D::GetScale() const -{ - Pointf d = GetScaleXY(); - return Length(d) / M_SQRT2; -} - -bool Xform2D::IsRegular() const -{ - Pointf d = GetScaleXY(); - return fabs(d.x - d.y) < 1e-10 * fabs(max(d.x, d.y)); -} - -Xform2D::Xform2D() -{ - x.x = y.y = 1; - x.y = y.x = t.x = t.y = 0; -} - -Xform2D operator*(const Xform2D& a, const Xform2D& b) -{ - Xform2D r; - r.x.x = a.x.x * b.x.x + a.y.x * b.x.y; - r.x.y = a.x.y * b.x.x + a.y.y * b.x.y; - r.y.x = a.x.x * b.y.x + a.y.x * b.y.y; - r.y.y = a.x.y * b.y.x + a.y.y * b.y.y; - r.t.x = a.t.x * b.x.x + a.t.y * b.x.y + b.t.x; - r.t.y = a.t.x * b.y.x + a.t.y * b.y.y + b.t.y; - return r; -} - -Xform2D Xform2D::Translation(double x, double y) -{ - Xform2D m; - m.x.x = m.y.y = 1; - m.t = Pointf(x, y); - return m; -} - -Xform2D Xform2D::Scale(double sx, double sy) -{ - Xform2D m; - m.x.x = sx; - m.y.y = sy; - return m; -} - -Xform2D Xform2D::Scale(double scale) -{ - return Scale(scale, scale); -} - -Xform2D Xform2D::Rotation(double fi) -{ - double cosf = cos(fi); - double sinf = sin(fi); - Xform2D m; - m.x.x = cosf; - m.x.y = -sinf; - m.y.x = sinf; - m.y.y = cosf; - m.t.x = m.t.y = 0; - return m; -} - -Xform2D Xform2D::Sheer(double fi) -{ - Xform2D m; - m.x.x = m.y.y = 1; - m.x.y = atan(fi); - return m; -} - -Xform2D Xform2D::Identity() -{ - Xform2D m; - m.x.x = m.y.y = 1; - return m; -} - -double Determinant(const Xform2D& m) -{ - return m.x.x * m.y.y - m.y.x * m.x.y; -} - -Xform2D Inverse(const Xform2D& m) -{ - Xform2D r; - double det = Determinant(m); - r.x = Pointf(m.y.y, -m.x.y) / det; - r.y = Pointf(-m.y.x, m.x.x) / det; - r.t.x = -m.t.x * r.x.x - m.t.y * r.x.y; - r.t.y = -m.t.x * r.y.x - m.t.y * r.y.y; - return r; -} - -END_UPP_NAMESPACE +#include "Painter.h" + +NAMESPACE_UPP + +Pointf Xform2D::Transform(const Pointf& f) const +{ + return Pointf(f.x * x.x + f.y * x.y + t.x, f.x * y.x + f.y * y.y + t.y); +} + +Pointf Xform2D::Transform(double x, double y) const +{ + return Transform(Pointf(x, y)); +} + +Pointf Xform2D::GetScaleXY() const +{ + return Pointf(sqrt(x.x * x.x + y.x * y.x), sqrt(x.y * x.y + y.y * y.y)); +} + +double Xform2D::GetScale() const +{ + Pointf d = GetScaleXY(); + return Length(d) / M_SQRT2; +} + +bool Xform2D::IsRegular() const +{ + Pointf d = GetScaleXY(); + return fabs(d.x - d.y) < 1e-10 * fabs(max(d.x, d.y)); +} + +Xform2D::Xform2D() +{ + x.x = y.y = 1; + x.y = y.x = t.x = t.y = 0; +} + +Xform2D operator*(const Xform2D& a, const Xform2D& b) +{ + Xform2D r; + r.x.x = a.x.x * b.x.x + a.y.x * b.x.y; + r.x.y = a.x.y * b.x.x + a.y.y * b.x.y; + r.y.x = a.x.x * b.y.x + a.y.x * b.y.y; + r.y.y = a.x.y * b.y.x + a.y.y * b.y.y; + r.t.x = a.t.x * b.x.x + a.t.y * b.x.y + b.t.x; + r.t.y = a.t.x * b.y.x + a.t.y * b.y.y + b.t.y; + return r; +} + +Xform2D Xform2D::Translation(double x, double y) +{ + Xform2D m; + m.x.x = m.y.y = 1; + m.t = Pointf(x, y); + return m; +} + +Xform2D Xform2D::Scale(double sx, double sy) +{ + Xform2D m; + m.x.x = sx; + m.y.y = sy; + return m; +} + +Xform2D Xform2D::Scale(double scale) +{ + return Scale(scale, scale); +} + +Xform2D Xform2D::Rotation(double fi) +{ + double cosf = cos(fi); + double sinf = sin(fi); + Xform2D m; + m.x.x = cosf; + m.x.y = -sinf; + m.y.x = sinf; + m.y.y = cosf; + m.t.x = m.t.y = 0; + return m; +} + +Xform2D Xform2D::Sheer(double fi) +{ + Xform2D m; + m.x.x = m.y.y = 1; + m.x.y = atan(fi); + return m; +} + +Xform2D Xform2D::Identity() +{ + Xform2D m; + return m; +} + +double Determinant(const Xform2D& m) +{ + return m.x.x * m.y.y - m.y.x * m.x.y; +} + +Xform2D Inverse(const Xform2D& m) +{ + Xform2D r; + double det = Determinant(m); + r.x = Pointf(m.y.y, -m.x.y) / det; + r.y = Pointf(-m.y.x, m.x.x) / det; + r.t.x = -m.t.x * r.x.x - m.t.y * r.x.y; + r.t.y = -m.t.x * r.y.x - m.t.y * r.y.y; + return r; +} + +END_UPP_NAMESPACE