Files
tlib/oversampling/WDL/lice/lice_line.cpp
2024-05-24 13:28:31 +02:00

2036 lines
54 KiB
C++

#ifndef WDL_NO_DEFINE_MINMAX
#define WDL_NO_DEFINE_MINMAX
#endif
#include "lice.h"
#include "lice_combine.h"
#include "lice_extended.h"
#include <math.h>
#include <stdio.h>
//#include <assert.h>
#define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING)
template <class T> inline void SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; }
enum { eOK = 0, eXLo = 1, eXHi = 2, eYLo = 4, eYHi = 8 };
static int OffscreenTest(int x, int y, int nX, int nY)
{
int e = eOK;
if (x < 0) e |= eXLo;
else if (x >= nX) e |= eXHi;
if (y < 0) e |= eYLo;
else if (y >= nY) e |= eYHi;
return e;
}
// Cohen-Sutherland. Returns false if the line is entirely offscreen.
static bool ClipLine(int* pX1, int* pY1, int* pX2, int* pY2, int nX, int nY)
{
int x1 = *pX1, y1 = *pY1, x2 = *pX2, y2 = *pY2;
int e1 = OffscreenTest(x1, y1, nX, nY);
int e2 = OffscreenTest(x2, y2, nX, nY);
int timeout = 32;
bool accept = false, done = false;
do
{
if (!(e1 | e2)) {
accept = done = true;
}
else
if (e1 & e2) {
done = true; // Line is entirely offscreen.
}
else {
int x, y;
int eOut = e1 ? e1 : e2;
if (eOut & eYHi) {
x = x1 + (int) ((double) (x2 - x1) * (double) (nY - 1 - y1) / (double) (y2 - y1));
y = nY - 1;
}
else
if (eOut & eYLo) {
x = x1 + (int) ((double) (x2 - x1) * (double) -y1 / (double) (y2 - y1));
y = 0;
}
else
if (eOut & eXHi) {
y = y1 + (int) ((double) (y2 - y1) * (double) (nX - 1 - x1) / (double) (x2 - x1));
x = nX - 1;
}
else {
y = y1 + (int) ((double) (y2 - y1) * (double) -x1 / (double) (x2 - x1));
x = 0;
}
if (eOut == e1) {
x1 = x;
y1 = y;
e1 = OffscreenTest(x1, y1, nX, nY);
}
else {
x2 = x;
y2 = y;
e2 = OffscreenTest(x2, y2, nX, nY);
}
}
}
while (!done && timeout--);
*pX1 = x1;
*pY1 = y1;
*pX2 = x2;
*pY2 = y2;
return accept;
}
template<class T> static int OffscreenFTest(T x, T y, T w, T h)
{
int e = eOK;
if (x < 0.0f) e |= eXLo;
else if (x >= w) e |= eXHi;
if (y < 0.0f) e |= eYLo;
else if (y >= h) e |= eYHi;
return e;
}
template<class T> static bool ClipFLine(T * x1, T * y1, T * x2, T *y2, int w, int h)
{
T tx1 = *x1, ty1 = *y1, tx2 = *x2, ty2 = *y2;
T tw = (T)(w-1), th = (T)(h-1);
if (!lice_isfinite(tx1) || !lice_isfinite(tx2) ||
!lice_isfinite(ty1) || !lice_isfinite(ty2)) return false;
int e1 = OffscreenFTest(tx1, ty1, tw, th);
int e2 = OffscreenFTest(tx2, ty2, tw, th);
int timeout = 32;
bool accept = false, done = false;
do
{
if (!(e1|e2))
{
accept = done = true;
}
else
if (e1&e2)
{
done = true; // Line is entirely offscreen.
}
else
{
T x, y;
int eOut = (e1 ? e1 : e2);
if (eOut&eYHi)
{
x = tx1+(tx2-tx1)*(th-ty1)/(ty2-ty1);
y = th-1.0f;
}
else if (eOut&eYLo)
{
x = tx1+(tx2-tx1)*ty1/(ty1-ty2);
y = 0.0f;
}
else if (eOut&eXHi)
{
y = ty1+(ty2-ty1)*(tw-tx1)/(tx2-tx1);
x = tw-1.0f;
}
else
{
y = ty1+(ty2-ty1)*tx1/(tx1-tx2);
x = 0.0f;
}
if (eOut == e1)
{
tx1 = x;
ty1 = y;
e1 = OffscreenFTest(tx1, ty1, tw, th);
}
else
{
tx2 = x;
ty2 = y;
e2 = OffscreenFTest(tx2, ty2, tw, th);
}
}
}
while (!done && timeout--);
*x1 = tx1;
*y1 = ty1;
*x2 = tx2;
*y2 = ty2;
return accept;
}
inline static void LICE_DiagLineFAST(LICE_pixel *px, int span, int n, int xstep, int ystep, LICE_pixel color, bool aa)
{
int step = xstep+ystep;
if (aa)
{
LICE_pixel color75 = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f);
LICE_pixel color25 = (color>>2)&0x3f3f3f3f;
while (n--)
{
_LICE_CombinePixelsThreeQuarterMix2FAST::doPixFAST(px, color75);
_LICE_CombinePixelsQuarterMix2FAST::doPixFAST(px+xstep, color25);
_LICE_CombinePixelsQuarterMix2FAST::doPixFAST(px+ystep, color25);
px += step;
}
_LICE_CombinePixelsThreeQuarterMix2FAST::doPixFAST(px, color75);
}
else
{
++n;
while (n--)
{
*px = color;
px += step;
}
}
}
inline static void LICE_DottedVertLineFAST(LICE_IBitmap* dest, int x, int y1, int y2, LICE_pixel color)
{
int span = dest->getRowSpan();
LICE_pixel* px = dest->getBits()+y1*span+x;
int n = (y2-y1+1)/2;
while (n--)
{
*px = color;
px += 2*span;
}
}
// this is the white-color table, doing this properly requires correcting the destination color specifically
#define DO_AA_GAMMA_CORRECT 0
#if DO_AA_GAMMA_CORRECT
static unsigned char AA_GAMMA_CORRECT[256] =
{
// 1.8 gamma
0,11,17,21,25,28,31,34,37,39,42,44,46,48,50,52,54,56,58,60,61,63,65,67,68,70,71,73,74,76,77,79,80,81,83,84,85,87,88,89,91,92,93,94,96,97,98,99,100,101,103,104,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,132,133,134,135,136,137,138,139,140,141,142,142,143,144,145,146,147,148,149,149,150,151,152,153,154,154,155,156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,179,179,180,181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,194,194,195,196,196,197,198,198,199,200,200,201,202,202,203,204,204,205,206,206,207,208,208,209,210,210,211,212,212,213,214,214,215,215,216,217,217,218,219,219,220,220,221,222,222,223,224,224,225,225,226,227,227,228,228,229,230,230,231,231,232,233,233,234,234,235,236,236,237,237,238,239,239,240,240,241,241,242,243,243,244,244,245,245,246,247,247,248,248,249,249,250,251,251,252,252,253,253,254,255
// 2.0 gamma
//0,15,22,27,31,35,39,42,45,47,50,52,55,57,59,61,63,65,67,69,71,73,74,76,78,79,81,82,84,85,87,88,90,91,93,94,95,97,98,99,100,102,103,104,105,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,141,142,143,144,145,146,147,148,148,149,150,151,152,153,153,154,155,156,157,158,158,159,160,161,162,162,163,164,165,165,166,167,168,168,169,170,171,171,172,173,174,174,175,176,177,177,178,179,179,180,181,182,182,183,184,184,185,186,186,187,188,188,189,190,190,191,192,192,193,194,194,195,196,196,197,198,198,199,200,200,201,201,202,203,203,204,205,205,206,206,207,208,208,209,210,210,211,211,212,213,213,214,214,215,216,216,217,217,218,218,219,220,220,221,221,222,222,223,224,224,225,225,226,226,227,228,228,229,229,230,230,231,231,232,233,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,255
// 2.2 gamma
//0,20,28,33,38,42,46,49,52,55,58,61,63,65,68,70,72,74,76,78,80,81,83,85,87,88,90,91,93,94,96,97,99,100,102,103,104,106,107,108,109,111,112,113,114,115,117,118,119,120,121,122,123,124,125,126,128,129,130,131,132,133,134,135,136,136,137,138,139,140,141,142,143,144,145,146,147,147,148,149,150,151,152,153,153,154,155,156,157,158,158,159,160,161,162,162,163,164,165,165,166,167,168,168,169,170,171,171,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,190,191,192,192,193,194,194,195,196,196,197,197,198,199,199,200,200,201,202,202,203,203,204,205,205,206,206,207,208,208,209,209,210,210,211,212,212,213,213,214,214,215,216,216,217,217,218,218,219,219,220,220,221,222,222,223,223,224,224,225,225,226,226,227,227,228,228,229,229,230,230,231,231,232,232,233,233,234,234,235,235,236,236,237,237,238,238,239,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,249,250,250,251,251,252,252,253,253,254,254,255
// 2.6 gamma
//0,30,39,46,51,56,60,63,67,70,73,76,78,81,83,85,87,89,91,93,95,97,99,101,102,104,105,107,109,110,111,113,114,116,117,118,120,121,122,123,125,126,127,128,129,130,131,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,164,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,175,176,177,177,178,179,179,180,181,181,182,183,183,184,185,185,186,187,187,188,188,189,190,190,191,192,192,193,193,194,195,195,196,196,197,197,198,199,199,200,200,201,201,202,203,203,204,204,205,205,206,206,207,207,208,208,209,210,210,211,211,212,212,213,213,214,214,215,215,216,216,217,217,218,218,219,219,220,220,221,221,222,222,223,223,223,224,224,225,225,226,226,227,227,228,228,229,229,230,230,230,231,231,232,232,233,233,234,234,234,235,235,236,236,237,237,237,238,238,239,239,240,240,240,241,241,242,242,243,243,243,244,244,245,245,245,246,246,247,247,247,248,248,249,249,249,250,250,251,251,251,252,252,253,253,253,254,254,255
};
#endif
static void GetAAPxWeight(int err, int alpha, int* wt, int* iwt)
{
int i = err>>8;
int w = 255-i;
#if DO_AA_GAMMA_CORRECT
w = AA_GAMMA_CORRECT[w];
i = AA_GAMMA_CORRECT[i];
#endif
w = (alpha*w) >> 8;
i = (alpha*i) >> 8;
*wt = w;
*iwt = i;
}
static void GetAAPxWeightFAST(int err, int* wt, int* iwt)
{
int i = err>>8;
int w = 255-i;
#if DO_AA_GAMMA_CORRECT
w = AA_GAMMA_CORRECT[w];
i = AA_GAMMA_CORRECT[i];
#endif
*wt = w;
*iwt = i;
}
template <class COMBFUNC> class __LICE_LineClassSimple
{
public:
static void LICE_VertLineFAST(LICE_pixel *px, int span, int len, LICE_pixel color)
{
while (len--)
{
COMBFUNC::doPixFAST(px, color);
px+=span;
}
}
static void LICE_HorizLineFAST(LICE_pixel *px, int n, LICE_pixel color)
{
while (n--)
{
COMBFUNC::doPixFAST(px, color);
px++;
}
}
static void LICE_VertLine(LICE_pixel *px, int span, int len, int color, int aw)
{
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
while (len--)
{
COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, aw);
px+=span;
}
}
static void LICE_HorizLine(LICE_pixel *px, int n, LICE_pixel color, int aw)
{
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
while (n--)
{
COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, aw);
px++;
}
}
static void LICE_DiagLine(LICE_pixel *px, int span, int n, int xstep, int ystep, LICE_pixel color, int aw)
{
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
int step = xstep+ystep;
for (int i = 0; i <= n; ++i, px += step)
{
COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, aw);
}
}
static void LICE_DiagLineAA(LICE_pixel *px, int span, int n, int xstep, int ystep, LICE_pixel color, int aw)
{
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
int step = xstep+ystep;
#if DO_AA_GAMMA_CORRECT
int iw = aw*AA_GAMMA_CORRECT[256*3/4]/256;
int dw = aw*AA_GAMMA_CORRECT[256/4]/256;
#else
int iw = aw*3/4;
int dw = aw/4;
#endif
for (int i = 0; i < n; ++i, px += step)
{
COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, iw);
COMBFUNC::doPix((LICE_pixel_chan*) (px+xstep), r, g, b, a, dw);
COMBFUNC::doPix((LICE_pixel_chan*) (px+ystep), r, g, b, a, dw);
}
COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, iw);
}
};
#ifndef LICE_FAVOR_SIZE
template<class COMBFUNC>
#endif
class __LICE_LineClass
{
public:
#ifdef LICE_FAVOR_SIZE
#define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia);
#else
#define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia);
#endif
static void DashedLine(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int pxon, int pxoff, LICE_pixel color, int aw
#ifdef LICE_FAVOR_SIZE
, LICE_COMBINEFUNC combFunc
#endif
)
{
int span = dest->getRowSpan();
LICE_pixel* px = dest->getBits()+y1*span+x1;
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
if (x1 == x2)
{
int i, y;
for (y = y1; y < y2-pxon; y += pxon+pxoff)
{
for (i = 0; i < pxon; ++i, px += span) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw)
px += pxoff*span;
}
for (i = 0; i < lice_min(pxon, y2-y); ++i, px += span) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw)
}
else if (y1 == y2)
{
int i, x;
for (x = x1; x < x2-pxon; x += pxon+pxoff)
{
for (i = 0; i < pxon; ++i, ++px) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw)
px += pxoff;
}
for (i = 0; i < lice_min(pxon, x2-x); ++i, ++px) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw)
}
}
static void LICE_LineImpl(LICE_pixel *px, LICE_pixel *px2, int derr, int astep, int da, int bstep, LICE_pixel color, int aw, bool aa
#ifdef LICE_FAVOR_SIZE
, LICE_COMBINEFUNC combFunc
#endif
)
{
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
int err = 0;
int i;
int n = (da+1)/2;
if (aa)
{
DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw)
DOPIX((LICE_pixel_chan*) px2, r, g, b, a, aw)
px += astep;
px2 -= astep;
err = derr;
if (aw == 256)
{
for (i = 1; i < n; ++i)
{
int wt, iwt;
GetAAPxWeightFAST(err, &wt, &iwt);
DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt)
DOPIX((LICE_pixel_chan*)px2, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px2-bstep), r, g, b, a, iwt)
err += derr;
if (err >= 65536)
{
px += bstep;
px2 -= bstep;
err -= 65536;
}
px += astep;
px2 -= astep;
}
}
else // alpha<256
{
for (i = 1; i < n; ++i)
{
int wt, iwt;
GetAAPxWeight(err, aw, &wt, &iwt);
DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt)
DOPIX((LICE_pixel_chan*)px2, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px2-bstep), r, g, b, a, iwt)
err += derr;
if (err >= 65536)
{
px += bstep;
px2 -= bstep;
err -= 65536;
}
px += astep;
px2 -= astep;
}
}
if (!(da%2))
{
int wt, iwt;
if (aw == 256) GetAAPxWeightFAST(err, &wt, &iwt);
else GetAAPxWeight(err, aw, &wt, &iwt);
DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt)
}
}
else // not aa
{
for (i = 0; i < n; ++i)
{
DOPIX((LICE_pixel_chan*)px, r, g, b, a, aw)
DOPIX((LICE_pixel_chan*)px2, r, g, b, a, aw)
err += derr;
if (err >= 65536/2)
{
px += bstep;
px2 -= bstep;
err -= 65536;
}
px += astep;
px2 -= astep;
}
if (!(da%2))
{
DOPIX((LICE_pixel_chan*)px, r, g, b, a, aw)
}
}
}
static void LICE_FLineImpl(LICE_pixel *px, int n , int err, int derr, int astep, int bstep, LICE_pixel color, int aw
#ifdef LICE_FAVOR_SIZE
, LICE_COMBINEFUNC combFunc
#endif
) // only does AA
{
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
int wt, iwt;
int i;
if (aw == 256)
{
for (i = 0; i <= n; ++i)
{
GetAAPxWeightFAST(err, &wt, &iwt);
DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt)
err += derr;
if (err >= 65536)
{
px += bstep;
err -= 65536;
}
px += astep;
}
}
else // alpha != 256
{
for (i = 0; i <= n; ++i)
{
GetAAPxWeight(err, aw, &wt, &iwt);
DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt)
DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt)
err += derr;
if (err >= 65536)
{
px += bstep;
err -= 65536;
}
px += astep;
}
}
}
static void LICE_FLineImplFill(LICE_pixel *px, int n , int err, int derr, int astep, int bstep, LICE_pixel color, int aw,
int fill_sz, int b_pos, unsigned int b_max
#ifdef LICE_FAVOR_SIZE
, LICE_COMBINEFUNC combFunc
#endif
)
{
// fill_sz always >= 2
int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);
int wt, iwt;
int i;
const int dbpos = bstep < 0 ? -1 : 1;
const int b_adj = -(fill_sz/2);
b_pos += b_adj*dbpos;
px += b_adj*bstep;
fill_sz--; // fill size of 2 has one extra pixel in the middle, 2 AA pixels
if (aw == 256)
{
for (i = 0; i <= n; ++i)
{
GetAAPxWeightFAST(err, &wt, &iwt);
LICE_pixel *wr = px;
unsigned int bp = b_pos;
if (bp<b_max) { DOPIX((LICE_pixel_chan*)wr, r, g, b, a, wt) }
for (int j=0;j<fill_sz;j++)
{
wr += bstep;
if ((bp+=dbpos)<b_max) { DOPIX((LICE_pixel_chan*)wr, r, g, b, a, 256) }
}
if ((bp+dbpos)<b_max) { DOPIX((LICE_pixel_chan*)(wr+bstep), r, g, b, a, iwt) }
err += derr;
if (err >= 65536)
{
px += bstep;
b_pos += dbpos;
err -= 65536;
}
px += astep;
}
}
else // alpha != 256
{
for (i = 0; i <= n; ++i)
{
GetAAPxWeight(err, aw, &wt, &iwt);
LICE_pixel *wr = px;
unsigned int bp = b_pos;
if (bp<b_max) { DOPIX((LICE_pixel_chan*)wr, r, g, b, a, wt) }
for (int j=0;j<fill_sz;j++)
{
wr += bstep;
if ((bp+=dbpos)<b_max) { DOPIX((LICE_pixel_chan*)wr, r, g, b, a, aw) }
}
if ((bp+dbpos)<b_max) { DOPIX((LICE_pixel_chan*)(wr+bstep), r, g, b, a, iwt) }
err += derr;
if (err >= 65536)
{
px += bstep;
b_pos += dbpos;
err -= 65536;
}
px += astep;
}
}
}
#undef DOPIX
};
void LICE_Line(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, LICE_pixel color, float alpha, int mode, bool aa)
{
if (!dest) return;
int w = dest->getWidth();
int h = dest->getHeight();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc>0)
{
__LICE_SCU(w);
__LICE_SCU(h);
if (!IGNORE_SCALING(mode))
{
__LICE_SC(x1);
__LICE_SC(y1);
__LICE_SC(x2);
__LICE_SC(y2);
}
}
#ifndef DISABLE_LICE_EXTENSIONS
if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_LINE_ACCEL))
{
LICE_Ext_Line_acceldata data(x1, y1, x2, y2, color, alpha, mode, aa);
if (dest->Extended(LICE_EXT_LINE_ACCEL, &data)) return;
}
#endif
if (dest->isFlipped())
{
y1 = h-1-y1;
y2 = h-1-y2;
}
if (ClipLine(&x1, &y1, &x2, &y2, w, h))
{
int xdiff = x2-x1;
if (y1 == y2) // horizontal line optimizations
{
if (x1 > x2) SWAP(x1, x2);
int span = dest->getRowSpan();
LICE_pixel* px = dest->getBits()+y1*span+x1;
int n=x2-x1+1;
if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f)
{
__LICE_LineClassSimple<_LICE_CombinePixelsClobberFAST>::LICE_HorizLineFAST(px, n, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.5f)
{
color = (color>>1)&0x7f7f7f7f;
__LICE_LineClassSimple<_LICE_CombinePixelsHalfMix2FAST>::LICE_HorizLineFAST(px, n, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.25f)
{
color = (color>>2)&0x3f3f3f3f;
__LICE_LineClassSimple<_LICE_CombinePixelsQuarterMix2FAST>::LICE_HorizLineFAST(px, n, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.75f)
{
color = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f);
__LICE_LineClassSimple<_LICE_CombinePixelsThreeQuarterMix2FAST>::LICE_HorizLineFAST(px, n, color);
}
else
{
int aw = (int)(256.0f*alpha);
#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple<COMBFUNC>::LICE_HorizLine(px, n, color, aw)
__LICE_ACTION_CONSTANTALPHA(mode, aw, false);
#undef __LICE__ACTION
}
}
else if (!xdiff) // vertical line optimizations
{
if (y1 > y2) SWAP(y1, y2);
int len=y2+1-y1;
int span = dest->getRowSpan();
LICE_pixel* px = dest->getBits()+y1*span+x1;
int aw = (int)(256.0f*alpha);
if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f)
{
__LICE_LineClassSimple<_LICE_CombinePixelsClobberFAST>::LICE_VertLineFAST(px, span, len, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.5f)
{
color = (color>>1)&0x7f7f7f7f;
__LICE_LineClassSimple<_LICE_CombinePixelsHalfMix2FAST>::LICE_VertLineFAST(px, span, len, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.25f)
{
color = (color>>2)&0x3f3f3f3f;
__LICE_LineClassSimple<_LICE_CombinePixelsQuarterMix2FAST>::LICE_VertLineFAST(px, span, len, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.75f)
{
color = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f);
__LICE_LineClassSimple<_LICE_CombinePixelsThreeQuarterMix2FAST>::LICE_VertLineFAST(px, span, len, color);
}
else
{
#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple<COMBFUNC>::LICE_VertLine(px, span, len, color,aw)
__LICE_ACTION_CONSTANTALPHA(mode, aw, false);
#undef __LICE__ACTION
}
}
else if ((xdiff=abs(xdiff)) == abs(y2-y1)) // diagonal line optimizations
{
int span = dest->getRowSpan();
LICE_pixel* px = dest->getBits()+y1*span+x1;
int aw = (int)(256.0f*alpha);
int xstep = (x2 > x1 ? 1 : -1);
int ystep = (y2 > y1 ? span : -span);
if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f)
{
LICE_DiagLineFAST(px,span, xdiff, xstep, ystep, color, aa);
}
else
{
if (aa)
{
#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple<COMBFUNC>::LICE_DiagLineAA(px,span, xdiff, xstep, ystep, color, aw)
__LICE_ACTION_NOSRCALPHA(mode, aw, false);
#undef __LICE__ACTION
}
else
{
#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple<COMBFUNC>::LICE_DiagLine(px,span, xdiff, xstep, ystep, color, aw)
__LICE_ACTION_CONSTANTALPHA(mode, aw, false);
#undef __LICE__ACTION
}
}
}
else
{
// common set-up for normal line draws
int span = dest->getRowSpan();
int aw = (int)(256.0f*alpha);
LICE_pixel* px = dest->getBits()+y1*span+x1;
LICE_pixel* px2 = dest->getBits()+y2*span+x2;
int da, db;
int astep, bstep;
int dx = x2-x1;
int dy = y2-y1;
if (abs(dx) > abs(dy))
{
da = dx;
db = dy;
astep = 1;
bstep = span;
}
else
{
da = dy;
db = dx;
astep = span;
bstep = 1;
}
if (da < 0)
{
da = -da;
db = -db;
SWAP(px, px2);
}
if (db < 0)
{
db = -db;
bstep = -bstep;
}
double dbda = (double)db/(double)da;
int derr = (int)(dbda*65536.0);
#ifdef LICE_FAVOR_SIZE
LICE_COMBINEFUNC blitfunc=NULL;
#define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else
#define __LICE__ACTION(COMBFUNC) __LICE_LineClass<COMBFUNC>::LICE_LineImpl(px,px2, derr, astep, da, bstep, color, aw, aa)
#endif
if (aa)
{
__LICE_ACTION_NOSRCALPHA(mode, aw, false);
}
else
{
__LICE_ACTION_CONSTANTALPHA(mode, aw, false);
}
#undef __LICE__ACTION
#ifdef LICE_FAVOR_SIZE
if (blitfunc) __LICE_LineClass::LICE_LineImpl(px,px2, derr, astep, da, bstep, color, aw, aa, blitfunc);
#endif
}
}
}
void LICE_FLine(LICE_IBitmap* dest, float x1, float y1, float x2, float y2, LICE_pixel color, float alpha, int mode, bool aa)
{
if (!dest) return;
if (!aa)
{
LICE_Line(dest,(int)x1,(int)y1,(int)x2,(int)y2,color,alpha,mode,false);
return;
}
int w = dest->getWidth();
int h = dest->getHeight();
if (dest->isFlipped())
{
y1 = (float)(h-1)-y1;
y2 = (float)(h-1)-y2;
}
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc>0)
{
__LICE_SCU(w);
__LICE_SCU(h);
if (!IGNORE_SCALING(mode))
{
__LICE_SC(x1);
__LICE_SC(x2);
__LICE_SC(y1);
__LICE_SC(y2);
}
}
if (ClipFLine(&x1, &y1, &x2, &y2, w, h))
{
if (x1 != x2 || y1 != y2)
{
int span = dest->getRowSpan();
int aw = (int)(256.0f*alpha);
float a1, a2, b1, b2, da, db;
int astep, bstep;
float dx = x2-x1;
float dy = y2-y1;
if (fabs(dx) > fabs(dy))
{
a1 = x1;
a2 = x2;
b1 = y1;
b2 = y2;
da = dx;
db = dy;
astep = 1;
bstep = span;
}
else
{
a1 = y1;
a2 = y2;
b1 = x1;
b2 = x2;
da = dy;
db = dx;
astep = span;
bstep = 1;
}
if (da < 0.0f)
{
da = -da;
db = -db;
SWAP(a1, a2);
SWAP(b1, b2);
}
if (db < 0.0f)
{
bstep = -bstep;
}
int n = (int)(floor(a2)-ceil(a1));
float dbda = db/da;
float ta = ceil(a1);
float tb = b1+(ta-a1)*dbda;
float bf = tb-floor(tb);
int err = (int)(bf*65536.0f);
if (bstep < 0) err = 65535-err;
int derr = (int)(fabs(dbda)*65536.0f);
LICE_pixel* px = dest->getBits()+(int)ta*astep+(int)tb*abs(bstep);
if (bstep < 0) px -= bstep;
#ifdef LICE_FAVOR_SIZE
LICE_COMBINEFUNC blitfunc=NULL;
#define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else
#define __LICE__ACTION(COMBFUNC) __LICE_LineClass<COMBFUNC>::LICE_FLineImpl(px,n,err,derr,astep,bstep, color, aw)
#endif
__LICE_ACTION_NOSRCALPHA(mode, aw, false);
#ifdef LICE_FAVOR_SIZE
if (blitfunc) __LICE_LineClass::LICE_FLineImpl(px,n,err,derr,astep,bstep, color, aw, blitfunc);
#endif
#undef __LICE__ACTION
}
}
}
void LICE_DashedLine(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int pxon, int pxoff, LICE_pixel color, float alpha, int mode, bool aa)
{
if (!dest) return;
int w = dest->getWidth();
int h = dest->getHeight();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc>0)
{
__LICE_SCU(w);
__LICE_SCU(h);
if (!IGNORE_SCALING(mode))
{
__LICE_SC(x1);
__LICE_SC(y1);
__LICE_SC(x2);
__LICE_SC(y2);
__LICE_SCU(pxon);
__LICE_SCU(pxoff);
}
}
#ifndef DISABLE_LICE_EXTENSIONS
if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DASHEDLINE_ACCEL))
{
LICE_Ext_DashedLine_acceldata data(x1, y1, x2, y2, pxon, pxoff, color, alpha, mode, aa);
if (dest->Extended(LICE_EXT_DASHEDLINE_ACCEL, &data)) return;
}
#endif
if (ClipLine(&x1, &y1, &x2, &y2, w, h))
{
if (y1 > y2) SWAP(y1, y2);
if (pxon == 1 && pxoff == 1 && x1 == x2 && (mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f)
{
LICE_DottedVertLineFAST(dest, x1, y1, y2, color);
}
else
{
int aw = (int)(256.0f*alpha);
if (x1 > x2) SWAP(x1, x2);
#ifdef LICE_FAVOR_SIZE
LICE_COMBINEFUNC blitfunc=NULL;
#define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else
#define __LICE__ACTION(COMBFUNC) __LICE_LineClass<COMBFUNC>::DashedLine(dest, x1, y1, x2, y2, pxon, pxoff, color, aw);
#endif
__LICE_ACTION_CONSTANTALPHA(mode, aw, false);
#ifdef LICE_FAVOR_SIZE
if (blitfunc) __LICE_LineClass::DashedLine(dest, x1, y1, x2, y2, pxon, pxoff, color, aw, blitfunc);
#endif
#undef __LICE__ACTION
}
}
}
bool LICE_ClipLine(int* pX1, int* pY1, int* pX2, int* pY2, int xLo, int yLo, int xHi, int yHi)
{
int x1 = *pX1-xLo;
int y1 = *pY1-yLo;
int x2 = *pX2-xLo;
int y2 = *pY2-yLo;
bool onscreen = ClipLine(&x1, &y1, &x2, &y2, xHi-xLo, yHi-yLo);
*pX1 = x1+xLo;
*pY1 = y1+yLo;
*pX2 = x2+xLo;
*pY2 = y2+yLo;
return onscreen;
}
bool LICE_ClipFLine(float* px1, float* py1, float* px2, float* py2, float xlo, float ylo, float xhi, float yhi)
{
float x1 = *px1-xlo;
float y1 = *py1-ylo;
float x2 = *px2-xlo;
float y2 = *py2-ylo;
bool onscreen = ClipFLine(&x1, &y1, &x2, &y2, xhi-xlo, yhi-ylo);
*px1 = x1+xlo;
*py1 = y1+ylo;
*px2 = x2+xlo;
*py2 = y2+ylo;
return onscreen;
}
#include "lice_bezier.h"
static void DoBezierFillSegment(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int yfill, LICE_pixel color, float alpha, int mode)
{
if (x2 < x1) return;
if (x2 == x1)
{
if (y1 > y2) SWAP(y1,y2);
int ylo = lice_min(y1,yfill);
int yhi = lice_max(y2,yfill+1);
LICE_FillRect(dest, x1, ylo, 1, yhi-ylo+1, color, alpha, mode);
return;
}
if ((y1 < yfill) == (y2 < yfill))
{
if (y1 < yfill) ++yfill;
int x[4] = { x1, x1, x2, x2 };
int y[4] = { y1, yfill, y2, yfill };
LICE_FillConvexPolygon(dest, x, y, 4, color, alpha, mode);
}
else
{
int x = x1+(int)((double)(yfill-y1)*(double)(x2-x1)/(double)(y2-y1));
int yf = yfill;
if (y1 < yfill) ++yf;
LICE_FillTriangle(dest, x1, y1, x1, yf, x, yf, color, alpha, mode);
yf = yfill;
if (y2 < yfill) ++yf;
LICE_FillTriangle(dest, x, yf, x2, yf, x2, y2, color, alpha, mode);
}
}
static void DoBezierFillSegmentX(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int xfill, LICE_pixel color, float alpha, int mode)
{
if (y2 < y1) return;
if (y2 == y1)
{
if (x1 > x2) SWAP(x1,x2);
int xlo = lice_min(x1,xfill);
int xhi = lice_max(x2,xfill+1);
LICE_FillRect(dest, xlo, y1, xhi-xlo+1, 1, color, alpha, mode);
return;
}
if ((x1 < xfill) == (x2 < xfill))
{
if (x1 < xfill) ++xfill;
int x[4] = { x1, xfill, x2, xfill };
int y[4] = { y1, y1, y2+1, y2+1 };
LICE_FillConvexPolygon(dest, x, y, 4, color, alpha, mode);
}
else
{
int y = y1+(int)((double)(xfill-x1)*(double)(y2-y1)/(double)(x2-x1));
int xf = xfill;
if (x1 < xfill) ++xf;
LICE_FillTriangle(dest, x1, y1, xf, y1, xf, y, color, alpha, mode);
xf = xfill;
if (x2 < xfill) ++xf;
LICE_FillTriangle(dest, xf, y, xf, y2, x2, y2, color, alpha, mode);
}
}
// quadratic bezier ... NOT TESTED YET
// attempt to draw segments no longer than tol px
void LICE_DrawQBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl, double yctl, double xend, double yend,
LICE_pixel color, float alpha, int mode, bool aa, double tol)
{
if (!dest) return;
int w = dest->getWidth();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc)
{
__LICE_SCU(w);
if (!IGNORE_SCALING(mode))
{
__LICE_SC(xstart);
__LICE_SC(ystart);
__LICE_SC(xctl);
__LICE_SC(yctl);
__LICE_SC(xend);
__LICE_SC(yend);
}
mode|=LICE_BLIT_IGNORE_SCALING;
}
if (xstart > xend)
{
SWAP(xstart, xend);
SWAP(ystart, yend);
}
double len = sqrt((xctl-xstart)*(xctl-xstart)+(yctl-ystart)*(yctl-ystart));
len += sqrt((xend-xctl)*(xend-xctl)+(yend-yctl)*(yend-yctl));
double xlo = xstart;
double xhi = xend;
double ylo = ystart;
double yhi = yend;
double tlo = 0.0;
double thi = 1.0;
if (xlo < 0.0f)
{
xlo = 0.0f;
ylo = LICE_Bezier_GetY(xstart, xctl, xend, ystart, yctl, yend, xlo, &tlo);
}
if (xhi >= (float)w)
{
xhi = (float)(w-1);
yhi = LICE_Bezier_GetY(xstart, xctl, xend, ystart, yctl, yend, xhi, &thi);
}
if (xlo > xhi) return;
len *= (thi-tlo);
if (tol <= 0.0f) tol = 1.0f;
int nsteps = (int)(len/tol);
if (nsteps <= 0) nsteps = 1;
double dt = (thi-tlo)/(double)nsteps;
double t = tlo+dt;
double lastx = xlo;
double lasty = ylo;
double x, y;
int i;
for (i = 1; i < nsteps; ++i)
{
LICE_Bezier(xstart, xctl, xend, ystart, yctl, yend, t, &x, &y);
LICE_FLine(dest, lastx, lasty, x, y, color, alpha, mode, aa);
lastx = x;
lasty = y;
t += dt;
}
LICE_FLine(dest, lastx, lasty, xhi, yhi, color, alpha, mode, aa);
}
int LICE_CBezPrep(int dest_w, double xstart, double ystart, double xctl1, double yctl1,
double xctl2, double yctl2, double xend, double yend, double tol, bool xbasis,
double* ax, double* bx, double* cx, double* dx, double* ay, double* by, double* cy, double* dy,
double* xlo, double* xhi, double* ylo, double* yhi, double* tlo, double* thi)
{
const int w = dest_w;
if ((xbasis && xstart > xend) || (!xbasis && ystart > yend))
{
SWAP(xstart, xend);
SWAP(xctl1, xctl2);
SWAP(ystart, yend);
SWAP(yctl1, yctl2);
}
double len = sqrt((xctl1-xstart)*(xctl1-xstart)+(yctl1-ystart)*(yctl1-ystart));
len += sqrt((xctl2-xctl1)*(xctl2-xctl1)+(yctl2-yctl1)*(yctl2-yctl1));
len += sqrt((xend-xctl2)*(xend-xctl2)+(yend-yctl2)*(yend-yctl2));
LICE_CBezier_GetCoeffs(xstart, xctl1, xctl2, xend, ystart, yctl1, yctl2, yend, ax, bx, cx, ay, by, cy);
*dx = xstart;
*dy = ystart;
*xlo = xstart;
*xhi = xend;
*ylo = ystart;
*yhi = yend;
*tlo = 0.0;
*thi = 1.0;
if (*xlo < 0.0f)
{
*xlo = 0.0f;
*ylo = LICE_CBezier_GetY(xstart, xctl1, xctl2, xend, ystart, yctl1, yctl2, yend, *xlo, (double*)NULL, (double*)NULL, (double*)NULL, tlo);
}
if (*xhi > w)
{
*xhi = w;
*yhi = LICE_CBezier_GetY(xstart, xctl1, xctl2, xend, ystart, yctl1, yctl2, yend, *xhi, (double*)NULL, (double*)(double*)NULL, thi, (double*)NULL);
}
if ((xbasis && *xlo > *xhi) || (!xbasis && *ylo > *yhi))
{
return 0;
}
len *= (*thi-*tlo);
if (tol <= 0.0f) tol = 1.0f;
int nsteps = (int)(len/tol);
if (nsteps <= 0) nsteps = 1;
return nsteps;
}
#define __LICE_SC_BEZ \
__LICE_SC(destbm_w); \
if (!IGNORE_SCALING(mode)) { \
__LICE_SC(xstart); \
__LICE_SC(ystart); \
__LICE_SC(xctl1); \
__LICE_SC(yctl1); \
__LICE_SC(xctl2); \
__LICE_SC(yctl2); \
__LICE_SC(xend); \
__LICE_SC(yend); \
}
void LICE_DrawCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1,
double xctl2, double yctl2, double xend, double yend, LICE_pixel color, float alpha, int mode, bool aa, double tol)
{
if (!dest) return;
int destbm_w = dest->getWidth();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc)
{
__LICE_SC_BEZ
mode|=LICE_BLIT_IGNORE_SCALING;
}
#ifndef DISABLE_LICE_EXTENSIONS
if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DRAWCBEZIER_ACCEL))
{
LICE_Ext_DrawCBezier_acceldata data(xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, color, alpha, mode, aa);
if (dest->Extended(LICE_EXT_DRAWCBEZIER_ACCEL, &data)) return;
}
#endif
double ax, bx, cx, dx, ay, by, cy, dy;
double xlo, xhi, ylo, yhi;
double tlo, thi;
int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, true,
&ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi);
if (!nsteps) return;
double dt = (thi-tlo)/(double)nsteps;
double t = tlo+dt;
double lastx = xlo;
double lasty = ylo;
double x, y;
int i;
for (i = 1; i < nsteps-1; ++i)
{
EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t);
LICE_FLine(dest, lastx, lasty, x, y, color, alpha, mode, aa);
lastx = x;
lasty = y;
t += dt;
}
LICE_FLine(dest, lastx, lasty, xhi, yhi, color, alpha, mode, aa);
}
void LICE_DrawThickCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1,
double xctl2, double yctl2, double xend, double yend, LICE_pixel color, float alpha, int mode, int wid, double tol)
{
if (!dest) return;
int destbm_w = dest->getWidth();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc)
{
__LICE_SC_BEZ
mode|=LICE_BLIT_IGNORE_SCALING;
}
double ax, bx, cx, dx, ay, by, cy, dy;
double xlo, xhi, ylo, yhi;
double tlo, thi;
int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, true,
&ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi);
if (!nsteps) return;
double dt = (thi-tlo)/(double)nsteps;
double t = tlo+dt;
double lastx = xlo;
double lasty = ylo;
double x, y;
bool last_xmaj=false;
int i;
for (i = 1; i < nsteps; ++i)
{
if (i == nsteps-1)
{
x = xhi;
y = yhi;
}
else
{
EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t);
}
LICE_ThickFLine(dest, lastx, lasty, x, y, color, alpha, mode, wid);
bool xmaj = fabs(x-lastx) > fabs(y-lasty);
if (i>1 && xmaj != last_xmaj)
{
//int color = LICE_RGBA(255,0,0,0);
if (wid>2)
{
// tested this with w=3, w=4, w=8 and all looked pretty decent
double r = wid*.5 - 1;
if (r<0) r=0;
LICE_FillCircle(dest,floor(lastx+0.5),floor(lasty+0.5),.5+r*.707,color,alpha,mode,true);
}
else
{
const int ix = (int)floor(lastx+0.5), iy = (int)floor(lasty);
const double da = lasty - iy;
LICE_PutPixel(dest,ix,iy,color,alpha * (1.0-da),mode);
LICE_PutPixel(dest,ix,iy+1,color,alpha*da,mode);
}
}
last_xmaj = xmaj;
lastx = x;
lasty = y;
t += dt;
}
}
void LICE_FillCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1,
double xctl2, double yctl2, double xend, double yend, int yfill, LICE_pixel color, float alpha, int mode, double tol)
{
if (!dest) return;
int destbm_w = dest->getWidth();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc)
{
__LICE_SC_BEZ
if (!IGNORE_SCALING(mode))
{
__LICE_SC(yfill);
mode|=LICE_BLIT_IGNORE_SCALING;
}
}
double ax, bx, cx, dx, ay, by, cy, dy;
double xlo, xhi, ylo, yhi;
double tlo, thi;
int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, true,
&ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi);
if (!nsteps) return;
double dt = (thi-tlo)/(double)nsteps;
double t = tlo+dt;
int lastfillx = (int)xlo;
int lastfilly = (int)(ylo+0.5f);
double x, y;
int i;
for (i = 1; i < nsteps-1; ++i)
{
EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t);
if ((int)x >= lastfillx)
{
int xi = (int)x;
int yi = (int)(y+0.5f);
DoBezierFillSegment(dest, lastfillx, lastfilly, xi, yi, yfill, color, alpha, mode);
lastfillx = xi+1;
lastfilly = yi;
}
t += dt;
}
if ((int)(xhi-1.0f) >= lastfillx)
{
DoBezierFillSegment(dest, lastfillx, lastfilly, (int)(xhi-1.0f),(int)(yhi+0.5f), yfill, color, alpha, mode);
}
}
void LICE_FillCBezierX(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1,
double xctl2, double yctl2, double xend, double yend, int xfill, LICE_pixel color, float alpha, int mode, double tol)
{
if (!dest) return;
int destbm_w = dest->getWidth();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc)
{
__LICE_SC_BEZ
if (!IGNORE_SCALING(mode))
{
__LICE_SC(xfill);
mode|=LICE_BLIT_IGNORE_SCALING;
}
}
double ax, bx, cx, dx, ay, by, cy, dy;
double xlo, xhi, ylo, yhi;
double tlo, thi;
int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, false,
&ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi);
if (!nsteps) return;
double dt = (thi-tlo)/(double)nsteps;
double t = tlo+dt;
int lastfillx = (int)(xlo+0.5f);
int lastfilly = (int)ylo;
double x, y;
int i;
for (i = 1; i < nsteps-1; ++i)
{
EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t);
if ((int)y >= lastfilly)
{
int xi = (int)(x+0.5f);
int yi = (int)y;
DoBezierFillSegmentX(dest, lastfillx, lastfilly, xi, yi, xfill, color, alpha, mode);
lastfillx = xi;
lastfilly = yi+1;
}
t += dt;
}
if ((int)(yhi-1.0f) >= lastfilly)
{
DoBezierFillSegmentX(dest, lastfillx, lastfilly, (int)(xhi+0.5),(int)(yhi-1.0f), xfill, color, alpha, mode);
}
}
void LICE_DrawRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha, int mode)
{
const int __sc = IGNORE_SCALING(mode) ? 0 : (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc>0)
{
double x1 = x, y1 = y, x2 = x+w, y2 = y+h;
const double amt = 1.0 - 256.0/__sc;
x1 += amt;
y1 += amt;
x2 -= amt;
y2 -= amt;
LICE_FLine(dest, x1, y1, x2, y1, color, alpha, mode, true);
LICE_FLine(dest, x2, y1, x2, y2, color, alpha, mode, true);
LICE_FLine(dest, x2, y2, x1, y2, color, alpha, mode, true);
LICE_FLine(dest, x1, y2, x1, y1, color, alpha, mode, true);
}
else
{
LICE_Line(dest, x, y, x+w, y, color, alpha, mode, false);
LICE_Line(dest, x+w, y, x+w, y+h, color, alpha, mode, false);
LICE_Line(dest, x+w, y+h, x, y+h, color, alpha, mode, false);
LICE_Line(dest, x, y+h, x, y, color, alpha, mode, false);
}
}
void LICE_BorderedRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel bgcolor, LICE_pixel fgcolor, float alpha, int mode)
{
LICE_FillRect(dest, x+1, y+1, w-1, h-1, bgcolor, alpha, mode);
LICE_DrawRect(dest, x, y, w, h, fgcolor, alpha, mode);
}
#ifndef LICE_FAVOR_SIZE_EXTREME
template<class COMBFUNC>
#endif
class _LICE_Fill
{
#ifdef LICE_FAVOR_SIZE_EXTREME
#define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia);
#else
#define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia);
#endif
public:
// da, db are [0..65536]
static void FillClippedTrapezoid(int wid, int span, LICE_pixel *px, int y, int xa, int xb, int da, int db, int a, int b, int astep, int bstep, int cr, int cg, int cb, int ca, int aw
#ifdef LICE_FAVOR_SIZE_EXTREME
, LICE_COMBINEFUNC combFunc
#endif
)
{
if (!da && !db)
{
while (y-->0)
{
LICE_pixel* xpx = px;
int x=xb;
while (x--)
{
DOPIX((LICE_pixel_chan*)xpx, cr, cg, cb, ca, aw)
++xpx;
}
px += span;
}
return;
}
while (y-->0)
{
int x1=lice_max(xa,0);
int x2=lice_min(xb,wid);
LICE_pixel* xpx = px + x1;
int cnt=x2-x1;
while (cnt-->0)
{
DOPIX((LICE_pixel_chan*)xpx, cr, cg, cb, ca, aw)
++xpx;
}
a += da;
b += db;
if (a >= 65536)
{
int na = a>>16;
a &= 65535;
if (astep<0)na=-na;
xa += na;
}
if (b >= 65536)
{
int nb = b>>16;
b &= 65535;
if (bstep<0)nb=-nb;
xb += nb;
}
px += span;
}
}
};
template <class COMBFUNC> class _LICE_FillFast
{
public:
// da, db are [0..65536]
static void FillClippedTrapezoidFAST(int wid, int span, LICE_pixel *px, int y, int xa, int xb, int da, int db, int a, int b, int astep, int bstep, LICE_pixel color)
{
if (!da && !db)
{
while (y-->0)
{
LICE_pixel* xpx = px;
int x=xb;
while (x--)
{
COMBFUNC::doPixFAST(xpx, color);
++xpx;
}
px += span;
}
return;
}
while (y-->0)
{
int x1=lice_max(xa,0);
int x2=lice_min(xb,wid);
LICE_pixel* xpx = px + x1;
int cnt=x2-x1;
while (cnt-->0)
{
COMBFUNC::doPixFAST(xpx, color);
++xpx;
}
a += da;
b += db;
if (a >= 65536)
{
int na = a>>16;
a &= 65535;
if (astep<0)na=-na;
xa += na;
}
if (b >= 65536)
{
int nb = b>>16;
b &= 65535;
if (bstep<0)nb=-nb;
xb += nb;
}
px += span;
}
}
};
static double FindXOnSegment(int x1, int y1, int x2, int y2, int ty)
{
if (y1 > y2)
{
SWAP(x1, x2);
SWAP(y1, y2);
}
if (ty <= y1) return x1;
if (ty >= y2) return x2;
const double dxdy = (x2-x1)/(double)(y2-y1);
return x1+(ty-y1)*dxdy;
}
void LICE_FillTrapezoidF(LICE_IBitmap* dest, double fx1a, double fx1b, int y1, double fx2a, double fx2b, int y2, LICE_pixel color, float alpha, int mode)
{
if (!dest) return;
if (y1 > y2)
{
SWAP(y1, y2);
SWAP(fx1a, fx2a);
SWAP(fx1b, fx2b);
}
if (fx1a > fx1b) SWAP(fx1a, fx1b);
if (fx2a > fx2b) SWAP(fx2a, fx2b);
int w = dest->getWidth();
int h = dest->getHeight();
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc>0)
{
__LICE_SCU(w);
__LICE_SCU(h);
if (!IGNORE_SCALING(mode))
{
__LICE_SC(fx1a);
__LICE_SC(fx1b);
__LICE_SC(fx2a);
__LICE_SC(fx2b);
__LICE_SC(y1);
__LICE_SC(y2);
}
}
if (fx1b < 0 && fx2b < 0) return;
if (fx1a >= w && fx2a >= w) return;
if (fx1a <= 0 && fx2a <= 0) fx1a = fx2a = 0;
if (fx1b >= w-1 && fx2b >= w-1) fx1b = fx2b = w-1;
if (y2 < 0 || y1 >= h) return;
int aw = (int)(alpha*256.0f);
double idy = y2==y1 ? 0.0 : (65536.0/(y2-y1));
const double maxv=(double)(1<<29);
double tmp = (fx2a-fx1a)*idy;
if (tmp > maxv) tmp=maxv;
else if (tmp < -maxv) tmp=-maxv;
int dxady = (int)floor(tmp+0.5);
tmp = ((fx2b-fx1b)*idy);
if (tmp > maxv) tmp=maxv;
else if (tmp < -maxv) tmp=-maxv;
int dxbdy = (int)floor(tmp+0.5);
int astep = 1;
int bstep = 1;
if (dxady < 0)
{
dxady = -dxady;
astep = -1;
}
if (dxbdy < 0)
{
dxbdy = -dxbdy;
bstep = -1;
}
int x1a = (int)floor(fx1a);
int x1b = (int)floor(fx1b);
int a = (int) floor((fx1a-x1a)*65536.0*astep+0.5);
int b = (int) floor((fx1b-x1b)*65536.0*bstep+0.5);
if (y1<0)
{
a -= dxady*y1;
b -= dxbdy*y1;
y1=0;
}
if (a< 0 || a >= 65536)
{
int na = a>>16;
a &= 65535;
if (astep<0)na=-na;
x1a += na;
}
if (b < 0 || b >= 65536)
{
int nb = b>>16;
b &= 65535;
if (bstep<0)nb=-nb;
x1b += nb;
}
const int extra = __sc> 0 && !IGNORE_SCALING(mode) ? (__sc/256 - 1) : 0;
if (y2 > h-1-extra) y2 = h-1-extra;
int wid = w;
int span = dest->getRowSpan();
LICE_pixel* px = dest->getBits()+y1*span;
int y = y2-y1 + 1 + extra;
x1b++; // from now on draw [x1a,x1b)
if (!dxady && !dxbdy)
{
if (x1a<0)x1a=0;
x1b = lice_min(x1b,wid)-x1a;
px+=x1a;
if (x1b<1) return;
}
if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==256)
{
_LICE_FillFast<_LICE_CombinePixelsClobberFAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==128)
{
color = (color>>1)&0x7f7f7f7f;
_LICE_FillFast<_LICE_CombinePixelsHalfMix2FAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==64)
{
color = (color>>2)&0x3f3f3f3f;
_LICE_FillFast<_LICE_CombinePixelsQuarterMix2FAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, color);
}
else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==192)
{
color = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f);
_LICE_FillFast<_LICE_CombinePixelsThreeQuarterMix2FAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy,a,b, astep,bstep, color);
}
else
{
int cr = LICE_GETR(color), cg = LICE_GETG(color), cb = LICE_GETB(color), ca = LICE_GETA(color);
#ifdef LICE_FAVOR_SIZE_EXTREME
LICE_COMBINEFUNC blitfunc=NULL;
#define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else
#define __LICE__ACTION(COMBFUNC) _LICE_Fill<COMBFUNC>::FillClippedTrapezoid(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, cr,cg,cb,ca, aw);
#endif
__LICE_ACTION_CONSTANTALPHA(mode, aw, false);
#ifdef LICE_FAVOR_SIZE_EXTREME
if (blitfunc) _LICE_Fill::FillClippedTrapezoid(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, cr,cg,cb,ca, aw, blitfunc);
#endif
#undef __LICE__ACTION
}
}
void LICE_FillTrapezoid(LICE_IBitmap* dest, int x1a, int x1b, int y1, int x2a, int x2b, int y2, LICE_pixel color, float alpha, int mode)
{
LICE_FillTrapezoidF(dest,x1a,x1b,y1,x2a,x2b,y2,color,alpha,mode);
}
static int _ysort(const void* a, const void* b)
{
int* xya = (int*)a;
int* xyb = (int*)b;
if (xya[1] < xyb[1]) return -1;
if (xya[1] > xyb[1]) return 1;
if (xya[0] < xyb[0]) return -1;
if (xya[0] > xyb[0]) return 1;
return 0;
}
#define _X(i) xy[2*(i)]
#define _Y(i) xy[2*(i)+1]
static int FindNextEdgeVertex(int* xy, int a, int n, int dir)
{
bool init = false;
double dxdy_best = 0.0f;
int i, ilo = a;
for (i = a+1; i < n; ++i)
{
if (_Y(i) == _Y(a)) continue;
const double dxdy = (_X(i)-_X(a))/(double)(_Y(i)-_Y(a));
if (!init || dxdy == dxdy_best || (dir < 0 && dxdy < dxdy_best) || (dir > 0 && dxdy > dxdy_best))
{
init = true;
ilo = i;
dxdy_best = dxdy;
}
}
return ilo;
}
void LICE_FillConvexPolygon(LICE_IBitmap* dest, const int* x, const int* y, int npoints, LICE_pixel color, float alpha, int mode)
{
if (!dest) return;
if (npoints < 3) return;
int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc)
{
__LICE_SCU(destbm_w);
__LICE_SCU(destbm_h);
if (IGNORE_SCALING(mode)) __sc = 0; // input is already scaled, ignore
mode |= LICE_BLIT_IGNORE_SCALING;
}
int* xy = 0;
int xyt[1024]; // use stack space if small
bool usestack = npoints <= (int) (sizeof(xyt)/sizeof(int)/2);
if (usestack) xy = xyt;
else xy = (int*)malloc(npoints*sizeof(int)*2);
int i;
{
int min_x=destbm_w,max_x=0;
for (i = 0; i < npoints; ++i)
{
int tx = x[i], ty=y[i];
if (__sc)
{
__LICE_SC(tx);
__LICE_SC(ty);
}
if (tx < min_x) min_x=tx;
if (tx > max_x) max_x=tx;
_X(i) = tx;
if (dest->isFlipped()) ty = destbm_h-ty-1;
_Y(i) = ty;
}
qsort(xy, npoints, 2*sizeof(int), _ysort); // sorts by y, at same y sorts by x
int ty=_Y(0);
if (ty == _Y(npoints-1))
{
// special case 1px high polygon
if (ty >= 0 && ty < dest->getHeight() && min_x <= max_x)
{
LICE_FillTrapezoid(dest,min_x,max_x,ty,min_x,max_x,ty,color,alpha,mode);
}
if (!usestack) free(xy);
return;
}
}
int a1, b1; // index of previous vertex L and R
int a2, b2; // index of next vertex L and R
int y1; // top and bottom of current trapezoid
a1 = b1 = 0;
y1 = _Y(0);
for (i = 1; i < npoints && _Y(i) == y1; ++i)
{
if (_X(i) == _X(0)) a1 = i;
b1 = i;
}
a2 = FindNextEdgeVertex(xy, a1, npoints, -1);
b2 = FindNextEdgeVertex(xy, b1, npoints, 1);
while (a1 != a2 || b1 != b2)
{
int y_a2 = _Y(a2);
int y_b2 = _Y(b2);
int y2 = lice_min(y_a2, y_b2);
double x1a = FindXOnSegment(_X(a1), _Y(a1), _X(a2), y_a2, y1);
double x1b = FindXOnSegment(_X(b1), _Y(b1), _X(b2), y_b2, y1);
double x2a = FindXOnSegment(_X(a1), _Y(a1), _X(a2), y_a2, y2);
double x2b = FindXOnSegment(_X(b1), _Y(b1), _X(b2), y_b2, y2);
LICE_FillTrapezoidF(dest, x1a, x1b, y1, x2a, x2b, y2, color, alpha, mode);
bool dir = y1<=y2; // should always be true
y1 = y2;
if (y_a2 == y1)
{
a1 = a2;
a2 = FindNextEdgeVertex(xy, a2, npoints, -1);
}
if (y_b2 == y1)
{
b1 = b2;
b2 = FindNextEdgeVertex(xy, b2, npoints, 1);
}
if (dir) y1++;
else y1--;
}
if (!usestack) free(xy);
}
#undef _X
#undef _Y
void LICE_FillTriangle(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, int x3, int y3, LICE_pixel color, float alpha, int mode)
{
if (!dest) return;
int x[3] = { x1, x2, x3 };
int y[3] = { y1, y2, y3 };
LICE_FillConvexPolygon(dest, x, y, 3, color, alpha, mode);
}
void LICE_ThickFLine(LICE_IBitmap* dest, double x1, double y1, double x2, double y2, LICE_pixel color, float alpha, int mode, int wid) // always AA. wid is not affected by scaling (1 is always normal line, 2 is always 2 physical pixels, etc)
{
if (!dest || wid<1) return;
if (wid==1)
{
LICE_Line(dest,(float)x1,(float)y1,(float)x2,float(y2),color,alpha,mode,true);
return;
}
int w = dest->getWidth();
int h = dest->getHeight();
if (dest->isFlipped())
{
y1 = (h-1)-y1;
y2 = (h-1)-y2;
}
const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
if (__sc>0)
{
__LICE_SCU(w);
__LICE_SCU(h);
if (!IGNORE_SCALING(mode))
{
__LICE_SC(x1);
__LICE_SC(x2);
__LICE_SC(y1);
__LICE_SC(y2);
}
}
bool steep = fabs(y2-y1) >= fabs(x2-x1);
if (ClipFLine(&x1, &y1, &x2, &y2, w, h))
{
if (x1 != x2 || y1 != y2)
{
int span = dest->getRowSpan();
int aw = (int)(256.0f*alpha);
double a1, a2, b1, b2, da, db;
int astep, bstep;
double dx = x2-x1;
double dy = y2-y1;
int b_max;
if (!steep)
{
a1 = x1;
a2 = x2;
b1 = y1;
b2 = y2;
da = dx;
db = dy;
astep = 1;
bstep = span;
b_max = h;
}
else
{
a1 = y1;
a2 = y2;
b1 = x1;
b2 = x2;
da = dy;
db = dx;
astep = span;
bstep = 1;
b_max = w;
}
if (da < 0.0)
{
da = -da;
db = -db;
SWAP(a1, a2);
SWAP(b1, b2);
}
if (db < 0.0)
{
bstep = -bstep;
}
int n = (int)(floor(a2)-ceil(a1));
double dbda = db/da;
double ta = ceil(a1);
double tb = b1+(ta-a1)*dbda;
double bf = tb-floor(tb);
int err = (int)(bf*65536.0);
if (bstep < 0) err = 65535-err;
int derr = (int)(fabs(dbda)*65536.0);
int b_pos = (int) tb;
LICE_pixel *px = dest->getBits() + (int)ta*astep+b_pos*abs(bstep);
if (bstep < 0) { px -= bstep; b_pos++; }
#ifdef LICE_FAVOR_SIZE
LICE_COMBINEFUNC blitfunc=NULL;
#define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else
#define __LICE__ACTION(COMBFUNC) \
__LICE_LineClass<COMBFUNC>::LICE_FLineImplFill(px,n,err,derr,astep,bstep, color, aw, wid, b_pos, b_max)
#endif
__LICE_ACTION_NOSRCALPHA(mode, aw, false);
#ifdef LICE_FAVOR_SIZE
if (blitfunc)
{
__LICE_LineClass::LICE_FLineImplFill(px,n,err,derr,astep,bstep, color, aw, wid, b_pos, b_max, blitfunc);
}
#endif
#undef __LICE__ACTION
}
}
}