/******************************************************************************
 * Rage 128 Chapter 4 sample code                                             *
 *                                                                            *
 * tblt.c - This program uses the engine to perform transparent blts          *
 *                                                                            *
 * Copyright (c) 1999 ATI Technologies Inc. All rights reserved.              *
 ******************************************************************************/
#include <stdio.h>
#include <i86.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include "..\util\regdef.h"
#include "..\util\defines.h"
#include "..\util\main.h"
#include "..\util\viewtga.h"

// Globals
_tbltdata TBLT;
WORD srcx, srcy, dstx, dsty, srcheight, srcwidth, bytepp;
WORD squares_width, squares_height;

// Prototypes
void R128_TransparentBlt (void);
void PrintStatus (void);
void DrawBkgd (void);
void DrawImages (void);

/****************************************************************************
 * Main Program to demonstrate transparent bit block transfers (blts)       *
 *  Function: Test images are drawn on the screen.  The keyboard controls   *
 *            various colour compare states, allowing the user to see the   *
 *            results of the combination of these states.                   *
 *    Inputs: Arguments for mode spatial and colour resolution              *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void main (int argc, char *argv[])
{
    BYTE test;
    char input;
    BYTE dstfn, srcfn, clrcmp;
    char filename[255];
    char tf[255];
    img_handle *ImgPtr;
    int retval;
    TARGA_HEADER tgaheader;
    TARGA_HEADER squares;
    WORD dst_fn_code[4] = { 0, 1, 4, 5 };
    WORD src_fn_code[5] = { 0, 1, 4, 5, 7 };
    WORD clr_cmp_src[3] = { 0, 1, 2 };
    WORD clr_cmp_clr_src, clr_cmp_clr_dst;

    R128_StartUp (argc, argv);

    R128_LoadText ();

    if (R128_AdapterInfo.xres <= 320)
    {
        R128_ShutDown ();
        printf ("\nThis program requires a resolution of at least 400x300.");
        printf ("\nProgram terminated.");
        exit (1);
    }

    if (R128_AdapterInfo.bpp == 24)
    {
        R128_ShutDown ();
        printf ("\nThis program does not operate in 24 bpp mode.");
        printf ("\nProgram terminated.");
        exit (1);
    }

    // clear screen.
    R128_ClearScreen (BLACK);

    // determine which image we will be loading, depending on pixel depth
    switch (R128_AdapterInfo.bpp)
    {
        case 8:     strcpy (filename, "..\\..\\image\\test8.tga");
                    strcpy (tf, "..\\..\\image\\sqr8.tga");
                    break;
        case 15:    strcpy (filename, "..\\..\\image\\test15.img");
                    strcpy (tf, "..\\..\\image\\sqr15.img");
                    break;
        case 16:    strcpy (filename, "..\\..\\image\\test16.img");
                    strcpy (tf, "..\\..\\image\\sqr16.img");
                    break;
        case 24:    strcpy (filename, "..\\..\\image\\test24.tga");
                    strcpy (tf, "..\\..\\image\\sqr24.tga");
                    break;
        case 32:    strcpy (filename, "..\\..\\image\\test32.img");
                    strcpy (tf, "..\\..\\image\\sqr32.img");
                    break;
    }

    if ((R128_AdapterInfo.bpp == 8) || (R128_AdapterInfo.bpp == 24))
    {

        test = get_targa_header (filename, &tgaheader);
        if (test != SUCCESS)
        {
            R128_ShutDown ();
            printf ("\nUnable to read image header.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        srcwidth = tgaheader.width;
        srcheight = tgaheader.height;

        if (R128_AdapterInfo.bpp == 8)
        {
            test = set_targa_palette (filename);
        }

        // Load the image into offscreen memory, just off the visible page
        test = load_targa (filename, 0, R128_AdapterInfo.yres);

        if (test != SUCCESS)
        {
            R128_ShutDown ();
            printf ("\nUnable to load image.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        test = get_targa_header (tf, &squares);
        if (test != SUCCESS)
        {
            R128_ShutDown ();
            printf ("\nUnable to read image header.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        squares_width = squares.width;
        squares_height = squares.height;

        // Load the image into offscreen memory, just off the visible page
        test = load_targa (tf, 0, R128_AdapterInfo.yres + srcheight);

        if (test != SUCCESS)
        {
            R128_ShutDown ();
            printf ("\nUnable to load image.");
            printf ("\nProgram terminated.");
            exit (1);
        }


    }
    else
    {
        ImgPtr = get_img_header (filename);
        if (ImgPtr == NULL)
        {
            R128_ShutDown ();
            printf ("\nUnable to read image header.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        srcwidth = ImgPtr->image->width;
        srcheight = ImgPtr->image->height;

        // Load the image into offscreen memory, just off the visible page
        retval = load_img (ImgPtr, 0, R128_AdapterInfo.yres);

        if (retval == NULL)
        {
            R128_ShutDown ();
            printf ("\nUnable to load image.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        ImgPtr = get_img_header (tf);
        if (ImgPtr == NULL)
        {
            R128_ShutDown ();
            printf ("\nUnable to read image header.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        squares_width = ImgPtr->image->width;
        squares_height = ImgPtr->image->height;

        // Load the image into offscreen memory, just off the visible page
        retval = load_img (ImgPtr, 0, R128_AdapterInfo.yres+srcheight);

        if (retval == NULL)
        {
            R128_ShutDown ();
            printf ("\nUnable to load image.");
            printf ("\nProgram terminated.");
            exit (1);
        }

        free (ImgPtr);
    }

    // Set up initial values for variables
    srcx = 00;
    srcy = R128_AdapterInfo.yres;
    clrcmp = dstfn = srcfn = dstx = dsty = 0;
    clr_cmp_clr_dst = clr_cmp_clr_src = 0;

    // Draw inital screen
    DrawBkgd ();
    TBLT.src_cmp_fcn = src_fn_code[srcfn];
    TBLT.dst_cmp_fcn = dst_fn_code[dstfn];
    TBLT.cmp_src = clr_cmp_src[clrcmp];
    TBLT.dst_clr = clr_cmp_clr_dst;
    TBLT.src_clr = clr_cmp_clr_src;
    TBLT.src_x = srcx;
    TBLT.src_y = srcy;
    TBLT.src_width = srcwidth;
    TBLT.src_height = srcheight;
    TBLT.dst_x = dstx;
    TBLT.dst_y = dsty;

    DrawImages ();
    PrintStatus ();

    // main program loop to change colour compare states
    while (input != 32)
    {
        input = getch ();

        switch (input)
        {
            case 'c':
            case 'C':   clrcmp++;
                        if (clrcmp == 3)
                        {
                            clrcmp = 0;
                        }
                        break;
            case 'd':
            case 'D':   dstfn++;
                        if (dstfn == 4)
                        {
                            dstfn = 0;
                        }
                        break;
            case 's':
            case 'S':   srcfn++;
                        if (srcfn == 5)
                        {
                            srcfn = 0;
                        }
                        break;
            case 'f':
            case 'F':   clr_cmp_clr_src++;
                        if (clr_cmp_clr_src == 16)
                        {
                            clr_cmp_clr_src = 0;
                        }
                        break;
            case 'b':
            case 'B':   clr_cmp_clr_dst++;
                        if (clr_cmp_clr_dst == 16)
                        {
                            clr_cmp_clr_dst = 0;
                        }
                        break;

            default:    break;

        } // switch

        TBLT.src_cmp_fcn = src_fn_code[srcfn];
        TBLT.dst_cmp_fcn = dst_fn_code[dstfn];
        TBLT.cmp_src = clr_cmp_src[clrcmp];
        TBLT.dst_clr = clr_cmp_clr_dst;
        TBLT.src_clr = clr_cmp_clr_src;

        DrawBkgd ();
        DrawImages ();
        PrintStatus ();

    } // while

    // batch command to restore old mode.
    R128_ShutDown ();

    exit (0);                           // No errors.

} // main


/****************************************************************************
 * R128_TransparentBlt (void)                                               *
 *  Function: performs a blt with the colour compare circuitry set up as    *
 *            per the TBLT structure.                                       *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void R128_TransparentBlt (void)
{
    DWORD temp, save_dp_mix, save_dp_cntl, save_dp_datatype;

    R128_WaitForFifo (3);
    save_dp_mix = regr (DP_MIX);
    save_dp_cntl = regr (DP_CNTL);
    save_dp_datatype = regr (DP_DATATYPE);

    R128_WaitForFifo (3);
    // set up the engine trajectory registers for a blt.
    regw (DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
    regw (DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
    temp = R128_GetBPPValue (R128_AdapterInfo.bpp);
    regw (DP_DATATYPE, temp | SRC_DSTCOLOR | BRUSH_SOLIDCOLOR);

    R128_WaitForFifo (4);
    // set up the transparency function in the colour compare circuitry
    regw (CLR_CMP_CLR_DST, R128_GetColourCode (TBLT.dst_clr));
    regw (CLR_CMP_CLR_SRC, R128_GetColourCode (TBLT.src_clr));
    regw (CLR_CMP_MASK, 0xFFFFFFFF);
    regw (CLR_CMP_CNTL, (TBLT.cmp_src << 24) |
                        (TBLT.dst_cmp_fcn << 8) |
                         TBLT.src_cmp_fcn);

    // Set up source and destination x and y values
    R128_WaitForFifo (3);
    regw (SRC_Y_X, (TBLT.src_y << 16) | TBLT.src_x);
    regw (DST_Y_X, (TBLT.dst_y << 16) | TBLT.dst_x);
    regw (DST_HEIGHT_WIDTH, (TBLT.src_height << 16) | TBLT.src_width);

      // Restore the modified registers
    R128_WaitForFifo (3);
    regw (DP_MIX, save_dp_mix);
    regw (DP_CNTL, save_dp_cntl);
    regw (DP_DATATYPE, save_dp_datatype);

    return;

} // R128_TransparentBlt ()


/****************************************************************************
 * DrawImages (void)                                                        *
 *  Function: draws the test images onto the screen to demonstrate the      *
 *            various colour compare states that the Rage 128 features.     *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void DrawImages (void)
{
    WORD loop, space, num_images;

    // set up values for first image blt
    TBLT.dst_x = 0;
    TBLT.dst_y = 0;
    TBLT.src_height = srcheight;
    TBLT.src_width = srcwidth;
    TBLT.src_y = R128_AdapterInfo.yres;
    TBLT.src_x = 0;

    // blt first image, in top left corner
    R128_TransparentBlt ();

    // blt image to top right corner
    TBLT.dst_x = R128_AdapterInfo.xres - srcwidth;
    R128_TransparentBlt ();

    // blt second image, repeatedly across the screen
    num_images = R128_AdapterInfo.xres/squares_width;
    space = R128_AdapterInfo.xres - (squares_width*(num_images-1));
    space /= (num_images-2);
    TBLT.dst_y = srcheight + 10;
    TBLT.dst_x = 0;
    TBLT.src_height = squares_height;
    TBLT.src_width = squares_width;
    TBLT.src_y = R128_AdapterInfo.yres + srcheight;
    TBLT.src_x = 0;

    for (loop = 0; loop < (num_images - 1); loop++)
    {
        R128_TransparentBlt ();
        TBLT.dst_x += (space + squares_width);
    }

    return;

} // DrawImages ()


/****************************************************************************
 * DrawBkgd (void)                                                          *
 *  Function: draws the background, which is vertical strips of the 16      *
 *            basic colours used in the SDK.                                *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void DrawBkgd (void)
{
    DWORD temp, save_dp_mix, save_dp_cntl, save_dp_datatype, save_clr_cmp_cntl;
    WORD incx, test, colour;

    // save modified registers
    R128_WaitForFifo (4);
    save_dp_mix = regr (DP_MIX);
    save_dp_cntl = regr (DP_CNTL);
    save_dp_datatype = regr (DP_DATATYPE);
    save_clr_cmp_cntl = regr (CLR_CMP_CNTL);

    // disable clr compare temporarily
    regw (CLR_CMP_CNTL, 0x00000000);

    R128_WaitForIdle ();

    temp = R128_GetBPPValue (R128_AdapterInfo.bpp);
    regw (DP_DATATYPE, temp | SRC_DSTCOLOR | BRUSH_SOLIDCOLOR);
    regw (DP_MIX, ROP3_PATCOPY | DP_SRC_RECT);
    regw (DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);

    // Draw thin rectangles of all 16 colours repeatedly across screen
    incx = R128_AdapterInfo.xres/16;
    for (colour=0, test=0; test < incx; colour++, test++)
    {
        R128_WaitForFifo (3);
        regw (DP_BRUSH_FRGD_CLR, R128_GetColourCode (colour));
        regw (DST_Y_X, (0 << 16) | (test*16));
        regw (DST_HEIGHT_WIDTH, (R128_AdapterInfo.yres << 16) | 16);
        if (colour == 16) colour = 0;
    }

    // Restore the modified registers
    R128_WaitForFifo (4);
    regw (DP_MIX, save_dp_mix);
    regw (DP_CNTL, save_dp_cntl);
    regw (DP_DATATYPE, save_dp_datatype);
    regw (CLR_CMP_CNTL, save_clr_cmp_cntl);

    return;
}

/****************************************************************************
 * PrintStatus (void)                                                       *
 *  Function: prints the status of the compare states in the bottom right   *
 *            corner of the screen.                                         *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void PrintStatus (void)
{
    WORD y;
    char scf[80] = "S key : Source compare function: ";
    char dcf[80] = "D key : Destination compare function: ";
    char sc[80] = "F key : Source colour: ";
    char dc[80] = "B key : Destination colour: ";
    char cs[80] = "C key : Colour compare source: ";

    switch (TBLT.src_cmp_fcn)
    {
        case 0: strcat (scf, "FALSE");
                break;
        case 1: strcat (scf, "TRUE");
                break;
        case 4: strcat (scf, "draw on equal");
                break;
        case 5: strcat (scf, "draw on not equal");
                break;
        case 7: strcat (scf, "flip on equal");
                break;
    }

    switch (TBLT.dst_cmp_fcn)
    {
        case 0: strcat (dcf, "FALSE");
                break;
        case 1: strcat (dcf, "TRUE");
                break;
        case 4: strcat (dcf, "draw on equal");
                break;
        case 5: strcat (dcf, "draw on not equal");
                break;
    }

    switch (TBLT.cmp_src)
    {
        case 0: strcat (cs, "destination");
                break;
        case 1: strcat (cs, "source");
                break;
        case 2: strcat (cs, "src and dst");
                break;
    }

    switch (TBLT.src_clr)
    {
        case 0: strcat (sc, "black");
                break;
        case 1: strcat (sc, "dark blue");
                break;
        case 2: strcat (sc, "dark green");
                break;
        case 3: strcat (sc, "dark cyan");
                break;
        case 4: strcat (sc, "dark red");
                break;
        case 5: strcat (sc, "dark magenta");
                break;
        case 6: strcat (sc, "brown");
                break;
        case 7: strcat (sc, "light gray");
                break;
        case 8: strcat (sc, "dark gray");
                break;
        case 9: strcat (sc, "light blue");
                break;
        case 10: strcat (sc, "light green");
                break;
        case 11: strcat (sc, "light cyan");
                break;
        case 12: strcat (sc, "light red");
                break;
        case 13: strcat (sc, "light magenta");
                break;
        case 14: strcat (sc, "yellow");
                break;
        case 15: strcat (sc, "white");
                break;
    }

    switch (TBLT.dst_clr)
    {
        case 0: strcat (dc, "black");
                break;
        case 1: strcat (dc, "dark blue");
                break;
        case 2: strcat (dc, "dark green");
                break;
        case 3: strcat (dc, "dark cyan");
                break;
        case 4: strcat (dc, "dark red");
                break;
        case 5: strcat (dc, "dark magenta");
                break;
        case 6: strcat (dc, "brown");
                break;
        case 7: strcat (dc, "light gray");
                break;
        case 8: strcat (dc, "dark gray");
                break;
        case 9: strcat (dc, "light blue");
                break;
        case 10: strcat (dc, "light green");
                break;
        case 11: strcat (dc, "light cyan");
                break;
        case 12: strcat (dc, "light red");
                break;
        case 13: strcat (dc, "light magenta");
                break;
        case 14: strcat (dc, "yellow");
                break;
        case 15: strcat (dc, "white");
                break;
    }
    y = R128_AdapterInfo.yres - (5 * TEXT_INFO.height);
    R128_PrintText (scf, 0, y, WHITE, BLACK, 0);
    y += TEXT_INFO.height;
    R128_PrintText (dcf, 0, y, WHITE, BLACK, 0);
    y += TEXT_INFO.height;
    R128_PrintText (cs, 0, y, WHITE, BLACK, 0);
    y += TEXT_INFO.height;
    R128_PrintText (sc, 0, y, WHITE, BLACK, 0);
    y += TEXT_INFO.height;
    R128_PrintText (dc, 0, y, WHITE, BLACK, 0);

    return;
} // PrintStatus ()



