//------------------------------------------------------------------------------
// File: alloclib.cpp
//
// Desc: DirectShow sample code
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------

#include "project.h"

#include <malloc.h>

#include "AllocLib.h"


/*****************************Private*Routine******************************\
* YV12PaintSurfaceBlack
*
\**************************************************************************/
HRESULT
YV12PaintSurfaceBlack(
    LPDIRECTDRAWSURFACE7 pDDrawSurface
    )
{
    CheckPointer(pDDrawSurface,E_POINTER);

    AMTRACE((TEXT("YV12PaintSurfaceBlack")));
    HRESULT hr = NOERROR;
    DDSURFACEDESC2 ddsd;

    // now lock the surface so we can start filling the surface with black
    ddsd.dwSize = sizeof(ddsd);

    for ( ;; ) {

        hr = pDDrawSurface->Lock(NULL, &ddsd,
                                 DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);

        if (hr == DD_OK || hr != DDERR_WASSTILLDRAWING) {
            break;
        }

        Sleep(1);
    }

    if (hr == DD_OK)
    {
        DWORD y;
        LPBYTE pDst = (LPBYTE)ddsd.lpSurface;
        LONG  OutStride = ddsd.lPitch;
        DWORD VSize = ddsd.dwHeight;
        DWORD HSize = ddsd.dwWidth;

        CheckPointer(pDst,E_UNEXPECTED);

        // Y Component
        for (y = 0; y < VSize; y++) {
            FillMemory(pDst, HSize, (BYTE)0x10);     // 1 line at a time
            pDst += OutStride;
        }

        HSize /= 2;
        VSize /= 2;
        OutStride /= 2;

        // Cb Component
        for (y = 0; y < VSize; y++) {
            FillMemory(pDst, HSize, (BYTE)0x80);     // 1 line at a time
            pDst += OutStride;
        }

        // Cr Component
        for (y = 0; y < VSize; y++) {
            FillMemory(pDst, HSize, (BYTE)0x80);     // 1 line at a time
            pDst += OutStride;
        }

        pDDrawSurface->Unlock(NULL);
    }

    return hr;
}


/******************************Public*Routine******************************\
* YUV16PaintSurfaceBlack
*
\**************************************************************************/
HRESULT
YUV16PaintSurfaceBlack(
    LPDIRECTDRAWSURFACE7 pdds,
    DWORD dwBlack
    )
{
    CheckPointer(pdds,E_POINTER);

    AMTRACE((TEXT("YUV16PaintSurfaceBlack")));
    HRESULT hr = NOERROR;
    DDSURFACEDESC2 ddsd;

    // Lock the surface so we can start filling the surface with black
    ddsd.dwSize = sizeof(ddsd);

    for ( ;; ) {

        hr = pdds->Lock(NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);

        if (hr == DD_OK || hr != DDERR_WASSTILLDRAWING) {
            break;
        }

        Sleep(1);
    }

    if (hr == DD_OK)
    {
        DWORD y, x;
        LPDWORD pDst = (LPDWORD)ddsd.lpSurface;
        CheckPointer(pDst,E_UNEXPECTED);
        
        LONG  OutStride = ddsd.lPitch;

        for (y = 0; y < ddsd.dwHeight; y++) {

            for (x = 0; x < ddsd.dwWidth / 2; x++) {
                pDst[x] = dwBlack;
            }

            // Dont forget that the stride is a byte count
            *((LPBYTE*)&pDst) += OutStride;
        }

        pdds->Unlock(NULL);
    }

    return hr;
}


/*****************************Private*Routine******************************\
* BlackPaintProc
*
\**************************************************************************/
HRESULT
BlackPaintProc(
    LPDIRECTDRAWSURFACE7 pDDrawSurface,
    DDSURFACEDESC2* lpddSurfaceDesc
    )
{
    CheckPointer(pDDrawSurface,E_POINTER);

    AMTRACE((TEXT("BlackPaintProc")));

    //
    // If the surface is YUV take care of the types that we
    // know the pixel format for.  Those surfaces that we don't know
    // about will get painted '0' which may be bright green for
    // YUV surfaces.
    //

    if (lpddSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC) {

        //
        // compute the black value if the fourCC code is suitable,
        // otherwise can't handle it
        //

        switch (lpddSurfaceDesc->ddpfPixelFormat.dwFourCC) {

        case mmioFOURCC('Y','V','1','2'):
        case mmioFOURCC('I','4','2','0'):
        case mmioFOURCC('I','Y','U','V'):
            return YV12PaintSurfaceBlack(pDDrawSurface);

        case mmioFOURCC('Y','U','Y','2'):
            return YUV16PaintSurfaceBlack(pDDrawSurface, 0x80108010);

        case mmioFOURCC('U','Y','V','Y'):
            return YUV16PaintSurfaceBlack(pDDrawSurface, 0x10801080);
        }
    }

    DDBLTFX ddFX;
    INITDDSTRUCT(ddFX);

    return pDDrawSurface->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddFX);
}


/*****************************Private*Routine******************************\
* PaintSurfaceBlack
*
\**************************************************************************/
HRESULT
PaintDDrawSurfaceBlack(
    LPDIRECTDRAWSURFACE7 pDDrawSurface
    )
{
    CheckPointer(pDDrawSurface,E_POINTER);

    AMTRACE((TEXT("PaintDDrawSurfaceBlack")));

    LPDIRECTDRAWSURFACE7 *ppDDrawSurface = NULL;
    DDSCAPS2 ddSurfaceCaps;
    DDSURFACEDESC2 ddSurfaceDesc;
    DWORD dwAllocSize;
    DWORD i = 0, dwBackBufferCount = 0;

    // get the surface description
    INITDDSTRUCT(ddSurfaceDesc);

    HRESULT hr = pDDrawSurface->GetSurfaceDesc(&ddSurfaceDesc);
    if (SUCCEEDED(hr)) {

        if (ddSurfaceDesc.dwFlags & DDSD_BACKBUFFERCOUNT) {
            dwBackBufferCount = ddSurfaceDesc.dwBackBufferCount;
        }

        hr = BlackPaintProc(pDDrawSurface, &ddSurfaceDesc);
        if (FAILED(hr))
        {
            DbgLog((LOG_ERROR,1,
                    TEXT("pDDrawSurface->Blt failed, hr = 0x%x"), hr));
            return hr;
        }

        if (dwBackBufferCount > 0) {

            dwAllocSize = (dwBackBufferCount + 1) * sizeof(LPDIRECTDRAWSURFACE);
            ppDDrawSurface = (LPDIRECTDRAWSURFACE7*)_alloca(dwAllocSize);
            if (!ppDDrawSurface)
                return E_OUTOFMEMORY;
                
            ZeroMemory(ppDDrawSurface, dwAllocSize);
            ZeroMemory(&ddSurfaceCaps, sizeof(ddSurfaceCaps));
            ddSurfaceCaps.dwCaps = DDSCAPS_FLIP | DDSCAPS_COMPLEX;

            if( DDSCAPS_OVERLAY & ddSurfaceDesc.ddsCaps.dwCaps ) {
                ddSurfaceCaps.dwCaps |= DDSCAPS_OVERLAY;
            }

            for (i = 0; i < dwBackBufferCount; i++) {

                LPDIRECTDRAWSURFACE7 pCurrentDDrawSurface = NULL;
                if (i == 0) {
                    pCurrentDDrawSurface = pDDrawSurface;
                }
                else {
                    pCurrentDDrawSurface = ppDDrawSurface[i];
                }
                ASSERT(pCurrentDDrawSurface);


                //
                // Get the back buffer surface and store it in the
                // next (in the circular sense) entry
                //

                hr = pCurrentDDrawSurface->GetAttachedSurface(
                        &ddSurfaceCaps,
                        &ppDDrawSurface[i + 1]);

                if (FAILED(hr))
                {
                    DbgLog((LOG_ERROR,1,
                            TEXT("Function pDDrawSurface->GetAttachedSurface ")
                            TEXT("failed, hr = 0x%x"), hr ));
                    break;
                }

                ASSERT(ppDDrawSurface[i+1]);

                //
                // Peform a DirectDraw colorfill BLT
                //

                hr = BlackPaintProc(ppDDrawSurface[i + 1], &ddSurfaceDesc);
                if (FAILED(hr)) {
                    DbgLog((LOG_ERROR,1,
                            TEXT("ppDDrawSurface[i + 1]->Blt failed, ")
                            TEXT("hr = 0x%x"), hr));
                    break;
                }
            }
        }
    }

    if (ppDDrawSurface) {
        for (i = 0; i < dwBackBufferCount + 1; i++) {
            if (ppDDrawSurface[i]) {
                ppDDrawSurface[i]->Release();
            }
        }
    }

    if (hr != DD_OK) {
        DbgLog((LOG_ERROR, 1, TEXT("PaintSurfaceBlack failed")));
        hr = S_OK;
    }

    return hr;
}
