#include "video.h" #include #include "subtitles.h" #define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x)) #define OV_COL_R 16 #define OV_COL_G 0 #define OV_COL_B 16 OverlayVideoOutput::OverlayVideoOutput() { lpDD=NULL; lpddsOverlay=NULL; lpddsPrimary=NULL; is_fullscreen=0; yuy2_output=uyvy_output=0; m_parent=NULL; initing=false; needchange=0; memset(&m_oldrd,0,sizeof(m_oldrd)); memset(&winRect,0,sizeof(winRect)); subFont=NULL; m_fontsize=0; resetSubtitle(); } OverlayVideoOutput::~OverlayVideoOutput() { if(is_fullscreen) removeFullScreen(); LPDIRECTDRAWSURFACE o=lpddsOverlay; lpddsOverlay=NULL; if(o) o->Release(); if(lpddsPrimary) lpddsPrimary->Release(); if (lpDD) lpDD->Release(); // BU added NULL check in response to talkback if(subFont) DeleteObject(subFont); } static DWORD DD_ColorMatch(LPDIRECTDRAWSURFACE pdds, COLORREF rgb) { COLORREF rgbT; HDC hdc; DWORD dw = CLR_INVALID; DDSURFACEDESC ddsd; HRESULT hres; // // use GDI SetPixel to color match for us // if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK) { rgbT = GetPixel(hdc, 0, 0); // save current pixel value SetPixel(hdc, 0, 0, rgb); // set our value pdds->ReleaseDC(hdc); } // // now lock the surface so we can read back the converted color // ddsd.dwSize = sizeof(ddsd); while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING) ; if (hres == DD_OK) { dw = *(DWORD *)ddsd.lpSurface; // get DWORD if(ddsd.ddpfPixelFormat.dwRGBBitCount<32) dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // mask it to bpp pdds->Unlock(NULL); } // // now put the color that was there back. // if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK) { SetPixel(hdc, 0, 0, rgbT); pdds->ReleaseDC(hdc); } return dw; } int OverlayVideoOutput::create(VideoOutput *parent, int w, int h, unsigned int ptype, int flipit, double aspectratio) { type=ptype; width=w; height=h; flip=flipit; m_parent=parent; initing=true; HWND hwnd=parent->getHwnd(); if (lpDD) lpDD->Release(); lpDD=NULL; update_monitor_coords(parent); if(!m_found_devguid) DirectDrawCreate(NULL,&lpDD,NULL); else DirectDrawCreate(&m_devguid,&lpDD,NULL); if(!lpDD) { initing=false; return 0; } lpDD->SetCooperativeLevel(hwnd,DDSCL_NOWINDOWCHANGES|DDSCL_NORMAL); DDSURFACEDESC ddsd; INIT_DIRECTDRAW_STRUCT(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL ); // init overlay DDSURFACEDESC ddsdOverlay; INIT_DIRECTDRAW_STRUCT(ddsdOverlay); ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_PITCH; ddsdOverlay.dwWidth=w; ddsdOverlay.dwHeight=h; ddsdOverlay.lPitch=w*4; ddsdOverlay.dwBackBufferCount=0; DDPIXELFORMAT pf[]= { {sizeof(DDPIXELFORMAT),DDPF_FOURCC,MAKEFOURCC('Y','U','Y','2'),0,0,0,0,0}, {sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('U','Y','V','Y'),0,0,0,0,0}, // UYVY {sizeof(DDPIXELFORMAT),DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0}, }; int tab[5]; if(type==NSV_MAKETYPE('Y','U','Y','2')) { tab[0]=0; // default is YUY2 tab[1]=1; tab[2]=-1; } else if(type==NSV_MAKETYPE('U','Y','V','Y')) { tab[0]=1; // make UYVY default tab[1]=0; tab[2]=-1; } else if(type==NSV_MAKETYPE('Y','V','1','2')) { /*tab[0]=2; tab[1]=0; tab[2]=1; tab[3]=-1;*/ //CT> Make YUY2 default too, cause YV12 is borked on some ATI cards/drivers :( tab[0]=0; tab[1]=1; tab[2]=-1; } else { tab[0]=-1; // default is RGB } int x=4096; HRESULT v=-1; for (x = 0; x < sizeof(tab)/sizeof(tab[0]) && tab[x]>=0; x ++) { ddsdOverlay.ddpfPixelFormat=pf[tab[x]]; v=lpDD->CreateSurface(&ddsdOverlay, &lpddsOverlay, NULL); if (!FAILED(v)) break; } if(FAILED(v)||x>=sizeof(tab)/sizeof(tab[0])||tab[x]<0) { initing=false; return 0; } yuy2_output = (tab[x] == 0); uyvy_output = (tab[x] == 1); INIT_DIRECTDRAW_STRUCT(capsDrv); ddrval = lpDD->GetCaps(&capsDrv, NULL); uDestSizeAlign = capsDrv.dwAlignSizeDest; uSrcSizeAlign = capsDrv.dwAlignSizeSrc; dwUpdateFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE; DEVMODE d; d.dmSize=sizeof(d); d.dmDriverExtra=0; EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &d); int rv=OV_COL_R, gv=OV_COL_G, bv=OV_COL_B; INIT_DIRECTDRAW_STRUCT(ovfx); ovfx.dwDDFX=0; switch(d.dmBitsPerPel) { case 16: ovfx.dckDestColorkey.dwColorSpaceLowValue=((rv>>3) << 11) | ((gv>>2) << 5) | (bv>>3); break; case 15: ovfx.dckDestColorkey.dwColorSpaceLowValue=((rv>>3) << 10) | ((gv>>3) << 5) | (bv>>3); break; case 24: case 32: ovfx.dckDestColorkey.dwColorSpaceLowValue=(rv << 16) | (gv << 8) | bv; break; } //try to get the correct bit depth thru directdraw (for fucked up 16 bits displays for ie.) { DDSURFACEDESC DDsd={sizeof(DDsd),}; lpddsPrimary->GetSurfaceDesc(&ddsd); DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth DDsd.dwWidth=8; DDsd.dwHeight=8; DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; LPDIRECTDRAWSURFACE tempsurf; if(lpDD->CreateSurface(&DDsd, &tempsurf, NULL)==DD_OK) { int res=DD_ColorMatch(tempsurf, RGB(rv,gv,bv)); if(res!=CLR_INVALID) ovfx.dckDestColorkey.dwColorSpaceLowValue=res; tempsurf->Release(); } } ovfx.dckDestColorkey.dwColorSpaceHighValue=ovfx.dckDestColorkey.dwColorSpaceLowValue; getRects(&rs,&rd); if(FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) { initing=false; return 0; } initing=false; DDSURFACEDESC dd={sizeof(dd),}; if (lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL) != DD_OK) return 0; unsigned char *o=(unsigned char*)dd.lpSurface; if (uyvy_output||yuy2_output) { int x=dd.lPitch*height/2; while (x--) { if (uyvy_output) { *o++=128; *o++=0; } else { *o++=0; *o++=-128; } } } else { memset(o,0,dd.lPitch*height); o+=dd.lPitch*height; memset(o,128,dd.lPitch*height/2); } lpddsOverlay->Unlock(&dd); InvalidateRect(hwnd,NULL,TRUE); return 1; } void OverlayVideoOutput::getRects(RECT *drs, RECT *drd) { HWND hwnd=m_parent->getHwnd(); if(GetParent(hwnd)) hwnd=GetParent(hwnd); RECT rd,rs; GetClientRect(hwnd,&rd); ClientToScreen(hwnd,(LPPOINT)&rd); ClientToScreen(hwnd,((LPPOINT)&rd) + 1); m_parent->adjustAspect(rd); rd.left-=m_mon_x; rd.right-=m_mon_x; rd.top-=m_mon_y; rd.bottom-=m_mon_y; memset(&rs,0,sizeof(rs)); rs.right=width; rs.bottom=height; //resize overlay for off-screen RECT rfull; //m_parent->getViewport(&rfull,NULL,1); //FUCKO: assume monitor 0 m_parent->getViewport(&rfull,hwnd,1); //FUCKO: okay to use this hwnd? (fixes multimon! -RG) if(rd.right>rfull.right) { int diff=rd.right-rfull.right; float sc=(float)(width)/(float)(rd.right-rd.left); rd.right=rfull.right; rs.right=width-(int)(diff*sc); } if(rd.leftrfull.bottom) { int diff=rd.bottom-rfull.bottom; float sc=(float)(height)/(float)(rd.bottom-rd.top); rd.bottom=rfull.bottom; rs.bottom=height-(int)(diff*sc); } if(rd.topUpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) { needchange=1; } } } int OverlayVideoOutput::onPaint(HWND hwnd, HDC hdc) { if(!m_parent) return 0; PAINTSTRUCT p; BeginPaint(hwnd,&p); RECT r; GetClientRect(hwnd,&r); LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),}; HBRUSH br=CreateBrushIndirect(&lb); FillRect(p.hdc,&r,br); DeleteObject(br); if (curSubtitle) { int m_lastsubxp=curSubtitle->xPos; int m_lastsubyp=curSubtitle->yPos; HDC out=p.hdc; HGDIOBJ oldobj=SelectObject(out,subFont); SetBkMode(out,TRANSPARENT); int centerflags=0; if (m_lastsubxp < 127) centerflags |= DT_LEFT; else if (m_lastsubxp > 127) centerflags |= DT_RIGHT; else centerflags |= DT_CENTER; if (m_lastsubyp < 127) centerflags |= DT_TOP; else if (m_lastsubyp > 127) centerflags |= DT_BOTTOM; // draw outline SetTextColor(out,RGB(0,0,0)); for (int y = -1; y < 2; y++) for (int x = -1; x < 2; x++) { if(!y && !x) continue; RECT r2={subRect.left+x,subRect.top+y,subRect.right+x,subRect.bottom+y}; DrawText(out,curSubtitle->text,-1,&r2,centerflags|DT_NOCLIP|DT_NOPREFIX); } // draw text SetTextColor(out,RGB(curSubtitle->colorRed,curSubtitle->colorGreen,curSubtitle->colorBlue)); DrawText(out,curSubtitle->text,-1,&subRect,centerflags|DT_NOCLIP|DT_NOPREFIX); SelectObject(out,oldobj); } EndPaint(hwnd,&p); return 1; } void OverlayVideoOutput::displayFrame(const char *buf, int size, int time) { if(!m_parent) return; DDSURFACEDESC dd={sizeof(dd),}; if (m_parent->vid_vsync) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0); HRESULT result; if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) { //CT>FUCKO:reenable me (ctrl+alt+del on win2k) //if(result==DDERR_SURFACELOST) width=-1; //will try to recreate the surface in the next processData() call return; } if(type==NSV_MAKETYPE('Y','V','1','2')) { const YV12_PLANES *planes=(YV12_PLANES *)buf; if (uyvy_output||yuy2_output) { // YV12planar->UYVY or YUY2 unsigned char *o=(unsigned char*)dd.lpSurface; const unsigned char *yi=planes->y.baseAddr; const unsigned char *ui=planes->u.baseAddr; const unsigned char *vi=planes->v.baseAddr; int y=height; if (flip) o+=dd.lPitch*(height-1); while (y>0) { int x=width; unsigned char *oo=o; if (uyvy_output) while (x>0) { o[0]=*ui++; o[1]=*yi++; o[2]=*vi++; o[3]=*yi++; o+=4; x-=2; } else while (x>0) { o[0]=*yi++; o[1]=*ui++; o[2]=*yi++; o[3]=*vi++; o+=4; x-=2; } ui-=width/2; vi-=width/2; yi+=planes->y.rowBytes-width; x=width; if (flip) o=oo-dd.lPitch; else o+=dd.lPitch-width*2; oo=o; if (uyvy_output) while (x>0) { o[0]=*ui++; o[1]=*yi++; o[2]=*vi++; o[3]=*yi++; o+=4; x-=2; } else while (x>0) { o[0]=*yi++; o[1]=*ui++; o[2]=*yi++; o[3]=*vi++; o+=4; x-=2; } if (flip) o=oo-dd.lPitch; else o+=dd.lPitch-width*2; ui+=planes->u.rowBytes-(width/2); vi+=planes->v.rowBytes-(width/2); yi+=planes->y.rowBytes-width; y-=2; } } else { // woo native YV12 copy int f=!!flip; char *o=(char*)dd.lpSurface+(f*height*dd.lPitch); const char *i=(const char*)planes->y.baseAddr; int d_o=dd.lPitch; if (f) d_o=-d_o; else o-=d_o; int h2=height; while (h2--) { o+=d_o; memcpy(o,i,width); i+=planes->y.rowBytes; } d_o/=2; int w2=width/2; h2=height/2; i=(const char*)planes->v.baseAddr; o=(char*)dd.lpSurface+(height*dd.lPitch*(f+4))/4; if (!f) o-=d_o; while (h2--) { o+=d_o; memcpy(o,i,w2); i+=planes->v.rowBytes; } o=(char*)dd.lpSurface+(height*dd.lPitch*(f+5))/4; i=(const char*)planes->u.baseAddr; h2=height/2; if (!f) o-=d_o; while (h2--) { o+=d_o; memcpy(o,i,w2);i+=planes->u.rowBytes; } } } else if(type==NSV_MAKETYPE('Y','U','Y','2') || type==NSV_MAKETYPE('U','Y','V','Y')) { const char *a=buf; char *b=(char *)dd.lpSurface; int l=width*2,l2=dd.lPitch; if(flip) { b+=(height-1)*l2; l2=-l2; } int is_uyvy=type==NSV_MAKETYPE('U','Y','V','Y'); if (uyvy_output && !is_uyvy || (yuy2_output && is_uyvy)) // convert to uyvy { for(int i=0;i0) { b[0]=a[1]; b[1]=a[0]; b[2]=a[3]; b[3]=a[2]; a+=4; b+=4; } memcpy(b,a,l); b+=l2; a+=l; } } else { //wee straight YUY2 copy for(int i=0;iUnlock(&dd); if (m_parent->osdShowing()) { RECT rs, rd; getRects(&rs,&rd); HDC hdc; #if 1 // set both these 1s to 0s to put it back on ryan's mode HWND h=m_parent->getHwnd(); hdc=GetDC(h); #else if (lpddsPrimary->GetDC(&hdc)==DD_OK) { #endif m_parent->drawOSD(hdc, &rd); #if 1 ReleaseDC(h,hdc); #else lpddsPrimary->ReleaseDC(hdc); } #endif } } void OverlayVideoOutput::goFullScreen() { /* fullscreen_controls = new GuiObjectWnd; fullscreen_controls->setContent("video.fullscreen_controls"); fullscreen_controls->init(m_parent); RECT r; Std::getViewport(&r,m_parent->gethWnd(),1); RECT nr = r; nr.top = (int)(r.bottom - (r.bottom - r.top) * 0.15); nr.bottom = (int)(r.bottom - (r.bottom - r.top) * 0.05); fullscreen_controls->resizeToRect(&nr); */ is_fullscreen=1; } void OverlayVideoOutput::removeFullScreen() { /* delete fullscreen_controls; fullscreen_controls = NULL;*/ is_fullscreen=0; } int OverlayVideoOutput::showOSD() { // if (fullscreen_controls != NULL) fullscreen_controls->setVisible(TRUE); // enabling the following code will cause the top & bottom OSD bars // to squish the image (instead of crop it): /*if(lpddsOverlay) { RECT rd,rs; getRects(&rs,&rd); HWND hwnd=m_parent->getHwnd(); if(GetParent(hwnd)) hwnd=GetParent(hwnd); RECT temp; GetClientRect(hwnd,&temp); int bottom_margin = ((temp.bottom-temp.top) - (rd.bottom-rd.top)) / 2; int pixels_to_clip = max(0, m_parent->getOSDbarHeight() - bottom_margin); rd.bottom -= pixels_to_clip; lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx); }*/ return 1; } void OverlayVideoOutput::hideOSD() { //if (fullscreen_controls != NULL) fullscreen_controls->setVisible(FALSE); // 1) repaint the OSD area with the overlay color here HWND hwnd = m_parent->getHwnd(); if(GetParent(hwnd)) hwnd=GetParent(hwnd); HDC hdc = GetDC(hwnd); if (hdc) { RECT r; GetClientRect(hwnd,&r); LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),}; HBRUSH br=CreateBrushIndirect(&lb); FillRect(hdc,&r,br); DeleteObject(br); ReleaseDC(hwnd, hdc); } // 2) readjust the overlay destination rectangle /*if(lpddsOverlay) { RECT rd,rs; getRects(&rs,&rd); lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx); }*/ } void OverlayVideoOutput::drawSubtitle(SubsItem *item) { curSubtitle=item; HWND hwnd=m_parent->getHwnd(); RECT oldrect=subRect; GetClientRect(hwnd,&subRect); if(item) { RECT oldwinRect=winRect; GetClientRect(hwnd,&winRect); if(!subFont || ((winRect.bottom-winRect.top)!=(oldwinRect.bottom-oldwinRect.top)) || m_fontsize!=item->fontSize) { if(subFont) DeleteObject(subFont); m_fontsize=item->fontSize; subFont=CreateFont(14+item->fontSize+18*(winRect.bottom-winRect.top)/768,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial"); } HDC out=GetDC(hwnd); SelectObject(out,subFont); SIZE s; GetTextExtentPoint32(out,item->text,strlen(item->text),&s); { // calcul for multiline text const char *p=item->text; int n=0; while(*p!=0) if(*p++=='\n') n++; if(n) s.cy*=(n+1); } if (item->xPos > 127) // towards the right { subRect.right -= ((subRect.right-subRect.left) * (255-item->xPos)) / 256; } else if (item->xPos < 127) { subRect.left += ((subRect.right-subRect.left) * item->xPos) / 256; } subRect.top += ((subRect.bottom-s.cy-subRect.top) * item->yPos)/255; subRect.bottom=subRect.top + s.cy; ReleaseDC(hwnd,out); } //just redraw the correct portion InvalidateRect(hwnd,&oldrect,TRUE); InvalidateRect(hwnd,&subRect,TRUE); } void OverlayVideoOutput::resetSubtitle() { curSubtitle=NULL; subRect.top=65536; }