/****************************************************************************
 *
 *  DRAWPROC.C
 *
 *  Standard AVI drawing handler.
 *
 *      InstallAVIDrawHandler()
 *
 *  Copyright (c) 1992 Microsoft Corporation.  All Rights Reserved.
 *
 *  You have a royalty-free right to use, modify, reproduce and
 *  distribute the Sample Files (and/or any modified version) in
 *  any way you find useful, provided that you agree that
 *  Microsoft has no warranty obligations or liability for any
 *  Sample Application Files which are modified.
 *
 ***************************************************************************/

#include <win32.h>
#include <vfw.h>
#include "common.h"
#include "mciavi.h"

#define FOURCC_VIDS         mmioFOURCC('v','i','d','s')
#define FOURCC_AVIDraw      mmioFOURCC('D','R','A','W')
#define VERSION_AVIDraw     0x00010000      // 1.00

#ifdef DEBUG
    HDRAWDIB ghdd;
#endif

#ifndef HUGE
    #define HUGE _huge
#endif

/***************************************************************************
 ***************************************************************************/

typedef struct {
    HDRAWDIB		hdd;

    HDC                 hdc;            // HDC to draw to
			
    int                 xDst;           // destination rectangle
    int                 yDst;
    int                 dxDst;
    int                 dyDst;
    int                 xSrc;           // source rectangle
    int                 ySrc;
    int                 dxSrc;
    int                 dySrc;
    BOOL                fBackground;

    BOOL                fRle;
    DWORD               biSizeImage;
    BOOL		fNeedUpdate;

    LONG                rate;           // playback rate (uSec / frame)
} INSTINFO, *PINSTINFO;

// static stuff in this file.
LONG FAR PASCAL _loadds ICAVIDrawProc(DWORD id, HDRVR hDriver, UINT uiMessage, LPARAM lParam1, LPARAM lParam2);

static LONG AVIDrawOpen(ICOPEN FAR * icopen);
static LONG AVIDrawClose(PINSTINFO pi);
static LONG AVIDrawGetInfo(ICINFO FAR *icinfo, LONG lSize);
static LONG AVIDrawQuery(PINSTINFO pi, LPBITMAPINFOHEADER lpbiIn);
static LONG AVIDrawSuggestFormat(PINSTINFO pi, ICDRAWSUGGEST FAR *lpicd, LONG cbicd);
static LONG AVIDrawBegin(PINSTINFO pi, ICDRAWBEGIN FAR *lpicd, LONG cbicd);
static LONG AVIDraw(PINSTINFO pi, ICDRAW FAR *lpicd, LONG cbicd);
static LONG AVIDrawEnd(PINSTINFO pi);
static LONG AVIDrawChangePalette(PINSTINFO pi, LPBITMAPINFOHEADER lpbi);

/***************************************************************************
 ***************************************************************************/

LONG FAR PASCAL _loadds ICAVIDrawProc(DWORD id, HDRVR hDriver, UINT uiMessage, LPARAM lParam1, LPARAM lParam2)
{
    INSTINFO *pi = (INSTINFO *)(UINT)id;

    switch (uiMessage)
    {
        case DRV_LOAD:
        case DRV_FREE:
            return 1;

        /*********************************************************************
            open
        *********************************************************************/

        case DRV_OPEN:
            if (lParam2 == 0L)
                return 1;

            return AVIDrawOpen((ICOPEN FAR *)lParam2);

	case DRV_CLOSE:
            return AVIDrawClose(pi);

        /*********************************************************************
            Configure/Info messages
        *********************************************************************/

        case DRV_QUERYCONFIGURE:    // configuration from drivers applet
            return 0;

        case DRV_CONFIGURE:
            return 1;

        case ICM_CONFIGURE:
        case ICM_ABOUT:
            return ICERR_UNSUPPORTED;

        /*********************************************************************
            state messages
        *********************************************************************/

        case ICM_GETSTATE:
        case ICM_SETSTATE:
            return 0L;

//      case ICM_GETINFO:
//          return AVIDrawGetInfo((ICINFO FAR *)lParam1, lParam2);

        /*********************************************************************
            decompress messages
        *********************************************************************/

        case ICM_DRAW_QUERY:
            return AVIDrawQuery(pi, (LPBITMAPINFOHEADER)lParam1);

	case ICM_DRAW_SUGGESTFORMAT:
	    return AVIDrawSuggestFormat(pi, (ICDRAWSUGGEST FAR *) lParam1, lParam2);

        case ICM_DRAW_BEGIN:
	    return AVIDrawBegin(pi, (ICDRAWBEGIN FAR *) lParam1, lParam2);

	case ICM_DRAW_REALIZE:
	    pi->hdc = (HDC) lParam1;
	
	    if (!pi->hdc || !pi->hdd)
		break;

	    pi->fBackground = (BOOL) lParam2;
	
	    return DrawDibRealize(pi->hdd, pi->hdc, pi->fBackground);

	case ICM_DRAW_GET_PALETTE:
	    if (!pi->hdd)
		break;

	    return (LONG) DrawDibGetPalette(pi->hdd);
	
        case ICM_DRAW:
            return AVIDraw(pi, (ICDRAW FAR *)lParam1, lParam2);

	case ICM_DRAW_CHANGEPALETTE:
	    return AVIDrawChangePalette(pi, (LPBITMAPINFOHEADER) lParam1);
	
        case ICM_DRAW_END:
            return AVIDrawEnd(pi);

        case ICM_DRAW_START:
            return DrawDibStart(pi->hdd, pi->rate);

        case ICM_DRAW_STOP:
            return DrawDibStop(pi->hdd);

        /*********************************************************************
            standard driver messages
        *********************************************************************/

        case DRV_DISABLE:
        case DRV_ENABLE:
            return 1;

        case DRV_INSTALL:
        case DRV_REMOVE:
            return 1;
    }

    return ICERR_UNSUPPORTED;
}

/*****************************************************************************
 *
 * AVIDrawOpen() is called from the DRV_OPEN message
 *
 ****************************************************************************/

static LONG AVIDrawOpen(ICOPEN FAR * icopen)
{
    INSTINFO *  pinst;

    if (icopen->dwFlags & ICMODE_COMPRESS)
        return 0;

    if (icopen->dwFlags & ICMODE_DECOMPRESS)
        return 0;

    pinst = (INSTINFO *)LocalAlloc(LPTR, sizeof(INSTINFO));

    if (!pinst)
    {
        icopen->dwError = ICERR_MEMORY;
        return 0;
    }

    DPF2(("*** AVIDrawOpen()\n"));

    //
    // init structure
    //
    pinst->hdd = DrawDibOpen();

#ifdef DEBUG
    ghdd = pinst->hdd;
#endif

    //
    // return success.
    //
    icopen->dwError = ICERR_OK;

    return (LONG) (UINT) pinst;
}

/*****************************************************************************
 *
 * Close() is called on the DRV_CLOSE message.
 *
 ****************************************************************************/
static LONG AVIDrawClose(PINSTINFO pi)
{
    DPF2(("*** AVIDrawClose()\n"));

    if (pi->hdd) {
	DrawDibClose(pi->hdd);
    }
    LocalFree((HLOCAL) pi);

    return 1;
}

#if 0
/*****************************************************************************
 *
 * AVIDrawGetInfo() implements the ICM_GETINFO message
 *
 ****************************************************************************/
static LONG AVIDrawGetInfo(ICINFO FAR *icinfo, LONG lSize)
{
    if (icinfo == NULL)
        return sizeof(ICINFO);

    if (lSize < sizeof(ICINFO))
        return 0;

    icinfo->dwSize	    = sizeof(ICINFO);
    icinfo->fccType         = FOURCC_VIDS;
    icinfo->fccHandler      = FOURCC_AVIDraw;
    icinfo->dwFlags	    = VIDCF_DRAW;    // supports inter-frame
    icinfo->dwVersion       = VERSION_AVIDraw;
    icinfo->dwVersionICM    = ICVERSION;
    icinfo->szName[0]       = 0;
    icinfo->szDescription[0]= 0;

    return sizeof(ICINFO);
}
#endif

/*****************************************************************************
 *
 * AVIDrawQuery() implements ICM_DRAW_QUERY
 *
 ****************************************************************************/
static LONG AVIDrawQuery(PINSTINFO pi,
			 LPBITMAPINFOHEADER lpbiIn)
{
    //
    // determine if the input DIB data is in a format we like.
    //
    if (lpbiIn == NULL)
        return ICERR_BADFORMAT;

    //
    // determine if the input DIB data is in a format we like.
    //

    // !!! Do we need a DrawDibQuery or something here to let this handle
    // any compressed format?

#ifdef DRAWDIBNODECOMPRESS
    if (lpbiIn->biCompression != BI_RGB &&
#if 0
        !(lpbiIn->biBitCount == 8 && lpbiIn->biCompression == BI_RLE8))
#else
	1)
#endif
        return ICERR_BADFORMAT;
#endif

    return ICERR_OK;
}


static LONG AVIDrawSuggestFormat(PINSTINFO pi, ICDRAWSUGGEST FAR *lpicd, LONG cbicd)
{
    HIC hic;

    if (lpicd->lpbiSuggest == NULL)
	return sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);

    hic = ICGetDisplayFormat(NULL, lpicd->lpbiIn, lpicd->lpbiSuggest,
			     0, lpicd->dxDst, lpicd->dyDst);

    if (hic)
	ICClose(hic);


    return sizeof(BITMAPINFOHEADER) + lpicd->lpbiSuggest->biClrUsed * sizeof(RGBQUAD);
}

/*****************************************************************************
 *
 * AVIDrawBegin() implements ICM_DRAW_BEGIN
 *
 ****************************************************************************/

static LONG AVIDrawBegin(PINSTINFO pi, ICDRAWBEGIN FAR *lpicd, LONG cbicd)
{
    LONG    l;
    UINT    wFlags;

    if (lpicd->dwFlags & ICDRAW_FULLSCREEN)
        return ICERR_UNSUPPORTED;

    l = AVIDrawQuery(pi, lpicd->lpbi);

    if ((l != 0) || (lpicd->dwFlags & ICDRAW_QUERY))
	return l;

    // Copy over whatever we want to remember
    pi->hdc = lpicd->hdc;
    pi->xDst = lpicd->xDst;
    pi->yDst = lpicd->yDst;
    pi->dxDst = lpicd->dxDst;
    pi->dyDst = lpicd->dyDst;
    pi->xSrc = lpicd->xSrc;
    pi->ySrc = lpicd->ySrc;
    pi->dxSrc = lpicd->dxSrc;
    pi->dySrc = lpicd->dySrc;
    pi->rate = muldiv32(lpicd->dwScale,1000000,lpicd->dwRate);

    // !!! Should this be done somewhere else? drawdib mabey!

    if (pi->hdc)
        SetStretchBltMode(pi->hdc, COLORONCOLOR);

    wFlags = 0;

    // !!! We need some way to have a "stupid" mode here....
    if (lpicd->dwFlags & ICDRAW_BUFFER)
        wFlags |= DDF_BUFFER;

    // Don't animate if we're realizing in the background
    if (lpicd->dwFlags & ICDRAW_ANIMATE && !(pi->fBackground))
        wFlags |= DDF_ANIMATE;

    //
    //  remember if this is RLE because we may need to hack it later.
    //
    pi->fRle = lpicd->lpbi->biCompression == BI_RLE8;
    pi->biSizeImage = (DWORD)(((UINT)lpicd->lpbi->biWidth+3)&~3)*(DWORD)(UINT)lpicd->lpbi->biHeight;

    DPF2(("*** AVIDrawBegin()\n"));

    if (lpicd->hpal == (HPALETTE)MCI_AVI_SETVIDEO_PALETTE_HALFTONE) {
        DrawDibSetPalette(pi->hdd, NULL);
        wFlags |= DDF_HALFTONE;
    }
    else
        DrawDibSetPalette(pi->hdd, lpicd->hpal);

    if (!DrawDibBegin(pi->hdd, pi->hdc,
		 pi->dxDst, pi->dyDst,
		 lpicd->lpbi,
		 pi->dxSrc, pi->dySrc,
		 wFlags))
	return ICERR_UNSUPPORTED;

    if (pi->hdc)
        DrawDibRealize(pi->hdd, pi->hdc, pi->fBackground);

    return ICERR_OK;
}


/*****************************************************************************
 *
 * AVIDraw() implements ICM_DRAW
 *
 ****************************************************************************/

static LONG AVIDraw(PINSTINFO pi, ICDRAW FAR *lpicd, LONG cbicd)
{
    UINT  wFlags;
    BOOL  f;

    wFlags = DDF_SAME_DRAW|DDF_SAME_HDC;  // !!! Right flags?

    if ((lpicd->lpData == NULL) || (lpicd->cbData == 0)) {

        if ((lpicd->dwFlags & ICDRAW_UPDATE) || pi->fNeedUpdate) {
            DrawDibRealize(pi->hdd, pi->hdc, pi->fBackground);
            wFlags |= DDF_UPDATE;
	    pi->fNeedUpdate = FALSE;
        }
	else
            return ICERR_OK;  // no data to draw.
    }
    else {
        if (lpicd->dwFlags & ICDRAW_PREROLL) {
            wFlags |= DDF_DONTDRAW;
	    pi->fNeedUpdate = TRUE;
	} else if (lpicd->dwFlags & ICDRAW_HURRYUP) {
            wFlags |= DDF_HURRYUP;
	    pi->fNeedUpdate = TRUE;
	} else
	    pi->fNeedUpdate = FALSE;

        if (lpicd->dwFlags & ICDRAW_NOTKEYFRAME)
            wFlags |= DDF_NOTKEYFRAME;

        //
        // if we get a update while playing and we are drawing RLE delta's
        // make sure we update.
        //
        if (pi->fRle && (lpicd->dwFlags & ICDRAW_UPDATE)) {
            DrawDibDraw(pi->hdd, pi->hdc, pi->xDst, pi->yDst,
                0,0,NULL,NULL,0,0,0,0,DDF_UPDATE|DDF_SAME_HDC|DDF_SAME_DRAW);
        }
    }

    //
    // We need a hack here for the RLE case, to make sure that
    // DIBs are marked correctly as BI_RLE8 or BI_RGB....
    //
    if (pi->fRle) {
        if (lpicd->cbData == pi->biSizeImage)
            ((LPBITMAPINFOHEADER)lpicd->lpFormat)->biCompression = BI_RGB;
        else
            ((LPBITMAPINFOHEADER)lpicd->lpFormat)->biCompression = BI_RLE8;
    }

    f = DrawDibDraw(pi->hdd, pi->hdc,
		pi->xDst, pi->yDst,
		pi->dxDst, pi->dyDst,
		lpicd->lpFormat,
                lpicd->lpData,
		pi->xSrc, pi->ySrc,
		pi->dxSrc, pi->dySrc,
                wFlags);

    if (pi->fRle)
        ((LPBITMAPINFOHEADER)lpicd->lpFormat)->biCompression = BI_RLE8;

    if (!f) {

	if (wFlags & DDF_UPDATE)
            DPF(("DrawDibUpdate failed\n"));
        else
            DPF(("DrawDibDraw failed\n"));

	if (wFlags & DDF_UPDATE)
	    return ICERR_CANTUPDATE;
	else
            return ICERR_ERROR;
    }

    return ICERR_OK;
}

static LONG AVIDrawChangePalette(PINSTINFO pi, LPBITMAPINFOHEADER lpbi)
{
    PALETTEENTRY    ape[256];
    LPRGBQUAD	    lprgb;
    int i;

    lprgb = (LPRGBQUAD) ((LPBYTE) lpbi + lpbi->biSize);

    for (i = 0; i < (int) lpbi->biClrUsed; i++) {
	ape[i].peRed = lprgb[i].rgbRed;
	ape[i].peGreen = lprgb[i].rgbGreen;
	ape[i].peBlue = lprgb[i].rgbBlue;
	ape[i].peFlags = 0;
    }
	
    DrawDibChangePalette(pi->hdd, 0, (int) lpbi->biClrUsed,
				 (LPPALETTEENTRY)ape);

    return ICERR_OK;
}

/*****************************************************************************
 *
 * AVIDrawEnd() implements ICM_DRAW_END
 *
 ****************************************************************************/

static LONG AVIDrawEnd(PINSTINFO pi)
{
    return ICERR_OK;
}