#ifndef WDL_NO_DEFINE_MINMAX #define WDL_NO_DEFINE_MINMAX #endif #include "lice.h" #include "lice_combine.h" #include "lice_extended.h" #include #include //#include #define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING) template 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 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 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 __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 #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= 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= 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::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::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::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::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::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::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::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 #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 _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::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::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 } } }