mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-21 06:45:39 -06:00
Subpixel rendering support in Painter
git-svn-id: svn://ultimatepp.org/upp/trunk@883 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
d712944aaa
commit
7db4871131
13 changed files with 1574 additions and 1496 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<double> *pattern;
|
||||
int patterni;
|
||||
double sum, rem;
|
||||
bool flag;
|
||||
Pointf p0;
|
||||
|
||||
void Put(const Pointf& p);
|
||||
|
||||
public:
|
||||
void Init(const Vector<double>& 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<Cell> {
|
||||
int16 x;
|
||||
int16 cover;
|
||||
int area;
|
||||
|
||||
bool operator<(const Cell& b) const { return x < b.x; }
|
||||
};
|
||||
|
||||
Rectf cliprect;
|
||||
Pointf p0;
|
||||
Buffer< Vector<Cell> > 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<double>& 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<byte> type;
|
||||
Vector<byte> data;
|
||||
};
|
||||
struct Attr : Moveable<Attr> {
|
||||
Xform2D mtx;
|
||||
double tolerance;
|
||||
bool evenodd;
|
||||
byte join;
|
||||
byte cap;
|
||||
double miter_limit;
|
||||
WithDeepCopy< Vector<double> > dash;
|
||||
WithDeepCopy< Vector<double> > stop;
|
||||
WithDeepCopy< Vector<RGBA> > stop_color;
|
||||
double dash_start;
|
||||
double opacity;
|
||||
int cliplevel;
|
||||
bool hasclip;
|
||||
bool mask;
|
||||
bool noaa;
|
||||
};
|
||||
|
||||
ImageBuffer& ib;
|
||||
|
||||
Attr attr;
|
||||
Attr pathattr;
|
||||
Array<Attr> attrstack;
|
||||
Vector< Buffer<ClipLine> > clip;
|
||||
Array< ImageBuffer > mask;
|
||||
|
||||
|
||||
Image gradient;
|
||||
RGBA gradient1, gradient2;
|
||||
int gradientn;
|
||||
|
||||
Path path;
|
||||
Pointf current, ccontrol, qcontrol, move;
|
||||
Rectf pathrect;
|
||||
|
||||
Rasterizer rasterizer;
|
||||
Buffer<RGBA> span;
|
||||
|
||||
void *PathAddRaw(int type, int size);
|
||||
template <class T> 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<ClipLine> 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<double> *pattern;
|
||||
int patterni;
|
||||
double sum, rem;
|
||||
bool flag;
|
||||
Pointf p0;
|
||||
|
||||
void Put(const Pointf& p);
|
||||
|
||||
public:
|
||||
void Init(const Vector<double>& 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<Cell> {
|
||||
int16 x;
|
||||
int16 cover;
|
||||
int area;
|
||||
|
||||
bool operator<(const Cell& b) const { return x < b.x; }
|
||||
};
|
||||
|
||||
Rectf cliprect;
|
||||
Pointf p0;
|
||||
Buffer< Vector<Cell> > 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<double>& 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<byte> type;
|
||||
Vector<byte> data;
|
||||
};
|
||||
struct Attr : Moveable<Attr> {
|
||||
Xform2D mtx;
|
||||
double tolerance;
|
||||
bool evenodd;
|
||||
byte join;
|
||||
byte cap;
|
||||
double miter_limit;
|
||||
WithDeepCopy< Vector<double> > dash;
|
||||
WithDeepCopy< Vector<double> > stop;
|
||||
WithDeepCopy< Vector<RGBA> > stop_color;
|
||||
double dash_start;
|
||||
double opacity;
|
||||
int cliplevel;
|
||||
bool hasclip;
|
||||
bool mask;
|
||||
bool noaa;
|
||||
};
|
||||
|
||||
ImageBuffer& ib;
|
||||
Buffer<int16> subpixel;
|
||||
int render_cx;
|
||||
|
||||
Attr attr;
|
||||
Attr pathattr;
|
||||
Array<Attr> attrstack;
|
||||
Vector< Buffer<ClipLine> > clip;
|
||||
Array< ImageBuffer > mask;
|
||||
|
||||
|
||||
Image gradient;
|
||||
RGBA gradient1, gradient2;
|
||||
int gradientn;
|
||||
|
||||
Path path;
|
||||
Pointf current, ccontrol, qcontrol, move;
|
||||
Rectf pathrect;
|
||||
|
||||
Rasterizer rasterizer;
|
||||
Buffer<RGBA> span;
|
||||
|
||||
void *PathAddRaw(int type, int size);
|
||||
template <class T> 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<ClipLine> 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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<byte> 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; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<LinearData>(MOVE).p = move;
|
||||
}
|
||||
|
||||
void BufferPainter::DoMove0()
|
||||
{
|
||||
if(IsNull(move))
|
||||
MoveOp(Pointf(0, 0), false);
|
||||
}
|
||||
|
||||
void BufferPainter::LineOp(const Pointf& p, bool rel)
|
||||
{
|
||||
DoMove0();
|
||||
PathAdd<LinearData>(LINE).p = ccontrol = qcontrol = EndPoint(p, rel);
|
||||
}
|
||||
|
||||
void BufferPainter::QuadraticOp(const Pointf& p1, const Pointf& p, bool rel)
|
||||
{
|
||||
DoMove0();
|
||||
QuadraticData& m = PathAdd<QuadraticData>(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<CubicData>(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<ArcData>(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<LinearData>(MOVE).p = move;
|
||||
}
|
||||
|
||||
void BufferPainter::DoMove0()
|
||||
{
|
||||
if(IsNull(move))
|
||||
MoveOp(Pointf(0, 0), false);
|
||||
}
|
||||
|
||||
void BufferPainter::LineOp(const Pointf& p, bool rel)
|
||||
{
|
||||
DoMove0();
|
||||
PathAdd<LinearData>(LINE).p = ccontrol = qcontrol = EndPoint(p, rel);
|
||||
}
|
||||
|
||||
void BufferPainter::QuadraticOp(const Pointf& p1, const Pointf& p, bool rel)
|
||||
{
|
||||
DoMove0();
|
||||
QuadraticData& m = PathAdd<QuadraticData>(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<CubicData>(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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<Cell>& 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<Cell>& 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<Cell>& 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<Cell>& 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
|
||||
|
|
|
|||
|
|
@ -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<ClipLine> BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color)
|
||||
{
|
||||
Buffer<ClipLine> 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<ClipLine> 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<ClipLine> BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color)
|
||||
{
|
||||
Buffer<ClipLine> 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<ClipLine> 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue