
#define CLIP_TOP        1
#define CLIP_BOTTOM     2
#define CLIP_RIGHT      4
#define CLIP_LEFT       8


static int _L1OutCode( clip_t *clip, int x, int y )
/*=================================

    Verify that a point is inside or outside the active viewport.   */
{
    int   flag;

    flag = 0;
    if( x < clip->xmin ) {
        flag |= CLIP_LEFT;
    } else if( x > clip->xmax ) {
        flag |= CLIP_RIGHT;
    }
    if( y < clip->ymin ) {
        flag |= CLIP_TOP;
    } else if( y > clip->ymax ) {
        flag |= CLIP_BOTTOM;
    }
    return( flag );
}


static void line_inter( int * x1, int* y1, int x2, int y2, int x )
/*===========================================================================

    Find the intersection of a line with a boundary of the viewport.
    (x1, y1) is outside and ( x2, y2 ) is inside the viewport.
    NOTE : the signs of denom and ( x - *x1 ) cancel out during division
           so make both of them positive before rounding.   */
{
    int            numer;
    int            denom;

    denom = abs( x2 - *x1 );
    numer = 2L * (long)( y2 - *y1 ) * abs( x - *x1 );
    if( numer > 0 ) {
        numer += denom;                     /* round to closest pixel   */
    } else {
        numer -= denom;
    }
    *y1 += numer / ( denom << 1 );
    *x1 = x;
}


int LineClip( clip_t *clip, int *x1, int *y1, int *x2, int *y2 )
/*=============================================================

    Clips the line with end points (x1,y1) and (x2,y2) to the active
    viewport using the Cohen-Sutherland clipping algorithm. Return the
    clipped coordinates and a decision drawing flag.    */
{
    int    flag1;
    int    flag2;

    flag1 = _L1OutCode( clip, *x1, *y1 );
    flag2 = _L1OutCode( clip, *x2, *y2 );
    for( ;; ) {
        if( flag1 & flag2 ) break;                  /* trivially outside    */
        if( flag1 == flag2 ) break;                 /* completely inside    */
        if( flag1 == 0 ) {                          /* first point inside   */
            if( flag2 & CLIP_TOP ) {
                line_inter( y2, x2, *y1, *x1, clip->ymin );
            } else if( flag2 & CLIP_BOTTOM ) {
                line_inter( y2, x2, *y1, *x1, clip->ymax );
            } else if( flag2 & CLIP_RIGHT ) {
                line_inter( x2, y2, *x1, *y1, clip->xmax );
            } else if( flag2 & CLIP_LEFT ) {
                line_inter( x2, y2, *x1, *y1, clip->xmin );
            }
            flag2 = _L1OutCode( clip, *x2, *y2 );
        } else {                                    /* second point inside  */
            if( flag1 & CLIP_TOP ) {
                line_inter( y1, x1, *y2, *x2, clip->ymin );
            } else if( flag1 & CLIP_BOTTOM ) {
                line_inter( y1, x1, *y2, *x2, clip->ymax );
            } else if( flag1 & CLIP_RIGHT ) {
                line_inter( x1, y1, *x2, *y2, clip->xmax );
            } else if( flag1 & CLIP_LEFT ) {
                line_inter( x1, y1, *x2, *y2, clip->xmin );
            }
            flag1 = _L1OutCode( clip, *x1, *y1 );
        }
    }
    return( flag1 & flag2 );
}


static void block_inter( clip_t *clip, int *x, int *y, int flag )
/*======================================================

    Find the intersection of a block with a boundary of the viewport.   */
{
    if( flag & CLIP_TOP ) {
        *y = clip->ymin;
    } else if( flag & CLIP_BOTTOM ) {
        *y = clip->ymax;
    } else if( flag & CLIP_RIGHT ) {
        *x = clip->xmax;
    } else if( flag & CLIP_LEFT ) {
        *x = clip->xmin;
    }
}


int BlockClip(clip_t *clip, int *x1, int *y1, int *x2, int* y2 )
/*==============================================================

    Clip a block with opposite corners (x1,y1) and (x2,y2) to the
    active viewport based on the Cohen-Sutherland algorithm for line
    clipping. Return the clipped coordinates and a decision drawing
    flag ( 0 draw : 1 don't draw ). */
{
    int  flag1;
    int  flag2;

    flag1 = _L1OutCode( clip, *x1, *y1 );
    flag2 = _L1OutCode( clip, *x2, *y2 );
    for( ;; ) {
        if( flag1 & flag2 ) break;                  /* trivially outside    */
        if( flag1 == flag2 ) break;                 /* completely inside    */
        if( flag1 == 0 ) {
            block_inter( clip, x2, y2, flag2 );
            flag2 = _L1OutCode( clip,  *x2, *y2 );
        } else {
            block_inter( clip, x1, y1, flag1 );
            flag1 = _L1OutCode( clip, *x1, *y1 );
        }
    }
    return( flag1 & flag2 );
}


int blit_clip(clip_t *dst_clip,int *dst_x,int *dst_y,
              clip_t *src_clip,int *src_x, int *src_y,
              int *w, int *h)
{
    int sx0, sy0, sx1, sy1;

    sx0 = *src_x;
    sy0 = *src_y;

    sx1 = sx0 + *w - 1;
    sy1 = sy0 + *h - 1;


    if( ! BlockClip( src_clip, &sx0, &sy0, &sx1, &sy1))
    {
      int dx0, dy0, dx1, dy1;

      dx0 = *dst_x + sx0 - *src_x;
      dy0 = *dst_y + sy0 - *src_y;

      dx1 = dx0 + sx1 - sx0;
      dy1 = dy0 + sy1 - sy0;

      if( ! BlockClip( dst_clip, &dx0, &dy0, &dx1, &dy1))
      {
         *w = dx1 - dx0 + 1;
         *h = dy1 - dy0 + 1;

         *src_x += dx0 - *dst_x;
         *src_y += dy0 - *dst_y;

         *dst_x = dx0;
         *dst_y = dy0;

         return 0;
      };
      return 1;
    }
    return 1;
};

