
/*------------------------------------------------------------------*
 *                                                                  *
 *  Video Toolkit For OS/2 Version 1.0                              *
 *  Example PM Application No. 5.                                   *
 *  Date : 13/07/95.                                                *
 *  Copyright Abbotsbury Software Ltd. (c), United Kingdom. 1999.   *
 *                                                                  *
 *  Filename : ex5cap.c                                             *
 *                                                                  *
 *------------------------------------------------------------------*/

#define     INCL_PM
#define     INCL_DOS
#define     INCL_SW
#define     INCL_OS2MM

#include    <os2.h>
#include    <os2me.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <time.h>
#include    <memory.h>
#include    <malloc.h>
#include    <vcadd.h>
#include    <vcai.h>
#include    "ex5.h"
#include    "ex5cap.h"
#include    "rgbconv.h"

extern  HAB     Hab;
extern  HWND    HwndFrame, HwndClient;
extern  LONG    Width, Height, X, Y;
extern  BOOL    CaptureOpen, ConvertASM;
extern  LONG    NumColours;
extern  LONG    Colours[256];

extern  void    msg_box (UCHAR *txt, UCHAR *title, ULONG options);
extern  void    err_msg (CHAR *text);

static  UCHAR   *Bmp_data;
static  BITMAPINFOHEADER2   Bmp_ihdr;
static  CHAR    txt[80];


                        // function protocols.
MRESULT EXPENTRY CaptureDlgProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);

                        // static function protocols.
static  BOOL    create_bitmap_Hdc_Hps (PHDC phdc, PHPS phps);
static  void    bmp_file_proc (void);
static  LONG    hextol (char *t);
static  void    convert_YUV422_RGB (BYTE *src, BYTE *dest, ULONG num);
static  BYTE    get_colour (HPS hps, USHORT r, USHORT g, USHORT b);

                        // functions.
MRESULT EXPENTRY CaptureDlgProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    static  HWND    hwnd_menu;
    static  HBITMAP hbm;
    static  HPS     hpsBMP;
    static  HDC     hdcBMP;
    static  LONG    x, y, width, height;
    static  UCHAR   *buf, *data;
    BITMAPINFO2     bmpi;
    PBITMAPINFO2    pbmpi = &bmpi;
    PBITMAPINFOHEADER2  pbmpih = &Bmp_ihdr;
    ULONG   len, i, j, size;
    ERRORID err;
    LONG    bx, by, ty;
    HPS     hps;
    RECTL   rcl;
    POINTL  pts;
    APIRET  rc;
    SWP     swp;

        switch (msg)
        {
            case WM_INITDLG :
                hwnd_menu = WinLoadMenu (hwnd, NULLHANDLE, IDD_CAPTURE);
                x = y = 0;
                bx = WinQuerySysValue (HWND_DESKTOP, SV_CXDLGFRAME);
                by = WinQuerySysValue (HWND_DESKTOP, SV_CYDLGFRAME);
                ty = WinQuerySysValue (HWND_DESKTOP, SV_CYTITLEBAR);
                WinSetWindowPos (hwnd, HWND_TOP, x, y,
                                 Width + (bx * 2), Height + (by * 2) + ty,
                                 SWP_SIZE | SWP_MOVE);
                WinSendMsg (hwnd, MSG_CAPTURE, NULL, NULL);
                CaptureOpen = TRUE;
                break;
            case WM_PAINT :
                hps = WinBeginPaint (hwnd, NULLHANDLE, &rcl);
                WinQueryWindowRect (hwnd, &rcl);
                WinFillRect (hps, &rcl, CLR_PALEGRAY);
                bx = WinQuerySysValue (HWND_DESKTOP, SV_CXDLGFRAME);
                by = WinQuerySysValue (HWND_DESKTOP, SV_CYDLGFRAME);
                pts.x = 0 + bx;
                pts.y = 0 + by;
                WinDrawBitmap (hps, hbm, NULL, &pts, CLR_NEUTRAL,
                               CLR_BACKGROUND, DBM_NORMAL | DBM_IMAGEATTRS);
                WinDrawBorder (hps, &rcl, bx, by, CLR_YELLOW,
                               CLR_DARKGRAY, DB_AREAATTRS | DB_DLGBORDER);
                WinEndPaint (hps);
                break;
            case WM_COMMAND :
                switch (SHORT1FROMMP (mp1))
                {
                    case IDM_CAPT_FILE_SAVE :
                        bmp_file_proc ();
                        return 0;
                    default :
                        break;
                }
                return 0;
            case WM_CLOSE :
            case WM_QUIT :
            case WM_DESTROY :
                GpiDestroyPS (hpsBMP);
                DevCloseDC (hdcBMP);
                GpiDeleteBitmap (hbm);
                DosFreeMem (Bmp_data);
                DosFreeMem (buf);
                CaptureOpen = FALSE;
                break;
            case MSG_CAPTURE :
                if (hbm)
                {
                    GpiDestroyPS (hpsBMP);
                    DevCloseDC (hdcBMP);
                    GpiDeleteBitmap (hbm);
                }

                width = (Width / 8) * 8;
                height = (Height / 8) * 8;
                WinQueryWindowPos (hwnd, &swp);
                x = swp.x;
                y = swp.y;
                bx = WinQuerySysValue (HWND_DESKTOP, SV_CXDLGFRAME);
                by = WinQuerySysValue (HWND_DESKTOP, SV_CYDLGFRAME);
                ty = WinQuerySysValue (HWND_DESKTOP, SV_CYTITLEBAR);
                WinSetWindowPos (hwnd, HWND_TOP, x, swp.y,
                                 Width + (bx * 2), Height + (by * 2) + (ty * 2),
                                 SWP_SIZE | SWP_MOVE);
                len = (ULONG)((width * height) * 2);
                rc = DosAllocMem ((PPVOID)&buf, len + 4L,
                                  (PAG_COMMIT | PAG_WRITE));
                if (rc)
                {
                    sprintf (txt, "error - DosAllocMem %d", rc);
                    msg_box (txt, "Error", MB_ERROR | MB_OK);
                    return;
                }

                rc = DosAllocMem ((PPVOID)&Bmp_data, (width * height * 4) + 4L,
                                  (PAG_WRITE | PAG_COMMIT));
                if (rc)
                {
                    sprintf (txt, "error - DosAllocMem %d", rc);
                    msg_box (txt, "Error", MB_ERROR | MB_OK);
                    return;
                }

                if (!ConvertASM)
                {
                    rc = DosAllocMem ((PPVOID)&data,
                                      (width * height * 4) + 4L,
                                      (PAG_WRITE | PAG_COMMIT));
                    if (rc)
                    {
                        sprintf (txt, "error - DosAllocMem %d", rc);
                        msg_box (txt, "Error", MB_ERROR | MB_OK);
                        return;
                    }
                }

                VcaiImageScaleGet ((USHORT)len,   (ULONG)buf,
                                   (USHORT)x,     (USHORT)(y + height),
                                   (USHORT)width, (USHORT)height,
                                   (USHORT)X,     (USHORT)Y,
                                   (USHORT)width, (USHORT)height);

                if (ConvertASM)
                {
                    YUV422_TO_IRGB24 ((USHORT *)buf, (BYTE *)Bmp_data,
                                      (ULONG)width,  (ULONG)height);
                }
                else
                {
                    convert_YUV422_RGB (buf, data, len);

                                // Transfer scan lines to bottom up.
                    for (i = 0, j = (width * height) - width;
                         i < height;
                         i++, j-=width)
                    {
                        memcpy (Bmp_data + j, data + (i*width), (size_t)width);
                    }
                }

                if (ConvertASM)
                {
                    pbmpi = malloc (sizeof (BITMAPINFO2));
                }
                else
                {
                    size = (sizeof (RGB2)) * (NumColours - 1);
                    pbmpi = malloc (sizeof (BITMAPINFO2) + size);
                }
                pbmpih->cbFix = pbmpi->cbFix = sizeof (BITMAPINFOHEADER2);
                pbmpih->cx            = pbmpi->cx            = width;
                pbmpih->cy            = pbmpi->cy            = height;
                pbmpih->cPlanes       = pbmpi->cPlanes       = 1;
                if (!ConvertASM)
                {
                    switch ((ULONG)NumColours)
                    {
                        case 2 :
                            pbmpih->cBitCount = pbmpi->cBitCount = 1;
                            break;
                        case 16 :
                            pbmpih->cBitCount = pbmpi->cBitCount = 4;
                            break;
                        case 256 :
                            pbmpih->cBitCount = pbmpi->cBitCount = 8;
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    pbmpih->cBitCount     = pbmpi->cBitCount     = 24;
                }
                pbmpih->ulCompression = pbmpi->ulCompression = BCA_UNCOMP;
                pbmpih->cbImage       = pbmpi->cbImage       = 0L;
                pbmpih->cxResolution  = pbmpi->cxResolution  = width;
                pbmpih->cyResolution  = pbmpi->cyResolution  = height;
                if (!ConvertASM)
                {
                    pbmpih->cclrUsed      = pbmpi->cclrUsed      = NumColours;
                    pbmpih->cclrImportant = pbmpi->cclrImportant = NumColours;
                }
                else
                {
                    pbmpih->cclrUsed      = pbmpi->cclrUsed      = 0L;
                    pbmpih->cclrImportant = pbmpi->cclrImportant = 0L;
                }
                pbmpih->usUnits       = pbmpi->usUnits       = BRU_METRIC;
                pbmpih->usReserved    = pbmpi->usReserved    = 0;
                pbmpih->usRecording   = pbmpi->usRecording   = BRA_BOTTOMUP;
                pbmpih->usRendering   = pbmpi->usRendering   = BRH_NOTHALFTONED;
                pbmpih->cSize1        = pbmpi->cSize1        = 0L;
                pbmpih->cSize2        = pbmpi->cSize2        = 0L;
                pbmpih->ulColorEncoding = pbmpi->ulColorEncoding = BCE_RGB;
                if (!ConvertASM)
                {
                    for (i = 0; i < NumColours; i++)
                    {
                        pbmpi->argbColor[i].bBlue  =
                                           (BYTE)(Colours[i] & 0x000000FF);
                        pbmpi->argbColor[i].bGreen =
                                    (BYTE)((Colours[i] & 0x0000FF00) / 0x100);
                        pbmpi->argbColor[i].bRed   =
                                  (BYTE)((Colours[i] & 0x00FF0000) / 0x10000);
                        pbmpi->argbColor[i].fcOptions = 0;
                    }
                }
                create_bitmap_Hdc_Hps (&hdcBMP, &hpsBMP);
                hbm = GpiCreateBitmap (hpsBMP, pbmpih, 0L, NULL, NULL);
                if (hbm == 0)
                {
                    err_msg ("GpiCreateBitmap");
                    return;
                }
                if (GpiSetBitmap (hpsBMP, hbm) == HBM_ERROR)
                {
                    err_msg ("GpiSetBitmap");
                    return;
                }
                if (GpiSetBitmapBits (hpsBMP, 0L, height, Bmp_data, pbmpi)
                    != height)
                {
                    err_msg ("GpiSetBitmapBits");
                    return;
                }
                DosFreeMem (data);
                DosFreeMem (buf);
                DosFreeMem (Bmp_data);
                free (pbmpi);
                WinQueryWindowRect (hwnd, &rcl);
                WinInvalidateRect (hwnd, &rcl, TRUE);
                break;
            default :
                break;
        }
        return WinDefDlgProc (hwnd, msg, mp1, mp2);
}

static BOOL create_bitmap_Hdc_Hps (PHDC phdc, PHPS phps)
{
    SIZEL    sizl;
    HDC      hdc;
    HPS      hps;
    DEVOPENSTRUC dop = {NULL, "DISPLAY", NULL, NULL, NULL, NULL, NULL, NULL,
                        NULL};

        hdc = DevOpenDC (Hab, OD_MEMORY, "*", 3L, (PDEVOPENDATA) &dop,
                         NULLHANDLE);
        if (!hdc)
            return (FALSE);

        sizl.cx = sizl.cy = 1L;
        hps = GpiCreatePS (Hab, hdc, &sizl, PU_PELS | GPIA_ASSOC | GPIT_MICRO);
        if (!hps)
            return (FALSE);

        *phdc = hdc;
        *phps = hps;
        return (TRUE);
}

static void bmp_file_proc ()
{
    HWND    hwndDlg;
    CHAR    title[20];
    FILE    *fd;
    BYTE    *d;
    LONG    width, height;
    ULONG   i;
    FILEDLG file_dlg;
    BITMAPFILEHEADER2   bmp_fhdr;

                                    // initialise FILEDLG structure.
        memset (&file_dlg, 0, sizeof (FILEDLG));
        file_dlg.cbSize = sizeof (FILEDLG);
        file_dlg.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_SAVEAS_DIALOG;
        sprintf (title, "Save bitmap");
        file_dlg.pszTitle = title;
        sprintf (file_dlg.szFullFile, "*.bmp");

                                    // open file dialog.
        hwndDlg = WinFileDlg (HWND_DESKTOP, HwndFrame, &file_dlg);
        if (hwndDlg && (file_dlg.lReturn != DID_OK))
            return;

                                    // save bitmap.
        fd = fopen (file_dlg.szFullFile, "wb");
        if (!fd)
            return;
        bmp_fhdr.usType  = BFT_BMAP;
        bmp_fhdr.cbSize  = sizeof (BITMAPFILEHEADER2);
        bmp_fhdr.offBits = bmp_fhdr.cbSize;
        bmp_fhdr.bmp2    = Bmp_ihdr;
        d = (BYTE *)&bmp_fhdr;
        for (i = 0; i < (sizeof (BITMAPFILEHEADER2)); i++, d++)
            fputc ((INT) *d, fd);
        width = (Width / 8) * 8;
        height = (Height / 8) * 8;
        for (i = 0; i < (width * height * 4); i++)
            fputc (Bmp_data[i], fd);
        fclose (fd);
}

static LONG hextol (char *t)
{
    LONG tot;

        tot = 0L;

        while (*t)
        {
            if (*t >= '0' && *t <= '9')
                break;
            if (*t >= 'a' && *t <= 'f')
                break;
            if (*t >= 'A' && *t <= 'F')
                break;
            t++;
        }
        while (*t)
        {
            if ((!(*t >= '0' && *t <= '9')) &&
                (!(*t >= 'a' && *t <= 'f')) &&
                (!(*t >= 'A' && *t <= 'F')))
                break;

            if (*t >= '0' && *t <= '9')
            {
                tot *= 16L;
                tot += (*t - '0');
                t++;
                continue;
            }
            if (*t >= 'a' && *t <= 'f')
            {
                tot *= 16L;
                tot += (*t - 'a') + 0xA;
                t++;
                continue;
            }
            if (*t >= '0' && *t <= '9')
            {
                tot *= 16L;
                tot += (*t - 'A') + 0xA;
                t++;
                continue;
            }
        }
        return tot;
}

static void convert_YUV422_RGB (BYTE *src, BYTE *dest, ULONG len)
{
    /*
        Convert YUV 4:2:2 image data to RGB format for use in creating
        a bitmap of the image. This code only supports resolutions with
        256 colours.

        Rt = (V * 179) / 127.
        Bt = (U * 227) / 127.
        R1 = Rt + Y1.
        R2 = Rt + Y2.
        B1 = Bt + Y1.
        B2 = Bt + Y2.
        G1 = ((170 * Y1) - (51 * R1) - (19 * B1)) / 100.
        G2 = ((170 * Y2) - (51 * R2) - (19 * B2)) / 100.
    */

    USHORT  R1, R2, G1, G2, B1, B2, Y1, Y2, Rt, Bt;
    SHORT   U, V;
    LONG    RGB1, RGB2, d;
    ULONG   i, j;
    HPS     hps;

        hps = WinGetPS (HwndClient);
        if (!GpiCreateLogColorTable (hps, LCOL_RESET, LCOLF_CONSECRGB, 0L,
                                     NumColours, Colours))
            err_msg ("GpiCreateLogColorTable");
        for (i = 0, j = 0; i < len; i+=4, j+=2)
        {
            Y1 = (USHORT)(src[i] & 0xff);
            U  = (SHORT)(src[i+1] & 0xff);
            Y2 = (USHORT)(src[i+2] & 0xff);
            V  = (SHORT)(src[i+3] & 0xff);
            U -= 128;
            V -= 128;
            Rt = (V * 179) / 127;
            Bt = (U * 227) / 127;
            R1 = Rt + Y1;
            R2 = Rt + Y2;
            B1 = Bt + Y1;
            B2 = Bt + Y2;
            G1 = ((170 * Y1) - (51 * R1) - (19 * B1)) / 100;
            G2 = ((170 * Y2) - (51 * R2) - (19 * B2)) / 100;
            R1 = (R1 >= 0x100) ? 0xFF :
                 (R1 <= 0) ? 0 : R1;
            R2 = (R2 >= 0x100) ? 0xFF :
                 (R2 <= 0) ? 0 : R2;
            B1 = (B1 >= 0x100) ? 0xFF :
                 (B1 <= 0) ? 0 : B1;
            B2 = (B2 >= 0x100) ? 0xFF :
                 (B2 <= 0) ? 0 : B2;
            G1 = (G1 >= 0x100) ? 0xFF :
                 (G1 <= 0) ? 0 : G1;
            G2 = (G2 >= 0x100) ? 0xFF :
                 (G2 <= 0) ? 0 : G2;
            dest[j]   = get_colour (hps, R1, G1, B1);
            dest[j+1] = get_colour (hps, R2, G2, B2);
        }
        WinReleasePS (hps);
}

static BYTE get_colour (HPS hps, USHORT r, USHORT g, USHORT b)
{
    ULONG   i;
    LONG    d;
    LONG    rgb;

        rgb = (LONG)((r * 0x10000) + (g * 0x100) + b);
        rgb = GpiQueryNearestColor (hps, 0, rgb);
        d = GpiQueryColorIndex (hps, 0, rgb);
        return (BYTE)(d & 0xff);
}
