mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
Painter improvements: Multithreaded rendering improved, new image filter (like Lanczos 3) option, image mapping is now more precise
461 lines
No EOL
7.4 KiB
C++
461 lines
No EOL
7.4 KiB
C++
#include "Painter.h"
|
|
#include "Fillers.h"
|
|
#include "AlphaBlend.h"
|
|
|
|
namespace Upp {
|
|
|
|
void SolidFiller::Start(int minx, int maxx)
|
|
{
|
|
t += minx;
|
|
}
|
|
|
|
inline RGBA InvertRGBA(const RGBA& c)
|
|
{
|
|
RGBA a;
|
|
a.r = ~c.r;
|
|
a.g = ~c.g;
|
|
a.b = ~c.b;
|
|
a.a = 255;
|
|
return a;
|
|
}
|
|
|
|
void SolidFiller::Render(int val)
|
|
{
|
|
AlphaBlend(t, invert ? InvertRGBA(*t) : c, val);
|
|
t++;
|
|
}
|
|
|
|
void SolidFiller::Render(int val, int len)
|
|
{
|
|
if(val == 0) {
|
|
t += len;
|
|
return;
|
|
}
|
|
if(invert) {
|
|
RGBA *e = t + len;
|
|
if(val == 256)
|
|
while(t < e) {
|
|
*t = InvertRGBA(*t);
|
|
t++;
|
|
}
|
|
else
|
|
while(t < e) {
|
|
AlphaBlend(t, InvertRGBA(*t), val);
|
|
t++;
|
|
}
|
|
}
|
|
else {
|
|
if(((val - 256) | (c.a - 255)) == 0)
|
|
Fill(t, c, len);
|
|
else
|
|
AlphaBlend(t, c, val, len);
|
|
t += len;
|
|
}
|
|
}
|
|
|
|
void SubpixelFiller::Start(int minx, int maxx)
|
|
{
|
|
int x = minx / 3;
|
|
if(x > 0) {
|
|
begin = sbuffer;
|
|
x--;
|
|
}
|
|
else
|
|
begin = sbuffer + 3;
|
|
t += x;
|
|
sbuffer[0] = sbuffer[1] = sbuffer[2] = sbuffer[3] =
|
|
sbuffer[4] = sbuffer[5] = sbuffer[6] = sbuffer[7] = 0;
|
|
v = sbuffer + 3 + minx % 3;
|
|
if(ss) {
|
|
int xx = maxx / 3;
|
|
ss->Get(buffer, x, y, xx - x + 2);
|
|
s = buffer;
|
|
}
|
|
}
|
|
|
|
void SubpixelFiller::Render(int val)
|
|
{
|
|
int16 *w = v;
|
|
int h = val / 9;
|
|
int h2 = h + h;
|
|
w[-2] += h;
|
|
w[2] += h;
|
|
w[-1] += h2;
|
|
w[1] += h2;
|
|
w[0] += val - h2 - h2 - h2;
|
|
w[3] = 0;
|
|
v++;
|
|
}
|
|
|
|
void SubpixelFiller::RenderN(int val, int h, int n)
|
|
{
|
|
int16 *w = v;
|
|
int h2 = h + h;
|
|
int hv2 = val - h2 - h2;
|
|
int h3 = h2 + h;
|
|
int hh;
|
|
v += n;
|
|
switch(n) {
|
|
case 1:
|
|
w[-2] += h;
|
|
w[-1] += h2;
|
|
w[1] += h2;
|
|
w[0] += hv2 - h2;
|
|
w[2] += h;
|
|
w[3] = 0;
|
|
break;
|
|
case 2:
|
|
w[-2] += h;
|
|
w[3] = h;
|
|
w[-1] += h3;
|
|
w[2] += h3;
|
|
w[0] += hv2;
|
|
w[1] += hv2;
|
|
w[4] = 0;
|
|
break;
|
|
case 3:
|
|
w[-2] += h;
|
|
w[4] = h;
|
|
w[-1] += h3;
|
|
w[3] = h3;
|
|
hh = hv2 + h;
|
|
w[0] += hh;
|
|
w[2] += hh;
|
|
w[1] += hv2 + h2;
|
|
w[5] = 0;
|
|
break;
|
|
case 4:
|
|
w[-2] += h;
|
|
w[5] = h;
|
|
w[-1] += h3;
|
|
w[4] = h3;
|
|
hh = hv2 + h;
|
|
w[0] += hh;
|
|
w[3] = hh;
|
|
hh = hv2 + h3;
|
|
w[1] += hh;
|
|
w[2] += hh;
|
|
w[6] = 0;
|
|
break;
|
|
case 5:
|
|
w[-2] += h;
|
|
w[6] = h;
|
|
w[-1] += h3;
|
|
w[5] = h3;
|
|
hh = hv2 + h;
|
|
w[0] += hh;
|
|
w[4] = hh;
|
|
hh = h3 + hv2;
|
|
w[1] += hh;
|
|
w[3] = hh;
|
|
w[2] += h3 + hv2 + h;
|
|
w[7] = 0;
|
|
break;
|
|
case 6:
|
|
w[-2] += h;
|
|
w[7] = h;
|
|
w[-1] += h3;
|
|
w[6] = h3;
|
|
hh = hv2 + h;
|
|
w[0] += hh;
|
|
w[5] = hh;
|
|
hh = h3 + hv2;
|
|
w[1] += hh;
|
|
w[4] = hh;
|
|
hh = h3 + hv2 + h;
|
|
w[2] += hh;
|
|
w[3] = hh;
|
|
w[8] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SubpixelFiller::Render(int val, int len)
|
|
{
|
|
int h = val / 9;
|
|
if(len > 6) {
|
|
int q = (3333333 - (v + 2 - begin)) % 3;
|
|
len -= q + 2;
|
|
int l = int(v + 2 + q - begin);
|
|
RenderN(val, h, q + 4);
|
|
Write(l / 3);
|
|
l = len / 3;
|
|
len -= 3 * l;
|
|
RGBA *e = min(t + l, end);
|
|
if(invert && !ss) {
|
|
if(val == 256)
|
|
while(t < e) {
|
|
*t = InvertRGBA(*t);
|
|
t++;
|
|
}
|
|
else
|
|
while(t < e) {
|
|
AlphaBlend(t, InvertRGBA(*t), val);
|
|
t++;
|
|
}
|
|
}
|
|
else {
|
|
if(val == 256)
|
|
if(!ss && color.a == 255) {
|
|
Fill(t, color, int(e - t));
|
|
t = e;
|
|
}
|
|
else
|
|
while(t < e)
|
|
AlphaBlend(t++, ss ? Mul8(*s++, alpha) : color);
|
|
else
|
|
if(ss)
|
|
while(t < e)
|
|
AlphaBlend(t++, Mul8(*s++, alpha), val);
|
|
else {
|
|
RGBA c = Mul8(color, val);
|
|
while(t < e)
|
|
AlphaBlend(t++, c);
|
|
}
|
|
}
|
|
v = begin = sbuffer + 3;
|
|
v[0] = h + h + h;
|
|
v[1] = h;
|
|
v[2] = 0;
|
|
}
|
|
RenderN(val, h, len);
|
|
}
|
|
|
|
void SubpixelFiller::Write(int len)
|
|
{
|
|
RGBA *e = min(t + len, end);
|
|
int16 *q = begin;
|
|
while(t < e) {
|
|
RGBA c = ss ? Mul8(*s++, alpha) : invert ? InvertRGBA(*t) : color;
|
|
int a;
|
|
if(t->a != 255)
|
|
AlphaBlend(t, c, (q[0] + q[1] + q[2]) / 3);
|
|
else
|
|
if(c.a == 255) {
|
|
t->r = (c.r * q[0] >> 8) + ((257 - q[0]) * t->r >> 8);
|
|
t->g = (c.g * q[1] >> 8) + ((257 - q[1]) * t->g >> 8);
|
|
t->b = (c.b * q[2] >> 8) + ((257 - q[2]) * t->b >> 8);
|
|
}
|
|
else {
|
|
a = c.a * q[0] >> 8;
|
|
t->r = (c.r * q[0] >> 8) + ((256 - a - (a >> 7)) * t->r >> 8);
|
|
a = c.a * q[1] >> 8;
|
|
t->g = (c.g * q[1] >> 8) + ((256 - a - (a >> 7)) * t->g >> 8);
|
|
a = c.a * q[2] >> 8;
|
|
t->b = (c.b * q[2] >> 8) + ((256 - a - (a >> 7)) * t->b >> 8);
|
|
}
|
|
t++;
|
|
q += 3;
|
|
}
|
|
}
|
|
|
|
void SubpixelFiller::End()
|
|
{
|
|
v[3] = v[4] = v[5] = 0;
|
|
Write(int(v + 3 - begin) / 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;
|
|
AlphaBlend(t++, *s++, val);
|
|
}
|
|
|
|
void SpanFiller::Render(int val, int len)
|
|
{
|
|
if(val == 0) {
|
|
t += len;
|
|
s += len;
|
|
return;
|
|
}
|
|
if(alpha != 256)
|
|
val = alpha * val >> 8;
|
|
AlphaBlend(t, s, val, len);
|
|
t += len;
|
|
s += len;
|
|
}
|
|
|
|
void ClipFiller::Init(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(ClippingLine& cl)
|
|
{
|
|
if(empty)
|
|
return;
|
|
while(x < cx) {
|
|
int n = min(cx - x, 128);
|
|
*t++ = 0;
|
|
*t++ = n - 1;
|
|
x += n;
|
|
full = false;
|
|
}
|
|
ASSERT(t - ~buffer <= 2 * cx);
|
|
if(full)
|
|
cl.SetFull();
|
|
else
|
|
cl.Set(~buffer, int(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);
|
|
}
|
|
|
|
} |