/*
  pygame - Python Game Library
  Copyright (C) 2000-2001  Pete Shinners
  Copyright (C) 2007 Marcus von Appen

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Pete Shinners
  pete@shinners.org
*/

#ifndef SURFACE_H
#define SURFACE_H

/* This is defined in SDL.h */
#if defined(_POSIX_C_SOURCE)
#undef _POSIX_C_SOURCE
#endif

#include <SDL.h>

#define PYGAME_BLEND_ADD  0x1
#define PYGAME_BLEND_SUB  0x2
#define PYGAME_BLEND_MULT 0x3
#define PYGAME_BLEND_MIN  0x4
#define PYGAME_BLEND_MAX  0x5

#define PYGAME_BLEND_RGB_ADD  0x1
#define PYGAME_BLEND_RGB_SUB  0x2
#define PYGAME_BLEND_RGB_MULT 0x3
#define PYGAME_BLEND_RGB_MIN  0x4
#define PYGAME_BLEND_RGB_MAX  0x5

#define PYGAME_BLEND_RGBA_ADD  0x6
#define PYGAME_BLEND_RGBA_SUB  0x7
#define PYGAME_BLEND_RGBA_MULT 0x8
#define PYGAME_BLEND_RGBA_MIN  0x9
#define PYGAME_BLEND_RGBA_MAX  0x10
#define PYGAME_BLEND_PREMULTIPLIED  0x11





#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define GET_PIXEL_24(b) (b[0] + (b[1] << 8) + (b[2] << 16))
#else
#define GET_PIXEL_24(b) (b[2] + (b[1] << 8) + (b[0] << 16))
#endif

#define GET_PIXEL(pxl, bpp, source)               \
    switch (bpp)                                  \
    {                                             \
    case 2:                                       \
        pxl = *((Uint16 *) (source));             \
        break;                                    \
    case 4:                                       \
        pxl = *((Uint32 *) (source));             \
        break;                                    \
    default:                                      \
    {                                             \
        Uint8 *b = (Uint8 *) source;              \
        pxl = GET_PIXEL_24(b);                    \
    }                                             \
    break;                                        \
    }

#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa)               \
    _sR = ((px & fmt->Rmask) >> fmt->Rshift);                         \
    _sR = (_sR << fmt->Rloss) + (_sR >> (8 - (fmt->Rloss << 1)));     \
    _sG = ((px & fmt->Gmask) >> fmt->Gshift);                         \
    _sG = (_sG << fmt->Gloss) + (_sG >> (8 - (fmt->Gloss << 1)));     \
    _sB = ((px & fmt->Bmask) >> fmt->Bshift);                         \
    _sB = (_sB << fmt->Bloss) + (_sB >> (8 - (fmt->Bloss << 1)));     \
    if (ppa)                                                          \
    {                                                                 \
        _sA = ((px & fmt->Amask) >> fmt->Ashift);                     \
        _sA = (_sA << fmt->Aloss) + (_sA >> (8 - (fmt->Aloss << 1))); \
    }                                                                 \
    else                                                              \
    {                                                                 \
        _sA = 255;                                                    \
    }

#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt)    \
    sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \
    sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \
    sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \
    sa = 255;







#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define SET_OFFSETS_24(or, og, ob, fmt)           \
    {                                             \
    or = (fmt->Rshift == 0 ? 0 :                  \
          fmt->Rshift == 8 ? 1 :                  \
                         2   );                   \
    og = (fmt->Gshift == 0 ? 0 :                  \
          fmt->Gshift == 8 ? 1 :                  \
                         2   );                   \
    ob = (fmt->Bshift == 0 ? 0 :                  \
          fmt->Bshift == 8 ? 1 :                  \
                         2   );                   \
    }

#define SET_OFFSETS_32(or, og, ob, fmt)           \
    {                                             \
    or = (fmt->Rshift == 0  ? 0 :                 \
          fmt->Rshift == 8  ? 1 :                 \
          fmt->Rshift == 16 ? 2 :                 \
                          3   );                  \
    og = (fmt->Gshift == 0  ? 0 :                 \
          fmt->Gshift == 8  ? 1 :                 \
          fmt->Gshift == 16 ? 2 :                 \
                          3   );                  \
    ob = (fmt->Bshift == 0  ? 0 :                 \
          fmt->Bshift == 8  ? 1 :                 \
          fmt->Bshift == 16 ? 2 :                 \
                          3   );                  \
    }
#else
#define SET_OFFSETS_24(or, og, ob, fmt)           \
    {                                             \
    or = (fmt->Rshift == 0 ? 2 :                  \
          fmt->Rshift == 8 ? 1 :                  \
                         0   );                   \
    og = (fmt->Gshift == 0 ? 2 :                  \
          fmt->Gshift == 8 ? 1 :                  \
                         0   );                   \
    ob = (fmt->Bshift == 0 ? 2 :                  \
          fmt->Bshift == 8 ? 1 :                  \
                         0   );                   \
    }

#define SET_OFFSETS_32(or, og, ob, fmt)           \
    {                                             \
    or = (fmt->Rshift == 0  ? 3 :                 \
          fmt->Rshift == 8  ? 2 :                 \
          fmt->Rshift == 16 ? 1 :                 \
                          0   );                  \
    og = (fmt->Gshift == 0  ? 3 :                 \
          fmt->Gshift == 8  ? 2 :                 \
          fmt->Gshift == 16 ? 1 :                 \
                          0   );                  \
    ob = (fmt->Bshift == 0  ? 3 :                 \
          fmt->Bshift == 8  ? 2 :                 \
          fmt->Bshift == 16 ? 1 :                 \
                          0   );                  \
    }
#endif


#define CREATE_PIXEL(buf, r, g, b, a, bp, ft)     \
    switch (bp)                                   \
    {                                             \
    case 2:                                       \
        *((Uint16 *) (buf)) =                     \
            ((r >> ft->Rloss) << ft->Rshift) |    \
            ((g >> ft->Gloss) << ft->Gshift) |    \
            ((b >> ft->Bloss) << ft->Bshift) |    \
            ((a >> ft->Aloss) << ft->Ashift);     \
        break;                                    \
    case 4:                                       \
        *((Uint32 *) (buf)) =                     \
            ((r >> ft->Rloss) << ft->Rshift) |    \
            ((g >> ft->Gloss) << ft->Gshift) |    \
            ((b >> ft->Bloss) << ft->Bshift) |    \
            ((a >> ft->Aloss) << ft->Ashift);     \
        break;                                    \
    }

/* Pretty good idea from Tom Duff :-). */
#define LOOP_UNROLLED4(code, n, width) \
    n = (width + 3) / 4;               \
    switch (width & 3)                 \
    {                                  \
    case 0: do { code;                 \
        case 3: code;                  \
        case 2: code;                  \
        case 1: code;                  \
        } while (--n > 0);             \
    }

/* Used in the srcbpp == dstbpp == 1 blend functions */
#define REPEAT_3(code) \
    code;              \
    code;              \
    code;

#define REPEAT_4(code) \
    code;              \
    code;              \
    code;              \
    code;


#define BLEND_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA)  \
    tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255);       \
    tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255);       \
    tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255);

#define BLEND_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
    tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0);          \
    tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0);          \
    tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0);

#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
    dR = (dR && sR) ? (dR * sR) >> 8 : 0;          \
    dG = (dG && sG) ? (dG * sG) >> 8 : 0;          \
    dB = (dB && sB) ? (dB * sB) >> 8 : 0;

#define BLEND_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \
    if(sR < dR) { dR = sR; }                      \
    if(sG < dG) { dG = sG; }                      \
    if(sB < dB) { dB = sB; }

#define BLEND_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \
    if(sR > dR) { dR = sR; }                      \
    if(sG > dG) { dG = sG; }                      \
    if(sB > dB) { dB = sB; }






#define BLEND_RGBA_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA)  \
    tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255);       \
    tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255);       \
    tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255);       \
    tmp = dA + sA; dA = (tmp <= 255 ? tmp : 255);

#define BLEND_RGBA_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
    tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0);          \
    tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0);          \
    tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0);          \
    tmp = dA - sA; dA = (tmp >= 0 ? tmp : 0);

#define BLEND_RGBA_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
    dR = (dR && sR) ? (dR * sR) >> 8 : 0;          \
    dG = (dG && sG) ? (dG * sG) >> 8 : 0;          \
    dB = (dB && sB) ? (dB * sB) >> 8 : 0;          \
    dA = (dA && sA) ? (dA * sA) >> 8 : 0;

#define BLEND_RGBA_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \
    if(sR < dR) { dR = sR; }                      \
    if(sG < dG) { dG = sG; }                      \
    if(sB < dB) { dB = sB; }                      \
    if(sA < dA) { dA = sA; }

#define BLEND_RGBA_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \
    if(sR > dR) { dR = sR; }                      \
    if(sG > dG) { dG = sG; }                      \
    if(sB > dB) { dB = sB; }                      \
    if(sA > dA) { dA = sA; }











#if 1
/* Choose an alpha blend equation. If the sign is preserved on a right shift
 * then use a specialized, faster, equation. Otherwise a more general form,
 * where all additions are done before the shift, is needed.
*/
#if (-1 >> 1) < 0
#define ALPHA_BLEND_COMP(sC, dC, sA) ((((sC - dC) * sA + sC) >> 8) + dC)
#else
#define ALPHA_BLEND_COMP(sC, dC, sA) (((dC << 8) + (sC - dC) * sA + sC) >> 8)
#endif

#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \
    do {                                            \
        if (dA)                                     \
        {                                           \
            dR = ALPHA_BLEND_COMP(sR, dR, sA);      \
            dG = ALPHA_BLEND_COMP(sG, dG, sA);      \
            dB = ALPHA_BLEND_COMP(sB, dB, sA);      \
            dA = sA + dA - ((sA * dA) / 255);       \
        }                                           \
        else                                        \
        {                                           \
            dR = sR;                                \
            dG = sG;                                \
            dB = sB;                                \
            dA = sA;                                \
        }                                           \
    } while(0)

#define ALPHA_BLEND_PREMULTIPLIED_COMP(sC, dC, sA) (sC + dC - ((dC * sA) >> 8))

#define ALPHA_BLEND_PREMULTIPLIED(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
    do {                                            \
            tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sR, dR, sA); dR = (tmp > 255 ? 255 : tmp); \
            tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sG, dG, sA); dG = (tmp > 255 ? 255 : tmp); \
            tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sB, dB, sA); dB = (tmp > 255 ? 255 : tmp); \
            dA = sA + dA - ((sA * dA) / 255);       \
    } while(0)
#elif 0

#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA)    \
    do {                                               \
        if(sA){                                        \
            if(dA && sA < 255){                        \
                int dContrib = dA*(255 - sA)/255;      \
                dA = sA+dA - ((sA*dA)/255);            \
                dR = (dR*dContrib + sR*sA)/dA;         \
                dG = (dG*dContrib + sG*sA)/dA;         \
                dB = (dB*dContrib + sB*sA)/dA;         \
            }else{                                     \
                dR = sR;                               \
                dG = sG;                               \
                dB = sB;                               \
                dA = sA;                               \
            }                                          \
        }                                              \
    } while(0)
#endif

int
surface_fill_blend (SDL_Surface *surface, SDL_Rect *rect, Uint32 color,
                    int blendargs);

void
surface_respect_clip_rect (SDL_Surface *surface, SDL_Rect *rect);

int
pygame_AlphaBlit (SDL_Surface * src, SDL_Rect * srcrect,
                  SDL_Surface * dst, SDL_Rect * dstrect, int the_args);

int
pygame_Blit (SDL_Surface * src, SDL_Rect * srcrect,
             SDL_Surface * dst, SDL_Rect * dstrect, int the_args);

#endif /* SURFACE_H */
