/*         ______   ___    ___
 *        /\  _  \ /\_ \  /\_ \
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Sprite drawing functions.
 *
 *      By Michael Bukin.
 *
 *      See readme.txt for copyright information.
 */

#ifndef __bma_cspr_h
#define __bma_cspr_h

/* _linear_draw_sprite:
 *  Draws a sprite onto a linear bitmap at the specified x, y position,
 *  using a masked drawing mode where zero pixels are not output.
 */
void FUNC_LINEAR_DRAW_SPRITE(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_PIXEL(d, c);
	    }
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg + y], dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_MEMORY_PIXEL(d, c);
	    }
	 }
      }
   }
}

void FUNC_LINEAR_DRAW_SPRITE_END(void) { }



/* _linear_draw_256_sprite:
 *  Draws a 256 coor sprite onto a linear bitmap at the specified x, y
 *  position, using a masked drawing mode where zero pixels are not output.
 */
void FUNC_LINEAR_DRAW_256_SPRITE(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   int *table;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   table = _palette_expansion_table(bitmap_color_depth(dst));
   ASSERT(table);

   if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 unsigned char *s = src->line[sybeg + y] + sxbeg;
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
	    unsigned long c = *s;
	    if (c != 0) {
	       c = table[c];
	       PUT_PIXEL(d, c);
	    }
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 unsigned char *s = src->line[sybeg + y] + sxbeg;
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg + y], dxbeg);

	 for (x = w - 1; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
	    unsigned long c = *s;
	    if (c != 0) {
	       c = table[c];
	       PUT_MEMORY_PIXEL(d, c);
	    }
	 }
      }
   }
}



/* _linear_draw_sprite_v_flip:
 *  Draws a sprite to a linear bitmap, flipping vertically.
 */
void FUNC_LINEAR_DRAW_SPRITE_V_FLIP(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;

      /* use backward drawing onto dst */
      sybeg = src->h - (sybeg + h);
      dybeg += h - 1;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy + h - 1;
   }

   if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg - y), dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_PIXEL(d, c);
	    }
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg - y], dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_MEMORY_PIXEL(d, c);
	    }
	 }
      }
   }
}



/* _linear_draw_sprite_h_flip:
 *  Draws a sprite to a linear bitmap, flipping horizontally.
 */
void FUNC_LINEAR_DRAW_SPRITE_H_FLIP(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      /* use backward drawing onto dst */
      sxbeg = src->w - (sxbeg + w);
      dxbeg += w - 1;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx + w - 1;
      dybeg = dy;
   }

   if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), DEC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_PIXEL(d, c);
	    }
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg + y], dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), DEC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_MEMORY_PIXEL(d, c);
	    }
	 }
      }
   }
}



/* _linear_draw_sprite_vh_flip:
 *  Draws a sprite to a linear bitmap, flipping both vertically and horizontally.
 */
void FUNC_LINEAR_DRAW_SPRITE_VH_FLIP(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      /* use backward drawing onto dst */
      sxbeg = src->w - (sxbeg + w);
      dxbeg += w - 1;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;

      /* use backward drawing onto dst */
      sybeg = src->h - (sybeg + h);
      dybeg += h - 1;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx + w - 1;
      dybeg = dy + h - 1;
   }

   if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg - y), dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), DEC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_PIXEL(d, c);
	    }
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg - y], dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), DEC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
	    if (!IS_SPRITE_MASK(src, c)) {
	       PUT_MEMORY_PIXEL(d, c);
	    }
	 }
      }
   }
}



/* _linear_draw_trans_sprite:
 *  Draws a translucent sprite onto a linear bitmap.
 */
void FUNC_LINEAR_DRAW_TRANS_SPRITE(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   DTS_BLENDER blender;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   blender = MAKE_DTS_BLENDER();

   if ((src->vtable->color_depth == 8) && (dst->vtable->color_depth != 8)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 unsigned char *s = src->line[sybeg + y] + sxbeg;
	 PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
	 PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
	    unsigned long c = *s;
#if PP_DEPTH == 8
	    c = DTS_BLEND(blender, GET_PIXEL(ds), c);
	    PUT_PIXEL(dd, c);
#else
            if (!IS_SPRITE_MASK(src, c)) {
	       c = DTS_BLEND(blender, GET_PIXEL(ds), c);
	       PUT_PIXEL(dd, c);
	    }
#endif
	 }
      }

      bmp_unwrite_line(dst);
   }
   else if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
	 PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
#if PP_DEPTH == 8
	    c = DTS_BLEND(blender, GET_PIXEL(ds), c);
	    PUT_PIXEL(dd, c);
#else
	    if (!IS_SPRITE_MASK(src, c)) {
	       c = DTS_BLEND(blender, GET_PIXEL(ds), c);
	       PUT_PIXEL(dd, c);
	    }
#endif
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg + y], dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);
#if PP_DEPTH == 8
	    c = DTS_BLEND(blender, GET_MEMORY_PIXEL(d), c);
	    PUT_MEMORY_PIXEL(d, c);
#else
	    if (!IS_SPRITE_MASK(src, c)) {
	       c = DTS_BLEND(blender, GET_MEMORY_PIXEL(d), c);
	       PUT_MEMORY_PIXEL(d, c);
	    }
#endif
	 }
      }
   }
}



#if (PP_DEPTH != 8) && (PP_DEPTH != 32)

/* _linear_draw_trans_rgba_sprite:
 *  Draws a translucent RGBA sprite onto a linear bitmap.
 */
void FUNC_LINEAR_DRAW_TRANS_RGBA_SPRITE(BITMAP *dst, BITMAP *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   RGBA_BLENDER blender;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   blender = MAKE_RGBA_BLENDER();

   bmp_select(dst);

   for (y = 0; y < h; y++) {
      uint32_t *s = (uint32_t *)src->line[sybeg + y] + sxbeg;
      PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
      PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

      for (x = w - 1; x >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
	 unsigned long c = *s;

	 if (c != MASK_COLOR_32) {
	    c = RGBA_BLEND(blender, GET_PIXEL(ds), c);
	    PUT_PIXEL(dd, c);
	 }
      }
   }

   bmp_unwrite_line(dst);
}

#endif



/* _linear_draw_lit_sprite:
 *  Draws a lit sprite onto a linear bitmap.
 */
void FUNC_LINEAR_DRAW_LIT_SPRITE(BITMAP *dst, BITMAP *src, int dx, int dy, int color)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   DLS_BLENDER blender;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   blender = MAKE_DLS_BLENDER(color);

   if (dst->id & (BMP_ID_VIDEO | BMP_ID_SYSTEM)) {
      bmp_select(dst);

      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);

	    if (!IS_MASK(c)) {
	       c = DLS_BLEND(blender, color, c);
	       PUT_PIXEL(d, c);
	    }
	 }
      }

      bmp_unwrite_line(dst);
   }
   else {
      for (y = 0; y < h; y++) {
	 PIXEL_PTR s = OFFSET_PIXEL_PTR(src->line[sybeg + y], sxbeg);
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(dst->line[dybeg + y], dxbeg);

	 for (x = w - 1; x >= 0; INC_PIXEL_PTR(s), INC_PIXEL_PTR(d), x--) {
	    unsigned long c = GET_MEMORY_PIXEL(s);

	    if (!IS_MASK(c)) {
	       c = DLS_BLEND(blender, color, c);
	       PUT_MEMORY_PIXEL(d, c);
	    }
	 }
      }
   }
}



/* _linear_draw_character:
 *  For proportional font output onto a linear bitmap: uses the sprite as
 *  a mask, replacing all set pixels with the specified color.
 */
void FUNC_LINEAR_DRAW_CHARACTER(BITMAP *dst, BITMAP *src, int dx, int dy, int color, int bg)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   bmp_select(dst);

   if (bg < 0) {
      /* Masked character.  */
      for (y = 0; y < h; y++) {
	 unsigned char *s = src->line[sybeg + y] + sxbeg;
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
	    unsigned long c = *s;

	    if (c != 0) {
	       PUT_PIXEL(d, color);
	    }
	 }
      }
   }
   else {
      /* Opaque character.  */
      for (y = 0; y < h; y++) {
	 unsigned char *s = src->line[sybeg + y] + sxbeg;
	 PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);

	 for (x = w - 1; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
	    unsigned long c = *s;

	    if (c != 0) {
	       PUT_PIXEL(d, color);
	    }
	    else {
	       PUT_PIXEL(d, bg);
	    }
	 }
      }
   }

   bmp_unwrite_line(dst);
}



/* _linear_draw_rle_sprite:
 *  Draws an RLE sprite onto a linear bitmap at the specified position.
 */
void FUNC_LINEAR_DRAW_RLE_SPRITE(BITMAP *dst, AL_CONST RLE_SPRITE *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   RLE_PTR s;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   s = (RLE_PTR) (src->dat);

   /* Clip top.  */
   for (y = sybeg - 1; y >= 0; y--) {
      long c = *s++;

      while (!RLE_IS_EOL(c)) {
	 if (c > 0)
	    s += c;
	 c = *s++;
      }
   }

   bmp_select(dst);

   /* Visible part.  */
   if (sxbeg || dx+src->w >= dst->cr) {
      for (y = 0; y < h; y++) {
         PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Clip left.  */
         for (x = sxbeg; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully clipped.  */
	          x -= c;
	          s += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c -= x;
	          s += x;
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       if ((x + c) >= 0) {
	          /* Fully clipped.  */
	          x += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c += x;
	          break;
	       }
	    }

	    c = *s++;
         }

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(d), c--) {
		     unsigned long col = *s;
		     PUT_PIXEL(d, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
		     unsigned long col = *s;
		     PUT_PIXEL(d, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       d = OFFSET_PIXEL_PTR(d, -c);
	    }

	    c = *s++;
         }

         /* Clip right.  */
         while (!RLE_IS_EOL(c)) {
	    if (c > 0)
	       s += c;
	    c = *s++;
         }

	 next_line: ;
      }
   }
   else {
      for (y = 0; y < h; y++) {
         PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line2;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(d), c--) {
		     unsigned long col = *s;
		     PUT_PIXEL(d, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
		     unsigned long col = *s;
		     PUT_PIXEL(d, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       d = OFFSET_PIXEL_PTR(d, -c);
	    }

	    c = *s++;
         }

	 next_line2: ;
      }
   }

   bmp_unwrite_line(dst);
}



/* _linear_draw_trans_rle_sprite:
 *  Draws a translucent RLE sprite onto a linear bitmap.
 */
void FUNC_LINEAR_DRAW_TRANS_RLE_SPRITE(BITMAP *dst, AL_CONST RLE_SPRITE *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   RLE_PTR s;
   DTS_BLENDER blender;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   blender = MAKE_DTS_BLENDER();
   s = (RLE_PTR) (src->dat);

   /* Clip top.  */
   for (y = sybeg - 1; y >= 0; y--) {
      long c = *s++;

      while (!RLE_IS_EOL(c)) {
	 if (c > 0)
	    s += c;
	 c = *s++;
      }
   }

   bmp_select(dst);

   /* Visible part.  */
   if (sxbeg || dx+src->w >= dst->cr) {
      for (y = 0; y < h; y++) {
         PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
         PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Clip left.  */
         for (x = sxbeg; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully clipped.  */
	          x -= c;
	          s += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c -= x;
	          s += x;
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       if ((x + c) >= 0) {
	          /* Fully clipped.  */
	          x += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c += x;
	          break;
	       }
	    }

	    c = *s++;
         }

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), c--) {
		     unsigned long col = DTS_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
		     unsigned long col = DTS_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       ds = OFFSET_PIXEL_PTR(ds, -c);
	       dd = OFFSET_PIXEL_PTR(dd, -c);
	    }

	    c = *s++;
         }

         /* Clip right.  */
         while (!RLE_IS_EOL(c)) {
	    if (c > 0)
	       s += c;
	    c = *s++;
         }

next_line:{}
      }
   }
   else {
      for (y = 0; y < h; y++) {
         PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
         PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line2;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), c--) {
		     unsigned long col = DTS_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
		     unsigned long col = DTS_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       ds = OFFSET_PIXEL_PTR(ds, -c);
	       dd = OFFSET_PIXEL_PTR(dd, -c);
	    }

	    c = *s++;
         }

         /* Clip right.  */
         while (!RLE_IS_EOL(c)) {
	    if (c > 0)
	       s += c;
	    c = *s++;
         }

next_line2:{}
      }
   }

   bmp_unwrite_line(dst);
}



#if (PP_DEPTH != 8) && (PP_DEPTH != 32)

/* _linear_draw_trans_rgba_rle_sprite:
 *  Draws a translucent RGBA RLE sprite onto a linear bitmap.
 */
void FUNC_LINEAR_DRAW_TRANS_RGBA_RLE_SPRITE(BITMAP *dst, AL_CONST RLE_SPRITE *src, int dx, int dy)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   uint32_t *s;
   RGBA_BLENDER blender;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   blender = MAKE_RGBA_BLENDER();
   s = (uint32_t *) (src->dat);

   /* Clip top.  */
   for (y = sybeg - 1; y >= 0; y--) {
      long c = *s++;

      while (c != MASK_COLOR_32) {
	 if (c > 0)
	    s += c;
	 c = *s++;
      }
   }

   bmp_select(dst);

   /* Visible part.  */
   if (sxbeg || dx+src->w >= dst->cr) {
      for (y = 0; y < h; y++) {
         PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
         PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Clip left.  */
         for (x = sxbeg; x > 0; ) {
	    if (c == MASK_COLOR_32)
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully clipped.  */
	          x -= c;
	          s += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c -= x;
	          s += x;
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       if ((x + c) >= 0) {
	          /* Fully clipped.  */
	          x += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c += x;
	          break;
	       }
	    }

	    c = *s++;
         }

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (c == MASK_COLOR_32)
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), c--) {
		     unsigned long col = RGBA_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
		     unsigned long col = RGBA_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       ds = OFFSET_PIXEL_PTR(ds, -c);
	       dd = OFFSET_PIXEL_PTR(dd, -c);
	    }

	    c = *s++;
         }

         /* Clip right.  */
         while (c != MASK_COLOR_32) {
	    if (c > 0)
	       s += c;
	    c = *s++;
         }

      next_line:{}
      }
   }
   else {
      for (y = 0; y < h; y++) {
         PIXEL_PTR ds = OFFSET_PIXEL_PTR(bmp_read_line(dst, dybeg + y), dxbeg);
         PIXEL_PTR dd = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (c == MASK_COLOR_32)
	       goto next_line2;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), c--) {
		     unsigned long col = RGBA_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(ds), INC_PIXEL_PTR(dd), x--) {
		     unsigned long col = RGBA_BLEND(blender, GET_PIXEL(ds), *s);
		     PUT_PIXEL(dd, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       ds = OFFSET_PIXEL_PTR(ds, -c);
	       dd = OFFSET_PIXEL_PTR(dd, -c);
	    }

	    c = *s++;
         }

      next_line2:{}
      }
   }

   bmp_unwrite_line(dst);
}

#endif



/* _linear_draw_lit_rle_sprite:
 *  Draws a tinted RLE sprite onto a linear bitmap.
 */
void FUNC_LINEAR_DRAW_LIT_RLE_SPRITE(BITMAP *dst, AL_CONST RLE_SPRITE *src, int dx, int dy, int color)
{
   int x, y, w, h;
   int dxbeg, dybeg;
   int sxbeg, sybeg;
   RLE_PTR s;
   DLS_BLENDER blender;

   ASSERT(dst);
   ASSERT(src);

   if (dst->clip) {
      int tmp;

      tmp = dst->cl - dx;
      sxbeg = ((tmp < 0) ? 0 : tmp);
      dxbeg = sxbeg + dx;

      tmp = dst->cr - dx;
      w = ((tmp > src->w) ? src->w : tmp) - sxbeg;
      if (w <= 0)
	 return;

      tmp = dst->ct - dy;
      sybeg = ((tmp < 0) ? 0 : tmp);
      dybeg = sybeg + dy;

      tmp = dst->cb - dy;
      h = ((tmp > src->h) ? src->h : tmp) - sybeg;
      if (h <= 0)
	 return;
   }
   else {
      w = src->w;
      h = src->h;
      sxbeg = 0;
      sybeg = 0;
      dxbeg = dx;
      dybeg = dy;
   }

   blender = MAKE_DLS_BLENDER(color);
   s = (RLE_PTR) (src->dat);

   /* Clip top.  */
   for (y = sybeg - 1; y >= 0; y--) {
      long c = *s++;

      while (!RLE_IS_EOL(c)) {
	 if (c > 0)
	    s += c;
	 c = *s++;
      }
   }

   bmp_select(dst);

   /* Visible part.  */
   if (sxbeg || dx+src->w >= dst->cr) {
      for (y = 0; y < h; y++) {
         PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Clip left.  */
         for (x = sxbeg; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully clipped.  */
	          x -= c;
	          s += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c -= x;
	          s += x;
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       if ((x + c) >= 0) {
	          /* Fully clipped.  */
	          x += c;
	       }
	       else {
	          /* Visible on the right.  */
	          c += x;
	          break;
	       }
	    }

	    c = *s++;
         }

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(d), c--) {
		     unsigned long col = DLS_BLEND(blender, color, *s);
		     PUT_PIXEL(d, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
		     unsigned long col = DLS_BLEND(blender, color, *s);
		     PUT_PIXEL(d, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       d = OFFSET_PIXEL_PTR(d, -c);
	    }

	    c = *s++;
         }

         /* Clip right.  */
         while (!RLE_IS_EOL(c)) {
	    if (c > 0)
	       s += c;
	    c = *s++;
         }

next_line:{}

      }
   }
   else {
      for (y = 0; y < h; y++) {
         PIXEL_PTR d = OFFSET_PIXEL_PTR(bmp_write_line(dst, dybeg + y), dxbeg);
         long c = *s++;

         /* Visible part.  */
         for (x = w; x > 0; ) {
	    if (RLE_IS_EOL(c))
	       goto next_line2;
	    else if (c > 0) {
	       /* Run of solid pixels.  */
	       if ((x - c) >= 0) {
	          /* Fully visible.  */
	          x -= c;
	          for (c--; c >= 0; s++, INC_PIXEL_PTR(d), c--) {
		     unsigned long col = DLS_BLEND(blender, color, *s);
		     PUT_PIXEL(d, col);
	          }
	       }
	       else {
	          /* Clipped on the right.  */
	          c -= x;
	          for (x--; x >= 0; s++, INC_PIXEL_PTR(d), x--) {
		     unsigned long col = DLS_BLEND(blender, color, *s);
		     PUT_PIXEL(d, col);
	          }
	          break;
	       }
	    }
	    else {
	       /* Run of transparent pixels.  */
	       x += c;
	       d = OFFSET_PIXEL_PTR(d, -c);
	    }

	    c = *s++;
         }

      next_line2:{}
      }
   }

   bmp_unwrite_line(dst);
}

#endif /* !__bma_cspr_h */

