/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = EDDSCNLR                                       */
/*                                                                    */
/*   Description     = Display Device Driver function ScanLR          */
/*                                                                    */
/*   Function        = Scan to the left and\or right to find the      */
/*                     first pel that matches the colour if the       */
/*                     LR_BORDER option is specified or that does     */
/*                     not match the colour if the LR_NOTBORDER       */
/*                     option is specified.                           */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#define INCL_GRE_CLIP
#define INCL_GRE_BITMAPS
#include <eddinclt.h>

#ifdef FLOOD_FILL
#include <eddhcone.h>

#include <edddtypt.h>
#include <eddhtype.h>

#include <eddcextf.h>
#include <eddgextf.h>

#include <eddhmacr.h>

#include <hwaccess.h>

#ifdef _8514
#include <8514.h>
#endif

#define PBITMAPHEADER         pBitmapHeader
#define bm_header             pdc->DCISelListEntry
#define GCR_ERROR             (-1)
#define MOST_SIGNIFICANT_BIT  7
#define LEAST_SIGNIFICANT_BIT 0
#define MIN_COORD             0xf800
#define MAX_COORD             0x07ff


/* globals */
extern PPFNL               EnginesDispatchTable;
extern RECTL               ClipBounds;
extern RGNRECT             ClipControl;
#ifndef   _8514
extern MMReg               ShadowXGARegs;
#else
extern MM8514Reg           Shadow8514Regs;
extern VOID CopyVRAMToMemory( PVOID, ULONG, ULONG, ULONG, ULONG);
extern ULONG Screen8514VisWidth;
#endif
extern ULONG               pPhunkPhys;
extern PBYTE               pPhunkVirt;
extern BitmapHeader        DirectListEntry;
extern SHORT               softDrawInUse;


/* external functions */
LONG DRIVERCALL bit_scan_forward(ULONG scanbyte);
LONG DRIVERCALL bit_scan_reverse(ULONG scanbyte);
LONG DRIVERCALL scan8_whole_bytes(PBYTE  start_address,
                                  ULONG  bytes_to_scan,
                                  ULONG  options,
                                  ULONG  colour);
LONG DRIVERCALL scan1_whole_bytes(PBYTE  start_address,
                                  ULONG  pels_to_scan,
                                  ULONG  options,
                                  ULONG  colour);
LONG DRIVERCALL scan4_whole_bytes(PBYTE  start_address,
                                  ULONG  pels_to_scan,
                                  ULONG  options,
                                  ULONG  colour);
LONG DRIVERCALL scan16(PBYTE  start_address,
                       ULONG  pixels_to_scan,
                       ULONG  options,
                       ULONG  colour);
#ifdef BPP24
LONG DRIVERCALL scan24(PBYTE  start_address,
                       ULONG  pixels_to_scan,
                       ULONG  options,
                       ULONG  colour);
#endif



#ifndef   _8514

/**********************************************************************/
/* Copies the scanline screen_line from the screen into system memory.*/
/**********************************************************************/
VOID DRIVERCALL CopyScanlineToPhunk(ULONG screen_line)
{
    /*******************************************************************/
    /* Destination is the Phunk.                                       */
    /*******************************************************************/
    SHORT tempDrawMode;

    ShadowXGARegs.PixMapBaseA   = pPhunkPhys;
    ShadowXGARegs.PixMapWidthA  = DirectListEntry.Info.HWWidth;
    ShadowXGARegs.PixMapHeightA = 0;
    ShadowXGARegs.PixMapFormatA = (BYTE)DirectListEntry.Info.HWFormat;

    /*******************************************************************/
    /* Source is the screen.                                           */
    /*******************************************************************/
    ShadowXGARegs.PixMapBaseB   = DirectListEntry.BMPhys;
    ShadowXGARegs.PixMapWidthB  = DirectListEntry.Info.HWWidth;
    ShadowXGARegs.PixMapHeightB = DirectListEntry.Info.HWHeight;
    ShadowXGARegs.PixMapFormatB = (BYTE)DirectListEntry.Info.HWFormat;

    ShadowXGARegs.DstXAddr =
    ShadowXGARegs.DstYAddr =
    ShadowXGARegs.SrcXAddr = 0;
    ShadowXGARegs.SrcYAddr = (USHORT)screen_line;

    /*******************************************************************/
    /* We want a straight copy done.                                   */
    /*******************************************************************/
    ShadowXGARegs.FgMix = HWMIX_SOURCE;
    ShadowXGARegs.BgMix = HWMIX_SOURCE;
    ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;

    ShadowXGARegs.OpDim1 = DirectListEntry.Info.HWWidth;
    ShadowXGARegs.OpDim2 = 0;

    ShadowXGARegs.PixOp = BACK_SRC_SRC_PIX_MAP |
                          FORE_SRC_SRC_PIX_MAP |
                          STEP_PXBLT           |
                          SRC_PIX_MAP_B        |
                          DST_PIX_MAP_A        |
                          PAT_PIX_MAP_FORE     |
                          MASK_PIX_MAP_OFF     |
                          DRAW_MODE_DONTCARE   |
                          DIR_OCTANT_LRTB;

    /******************************************************************/
    /* Now actually set the blt off. We must do this blt using the    */
    /* hardware even if we are in softdraw mode.                      */
    /******************************************************************/
    tempDrawMode = softDrawInUse;
    softDrawInUse = FALSE;
    TransferShadowRegisters( TSR_MAP_A       |
                             TSR_MAP_B       |
                             TSR_COLOUR_MIX  |
                             TSR_COORDINATES |
                             TSR_PIXELOP );
    softDrawInUse = tempDrawMode;
    WaitForRealHW

} /* CopyScanlineToPhunk */

#endif


/**********************************************************************/
/* Returns the address of the ptlStart pel.                           */
/*                                                                    */
/* For a screen scanline the scanline is first copied into main       */
/* memory.                                                            */
/**********************************************************************/
PBYTE DRIVERCALL GetAddressOfPoint(POINTL ptlStart)
{
    PBYTE scan_line_address;

    if ( pdc->DCIDCType == OD_DIRECT )
    {
        /**************************************************************/
        /* We must copy the scanline from the screen to the Phunk     */
        /* so that we can access it.                                  */
        /**************************************************************/
#ifndef _8514
        CopyScanlineToPhunk(ptlStart.y);
#else
        ULONG   VRAMptr;

        #ifndef   S3
        VRAMptr = ( ptlStart.y );
        CopyVRAMToMemory(pPhunkVirt,VRAMptr,SCR_8514_WIDTH-1,0,EIGHT_BPP);
        #else
        VRAMptr = ( ptlStart.y );
        CopyVRAMToMemory(pPhunkVirt,VRAMptr,SCR_S3_WIDTH-1,0,DirectListEntry.Info.HWFormat);
        #endif
#endif
        scan_line_address = pPhunkVirt;
    }
    else /* off-screen bitmap */
    {
        /**************************************************************/
        /* Calculate the address of the scanline in memory.           */
        /**************************************************************/
        scan_line_address = bm_header->Bitmap +
                            ptlStart.y * bm_header->BytesPerLine;
    }

    return(scan_line_address +
           ((ptlStart.x * bm_header->Info.BitCount) / 8));

} /* GetAddressOfPoint */


/**********************************************************************/
/* Puts the the clip rectangles defined by ClipControl and bounded by */
/* ClipBounds into clip_rect.                                         */
/*                                                                    */
/* Returns TRUE if no error occured, otherwise FALSE.                 */
/* A TRUE return value also occurs if no rectangles were found.       */
/**********************************************************************/
BOOL DRIVERCALL get_clip_rectangle(PRECTL clip_rect)
{
    if ( !EnginesDispatchTable[NGreGetClipRects & 0xff](
                                      pdc->DCIhdc,
                                      &ClipBounds,
                                      &ClipControl,
                                      clip_rect,
                                      0,
                                      NGreGetClipRects
                                      ) )
    {
        /**************************************************************/
        /* The engine returned an error.                              */
        /**************************************************************/
        return(FALSE);
    }

    return (TRUE);
} /* get_clip_rectangle */


/**********************************************************************/
/* Initialises the global data structures for finding a clip          */
/* rectangle to the right of the ptlStart.                            */
/**********************************************************************/
VOID DRIVERCALL clip_right_setup(POINTL ptlStart)
{

    ClipBounds.xLeft   = ptlStart.x;
    ClipBounds.xRight  = bm_header->Info.Width;
    ClipBounds.yBottom = pdc->DCIConvFactor - ptlStart.y +
                                                       pdc->DCIOrigin.Y;
    ClipBounds.yTop    = ClipBounds.yBottom + 1;

    ClipControl.ircStart = 1;
    ClipControl.crc = 1;

} /* clip_right_setup */



/**********************************************************************/
/* finds the leftmost x coordinate inside the leftmost clip rectangle */
/* (if any) to the right of ptlStart - if none, MAX_COORD is returned */
/**********************************************************************/
LONG DRIVERCALL first_clip_right(POINTL ptlStart)
{
    RECTL clip_rect;

    /******************************************************************/
    /* Set up the GetClipsRects structure.                            */
    /******************************************************************/
    clip_right_setup(ptlStart);

    ClipControl.ulDirection = RECTDIR_LFRT_TOPBOT;

    if ( get_clip_rectangle(&clip_rect) == TRUE )
    {
        if ( ClipControl.crcReturned == 0)
        {
            /**********************************************************/
            /* There was no clip rectangle to the right.              */
            /**********************************************************/
            return (MAX_COORD);
        }
        else
        {
            /**********************************************************/
            /* We found a clip rectangle so return the left extreme   */
            /* of that rectangle.                                     */
            /**********************************************************/
            return (clip_rect.xLeft);
        }
    }
    else /* get_clip_rectangle error */
    {
        return(GCR_ERROR);
    }

} /* first_clip_right */


/**********************************************************************/
/* Finds the rightmost x coordinate inside the rightmost clip         */
/* rectangle (if any) to the right of ptlStart                        */
/**********************************************************************/
LONG DRIVERCALL last_clip_right(POINTL ptlStart)
{
    RECTL clip_rect;

    /******************************************************************/
    /* Set up the GetClipsRects structure.                            */
    /******************************************************************/
    clip_right_setup(ptlStart);

    ClipControl.ulDirection = RECTDIR_RTLF_TOPBOT;

    if ( get_clip_rectangle(&clip_rect) == TRUE )
    {
        if ( ClipControl.crcReturned == 0)
        {
            /**********************************************************/
            /* There was no clip rectangle to the right.              */
            /**********************************************************/
            return(ptlStart.x);
        }
        else
        {
            /**********************************************************/
            /* Return the point in the rightmost clip rectangle.      */
            /**********************************************************/
            return ( clip_rect.xRight - 1 );
        }
    }
    else /* get_clip_rectangle error */
    {
        return(GCR_ERROR);
    }

} /* last_clip_right */


/**********************************************************************/
/* Initialises the global data structures for finding a clip          */
/* rectangle to the left of the ptlStart                              */
/**********************************************************************/
VOID DRIVERCALL clip_left_setup(POINTL ptlStart)
{

    ClipBounds.xLeft   = 0;
    ClipBounds.xRight  = ptlStart.x+1;
    ClipBounds.yBottom = pdc->DCIConvFactor - ptlStart.y +
                                                    pdc->DCIOrigin.Y;
    ClipBounds.yTop    = ClipBounds.yBottom + 1;

    ClipControl.ircStart = 1;
    ClipControl.crc      = 1;

} /* clip_left_setup */


/**********************************************************************/
/* Finds the rightmost x coordinate inside the rightmost clip         */
/* rectangle (if any) to the left of ptlStart - if none, MIN_COORD is */
/* returned                                                           */
/**********************************************************************/
LONG DRIVERCALL first_clip_left(POINTL ptlStart)
{
    RECTL clip_rect;

    /******************************************************************/
    /* Set up the GetClipsRects structure.                            */
    /******************************************************************/
    clip_left_setup(ptlStart);

    ClipControl.ulDirection = RECTDIR_RTLF_TOPBOT;

    if ( get_clip_rectangle(&clip_rect) == TRUE )
    {
        if ( ClipControl.crcReturned == 0)
        {
            /**********************************************************/
            /* There was no clip rectangle to the left.               */
            /**********************************************************/
            return (MIN_COORD);
        }
        else
        {
            /**********************************************************/
            /* We found a clip rectangle so return the right extreme  */
            /* of that rectangle.  (with adjustment because right     */
            /* edge of clip rectangle is exclusive).                  */
            /**********************************************************/
            return ( clip_rect.xRight - 1 );
        }
    }
    else /* get_clip_rectangle error */
    {
        return(GCR_ERROR);
    }

} /* first_clip_left */


/**********************************************************************/
/* Finds the leftmost x coordinate inside the leftmost clip rectangle */
/* (if any) to the left of ptlStart                                   */
/**********************************************************************/
LONG DRIVERCALL last_clip_left(POINTL ptlStart)
{
    RECTL clip_rect;

    /******************************************************************/
    /* Set up the GetClipsRects structure.                            */
    /******************************************************************/
    clip_left_setup(ptlStart);

    ClipControl.ulDirection = RECTDIR_LFRT_TOPBOT;

    if ( get_clip_rectangle(&clip_rect) == TRUE )
    {
        if ( ClipControl.crcReturned == 0)
        {
            /**********************************************************/
            /* There was no clip rectangle to the left of this point. */
            /**********************************************************/
            return(ptlStart.x);
        }
        else
        {
            /**********************************************************/
            /* Return the left most point.                            */
            /**********************************************************/
            return ( clip_rect.xLeft );
        }
    }
    else /* get_clip_rectangle error */
    {
        return(GCR_ERROR);
    }

} /* last_clip_left */


/**********************************************************************/
/* Sets pxLeft and/or pxRight for a clipped starting point            */
/**********************************************************************/
ULONG DRIVERCALL process_clipped_start_pt(ULONG  direction,
                                          POINTL ptlStart,
                                          PLONG  pxLeft,
                                          PLONG  pxRight)
{

    /******************************************************************/
    /* Process the right direction if required.                       */
    /******************************************************************/
    if ( direction & LR_RIGHT )
    {
        *pxRight = first_clip_right(ptlStart);
        if ( *pxRight == GCR_ERROR )
        {
            return(GPI_ERROR);
        }
    }

    /*******************************************************************/
    /* Process the left direction if required.                         */
    /*******************************************************************/
    if ( direction & LR_LEFT )
    {
        *pxLeft = first_clip_left(ptlStart);
        if ( *pxLeft == GCR_ERROR )
        {
            return(GPI_ERROR);
        }
    }

    /*******************************************************************/
    /* If we got here then we did not hit an error, so return that the */
    /* point was processed as clipped.                                 */
    /*******************************************************************/
    return(LR_CLIPPED);

} /* process_clipped_start_pt */


/**********************************************************************/
/* Returns the value to be scanned for based on the bits per pel,     */
/* the options and the colour supplied.                               */
/*                                                                    */
/* The values returned are not consistent across bpps, but are what   */
/* the lower level routines specifically need.                        */
/**********************************************************************/
ULONG DRIVERCALL value_to_scan_for(ULONG  colour,
                                   ULONG  option)
{
    switch ( bm_header->Info.BitCount)
    {
        case 1:
            if ( option & LR_BORDER )
            {
                return(colour - 1);
            }
            else /* NOTBORDER */
            {
                return(-colour);
            }

        case 4:
            /**********************************************************/
            /* Return a BYTE packed with the same pel colour in both  */
            /* pels in the byte.                                      */
            /**********************************************************/
            return((colour << 4) | colour);

        case 8:
        case 16:
        #ifdef BPP24
        case 24:
        #endif
            /**********************************************************/
            /* Simply return the colour as passed.                    */
            /**********************************************************/
            return(colour);
    }

} /* value_to_scan_for */


/**********************************************************************/
/* num_pels_to_right_scan                                             */
/*                                                                    */
/* Calculates the number of pels that need to be scanned to the       */
/* right of the ptlStart.                                             */
/**********************************************************************/
LONG DRIVERCALL num_pels_to_right_scan(POINTL ptlStart)
{
    LONG lastclipright;

    lastclipright = last_clip_right(ptlStart);

    if (lastclipright == GCR_ERROR)
    {
        return(0);
    }
    else
    {
        return( lastclipright - ptlStart.x + 1 );
    }
} /* num_pels_to_right_scan */


/**********************************************************************/
/* num_pels_to_left_scan                                              */
/*                                                                    */
/* Calculates the number of pels that need to be scanned to the       */
/* left of the ptlStart.                                              */
/**********************************************************************/
LONG DRIVERCALL num_pels_to_left_scan(POINTL ptlStart)
{
    LONG    lastclipleft;

    lastclipleft = last_clip_left(ptlStart);

    if (lastclipleft == GCR_ERROR)
    {
        return(0);
    }
    else
    {
        return( ptlStart.x - lastclipleft + 1 );
    }
} /* num_pels_to_left_scan */


/**********************************************************************/
/* first_byte_bit_offset                                              */
/*                                                                    */
/* Returns the bit position of the first index to be scanned in the   */
/* byte (8bpp or 16bpp always 7), 4bpp 7 or 3, 1bpp 7..0)             */
/**********************************************************************/
LONG DRIVERCALL first_byte_bit_offset(ULONG x_coord)
{
    if (bm_header->Info.BitCount >= 8)
    {
        /**************************************************************/
        /* bpp == 8 or 16 or 24                                       */
        /**************************************************************/
        return MOST_SIGNIFICANT_BIT;
    }
    else if (bm_header->Info.BitCount == 4)
    {
        return( MOST_SIGNIFICANT_BIT - ((x_coord % 2) * 4) );
    }
    else /* if (bm_header->Info.BitCount == 1) */
    {
        return( MOST_SIGNIFICANT_BIT - (x_coord % 8) );
    }
} /* first_byte_bit_offset */


/**********************************************************************/
/* 4 bpp scan of the first byte when one pel in the byte is not       */
/* in the scan - returns -1 if no match, 0 (pel position) if match    */
/**********************************************************************/
LONG DRIVERCALL scan4_first_byte(BYTE   scan_byte,
                                 ULONG  options,
                                 ULONG  value,
                                 ULONG  mask)
{
    /******************************************************************/
    /* First use the mask on the value passed.                        */
    /******************************************************************/
    value &= mask;

    if ( options & LR_BORDER )
    {
        /**************************************************************/
        /* Looking for a bordered area, so values must match.         */
        /**************************************************************/
        if ( (scan_byte & mask) == value )
        {
            return(0);
        }
    }
    else /* LR_NOTBORDER */
    {
        /**************************************************************/
        /* Looking for a inside are so values must not match.         */
        /**************************************************************/
        if ( (scan_byte & mask) != value )
        {
            return(0);
        }
    }
    /******************************************************************/
    /* Value was not found                                            */
    /******************************************************************/
    return(-1);

} /* scan4_first_byte */


/**********************************************************************/
/* 1 bpp scan right of the first byte when the leftmost pel is not    */
/* in the scan - returns -1 if no match, pel position offset from     */
/* bit_offset if match                                                */
/**********************************************************************/
LONG DRIVERCALL scan1_right_first_byte(BYTE   scan_byte,
                                       LONG   bit_offset,
                                       ULONG  value)
{

    LONG  first_match;

    if ( value == 0 )
    {
        first_match = bit_scan_reverse( (BYTE)
                 (scan_byte & (0xff >> (7 - bit_offset))) );
    }
    else /* value = 0xff */
    {
        first_match = bit_scan_reverse( (BYTE)
                  (~scan_byte & (BYTE)(0xff >> (7 - bit_offset))) );
    }

    if ( first_match >= 0 )
    {
        /**************************************************************/
        /* Positive first_match means value was found.  Return the    */
        /* number of pels until the matching one.                     */
        /**************************************************************/
        return(bit_offset - first_match);
    }
    else /* no match found */
    {
        /**************************************************************/
        /* Pass back the -1 value which means not found.              */
        /**************************************************************/
        return(-1);
    }

} /* scan1_right_first_byte */


/**********************************************************************/
/* Does a right scan of the first byte when the entire byte is not to */
/* be scanned (can only apply to 1 and 4 bpp) and returns the pel     */
/* position of the matched bit as an offset from bit_offset (or -1 if */
/* no match)                                                          */
/**********************************************************************/
LONG DRIVERCALL scan_right_first_byte(BYTE   scan_byte,
                                      LONG   bit_offset,
                                      ULONG  options,
                                      ULONG  value)
{

    if ( bm_header->Info.BitCount == 4)
    {
        return( scan4_first_byte(scan_byte,
                                 options,
                                 value,
                                 0x000F) );
    }
    else /* 1 bpp */
    {
        return( scan1_right_first_byte(scan_byte,
                                       bit_offset,
                                       value) );

    }
} /* scan_right_first_byte */


/**********************************************************************/
/* Scans all pels after a (possible) first byte containing less than  */
/* a byte of data.                                                    */
/* Returns the pel offset from the start if found - this can range    */
/* from 0 if the current pel matches to pels_to_scan if no pels       */
/* match.                                                             */
/**********************************************************************/
LONG DRIVERCALL scan_whole_bytes(PBYTE  scan_address,
                                 ULONG  pels_to_scan,
                                 ULONG  options,
                                 LONG   colour)
{
    switch ( bm_header->Info.BitCount )
    {
        #ifdef BPP24
        case 24:
            return( scan24(scan_address,
                           pels_to_scan,
                           options,
                           colour) );
            break;
        #endif
        case 16:
            return( scan16(scan_address,
                           pels_to_scan,
                           options,
                           colour) );
            break;

        case 8:
            return( scan8_whole_bytes(scan_address,
                                      pels_to_scan,
                                      options,
                                      colour) );
            break;

        case 4:
            return( scan4_whole_bytes(scan_address,
                                      pels_to_scan,
                                      options,
                                      colour) );
            break;

        case 1:
            return( scan1_whole_bytes(scan_address,
                                      pels_to_scan,
                                      options,
                                      colour) );
            break;
    }

} /* scan_whole_bytes */


/**********************************************************************/
/* Does a scan to the right of the start point.                       */
/*                                                                    */
/* Returns the number of pels to the right that the scan did. (May be */
/* zero if the first pel matches).                                    */
/* If no match was found then the return value will be pels_to_scan   */
/* which is the first pel which is clipped out.                       */
/**********************************************************************/
LONG DRIVERCALL scan_right(PBYTE  pStartPel,
                           ULONG  first_bit,
                           ULONG  options,
                           ULONG  pels_to_scan,
                           LONG   colour)
{
    LONG        pels_scanned;
    ULONG       pels_left;

    if ( first_bit != MOST_SIGNIFICANT_BIT )
    {
        /**************************************************************/
        /* We have a pel to scan from which is part way into the first*/
        /* byte.                                                      */
        /**************************************************************/

        /**************************************************************/
        /* Do the scan.                                               */
        /**************************************************************/
        pels_scanned = scan_right_first_byte(*pStartPel++,
                                             first_bit,
                                             options,
                                             colour);

        /**************************************************************/
        /* A positive value means we found the required value.        */
        /**************************************************************/
        if ( pels_scanned >= 0 )
        {
            return(pels_scanned);
        }

        /**************************************************************/
        /* Work out how many pels there were in the first byte.       */
        /**************************************************************/
        pels_scanned = (first_bit + 1) / bm_header->Info.BitCount;

        /**************************************************************/
        /* Work out how many pels are left in total.                  */
        /**************************************************************/
        pels_left = pels_to_scan - pels_scanned;

        if ( pels_left == 0 )
        {
            /**********************************************************/
            /* There are no pels left so return the first clipped pel.*/
            /**********************************************************/
            return(pels_to_scan);
        }
    }
    else /* first byte is a full byte of data */
    {
        pels_scanned = 0;
        pels_left = pels_to_scan;
    }

    pels_scanned += scan_whole_bytes(pStartPel,
                                     pels_left,
                                     options,
                                     colour);

    return(pels_scanned);

} /* scan_right */


/**********************************************************************/
/* 1 bpp scan left of the first byte when the rightmost pel is not    */
/* in the scan - returns -1 if no match, pel position offset from     */
/* bit_offset if match                                                */
/**********************************************************************/
LONG DRIVERCALL scan1_left_first_byte(BYTE   scan_byte,
                                      LONG   bit_offset,
                                      LONG   colour)
{

    LONG  first_match;

    if ( colour == 0 )
    {
        first_match = bit_scan_forward( (BYTE)
                       (scan_byte & (0xff << bit_offset)) );
    }

    else /* colour = 0xff */
    {
        first_match = bit_scan_forward( (BYTE)
                        (~scan_byte & (0xff << bit_offset)) );
    }

    if ( first_match >= 0 )
    {
        return(first_match - bit_offset);
    }

    else /* no match found */
    {
        return(first_match);
    }

} /* scan1_left_first_byte */


/**********************************************************************/
/* Does a left scan of the first byte when the entire byte is not to  */
/* be scanned (can only apply to 1 and 4 bpp)and returns the pel      */
/* position of the matched bit as an offset from bit_offset (or -1 if */
/* no match)                                                          */
/**********************************************************************/
LONG DRIVERCALL scan_left_first_byte(BYTE   scan_byte,
                                     LONG   bit_offset,
                                     ULONG  options,
                                     LONG   colour)
{
    if ( bm_header->Info.BitCount == 4)
    {
        return( scan4_first_byte(scan_byte,
                                 options,
                                 colour,
                                 0x00F0) );
    }
    else /* 1 bpp */
    {
        return( scan1_left_first_byte(scan_byte,
                                      bit_offset,
                                      colour) );
    }
} /* scan_left_first_byte */


/**********************************************************************/
/* Does a scan to the left of the start point.                        */
/*                                                                    */
/* Returns the number of pels to the right that the scan did. (May be */
/* zero if the first pel matches).                                    */
/* If no match was found then the return value will be pels_to_scan   */
/* which is the first pel which is clipped out.                       */
/**********************************************************************/
LONG DRIVERCALL scan_left(PBYTE  pStartPel,
                          ULONG  first_bit,
                          ULONG  options,
                          ULONG  pels_to_scan,
                          LONG   colour)
{
    ULONG           pels_per_byte;
    LONG            pels_scanned;
    ULONG           pels_left;

    /******************************************************************/
    /* At 1bpp and 4bpp we may have a first byte to consider.         */
    /******************************************************************/
    if ( (first_bit + 1) > bm_header->Info.BitCount )
    {
        pels_scanned = scan_left_first_byte(*pStartPel--,
                                            first_bit,
                                            options,
                                            colour);

        /**************************************************************/
        /* Positive value means pel was matched.                      */
        /**************************************************************/
        if ( pels_scanned >= 0)
        {
            return(pels_scanned);
        }

        /**************************************************************/
        /* This calculation is OK because we must be 1 or 4 bpp here. */
        /**************************************************************/
        pels_per_byte = 8 / bm_header->Info.BitCount;

        pels_scanned = pels_per_byte -
                                 (first_bit / bm_header->Info.BitCount);

        /**************************************************************/
        /* Calculate number of remaining pels.                        */
        /**************************************************************/
        pels_left = pels_to_scan - pels_scanned;
        if ( pels_left == 0 )
        {
            /**********************************************************/
            /* No pels left means no match found.                     */
            /**********************************************************/
            return(pels_to_scan);
        }
    }
    else /* first byte is a full byte of data */
    {
        pels_scanned = 0;
        pels_left = pels_to_scan;
    }

    pels_scanned += scan_whole_bytes(pStartPel,
                                     pels_left,
                                     options,
                                     colour);

    return(pels_scanned);

} /* scan_left */


/**********************************************************************/
/* Does the scan of the scanline and sets pxLeft and/or pxRight       */
/* depending on the LR_LEFT/LR_RIGHT options                          */
/**********************************************************************/
ULONG DRIVERCALL do_the_scan(ULONG  options,
                             POINTL ptlStart,
                             LONG   colour,
                             PLONG  pxLeft,
                             PLONG  pxRight)
{
    PBYTE   pStartPel;
    LONG    end_x;
    ULONG   scan_value;
    ULONG   first_bit;
    ULONG   ulPelsToScan;

    /******************************************************************/
    /* Adjust the colour into a value to be passed to the lower level */
    /* routines.                                                      */
    /******************************************************************/
    scan_value = value_to_scan_for(colour, options);

    /******************************************************************/
    /* Get the address of the byte holding the start pel.             */
    /******************************************************************/
    pStartPel = GetAddressOfPoint(ptlStart);

    /******************************************************************/
    /* For 1 bpp or 4 bpp we might have a pel which is part way into  */
    /* the start byte.                                                */
    /******************************************************************/
    first_bit = first_byte_bit_offset(ptlStart.x);

    /******************************************************************/
    /* If required do the scan to the right.                          */
    /******************************************************************/
    if ( options & LR_RIGHT )
    {
        ulPelsToScan = num_pels_to_right_scan(ptlStart);
        end_x = scan_right(pStartPel,
                           first_bit,
                           options & ~LR_LEFT,
                           ulPelsToScan,
                           scan_value);

        *pxRight = end_x + ptlStart.x;
    }

    /******************************************************************/
    /* If required do the scan to the left.                           */
    /******************************************************************/
    if ( options & LR_LEFT )
    {
        ulPelsToScan = num_pels_to_left_scan(ptlStart);
        end_x = scan_left( pStartPel,
                           first_bit,
                           options & ~LR_RIGHT,
                           ulPelsToScan,
                           scan_value);

        *pxLeft = (LONG)(ptlStart.x - end_x);
    }

    return(GPI_OK);

} /* do_the_scan */


/**********************************************************************/
/*  DCR 25020 and 25106: Paint packages for PM                        */
/*                                                                    */
/*          ScanLR (HDC     hdc,                                      */
/*                  PPOINTL pPoint,                                   */
/*                  LONG    flCmd,                                    */
/*                  LONG    Colour,                                   */
/*                  PLONG   pxLeft,                                   */
/*                  PLONG   pxRight,                                  */
/*                  PDC     pdcArg,                                   */
/*                  ULONG   FunN)                                     */
/*                                                                    */
/* This searches from pPoint, looking left and/or right depending     */
/* in whether LR_LEFT/LR_RIGHT are set in flCmd.                      */
/*                                                                    */
/* If LR_BORDER is set in flCmd, then ScanLR should return the first  */
/* point (in each direction specified) which matches colour Colour.   */
/* If LR_NOTBORDER is set in flCmd, then ScanLR returns the first     */
/* point which does not match Colour (ie skips over a solid single    */
/* coloured area).                                                    */
/* If the start point is outside the clip region, then ScanLR should  */
/* return LR_CLIPPED, and pxLeft/pxRight contain the nearest points   */
/* in the clip region on the given scanline, (or the leftmost/        */
/* rightmost coord. if the clip  region does not intersect this line) */
/*                                                                    */
/* Note that coordinates are passed to and from this function as      */
/* screen coordinates (ie. 0,0 is bottom left of screen).             */
/**********************************************************************/
DDIENTRY ScanLR(HDC     hdc,
                PPOINTL pPoint,
                LONG    flCmd,
                LONG    Colour,
                PLONG   pxLeft,
                PLONG   pxRight,
                PDC     pdcArg,
                ULONG   FunN)

{
    ULONG       rc;
    POINTL      ptlStart;

    /******************************************************************/
    /* Grab the driver semaphore.                                     */
    /******************************************************************/
    EnterDriver(pdcArg, FunN, EDF_STANDARD);

    /******************************************************************/
    /* Invalid in path or area.                                       */
    /******************************************************************/
    if (FunNTest(COM_AREA | COM_PATH))
    {
        if (FunNTest(COM_AREA))
        {
            rc = PMERR_INV_IN_AREA;
        }
        else
        {
            rc = PMERR_INV_IN_PATH;
        }
        goto SCANLR_LOGERR_EXIT;
    }

    /******************************************************************/
    /* Check for the existance of a bitmap.                           */
    /******************************************************************/
    if ( pdc->DCIBitmapType == BITMAP_NOT_SELECTED )
    {
        rc = PMERR_BITMAP_NOT_SELECTED;
        goto SCANLR_LOGERR_EXIT;
    }

    /******************************************************************/
    /* Convert the start point to XGA coordinates (ie.  so 0,0 is     */
    /* top left of screen).                                           */
    /******************************************************************/
    ptlStart.x = pPoint->x;
    ptlStart.y = pdc->DCIConvFactor - pPoint->y + pdc->DCIOrigin.Y;

    /******************************************************************/
    /* Check to see if the start point is visible. (clipped or not)   */
    /* Processing depends on whether the point is clipped or not.     */
    /******************************************************************/
    rc = EnginesDispatchTable[NGrePtVisible & 0xff](hdc,
                                                    pPoint,
                                                    NULL,
                                                    NGrePtVisible);

    switch (rc)
    {
        case PVIS_INVISIBLE:
            rc = process_clipped_start_pt(flCmd,
                                          ptlStart,
                                          pxLeft,
                                          pxRight);
            break;

        case PVIS_VISIBLE:
            rc = do_the_scan(flCmd,
                             ptlStart,
                             LogToPhyIndex(Colour),
                             pxLeft,
                             pxRight);
            break;

        default:
            /* rc = error */;
    }

    ExitDriver(pdcArg, FunN, EDF_STANDARD);
    return(rc);

    /******************************************************************/
    /* error handler                                                  */
    /******************************************************************/
SCANLR_LOGERR_EXIT:

    LogError(rc);
    ExitDriver(pdcArg, FunN, EDF_STANDARD);
    return(GPI_ERROR);

} /* ScanLR */
#endif /* FLOOD_FILL */
