diff --git a/uppdev/Painter/BufferPainter.h b/uppdev/Painter/BufferPainter.h index cf8faaa03..18511604e 100644 --- a/uppdev/Painter/BufferPainter.h +++ b/uppdev/Painter/BufferPainter.h @@ -154,7 +154,7 @@ private: bool BeginRender(int y, const Cell *&c, const Cell *&e); public: - struct Target { + struct Filler { virtual void Start(int x, int len) = 0; virtual void Render(int val) = 0; virtual void Render(int val, int len) = 0; @@ -166,29 +166,18 @@ public: int MinY() const { return min_y; } int MaxY() const { return max_y; } - void Render(int y, Target& g, bool evenodd); + void Render(int y, Filler& g, bool evenodd); void Reset(); Rasterizer(int cx, int cy); }; -struct ScanLine { - int xmin, xmax; - Buffer data; - int datalen; - - bool IsFull(); - bool IsEmpty(); - - String ToString() const; -}; - struct SpanSource { virtual void Get(RGBA *span, int x, int y, unsigned len) = 0; }; -struct SolidFiller : Rasterizer::Target { +struct SolidFiller : Rasterizer::Filler { RGBA *t; RGBA c; @@ -197,7 +186,7 @@ struct SolidFiller : Rasterizer::Target { void Render(int val, int len); }; -struct SpanFiller : Rasterizer::Target { +struct SpanFiller : Rasterizer::Filler { RGBA *t; const RGBA *s; int y; @@ -210,24 +199,54 @@ struct SpanFiller : Rasterizer::Target { void Render(int val, int len); }; -struct RecFiller : Rasterizer::Target { +class ClipLine { + byte *data; + +public: + 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() { if(!IsFull()) delete[] data; } +}; + +struct ClipFiller : Rasterizer::Filler { Buffer buffer; byte *t; int x; - int maxx; - int maxlen; int cx; + int last; + byte *lastn; bool empty; + bool full; void Span(int c, int len); virtual void Render(int val); virtual void Render(int val, int len); virtual void Start(int x, int len); - void Finish(); - void GetResult(Buffer& tgt, int& maxx, int& maxlen); + + void Clear(); + void Finish(ClipLine& cl); - RecFiller(int cx); + ClipFiller(int cx); +}; + +struct MaskFillerFilter : Rasterizer::Filler { + Rasterizer::Filler *t; + const byte *mask; + int empty; + int full; + + void Start(int minx, int maxx); + void Render(int val, int len); + void Render(int val); + + void Set(Rasterizer::Filler *f, const byte *m) { t = f; mask = m; empty = full = 0; } }; Image MipMap(const Image& img); @@ -345,9 +364,14 @@ public: ImageBuffer& ib; - Attr attr; - Attr pathattr; - Array attrstack; + Attr attr; + Attr pathattr; + Array attrstack; + Vector< Buffer > clip; + + Image gradient; + RGBA gradient1, gradient2; + int gradientn; Path path; Pointf current, ccontrol, qcontrol, move; @@ -359,17 +383,18 @@ public: 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(); - void 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 *t, RGBA color1, RGBA color2, int cx); - Image GetGradient(const RGBA& color1, const RGBA& color2, const Pointf& p1, const Pointf& p2); - void ColorStop0(Attr& a, double pos, const RGBA& color); + 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); public: BufferPainter(ImageBuffer& ib); diff --git a/uppdev/Painter/Context.cpp b/uppdev/Painter/Context.cpp index 3b248f6d9..c79f1f9a1 100644 --- a/uppdev/Painter/Context.cpp +++ b/uppdev/Painter/Context.cpp @@ -16,8 +16,8 @@ void BufferPainter::EndOp() } pathattr = attr = attrstack.Top(); attrstack.Drop(); -#if 0 clip.SetCount(attr.cliplevel); +#if 0 if(attr.mask) FinishMask(); #endif @@ -126,6 +126,8 @@ BufferPainter::BufferPainter(ImageBuffer& ib) attr.mask = false; attr.noaa = false; pathattr = attr; + + gradientn = Null; } END_UPP_NAMESPACE diff --git a/uppdev/Painter/Gradient.cpp b/uppdev/Painter/Gradient.cpp index 5b5d45b8a..e0c922b91 100644 --- a/uppdev/Painter/Gradient.cpp +++ b/uppdev/Painter/Gradient.cpp @@ -2,19 +2,26 @@ NAMESPACE_UPP -void BufferPainter::MakeGradient(RGBA *t, RGBA color1, RGBA color2, int cx) +void BufferPainter::MakeGradient(RGBA color1, RGBA color2, int n) { + if(n == gradientn && color1 == gradient1 && color2 == gradient2) + return; + gradientn = n; + gradient1 = color1; + gradient2 = color2; + ImageBuffer ib(n, 1); + RGBA *t = ib[0]; int l = 0; RGBA cl = color1; for(int i = 0; i <= pathattr.stop.GetCount(); i++) { int h; RGBA ch; if(i < pathattr.stop.GetCount()) { - h = (int)(pathattr.stop[i] * (cx - 1)); + h = (int)(pathattr.stop[i] * (n - 1)); ch = pathattr.stop_color[i]; } else { - h = cx - 1; + h = n - 1; ch = color2; } int w = h - l; @@ -29,19 +36,18 @@ void BufferPainter::MakeGradient(RGBA *t, RGBA color1, RGBA color2, int cx) l = h; } *t = cl; + gradient = ib; } -Image BufferPainter::GetGradient(const RGBA& color1, const RGBA& color2, const Pointf& p1, const Pointf& p2) +void BufferPainter::Gradient(const RGBA& color1, const RGBA& color2, const Pointf& p1, const Pointf& p2) { - int n = minmax(int(Distance(p1, p2) * pathattr.mtx.GetScale()), 2, 4096); - ImageBuffer ib(n, 1); - MakeGradient(ib, color1, color2, n); - return ib; + MakeGradient(color1, color2, minmax(int(Distance(p1, p2) * pathattr.mtx.GetScale()), 2, 4096)); } void BufferPainter::FillOp(const Pointf& p1, const RGBA& color1, const Pointf& p2, const RGBA& color2, int style) { - Fill(GetGradient(color1, color2, p1, p2), p1, p2, + Gradient(color1, color2, p1, p2); + Fill(gradient, p1, p2, FILL_VPAD | FILL_FAST | (style == GRADIENT_PAD ? FILL_HPAD : style == GRADIENT_REPEAT ? FILL_HREPEAT : FILL_HREFLECT)); @@ -49,7 +55,8 @@ void BufferPainter::FillOp(const Pointf& p1, const RGBA& color1, const Pointf& p void BufferPainter::StrokeOp(double width, const Pointf& p1, const RGBA& color1, const Pointf& p2, const RGBA& color2, int style) { - Stroke(width, GetGradient(color1, color2, p1, p2), p1, p2, + Gradient(color1, color2, p1, p2); + Stroke(width, gradient, p1, p2, FILL_VPAD | FILL_FAST | (style == GRADIENT_PAD ? FILL_HPAD : style == GRADIENT_REPEAT ? FILL_HREPEAT : FILL_HREFLECT)); diff --git a/uppdev/Painter/Mask.cpp b/uppdev/Painter/Mask.cpp index 1d723834d..127bc285c 100644 --- a/uppdev/Painter/Mask.cpp +++ b/uppdev/Painter/Mask.cpp @@ -2,115 +2,96 @@ NAMESPACE_UPP -RecFiller::RecFiller(int _cx) +ClipFiller::ClipFiller(int _cx) { cx = _cx; buffer.Alloc(2 * cx + 2); } -void RecFiller::Start(int xmin, int xmax) +void ClipFiller::Clear() { t = ~buffer; x = 0; - maxlen = 0; - maxx = 0; + empty = true; + full = true; + last = -1; +} + +void ClipFiller::Start(int xmin, int xmax) +{ Render(0, xmin); } -void RecFiller::Render(int val) +void ClipFiller::Span(int val, int len) { - Render(val, 1); //!Optimize later! -/* - if(val == 0) { - *t++ = 0; - *t++ = 0; + int v = val >> 1; + if(last == val) { + int n = min(v + 128 - *lastn - 1, len); + *lastn += n; + len -= n; } - else - if(val == 256) { - *t++ = 0; - *t++ = 128; - } - else - *t++ = val; - x++; -*/ -} - -void RecFiller::Span(int c, int len) -{ + last = -1; while(len > 128) { int n = min(len, 128); *t++ = 0; - *t++ = c + n - 1; + *t++ = v + n - 1; len -= n; } if(len) { *t++ = 0; - *t++ = c + len - 1; + last = val; + lastn = t; + *t++ = v + len - 1; } } -void RecFiller::Render(int val, int len) +void ClipFiller::Render(int val, int len) { - DLOG(val << " " << len); if(val == 256) { - Span(128, len); - if(len > maxlen) { - maxx = x; - maxlen = len; - } + Span(256, len); + empty = false; } - else - if(val == 0) - Span(0, len); else { - memset(t, val, len); - t += len; + full = false; + if(val == 0) + Span(0, len); + else { + memset(t, val, len); + t += len; + empty = false; + last = -1; + } } x += len; } -void RecFiller::Finish() +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 RecFiller::GetResult(Buffer& tgt, int& _maxx, int& _maxlen) +void MaskFillerFilter::Render(int val) { - tgt.Clear(); - int l = t - ~buffer; - tgt.Alloc(l); - memcpy(~tgt, ~buffer, l); - _maxx = maxx; - _maxlen = maxlen; - LOGHEXDUMP(~tgt, l); - DDUMP(maxx); - DDUMP(maxlen); -} - -struct RasterizerMaskFilter : Rasterizer::Target { - Rasterizer::Target *t; - byte *mask; - int empty; - int full; - - void Start(int minx, int maxx); - void Render(int val, int len); - void Render(int val); - RasterizerMaskFilter() { empty = full = 0; } -}; - -void RasterizerMaskFilter::Render(int val) -{ - Render(val, 1); -/* for(;;) { + for(;;) { if(empty) { - f.t++; + t->Render(0); empty--; return; } @@ -124,17 +105,15 @@ void RasterizerMaskFilter::Render(int val) t->Render(val * m >> 8); return; } - else { - if(m < 128) - empty = m + 1; - else - full = m - 128 + 1; - } + m = *mask++; + if(m < 128) + empty = m + 1; + else + full = m - 128 + 1; } -*/ } -void RasterizerMaskFilter::Render(int val, int len) +void MaskFillerFilter::Render(int val, int len) { while(len) if(empty) { @@ -146,21 +125,19 @@ void RasterizerMaskFilter::Render(int val, int len) else if(full) { int n = min(len, full); - t->Render(256, n); + t->Render(val, n); full -= n; len -= n; - empty = false; } else { byte m = *mask++; if(m) { int r = val * m >> 8; - if(r) - empty = false; t->Render(r); len--; } else { + m = *mask++; if(m < 128) empty = m + 1; else @@ -169,17 +146,17 @@ void RasterizerMaskFilter::Render(int val, int len) } } -struct RasterizerNil : Rasterizer::Target { +struct NilFiller : Rasterizer::Filler { void Start(int minx, int maxx) {} void Render(int val, int len) {} void Render(int val) {} }; -void RasterizerMaskFilter::Start(int minx, int maxx) +void MaskFillerFilter::Start(int minx, int maxx) { t->Start(minx, maxx); - Rasterizer::Target *h = t; - RasterizerNil nil; + Rasterizer::Filler *h = t; + NilFiller nil; t = &nil; Render(0, minx); t = h; diff --git a/uppdev/Painter/RadialGradient.cpp b/uppdev/Painter/RadialGradient.cpp index dbd3f09e1..37809defa 100644 --- a/uppdev/Painter/RadialGradient.cpp +++ b/uppdev/Painter/RadialGradient.cpp @@ -127,7 +127,7 @@ struct PainterRadialSpan : SpanSource { int cx, cy, r, fx, fy; int style; int alpha; - RGBA gradient[2048]; + const RGBA *gradient; bool focus; double C; @@ -201,7 +201,8 @@ void BufferPainter::RenderRadial(double width, const Pointf& f, const RGBA& colo sg.interpolator.Set(Inverse(pathattr.mtx)); sg.style = style; sg.Set((int)c.x, (int)c.y, (int)r, (int)f.x, (int)f.y); - MakeGradient(sg.gradient, color1, color2, 2048); + MakeGradient(color1, color2, 2048); + sg.gradient = gradient[0]; RenderPath(width, &sg, RGBAZero()); } diff --git a/uppdev/Painter/Rasterizer.cpp b/uppdev/Painter/Rasterizer.cpp index 00d3fefe4..1b7634932 100644 --- a/uppdev/Painter/Rasterizer.cpp +++ b/uppdev/Painter/Rasterizer.cpp @@ -282,7 +282,7 @@ void Rasterizer::LineRaw(int x1, int y1, int x2, int y2) RenderHLine(ey1, x_from, 256 - first, x2, fy2); } -void Rasterizer::Render(int y, Target& g, bool evenodd) +void Rasterizer::Render(int y, Rasterizer::Filler& g, bool evenodd) { PAINTER_TIMING("Render"); const Cell *c, *e; diff --git a/uppdev/Painter/Render.cpp b/uppdev/Painter/Render.cpp index e9721158d..d3e8d9f93 100644 --- a/uppdev/Painter/Render.cpp +++ b/uppdev/Painter/Render.cpp @@ -7,10 +7,11 @@ void BufferPainter::ClearOp(const RGBA& color) UPP::Fill(~ib, color, ib.GetLength()); } -void BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) +Buffer BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) { + Buffer newclip; if(width == 0) - return; + return newclip; Transformer trans(pathattr.mtx); trans.target = &rasterizer; LinearPathConsumer *g; @@ -37,13 +38,14 @@ void BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) int opacity = int(256 * pathattr.opacity); Pointf pos = Pointf(0, 0); int i = 0; - Rasterizer::Target *rg; + Rasterizer::Filler *rg; SpanFiller span_filler; SolidFiller solid_filler; - RecFiller record_filler(ib.GetWidth()); - bool clip = width == -2; - if(clip) { - rg = &record_filler; + ClipFiller clip_filler(ib.GetWidth()); + bool doclip = width == -2; + if(doclip) { + rg = &clip_filler; + newclip.Alloc(ib.GetHeight()); } else if(ss) { @@ -58,19 +60,29 @@ void BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) solid_filler.c = Mul8(color, opacity); rg = &solid_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.Render(y, *rg, evenodd); - if(clip) { - record_filler.Finish(); - Buffer h; - int maxx, maxlen; - record_filler.GetResult(h, maxx, maxlen); + 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()) @@ -113,11 +125,12 @@ void BufferPainter::RenderPath(double width, SpanSource *ss, const RGBA& color) } default: NEVER(); - return; + return newclip; } i++; } current = Null; + return newclip; } void BufferPainter::FillOp(const RGBA& color) @@ -132,7 +145,14 @@ void BufferPainter::StrokeOp(double width, const RGBA& color) void BufferPainter::ClipOp() { - RenderPath(-2, NULL, RGBAZero()); + 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/uppdev/PainterExamples/Clipping.cpp b/uppdev/PainterExamples/Clipping.cpp index 44e1b9580..70c35cf13 100644 --- a/uppdev/PainterExamples/Clipping.cpp +++ b/uppdev/PainterExamples/Clipping.cpp @@ -2,8 +2,6 @@ void ClippingExample(Painter& sw) { - sw.Rectangle(0, 0, 100000, 100000).Clip(); - Font fnt = Roman(150).Bold(); String txt = "CLIPPED!"; Size tsz = GetTextSize(txt, fnt); @@ -12,8 +10,32 @@ void ClippingExample(Painter& sw) sw.Text(0, 0, txt, fnt).Fill(Green()); sw.End(); sw.Text(0, 0, txt, fnt).Stroke(0.5, Black()); + +} + +void NoClippingExample(Painter& sw) +{ + Font fnt = Roman(150).Bold(); + String txt = "CLIPPED!"; + Size tsz = GetTextSize(txt, fnt); + sw.Text(0, 0, txt, fnt).Fill(Green()); + sw.Text(0, 0, txt, fnt).Stroke(0.5, Black()); +} + +void ClippingExample2(Painter& sw) +{ + sw.Begin(); + sw.Ellipse(200, 200, 200, 200).Stroke(2, Red()).Clip(); + sw.Begin(); + sw.Ellipse(200, 200, 100, 300).Clip().Stroke(2, Red()); + sw.Text(0, 0, "CL", Arial(360).Bold()).Fill(Blue()); + sw.End(); + sw.Text(0, 0, "CL", Arial(360).Bold()).Opacity(0.4).Fill(Black()); + sw.End(); } INITBLOCK { RegisterExample("Clipping", ClippingExample); + RegisterExample("Clipping removed (to benchmark) ", NoClippingExample); + RegisterExample("Clipping 2", ClippingExample2); } diff --git a/uppdev/PainterExamples/TextFillGradient.cpp b/uppdev/PainterExamples/TextFillGradient.cpp index 0f948f878..04fe29b7c 100644 --- a/uppdev/PainterExamples/TextFillGradient.cpp +++ b/uppdev/PainterExamples/TextFillGradient.cpp @@ -6,18 +6,8 @@ void TextFillGradient(Painter& sw) Font fnt = Arial(100).Bold().Italic(); Size tsz = GetTextSize(txt, fnt); sw.Text(100, 100, txt, fnt) - .Fill(100, 100, Blue(), 100 + tsz.cx, 100/* + tsz.cy*/, LtRed()); + .Fill(100, 100, Blue(), 100 + tsz.cx, 100 + tsz.cy, LtRed()); sw.Translate(0, 200); - sw.Rectangle(100, 100, tsz.cx, tsz.cy) - .Stroke(1, Blue()) - .Fill(TestImg::test(), 100, 100, 100 + tsz.cx, 100 + tsz.cy); - sw.Move(100, 300).RelLine(tsz.cx + 50, 0).RelLine(-30, tsz.cy).RelLine(-tsz.cx / 2, 0) - .Fill(100, 300, Blue(), 100 + tsz.cx, 300/* + tsz.cy*/, LtRed()) -// .Fill(TestImg::test(), 100, 300, 100 + tsz.cx, 300 + tsz.cy) - .Stroke(1, Blue()); -/* sw.Text(100, 100, txt, fnt) - .Fill(TestImg::test(), 100, 100, 100 + tsz.cx, 100 + tsz.cy) - .Stroke(1, Black());*/ } INITBLOCK {