/*
**  VLab hardware control stuff.
**  © 2002 Kakace Productions
**
*/


#ifndef  VLAB_CONTROL_H
#include "VLab_Control.h"
#endif


#include <proto/exec.h>
#include <proto/expansion.h>
#include <proto/mathieeedoubtrans.h>
#include <proto/mathieeesingbas.h>
#include <proto/mathieeesingtrans.h>


/*------------------------------------------------------------------------------------------------*/


struct ExpansionBase *ExpansionBase;

struct VLabInfos VLabPrivate;


/*
** Semaphore, as used by the original vlab.library
*/

static struct VLabSemaphore {
    struct SignalSemaphore semaphore;
    UWORD   count;
    UBYTE   name[23];
    UBYTE   flag;
} *PublicSem, *PrivateSem;


/*
** Init stream (SAA9051)
*/

static UBYTE initStream[8] = {
    0x8A,                       /* SAA9051 ID                   */
    0x00,                       /* Write the first register     */
    0x64,                       /* IDEL      = -14.800 µs       */
    0x35,                       /* HSY Start =  -3.922 µs       */
    0x0A,                       /* HSY Stop  =  -1.184 µs       */
    0xF8,                       /* HC Start  =  +0.592 µs       */
    0xCA,                       /* HC Stop   =  +3.996 µs       */
    0xEC                        /* HS Start  =  -5.920 µs       */
};


/*
** SAA9051 registers.
*/

#define SAA9051_IDEL            0x00    /* Increment delay */
#define SAA9051_HSY_Start       0x01
#define SAA9051_HSY_Stop        0x02
#define SAA9051_HC_Start        0x03
#define SAA9051_HC_Stop         0x04
#define SAA9051_HS_Start        0x05
#define SAA9051_HorPeaking      0x06    /* BY    PF    BP2   BP1   COR2  COR1  AP2   AP1   */
#define SAA9051_HueControl      0x07    /* A77   A76   A75   A74   A73   A72   A71   A70   */
#define SAA9051_Control_1       0x08    /* HPLL  FS    VTR   CO    ALT   YPN   CCFR1 CCFR0 */
#define SAA9051_Control_2       0x09    /* VNL   OEY   OEC   X     CI    AFCC  SS1   SS0   */
#define SAA9051_Control_3       0x0A    /* SYC   CT    YC    SS3   SS2   YDL2  YDL1  YDL0  */
#define SAA9051_SECAM_Delay     0x0B    /* X     SCDC6 SCDC5 SCDC4 SCDC3 SCDC3 SCDC1 SCDC0 */


/*
** Command stream (SAA9051)
*/

static UBYTE command[8] = {0x8A, SAA9051_HorPeaking, 0x69, 0x00, 0x18, 0xE0, 0x00, 0x00};

#define HORPEAKING  2
#define HUECONTROL  3
#define CONTROL_1   4
#define CONTROL_2   5
#define CONTROL_3   6


/*
** SAA9051_HorPeaking bit definitions
*/
#define SAA9051F_BypassColor    0x80    /* BY bit : Bypass color-carrier frequency. YPN ignored. */
#define SAA9051F_PreFilter      0x40    /* PF bit : Prefilter control : 0 = ON, 1 = OFF          */
#define SAA9051F_BandPass_Mask  0x30    /* BP2/BP1 bits.                    */
#define SAA9051F_BandPass_4_1   0x00    /* 4.1 MHz                          */
#define SAA9051F_BandPass_3_8   0x10    /* 3.8 MHz                          */
#define SAA9051F_BandPass_2_6   0x20    /* 2.6 MHz                          */
#define SAA9051F_BandPass_2_9   0x30    /* 2.9 MHz                          */
#define SAA9051F_Coring_Mask    0x0C    /* COR2/COR1 bits                   */
#define SAA9051F_Coring_OFF     0x00    /* Squelch/Noise suppression OFF    */
#define SAA9051F_Coring_3       0x04    /* Threshold : 3 LSb                */
#define SAA9051F_Coring_4       0x08    /* Threshold : 4 LSb                */
#define SAA9051F_Coring_5       0x0C    /* Threshold : 5 LSb                */
#define SAA9051F_AP             0x03    /* AP2/AP1 bits                     */
#define SAA9051F_Filters_Mask   0x3F

/*
** SAA9051_Control_1 bit definitions
*/
#define SAA9051F_HPLL           0x80    /* HPLL bit : 1 = Open, 0 = Close                      */
#define SAA9051F_FrameSize_NTSC 0x40    /* FS bit   : 1 = 60 Hz 525 lines, 0 = 50 Hz 625 lines */
#define SAA9051F_VTR            0x20    /* VTR bit  : 1 = Video Tape Recorder, 0 = TV          */
#define SAA9051F_Color          0x10    /* CO bit   : 1 = Color ON, 0 = Color OFF              */
#define SAA9051F_PAL            0x08    /* ALT bit  : 1 = PAL, 0 = NTSC                        */
#define SAA9051F_ColorFreq_NTSC 0x04    /* YPN bit  : 0 = 4.43 MHz (PAL), 1 = 3.58 MHz (NTSC)  */
#define SAA9051F_CCFR_Mask      0x03    /* CCFR1/CCFR0 bits                                    */
#define SAA9051F_CCFR_PAL_BG    0x00    /* 4.43361885 MHz (PAL-B, G, H, I; NTSC 4.43)          */
#define SAA9051F_CCFR_PAL_M     0x01    /* 3.57561149 MHz (PAL-M)                              */
#define SAA9051F_CCFR_PAL_N     0x02    /* 3.58205625 MHz (PAL-N)                              */
#define SAA9051F_CCFR_NTSC_M    0x03    /* 3.57954500 MHz (NTSC-M)                             */

/*
** SAA9051_Control_2 bit definitions
*/
#define SAA9051F_VNL            0x80    /* VNL bit : 1 = Active, 0 = Bypass (VNL sync signal)   */
#define SAA9051F_OEY            0x40    /* OEY bit : Data bus control (1 = active, 0 = Z-State) */
#define SAA9051F_OEC            0x20    /* OEC bit : UV connections (1 = active, 0 = Z-state)   */
#define SAA9051F_CI             0x08    /* CI bit                                               */
#define SAA9051F_AFCC           0x04    /* AFCC bit : 1 = High spike, 0 = Low spike             */
#define SAA9051F_SS1            0x02    /* SS1 bit                                              */
#define SAA9051F_SS0            0x01    /* SS0 bit                                              */

/*
** SAA9051_Control_3 bit definitions
*/
#define SAA9051F_SyncEnable     0x80    /* SYC bit : 1 = Sync pulses disables, 0 = Enabled (HSY + HC) */
#define SAA9051F_CVBS           0x40    /* CT bit  : 1 = CVBS active, 0 = Z-State               */
#define SAA9051F_YC             0x20    /* YC bit  : 1 = Y/C inputs, 0 = CVBS                   */
#define SAA9051F_SS3            0x10    /* SS3 bit                                              */
#define SAA9051F_SS2            0x08    /* SS2 bit                                              */
#define SAA9051F_YDL_Mask       0x07    /* YDL2/YDL1/YDL0 bits : Luminance processing delay     */

/*
** SAA9051 status byte
*/
#define SAA9051F_I2C_ERROR      0x01    /* Private bit set by I2C functions */
#define SAA9051F_SECAM          0x02
#define SAA9051F_COLOR          0x04
#define SAA9051F_60HZ           0x10
#define SAA9051F_HFLOCK         0x40
#define SAA9051F_POWERON        0x80


/*------------------------------------------------------------------------------------------------*/

/****** VLab_control/VLab_OwnHardware ***************************************
*
*   NAME
*   VLab_OwnHardware -- Lock the VLab.
*
*   SYNOPSIS
*   success = VLab_OwnHardware()
*
*   int VLab_OwnHardware();
*
*   FUNCTION
*   Create and/or obtain the public semaphore to lock out other applications.
*
*   RESULT
*   success - TRUE or FALSE.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


int VLab_OwnHardware(void)
{
    extern void putCharProc();

    struct VLabSemaphore *sem;
    int success = FALSE;

    if ( (PrivateSem = (struct VLabSemaphore *) AllocVec(sizeof(struct VLabSemaphore), MEMF_CLEAR | MEMF_PUBLIC)) ) {

        RawDoFmt("VLab Hardware %08lx", &VLabPrivate.vi_VLabAddress, putCharProc, PrivateSem->name);
        Forbid();

        if ((sem = (struct VLabSemaphore *) FindSemaphore(PrivateSem->name)) == NULL) {
            PrivateSem->semaphore.ss_Link.ln_Name = PrivateSem->name;
            AddSemaphore((struct SignalSemaphore *) PrivateSem);
            PublicSem = PrivateSem;
        }
        else {
            FreeVec(PrivateSem);        /* The semaphore already exists. */
            PrivateSem = NULL;
            PublicSem  = sem;           /* So we don't need a new one.   */
        }

        ++PublicSem->count;
        Permit();
        ObtainSemaphore((struct SignalSemaphore *) PublicSem);

        success = TRUE;
    }

    return success;
}


/****** VLab_control/VLab_FreeHardware **************************************
*
*   NAME
*   VLab_FreeHardware -- Release the VLab
*
*   SYNOPSIS
*   VLab_FreeHardware()
*
*   void VLab_FreeHardware();
*
*   FUNCTION
*   Release the public semaphore. If this semaphore has been created by us,
*   remove it from the public list then free the associated resources.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


void VLab_FreeHardware(void)
{
    if (PublicSem) {
        ReleaseSemaphore((struct SignalSemaphore *) PublicSem);

        if (--PublicSem->count == 0 && PublicSem == PrivateSem) {
            Forbid();
            RemSemaphore((struct SignalSemaphore *) PublicSem);
            FreeVec(PrivateSem);
            Permit();
        }
    }
}


/****** VLab_control/VLab_AllocateYUV ***************************************
*
*   NAME
*   VLab_AllocateYUV -- Allocate YUV buffers.
*
*   SYNOPSIS
*   success = VLab_AllocateYUV()
*
*   int VLab_AllocateYUV();
*
*   FUNCTION
*   Allocate 2 buffers (Y and U,V components) large enough to store the
*   largest possible picture.
*
*   RESULT
*   success - FALSE if at least one allocation failed, TRUE otherwise.
*
*   SEE ALSO
*   VLab_FreeYUV()
*
*****************************************************************************
*
*/


int VLab_AllocateYUV(void)
{
    #define VLAB_MAXPIXELS  (VLAB_MAXWIDTH * VLAB_MAXHEIGHT_PAL)

    int success = FALSE;
    struct VLabInfos *vi = &VLabPrivate;

    if ( (vi->vi_YBuff = (UBYTE *) AllocVec(VLAB_MAXPIXELS, MEMF_ANY)) )
        if ( (vi->vi_UVBuff = (UBYTE *) AllocVec((VLAB_MAXPIXELS / VLAB_RATIO_YUV) * 2, MEMF_ANY)) )
            success = TRUE;

    return success;
}


/****** VLab_control/VLab_FreeYUV *******************************************
*
*   NAME
*   VLab_FreeYUV -- Free YUV buffers.
*
*   SYNOPSIS
*   VLab_FreeYUV()
*
*   void VLab_FreeYUV();
*
*   FUNCTION
*   Deallocate Y and U,V buffers.
*
*   SEE ALSO
*   VLab_AllocateYUV()
*
*****************************************************************************
*
*/


void VLab_FreeYUV(void)
{
    struct VLabInfos *vi = &VLabPrivate;

    if (vi->vi_YBuff) {
        FreeVec(vi->vi_YBuff);
        vi->vi_YBuff = NULL;
    }

    if (vi->vi_UVBuff) {
        FreeVec(vi->vi_UVBuff);
        vi->vi_UVBuff = NULL;
    }
}


/****** VLab_control/VLab_Change#? ******************************************
*
*   NAME
*   VLab_ChangeInput   -- Select input
*   VLab_ChangeVideo   -- Change the video standard.
*   VLab_ChangeHue     -- Change hue control value.
*   VLab_ChangeFlags   -- Update flags settings.
*   VLab_ChangeFilters -- Update filters settings.
*
*   SYNOPSIS
*   VLab_ChangeInput(value)
*   VLab_ChangeVideo(value)
*   VLab_ChangeHue(value)
*   VLab_ChangeFlags(value)
*   VLab_ChangeFilters(value)
*
*   void VLab_ChangeInput(ULONG);
*   void VLab_ChangeVideo(ULONG);
*   void VLab_ChangeHue(LONG);
*   void VLab_ChangeFlags(ULONG);
*   void VLab_ChangeFilters(ULONG);
*
*   FUNCTION
*   Change the settings. The hardware is updated immediately, so the
*   monitoring window reflects any changes made in real time.
*
*   INPUTS
*   value - New value to be used.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


void VLab_ChangeInput(ULONG input)
{
    if (input == 2) {                               /* Y/C input */
        command[HORPEAKING] |= SAA9051F_BypassColor;
        command[CONTROL_3]  |= SAA9051F_YC;
    }
    else {
        if ( !(VLabPrivate.vi_Flags & VLABF_BW_SOURCE))
            command[HORPEAKING] &= ~SAA9051F_BypassColor;
        command[CONTROL_3]  &= ~SAA9051F_YC;
    }

    if (VLabPrivate.vi_BoardType && input)          /* VLab Y/C, input #1 or #2 */
        input = 3 - input;                          /* Translate input number to input code */

    command[CONTROL_2] = (command[CONTROL_2] & ~(SAA9051F_SS1 | SAA9051F_SS0)) | input;
    VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, command, sizeof(command));
}


void VLab_ChangeVideo(ULONG videoStd)
{
    struct VLabInfos *vi = &VLabPrivate;
    UBYTE ctrl1;

    vi->vi_Video = videoStd;

    ctrl1 = command[CONTROL_1] & ~(SAA9051F_ColorFreq_NTSC | SAA9051F_PAL | SAA9051F_FrameSize_NTSC | SAA9051F_CCFR_Mask);

    switch(videoStd) {
        case VIDEO_PAL_B:               /* also PAL G, PAL H and PAL I */
            ctrl1 |= SAA9051F_PAL;
            break;

        case VIDEO_PAL_N:
            ctrl1 |= SAA9051F_PAL | SAA9051F_CCFR_PAL_N;
            break;

        case VIDEO_PAL_M:
            ctrl1 |= SAA9051F_PAL | SAA9051F_CCFR_PAL_M | SAA9051F_FrameSize_NTSC | SAA9051F_ColorFreq_NTSC;
            break;

        case VIDEO_NTSC_443:
            ctrl1 |= SAA9051F_FrameSize_NTSC;
            break;

        case VIDEO_NTSC_M:
            ctrl1 |= SAA9051F_CCFR_NTSC_M | SAA9051F_FrameSize_NTSC | SAA9051F_ColorFreq_NTSC;
            break;
    }

    command[CONTROL_1] = ctrl1;
    VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, command, sizeof(command));

    vi->vi_ImageHeight = VLab_FrameHeight(videoStd, vi->vi_Flags);
}


void VLab_ChangeHue(LONG hue)
{
    if (hue < 179)
        hue = ((hue + 180) * 10) / 14;          /* hue = [0; 255]. 128 = 0 degree */
    else
        hue -= 180;                             /* Translates 180 to 0, and 179 to 255 */

    command[HUECONTROL] = (VLabPrivate.vi_Video >= VIDEO_NTSC_443) ? hue : 0xFF;
    VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, command, sizeof(command));
}


void VLab_ChangeFlags(ULONG flags)
{
    struct VLabInfos *vi = &VLabPrivate;

    vi->vi_Flags = flags;

    command[HORPEAKING] &= ~(SAA9051F_PreFilter | SAA9051F_BypassColor);
    if (flags & VLABF_PREFILTER)
        command[HORPEAKING] |= SAA9051F_PreFilter;

    if ((flags & VLABF_BW_SOURCE) || (command[CONTROL_3] & SAA9051F_YC))
        command[HORPEAKING] |= SAA9051F_BypassColor;

    command[CONTROL_1] &= ~SAA9051F_VTR;
    if (flags & VLABF_VTR)
        command[CONTROL_1] |= SAA9051F_VTR;

    VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, command, sizeof(command));

    vi->vi_ImageWidth  = VLab_FrameWidth(flags);
    vi->vi_ImageHeight = VLab_FrameHeight(vi->vi_Video, flags);
}


void VLab_ChangeFilters(ULONG filters)
{
    command[HORPEAKING] = (command[HORPEAKING] & ~SAA9051F_Filters_Mask) | filters;
    VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, command, sizeof(command));
}


ULONG VLab_FrameWidth(ULONG flags)
{
    return (flags & VLABF_HIRES) ? VLAB_MAXWIDTH : VLAB_MAXWIDTH / 2;
}


ULONG VLab_FrameHeight(ULONG video, ULONG flags)
{
    ULONG h = (video < VIDEO_525) ? VLAB_MAXHEIGHT_PAL : VLAB_MAXHEIGHT_NTSC;

    if (!(flags & VLABF_LACE))
        h /= 2;

    return h;
}


/****** VLab_control/VLab_IdentifyHardware **********************************
*
*   NAME
*   VLab_IdentifyHardware -- Identify the VLab
*
*   SYNOPSIS
*   inputs = VLab_IdentifyHardware()
*
*   ULONG VLab_IdentifyHardware();
*
*   FUNCTION
*   This function scan the ConfigDev list to find the first VLab Zorro II
*   (either Classic or Y/C).
*
*   RESULT
*   inputs - Number of inputs on the first board found, or 0 if no board is
*            present. Therefore, a result of 2 identifies a VLab Classic,
*            and a result of 3 identifies a VLab Y/C.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


ULONG VLab_IdentifyHardware(void)
{
    ULONG inputs = 0;               /* Assume no hardware present */
    struct ConfigDev *cDev;

    if ( (ExpansionBase = (struct ExpansionBase *) OpenLibrary("expansion.library", 37)) ) {

        /*
        ** Search the ConfigDev list.
        ** MacroSystem ID = 18260
        ** Board ID = 4
        */
        if ( (cDev = FindConfigDev(NULL, 18260, 4)) ) {

            VLabPrivate.vi_VLabAddress = cDev->cd_BoardAddr;
            VLabPrivate.vi_BoardType   = cDev->cd_Rom.er_SerialNumber;

            if (cDev->cd_Rom.er_SerialNumber == 65538) {
                inputs = 3;         /* VLab Y/C */

                VLabPrivate.vi_fSkipLuma   = VLabYC_SkipLuma;
                VLabPrivate.vi_fSkipChroma = VLabYC_SkipChroma;
                VLabPrivate.vi_fGrabLuma   = VLabYC_GrabLuma;
                VLabPrivate.vi_fGrabChroma = VLabYC_GrabChroma;
            }
            else {
                inputs = 2;         /* VLab Classic */

                VLabPrivate.vi_fSkipLuma   = VLab_SkipLuma;
                VLabPrivate.vi_fSkipChroma = VLab_SkipChroma;
                VLabPrivate.vi_fGrabLuma   = VLab_GrabLuma;
                VLabPrivate.vi_fGrabChroma = VLab_GrabChroma;
            }
        }

        CloseLibrary((struct Library *) ExpansionBase);
    }

    return inputs;
}


/****** VLab_control/VLab_LumaCorrection ************************************
*
*   NAME
*   VLab_LumaCorrection -- Create a new luma correction table.
*
*   SYNOPSIS
*   VLab_LumaCorrection(gamma, brightness, contrast)
*
*   void VLab_LumaCorrection(LONG, LONG, LONG);
*
*   FUNCTION
*   Create a new luma correction table for the Y component. This table is
*   used when we convert raw YUV datas (as written by the SAA9051) to RGB
*   values.
*
*   INPUTS
*   gamma - Gamma value [VLAB_GAMMA_MIN; VLAB_GAMMA_MAX]. The real value
*           is computed as : 10.0 / gamma.
*   brightness - Brightness value [VLAB_BRIGHT_MIN; VLAB_BRIGHT_MAX].
*   contrast - Contrast value [VLAB_CONTRAST_MIN; VLAB_CONTRAST_MAX].
*
*   NOTES
*   The SAA9051 stores Y values in the range [14; 230]. The function creates
*   a table (providing gamma, brightness and contrast corrections) that
*   translates Y entries to Luma values in the range [0; 255].
*   The function performs some kind of caching to speed up table changes.
*
*   SEE ALSO
*
*****************************************************************************
*
*/

UBYTE VLab_Y_Correction[256];


void VLab_LumaCorrection(LONG gamma, LONG brightness, LONG contrast)
{
    static float logX[256];
    static WORD gTable[256];
    static int logX_ready = FALSE;
    static LONG oldGamma = VLAB_GAMMA_MIN - 1000;

    int i;
    LONG value;
    UBYTE *table = VLab_Y_Correction;

    /*
    ** Create the static log(x) table.
    */

    if (!logX_ready) {
        float x;

        for (i = 15; i < 230; i++) {
            x = IEEESPSub(IEEESPLog(IEEESPFlt(i - 14)), 5.3752784f);   /* log(216) = 5.3752784... */
            logX[i] = x;          /* = log((i-14)/216) */
        }
        logX_ready = TRUE;
    }

    /*
    ** Compute a new gamma table.
    */

    if (gamma != oldGamma) {
        float y;
        float Gamma;

        if (gamma >= 0)
            Gamma = IEEESPDiv(10.0f, IEEESPFlt(10 + gamma));
        else
            Gamma = IEEESPDiv(IEEESPFlt(10 - gamma), 10.0f);

        for (i = 15; i < 230; i++) {
            y = IEEESPExp(IEEESPMul(Gamma, logX[i]));
            gTable[i] = IEEESPFix(IEEESPMul(255.0f, y));
        }
        oldGamma = gamma;
    }

    /*
    ** Create the final table
    */

    for (i = 0; i < 15; i++)
        *table++ = 0;

    for (i = 15; i < 230; i++) {
        value = ((i - (128 + 14)) * contrast) / 128 + brightness + gTable[i];

        if (value < 0)
            value = 0;
        if (value > 255)
            value = 255;

        *table++ = value;
    }

    for (i = 230; i < 256; i++)
        *table++ = 255;
}


/****** VLab_control/VLab_InitHardware **************************************
*
*   NAME
*   VLab_InitHardware -- Initialize the hardware for scanning.
*
*   SYNOPSIS
*   failure = VLab_InitHardware()
*
*   BOOL VLab_InitHardware(void);
*
*   FUNCTION
*   Initialize the chipset with the current parameters.
*
*   RESULT
*   failure - TRUE if an error occured.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


BOOL VLab_InitHardware(void)
{
    BOOL error;

    error = VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, initStream, sizeof(initStream)) & SAA9051F_I2C_ERROR;
    return error | (VLab_WriteRegisters(VLabPrivate.vi_VLabAddress, command, sizeof(command)) & SAA9051F_I2C_ERROR);
}


/****** VLab_control/VLab_GetErrorString ************************************
*
*   NAME
*   VLab_GetErrorString -- Get a VLab specific error string
*
*   SYNOPSIS
*   string = VLab_GetErrorString(err)
*
*   const char *VLab_GetErrorString(ULONG);
*
*   FUNCTION
*   Returns the error string associated with the error code err.
*
*   INPUTS
*   err - Error code.
*
*   RESULT
*   string - English string describing the error.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


const char *VLab_GetErrorString(ULONG err)
{
    static const char *errStrings[] = {
        "No error",
        "Bad parameters",
        "Communication error",
        "No video signal",
        "Scanning error"
    };

    const char *errStr = "Undefined error";

    if (err < sizeof(errStrings) / sizeof(errStrings[0]))
        errStr = errStrings[err];

    return errStr;
}


/****** VLab_Driver/ VLab_Grab **********************************************
*
*   NAME
*   VLab_Grab -- Grab a new picture.
*
*   SYNOPSIS
*   error = VLab_Grab(xpos, ypos, width, height, mode, color)
*
*   ULONG VLab_Grab(ULONG, ULONG, ULONG, ULONG, ULONG, BOOL);
*
*   FUNCTION
*   Grab a new picture then fill the output buffer with the YUV components.
*   The mode parameter prescribes the resolution of the resulting image
*   (thumbnail, LoRes or HiRes). Other parameters are obtained from the
*   current settings.
*
*   INPUTS
*   xpos     - Left margin.
*   ypos     - Top margin.
*   width    - Width.
*   height   - Height.
*   mode     - Mode of operation (CAPTURE_#?).
*   color    - FALSE for greyscale scans.
*
*   RESULT
*   error - VLABERR_#? error code.
*
*   BUGS
*   No bounds checking is done against the position or dimension values, but
*   since the GUI driven implementation doesn't allow out of range values,
*   this shall not be a problem.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


ULONG VLab_Grab(ULONG xpos, ULONG ypos, ULONG width, ULONG height, ULONG mode, BOOL color)
{
    struct VLabInfos *vi = &VLabPrivate;
    int error = VLABERR_BADPARAM;

    if (vi->vi_VLabAddress && vi->vi_UVBuff) {
        ULONG scaled_xpos, scaled_width;
        ULONG line_spacing;                     /* Left + Right margins             */
        ULONG top_margin;
        ULONG inter_frame = 0;                  /* Bottom + Top margins             */
        ULONG luma_spacing = 0;                 /* Luma output offset (lace mode)   */
        ULONG chroma_spacing = 0;               /* Chroma output offset (lace mode) */

        scaled_xpos  = xpos;
        scaled_width = width;

        if (mode == CAPTURE_THUMBNAIL) {        /* Non-lace only (monitoring) */
            scaled_xpos  *= 4;
            scaled_width *= 4;
            top_margin   = (VLAB_MAXWIDTH * 2) * ypos + scaled_xpos;
            line_spacing = (VLAB_MAXWIDTH * 2) - scaled_width;      /* == Blank line + Left + Right margins */
        }
        else {      /* CAPTURE_LORES, CAPTURE_HIRES */
            if (mode == CAPTURE_LORES) {
                scaled_xpos  *= 2;
                scaled_width *= 2;
            }
            line_spacing = VLAB_MAXWIDTH - scaled_width;            /* == Left + Right margins */

            if (vi->vi_Flags & VLABF_LACE) {
                top_margin   = VLAB_MAXWIDTH * (ypos / 2) + scaled_xpos;
                height /= 2;
                inter_frame    = ((vi->vi_ImageHeight / 2) - height + 3) * VLAB_MAXWIDTH + line_spacing;
                luma_spacing   = width;
                chroma_spacing = scaled_width / (VLAB_RATIO_YUV / 2);
            }
            else
                top_margin = VLAB_MAXWIDTH * ypos + scaled_xpos;
        }

        /*
        ** Now, grab the frame(s).
        */

        vi->vi_fSkipLuma(vi->vi_VLabAddress, 1, top_margin);
        vi->vi_fGrabLuma(vi->vi_VLabAddress, vi->vi_YBuff, luma_spacing, width, height, line_spacing, mode);

        if (vi->vi_Flags & VLABF_LACE) {
            vi->vi_fSkipLuma(vi->vi_VLabAddress, 2, inter_frame);
            vi->vi_fGrabLuma(vi->vi_VLabAddress, vi->vi_YBuff + luma_spacing, luma_spacing, width, height, line_spacing, mode);
        }

        if (color) {
            vi->vi_fSkipChroma(vi->vi_VLabAddress, 1, top_margin);
            vi->vi_fGrabChroma(vi->vi_VLabAddress, vi->vi_UVBuff, chroma_spacing, scaled_width, height, line_spacing);

            if (vi->vi_Flags & VLABF_LACE) {
                vi->vi_fSkipChroma(vi->vi_VLabAddress, 2, inter_frame);
                vi->vi_fGrabChroma(vi->vi_VLabAddress, vi->vi_UVBuff + chroma_spacing,
                        chroma_spacing, scaled_width, height, line_spacing);
            }
        }

        error = VLab_ReadStatus(vi->vi_VLabAddress);

        if (error & SAA9051F_I2C_ERROR)
            error = VLABERR_I2C;
        else if (error & SAA9051F_HFLOCK)
            error = VLABERR_NOVIDEO;
        else
            error = VLABERR_OK;
    }

    return error;
}


/****** VLab_control/VLab_GetYBuffer ****************************************
*
*   NAME
*   VLab_GetYBuffer -- Obtain Y buffer.
*
*   SYNOPSIS
*   buffer = VLab_GetYBuffer()
*
*   UBYTE * VLab_GetYBuffer();
*
*   FUNCTION
*   Returns the address of the Y (luma) buffer which can be used as is to
*   display greyscale images.
*
*   RESULT
*   buffer - Pointer to the Y buffer (luma).
*
*   SEE ALSO
*
*****************************************************************************
*
*/


UBYTE *VLab_GetYBuffer(void)
{
    return VLabPrivate.vi_YBuff;
}


/****** VLab_control/VLab_YUV_to_RGB ****************************************
*
*   NAME
*   VLab_YUV_to_RGB -- Convert from YUV to RGB.
*
*   SYNOPSIS
*   VLab_YUV_to_RGB(buffer, numPix, mode)
*
*   void VLab_YUV_to_RGB(UBYTE *, ULONG, ULONG);
*
*   FUNCTION
*   Convert the YUV values (internal representation) to RGB-24.
*
*   INPUTS
*   buffer - Pointer to a buffer for the RGB components.
*   numPix - Number of pixels (Y values) to translate.
*   mode   - Capture resolution, which describes the internal representation
*            of YUV components.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


void VLab_YUV_to_RGB(UBYTE *buffer, ULONG numPix, ULONG mode)
{
    UBYTE *yBuff = VLabPrivate.vi_YBuff;
    UBYTE *uvBuff = VLabPrivate.vi_UVBuff;

    if (buffer && yBuff && uvBuff) {

        switch(mode) {
            case CAPTURE_HIRES:
                VLab_411_to_RGB(yBuff, uvBuff, buffer, numPix);
                break;

            case CAPTURE_LORES:
                VLab_211_to_RGB(yBuff, uvBuff, buffer, numPix);
                break;

            case CAPTURE_THUMBNAIL:
                VLab_111_to_RGB(yBuff, uvBuff, buffer, numPix);
                break;
        }
    }
}


/****** VLab_control/VLab_YUV_to_Color **************************************
*
*   NAME
*   VLab_YUV_to_Color -- Convert from YUV to YUV 4:2:2
*
*   SYNOPSIS
*   VLab_YUV_to_Color(buffer, numPix, mode)
*
*   void VLab_YUV_to_Color(UBYTE *, ULONG, ULONG);
*
*   FUNCTION
*   Convert the YUV values (internal representation) to YUV 4:2:2 as expected
*   by PiP or Video Layer windows.
*
*   INPUTS
*   buffer - Pointer to the YUV 4:2:2 buffer (bitmap)
*   numPix - Number of pixels (Y values) to translate.
*   mode   - Capture resolution, which describes the internal representation
*            of YUV components.
*
*   NOTES
*   YUV 4:2:2 is only used by the monitoring, so the CAPTURE_HIRES mode is
*   not supported.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


void VLab_YUV_to_Color(UBYTE *buffer, ULONG numPix, ULONG mode)
{
    UBYTE *yBuff = VLabPrivate.vi_YBuff;
    UBYTE *uvBuff = VLabPrivate.vi_UVBuff;

    if (buffer && yBuff && uvBuff) {

        switch(mode) {
            case CAPTURE_LORES:
                VLab_211_to_422(yBuff, uvBuff, buffer, numPix);
                break;

            case CAPTURE_THUMBNAIL:
                VLab_111_to_422(yBuff, uvBuff, buffer, numPix);
                break;
        }
    }
}


/****** VLab_control/VLab_YUV_to_Grey ***************************************
*
*   NAME
*   VLab_YUV_to_Grey -- Convert from YUV to YUV 4:2:2
*
*   SYNOPSIS
*   VLab_YUV_to_Grey(buffer, numPix, mode)
*
*   void VLab_YUV_to_Grey(UBYTE *, ULONG, ULONG);
*
*   FUNCTION
*   Convert the YUV values (internal representation) to YUV 4:2:2 as expected
*   by PiP or Video Layer windows.
*
*   INPUTS
*   buffer - Pointer to the YUV 4:2:2 buffer (bitmap)
*   numPix - Number of pixels (Y values) to translate.
*   mode   - Capture resolution, which describes the internal representation
*            of YUV components.
*
*   NOTES
*   YUV 4:2:2 is only used by the monitoring, so the CAPTURE_HIRES mode is
*   not supported.
*
*   SEE ALSO
*
*****************************************************************************
*
*/


void VLab_YUV_to_Grey(UBYTE *buffer, ULONG numPix)
{
    VLab_Y_to_422(VLabPrivate.vi_YBuff, buffer, numPix);
}

