mpv/libvo/vo_kva.c

1092 lines
29 KiB
C

/*
* OS/2 video output driver
*
* Copyright (c) 2007-2009 by KO Myung-Hun (komh@chollian.net)
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define INCL_WIN
#define INCL_GPI
#define INCL_DOS
#include <os2.h>
#include <mmioos2.h>
#include <fourcc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <kva.h>
#include "config.h"
#include "mp_msg.h"
#include "video_out.h"
#include "video_out_internal.h"
#include "aspect.h"
#include "fastmemcpy.h"
#include "mp_fifo.h"
#include "osdep/keycodes.h"
#include "input/input.h"
#include "input/mouse.h"
#include "subopt-helper.h"
#include "sub.h"
#include "cpudetect.h"
#include "libswscale/swscale.h"
#include "libmpcodecs/vf_scale.h"
static const vo_info_t info = {
"SNAP/WarpOverlay!/DIVE video output",
"kva",
"KO Myung-Hun <komh@chollian.net>",
""
};
const LIBVO_EXTERN(kva)
#define WC_MPLAYER "WC_MPLAYER"
#define SRC_WIDTH m_int.kvas.szlSrcSize.cx
#define SRC_HEIGHT m_int.kvas.szlSrcSize.cy
#define HWNDFROMWINID(wid) ((wid) + 0x80000000UL)
static const struct mp_keymap m_vk_map[] = {
{VK_NEWLINE, KEY_ENTER}, {VK_TAB, KEY_TAB}, {VK_SPACE, ' '},
// control keys
{VK_CTRL, KEY_CTRL}, {VK_BACKSPACE, KEY_BS},
{VK_DELETE, KEY_DELETE}, {VK_INSERT, KEY_INSERT},
{VK_HOME, KEY_HOME}, {VK_END, KEY_END},
{VK_PAGEUP, KEY_PAGE_UP}, {VK_PAGEDOWN, KEY_PAGE_DOWN},
{VK_ESC, KEY_ESC},
// cursor keys
{VK_RIGHT, KEY_RIGHT}, {VK_LEFT, KEY_LEFT},
{VK_DOWN, KEY_DOWN}, {VK_UP, KEY_UP},
// function keys
{VK_F1, KEY_F+1}, {VK_F2, KEY_F+2}, {VK_F3, KEY_F+3}, {VK_F4, KEY_F+4},
{VK_F5, KEY_F+5}, {VK_F6, KEY_F+6}, {VK_F7, KEY_F+7}, {VK_F8, KEY_F+8},
{VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F12, KEY_F+12},
{0, 0}
};
static const struct mp_keymap m_keypad_map[] = {
// keypad keys
{0x52, KEY_KP0}, {0x4F, KEY_KP1}, {0x50, KEY_KP2}, {0x51, KEY_KP3},
{0x4B, KEY_KP4}, {0x4C, KEY_KP5}, {0x4D, KEY_KP6}, {0x47, KEY_KP7},
{0x48, KEY_KP8}, {0x49, KEY_KP9}, {0x53, KEY_KPDEC}, {0x5A, KEY_KPENTER},
{0, 0}
};
static const struct mp_keymap m_mouse_map[] = {
{WM_BUTTON1DOWN, MOUSE_BTN0},
{WM_BUTTON3DOWN, MOUSE_BTN1},
{WM_BUTTON2DOWN, MOUSE_BTN2},
{WM_BUTTON1DBLCLK, MOUSE_BTN0_DBL},
{WM_BUTTON3DBLCLK, MOUSE_BTN1_DBL},
{WM_BUTTON2DBLCLK, MOUSE_BTN2_DBL},
{0, 0}
};
struct {
HAB hab;
HMQ hmq;
HWND hwndFrame;
HWND hwndClient;
HWND hwndSysMenu;
HWND hwndTitleBar;
HWND hwndMinMax;
FOURCC fcc;
int iImageFormat;
int nChromaShift;
KVASETUP kvas;
KVACAPS kvac;
RECTL rclDst;
int bpp;
LONG lStride;
PBYTE pbImage;
BOOL fFixT23;
PFNWP pfnwpOldFrame;
uint8_t *planes[MP_MAX_PLANES]; // y = 0, u = 1, v = 2
int stride[MP_MAX_PLANES];
BOOL fHWAccel;
RECTL rclParent;
struct SwsContext *sws;
} m_int;
static inline void setAspectRatio(ULONG ulRatio)
{
ULONG ulValue;
int i;
m_int.kvas.ulRatio = ulRatio;
kvaSetup(&m_int.kvas);
// Setup initializes all attributes, so need to restore them.
for (i = 0; i < KVAA_LAST; i++) {
kvaQueryAttr(i, &ulValue);
kvaSetAttr(i, &ulValue);
}
}
static int query_format_info(int format, PBOOL pfHWAccel, PFOURCC pfcc,
int *pbpp, int *pnChromaShift)
{
BOOL fHWAccel;
FOURCC fcc;
INT bpp;
INT nChromaShift;
switch (format) {
case IMGFMT_YV12:
fHWAccel = m_int.kvac.ulInputFormatFlags & KVAF_YV12;
fcc = FOURCC_YV12;
bpp = 1;
nChromaShift = 1;
break;
case IMGFMT_YUY2:
fHWAccel = m_int.kvac.ulInputFormatFlags & KVAF_YUY2;
fcc = FOURCC_Y422;
bpp = 2;
nChromaShift = 0;
break;
case IMGFMT_YVU9:
fHWAccel = m_int.kvac.ulInputFormatFlags & KVAF_YVU9;
fcc = FOURCC_YVU9;
bpp = 1;
nChromaShift = 2;
break;
case IMGFMT_BGR24:
fHWAccel = m_int.kvac.ulInputFormatFlags & KVAF_BGR24;
fcc = FOURCC_BGR3;
bpp = 3;
nChromaShift = 0;
break;
case IMGFMT_BGR16:
fHWAccel = m_int.kvac.ulInputFormatFlags & KVAF_BGR16;
fcc = FOURCC_R565;
bpp = 2;
nChromaShift = 0;
break;
case IMGFMT_BGR15:
fHWAccel = m_int.kvac.ulInputFormatFlags & KVAF_BGR15;
fcc = FOURCC_R555;
bpp = 2;
nChromaShift = 0;
break;
default:
return 1;
}
if (pfHWAccel)
*pfHWAccel = fHWAccel;
if (pfcc)
*pfcc = fcc;
if (pbpp)
*pbpp = bpp;
if (pnChromaShift)
*pnChromaShift = nChromaShift;
return 0;
}
static void imgCreate(void)
{
int size = SRC_HEIGHT * m_int.lStride;;
switch (m_int.iImageFormat) {
case IMGFMT_YV12:
size += size / 2;
break;
case IMGFMT_YVU9:
size += size / 8;
break;
}
m_int.pbImage = malloc(size);
memset(m_int.planes, 0, sizeof(m_int.planes));
memset(m_int.stride, 0, sizeof(m_int.stride));
m_int.planes[0] = m_int.pbImage;
m_int.stride[0] = m_int.lStride;
// YV12 or YVU9 ?
if (m_int.nChromaShift) {
m_int.planes[1] = m_int.planes[0] + SRC_HEIGHT * m_int.stride[0];
m_int.stride[1] = m_int.stride[0] >> m_int.nChromaShift;
m_int.planes[2] = m_int.planes[1] +
(SRC_HEIGHT >> m_int.nChromaShift) * m_int.stride[1];
m_int.stride[2] = m_int.stride[1];
}
}
static void imgFree(void)
{
free(m_int.pbImage);
m_int.pbImage = NULL;
}
static void imgDisplay(void)
{
PVOID pBuffer;
ULONG ulBPL;
if (!kvaLockBuffer(&pBuffer, &ulBPL)) {
uint8_t *dst[MP_MAX_PLANES] = {NULL};
int dstStride[MP_MAX_PLANES] = {0};
// Get packed or Y
dst[0] = pBuffer;
dstStride[0] = ulBPL;
// YV12 or YVU9 ?
if (m_int.nChromaShift) {
// Get V
dst[2] = dst[0] + SRC_HEIGHT * dstStride[0];
dstStride[2] = dstStride[0] >> m_int.nChromaShift;
// Get U
dst[1] = dst[2] +
(SRC_HEIGHT >> m_int.nChromaShift ) * dstStride[2];
dstStride[1] = dstStride[2];
}
if (m_int.fHWAccel) {
int w, h;
w = m_int.stride[0];
h = SRC_HEIGHT;
// Copy packed or Y
mem2agpcpy_pic(dst[0], m_int.planes[0], w, h,
dstStride[0], m_int.stride[0]);
// YV12 or YVU9 ?
if (m_int.nChromaShift) {
w >>= m_int.nChromaShift; h >>= m_int.nChromaShift;
// Copy U
mem2agpcpy_pic(dst[1], m_int.planes[1], w, h,
dstStride[1], m_int.stride[1]);
// Copy V
mem2agpcpy_pic(dst[2], m_int.planes[2], w, h,
dstStride[2], m_int.stride[2]);
}
} else {
sws_scale(m_int.sws, m_int.planes, m_int.stride, 0, SRC_HEIGHT,
dst, dstStride);
}
kvaUnlockBuffer();
}
}
// Frame window procedure to work around T23 laptop with S3 video card,
// which supports upscaling only.
static MRESULT EXPENTRY NewFrameWndProc(HWND hwnd, ULONG msg, MPARAM mp1,
MPARAM mp2)
{
switch (msg) {
case WM_QUERYTRACKINFO:
{
PTRACKINFO pti = (PTRACKINFO)mp2;
RECTL rcl;
if (vo_fs)
break;
m_int.pfnwpOldFrame(hwnd, msg, mp1, mp2);
rcl.xLeft = 0;
rcl.yBottom = 0;
rcl.xRight = SRC_WIDTH + 1;
rcl.yTop = SRC_HEIGHT + 1;
WinCalcFrameRect(hwnd, &rcl, FALSE);
pti->ptlMinTrackSize.x = rcl.xRight - rcl.xLeft;
pti->ptlMinTrackSize.y = rcl.yTop - rcl.yBottom;
pti->ptlMaxTrackSize.x = vo_screenwidth;
pti->ptlMaxTrackSize.y = vo_screenheight;
return (MRESULT)TRUE;
}
case WM_ADJUSTWINDOWPOS:
{
PSWP pswp = (PSWP)mp1;
RECTL rcl;
if (vo_fs)
break;
if (pswp->fl & SWP_SIZE) {
rcl.xLeft = pswp->x;
rcl.yBottom = pswp->y;
rcl.xRight = rcl.xLeft + pswp->cx;
rcl.yTop = rcl.yBottom + pswp->cy;
WinCalcFrameRect(hwnd, &rcl, TRUE);
if (rcl.xRight - rcl.xLeft <= SRC_WIDTH)
rcl.xRight = rcl.xLeft + (SRC_WIDTH + 1);
if (rcl.yTop - rcl.yBottom <= SRC_HEIGHT)
rcl.yTop = rcl.yBottom + (SRC_HEIGHT + 1);
WinCalcFrameRect(hwnd, &rcl, FALSE);
if (rcl.xRight - rcl.xLeft > vo_screenwidth) {
rcl.xLeft = 0;
rcl.xRight = vo_screenwidth;
}
if (rcl.yTop - rcl.yBottom > vo_screenheight) {
rcl.yBottom = 0;
rcl.yTop = vo_screenheight;
}
pswp->fl |= SWP_MOVE;
pswp->x = rcl.xLeft;
pswp->y = rcl.yBottom;
pswp->cx = rcl.xRight - rcl.xLeft;
pswp->cy = rcl.yTop - rcl.yBottom;
}
break;
}
}
return m_int.pfnwpOldFrame(hwnd, msg, mp1, mp2);
}
static MRESULT EXPENTRY WndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
// if slave mode, ignore mouse events and deliver them to a parent window
if (WinID != -1 &&
((msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
(msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST))) {
WinPostMsg(HWNDFROMWINID(WinID), msg, mp1, mp2);
return (MRESULT)TRUE;
}
switch (msg) {
case WM_CLOSE:
mplayer_put_key(KEY_CLOSE_WIN);
return 0;
case WM_CHAR:
{
USHORT fsFlags = SHORT1FROMMP(mp1);
UCHAR uchScan = CHAR4FROMMP(mp1);
USHORT usCh = SHORT1FROMMP(mp2);
USHORT usVk = SHORT2FROMMP(mp2);
int mpkey;
if (fsFlags & KC_KEYUP)
break;
if (fsFlags & KC_SCANCODE) {
mpkey = lookup_keymap_table(m_keypad_map, uchScan);
if (mpkey) {
// distinguish KEY_KP0 and KEY_KPINS
if (mpkey == KEY_KP0 && usCh != '0')
mpkey = KEY_KPINS;
// distinguish KEY_KPDEC and KEY_KPDEL
if (mpkey == KEY_KPDEC && usCh != '.')
mpkey = KEY_KPDEL;
mplayer_put_key(mpkey);
return (MRESULT)TRUE;
}
}
if (fsFlags & KC_VIRTUALKEY) {
mpkey = lookup_keymap_table(m_vk_map, usVk);
if (mpkey) {
mplayer_put_key(mpkey);
return (MRESULT)TRUE;
}
}
if ((fsFlags & KC_CHAR) && !HIBYTE(usCh))
mplayer_put_key(usCh);
return (MRESULT)TRUE;
}
case WM_BUTTON1DOWN:
case WM_BUTTON3DOWN:
case WM_BUTTON2DOWN:
case WM_BUTTON1DBLCLK:
case WM_BUTTON3DBLCLK:
case WM_BUTTON2DBLCLK:
if (WinQueryFocus(HWND_DESKTOP) != hwnd)
WinSetFocus(HWND_DESKTOP, hwnd);
else if (!vo_nomouse_input)
mplayer_put_key(lookup_keymap_table(m_mouse_map, msg));
return (MRESULT)TRUE;
case WM_PAINT:
{
HPS hps;
RECTL rcl, rclDst;
PRECTL prcl = NULL;
HRGN hrgn, hrgnDst;
RGNRECT rgnCtl;
// get a current movie area
kvaAdjustDstRect(&m_int.kvas.rclSrcRect, &rclDst);
// get a current invalidated area
hps = WinBeginPaint(hwnd, NULLHANDLE, &rcl);
// create a region for an invalidated area
hrgn = GpiCreateRegion(hps, 1, &rcl);
// create a region for a movie area
hrgnDst = GpiCreateRegion(hps, 1, &rclDst);
// exclude a movie area from an invalidated area
GpiCombineRegion(hps, hrgn, hrgn, hrgnDst, CRGN_DIFF);
// get rectangles from the region
rgnCtl.ircStart = 1;
rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, NULL);
if (rgnCtl.crcReturned > 0) {
rgnCtl.crc = rgnCtl.crcReturned;
prcl = malloc(sizeof(RECTL) * rgnCtl.crcReturned);
}
// draw black bar if needed
if (prcl && GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, prcl)) {
int i;
for (i = 0; i < rgnCtl.crcReturned; i++)
WinFillRect(hps, &prcl[i], CLR_BLACK);
}
free(prcl);
GpiDestroyRegion(hps, hrgnDst);
GpiDestroyRegion(hps, hrgn);
WinEndPaint(hps);
return 0;
}
}
return WinDefWindowProc(hwnd, msg, mp1, mp2);
}
// Change process type from VIO to PM to use PM APIs.
static void morphToPM(void)
{
PPIB pib;
DosGetInfoBlocks(NULL, &pib);
// Change flag from VIO to PM:
if (pib->pib_ultype == 2)
pib->pib_ultype = 3;
}
static int preinit(const char *arg)
{
HWND hwndParent;
ULONG flFrameFlags;
ULONG kvaMode = 0;
int fUseSnap = 0;
int fUseWO = 0;
int fUseDive = 0;
int fFixT23 = 0;
const opt_t subopts[] = {
{"snap", OPT_ARG_BOOL, &fUseSnap, NULL},
{"wo", OPT_ARG_BOOL, &fUseWO, NULL},
{"dive", OPT_ARG_BOOL, &fUseDive, NULL},
{"t23", OPT_ARG_BOOL, &fFixT23, NULL},
{NULL, 0, NULL, NULL}
};
PCSZ pcszVideoModeStr[3] = {"DIVE", "WarpOverlay!", "SNAP"};
if (subopt_parse(arg, subopts) != 0)
return -1;
morphToPM();
memset(&m_int, 0, sizeof(m_int));
m_int.hab = WinInitialize(0);
m_int.hmq = WinCreateMsgQueue(m_int.hab, 0);
WinRegisterClass(m_int.hab,
WC_MPLAYER,
WndProc,
CS_SIZEREDRAW | CS_MOVENOTIFY,
sizeof(PVOID));
if (WinID == -1) {
hwndParent = HWND_DESKTOP;
flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX |
FCF_SIZEBORDER | FCF_TASKLIST;
} else {
ULONG ulStyle;
hwndParent = HWNDFROMWINID(WinID);
flFrameFlags = 0;
// Prevent a parent window from painting over our window
ulStyle = WinQueryWindowULong(hwndParent, QWL_STYLE);
WinSetWindowULong(hwndParent, QWL_STYLE, ulStyle | WS_CLIPCHILDREN);
}
m_int.hwndFrame =
WinCreateStdWindow(hwndParent, // parent window handle
WS_VISIBLE, // frame window style
&flFrameFlags, // window style
WC_MPLAYER, // class name
"", // window title
0L, // default client style
NULLHANDLE, // resource in exe file
1, // frame window id
&m_int.hwndClient); // client window handle
if (m_int.hwndFrame == NULLHANDLE)
return -1;
m_int.hwndSysMenu = WinWindowFromID(m_int.hwndFrame, FID_SYSMENU);
m_int.hwndTitleBar = WinWindowFromID(m_int.hwndFrame, FID_TITLEBAR);
m_int.hwndMinMax = WinWindowFromID(m_int.hwndFrame, FID_MINMAX);
m_int.fFixT23 = fFixT23;
if (m_int.fFixT23)
m_int.pfnwpOldFrame = WinSubclassWindow(m_int.hwndFrame,
NewFrameWndProc);
if (!!fUseSnap + !!fUseWO + !!fUseDive > 1)
mp_msg(MSGT_VO, MSGL_WARN,"KVA: Multiple mode specified!!!\n");
if (fUseSnap)
kvaMode = KVAM_SNAP;
else if (fUseWO)
kvaMode = KVAM_WO;
else if (fUseDive)
kvaMode = KVAM_DIVE;
else
kvaMode = KVAM_AUTO;
if (kvaInit(kvaMode, m_int.hwndClient, vo_colorkey)) {
mp_msg(MSGT_VO, MSGL_ERR, "KVA: Init failed!!!\n");
return -1;
}
kvaCaps(&m_int.kvac);
mp_msg(MSGT_VO, MSGL_V, "KVA: Selected video mode = %s\n",
pcszVideoModeStr[m_int.kvac.ulMode - 1]);
kvaDisableScreenSaver();
// Might cause PM DLLs to be loaded which incorrectly enable SIG_FPE,
// so mask off all floating-point exceptions.
_control87(MCW_EM, MCW_EM);
return 0;
}
static void uninit(void)
{
kvaEnableScreenSaver();
imgFree();
sws_freeContext(m_int.sws);
if (m_int.hwndFrame != NULLHANDLE) {
kvaResetAttr();
kvaDone();
if (m_int.fFixT23)
WinSubclassWindow(m_int.hwndFrame, m_int.pfnwpOldFrame);
WinDestroyWindow(m_int.hwndFrame);
}
WinDestroyMsgQueue(m_int.hmq);
WinTerminate(m_int.hab);
}
static int config(uint32_t width, uint32_t height,
uint32_t d_width, uint32_t d_height,
uint32_t flags, char *title, uint32_t format)
{
RECTL rcl;
mp_msg(MSGT_VO, MSGL_V,
"KVA: Using 0x%X (%s) image format, vo_config_count = %d\n",
format, vo_format_name(format), vo_config_count);
imgFree();
if (query_format_info(format, &m_int.fHWAccel, &m_int.fcc, &m_int.bpp,
&m_int.nChromaShift))
return 1;
m_int.iImageFormat = format;
// if there is no hw accel for given format,
// try any format supported by hw accel
if (!m_int.fHWAccel) {
int dstFormat = 0;
sws_freeContext(m_int.sws);
if (m_int.kvac.ulInputFormatFlags & KVAF_YV12)
dstFormat = IMGFMT_YV12;
else if (m_int.kvac.ulInputFormatFlags & KVAF_YUY2)
dstFormat = IMGFMT_YUY2;
else if (m_int.kvac.ulInputFormatFlags & KVAF_YVU9)
dstFormat = IMGFMT_YVU9;
else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR24)
dstFormat = IMGFMT_BGR24;
else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR16)
dstFormat = IMGFMT_BGR16;
else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR15)
dstFormat = IMGFMT_BGR15;
if (query_format_info(dstFormat, NULL, &m_int.fcc, NULL, NULL))
return 1;
m_int.sws = sws_getContextFromCmdLine(width, height, format,
width, height, dstFormat);
}
mp_msg(MSGT_VO, MSGL_V, "KVA: Selected FOURCC = %.4s\n", (char *)&m_int.fcc);
m_int.kvas.ulLength = sizeof(KVASETUP);
m_int.kvas.szlSrcSize.cx = width;
m_int.kvas.szlSrcSize.cy = height;
m_int.kvas.rclSrcRect.xLeft = 0;
m_int.kvas.rclSrcRect.yTop = 0;
m_int.kvas.rclSrcRect.xRight = width;
m_int.kvas.rclSrcRect.yBottom = height;
m_int.kvas.ulRatio = vo_keepaspect ? KVAR_FORCEANY : KVAR_NONE;
m_int.kvas.ulAspectWidth = d_width;
m_int.kvas.ulAspectHeight = d_height;
m_int.kvas.fccSrcColor = m_int.fcc;
m_int.kvas.fDither = TRUE;
if (kvaSetup(&m_int.kvas)) {
mp_msg(MSGT_VO, MSGL_ERR, "KVA: Setup failed!!!\n");
return 1;
}
m_int.lStride = width * m_int.bpp;
imgCreate();
if (WinID == -1) {
WinSetWindowText(m_int.hwndFrame, title);
// initialize 'vo_fs' only once at first config() call
if (vo_config_count == 0)
vo_fs = flags & VOFLAG_FULLSCREEN;
// workaround for T23 laptop with S3 Video by Franz Bakan
if (!vo_fs && m_int.fFixT23) {
d_width++;
d_height++;
}
m_int.rclDst.xLeft = ((LONG)vo_screenwidth - (LONG)d_width) / 2;
m_int.rclDst.yBottom = ((LONG)vo_screenheight - (LONG)d_height) / 2;
m_int.rclDst.xRight = m_int.rclDst.xLeft + d_width;
m_int.rclDst.yTop = m_int.rclDst.yBottom + d_height;
if (vo_fs) {
d_width = vo_screenwidth;
d_height = vo_screenheight;
// when -fs option is used without this, title bar is not highlighted
WinSetActiveWindow(HWND_DESKTOP, m_int.hwndFrame);
WinSetParent(m_int.hwndSysMenu, HWND_OBJECT, FALSE);
WinSetParent(m_int.hwndTitleBar, HWND_OBJECT, FALSE);
WinSetParent(m_int.hwndMinMax, HWND_OBJECT, FALSE);
setAspectRatio(KVAR_FORCEANY);
}
rcl.xLeft = ((LONG)vo_screenwidth - (LONG)d_width) / 2;
rcl.yBottom = ((LONG)vo_screenheight - (LONG)d_height) /2 ;
rcl.xRight = rcl.xLeft + d_width;
rcl.yTop = rcl.yBottom + d_height;
} else {
vo_fs = 0;
WinQueryWindowRect(HWNDFROMWINID(WinID), &m_int.rclDst);
rcl = m_int.rclDst;
}
WinCalcFrameRect(m_int.hwndFrame, &rcl, FALSE);
WinSetWindowPos(m_int.hwndFrame, HWND_TOP,
rcl.xLeft, rcl.yBottom,
rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW |
(WinID == -1 ? SWP_ACTIVATE : 0));
WinInvalidateRect(m_int.hwndFrame, NULL, TRUE);
return 0;
}
static uint32_t get_image(mp_image_t *mpi)
{
if (m_int.iImageFormat != mpi->imgfmt)
return VO_FALSE;
if (mpi->type == MP_IMGTYPE_STATIC || mpi->type == MP_IMGTYPE_TEMP) {
if (mpi->flags & MP_IMGFLAG_PLANAR) {
mpi->planes[1] = m_int.planes[1];
mpi->planes[2] = m_int.planes[2];
mpi->stride[1] = m_int.stride[1];
mpi->stride[2] = m_int.stride[2];
}
mpi->planes[0] = m_int.planes[0];
mpi->stride[0] = m_int.stride[0];
mpi->flags |= MP_IMGFLAG_DIRECT;
return VO_TRUE;
}
return VO_FALSE;
}
static uint32_t draw_image(mp_image_t *mpi)
{
// if -dr or -slices then do nothing:
if (mpi->flags & (MP_IMGFLAG_DIRECT | MP_IMGFLAG_DRAW_CALLBACK))
return VO_TRUE;
draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, mpi->x, mpi->y);
return VO_TRUE;
}
static int query_format(uint32_t format)
{
BOOL fHWAccel;
int res;
if (query_format_info(format, &fHWAccel, NULL, NULL, NULL))
return 0;
res = VFCAP_CSP_SUPPORTED | VFCAP_OSD;
if (fHWAccel) {
res |= VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP;
if (!m_int.fFixT23)
res |= VFCAP_HWSCALE_DOWN;
}
return res;
}
static int fs_toggle(void)
{
RECTL rcl;
vo_fs = !vo_fs;
if (vo_fs) {
SWP swp;
WinQueryWindowPos(m_int.hwndFrame, &swp);
m_int.rclDst.xLeft = swp.x;
m_int.rclDst.yBottom = swp.y;
m_int.rclDst.xRight = m_int.rclDst.xLeft + swp.cx;
m_int.rclDst.yTop = m_int.rclDst.yBottom + swp.cy;
WinCalcFrameRect(m_int.hwndFrame, &m_int.rclDst, TRUE);
if (WinID != -1)
WinSetParent(m_int.hwndFrame, HWND_DESKTOP, FALSE);
WinSetParent(m_int.hwndSysMenu, HWND_OBJECT, FALSE);
WinSetParent(m_int.hwndTitleBar, HWND_OBJECT, FALSE);
WinSetParent(m_int.hwndMinMax, HWND_OBJECT, FALSE);
rcl.xLeft = 0;
rcl.yBottom = 0;
rcl.xRight = vo_screenwidth;
rcl.yTop = vo_screenheight;
setAspectRatio(KVAR_FORCEANY);
} else {
if (WinID != -1)
WinSetParent(m_int.hwndFrame, HWNDFROMWINID(WinID), TRUE);
WinSetParent(m_int.hwndSysMenu, m_int.hwndFrame, FALSE);
WinSetParent(m_int.hwndTitleBar, m_int.hwndFrame, FALSE);
WinSetParent(m_int.hwndMinMax, m_int.hwndFrame, FALSE);
rcl = m_int.rclDst;
setAspectRatio(vo_keepaspect ? KVAR_FORCEANY : KVAR_NONE);
}
WinCalcFrameRect(m_int.hwndFrame, &rcl, FALSE);
WinSetWindowPos(m_int.hwndFrame, HWND_TOP,
rcl.xLeft, rcl.yBottom,
rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW |
(WinID == -1 ? SWP_ACTIVATE : 0));
return VO_TRUE;
}
static int color_ctrl_set(char *what, int value)
{
ULONG ulAttr;
ULONG ulValue;
if (!strcmp(what, "brightness"))
ulAttr = KVAA_BRIGHTNESS;
else if (!strcmp(what, "contrast"))
ulAttr = KVAA_CONTRAST;
else if (!strcmp(what, "hue"))
ulAttr = KVAA_HUE;
else if (!strcmp(what, "saturation"))
ulAttr = KVAA_SATURATION;
else
return VO_NOTIMPL;
ulValue = (value + 100) * 255 / 200;
if (kvaSetAttr(ulAttr, &ulValue))
return VO_NOTIMPL;
return VO_TRUE;
}
static int color_ctrl_get(char *what, int *value)
{
ULONG ulAttr;
ULONG ulValue;
if (!strcmp(what, "brightness"))
ulAttr = KVAA_BRIGHTNESS;
else if (!strcmp(what, "contrast"))
ulAttr = KVAA_CONTRAST;
else if (!strcmp(what, "hue"))
ulAttr = KVAA_HUE;
else if (!strcmp(what, "saturation"))
ulAttr = KVAA_SATURATION;
else
return VO_NOTIMPL;
if (kvaQueryAttr(ulAttr, &ulValue))
return VO_NOTIMPL;
// add 1 to adjust range
*value = ((ulValue + 1) * 200 / 255) - 100;
return VO_TRUE;
}
static int control(uint32_t request, void *data)
{
switch (request) {
case VOCTRL_GET_IMAGE:
return get_image(data);
case VOCTRL_DRAW_IMAGE:
return draw_image(data);
case VOCTRL_QUERY_FORMAT:
return query_format(*(uint32_t *)data);
case VOCTRL_FULLSCREEN:
return fs_toggle();
case VOCTRL_SET_EQUALIZER:
{
struct voctrl_set_equalizer_args *args = data;
color_ctrl_set(args->name, args->value);
}
case VOCTRL_GET_EQUALIZER:
{
struct voctrl_get_equalizer_args *args = data;
return color_ctrl_get(args->name, args->valueptr);
}
case VOCTRL_UPDATE_SCREENINFO:
vo_screenwidth = m_int.kvac.cxScreen;
vo_screenheight = m_int.kvac.cyScreen;
aspect_save_screenres(vo_screenwidth, vo_screenheight);
return VO_TRUE;
}
return VO_NOTIMPL;
}
static int draw_frame(uint8_t *src[])
{
return VO_ERROR;
}
static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
{
uint8_t *s;
uint8_t *d;
// copy packed or Y
d = m_int.planes[0] + m_int.stride[0] * y + x;
s = src[0];
mem2agpcpy_pic(d, s, w * m_int.bpp, h, m_int.stride[0], stride[0]);
// YV12 or YVU9
if (m_int.nChromaShift) {
w >>= m_int.nChromaShift; h >>= m_int.nChromaShift;
x >>= m_int.nChromaShift; y >>= m_int.nChromaShift;
// copy U
d = m_int.planes[1] + m_int.stride[1] * y + x;
s = src[1];
mem2agpcpy_pic(d, s, w, h, m_int.stride[1], stride[1]);
// copy V
d = m_int.planes[2] + m_int.stride[2] * y + x;
s = src[2];
mem2agpcpy_pic(d, s, w, h, m_int.stride[2], stride[2]);
}
return 0;
}
#define vo_draw_alpha(imgfmt) \
vo_draw_alpha_##imgfmt(w, h, src, srca, stride, \
m_int.planes[0] + m_int.stride[0] * y0 + m_int.bpp * x0, \
m_int.stride[0])
static void draw_alpha(int x0, int y0, int w, int h,
unsigned char *src, unsigned char *srca, int stride)
{
switch (m_int.iImageFormat) {
case IMGFMT_YV12:
case IMGFMT_YVU9:
vo_draw_alpha(yv12);
break;
case IMGFMT_YUY2:
vo_draw_alpha(yuy2);
break;
case IMGFMT_BGR24:
vo_draw_alpha(rgb24);
break;
case IMGFMT_BGR16:
vo_draw_alpha(rgb16);
break;
case IMGFMT_BGR15:
vo_draw_alpha(rgb15);
break;
}
}
static void draw_osd(void)
{
vo_draw_text(SRC_WIDTH, SRC_HEIGHT, draw_alpha);
}
static void flip_page(void)
{
imgDisplay();
}
static void check_events(void)
{
QMSG qm;
// On slave mode, we need to change our window size according to a
// parent window size
if (WinID != -1) {
RECTL rcl;
WinQueryWindowRect(HWNDFROMWINID(WinID), &rcl);
if (rcl.xLeft != m_int.rclParent.xLeft ||
rcl.yBottom != m_int.rclParent.yBottom ||
rcl.xRight != m_int.rclParent.xRight ||
rcl.yTop != m_int.rclParent.yTop) {
WinSetWindowPos(m_int.hwndFrame, NULLHANDLE,
rcl.xLeft, rcl.yBottom,
rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
SWP_SIZE | SWP_MOVE);
m_int.rclParent = rcl;
}
}
while (WinPeekMsg(m_int.hab, &qm, NULLHANDLE, 0, 0, PM_REMOVE))
WinDispatchMsg(m_int.hab, &qm);
}