/****************************************************************************/
/*                                                                          */
/*  The FreeType project -- a free and portable quality TrueType renderer.  */
/*                                                                          */
/*  Copyright 1996-1999 by                                                  */
/*  D. Turner, R.Wilhelm, and W. Lemberg                                    */
/*                                                                          */
/*  blitter.c: Support for blitting of bitmaps with various depth.          */
/*                                                                          */
/****************************************************************************/

#include "blitter.h"


  typedef struct TBlitter_
  {
    int  width;   /* width in pixels of the written area  */
    int  height;  /* height in pixels of the written area */

    int  xread;   /* x position of start point in read area */
    int  yread;   /* y position of start point in read area */

    int  xwrite;  /* x position of start point in write area */
    int  ywrite;  /* y position of start point in write area */

    int  right_clip;   /* amount of right clip               */

    char*  read;       /* top left corner of source map */
    char*  write;      /* top left corner of target map */

    int    read_line;  /* byte increment to go down one row in read area  */
    int    write_line; /* byte increment to go down one row in write area */

    TT_Raster_Map  source;
    TT_Raster_Map  target;

    int  source_depth;
    int  target_depth;

  } TBlitter;


  static
  int  compute_clips( TBlitter*  blit,
                      int        x_offset,
                      int        y_offset )
  {
    int  xmin, ymin, xmax, ymax, width, height, target_width;


    /* perform clipping and setup variables */
    width  = blit->source.width;
    height = blit->source.rows;

    switch ( blit->source_depth )
    {
    case 1:
      width = (width + 7) & -8;
      break;

    case 4:
      width = (width + 1) & -2;
      break;
    }

    xmin = x_offset;
    ymin = y_offset;
    xmax = xmin + width-1;
    ymax = ymin + height-1;

    /* clip if necessary */
    if ( width == 0 || height == 0                ||
         xmax < 0   || xmin >= blit->target.width ||
         ymax < 0   || ymin >= blit->target.rows  )
      return 1;

    /* set up clipping and cursors */
    blit->yread = 0;
    if ( ymin < 0 )
    {
      blit->yread  -= ymin;
      height       += ymin;
      blit->ywrite  = 0;
    }
    else
      blit->ywrite  = ymin;

    if ( ymax >= blit->target.rows )
      height -= ymax - blit->target.rows + 1;

    blit->xread = 0;
    if ( xmin < 0 )
    {
      blit->xread  -= xmin;
      width        += xmin;
      blit->xwrite  = 0;
    }
    else
      blit->xwrite  = xmin;

    target_width = blit->target.width;

    switch ( blit->target_depth )
    {
    case 1:
      target_width = (target_width + 7) & -8;
      break;
    case 4:
      target_width = (target_width + 1) & -2;
      break;
    }

    blit->right_clip = xmax - target_width + 1;
    if ( blit->right_clip > 0 )
      width -= blit->right_clip;
    else
      blit->right_clip = 0;

    blit->width  = width;
    blit->height = height;

    /* set read and write to the top-left corner of the the read */
    /* and write area before clipping.                           */

    blit->read  = (char*)blit->source.bitmap;
    blit->write = (char*)blit->target.bitmap;

    if ( blit->source.flow == TT_Flow_Up )
    {
      blit->read_line = -blit->source.cols;
      blit->read     += (blit->source.rows-1) * blit->source.cols;
    }
    else
      blit->read_line =  blit->source.cols;

    if ( blit->target.flow == TT_Flow_Up )
    {
      blit->write_line = -blit->target.cols;
      blit->write     += (blit->target.rows-1) * blit->target.cols;
    }
    else
      blit->write_line =  blit->target.cols;

    /* now go to the start line. Note that we do not move the   */
    /* x position yet, as this is dependent on the pixel format */
    blit->read  += blit->yread * blit->read_line;
    blit->write += blit->ywrite * blit->write_line;

    return 0;
  }


/**************************************************************************/
/*                                                                        */
/* <Function> blit_bitmap_to_bitmap                                       */
/*                                                                        */
/**************************************************************************/

  static
  void  blit_bitmap_to_bitmap( TBlitter*  blit )
  {
    int             shift, left_clip, x, y;
    unsigned char*  read;
    unsigned char*  write;


    left_clip = ( blit->xread > 0 );
    shift     = ( blit->xwrite - blit->xread ) & 7;

    read  = (unsigned char*)blit->read  + (blit->xread >> 3);
    write = (unsigned char*)blit->write + (blit->xwrite >> 3);

    if ( shift == 0 )
    {
      y = blit->height;
      do
      {
        unsigned char*  _read  = read;
        unsigned char*  _write = write;


        x = blit->width;

        do
        {
          *_write++ |= *_read++;
          x -= 8;
        } while ( x > 0 );

        read  += blit->read_line;
        write += blit->write_line;
        y--;
      } while ( y > 0 );
    }
    else
    {
      int  first, last, count;


      first = blit->xwrite >> 3;
      last  = (blit->xwrite + blit->width-1) >> 3;

      count = last - first;

      if ( blit->right_clip )
        count++;

      y = blit->height;

      do
      {
        unsigned char*  _read  = read;
        unsigned char*  _write = write;
        unsigned char   old;
        int             shift2 = (8-shift);


        if ( left_clip )
          old = (*_read++) << shift2;
        else
          old = 0;

        x = count;
        while ( x > 0 )
        {
          unsigned char val;


          val = *_read++;
          *_write++ |= ( (val >> shift) | old );
          old = val << shift2;
          x--;
        }

        if ( !blit->right_clip )
          *_write |= old;

        read  += blit->read_line;
        write += blit->write_line;
        y--;

      } while ( y > 0 );
    }
  }


/**************************************************************************/
/*                                                                        */
/* <Function> blit_bitmap_to_pixmap8                                      */
/*                                                                        */
/**************************************************************************/

  static
  void  blit_bitmap_to_pixmap8( TBlitter*      blit,
                                unsigned char  color )
  {
    int             x, y;
    unsigned int    left_mask;
    unsigned char*  read;
    unsigned char*  write;


    read  = (unsigned char*)blit->read  + (blit->xread >> 3);
    write = (unsigned char*)blit->write +  blit->xwrite;

    left_mask = 0x80 >> (blit->xread & 7);

    y = blit->height;
    do
    {
      unsigned char*  _read  = read;
      unsigned char*  _write = write;
      unsigned int    mask   = left_mask;
      unsigned int    val    = 0;


      x = blit->width;
      do
      {
        if ( mask == 0x80 )
          val = *_read++;

        if ( val & mask )
          *_write = (unsigned char)color;

        mask >>= 1;
        if ( mask == 0 )
          mask = 0x80;

        _write++;
        x--;
      } while ( x > 0 );

      read  += blit->read_line;
      write += blit->write_line;
      y--;
    } while ( y > 0 );
  }


/**************************************************************************/
/*                                                                        */
/* <Function> blit_bitmap_to_pixmap4                                      */
/*                                                                        */
/**************************************************************************/

  static
  void  blit_bitmap_to_pixmap4( TBlitter*      blit,
                                unsigned char  color )
  {
    int             x, y, phase;
    unsigned int    left_mask;
    unsigned char*  read;
    unsigned char*  write;


    color = color & 15;

    read  = (unsigned char*)blit->read  + (blit->xread >> 3);
    write = (unsigned char*)blit->write + (blit->xwrite >> 1);

    /* now begin blit */
    left_mask = 0x80 >> (blit->xread & 7);
    phase     = blit->xwrite & 1;

    y = blit->height;
    do
    {
      unsigned char*  _read  = read;
      unsigned char*  _write = write;
      unsigned int    mask   = left_mask;
      int             _phase = phase;
      unsigned int    val    = 0;

      x = blit->width;
      do
      {
        if ( mask == 0x80 )
          val = *_read++;

        if ( val & mask )
        {
          if ( _phase )
            *_write = (*_write & 0xF0) | color;
          else
            *_write = (*_write & 0x0F) | (color << 4);
        }

        mask >>= 1;
        if ( mask == 0 )
          mask = 0x80;

        _write += _phase;
        _phase ^= 1;
        x--;
      } while ( x > 0 );

      read  += blit->read_line;
      write += blit->write_line;
      y--;
    } while ( y > 0 );
  }


/**************************************************************************/
/*                                                                        */
/* <Function> blit_bitmap_to_pixmap16                                     */
/*                                                                        */
/**************************************************************************/

  static
  void  blit_bitmap_to_pixmap16( TBlitter*       blit,
                                 unsigned short  color )
  {
    int              x, y;
    unsigned int     left_mask;
    unsigned char*   read;
    unsigned short*  write;


    read  = (unsigned char*)blit->read + (blit->xread >> 3);
    write = (unsigned short*)(blit->write + blit->xwrite*2);

    left_mask = 0x80 >> (blit->xread & 7);

    y = blit->height;
    do
    {
      unsigned char*   _read  = read;
      unsigned short*  _write = write;
      unsigned int     mask   = left_mask;
      unsigned int     val    = 0;

      x = blit->width;
      do
      {
        if ( mask == 0x80 )
          val = *_read++;

        if ( val & mask )
          *_write = color;

        mask >>= 1;
        if ( mask == 0 )
          mask = 0x80;

        _write++;
        x--;
      } while ( x > 0 );

      read  += blit->read_line;
      write += blit->write_line;
      y--;
    } while ( y > 0 );
  }


  /* <Function>                                                          */
  /*    Blit_Bitmap                                                      */
  /*                                                                     */
  /* <Description>                                                       */
  /*    blit a source bitmap to a target bitmap or pixmap                */
  /*                                                                     */
  /* <Input>                                                             */
  /*    target       :: target bitmap or pixmap                          */
  /*    source       :: source bitmap (depth must be 1)                  */
  /*    target_depth :: pixel bit depth of target map                    */
  /*    x_offset     :: horizontal offset of source in target            */
  /*    y_offset     :: vertical offset of source in target              */
  /*    color        :: color to use when blitting to color pixmap       */
  /*                                                                     */
  /* <Return>                                                            */
  /*    error code. 0 means success                                      */
  /*                                                                     */
  /* <Note>                                                              */
  /*    an error occurs when the target bit depth isn't supported, or    */
  /*    if the source's bit depth isn't 1.                               */
  /*                                                                     */
  /*    the offsets are relative to the top-left corner of the target    */
  /*    map. Positive y are downwards.                                   */
  /*                                                                     */
  extern
  int  Blit_Bitmap( TT_Raster_Map*  target,
                    TT_Raster_Map*  source,
                    int             target_depth,
                    int             x_offset,
                    int             y_offset,
                    int             color )
  {
    TBlitter  blit;


    if ( !target || !source )
      return -1;

    blit.source       = *source;
    blit.target       = *target;
    blit.source_depth = 1;
    blit.target_depth = target_depth;

    /* set up blitter and compute clipping. Return immediately if needed */
    if ( compute_clips( &blit, x_offset, y_offset ) )
      return 0;

    /* now perform the blit */
    switch ( target_depth )
    {
    case 1:
      blit_bitmap_to_bitmap( &blit );
      break;

    case 4:
      blit_bitmap_to_pixmap4( &blit, (unsigned char)color );
      break;

    case 8:
      blit_bitmap_to_pixmap8( &blit, (unsigned char)color );
      break;

    case 16:
      blit_bitmap_to_pixmap16( &blit, (unsigned short)color );
      break;

    default:
      return -2;
    }

    return 0;
  }


/* End */
