/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: ddblt.cpp,v 1.3.34.2 2004/07/09 01:59:19 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxcom.h"
#include "hxtypes.h"
#include "hxwintyp.h"
#include "ddblt.h"

#ifdef __cplusplus
extern "C"
#endif

#if 0
static void MovePlain(
    UCHAR *srcPtr, int srcPitch, UCHAR *destPtr, int destPitch,
    int plainWidth, int plainHeight)
{
    register int i;

    for (i = 0; i < plainHeight; i ++)
    {
        /* must be done using MMX movqs,
         * but this is a simple test app, not a production code: */
        memcpy(destPtr, srcPtr, plainWidth); /* Flawfinder: ignore */
        srcPtr += srcPitch;
        destPtr += destPitch;
    }
}

#endif //0

CDDBlt::CDDBlt()
 :  CWinBlt()
 ,  m_pDD(NULL)
 ,  m_pPrimary(NULL)
 ,  m_pOffscreen(NULL)

{
}

CDDBlt::~CDDBlt()
{
    DestroySurface(0);
}

HX_RESULT CDDBlt::CreateSurface(int cidIn, int& cidOut,
                                int nWidth, int nHeight,
                                int nFlags, HWND hWnd,
                                int nCountIn, int& nCountOut)
{
    HRESULT hr;

    if (!m_pDD)
    {
        hr = DirectDrawCreate(NULL, &m_pDD, NULL);
        if (hr)
			return HXR_FAIL;
    }

    IDirectDraw2 *pDD2 = NULL;
    hr = m_pDD->QueryInterface(IID_IDirectDraw2, (void **)&pDD2);

    if (!pDD2)
    {
	HX_RELEASE(m_pDD);
	return HXR_FAIL;
    }

    m_hWnd = hWnd;
    hr = pDD2->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
	if (hr)
	{
	    HX_RELEASE(pDD2);
		return HXR_FAIL;
	}

    if (!m_pPrimary)
    {
	// Create primary surface
	DDSURFACEDESC ddsd;
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

	hr = pDD2->CreateSurface (&ddsd, &m_pPrimary, NULL);

	if (!m_pPrimary || hr != DD_OK)
	{
	    HX_RELEASE(m_pDD);
	    HX_RELEASE(pDD2);
	    return HXR_FAIL;
	}

	IDirectDrawClipper *pDDclip;
	if (DD_OK == pDD2->CreateClipper(0, &pDDclip, NULL))
	{
	    pDDclip->SetHWnd(0, m_hWnd);
	    m_pPrimary->SetClipper(pDDclip);
	    HX_RELEASE(pDDclip);
	}
    }

    // Create offscreen surface
    DDSURFACEDESC ddsd;
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
    ddsd.dwWidth = nWidth;
    ddsd.dwHeight = nHeight;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

#ifndef HELIX_FEATURE_DD_AUTOPIXELFORMAT
    ddsd.dwFlags |= DDSD_PIXELFORMAT;
    ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
#endif  //!HELIX_FEATURE_DD_AUTOPIXELFORMAT

    const int nOutputFormats = 6;
    UINT32 fourCC[nOutputFormats];
    UINT32 bbp[nOutputFormats];
    UINT32 rbm[nOutputFormats];
    UINT32 gbm[nOutputFormats];
    UINT32 bbm[nOutputFormats];
    UINT32 flags[nOutputFormats];
    int    cid[nOutputFormats];

    memset(&rbm, 0, sizeof(rbm));
    memset(&gbm, 0, sizeof(gbm));
    memset(&bbm, 0, sizeof(bbm));
    memset(&flags, 0, sizeof(flags));

    int nOutputTypes = 0;

#if defined (HELIX_FEATURE_CC_I420out)
    fourCC[nOutputTypes] = MAKEFOURCC('I', '4', '2', '0');
    bbp[nOutputTypes] = 12;
    cid[nOutputTypes] = CID_I420;
    flags[nOutputTypes] = DDPF_FOURCC;

    ++nOutputTypes;
#endif //HELIX_FEATURE_CC_I420out

#if defined (HELIX_FEATURE_CC_YV12out)
    fourCC[nOutputTypes] = MAKEFOURCC('Y', 'V', '1', '2');
    bbp[nOutputTypes] = 12;
    cid[nOutputTypes] = CID_YV12;
    flags[nOutputTypes] = DDPF_FOURCC;

    ++nOutputTypes;
#endif //HELIX_FEATURE_CC_YV12out

#if defined (HELIX_FEATURE_CC_YUY2out)
    fourCC[nOutputTypes] = MAKEFOURCC('Y', 'U', 'Y', '2');
    bbp[nOutputTypes] = 16;
    cid[nOutputTypes] = CID_YUY2;
    flags[nOutputTypes] = DDPF_FOURCC;

    ++nOutputTypes;
#endif //HELIX_FEATURE_CC_YUY2out

#if defined (HELIX_FEATURE_CC_UYVYout)
    fourCC[nOutputTypes] = MAKEFOURCC('U', 'Y', 'V', 'Y');
    bbp[nOutputTypes] = 16;
    cid[nOutputTypes] = CID_UYVY;
    flags[nOutputTypes] = DDPF_FOURCC;

    ++nOutputTypes;
#endif //HELIX_FEATURE_CC_UYVYout

#if defined (HELIX_FEATURE_CC_RGB32out)
    fourCC[nOutputTypes] = BI_RGB;
    bbp[nOutputTypes] = 32;
    cid[nOutputTypes] = CID_RGB32;

    rbm[nOutputTypes] = 0xFF0000;
    gbm[nOutputTypes] = 0x00FF00;
    bbm[nOutputTypes] = 0x0000FF;
#if defined (HELIX_FEATURE_DD_AUTOPIXELFORMAT)
    flags[nOutputTypes] = DDPF_RGB|DDPF_ALPHAPREMULT|DDPF_ALPHAPIXELS;
#else
	flags[nOutputTypes] = DDPF_RGB;
#endif //HELIX_FEATURE_DD_AUTOPIXELFORMAT
    ++nOutputTypes;
#endif //HELIX_FEATURE_CC_RGB32out

#if defined (HELIX_FEATURE_CC_RGB565out)
    fourCC[nOutputTypes] = BI_RGB;
    bbp[nOutputTypes] = 16;
    cid[nOutputTypes] = CID_RGB565;

    rbm[nOutputTypes] = 0x7C00;
    gbm[nOutputTypes] = 0x03E0;
    bbm[nOutputTypes] = 0x001F;
    flags[nOutputTypes] = DDPF_RGB;

    ++nOutputTypes;
#endif //HELIX_FEATURE_CC_RGB565out



    for (int i=0; i<nOutputTypes; i++)
    {
		ddsd.ddpfPixelFormat.dwFourCC       = fourCC[i];
        ddsd.ddpfPixelFormat.dwRGBBitCount  = bbp[i];
        ddsd.ddpfPixelFormat.dwRBitMask     = rbm[i];
        ddsd.ddpfPixelFormat.dwGBitMask     = gbm[i];
        ddsd.ddpfPixelFormat.dwBBitMask     = bbm[i];
        ddsd.ddpfPixelFormat.dwFlags        = flags[i];

#ifdef _DEBUG
		HX_TRACE( "CDDBlt::CreateSurface: BEFORE CreateSurface(Offscreen) returned: \n");
		HX_TRACE( "   CCFormat: 0x%08X \n", ddsd.ddpfPixelFormat.dwFourCC );
		HX_TRACE( "   RGBBitCount (bbp): %d \n", ddsd.ddpfPixelFormat.dwRGBBitCount );
		HX_TRACE( "   Flags: 0x%08X \n", ddsd.ddpfPixelFormat.dwFlags );
		HX_TRACE( "   RBitMask: 0x%08X \n", ddsd.ddpfPixelFormat.dwRBitMask );
		HX_TRACE( "   GBitMask: 0x%08X \n", ddsd.ddpfPixelFormat.dwGBitMask );
		HX_TRACE( "   BBitMask: 0x%08X \n", ddsd.ddpfPixelFormat.dwBBitMask );
#endif //_DEBUG

		hr = pDD2->CreateSurface (&ddsd, &m_pOffscreen, NULL);
        if (DD_OK == hr)	
        {
            m_nCID = cid[i];

#ifdef _DEBUG
			memset( &(ddsd.ddpfPixelFormat), 0, sizeof(ddsd.ddpfPixelFormat) );
		    ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
			hr = m_pOffscreen->GetPixelFormat( &(ddsd.ddpfPixelFormat) );
			if (FAILED(hr))
			{
			}
			else
			{
				HX_TRACE( "CDDBlt::CreateSurface: AFTER CreateSurface(Offscreen) returned: \n");
				HX_TRACE( "   CCFormat: 0x%08X \n", ddsd.ddpfPixelFormat.dwFourCC );
				HX_TRACE( "   RGBBitCount (bbp): %d \n", ddsd.ddpfPixelFormat.dwRGBBitCount );
				HX_TRACE( "   Flags: 0x%08X \n", ddsd.ddpfPixelFormat.dwFlags );
				HX_TRACE( "   RBitMask: 0x%08X \n", ddsd.ddpfPixelFormat.dwRBitMask );
				HX_TRACE( "   GBitMask: 0x%08X \n", ddsd.ddpfPixelFormat.dwGBitMask );
				HX_TRACE( "   BBitMask: 0x%08X \n", ddsd.ddpfPixelFormat.dwBBitMask );
			}
#endif //_DEBUG

            break;
        }
    }

    HX_RELEASE(pDD2);

    // Don't use DirectDraw
    if (!m_pOffscreen)
    {
        HX_RELEASE(m_pPrimary);
        HX_RELEASE(m_pDD);

        return HXR_FAIL;
    }

    return HXR_OK;
}

HX_RESULT CDDBlt::LockSurface(UCHAR** ppDestPtr,
                              LONG32* pnDestPitch,
                              int& cid,
                              REF(HXxSize) dstSize,
                              int nIndex)
{
	if (!m_pPrimary || !m_pOffscreen)
		return HXR_FAIL;

    DDSURFACEDESC ddsd;
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);

    HRESULT hr;
    for (int i=0; i<3; i++)
    {
	hr = m_pOffscreen->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);

        if (DDERR_SURFACELOST == hr)
	{
	    m_pOffscreen->Restore();
	    m_pPrimary->Restore();
	}
	else if (DD_OK == hr)
	{
	    *ppDestPtr = (UCHAR*)ddsd.lpSurface;
	    *pnDestPitch = ddsd.lPitch;

	    break;
	}
	else
	{
	    return HXR_FAIL;
	}
    }

    return HXR_OK;
}

HX_RESULT CDDBlt::FillSurface(int cidIn,
                              UCHAR* pSrcBuffer,
                              HXxSize* pSrcSize,
                              HXxRect* prSrcRect,
                              UCHAR* pDstBuffer,
                              LONG32 nDstPitch,
                              HXxRect* prDestRect)
{
#if 1
    return HXR_FAIL;

#else
    // If input and output are both planar, just copy each plane
    if (CID_YV12 != m_nCID ||
        (cidIn != CID_I420 && cidIn != CID_XING))
        return HXR_FAIL;

    INT32 yPitch = pSrcSize->cx;
    INT32 uvPitch = pSrcSize->cx/2;
    UCHAR *pU = pSrcBuffer + pSrcSize->cy*yPitch;
    UCHAR *pV = pU + pSrcSize->cy/2*uvPitch;

    if (cidIn == CID_XING)
    {
        yPitch = 768;
        uvPitch = 768;
        pU = pSrcBuffer + pSrcSize->cy*yPitch;
        pV = pU + yPitch/2;
    }

    // We only support YV12
    UCHAR *pDestV = pDstBuffer + pSrcSize->cy*nDstPitch;
    UCHAR *pDestU = pDestV + pSrcSize->cy*nDstPitch/4;

    MovePlain(pSrcBuffer, yPitch, pDstBuffer, nDstPitch,
              pSrcSize->cx,pSrcSize->cy);

    MovePlain(pU, uvPitch, pDestU, nDstPitch/2,
              pSrcSize->cx/2,pSrcSize->cy/2);

    MovePlain(pV, uvPitch, pDestV, nDstPitch/2,
              pSrcSize->cx/2,pSrcSize->cy/2);

#endif //1
    return HXR_OK;
}

HX_RESULT CDDBlt::UnlockSurface(UCHAR* pSurfPtr, int nIndex)
{
    m_pOffscreen->Unlock(NULL);
    return HXR_OK;
}

HX_RESULT CDDBlt::RenderSurface(HXxSize* pSrcSize,
                                HXxRect* prSrcRect,
                                HXxRect* prDestRect,
                                int nIndex)
{
    // Map to screen coordinates
    POINT   po = {0,0};
    ClientToScreen(m_hWnd, &po);

    RECT rc;
    GetClientRect(m_hWnd, &rc);

    rc.left += po.x;
    rc.right += po.x;
    rc.top += po.y;
    rc.bottom += po.y;

    DDBLTFX ddbltfx;
    memset(&ddbltfx, 0, sizeof(ddbltfx));
    ddbltfx.dwSize = sizeof(ddbltfx);
    ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
    HRESULT hr = m_pPrimary->Blt(&rc, m_pOffscreen, NULL, DDBLT_WAIT, &ddbltfx);

    if (DD_OK == hr)
        return HXR_OK;
    else
        return HXR_FAIL;
}

HX_RESULT CDDBlt::DestroySurface(int cid)
{
    HX_RELEASE(m_pPrimary);
    HX_RELEASE(m_pOffscreen);
    HX_RELEASE(m_pDD);

    return HXR_OK;
}
