root/modules/dx_hw/dx_2d.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pixelformat_yuv
  2. get_win_4CC
  3. DD_ClearBackBuffer
  4. CreateBackBuffer
  5. InitDirectDraw
  6. DD_LockSurface
  7. DD_UnlockSurface
  8. DD_LockBackBuffer
  9. LockOSContext
  10. DD_BlitSurface
  11. DD_GetSurface
  12. DD_Blit
  13. is_yuv_supported
  14. is_yuv_planar
  15. DD_InitYUV
  16. DD_SetBackBufferSize
  17. DD_SetupDDraw

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / DirectX audio and video render module
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include "dx_hw.h"

#define DDCONTEXT       DDContext *dd = (DDContext *)dr->opaque;
#define DDBACK          DDSurface *pBack = (DDSurface *) gf_list_get(dd->surfaces, 0);




static Bool pixelformat_yuv(u32 pixel_format)
{
        switch (pixel_format) {
        case GF_PIXEL_YUY2:
        case GF_PIXEL_YVYU:
        case GF_PIXEL_UYVY:
        case GF_PIXEL_VYUY:
        case GF_PIXEL_Y422:
        case GF_PIXEL_UYNV:
        case GF_PIXEL_YUNV:
        case GF_PIXEL_V422:
        case GF_PIXEL_YV12:
        case GF_PIXEL_IYUV:
        case GF_PIXEL_I420:
        case GF_PIXEL_YV12_10:
        case GF_PIXEL_YUV444:
        case GF_PIXEL_YUV444_10:
        case GF_PIXEL_YUV422_10:
        case GF_PIXEL_YUV422:
                return GF_TRUE;
        default:
                return GF_FALSE;
        }
}


static u32 get_win_4CC(u32 pixel_format)
{
        switch (pixel_format) {
        case GF_PIXEL_YUY2:
                return mmioFOURCC('Y', 'U', 'Y', '2');
        case GF_PIXEL_YVYU:
                return mmioFOURCC('Y', 'V', 'Y', 'U');
        case GF_PIXEL_UYVY:
                return mmioFOURCC('U', 'Y', 'V', 'Y');
        case GF_PIXEL_VYUY:
                return mmioFOURCC('V', 'Y', 'U', 'Y');
        case GF_PIXEL_Y422:
                return mmioFOURCC('Y', '4', '2', '2');
        case GF_PIXEL_UYNV:
                return mmioFOURCC('U', 'Y', 'N', 'V');
        case GF_PIXEL_YUNV:
                return mmioFOURCC('Y', 'U', 'N', 'V');
        case GF_PIXEL_V422:
                return mmioFOURCC('V', '4', '2', '2');
        case GF_PIXEL_YV12:
                return mmioFOURCC('Y', 'V', '1', '2');
        case GF_PIXEL_IYUV:
                return mmioFOURCC('I', 'Y', 'U', 'V');
        case GF_PIXEL_I420:
                return mmioFOURCC('I', '4', '2', '0');
        case GF_PIXEL_YV12_10:
                return mmioFOURCC('P', '0', '1', '0');
        case GF_PIXEL_YUV444:
                return mmioFOURCC('Y', '4', '4', '4');
        case GF_PIXEL_YUV444_10:
                return mmioFOURCC('Y', '4', '1', '0');
        case GF_PIXEL_YUV422_10:
                return mmioFOURCC('Y', '2', '1', '0');
        case GF_PIXEL_YUV422:
                return mmioFOURCC('Y', '4', '4', '2');
        default:
                return 0;
        }
}

static GF_Err DD_ClearBackBuffer(GF_VideoOutput *dr, u32 color)
{
        HRESULT hr;
        RECT rc;
        DDBLTFX ddbltfx;
        DDCONTEXT;

        // Erase the background
        ZeroMemory( &ddbltfx, sizeof(ddbltfx) );
        ddbltfx.dwSize = sizeof(ddbltfx);
        switch (dd->pixelFormat) {
        case GF_PIXEL_RGB_565:
                ddbltfx.dwFillColor = GF_COL_TO_565(color);
                break;
        case GF_PIXEL_RGB_555:
                ddbltfx.dwFillColor = GF_COL_TO_555(color);
                break;
        default:
                ddbltfx.dwFillColor = color;
                break;
        }
        rc.left = rc.top = 0;
        rc.right = dd->width;
        rc.bottom = dd->height;

        hr = dd->pBack->lpVtbl->Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx);

        return FAILED(hr) ? GF_IO_ERR : GF_OK;
}

GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_system_memory)
{
        Bool force_reinit;
        HRESULT hr;
        const char *opt;
        DDSURFDESC ddsd;

        DDCONTEXT;

        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "HardwareMemory");
        if (opt) {
                if (use_system_memory) {
                        if (!strcmp(opt, "Always")) use_system_memory = GF_FALSE;
                } else {
                        if (!strcmp(opt, "Never")) use_system_memory = GF_TRUE;
                        else if (!strcmp(opt, "Auto")) dd->force_video_mem_for_yuv = GF_TRUE;
                }
        }

        force_reinit = GF_FALSE;
        if (use_system_memory && !dd->systems_memory) force_reinit = GF_TRUE;
        else if (!use_system_memory && dd->systems_memory) force_reinit = GF_TRUE;
        if (dd->pBack && !force_reinit&& !dd->fullscreen && (dd->width == Width) && (dd->height == Height) ) {
                return GF_OK;
        }

        SAFE_DD_RELEASE(dd->pBack);

        /*create backbuffer*/
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

        if (dd->systems_memory==2) {
                ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
        } else {
                if (!use_system_memory) {
                        dd->systems_memory = GF_FALSE;
                        ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
                } else {
                        ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
                        dd->systems_memory = GF_TRUE;
                }
        }
        if (dd->systems_memory) dr->hw_caps &= ~GF_VIDEO_HW_HAS_RGB;
        else  dr->hw_caps |= GF_VIDEO_HW_HAS_RGB;


        ddsd.dwWidth = Width;
        ddsd.dwHeight = Height;

        hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL );

        if (FAILED(hr)) {
                if (!dd->systems_memory) {
                        ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
                        ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
                        dd->systems_memory = 2;
                        GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Hardware Video not available for backbuffer)\n"));

                        hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL );
                }
                if (FAILED(hr)) return GF_IO_ERR;
        }

        /*store size*/
        if (!dd->fullscreen) {
                dd->width = Width;
                dd->height = Height;
        }
        DD_ClearBackBuffer(dr, 0xFF000000);

        if (!dd->yuv_init) DD_InitYUV(dr);
        GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] BackBuffer %d x %d created on %s memory\n", Width, Height, dd->systems_memory ? "System" : "Video"));
        return GF_OK;
}


GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height)
{
        HRESULT hr;
        DWORD cooplev;
        LPDIRECTDRAW ddraw;
        DDSURFDESC ddsd;
        DDPIXELFORMAT pixelFmt;
        LPDIRECTDRAWCLIPPER pcClipper;
        DDCONTEXT;
        if (!dd->cur_hwnd || !Width || !Height || !dd->DirectDrawCreate) return GF_BAD_PARAM;
        DestroyObjects(dd);

        if( FAILED( hr = dd->DirectDrawCreate(NULL, &ddraw, NULL ) ) )
                return GF_IO_ERR;

        hr = ddraw->lpVtbl->QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD);
        ddraw->lpVtbl->Release(ddraw);
        if (FAILED(hr)) return GF_IO_ERR;


        dd->switch_res = GF_FALSE;
        cooplev = DDSCL_NORMAL;
        /*Setup FS*/
        if (dd->fullscreen) {

                /*change display mode*/
                if (dd->switch_res) {
#ifdef USE_DX_3
                        hr = dd->pDD->lpVtbl->SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp);
#else
                        hr = dd->pDD->lpVtbl->SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp, 0, 0 );
#endif
                        if( FAILED(hr)) return GF_IO_ERR;
                }
                dd->NeedRestore = 1;
//              cooplev = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
        }

        hr = dd->pDD->lpVtbl->SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev);
        if( FAILED(hr) ) return GF_IO_ERR;

        /*create primary*/
        ZeroMemory( &ddsd, sizeof( ddsd ) );
        ddsd.dwSize = sizeof( ddsd );
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

        hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL);
        if( FAILED(hr) ) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DX Out] Failed creating primary surface: error %08x\n", hr));
                return GF_IO_ERR;
        }

        /*get pixel format of video board*/
        memset (&pixelFmt, 0, sizeof(pixelFmt));
        pixelFmt.dwSize = sizeof(pixelFmt);

        hr = dd->pPrimary->lpVtbl->GetPixelFormat(dd->pPrimary, &pixelFmt);
        if( FAILED(hr) ) return GF_IO_ERR;

        switch(pixelFmt.dwRGBBitCount) {
        case 16:
                if ((pixelFmt.dwRBitMask == 0xf800) && (pixelFmt.dwGBitMask == 0x07e0) && (pixelFmt.dwBBitMask == 0x001f))
                        dd->pixelFormat = GF_PIXEL_RGB_565;
                else if ((pixelFmt.dwRBitMask == 0x7c00) && (pixelFmt.dwGBitMask == 0x03e0) && (pixelFmt.dwBBitMask == 0x001f))
                        dd->pixelFormat = GF_PIXEL_RGB_555;
                else
                        return GF_NOT_SUPPORTED;
                dd->video_bpp = 16;
                break;
        case 24:
                if ((pixelFmt.dwRBitMask == 0x0000FF) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0xFF0000))
                        dd->pixelFormat = GF_PIXEL_BGR_24;
                else if ((pixelFmt.dwRBitMask == 0xFF0000) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0x0000FF))
                        dd->pixelFormat = GF_PIXEL_RGB_24;
                dd->video_bpp = 24;
                break;
        case 32:
                if ((pixelFmt.dwRBitMask == 0x0000FF) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0xFF0000)) {
                        dd->pixelFormat = dd->force_alpha ? GF_PIXEL_RGBA : GF_PIXEL_BGR_32;
                } else if ((pixelFmt.dwRBitMask == 0xFF0000) && (pixelFmt.dwGBitMask == 0x00FF00) && (pixelFmt.dwBBitMask == 0x0000FF)) {
                        dd->pixelFormat = dd->force_alpha ? GF_PIXEL_ARGB : GF_PIXEL_RGB_32;
                }
                dd->video_bpp = 32;
                break;
        default:
                return GF_IO_ERR;
        }

        hr = dd->pDD->lpVtbl->CreateClipper(dd->pDD, 0, &pcClipper, NULL);
        if( FAILED(hr))
                return GF_IO_ERR;

        hr = pcClipper->lpVtbl->SetHWnd(pcClipper, 0, dd->cur_hwnd);
        if( FAILED(hr) ) {
                pcClipper->lpVtbl->Release(pcClipper);
                return GF_IO_ERR;
        }
        hr = dd->pPrimary->lpVtbl->SetClipper(dd->pPrimary, pcClipper);
        if( FAILED(hr)) {
                pcClipper->lpVtbl->Release(pcClipper);
                return GF_IO_ERR;
        }
        pcClipper->lpVtbl->Release(pcClipper);


        dd->ddraw_init = GF_TRUE;
        /*if YUV not initialize, init using HW video memory to setup HW caps*/
        return GF_OK;
}

static GF_Err DD_LockSurface(DDContext *dd, GF_VideoSurface *vi, LPDDRAWSURFACE surface)
{
        HRESULT hr;
        DDSURFDESC desc;

        if (!dd || !vi || !surface) return GF_BAD_PARAM;

        ZeroMemory(&desc, sizeof(desc));
        desc.dwSize = sizeof(DDSURFACEDESC);

        hr = surface->lpVtbl->Lock(surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL);
        if (FAILED(hr))
                return GF_IO_ERR;

        vi->video_buffer = (char*)desc.lpSurface;
        vi->width = desc.dwWidth;
        vi->height = desc.dwHeight;
        vi->pitch_x = 0;
        vi->pitch_y = desc.lPitch;
        vi->is_hardware_memory = dd->systems_memory ? GF_FALSE : GF_TRUE;
        return GF_OK;
}

static GF_Err DD_UnlockSurface(DDContext *dd, LPDDRAWSURFACE surface)
{
        HRESULT hr;
        if (!dd || !dd->ddraw_init) return GF_IO_ERR;
        hr = surface->lpVtbl->Unlock(surface, NULL);
        return FAILED(hr) ? GF_IO_ERR : GF_OK;
}


static GF_Err DD_LockBackBuffer(GF_VideoOutput *dr, GF_VideoSurface *vi, Bool do_lock)
{
        DDCONTEXT;

        if (do_lock) {
                memset(vi, 0, sizeof(GF_VideoSurface));
                vi->pixel_format = dd->pixelFormat;
                return DD_LockSurface(dd, vi, dd->pBack);
        }
        else return DD_UnlockSurface(dd, dd->pBack);
}

static void *LockOSContext(GF_VideoOutput *dr, Bool do_lock)
{
        DDCONTEXT;

        if (!dd->pBack) return NULL;

        if (do_lock) {
                if (!dd->lock_hdc && ! dd->pBack->lpVtbl->IsLost(dd->pBack)) {
                        if (FAILED(dd->pBack->lpVtbl->GetDC(dd->pBack, &dd->lock_hdc)) )
                                dd->lock_hdc = NULL;
                }
        } else if (dd->lock_hdc) {
                dd->pBack->lpVtbl->ReleaseDC(dd->pBack, dd->lock_hdc);
                dd->lock_hdc = NULL;
        }
        return (void *)dd->lock_hdc ;
}


static GF_Err DD_BlitSurface(DDContext *dd, DDSurface *src, GF_Window *src_wnd, GF_Window *dst_wnd, GF_ColorKey *key)
{
        HRESULT hr;
        u32 dst_w, dst_h, src_w, src_h, flags;
        RECT r_dst, r_src;
        u32 left, top;
        src_w = src_wnd ? src_wnd->w : src->width;
        src_h = src_wnd ? src_wnd->h : src->height;
        dst_w = dst_wnd ? dst_wnd->w : dd->width;
        dst_h = dst_wnd ? dst_wnd->h : dd->height;

        if (src_wnd != NULL) MAKERECT(r_src, src_wnd);
        if (dst_wnd != NULL) MAKERECT(r_dst, dst_wnd);

        if (key) {
                u32 col;
                DDCOLORKEY ck;
                col = GF_COL_ARGB(0xFF, key->r, key->g, key->b);
                ck.dwColorSpaceHighValue = ck.dwColorSpaceLowValue = col;
                hr = src->pSurface->lpVtbl->SetColorKey(src->pSurface, DDCKEY_SRCBLT, &ck);
                if (FAILED(hr))
                        return GF_IO_ERR;
        }

        if ((dst_w==src_w) && (dst_h==src_h)) {
                flags = DDBLTFAST_WAIT;
                if (key) flags |= DDBLTFAST_SRCCOLORKEY;
                if (dst_wnd) {
                        left = r_dst.left;
                        top = r_dst.top;
                }
                else left = top = 0;
                hr = dd->pBack->lpVtbl->BltFast(dd->pBack, left, top, src->pSurface, src_wnd ? &r_src : NULL, flags);
        } else {
                flags = DDBLT_WAIT;
                if (key) flags |= DDBLT_KEYSRC;
                hr = dd->pBack->lpVtbl->Blt(dd->pBack, dst_wnd ? &r_dst : NULL, src->pSurface, src_wnd ? &r_src : NULL, flags, NULL);
        }

        if (src->is_yuv && dd->force_video_mem_for_yuv && dd->systems_memory) return GF_IO_ERR;

#ifndef GPAC_DISABLE_LOG
        if (hr) {
                GF_LOG(dd->systems_memory ? GF_LOG_INFO : GF_LOG_ERROR, GF_LOG_MMIO, ("[DX Out] Failed blitting %s %s memory: error %08x\n", src->is_yuv ? "YUV" : "RGB", dd->systems_memory ? "systems" : "hardware", hr));
        } else {
                GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX Out] %s blit %s memory %dx%d -> %dx%d\n", src->is_yuv ? "YUV" : "RGB", dd->systems_memory ? "systems" : "hardware", src_w, src_h, dst_w, dst_h));
        }
#endif
        return FAILED(hr) ? GF_IO_ERR : GF_OK;
}

static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 pixel_format, Bool check_caps)
{
        DDSURFDESC ddsd;
        HRESULT hr;
        DDCONTEXT;

        if (!dd->pDD) return NULL;

        /*yuv format*/
        if (pixelformat_yuv(pixel_format)) {
                /*some drivers give broken result if YUV surface dimensions are not even*/
                while (width%2) width++;
                while (height%2) height++;

                if (dr->yuv_pixel_format) {
                        DDSurface *yuvp = &dd->yuv_pool;

                        /*don't recreate a surface if not needed*/
                        if (yuvp->pSurface && (yuvp->width >= width) && (yuvp->height >= height) ) return yuvp;

                        width = MAX(yuvp->width, width);
                        height = MAX(yuvp->height, height);

                        SAFE_DD_RELEASE(yuvp->pSurface);
                        memset(yuvp, 0, sizeof(DDSurface));
                        yuvp->is_yuv = GF_TRUE;

                        memset (&ddsd, 0, sizeof(ddsd));
                        ddsd.dwSize = sizeof(ddsd);
                        ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
                        ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;

                        ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;

                        if (!dd->offscreen_yuv_to_rgb && (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY))
                                ddsd.ddsCaps.dwCaps |= DDSCAPS_OVERLAY;

                        ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
                        ddsd.dwWidth = width;
                        ddsd.dwHeight = height;
                        ddsd.ddpfPixelFormat.dwFourCC = get_win_4CC(dr->yuv_pixel_format);

                        hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL);
                        if (FAILED(hr) ) {
                                if (!check_caps) return NULL;

                                /*try without offscreen yuv->rgbcap*/
                                if (dd->offscreen_yuv_to_rgb) {
                                        dd->offscreen_yuv_to_rgb = GF_FALSE;
                                        if (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) {
                                                ddsd.ddsCaps.dwCaps |= DDSCAPS_OVERLAY;
                                                hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL);
                                                if( FAILED(hr) ) {
                                                        return NULL;
                                                }
                                        } else {
                                                return NULL;
                                        }
                                }

                                /*try without overlay cap*/
                                if (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) {
                                        dr->hw_caps &= ~GF_VIDEO_HW_HAS_YUV_OVERLAY;
                                        ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;

                                        hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL);
                                        if( FAILED(hr) ) {
                                                return NULL;
                                        }
                                }
                                /*YUV not supported*/
                                else {
                                        return NULL;
                                }

                        }
                        yuvp->format = dr->yuv_pixel_format;
                        yuvp->width = width;
                        yuvp->height = height;


                        return yuvp;
                }
        }

        /*don't recreate a surface if not needed*/
        if ((dd->rgb_pool.width >= width) && (dd->rgb_pool.height >= height) ) return &dd->rgb_pool;
        width = MAX(dd->rgb_pool.width, width);
        height = MAX(dd->rgb_pool.height, height);
        SAFE_DD_RELEASE(dd->rgb_pool.pSurface);
        memset(&dd->rgb_pool, 0, sizeof(DDSurface));

        memset (&ddsd, 0, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        ddsd.dwWidth = width;
        ddsd.dwHeight = height;

        hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL);
        if (FAILED(hr))
                return NULL;
        dd->rgb_pool.width = width;
        dd->rgb_pool.height = height;
        dd->rgb_pool.format = dd->pixelFormat;
        return &dd->rgb_pool;
}

static GF_Err DD_Blit(GF_VideoOutput *dr, GF_VideoSurface *video_src, GF_Window *src_wnd, GF_Window *dst_wnd, u32 overlay_type)
{
        GF_VideoSurface temp_surf;
        GF_Err e;
        GF_Window src_wnd_2;
        u32 w, h;
        DDSurface *pool;
        DDCONTEXT;

        if (!video_src) {
                if (overlay_type && dd->yuv_pool.pSurface)
                        dd->yuv_pool.pSurface->lpVtbl->UpdateOverlay(dd->yuv_pool.pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL);
                return GF_OK;
        }
        if (src_wnd) {
                w = src_wnd->w;
                h = src_wnd->h;
        } else {
                w = video_src->width;
                h = video_src->height;
        }
        /*get RGB or YUV pool surface*/
        //if (video_src->pixel_format==GF_PIXEL_YUVD) return GF_NOT_SUPPORTED;
        if (video_src->pixel_format==GF_PIXEL_YUVD) video_src->pixel_format=GF_PIXEL_YV12;
        pool = DD_GetSurface(dr, w, h, video_src->pixel_format, GF_FALSE);
        if (!pool)
                return GF_IO_ERR;

        memset(&temp_surf, 0, sizeof(GF_VideoSurface));
        temp_surf.pixel_format = pool->format;
        e = DD_LockSurface(dd, &temp_surf, pool->pSurface);
        if (e) return e;

        /*copy pixels to pool*/
        dx_copy_pixels(&temp_surf, video_src, src_wnd);

        e = DD_UnlockSurface(dd, pool->pSurface);
        if (e) return e;

        if (overlay_type) {
                HRESULT hr;
                RECT dst_rc, src_rc;
                POINT pt;
                GF_Window dst = *dst_wnd;
                GF_Window src = *src_wnd;

                src.x = src.y = 0;
                MAKERECT(src_rc, (&src));
                pt.x = dst.x;
                pt.y = dst.y;
                ClientToScreen(dd->cur_hwnd, &pt);
                dst.x = pt.x;
                dst.y = pt.y;
                MAKERECT(dst_rc, (&dst));

                GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX] Blit surface to dest %d x %d - overlay type %s\n", dst.w, dst.h,
                                                   (overlay_type==0)? "none" : ((overlay_type==1) ? "Top-Level" : "ColorKey")
                                                  ));

#if 1
                if (overlay_type==1) {
                        hr = pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_AUTOFLIP, NULL);
                } else {
                        DDOVERLAYFX ddofx;
                        memset(&ddofx, 0, sizeof(DDOVERLAYFX));
                        ddofx.dwSize = sizeof(DDOVERLAYFX);
                        ddofx.dckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key;
                        ddofx.dckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key;
                        hr = pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE | DDOVER_AUTOFLIP, &ddofx);
                }
                if (FAILED(hr)) {
                        pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL);
                }
#else
                if (overlay_type==1) {
                        hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, 0, NULL);
                } else {
                        DDBLTFX ddfx;
                        memset(&ddfx, 0, sizeof(DDBLTFX));
                        ddfx.dwSize = sizeof(DDBLTFX);
                        ddfx.ddckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key;
                        ddfx.ddckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key;

                        hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, DDBLT_WAIT | DDBLT_KEYDESTOVERRIDE, &ddfx);
                }
#endif
                return FAILED(hr) ? GF_IO_ERR : GF_OK;
        }

        src_wnd_2.x = src_wnd_2.y = 0;
        if (src_wnd) {
                src_wnd_2.w = src_wnd->w;
                src_wnd_2.h = src_wnd->h;
        } else {
                src_wnd_2.w = video_src->width;
                src_wnd_2.h = video_src->height;
        }
        /*and blit surface*/
        return DD_BlitSurface(dd, pool, &src_wnd_2, dst_wnd, NULL);
}



static GFINLINE u32 is_yuv_supported(u32 win_4cc)
{
        if (win_4cc==get_win_4CC(GF_PIXEL_YV12)) return GF_PIXEL_YV12;
        else if (win_4cc==get_win_4CC(GF_PIXEL_I420)) return GF_PIXEL_I420;
        else if (win_4cc==get_win_4CC(GF_PIXEL_IYUV)) return GF_PIXEL_IYUV;
        else if (win_4cc==get_win_4CC(GF_PIXEL_UYVY)) return GF_PIXEL_UYVY;
        else if (win_4cc==get_win_4CC(GF_PIXEL_Y422)) return GF_PIXEL_Y422;
        else if (win_4cc==get_win_4CC(GF_PIXEL_UYNV)) return GF_PIXEL_UYNV;
        else if (win_4cc==get_win_4CC(GF_PIXEL_YUY2)) return GF_PIXEL_YUY2;
        else if (win_4cc==get_win_4CC(GF_PIXEL_YUNV)) return GF_PIXEL_YUNV;
        else if (win_4cc==get_win_4CC(GF_PIXEL_V422)) return GF_PIXEL_V422;
        else if (win_4cc==get_win_4CC(GF_PIXEL_YVYU)) return GF_PIXEL_YVYU;
        else return 0;
}

static GFINLINE Bool is_yuv_planar(u32 format)
{
        switch  (format) {
        case GF_PIXEL_YV12:
        case GF_PIXEL_I420:
        case GF_PIXEL_IYUV:
                return GF_TRUE;
        default:
                return GF_FALSE;
        }
}

#define YUV_NUM_TEST    20

/*gets fastest YUV format for YUV to RGB blit from YUV overlay (support is quite random on most cards)*/
void DD_InitYUV(GF_VideoOutput *dr)
{
        u32 w, h, j, i, num_yuv;
        Bool force_yv12 = GF_FALSE;
        DWORD numCodes;
        DWORD formats[30];
        DWORD *codes;
        u32 now, min_packed = 0xFFFFFFFF, min_planar = 0xFFFFFFFF;
        u32 best_packed = 0, best_planar = 0;
        Bool checkPacked = TRUE;
        const char *opt;

        DDCONTEXT;

        w = 720;
        h = 576;


        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "PreferedYUV4CC");
        if (opt) {
                char a, b, c, d;
                if (sscanf(opt, "%c%c%c%c", &a, &b, &c, &d)==4) {
                        dr->yuv_pixel_format = GF_4CC(a, b, c, d);
                        dr->hw_caps |= GF_VIDEO_HW_HAS_YUV;
                        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "HasOverlay");
                        if (opt && !strcmp(opt, "yes"))
                                dr->hw_caps |= GF_VIDEO_HW_HAS_YUV_OVERLAY;
                        dd->yuv_init = GF_TRUE;
                        num_yuv = 1;
                }
        }

        /*first run on this machine, to some benchmark*/
        if (! dd->yuv_init) {
                char szOpt[100];
                dd->yuv_init = GF_TRUE;

                dr->hw_caps |= GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY;

                dd->pDD->lpVtbl->GetFourCCCodes(dd->pDD, &numCodes, NULL);
                if (!numCodes) return;
                codes = (DWORD *)gf_malloc(numCodes*sizeof(DWORD));
                dd->pDD->lpVtbl->GetFourCCCodes(dd->pDD, &numCodes, codes);

                num_yuv = 0;
                for (i=0; i<numCodes; i++) {
                        formats[num_yuv] = is_yuv_supported(codes[i]);
                        if (formats[num_yuv]) num_yuv++;
                }
                gf_free(codes);

                /*too bad*/
                if (!num_yuv) {
                        dr->hw_caps &= ~(GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY);
                        dr->yuv_pixel_format = 0;
                        return;
                }

                for (i=0; i<num_yuv; i++) {
                        /*check planar first*/
                        if (!checkPacked && !is_yuv_planar(formats[i])) goto go_on;
                        /*then check packed */
                        if (checkPacked && is_yuv_planar(formats[i])) goto go_on;

                        if (dd->yuv_pool.pSurface) {
                                SAFE_DD_RELEASE(dd->yuv_pool.pSurface);
                                memset(&dd->yuv_pool, 0, sizeof(DDSurface));
                                dd->yuv_pool.is_yuv = GF_TRUE;
                        }

                        dr->yuv_pixel_format = formats[i];
                        if (DD_GetSurface(dr, w, h, dr->yuv_pixel_format, GF_TRUE) == NULL)
                                goto rem_fmt;

                        now = gf_sys_clock();
                        /*perform blank blit*/
                        for (j=0; j<YUV_NUM_TEST; j++) {
                                if (DD_BlitSurface(dd, &dd->yuv_pool, NULL, NULL, NULL) != GF_OK)
                                        goto rem_fmt;
                        }
                        now = gf_sys_clock() - now;
                        if (formats[i]== GF_PIXEL_YV12)
                                force_yv12 = GF_TRUE;

                        if (!checkPacked) {
                                if (now<min_planar) {
                                        min_planar = now;
                                        best_planar = dr->yuv_pixel_format;
                                }
                        } else {
                                if (now<min_packed) {
                                        min_packed = now;
                                        best_packed = dr->yuv_pixel_format;
                                }
                        }

go_on:
                        if (checkPacked && (i+1==num_yuv)) {
                                i = -1;
                                checkPacked = FALSE;
                        }
                        continue;

rem_fmt:
                        for (j=i; j<num_yuv-1; j++) {
                                formats[j] = formats[j+1];
                        }
                        i--;
                        num_yuv--;
                }

                if (dd->yuv_pool.pSurface) {
                        SAFE_DD_RELEASE(dd->yuv_pool.pSurface);
                        memset(&dd->yuv_pool, 0, sizeof(DDSurface));
                        dd->yuv_pool.is_yuv = GF_TRUE;
                }

                if (best_planar && (min_planar <= min_packed )) {
                        dr->yuv_pixel_format = best_planar;
                } else {
                        min_planar = min_packed;
                        dr->yuv_pixel_format = best_packed;
                }
                if (force_yv12)
                        dr->yuv_pixel_format = GF_PIXEL_YV12;

                /*store our options*/
                sprintf(szOpt, "%c%c%c%c", (dr->yuv_pixel_format>>24) & 0xFF, (dr->yuv_pixel_format>>16) & 0xFF, (dr->yuv_pixel_format>>8) & 0xFF, (dr->yuv_pixel_format) & 0xFF);
                gf_modules_set_option((GF_BaseInterface *)dr, "Video", "PreferedYUV4CC", szOpt);
                gf_modules_set_option((GF_BaseInterface *)dr, "Video", "HasOverlay", (dr->hw_caps & GF_VIDEO_HW_HAS_YUV_OVERLAY) ? "yes" : "no");
        }

        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "HardwareMemory");
        if (opt && !strcmp(opt, "Never")) num_yuv = 0;

        /*too bad*/
        if (!num_yuv) {
                dr->hw_caps &= ~(GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY);
                dr->yuv_pixel_format = 0;
                GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] YUV hardware not available\n"));
                return;
        }

        GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Picked YUV format %s - drawn in %d ms\n", gf_4cc_to_str(dr->yuv_pixel_format), min_planar));

        /*enable YUV->RGB on the backbuffer ?*/
        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "EnableOffscreenYUV");
        /*no set is the default*/
        if (!opt) {
                opt = "yes";
                gf_modules_set_option((GF_BaseInterface *)dr, "Video", "EnableOffscreenYUV", "yes");
        }
        if (opt && strcmp(opt, "yes")) dr->hw_caps &= ~GF_VIDEO_HW_HAS_YUV;
        else dd->offscreen_yuv_to_rgb = GF_TRUE;

        /*get YUV overlay key*/
        opt = gf_modules_get_option((GF_BaseInterface *)dr, "Video", "OverlayColorKey");
        /*no set is the default*/
        if (!opt) {
                opt = "0101FE";
                gf_modules_set_option((GF_BaseInterface *)dr, "Video", "OverlayColorKey", "0101FE");
        }
        sscanf(opt, "%06x", &dr->overlay_color_key);
        if (dr->overlay_color_key) dr->overlay_color_key |= 0xFF000000;
        GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] YUV->RGB enabled: %s - ColorKey enabled: %s (key %x)\n",
                                          (dr->hw_caps & GF_VIDEO_HW_HAS_YUV) ? "Yes" : "No",
                                          dr->overlay_color_key ? "Yes" : "No", dr->overlay_color_key
                                         ));
}

GF_Err DD_SetBackBufferSize(GF_VideoOutput *dr, u32 width, u32 height, Bool use_system_memory)
{
        GF_Err e;
        DDCONTEXT;
#ifndef GPAC_DISABLE_3D
        if (dd->output_3d_type) return GF_BAD_PARAM;
#endif
        if (!dd->ddraw_init) {
                e = InitDirectDraw(dr, width, height);
                if (e) return e;
        }
        return CreateBackBuffer(dr, width, height, use_system_memory);
}


void DD_SetupDDraw(GF_VideoOutput *driv)
{
        driv->hw_caps |= GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_STRETCH;
        driv->Blit = DD_Blit;
        driv->LockBackBuffer = DD_LockBackBuffer;
        driv->LockOSContext = LockOSContext;
}


/* [<][>][^][v][top][bottom][index][help] */