/*
  Cockos WDL - LICE - Lightweight Image Compositing Engine
  Copyright (C) 2007 and later, Cockos Incorporated
  File: lice.cpp (LICE core processing)
  See lice.h for license and other information
*/


#ifndef __LICE_CPP_IMPLEMENTED__
#define __LICE_CPP_IMPLEMENTED__

#ifndef WDL_NO_DEFINE_MINMAX
#define WDL_NO_DEFINE_MINMAX
#endif
#include "lice.h"
#include <math.h>
#include <stdio.h> // only included in case we need to debug with sprintf etc

#include "lice_combine.h"
#include "lice_extended.h"

#ifndef _WIN32
#include "../swell/swell.h"
#endif

#define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING)

#define DO_RECT_SC(mode) \
  const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); \
  if (__sc>0) { \
    if (!IGNORE_SCALING(mode)) { \
      __LICE_SC(x); \
      __LICE_SC(y); \
      __LICE_SCU(w); \
      __LICE_SCU(h); \
    } \
    __LICE_SCU(destbm_w); \
    __LICE_SCU(destbm_h); \
  }



_LICE_ImageLoader_rec *LICE_ImageLoader_list;

LICE_pixel LICE_CombinePixels(LICE_pixel dest, LICE_pixel src, float alpha, int mode)
{
  int r = LICE_GETR(src);
  int g = LICE_GETG(src);
  int b = LICE_GETB(src);
  int a = LICE_GETA(src);
  int al = (int)(alpha*256.0f);

#define __LICE__ACTION(COMBFUNC) COMBFUNC::doPix((LICE_pixel_chan*)&dest,r, g, b, a, al)
  __LICE_ACTION_SRCALPHA(mode, al,false);
#undef __LICE__ACTION

  return dest;
}


void LICE_CombinePixels2(LICE_pixel *destptr, int r, int g, int b, int a, int ia, int mode)
{
#define __LICE__ACTION(COMBFUNC) COMBFUNC::doPix((LICE_pixel_chan*)destptr,r, g, b, a, ia)
  __LICE_ACTION_SRCALPHA(mode, ia, false);
#undef __LICE__ACTION
}
void LICE_CombinePixels2Clamp(LICE_pixel *destptr, int r, int g, int b, int a, int ia, int mode)
{
#define __LICE__ACTION(COMBFUNC) COMBFUNC::doPix((LICE_pixel_chan*)destptr,r, g, b, a, ia)
  __LICE_ACTION_SRCALPHA(mode, ia, true);
#undef __LICE__ACTION
}


LICE_MemBitmap::LICE_MemBitmap(int w, int h, unsigned int linealign)
{
  m_allocsize=0;
  m_fb=0;
  m_width=0;
  m_height=0;
  m_linealign = linealign > 1 ? ((linealign & ~(linealign-1))-1) : 0; // force to be contiguous bits
  if (m_linealign>16) m_linealign=16;
  if (w>0&&h>0) __resize(w,h);
}

LICE_MemBitmap::~LICE_MemBitmap() { free(m_fb); }

bool LICE_MemBitmap::__resize(int w, int h)
{
  if (w!=m_width||h!=m_height)
  {
#ifdef DEBUG_TIGHT_ALLOC // dont enable for anything you want to be even remotely fast
    free(m_fb);
    m_fb = (LICE_pixel *)malloc((m_allocsize = ((w+m_linealign)&~m_linealign)*h*sizeof(LICE_pixel)) + LICE_MEMBITMAP_ALIGNAMT);
    m_width=m_fb?w:0;
    m_height=m_fb?h:0;
    return true;
#endif
    int sz=(((m_width=w)+m_linealign)&~m_linealign)*(m_height=h)*sizeof(LICE_pixel);

    if (sz<=0||w<1||h<1) { free(m_fb); m_fb=0; m_allocsize=0; }
    else if (!m_fb) m_fb=(LICE_pixel*)malloc((m_allocsize=sz) + LICE_MEMBITMAP_ALIGNAMT);
    else 
    {
      if (sz>m_allocsize)
      {
        void *op=m_fb;
        if (!(m_fb=(LICE_pixel*)realloc(m_fb,(m_allocsize=sz+sz/4)+LICE_MEMBITMAP_ALIGNAMT)))
        {
          free(op);
          m_fb=(LICE_pixel*)malloc((m_allocsize=sz)+LICE_MEMBITMAP_ALIGNAMT);
        }
      }
    }
    if (!m_fb) {m_width=m_height=0; }

    return true;
  }
  return false;
}



#ifndef _LICE_NO_SYSBITMAPS_

LICE_SysBitmap::LICE_SysBitmap(int w, int h)
{
  m_allocw=m_alloch=0;
#ifdef _WIN32
  m_dc = CreateCompatibleDC(NULL);
  m_bitmap = 0;
  m_oldbitmap = 0;
#else
  m_dc=0;
#endif
  m_bits=0;
  m_width=m_height=0;
  m_adv_scaling=0;
  m_draw_scaling=0;

  __resize(w,h);
}


LICE_SysBitmap::~LICE_SysBitmap()
{
#ifdef _WIN32
  if (m_oldbitmap && m_dc) SelectObject(m_dc,m_oldbitmap);
  if (m_bitmap) DeleteObject(m_bitmap);
  if (m_dc) DeleteDC(m_dc);
#else
  if (m_dc)
    SWELL_DeleteGfxContext(m_dc);
#endif
}

bool LICE_SysBitmap::__resize(int w, int h)
{
#ifdef _WIN32
  if (!m_dc) { m_width=m_height=0; m_bits=0; return false; }
#endif

  if (m_width==w && m_height == h) return false;

  m_width=w;
  m_height=h;

  if (m_draw_scaling > 0)
  {
    w = (w * m_draw_scaling) >> 8;
    h = (h * m_draw_scaling) >> 8;
  }
  w = (w+3)&~3; // always keep backing store a multiple of 4px wide

#ifndef DEBUG_TIGHT_ALLOC
  // dont resize down bitmaps
  if (w && h && w <= m_allocw && h <= m_alloch && m_bits) 
  {
#ifndef _WIN32
    if (isFlipped())
    {
      m_bits=(LICE_pixel*)SWELL_GetCtxFrameBuffer(m_dc);
      m_bits += (m_alloch-h)*m_allocw;
    }
#endif
    return true;
  }
#endif//!DEBUG_TIGHT_ALLOC

  m_allocw=w;
  m_alloch=h;

#ifdef _WIN32
  if (m_oldbitmap) 
  {
    SelectObject(m_dc,m_oldbitmap);
    m_oldbitmap=0;
  }
  if (m_bitmap) DeleteObject(m_bitmap);
  m_bitmap=0;
  m_bits=0;


  if (w<1 || h<1) return false;

  BITMAPINFO pbmInfo = {0,};
  pbmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  pbmInfo.bmiHeader.biWidth = w;
  pbmInfo.bmiHeader.biHeight = isFlipped()?h:-h;
  pbmInfo.bmiHeader.biPlanes = 1;
  pbmInfo.bmiHeader.biBitCount = sizeof(LICE_pixel)*8;
  pbmInfo.bmiHeader.biCompression = BI_RGB;
  m_bitmap = CreateDIBSection( NULL, &pbmInfo, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0);

  if (m_bitmap) m_oldbitmap=SelectObject(m_dc, m_bitmap);
  else { m_width=m_height=0; m_bits=0; }
#else
  if (m_dc) SWELL_DeleteGfxContext(m_dc);
  m_dc=0;
  m_bits=0;

  if (w<1 || h<1) return false;

  m_dc=SWELL_CreateMemContext(0,w,h);
  if (!m_dc) { m_width=m_height=0; m_bits=0; }
  else m_bits=(LICE_pixel*)SWELL_GetCtxFrameBuffer(m_dc);
#endif

  return true;
}

#endif // _LICE_NO_SYSBITMAPS_



#ifndef LICE_NO_BLIT_SUPPORT
void LICE_Copy(LICE_IBitmap *dest, LICE_IBitmap *src) // resizes dest
{
  if (src&&dest)
  {
    dest->resize(src->getWidth(),src->getHeight());
    LICE_Blit(dest,src,0,0,NULL,1.0,LICE_BLIT_MODE_COPY);
  }
}
#endif

template<class COMBFUNC> class _LICE_Template_Blit0 // these always templated
{
  public:
    static void solidBlitFAST(LICE_pixel *dest, int w, int h, 
                         LICE_pixel color,
                         int dest_span)
    {
      while (h--)
      {
        LICE_pixel *pout=dest;
        int n=w;
        while (n--)
        {
          COMBFUNC::doPixFAST(pout,color);          
          ++pout;
        }
        dest+=dest_span;
      }
    }



    static void scaleBlitFAST(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, 
                          int icurx, int icury, int idx, int idy, unsigned int clipright, unsigned int clipbottom,     
                          int src_span, int dest_span)
    {
      LICE_pixel* destpx = (LICE_pixel*) dest;
      int destpxspan = dest_span*(int)sizeof(LICE_pixel_chan)/(int)sizeof(LICE_pixel);

      while (h--)
      {
        const unsigned int cury = icury >> 16;
        if (cury < clipbottom)
        {
          int curx=icurx;
          const LICE_pixel_chan *inptr=src + cury * src_span;
          LICE_pixel* pout = destpx;
          int n=w;
          while (n--)
          {
            const unsigned int offs=curx >> 16;
            if (offs<clipright)
            {
              COMBFUNC::doPixFAST(pout,((LICE_pixel *)inptr)[offs]);
            }

            ++pout;
            curx+=idx;
          }
        }
        destpx += destpxspan;
        icury+=idy;
      }
    }

};

#ifndef LICE_FAVOR_SIZE_EXTREME
template<class COMBFUNC> 
#endif
class _LICE_Template_Blit1 // these controlled by LICE_FAVOR_SIZE_EXTREME
{
#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:
    static void solidBlit(LICE_pixel_chan *dest, int w, int h, 
                         int ir, int ig, int ib, int pxa, int ia,
                         int dest_span
#ifdef LICE_FAVOR_SIZE_EXTREME
                          , LICE_COMBINEFUNC combFunc
#endif
                         )
    {
      while (h--)
      {
        LICE_pixel_chan *pout=dest;
        int n=w;
        while (n--)
        {
          DOPIX(pout,ir,ig,ib,pxa,ia);
          pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
        }
        dest+=dest_span;
      }
    }
    static void gradBlit(LICE_pixel_chan *dest, int w, int h, 
                         int ir, int ig, int ib, int ia,
                         int drdx, int dgdx, int dbdx, int dadx,
                         int drdy, int dgdy, int dbdy, int dady,
                         int dest_span
#ifdef LICE_FAVOR_SIZE_EXTREME
                          , LICE_COMBINEFUNC combFunc
#endif
                         )
    {
      while (h--)
      {
        int r=ir,g=ig,b=ib,a=ia;
        ir+=drdy; ig+=dgdy; ib+=dbdy; ia+=dady;
        LICE_pixel_chan *pout=dest;
        int n=w;
        while (n--)
        {
          const int aa=a/65536;
          DOPIX(pout,r/65536,g/65536,b/65536,aa,aa);
          pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
          r+=drdx; g+=dgdx; b+=dbdx; a+=dadx;
        }
        dest+=dest_span;
      }
    }

#undef DOPIX
};


#ifndef LICE_FAVOR_SIZE
template<class COMBFUNC> 
#endif
class _LICE_Template_Blit2 // these controlled by LICE_FAVOR_SIZE
{
#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

  public:

    static void blit(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, int src_span, int dest_span, int ia
#ifdef LICE_FAVOR_SIZE
                          , LICE_COMBINEFUNC combFunc
#endif
      )
    {
      while (h-->0)
      {
        int n=w;
        const LICE_pixel_chan *pin=src;
        LICE_pixel_chan *pout=dest;
        while (n--)
        {

          DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia);

          pin += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
          pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
        }
        dest+=dest_span;
        src += src_span;
      }
    }

    // this is only designed for filtering down an image approx 2:1 to 4:1 or so.. it'll work (poortly) for higher, and for less it's crap too.
    // probably need to redo it using linear interpolation of the filter coefficients, but bleh I'm gonna go play some call of duty
    static void scaleBlitFilterDown(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, 
                          int icurx, int icury, int idx, int idy, int clipright, int clipbottom,     
                          int src_span, int dest_span, int ia, const int *filter, int filt_start, int filtsz
#ifdef LICE_FAVOR_SIZE
                          , LICE_COMBINEFUNC combFunc
#endif
                          
                          )
    {

      while (h--)
      {
        int cury = icury >> 16;
        int curx=icurx;          
        int n=w;
        if (cury >= 0 && cury < clipbottom)
        {
          const LICE_pixel_chan *inptr=src + (cury+filt_start) * src_span;
          LICE_pixel_chan *pout=dest;
          while (n--)
          {
            int offs=curx >> 16;
            if (offs>=0 && offs<clipright)
            {
              int r=0,g=0,b=0,a=0;
              int sc=0;
              int fy=filtsz;
              int ypos=cury+filt_start;
              const LICE_pixel_chan *rdptr  = inptr + (offs + filt_start)*(int) (sizeof(LICE_pixel)/sizeof(LICE_pixel_chan));
              const int *scaletab = filter;
              while (fy--)
              {
                if (ypos >= clipbottom) break;

                if (ypos >= 0)
                {
                  int xpos=offs + filt_start;
                  const LICE_pixel_chan *pin = rdptr;
                  int fx=filtsz;
                  while (fx--)
                  {
                    int tsc = *scaletab++;
                    if (xpos >= 0 && xpos < clipright)
                    {
                      r+=pin[LICE_PIXEL_R]*tsc;
                      g+=pin[LICE_PIXEL_G]*tsc;
                      b+=pin[LICE_PIXEL_B]*tsc;
                      a+=pin[LICE_PIXEL_A]*tsc;
                      sc+=tsc;
                    }
                    xpos++;
                    pin+=sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
                  }
                }
                else scaletab += filtsz;

                ypos++;
                rdptr+=src_span;
              }
              if (sc>0)
              {
                DOPIX(pout,r/sc,g/sc,b/sc,a/sc,ia);
              }
            }

            pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
            curx+=idx;
          }
        }
        dest+=dest_span;
        icury+=idy;
      }
    }
    static void scaleBlit(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, 
                          int icurx, int icury, int idx, int idy, unsigned int clipright, unsigned int clipbottom,     
                          int src_span, int dest_span, int ia, int filtermode
#ifdef LICE_FAVOR_SIZE
                          , LICE_COMBINEFUNC combFunc
#endif
                          
                          )
    {

      if (filtermode == LICE_BLIT_FILTER_BILINEAR)
      {
        while (h--)
        {
          const unsigned int cury = icury >> 16;
          const int yfrac=icury&65535;
          int curx=icurx;          
          const LICE_pixel_chan *inptr=src + cury * src_span;
          LICE_pixel_chan *pout=dest;
          int n=w;
          if (cury < clipbottom-1)
          {
            while (n--)
            {
              const unsigned int offs=curx >> 16;
              const LICE_pixel_chan *pin = inptr + offs*sizeof(LICE_pixel);
              if (offs<clipright-1)
              {
                int r,g,b,a;
                __LICE_BilinearFilterI(&r,&g,&b,&a,pin,pin+src_span,curx&0xffff,yfrac);
                DOPIX(pout,r,g,b,a,ia)
              }
              else if (offs==clipright-1)
              {
                int r,g,b,a;
                __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+src_span,yfrac);
                DOPIX(pout,r,g,b,a,ia)
              }

              pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
              curx+=idx;
            }
          }
          else if (cury == clipbottom-1)
          {
            while (n--)
            {
              const unsigned int offs=curx >> 16;
              const LICE_pixel_chan *pin = inptr + offs*sizeof(LICE_pixel);
              if (offs<clipright-1)
              {
                int r,g,b,a;
                __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+sizeof(LICE_pixel)/sizeof(LICE_pixel_chan),curx&0xffff);
                DOPIX(pout,r,g,b,a,ia)
              }
              else if (offs==clipright-1)
              {
                DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia)
              }

              pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
              curx+=idx;
            }
          }
          dest+=dest_span;
          icury+=idy;
        }
      }
      else
      {
        while (h--)
        {
          const unsigned int cury = icury >> 16;
          if (cury < clipbottom)
          {
            int curx=icurx;
            const LICE_pixel_chan *inptr=src + cury * src_span;
            LICE_pixel_chan *pout=dest;
            int n=w;
            while (n--)
            {
              const unsigned int offs=curx >> 16;
              if (offs<clipright)
              {
                const LICE_pixel_chan *pin = inptr + offs*sizeof(LICE_pixel);

                DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia)
              }

              pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
              curx+=idx;
            }
          }
          dest+=dest_span;
          icury+=idy;
        }
      }
    }


#undef DOPIX
};

#ifdef LICE_FAVOR_SPEED
template<class COMBFUNC> 
#endif
class _LICE_Template_Blit3 // stuff controlled by LICE_FAVOR_SPEED
{
#ifndef LICE_FAVOR_SPEED
    #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:

    static void deltaBlit(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, 
                          int isrcx, int isrcy, int idsdx, int idtdx, int idsdy, int idtdy,
                          int idsdxdy, int idtdxdy,
                          unsigned int src_right, unsigned int src_bottom,
                          int src_span, int dest_span, int ia, int filtermode
#ifndef LICE_FAVOR_SPEED
                          , LICE_COMBINEFUNC combFunc
#endif
                          )
    {
      if (filtermode == LICE_BLIT_FILTER_BILINEAR)
      {
        while (h--)
        {
          int thisx=isrcx;
          int thisy=isrcy;
          LICE_pixel_chan *pout=dest;
          int n=w;
          while (n--)
          {
            const unsigned int cury = thisy >> 16;
            const unsigned int curx = thisx >> 16;
            if (cury < src_bottom-1)
            {
              if (curx < src_right-1)
              {
                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);
                int r,g,b,a;

                __LICE_BilinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisx&65535,thisy&65535);

                DOPIX(pout,r,g,b,a,ia);
              }
              else if (curx==src_right-1)
              {

                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);
                int r,g,b,a;

                __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisy&65535);
                DOPIX(pout,r,g,b,a,ia);
              }
            }
            else if (cury==src_bottom-1)
            {
              if (curx<src_right-1)
              {
                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);

                int r,g,b,a;

                __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+sizeof(LICE_pixel)/sizeof(LICE_pixel_chan),thisx&65535);

                DOPIX(pout,r,g,b,a,ia);
              }
              else if (curx==src_right-1)
              {
                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);
                DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia);
              }
            }

            pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
            thisx+=idsdx;
            thisy+=idtdx;
          }
          idsdx+=idsdxdy;
          idtdx+=idtdxdy;
          isrcx+=idsdy;
          isrcy+=idtdy;
          dest+=dest_span;
        }
      }
      else
      {
        while (h--)
        {
          int thisx=isrcx;
          int thisy=isrcy;
          LICE_pixel_chan *pout=dest;
          int n=w;
          while (n--)
          {
            const unsigned int cury = thisy >> 16;
            const unsigned int curx = thisx >> 16;
            if (cury < src_bottom && curx < src_right)
            {
              const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);

              DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia);
            }

            pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
            thisx+=idsdx;
            thisy+=idtdx;
          }
          idsdx+=idsdxdy;
          idtdx+=idtdxdy;
          isrcx+=idsdy;
          isrcy+=idtdy;
          dest+=dest_span;
        }
      }
    }

#undef DOPIX
};

#ifdef LICE_FAVOR_SPEED
template<class COMBFUNC> 
#endif
class _LICE_Template_Blit4 // stuff controlled by LICE_FAVOR_SPEED
{
#ifndef LICE_FAVOR_SPEED
    #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:

    static void deltaBlitAlpha(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, 
                          int isrcx, int isrcy, int idsdx, int idtdx, int idsdy, int idtdy,
                          int idsdxdy, int idtdxdy,
                          unsigned int src_right, unsigned int src_bottom,
                          int src_span, int dest_span, int ia, int idadx, int idady, int idadxdy, int filtermode
#ifndef LICE_FAVOR_SPEED
                          , LICE_COMBINEFUNC combFunc
#endif
                          )
    {
      if (filtermode == LICE_BLIT_FILTER_BILINEAR)
      {
        while (h--)
        {
          int thisx=isrcx;
          int thisy=isrcy;
          int thisa=ia;
          LICE_pixel_chan *pout=dest;
          int n=w;
          while (n--)
          {
            const unsigned int cury = thisy >> 16;
            const unsigned int curx = thisx >> 16;
            if (cury < src_bottom-1)
            {
              if (curx < src_right-1)
              {
                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);
                int r,g,b,a;

                __LICE_BilinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisx&65535,thisy&65535);

                DOPIX(pout,r,g,b,a,thisa>>8);
              }
              else if (curx==src_right-1)
              {

                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);
                int r,g,b,a;

                __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisy&65535);
                DOPIX(pout,r,g,b,a,thisa>>8);
              }
            }
            else if (cury==src_bottom-1)
            {
              if (curx<src_right-1)
              {
                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);

                int r,g,b,a;

                __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+sizeof(LICE_pixel)/sizeof(LICE_pixel_chan),thisx&65535);

                DOPIX(pout,r,g,b,a,thisa>>8);
              }
              else if (curx==src_right-1)
              {
                const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);
                DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],thisa>>8);
              }
            }

            pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
            thisx+=idsdx;
            thisy+=idtdx;
            thisa+=idadx;
          }
          idsdx+=idsdxdy;
          idtdx+=idtdxdy;
          idadx+=idadxdy;
          isrcx+=idsdy;
          isrcy+=idtdy;
          ia += idady;
          dest+=dest_span;
        }
      }
      else
      {
        while (h--)
        {
          int thisx=isrcx;
          int thisy=isrcy;
          int thisa=ia;
          LICE_pixel_chan *pout=dest;
          int n=w;
          while (n--)
          {
            const unsigned int cury = thisy >> 16;
            const unsigned int curx = thisx >> 16;
            if (cury < src_bottom && curx < src_right)
            {
              const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel);

              DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],thisa>>8);
            }

            pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan);
            thisx+=idsdx;
            thisy+=idtdx;
            thisa+=idadx;
          }
          idsdx+=idsdxdy;
          idtdx+=idtdxdy;
          idadx+=idadxdy;
          isrcx+=idsdy;
          isrcy+=idtdy;
          ia+=idady;
          dest+=dest_span;
        }
      }
    }

#undef DOPIX
};



#ifndef LICE_NO_GRADIENT_SUPPORT

void LICE_GradRect(LICE_IBitmap *dest, int dstx, int dsty, int dstw, int dsth, 
                      float ir, float ig, float ib, float ia,
                      float drdx, float dgdx, float dbdx, float dadx,
                      float drdy, float dgdy, float dbdy, float dady,
                      int mode)
{
  if (!dest) return;

  ir*=255.0; ig*=255.0; ib*=255.0; ia*=256.0;
  drdx*=255.0; dgdx*=255.0; dbdx*=255.0; dadx*=256.0;
  drdy*=255.0; dgdy*=255.0; dbdy*=255.0; dady*=256.0;
  // dont scale alpha

  // clip to output
  if (dstx < 0) 
  { 
    ir-=dstx*drdx; ig-=dstx*dgdx; ib-=dstx*dbdx; ia-=dstx*dadx; 
    dstw+=dstx; 
    dstx=0; 
  }
  if (dsty < 0) 
  {
    ir -= dsty*drdy; ig-=dsty*dgdy; ib -= dsty*dbdy; ia -= dsty*dady;
    dsth += dsty; 
    dsty=0; 
  }  

  int dest_span=dest->getRowSpan()*sizeof(LICE_pixel);
  LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits();

  const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();

  if (!pdest || !dest_span || dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return;

  if (dstw > destbm_w-dstx) dstw = destbm_w-dstx;
  if (dsth > destbm_h-dsty) dsth = destbm_h-dsty;
 
  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else
  {
    pdest += dsty*dest_span;
  }
  pdest+=dstx*sizeof(LICE_pixel);
#define TOFIX(a) ((int)((a)*65536.0))

  int iir=TOFIX(ir), iig=TOFIX(ig), iib=TOFIX(ib), iia=TOFIX(ia), idrdx=TOFIX(drdx),idgdx=TOFIX(dgdx),idbdx=TOFIX(dbdx),idadx=TOFIX(dadx),
      idrdy=TOFIX(drdy), idgdy=TOFIX(dgdy), idbdy=TOFIX(dbdy), idady=TOFIX(dady);


#ifdef LICE_FAVOR_SIZE_EXTREME
  LICE_COMBINEFUNC blitfunc=NULL;      
  #define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else

  #define __LICE__ACTION(comb) _LICE_Template_Blit1<comb>::gradBlit(pdest,dstw,dsth,iir,iig,iib,iia,idrdx,idgdx,idbdx,idadx,idrdy,idgdy,idbdy,idady,dest_span)
#endif

    // todo: could predict whether or not the colors will ever go out of 0.255 range and optimize

    if ((mode & LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY && iia==65536 && idady==0 && idadx == 0)
    {
      __LICE__ACTION(_LICE_CombinePixelsClobberClamp);
    }
    else 
    {
      __LICE_ACTION_NOSRCALPHA(mode,256,true);
    }
  #undef __LICE__ACTION

#ifdef LICE_FAVOR_SIZE_EXTREME
   if (blitfunc) _LICE_Template_Blit1::gradBlit(pdest,dstw,dsth,iir,iig,iib,iia,idrdx,idgdx,idbdx,idadx,idrdy,idgdy,idbdy,idady,dest_span,blitfunc);
#endif

#undef TOFIX
}

#endif


#ifndef LICE_NO_BLIT_SUPPORT 

static void LICE_BlitInt(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, const RECT *srcrect, float alpha, int mode, bool allowSc)
{
  if (!dest || !src || !alpha) return;

  int srcbm_w = src->getWidth(), srcbm_h = src->getHeight();
  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  int __sc2 = (int)src->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc2 > 0)
  {
    const int __sc = __sc2;
    __LICE_SCU(srcbm_w);
    __LICE_SCU(srcbm_h);
  }

  RECT sr={0,0,srcbm_w, srcbm_h};
  if (srcrect) 
  {
    sr=*srcrect;    
    if (sr.left < 0) { dstx-=sr.left; sr.left=0; }
    if (sr.top < 0) { dsty-=sr.top; sr.top=0; }
    if (sr.right > srcbm_w) sr.right=srcbm_w;
    if (sr.bottom > srcbm_h) sr.bottom=srcbm_h;
  }

  int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (allowSc && __sc != __sc2 && !IGNORE_SCALING(mode))
  {
    const int w = sr.right-sr.left, h=sr.bottom-sr.top;
    LICE_ScaledBlit(dest,src,dstx,dsty,w,h, sr.left,sr.top,w,h,alpha,mode);
    return;
  }
  if (__sc>0)
  {
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(dstx);
      __LICE_SC(dsty);
    }
  }
  if (__sc2>0 && !IGNORE_SCALING(mode))
  {
    __sc=__sc2;
    __LICE_SC(sr.left);
    __LICE_SC(sr.right);
    __LICE_SC(sr.top);
    __LICE_SC(sr.bottom);
  }


  // clip to output
  if (dstx < 0) { sr.left -= dstx; dstx=0; }
  if (dsty < 0) { sr.top -= dsty; dsty=0; }  

  if (sr.left < 0 || sr.top < 0) return; // overflow check
  if (sr.right <= sr.left || sr.bottom <= sr.top || dstx >= destbm_w || dsty >= destbm_h) return;

  if (sr.right > sr.left + (destbm_w-dstx)) sr.right = sr.left + (destbm_w-dstx);
  if (sr.bottom > sr.top + (destbm_h-dsty)) sr.bottom = sr.top + (destbm_h-dsty);

  // ignore blits that are 0
  if (sr.right <= sr.left || sr.bottom <= sr.top) return;

#ifndef DISABLE_LICE_EXTENSIONS
  if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_BLIT_ACCEL))
  {
    LICE_Ext_Blit_acceldata data(src, dstx, dsty, 
      sr.left, sr.top, 
      sr.right-sr.left, sr.bottom-sr.top, 
        alpha, mode);
    if (dest->Extended(LICE_EXT_BLIT_ACCEL, &data)) return;
  }
#endif


  int dest_span=dest->getRowSpan()*sizeof(LICE_pixel);
  int src_span=src->getRowSpan()*sizeof(LICE_pixel);
  const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits();
  LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits();
  if (!psrc || !pdest) return;

  if (src->isFlipped())
  {
    psrc += (src->getHeight()-sr.top - 1)*src_span;
    src_span=-src_span;
  }
  else psrc += sr.top*src_span;
  psrc += sr.left*sizeof(LICE_pixel);

  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else pdest += dsty*dest_span;
  pdest+=dstx*sizeof(LICE_pixel);

  int i=sr.bottom-sr.top;
  int cpsize=sr.right-sr.left;

  if ((mode&LICE_BLIT_MODE_MASK) >= LICE_BLIT_MODE_CHANCOPY && (mode&LICE_BLIT_MODE_MASK) < LICE_BLIT_MODE_CHANCOPY+0x10)
  {
    while (i-->0)
    {
      LICE_pixel_chan *o=pdest+((mode>>2)&3);
      const LICE_pixel_chan *in=psrc+(mode&3);
      int a=cpsize;
      while (a--)
      {
        *o=*in;
        o+=sizeof(LICE_pixel);
        in+=sizeof(LICE_pixel);
      }
      pdest+=dest_span;
      psrc += src_span;
    }
  }
  // special fast case for copy with no source alpha and alpha=1.0 or 0.5
  else if ((mode&(LICE_BLIT_MODE_MASK|LICE_BLIT_USE_ALPHA))==LICE_BLIT_MODE_COPY && (alpha==1.0||alpha==0.5))
  {
    if (alpha==0.5)
    {
      while (i-->0)
      {
        int a=cpsize;
        const LICE_pixel *rd = (LICE_pixel *)psrc;
        LICE_pixel *wr = (LICE_pixel *)pdest;
        while (a-->0)
        {
          *wr = ((*wr>>1)&0x7f7f7f7f)+((*rd++>>1)&0x7f7f7f7f);
          wr++;
        }

        pdest+=dest_span;
        psrc += src_span;
      }
    }
    else
    {
      while (i-->0)
      {
        memmove(pdest,psrc,cpsize*sizeof(LICE_pixel));
        pdest+=dest_span;
        psrc += src_span;
      }
    }
  }
  else 
  {
    int ia=(int)(alpha*256.0);
    #ifdef LICE_FAVOR_SIZE
        LICE_COMBINEFUNC blitfunc=NULL;      
        #define __LICE__ACTION(comb) blitfunc=comb::doPix;
    #else
        #define __LICE__ACTION(comb) _LICE_Template_Blit2<comb>::blit(pdest,psrc,cpsize,i,src_span,dest_span,ia)
    #endif
      
        __LICE_ACTION_SRCALPHA(mode,ia,false);
    
    #undef __LICE__ACTION

    #ifdef LICE_FAVOR_SIZE
        if (blitfunc) _LICE_Template_Blit2::blit(pdest,psrc,cpsize,i,src_span,dest_span,ia,blitfunc);
    #endif
  }
}

void LICE_Blit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, const RECT *srcrect, float alpha, int mode)
{
  LICE_BlitInt(dest,src,dstx,dsty,srcrect,alpha,mode,true);
}

void LICE_Blit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch, float alpha, int mode)
{
  RECT r={srcx,srcy,srcx+srcw,srcy+srch};
  LICE_BlitInt(dest,src,dstx,dsty,&r,alpha,mode,true);
}

#endif

#ifndef LICE_NO_BLUR_SUPPORT

void LICE_Blur(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch) // src and dest can overlap, however it may look fudgy if they do
{
  if (!dest || !src) return;

  RECT sr={srcx,srcy,srcx+srcw,srcy+srch};
  if (sr.left < 0) sr.left=0;
  if (sr.top < 0) sr.top=0;
  if (sr.right > src->getWidth()) sr.right=src->getWidth();
  if (sr.bottom > src->getHeight()) sr.bottom = src->getHeight();

  // clip to output
  if (dstx < 0) { sr.left -= dstx; dstx=0; }
  if (dsty < 0) { sr.top -= dsty; dsty=0; }

  if (sr.left < 0 || sr.top < 0) return; // overflow check

  const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  if (sr.right <= sr.left || sr.bottom <= sr.top || dstx >= destbm_w || dsty >= destbm_h) return;

  if (sr.right > sr.left + (destbm_w-dstx)) sr.right = sr.left + (destbm_w-dstx);
  if (sr.bottom > sr.top + (destbm_h-dsty)) sr.bottom = sr.top + (destbm_h-dsty);

  // ignore blits that are smaller than 2x2
  if (sr.right <= sr.left+1 || sr.bottom <= sr.top+1) return;

  int dest_span=dest->getRowSpan();
  int src_span=src->getRowSpan();
  const LICE_pixel *psrc = (LICE_pixel *)src->getBits();
  LICE_pixel *pdest = (LICE_pixel *)dest->getBits();
  if (!psrc || !pdest) return;

  if (src->isFlipped())
  {
    psrc += (src->getHeight()-sr.top - 1)*src_span;
    src_span=-src_span;
  }
  else psrc += sr.top*src_span;
  psrc += sr.left;

  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else pdest += dsty*dest_span;
  pdest+=dstx;

  LICE_pixel *tmpbuf=NULL;
  int w=sr.right-sr.left;  

  // buffer to save the last unprocessed lines for the cases where blurring from a bitmap to itself
  LICE_pixel turdbuf[2048];
  if (src==dest)
  {
    if (w <= (int) (sizeof(turdbuf)/sizeof(turdbuf[0])/2)) tmpbuf=turdbuf;
    else tmpbuf=(LICE_pixel*)malloc(w*2*sizeof(LICE_pixel));
  }

  int i;
  for (i = sr.top; i < sr.bottom; i ++)
  {
    if (tmpbuf)
      memcpy(tmpbuf+((i&1)?w:0),psrc,w*sizeof(LICE_pixel));

    if (i==sr.top || i==sr.bottom-1)
    {
      const LICE_pixel *psrc2=psrc+(i==sr.top ? src_span : -src_span);

      LICE_pixel lp;

      pdest[0] = LICE_PIXEL_HALF(lp=psrc[0]) + 
                 LICE_PIXEL_QUARTER(psrc[1]) + 
                 LICE_PIXEL_QUARTER(psrc2[0]);
      int x;
      for (x = 1; x < w-1; x ++)
      {
        LICE_pixel tp;
        pdest[x] = LICE_PIXEL_HALF(tp=psrc[x]) + 
                   LICE_PIXEL_QUARTER(psrc2[x]) + 
                   LICE_PIXEL_EIGHTH(psrc[x+1]) +
                   LICE_PIXEL_EIGHTH(lp);
        lp=tp;
      }
      pdest[x] = LICE_PIXEL_HALF(psrc[x]) + 
                   LICE_PIXEL_QUARTER(lp) + 
                   LICE_PIXEL_QUARTER(psrc2[x]);
    }
    else
    {
      const LICE_pixel *psrc2=psrc-src_span;
      const LICE_pixel *psrc3=psrc+src_span;
      if (tmpbuf)
        psrc2=tmpbuf + ((i&1) ? 0 : w);

      LICE_pixel lp;
      pdest[0] = LICE_PIXEL_HALF(lp=psrc[0]) + 
                 LICE_PIXEL_QUARTER(psrc[1]) +
                 LICE_PIXEL_EIGHTH(psrc2[0]) +
                 LICE_PIXEL_EIGHTH(psrc3[0]);
      int x;
      for (x = 1; x < w-1; x ++)
      {
        LICE_pixel tp;
        pdest[x] = LICE_PIXEL_HALF(tp=psrc[x]) +
                   LICE_PIXEL_EIGHTH(psrc[x+1]) +
                   LICE_PIXEL_EIGHTH(lp) +
                   LICE_PIXEL_EIGHTH(psrc2[x]) + 
                   LICE_PIXEL_EIGHTH(psrc3[x]);
        lp=tp;
      }
      pdest[x] = LICE_PIXEL_HALF(psrc[x]) + 
                 LICE_PIXEL_QUARTER(lp) + 
                 LICE_PIXEL_EIGHTH(psrc2[x]) +
                 LICE_PIXEL_EIGHTH(psrc3[x]);
    }
    pdest+=dest_span;
    psrc += src_span;
  }
  if (tmpbuf && tmpbuf != turdbuf)
    free(tmpbuf);
}

#endif

#ifndef LICE_NO_BLIT_SUPPORT
void LICE_ScaledBlit(LICE_IBitmap *dest, LICE_IBitmap *src, 
                     int dstx, int dsty, int dstw, int dsth, 
                     float srcx, float srcy, float srcw, float srch, 
                     float alpha, int mode)
{
  if (!dest || !src || !dstw || !dsth || !alpha) return;

  int srcbm_w = src->getWidth(), srcbm_h = src->getHeight();
  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  const float srcx_orig = srcx, srcy_orig = srcy, srcw_orig = srcw, srch_orig = srch;
  const int dstx_orig = dstx, dsty_orig = dsty;
  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(dstx);
      __LICE_SC(dsty);
      __LICE_SC(dstw);
      __LICE_SC(dsth);
    }
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);
  }
  __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(srcx);
      __LICE_SC(srcy);
      __LICE_SC(srcw);
      __LICE_SC(srch);
    }
    __LICE_SCU(srcbm_w);
    __LICE_SCU(srcbm_h);
  }

  // non-scaling optimized omde
  if (fabs(srcw-dstw)<0.001 && fabs(srch-dsth)<0.001)
  {
    // and if not bilinear filtering, or 
    // the source coordinates are near their integer counterparts
    if ((mode&LICE_BLIT_FILTER_MASK)!=LICE_BLIT_FILTER_BILINEAR ||
        (fabs(srcx-floor(srcx+0.5f))<0.03 && fabs(srcy-floor(srcy+0.5f))<0.03))
    {
      RECT sr={(int)(srcx_orig+0.5f),(int)(srcy_orig+0.5f),};
      sr.right=sr.left+(int) (srcw_orig+0.5);
      sr.bottom=sr.top+(int) (srch_orig+0.5);
      LICE_BlitInt(dest,src,dstx_orig,dsty_orig,&sr,alpha,mode,false);
      return;
    }
  }

#ifndef DISABLE_LICE_EXTENSIONS
  if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_SCALEDBLIT_ACCEL))
  {
    LICE_Ext_ScaledBlit_acceldata data(src, dstx, dsty, dstw, dsth, srcx, srcy, srcw, srch, alpha, mode);
    if (dest->Extended(LICE_EXT_SCALEDBLIT_ACCEL, &data)) return;
  }
#endif

  if (dstw<0)
  {
    dstw=-dstw;
    dstx-=dstw;
    srcx+=srcw;  
    srcw=-srcw;
  }
  if (dsth<0)
  {
    dsth=-dsth;
    dsty-=dsth;
    srcy+=srch;
    srch=-srch;
  }

  double xadvance = srcw / dstw;
  double yadvance = srch / dsth;

  int clip_r=(int)(srcx+lice_max(srcw,0)+0.999999); // this rounding logic is shit, but we're stuck with it
  int clip_b=(int)(srcy+lice_max(srch,0)+0.999999);
  if (clip_r>srcbm_w) clip_r=srcbm_w;
  if (clip_b>srcbm_h) clip_b=srcbm_h;


  if (dstx < 0) { srcx -= (float) (dstx*xadvance); dstw+=dstx; dstx=0; }
  if (dsty < 0) { srcy -= (float) (dsty*yadvance); dsth+=dsty; dsty=0; }  
  
  if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return;

  if (dstw > destbm_w-dstx) dstw=destbm_w-dstx;
  if (dsth > destbm_h-dsty) dsth=destbm_h-dsty;

  const double fidx=floor(xadvance*65536.0), fidy=floor(yadvance*65536.0);

  double ficurx=floor(srcx*65536.0), ficury=floor(srcy*65536.0);

  // the clip area calculations need to be done fixed point so the results match runtime

  if (fidx>0)
  {
    if (ficurx < 0) // increase dstx, decrease dstw
    {
      double n = ceil( - (ficurx/(double)fidx));
      if (WDL_NOT_NORMALLY(n<0)) return;
      if (n >= dstw) return;
      dstw-=(int)n;
      dstx+=(int)n;
      ficurx+=fidx*(int)n;
    }
    if ((ficurx + fidx*(double)(dstw-1)) >= srcbm_w*65536.0)
    {
      int neww = (int)(((srcbm_w-1)*65536.0 - ficurx)/fidx);
      if (neww < dstw) dstw=neww;
    }
  }
  else if (fidx<0)
  {
    // todo: optimize source-clipping with reversed X axis
  }

  if (fidy > 0)
  {
    if (ficury < 0) // increase dsty, decrease dsth
    {
      double n = ceil( - (ficury / (double) fidy));
      if (WDL_NOT_NORMALLY(n<0)) return;
      if (n >= dsth) return;
      dsth-=(int)n;
      dsty+=(int)n;
      ficury+=fidy*(int)n;
    }
    if ((ficury + fidy*(double)(dsth-1)) >= srcbm_h*65536.0)
    {
      int newh = (int)(((srcbm_h-1)*65536.0 - ficury)/fidy);
      if (newh < dsth) dsth=newh;
    }
  }
  else if (fidy<0)
  {
    // todo: optimize source-clipping with reversed Y axis (check icury against src->getHeight(), etc)
  }
  if (dstw<1 || dsth<1) return;

  int dest_span=dest->getRowSpan()*sizeof(LICE_pixel);
  int src_span=src->getRowSpan()*sizeof(LICE_pixel);

  const LICE_pixel_chan *psrc = (const LICE_pixel_chan *)src->getBits();
  LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits();
  if (!psrc || !pdest) return;

  const int srcoffs_x = (int)((ficurx + (fidx<0 ? fidx*dstw : 0))*(1.0/65536.0));
  const int srcoffs_y = (int)((ficury + (fidy<0 ? fidy*dsth : 0))*(1.0/65536.0));

  if (srcoffs_y < 0 || srcoffs_x < 0) return;

  if (src->isFlipped())
  {
    psrc += (srcbm_h-1-srcoffs_y)*src_span;
    src_span=-src_span;
  }
  else psrc += srcoffs_y * src_span;
  psrc += srcoffs_x * sizeof(LICE_pixel);

  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else pdest += dsty*dest_span;
  pdest+=dstx*sizeof(LICE_pixel);

  clip_r -= srcoffs_x;
  clip_b -= srcoffs_y;

  const int icurx = (int) (ficurx - srcoffs_x*65536.0);
  const int icury = (int) (ficury - srcoffs_y*65536.0);
  const int idx = (int) fidx, idy = (int) fidy;

  if (clip_r<1||clip_b<1) return;

  int ia=(int)(alpha*256.0);

  if ((mode&(LICE_BLIT_FILTER_MASK|LICE_BLIT_MODE_MASK|LICE_BLIT_USE_ALPHA))==LICE_BLIT_MODE_COPY && (ia==128 || ia==256))
  {
    if (ia==128)
    {
      _LICE_Template_Blit0<_LICE_CombinePixelsHalfMixFAST>::scaleBlitFAST(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span);
    }
    else
    {
      _LICE_Template_Blit0<_LICE_CombinePixelsClobberFAST>::scaleBlitFAST(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span);
    }
  }
  else
  {
    if (xadvance>=1.7 && yadvance >=1.7 && (mode&LICE_BLIT_FILTER_MASK)==LICE_BLIT_FILTER_BILINEAR)
    {
      int msc = lice_max(idx,idy);
      const int filtsz=msc>(3<<16) ? 5 : 3;
      const int filt_start = - (filtsz/2);

      int filter[25]; // 5x5 max
      {
        int y;
      //  char buf[4096];
    //    sprintf(buf,"filter, msc=%f: ",msc);
        int *p=filter;
        for(y=0;y<filtsz;y++)
        {
          int x;
          for(x=0;x<filtsz;x++)
          {
            if (x==y && x==filtsz/2) *p++ = 65536; // src pix is always valued at 1.
            else
            {
              double dx=x+filt_start;
              double dy=y+filt_start;
              double v = (msc-1.0) / sqrt(dx*dx+dy*dy); // this needs serious tweaking...

  //            sprintf(buf+strlen(buf),"%f,",v);

              if(v<0.0) *p++=0;
              else if (v>1.0) *p++=65536;
              else *p++=(int)(v*65536.0);
            }
          }
        }
//        OutputDebugString(buf);
      }

      #ifdef LICE_FAVOR_SIZE
        LICE_COMBINEFUNC blitfunc=NULL;      
        #define __LICE__ACTION(comb) blitfunc=comb::doPix;
      #else
        #define __LICE__ACTION(comb) _LICE_Template_Blit2<comb>::scaleBlitFilterDown(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,filter,filt_start,filtsz)
      #endif
          __LICE_ACTION_SRCALPHA(mode,ia,false);
      #undef __LICE__ACTION

      #ifdef LICE_FAVOR_SIZE
        if (blitfunc) _LICE_Template_Blit2::scaleBlitFilterDown(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,filter,filt_start,filtsz,blitfunc);
      #endif

    }
    else
    {
      #ifdef LICE_FAVOR_SIZE
        LICE_COMBINEFUNC blitfunc=NULL;      
        #define __LICE__ACTION(comb) blitfunc=comb::doPix;
      #else
        #define __LICE__ACTION(comb) _LICE_Template_Blit2<comb>::scaleBlit(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK)
      #endif
          __LICE_ACTION_SRCALPHA(mode,ia,false);
      #undef __LICE__ACTION
      #ifdef LICE_FAVOR_SIZE
        if (blitfunc) _LICE_Template_Blit2::scaleBlit(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK,blitfunc);
      #endif
    }
  }
}

void LICE_DeltaBlit(LICE_IBitmap *dest, LICE_IBitmap *src, 
                    int dstx, int dsty, int dstw, int dsth, 
                    float srcx, float srcy, float srcw, float srch, 
                    double dsdx, double dtdx, double dsdy, double dtdy,
                    double dsdxdy, double dtdxdy,
                    bool cliptosourcerect, float alpha, int mode)
{
  if (!dest || !src || !dstw || !dsth) return;

  int srcbm_w = src->getWidth(), srcbm_h = src->getHeight();
  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();

  int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(dstx);
      __LICE_SC(dsty);
      __LICE_SC(dstw);
      __LICE_SC(dsth);
    }
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);

  }
  const int __scdest = __sc;
  __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL);

  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(srcx);
      __LICE_SC(srcy);
      __LICE_SC(srcw);
      __LICE_SC(srch);
    }
    __LICE_SCU(srcbm_w);
    __LICE_SCU(srcbm_h);
  }

  if (__scdest != __sc && !IGNORE_SCALING(mode))
  {
    const double adj = (__sc>0 ? __sc : 256.0) / (double) (__scdest>0 ? __scdest : 256.0);
    dsdx *= adj;
    dtdx *= adj;
    dsdy *= adj;
    dtdy *= adj;
    dsdxdy *= adj;
    dtdxdy *= adj;
  }

  double src_top=0.0,src_left=0.0,src_right=srcbm_w,src_bottom=srcbm_h;

  if (cliptosourcerect)
  {
    if (srcx > src_left) src_left=srcx;
    if (srcy > src_top) src_top=srcy;
    if (srcx+srcw < src_right) src_right=srcx+srcw;
    if (srcy+srch < src_bottom) src_bottom=srcy+srch;
  }

  if (dstw<0)
  {
    dstw=-dstw;
    dstx-=dstw;
    srcx+=srcw;  
  }
  if (dsth<0)
  {
    dsth=-dsth;
    dsty-=dsth;
    srcy+=srch;
  }


  if (dstx < 0) 
  { 
    srcx -= (float) (dstx*dsdx); 
    srcy -= (float) (dstx*dtdx);
    dstw+=dstx; 
    dstx=0; 
  }
  if (dsty < 0) 
  { 
    srcy -= (float) (dsty*dtdy);
    srcx -= (float) (dsty*dsdy);
    dsth+=dsty; 
    dsty=0; 
  }  

  if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return;

  if (dstw > destbm_w-dstx) dstw=destbm_w-dstx;
  if (dsth > destbm_h-dsty) dsth=destbm_h-dsty;


  int dest_span=dest->getRowSpan()*sizeof(LICE_pixel);
  int src_span=src->getRowSpan()*sizeof(LICE_pixel);

  const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits();
  LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits();
  if (!psrc || !pdest) return;

  if (src->isFlipped())
  {
    psrc += (srcbm_h-1)*src_span;
    src_span=-src_span;
  }

  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else pdest += dsty*dest_span;
  pdest+=dstx*sizeof(LICE_pixel);

  int sl=(int)(src_left);
  int sr=(int)(src_right);
  int st=(int)(src_top);
  int sb=(int)(src_bottom);

  sr -= sl;
  sb -= st;
  if (sr < 1 || sb < 1) return;

  psrc += src_span * st + sl * sizeof(LICE_pixel);

  int ia=(int)(alpha*256.0);
  int isrcx=(int)(srcx*65536.0);
  int isrcy=(int)(srcy*65536.0);
  int idsdx=(int)(dsdx*65536.0);
  int idtdx=(int)(dtdx*65536.0);
  int idsdy=(int)(dsdy*65536.0);
  int idtdy=(int)(dtdy*65536.0);
  int idsdxdy=(int)(dsdxdy*65536.0);
  int idtdxdy=(int)(dtdxdy*65536.0);

#ifndef LICE_FAVOR_SPEED
  LICE_COMBINEFUNC blitfunc=NULL;
  #define __LICE__ACTION(comb) blitfunc = comb::doPix;
#else
  #define __LICE__ACTION(comb) _LICE_Template_Blit3<comb>::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK)
#endif
      __LICE_ACTION_SRCALPHA(mode,ia,false);
  #undef __LICE__ACTION

#ifndef LICE_FAVOR_SPEED
  if (blitfunc) _LICE_Template_Blit3::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK,blitfunc);
#endif

}

void LICE_DeltaBlitAlpha(LICE_IBitmap *dest, LICE_IBitmap *src, 
                    int dstx, int dsty, int dstw, int dsth, 
                    float srcx, float srcy, float srcw, float srch, 
                    double dsdx, double dtdx, double dsdy, double dtdy,
                    double dsdxdy, double dtdxdy,
                    bool cliptosourcerect, float alpha, int mode, double dadx, double dady, double dadxdy)
{
  if (!dest || !src || !dstw || !dsth) return;

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  int srcbm_w = src->getWidth(), srcbm_h = src->getHeight();
  int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(dstx);
      __LICE_SC(dsty);
      __LICE_SC(dstw);
      __LICE_SC(dsth);
    }
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);

  }
  const int __scdest = __sc;
  __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL);

  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(srcx);
      __LICE_SC(srcy);
      __LICE_SC(srcw);
      __LICE_SC(srch);
    }
    __LICE_SCU(srcbm_w);
    __LICE_SCU(srcbm_h);
  }

  if (lice_max(__scdest,0) != lice_max(__sc,0))
  {
    const double adj = (__sc>0 ? __sc : 256.0) / (double) (__scdest>0 ? __scdest : 256.0);
    dsdx *= adj;
    dtdx *= adj;
    dadx *= adj;
    dsdy *= adj;
    dtdy *= adj;
    dady *= adj;
    dsdxdy *= adj;
    dtdxdy *= adj;
    dadxdy *= adj;
  }

  const double eps = 0.0001;
  if (fabs(dadx*dstw) < eps && fabs(dady*dsth) < eps && fabs(dadxdy*dsth) < eps)
  {
    LICE_DeltaBlit(dest, src, dstx, dsty, dstw, dsth, srcx, srcy, srcw, srch, dsdx, dtdx, dsdy, dtdy, dsdxdy, dtdxdy, cliptosourcerect, alpha, mode);
    return;
  }

  double src_top=0.0,src_left=0.0,src_right=srcbm_w,src_bottom=srcbm_h;

  if (cliptosourcerect)
  {
    if (srcx > src_left) src_left=srcx;
    if (srcy > src_top) src_top=srcy;
    if (srcx+srcw < src_right) src_right=srcx+srcw;
    if (srcy+srch < src_bottom) src_bottom=srcy+srch;
  }

  if (dstw<0)
  {
    dstw=-dstw;
    dstx-=dstw;
    srcx+=srcw;  
  }
  if (dsth<0)
  {
    dsth=-dsth;
    dsty-=dsth;
    srcy+=srch;
  }


  if (dstx < 0) 
  { 
    alpha -= (float) (dstx*dadx);
    srcx -= (float) (dstx*dsdx); 
    srcy -= (float) (dstx*dtdx);
    dstw+=dstx; 
    dstx=0; 
  }
  if (dsty < 0) 
  { 
    alpha -= (float) (dsty*dady);
    srcy -= (float) (dsty*dtdy);
    srcx -= (float) (dsty*dsdy);
    dsth+=dsty; 
    dsty=0; 
  }  

  if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return;

  if (dstw > destbm_w-dstx) dstw=destbm_w-dstx;
  if (dsth > destbm_h-dsty) dsth=destbm_h-dsty;


  int dest_span=dest->getRowSpan()*sizeof(LICE_pixel);
  int src_span=src->getRowSpan()*sizeof(LICE_pixel);

  const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits();
  LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits();
  if (!psrc || !pdest) return;

  if (src->isFlipped())
  {
    psrc += (srcbm_h-1)*src_span;
    src_span=-src_span;
  }

  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else pdest += dsty*dest_span;
  pdest+=dstx*sizeof(LICE_pixel);

  int sl=(int)(src_left);
  int sr=(int)(src_right);
  int st=(int)(src_top);
  int sb=(int)(src_bottom);

  sr -= sl;
  sb -= st;
  if (sr < 1 || sb < 1) return;

  psrc += src_span * st + sl * sizeof(LICE_pixel);

  int ia=(int)(alpha*65536.0);
  int isrcx=(int)(srcx*65536.0);
  int isrcy=(int)(srcy*65536.0);
  int idsdx=(int)(dsdx*65536.0);
  int idtdx=(int)(dtdx*65536.0);
  int idsdy=(int)(dsdy*65536.0);
  int idtdy=(int)(dtdy*65536.0);
  int idsdxdy=(int)(dsdxdy*65536.0);
  int idtdxdy=(int)(dtdxdy*65536.0);

  int idadx=(int)(dadx*65536.0);
  int idady=(int)(dady*65536.0);
  int idadxdy=(int)(dadxdy*65536.0);

#ifndef LICE_FAVOR_SPEED
  LICE_COMBINEFUNC blitfunc=NULL;
  #define __LICE__ACTION(comb) blitfunc = comb::doPix;
#else
  #define __LICE__ACTION(comb) _LICE_Template_Blit4<comb>::deltaBlitAlpha(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,idadx,idady,idadxdy,mode&LICE_BLIT_FILTER_MASK)
#endif
      __LICE_ACTION_NOSRCALPHA(mode,256,true);
  #undef __LICE__ACTION

#ifndef LICE_FAVOR_SPEED
  if (blitfunc) _LICE_Template_Blit4::deltaBlitAlpha(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,idadx,idady,idadxdy,mode&LICE_BLIT_FILTER_MASK,blitfunc);
#endif

}
                      
                      


void LICE_RotatedBlit(LICE_IBitmap *dest, LICE_IBitmap *src, 
                      int dstx, int dsty, int dstw, int dsth, 
                      float srcx, float srcy, float srcw, float srch, 
                      float angle, 
                      bool cliptosourcerect, float alpha, int mode, float rotxcent, float rotycent)
{
  if (!dest || !src || !dstw || !dsth) return;

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  int srcbm_w = src->getWidth(), srcbm_h = src->getHeight();
  int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(dstx);
      __LICE_SC(dsty);
      __LICE_SC(dstw);
      __LICE_SC(dsth);
    }
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);

  }
  __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL);

  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      __LICE_SC(srcx);
      __LICE_SC(srcy);
      __LICE_SC(srcw);
      __LICE_SC(srch);
    }
    __LICE_SCU(srcbm_w);
    __LICE_SCU(srcbm_h);
  }
  double src_top=0.0,src_left=0.0,src_right=srcbm_w,src_bottom=srcbm_h;

  if (cliptosourcerect)
  {
    if (srcx > src_left) src_left=srcx;
    if (srcy > src_top) src_top=srcy;
    if (srcx+srcw < src_right) src_right=srcx+srcw;
    if (srcy+srch < src_bottom) src_bottom=srcy+srch;
  }

  if (dstw<0)
  {
    dstw=-dstw;
    dstx-=dstw;
    srcx+=srcw;  
    srcw=-srcw;
  }
  if (dsth<0)
  {
    dsth=-dsth;
    dsty-=dsth;
    srcy+=srch;
    srch=-srch;
  }

  double cosa=cos(angle);
  double sina=sin(angle);

  double xsc=srcw / dstw;
  double ysc=srch / dsth;

  double dsdx = xsc * cosa;
  double dtdy = ysc * cosa;
  double dsdy = xsc * sina;
  double dtdx = ysc * -sina;

  srcx -= (float) (0.5 * (dstw*dsdx + dsth*dsdy - srcw) - rotxcent);
  srcy -= (float) (0.5 * (dsth*dtdy + dstw*dtdx - srch) - rotycent);

  if (dstx < 0) 
  { 
    srcx -= (float) (dstx*dsdx); 
    srcy -= (float) (dstx*dtdx);
    dstw+=dstx; 
    dstx=0; 
  }
  if (dsty < 0) 
  { 
    srcy -= (float) (dsty*dtdy);
    srcx -= (float) (dsty*dsdy);
    dsth+=dsty; 
    dsty=0; 
  }  

  if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return;

  if (dstw > destbm_w-dstx) dstw=destbm_w-dstx;
  if (dsth > destbm_h-dsty) dsth=destbm_h-dsty;


  int dest_span=dest->getRowSpan()*sizeof(LICE_pixel);
  int src_span=src->getRowSpan()*sizeof(LICE_pixel);

  const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits();
  LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits();
  if (!psrc || !pdest) return;

  if (src->isFlipped())
  {
    psrc += (srcbm_h-1)*src_span;
    src_span=-src_span;
  }

  if (dest->isFlipped())
  {
    pdest += (destbm_h-dsty - 1)*dest_span;
    dest_span=-dest_span;
  }
  else pdest += dsty*dest_span;
  pdest+=dstx*sizeof(LICE_pixel);

  int sl=(int)(src_left);
  int sr=(int)(src_right);
  int st=(int)(src_top);
  int sb=(int)(src_bottom);

  sr -= sl;
  sb -= st;
  srcx -= sl;
  srcy -= st;
  if (sr < 1 || sb < 1) return;

  psrc += src_span * st + sl * sizeof(LICE_pixel);

  int ia=(int)(alpha*256.0);
  int isrcx=(int)(srcx*65536.0);
  int isrcy=(int)(srcy*65536.0);
  int idsdx=(int)(dsdx*65536.0);
  int idtdx=(int)(dtdx*65536.0);
  int idsdy=(int)(dsdy*65536.0);
  int idtdy=(int)(dtdy*65536.0);

#ifndef LICE_FAVOR_SPEED
  LICE_COMBINEFUNC blitfunc=NULL;
  #define __LICE__ACTION(comb) blitfunc = comb::doPix;
#else
  #define __LICE__ACTION(comb) _LICE_Template_Blit3<comb>::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,0,0,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK)
#endif
      __LICE_ACTION_SRCALPHA(mode,ia,false);
  #undef __LICE__ACTION

#ifndef LICE_FAVOR_SPEED
  if (blitfunc) _LICE_Template_Blit3::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,0,0,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK,blitfunc);
#endif
}

#endif


void LICE_Clear(LICE_IBitmap *dest, LICE_pixel color)
{
  if (!dest) return;

#ifndef DISABLE_LICE_EXTENSIONS
  if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_CLEAR_ACCEL))
  {
    if (dest->Extended(LICE_EXT_CLEAR_ACCEL, &color)) return;
  }
#endif

  LICE_pixel *p=dest->getBits();
  int h=dest->getHeight();
  int w=dest->getWidth();
  const int sp=dest->getRowSpan();

  const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    __LICE_SCU(w);
    __LICE_SCU(h);
  }

  if (!p || w<1 || h<1 || !sp) return;

  while (h-->0)
  {
    int n=w;
    while (n--) *p++ = color;
    p+=sp-w;
  }
}




void LICE_MultiplyAddRect(LICE_IBitmap *dest, int x, int y, int w, int h, 
                          float rsc, float gsc, float bsc, float asc,
                          float radd, float gadd, float badd, float aadd)
{
  if (!dest) return;

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  DO_RECT_SC(0)

  if (x<0) { w+=x; x=0; }
  if (y<0) { h+=y; y=0; }

  LICE_pixel *p=dest->getBits();
  const int sp=dest->getRowSpan();
  if (!p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return;

  if (w>destbm_w-x) w=destbm_w-x;
  if (h>destbm_h-y) h=destbm_h-y;

  if (dest->isFlipped())
  {
    p+=(destbm_h - y - h)*sp;
  }
  else 
  {
    p+=sp*y;
  }

  p += x;

  int ir=(int)(rsc*256.0);
  int ig=(int)(gsc*256.0);
  int ib=(int)(bsc*256.0);
  int ia=(int)(asc*256.0);
  int ir2=(int)(radd*256.0);
  int ig2=(int)(gadd*256.0);
  int ib2=(int)(badd*256.0);
  int ia2=(int)(aadd*256.0);

  while (h-->0)
  {
    int n=w;
    while (n--) 
    {
      LICE_pixel_chan *ptr=(LICE_pixel_chan *)p++;
      _LICE_MakePixelClamp(ptr,(ptr[LICE_PIXEL_R]*ir+ir2)>>8,
                          (ptr[LICE_PIXEL_G]*ig+ig2)>>8,
                          (ptr[LICE_PIXEL_B]*ib+ib2)>>8,
                          (ptr[LICE_PIXEL_A]*ia+ia2)>>8);
    }
    p+=sp-w;
  }
}

void LICE_ProcessRect(LICE_IBitmap *dest, int x, int y, int w, int h, void (*procFunc)(LICE_pixel *p, void *parm), void *parm)
{
  if (!dest||!procFunc) return;

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  DO_RECT_SC(0)

  if (x<0) { w+=x; x=0; }
  if (y<0) { h+=y; y=0; }
  
  LICE_pixel *p=dest->getBits();
  const int sp=dest->getRowSpan();

  if (!p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return;

  if (w>destbm_w-x) w=destbm_w-x;
  if (h>destbm_h-y) h=destbm_h-y;

  if (dest->isFlipped())
  {
    p+=(destbm_h - y - h)*sp;
  }
  else 
  {
    p+=sp*y;
  }

  p += x;

  while (h--)
  {
    LICE_pixel *pout=p;
    int n=w;
    while (n-->0) procFunc(pout++,parm);
    p+=sp;
  }


}

void LICE_FillRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha, int mode)
{
  if (!dest) return;

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  DO_RECT_SC(mode)

#ifndef DISABLE_LICE_EXTENSIONS
  if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_FILLRECT_ACCEL))
  {
    LICE_Ext_FillRect_acceldata data(x, y, w, h, color, alpha, mode);
    if (dest->Extended(LICE_EXT_FILLRECT_ACCEL, &data)) return;
  }
#endif

  if (mode & LICE_BLIT_USE_ALPHA) alpha *= LICE_GETA(color)/255.0f;

  LICE_pixel *p=dest->getBits();
  const int sp=dest->getRowSpan();

  if (x<0) { w+=x; x=0; }
  if (y<0) { h+=y; y=0; }
  if (!alpha || !p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return;

  if (w>destbm_w-x) w=destbm_w-x;
  if (h>destbm_h-y) h=destbm_h-y;
  
  if (dest->isFlipped())
  {
    p+=(destbm_h - y - h)*sp;
  }
  else 
  {
    p+=sp*y;
  }

  p += x;

  const int ia=(int)(alpha*256.0);
  // copy, alpha=1, alpha=0.5, 0.25, 0.75 optimizations
  if ((mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY)
  {
    if (ia==256)
    {
      _LICE_Template_Blit0<_LICE_CombinePixelsClobberFAST>::solidBlitFAST(p,w,h,color,sp);
      return;
    }
    if (ia==128)
    {
      // we use _LICE_CombinePixelsHalfMix2 because we pre-halve color here
      _LICE_Template_Blit0<_LICE_CombinePixelsHalfMix2FAST>::solidBlitFAST(p,w,h,(color>>1)&0x7f7f7f7f,sp);
      return;
    }
    if (ia==64)
    {
      _LICE_Template_Blit0<_LICE_CombinePixelsQuarterMix2FAST>::solidBlitFAST(p,w,h,(color>>2)&0x3f3f3f3f,sp);
      return;
    }
    if (ia==192)
    {
      _LICE_Template_Blit0<_LICE_CombinePixelsThreeQuarterMix2FAST>::solidBlitFAST(p,w,h,
        ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f),sp);
      return;
    }
  }

#ifdef LICE_FAVOR_SIZE_EXTREME
  LICE_COMBINEFUNC blitfunc=NULL;      
  #define __LICE__ACTION(comb) blitfunc=comb::doPix;
#else
  #define __LICE__ACTION(comb) _LICE_Template_Blit1<comb>::solidBlit((LICE_pixel_chan*)p,w,h,LICE_GETR(color),LICE_GETG(color),LICE_GETB(color),LICE_GETA(color),ia,sp*sizeof(LICE_pixel))
#endif

    // we use __LICE_ACTION_NOSRCALPHA even though __LICE_ACTION_CONSTANTALPHA
    // is valid, sinc we optimized the 1.0f/0.5f cases above
      __LICE_ACTION_NOSRCALPHA(mode,ia,false);
  #undef __LICE__ACTION

#ifdef LICE_FAVOR_SIZE_EXTREME
  if (blitfunc) _LICE_Template_Blit1::solidBlit((LICE_pixel_chan*)p,w,h,LICE_GETR(color),LICE_GETG(color),LICE_GETB(color),LICE_GETA(color),ia,sp*sizeof(LICE_pixel),blitfunc);
#endif
}

void LICE_ClearRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel mask, LICE_pixel orbits)
{
  if (!dest) return;
  LICE_pixel *p=dest->getBits();

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  DO_RECT_SC(0)

  if (x<0) { w+=x; x=0; }
  if (y<0) { h+=y; y=0; }

  const int sp=dest->getRowSpan();
  if (!p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return;

  if (w>destbm_w-x) w=destbm_w-x;
  if (h>destbm_h-y) h=destbm_h-y;
  
  if (dest->isFlipped())
  {
    p+=(destbm_h - y - h)*sp;
  }
  else p+=sp*y;

  p += x;
  while (h-->0)
  {
    int n=w;
    while (n--) 
    {
      *p = (*p&mask)|orbits;
      p++;
    }
    p+=sp-w;
  }
}


LICE_pixel LICE_GetPixel(LICE_IBitmap *bm, int x, int y)
{
  if (!bm) return 0;

  int w = bm->getWidth(), h = bm->getHeight();
  const int __sc = (int)bm->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    __LICE_SCU(w);
    __LICE_SCU(h);
    __LICE_SC(x);
    __LICE_SC(y);
  }
#ifndef DISABLE_LICE_EXTENSIONS
  if (bm->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_GETPIXEL_ACCEL))
  {
    LICE_Ext_GetPixel_acceldata data(x, y);
    if (bm->Extended(LICE_EXT_GETPIXEL_ACCEL, &data)) return data.px;
  }
#endif

  const LICE_pixel *px;

  if (!(px=bm->getBits()) || x < 0 || y < 0 || x >= w || y>= h) return 0;
  if (bm->isFlipped()) return px[(h-1-y) * bm->getRowSpan() + x];
  return px[y * bm->getRowSpan() + x];
}

void LICE_PutPixel(LICE_IBitmap *bm, int x, int y, LICE_pixel color, float alpha, int mode)
{
  if (!bm) return;

  const int __sc = (int)bm->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0)
  {
    if (!IGNORE_SCALING(mode))
    {
      LICE_FillRect(bm,x,y,1,1,color,alpha,mode);
      return;
    }

  }

  int w = bm->getWidth(), h = bm->getHeight();
  if (__sc>0)
  {
    __LICE_SCU(w);
    __LICE_SCU(h);
  }
#ifndef DISABLE_LICE_EXTENSIONS
  if (bm->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_PUTPIXEL_ACCEL))
  {
    LICE_Ext_PutPixel_acceldata data(x, y, color, alpha, mode);
    if (bm->Extended(LICE_EXT_PUTPIXEL_ACCEL, &data)) return;
  }
#endif

  LICE_pixel *px;
  if (!(px=bm->getBits()) || x < 0 || y < 0 || x >= w || y >= h) return;

  if (bm->isFlipped()) px+=x+(h-1-y)*bm->getRowSpan();
  else px+=x+y*bm->getRowSpan();

  int ia = (int)(alpha * 256.0f);
  if ((mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY)
  {
    if (ia==256)
    {
      *px = color;
      return;
    }
    if (ia==128)
    {
      *px = ((*px>>1)&0x7f7f7f7f) + ((color>>1)&0x7f7f7f7f);
      return;
    }
    if (ia==64)
    {
      *px = ((*px>>1)&0x7f7f7f7f) + ((*px>>2)&0x3f3f3f3f) + ((color>>2)&0x3f3f3f3f);
      return;
    }
    if (ia==192)
    {
      *px = ((*px>>2)&0x3f3f3f3f) + ((color>>1)&0x7f7f7f7f) + ((color>>2)&0x3f3f3f3f);
      return;
    }
  }
#define __LICE__ACTION(comb) comb::doPix((LICE_pixel_chan *)px, LICE_GETR(color),LICE_GETG(color),LICE_GETB(color),LICE_GETA(color), ia)
  __LICE_ACTION_NOSRCALPHA(mode,ia,false);
#undef __LICE__ACTION
}


#ifndef LICE_NO_BLIT_SUPPORT

template<class T> class LICE_TransformBlit_class
{
  public:
  static void blit(LICE_IBitmap *dest, LICE_IBitmap *src,  
                    int dstx, int dsty, int dstw, int dsth,
                    const T *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates
                    float alpha, int mode)
{
  if (!dest || !src || dstw<1 || dsth<1 || div_w<2 || div_h<2) return;

  int cypos=dsty;
  double ypos=dsty;
  double dxpos=dstw/(float)(div_w-1);
  double dypos=dsth/(float)(div_h-1);
  int y;
  const T *curpoints=srcpoints;
  for (y = 0; y < div_h-1; y ++)
  {
    int nypos=(int)((ypos+=dypos) + 0.5);
    int x;
    double xpos=dstx;
    int cxpos=dstx;

    if (nypos != cypos)
    {
      double iy=1.0/(double)(nypos-cypos);
      for (x = 0; x < div_w-1; x ++)
      {
        int nxpos=(int) ((xpos+=dxpos) + 0.5);
        if (nxpos != cxpos)
        {
          int offs=x*2;
          double sx=curpoints[offs];
          double sy=curpoints[offs+1];
          double sw=curpoints[offs+2]-sx;
          double sh=curpoints[offs+3]-sy;

          offs+=div_w*2;
          double sxdiff=curpoints[offs]-sx;
          double sydiff=curpoints[offs+1]-sy;
          double sw3=curpoints[offs+2]-curpoints[offs];
          double sh3=curpoints[offs+3]-curpoints[offs+1];

          double ix=1.0/(double)(nxpos-cxpos);
          double dsdx=sw*ix;
          double dtdx=sh*ix;
          double dsdx2=sw3*ix;
          double dtdx2=sh3*ix;
          double dsdy=sxdiff*iy;
          double dtdy=sydiff*iy;
          double dsdxdy = (dsdx2-dsdx)*iy;
          double dtdxdy = (dtdx2-dtdx)*iy;

          LICE_DeltaBlit(dest,src,cxpos,cypos,nxpos-cxpos,nypos-cypos,
              (float)sx,(float)sy,(float)sw,(float)sh,
              dsdx,dtdx,dsdy,dtdy,dsdxdy,dtdxdy,false,alpha,mode);
        }

        cxpos=nxpos;
      }
    }
    curpoints+=div_w*2;
    cypos=nypos;
  }
}
};

template<class T> class LICE_TransformBlitAlpha_class
{
  public:
  static void blit(LICE_IBitmap *dest, LICE_IBitmap *src,  
                    int dstx, int dsty, int dstw, int dsth,
                    const T *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates
                    int mode)
{
  if (!dest || !src || dstw<1 || dsth<1 || div_w<2 || div_h<2) return;

  int cypos=dsty;
  double ypos=dsty;
  double dxpos=dstw/(float)(div_w-1);
  double dypos=dsth/(float)(div_h-1);
  int y;
  const T *curpoints=srcpoints;
  for (y = 0; y < div_h-1; y ++)
  {
    int nypos=(int)((ypos+=dypos)+0.5);
    int x;
    double xpos=dstx;
    int cxpos=dstx;

    if (nypos != cypos)
    {
      double iy=1.0/(double)(nypos-cypos);
      for (x = 0; x < div_w-1; x ++)
      {
        int nxpos=(int) ((xpos+=dxpos)+0.5);
        if (nxpos != cxpos)
        {
          int offs=x*3;
          double sx=curpoints[offs];
          double sy=curpoints[offs+1];
          double sa=curpoints[offs+2];
          double sw=curpoints[offs+3]-sx;
          double sh=curpoints[offs+4]-sy;
          double sa2 = curpoints[offs+5]-sa;

          offs+=div_w*3;
          double sxdiff=curpoints[offs]-sx;
          double sydiff=curpoints[offs+1]-sy;
          double sadiff=curpoints[offs+2]-sa;
          double sw3=curpoints[offs+3]-curpoints[offs];
          double sh3=curpoints[offs+4]-curpoints[offs+1];
          double sa3=curpoints[offs+5]-curpoints[offs+2];

          double ix=1.0/(double)(nxpos-cxpos);
          double dsdx=sw*ix;
          double dtdx=sh*ix;
          double dadx=sa2*ix;
          double dsdx2=sw3*ix;
          double dtdx2=sh3*ix;
          double dadx2=sa3*ix;
          double dsdy=sxdiff*iy;
          double dtdy=sydiff*iy;
          double dady=sadiff*iy;
          double dsdxdy = (dsdx2-dsdx)*iy;
          double dtdxdy = (dtdx2-dtdx)*iy;
          double dadxdy = (dadx2-dadx)*iy;

          LICE_DeltaBlitAlpha(dest,src,cxpos,cypos,nxpos-cxpos,nypos-cypos,
              (float)sx,(float)sy,(float)sw,(float)sh,
              dsdx,dtdx,dsdy,dtdy,dsdxdy,dtdxdy,false,sa,mode,dadx,dady,dadxdy);
        }

        cxpos=nxpos;
      }
    }
    curpoints+=div_w*3;
    cypos=nypos;
  }
}
};

void LICE_TransformBlit(LICE_IBitmap *dest, LICE_IBitmap *src,  
                    int dstx, int dsty, int dstw, int dsth,
                    const float *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates
                    float alpha, int mode)
{
  LICE_TransformBlit_class<float>::blit(dest,src,dstx,dsty,dstw,dsth,srcpoints,div_w,div_h,alpha,mode);
}
void LICE_TransformBlit2(LICE_IBitmap *dest, LICE_IBitmap *src,  
                    int dstx, int dsty, int dstw, int dsth,
                    const double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates
                    float alpha, int mode)
{
  LICE_TransformBlit_class<double>::blit(dest,src,dstx,dsty,dstw,dsth,srcpoints,div_w,div_h,alpha,mode);
}


void LICE_TransformBlit2Alpha(LICE_IBitmap *dest, LICE_IBitmap *src,  
                    int dstx, int dsty, int dstw, int dsth,
                    const double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*3 long, and be in source image coordinates+alpha
                    int mode)
{
  LICE_TransformBlitAlpha_class<double>::blit(dest,src,dstx,dsty,dstw,dsth,srcpoints,div_w,div_h,mode);
}
#endif

#ifndef LICE_NO_MISC_SUPPORT

void LICE_SetAlphaFromColorMask(LICE_IBitmap *dest, LICE_pixel color)
{
  if (!dest) return;
  LICE_pixel *p=dest->getBits();
  int h=dest->getHeight();
  int w=dest->getWidth();
  int sp=dest->getRowSpan();
  if (!p || w<1 || h<1 || sp<1) return;

  while (h-->0)
  {
    int n=w;
    while (n--) 
    {
      if ((*p&LICE_RGBA(255,255,255,0)) == color) *p&=LICE_RGBA(255,255,255,0);
      else *p|=LICE_RGBA(0,0,0,255);
      p++;
    }
    p+=sp-w;
  }
}


void LICE_SimpleFill(LICE_IBitmap *dest, int x, int y, LICE_pixel newcolor,  
                     LICE_pixel comparemask, 
                     LICE_pixel keepmask)
{
  if (!dest) return;
  int dw=dest->getWidth();
  int dh=dest->getHeight();
  int rowsize=dest->getRowSpan();
  if (x<0||x>=dw||y<0||y>=dh) return;
  LICE_pixel *ptr=dest->getBits();
  if (!ptr) return;
  ptr += rowsize*y;

  LICE_pixel compval=ptr[x]&comparemask;
  int ay;
  for (ay=y;ay<dh; ay++)
  {
    if ((ptr[x]&comparemask)!=compval) break;
    ptr[x]=(ptr[x]&keepmask)|newcolor;

    int ax;
    for(ax=x+1;ax<dw;ax++)
    {
      if ((ptr[ax]&comparemask)!=compval) break;
      ptr[ax]=(ptr[ax]&keepmask)|newcolor;
    }
    ax=x;
    while (--ax>=0)
    {
      if ((ptr[ax]&comparemask)!=compval) break;
      ptr[ax]=(ptr[ax]&keepmask)|newcolor;
    }
    
    ptr+=rowsize;
  }
  ptr =dest->getBits()+rowsize*y;

  ay=y;
  while (--ay>=0)
  {
    ptr-=rowsize;
    if ((ptr[x]&comparemask)!=compval) break;
    ptr[x]=(ptr[x]&keepmask)|newcolor;

    int ax;
    for(ax=x+1;ax<dw;ax++)
    {
      if ((ptr[ax]&comparemask)!=compval) break;
      ptr[ax]=(ptr[ax]&keepmask)|newcolor;
    }
    ax=x;
    while (--ax>=0)
    {
      if ((ptr[ax]&comparemask)!=compval) break;
      ptr[ax]=(ptr[ax]&keepmask)|newcolor;
    }   
  }
}

// VS6 instantiates this wrong as a template function, needs to be a template class
template <class COMBFUNC> class GlyphDrawImpl
{
public:
  static void DrawGlyph(const LICE_pixel_chan* srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color,  int span, int src_span, int aa)
  {
    const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);

    int xi, yi;
    for (yi = 0; yi < src_h; ++yi, srcalpha += src_span, destpx += span) {
      const LICE_pixel_chan* tsrc = srcalpha;
      LICE_pixel* tdest = destpx;
      for (xi = 0; xi < src_w; ++xi, ++tsrc, ++tdest) {
        const LICE_pixel_chan v = *tsrc;
        if (v) {  // glyphs should be expected to have a lot of "holes"
          COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256);
        }
      }
    }
  }
  static void DrawGlyphScale(const LICE_pixel_chan* srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color,  int span, int src_span, int aa, int scale)
  {
    const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);

    int xi, yi;
    int ysum = 0;
    for (yi = 0; yi < src_h; ++yi, srcalpha += src_span) {
      ysum += scale;
      while (ysum > 255)
      {
        const LICE_pixel_chan* tsrc = srcalpha;
        LICE_pixel* tdest = destpx;
        ysum -= 256;
        destpx += span;
        int sum = 0;
        for (xi = 0; xi < src_w; ++xi, ++tsrc) {
          const LICE_pixel_chan v = *tsrc;
          sum += scale;
          if (v) {  // glyphs should be expected to have a lot of "holes"
            while (sum>255)
            {
              COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256);
              tdest++;
              sum -= 256;
            }
          }
          else
          {
            tdest += sum/256;
            sum &= 255;
          }
        }
      }
    }
  }
  static void DrawGlyphMono(const unsigned char * srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color,  int span, int src_span, int aa)
  {
    const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);

    int xi, yi;
    for (yi = 0; yi < src_h; ++yi, srcalpha += src_span, destpx += span) {
      const unsigned char *tsrc = srcalpha;
      LICE_pixel* tdest = destpx;
      unsigned char cv=0;
      for (xi = 0; xi < src_w; ++xi, ++tdest) {
        if (!(xi&7)) cv = *tsrc++;
        const LICE_pixel_chan v = (cv&128)?255:0;
        cv<<=1;
        if (v) {  // glyphs should be expected to have a lot of "holes"
          COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256);
        }
      }
    }
  }
  static void DrawGlyphMonoScale(const unsigned char * srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color,  int span, int src_span, int aa, int scale)
  {
    const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color);

    int xi, yi;
    int ysum=0;
    for (yi = 0; yi < src_h; ++yi, srcalpha += src_span) {
      ysum += scale;
      while (ysum > 255)
      {
        const unsigned char *tsrc = srcalpha;
        LICE_pixel* tdest = destpx;
        unsigned char cv=0;
        int sum=0;
        ysum -= 256;
        destpx++;
        for (xi = 0; xi < src_w; ++xi) {
          if (!(xi&7)) cv = *tsrc++;
          const LICE_pixel_chan v = (cv&128)?255:0;
          cv<<=1;
          sum += scale;
          if (v) {  // glyphs should be expected to have a lot of "holes"
            while (sum > 255)
            {
              COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256);
              tdest++;
              sum -= 256;
            }
          }
          else
          {
            tdest += sum/256;
            sum &= 255;
          }
        }
      }
    }
  }
};

void LICE_DrawGlyphEx(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const LICE_pixel_chan* alphas, int glyph_w, int glyph_span, int glyph_h, float alpha, int mode)
{
  if (!dest) return;

#ifndef DISABLE_LICE_EXTENSIONS
  if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DRAWGLYPH_ACCEL) && glyph_w == glyph_span)
  {
    LICE_Ext_DrawGlyph_acceldata data(x, y, color, alphas, glyph_w, glyph_h, alpha, mode);
    if (dest->Extended(LICE_EXT_DRAWGLYPH_ACCEL, &data)) return;
  }
#endif

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0 && IGNORE_SCALING(mode))
  {
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);
  }

  if (glyph_span < 0) alphas += -glyph_span * (glyph_h-1);

  const int ia= (int)(alpha*256.0f);

  int src_x = 0, src_y = 0, src_w = glyph_w, src_h = glyph_h;
  if (x <= -src_w || y <= -src_h) return;
  
  if (x < 0) {
    src_x -= x;
    if (src_x < 0) return; // overflow
    src_w += x;
    x = 0;
  }
  if (y < 0) {
    src_y -= y;
    if (src_y < 0) return; // overflow
    src_h += y;
    y = 0;
  }

  if (src_w < 0 || src_h < 0 || x >= destbm_w || y >= destbm_h) return;

  if (src_h > destbm_h-y) src_h = destbm_h-y;
  if (src_w > destbm_w-x) src_w = destbm_w-x;
  
  if (src_w < 1 || src_h < 1) return;

  if (__sc>0 && !IGNORE_SCALING(mode))
  {
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);
    __LICE_SC(x);
    __LICE_SC(y);
  }

  LICE_pixel* destpx = dest->getBits();
  int span = dest->getRowSpan();
  if (dest->isFlipped()) {
    destpx += (destbm_h-y-1)*span+x;
    span = -span;
  }
  else {
    destpx += y*dest->getRowSpan()+x;
  }

  const LICE_pixel_chan* srcalpha = alphas+src_y*glyph_span+src_x;

  if (__sc>0 && !IGNORE_SCALING(mode))
  {
#define __LICE__ACTION(COMBFUNC)  GlyphDrawImpl<COMBFUNC>::DrawGlyphScale(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia,__sc)
	__LICE_ACTION_NOSRCALPHA(mode, ia, false);
#undef __LICE__ACTION
  }
  else
  {
#define __LICE__ACTION(COMBFUNC)  GlyphDrawImpl<COMBFUNC>::DrawGlyph(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia)
	__LICE_ACTION_NOSRCALPHA(mode, ia, false);
#undef __LICE__ACTION
  }
}

void LICE_DrawGlyph(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const LICE_pixel_chan* alphas, int glyph_w, int glyph_h, float alpha, int mode)
{
  LICE_DrawGlyphEx(dest,x,y,color,alphas,glyph_w,glyph_w,glyph_h,alpha,mode);
}


void LICE_DrawMonoGlyph(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const unsigned char *alphabits, int glyph_w, int glyph_span, int glyph_h, float alpha, int mode)
{
  if (!dest) return;

  int destbm_w = dest->getWidth(), destbm_h = dest->getHeight();
  const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL);
  if (__sc>0 && IGNORE_SCALING(mode))
  {
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);
  }

  if (glyph_span < 0) alphabits += -glyph_span * (glyph_h-1);

  const int ia= (int)(alpha*256.0f);

  int src_x = 0, src_y = 0, src_w = glyph_w, src_h = glyph_h;
  if (x <= -src_w || y <= -src_h) return;
  
  if (x < 0) {
    src_x -= x;
    src_w += x;
    x = 0;
  }
  if (y < 0) {
    src_y -= y;
    src_h += y;
    y = 0;
  }

  if (src_w < 0 || src_h < 0 || x >= destbm_w || y >= destbm_h) return;

  if (src_h > destbm_h-y) src_h = destbm_h-y;
  if (src_w > destbm_w-x) src_w = destbm_w-x;
  
  if (src_w < 1 || src_h < 1) return;

  if (__sc>0 && !IGNORE_SCALING(mode))
  {
    __LICE_SCU(destbm_w);
    __LICE_SCU(destbm_h);
    __LICE_SC(x);
    __LICE_SC(y);
  }

  LICE_pixel* destpx = dest->getBits();
  int span = dest->getRowSpan();
  if (dest->isFlipped()) {
    destpx += (destbm_h-y-1)*span+x;
    span = -span;
  }
  else {
    destpx += y*dest->getRowSpan()+x;
  }

  const unsigned char * srcalpha = alphabits+src_y*glyph_span+src_x;
  if (__sc>0 && !IGNORE_SCALING(mode))
  {
#define __LICE__ACTION(COMBFUNC)  GlyphDrawImpl<COMBFUNC>::DrawGlyphMonoScale(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia,__sc)
	__LICE_ACTION_NOSRCALPHA(mode, ia, false);
#undef __LICE__ACTION
  }
  else
  {
#define __LICE__ACTION(COMBFUNC)  GlyphDrawImpl<COMBFUNC>::DrawGlyphMono(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia)
	__LICE_ACTION_NOSRCALPHA(mode, ia, false);
#undef __LICE__ACTION
  }
}

void LICE_HalveBlitAA(LICE_IBitmap *dest, LICE_IBitmap *src)
{
  if (!dest||!src) return; 
  int w = dest->getWidth();
  if (w > src->getWidth()/2) w=src->getWidth()/2;
  int h = dest->getHeight();
  if (h > src->getHeight()/2) h=src->getHeight()/2;
  int src_span = src->getRowSpan();
  int dest_span = dest->getRowSpan();
  const LICE_pixel *srcptr = src->getBits();
  LICE_pixel *destptr = dest->getBits();

  while (h--)
  {
    const LICE_pixel *sp = srcptr;
    const LICE_pixel *sp2 = srcptr+src_span;
    LICE_pixel *dp = destptr;
    int x=w/2;

    // this is begging for SSE intrinsics, but we never use this function so leave it as-is :)

      // perhaps we should use more precision rather than chopping each src pixel to 6 bits, but oh well
    while (x--) // unroll 2px at a time, about 5% faster on core2 and ICC
    {
      int r1=((sp[0]>>2)&0x3f3f3f3f);
      int r2=((sp[2]>>2)&0x3f3f3f3f);
      r1 += ((sp[1]>>2)&0x3f3f3f3f);
      r2 += ((sp[3]>>2)&0x3f3f3f3f);
      dp[0] = r1  +
        ((sp2[0]>>2)&0x3f3f3f3f) +
        ((sp2[1]>>2)&0x3f3f3f3f);
      dp[1] = r2  +
        ((sp2[2]>>2)&0x3f3f3f3f) +
        ((sp2[3]>>2)&0x3f3f3f3f);
      sp+=4;
      sp2+=4;
      dp+=2;
    }
    if (w&1)
    {
      *dp =
        ((sp[0]>>2)&0x3f3f3f3f) +
        ((sp[1]>>2)&0x3f3f3f3f) +
        ((sp2[0]>>2)&0x3f3f3f3f) +
        ((sp2[1]>>2)&0x3f3f3f3f);
    }
    srcptr+=src_span*2;
    destptr+=dest_span;
  }
}

#endif // LICE_NO_MISC_SUPPORT

int LICE_BitmapCmp(LICE_IBitmap* a, LICE_IBitmap* b, int *coordsOut)
{
  return LICE_BitmapCmpEx(a,b,LICE_RGBA(255,255,255,255),coordsOut);
}

int LICE_BitmapCmpEx(LICE_IBitmap* a, LICE_IBitmap* b, LICE_pixel mask, int *coordsOut)
{
  if (!a || !b) {
    if (!a && b) return -1;
    if (a && !b) return 1;
    return 0;
  }

  int aw = a->getWidth(), bw = b->getWidth();
  if (aw != bw) return bw-aw;
  int ah = a->getHeight(), bh = b->getHeight();
  if (ah != bh) return bh-ah;
  
  //coordsOut
  const LICE_pixel *px1 = a->getBits();
  const LICE_pixel *px2 = b->getBits();
  int span1 = a->getRowSpan();
  int span2 = b->getRowSpan();
  if (a->isFlipped())
  {
    px1+=span1*(ah-1);
    span1=-span1;
  }
  if (b->isFlipped())
  {
    px2+=span2*(ah-1);
    span2=-span2;
  }

  int y;
  if (!coordsOut)
  {
    if (mask == (LICE_pixel)LICE_RGBA(255,255,255,255))
      for (y=0; y < ah; y ++)
      {
        const int dv = memcmp(px1,px2,aw*sizeof(LICE_pixel));
        if (dv) return dv;
        px1+=span1;
        px2+=span2;
      }
    else
      for (y=0; y < ah; y ++)
      {
        const LICE_pixel *ptr1 = px1, *ptr2 = px2;
        int x=aw;
        while (x--)
          if ((*ptr1++ ^ *ptr2++) & mask) return true;
        px1+=span1;
        px2+=span2;
      }
  }
  else
  {
    int x;

    // find first row that differs
    for (y=0; y < ah; y ++)
    {
      // check left side
      for (x=0;x<aw && !((px1[x]^px2[x])&mask);x++);
      if (x < aw) break;

      px1+=span1;
      px2+=span2;
    }
    if (y>=ah) 
    {
      memset(coordsOut,0,4*sizeof(int));
      return 0; // no differences
    }

    int miny=y;
    int minx=x;
    // scan right edge of top differing row
    for (x=aw-1;x>minx && !((px1[x]^px2[x])&mask);x--);
    int maxx=x;

    // find last row that differs
    px1+=span1 * (ah-1-y);
    px2+=span2 * (ah-1-y);
    for (y = ah-1; y > miny; y --)
    {
      // check left side
      for (x=0;x<aw && !((px1[x]^px2[x])&mask);x++);
      if (x < aw) 
      {
        if (x < minx) minx=x;
        break;
      }
      px1-=span1;
      px2-=span2;
    }
    int maxy=y;

    if (y > miny)
    {
      // scan right edge of bottom row that differs
      for (x=aw-1;x>maxx && !((px1[x]^px2[x])&mask);x--);
      maxx=x;
    }


    // find min/max x that differ
    px1+=span1 * (miny+1-y);
    px2+=span2 * (miny+1-y);
    for (y=miny+1;y<maxy && (minx>0 || maxx<aw-1);y++) 
    {
      for (x=0;x<minx && !((px1[x]^px2[x])&mask);x++);
      minx=x;
      for (x=aw-1;x>maxx && !((px1[x]^px2[x])&mask);x--);
      maxx=x;

      px1+=span1;
      px2+=span2;
    }

    coordsOut[0]=minx;
    coordsOut[1]=miny;
    coordsOut[2]=maxx-minx+1;
    coordsOut[3]=maxy-miny+1;

    return 1;
  }

  return 0;
}

unsigned short _LICE_RGB2HSV_invtab[256]={ // 65536/idx - 1
  0,      0xffff, 0x7fff, 0x5554, 0x3fff, 0x3332, 0x2aa9, 0x2491,
  0x1fff, 0x1c70, 0x1998, 0x1744, 0x1554, 0x13b0, 0x1248, 0x1110,
  0x0fff, 0x0f0e, 0x0e37, 0x0d78, 0x0ccb, 0x0c2f, 0x0ba1, 0x0b20,
  0x0aa9, 0x0a3c, 0x09d7, 0x097a, 0x0923, 0x08d2, 0x0887, 0x0841,
  0x07ff, 0x07c0, 0x0786, 0x074f, 0x071b, 0x06ea, 0x06bb, 0x068f,
  0x0665, 0x063d, 0x0617, 0x05f3, 0x05d0, 0x05af, 0x058f, 0x0571,
  0x0554, 0x0538, 0x051d, 0x0504, 0x04eb, 0x04d3, 0x04bc, 0x04a6,
  0x0491, 0x047c, 0x0468, 0x0455, 0x0443, 0x0431, 0x0420, 0x040f,
  0x03ff, 0x03ef, 0x03df, 0x03d1, 0x03c2, 0x03b4, 0x03a7, 0x039a,
  0x038d, 0x0380, 0x0374, 0x0368, 0x035d, 0x0352, 0x0347, 0x033c,
  0x0332, 0x0328, 0x031e, 0x0314, 0x030b, 0x0302, 0x02f9, 0x02f0,
  0x02e7, 0x02df, 0x02d7, 0x02cf, 0x02c7, 0x02bf, 0x02b8, 0x02b0,
  0x02a9, 0x02a2, 0x029b, 0x0294, 0x028e, 0x0287, 0x0281, 0x027b,
  0x0275, 0x026f, 0x0269, 0x0263, 0x025d, 0x0258, 0x0252, 0x024d,
  0x0248, 0x0242, 0x023d, 0x0238, 0x0233, 0x022f, 0x022a, 0x0225,
  0x0221, 0x021c, 0x0218, 0x0213, 0x020f, 0x020b, 0x0207, 0x0203,
  0x01ff, 0x01fb, 0x01f7, 0x01f3, 0x01ef, 0x01eb, 0x01e8, 0x01e4,
  0x01e0, 0x01dd, 0x01d9, 0x01d6, 0x01d3, 0x01cf, 0x01cc, 0x01c9,
  0x01c6, 0x01c2, 0x01bf, 0x01bc, 0x01b9, 0x01b6, 0x01b3, 0x01b1,
  0x01ae, 0x01ab, 0x01a8, 0x01a5, 0x01a3, 0x01a0, 0x019d, 0x019b,
  0x0198, 0x0196, 0x0193, 0x0191, 0x018e, 0x018c, 0x0189, 0x0187,
  0x0185, 0x0182, 0x0180, 0x017e, 0x017c, 0x0179, 0x0177, 0x0175,
  0x0173, 0x0171, 0x016f, 0x016d, 0x016b, 0x0169, 0x0167, 0x0165,
  0x0163, 0x0161, 0x015f, 0x015d, 0x015b, 0x0159, 0x0157, 0x0156,
  0x0154, 0x0152, 0x0150, 0x014f, 0x014d, 0x014b, 0x0149, 0x0148,
  0x0146, 0x0145, 0x0143, 0x0141, 0x0140, 0x013e, 0x013d, 0x013b,
  0x013a, 0x0138, 0x0137, 0x0135, 0x0134, 0x0132, 0x0131, 0x012f,
  0x012e, 0x012d, 0x012b, 0x012a, 0x0128, 0x0127, 0x0126, 0x0124,
  0x0123, 0x0122, 0x0120, 0x011f, 0x011e, 0x011d, 0x011b, 0x011a,
  0x0119, 0x0118, 0x0117, 0x0115, 0x0114, 0x0113, 0x0112, 0x0111,
  0x0110, 0x010e, 0x010d, 0x010c, 0x010b, 0x010a, 0x0109, 0x0108,
  0x0107, 0x0106, 0x0105, 0x0104, 0x0103, 0x0102, 0x0101, 0x0100,
};


#endif//__LICE_CPP_IMPLEMENTED__
