www.pudn.com > src.rar > DirectVobSubFilter.cpp


#include "stdafx.h" 
 
#include "resource.h" 
 
#include  
#include  
 
#include "DirectVobSubUIDs.h" 
 
#include "DirectVobSubFilter.h" 
#include "DirectVobSubInputPin.h" 
#include "DirectVobSubOutputPin.h" 
#include "TextInputPin.h" 
#include "DirectVobSubPropPage.h" 
 
#include "SubtitleSource.h" 
 
#include "Main.h" 
 
#include "misc.h" 
 
#include "systray.h" 
 
#include "Main.h" 
 
/////////////////////////////////////////////////////////////////////////// 
 
#include "../vobsub/colorconv.cpp" 
 
//////////////////////////////////////////////////////////////////////////// 
// 
// Constructor 
// 
 
CDirectVobSubFilter::CDirectVobSubFilter(TCHAR* tszName, LPUNKNOWN punk, HRESULT* phr, const GUID& guid) : 
	CTransformFilter(tszName, punk, guid), 
	m_Allocator(this, phr),  
	m_fUsingOwnAllocator(false) 
{ 
	AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
	memset(&m_bihIn, 0, sizeof(BITMAPINFOHEADER)); 
	memset(&m_bihSub, 0, sizeof(BITMAPINFOHEADER)); 
	memset(&m_bihOut, 0, sizeof(BITMAPINFOHEADER)); 
 
	m_mode = NONE; 
 
	m_textidx = 0; 
 
	m_file = NULL; 
 
	m_fAudioReconnected = false; 
 
	m_fMSMpeg4Fix = false; 
 
	m_fDivxPlusFix = false; 
 
	m_fVMRFilter = m_fVMRFilterActive = false; 
 
	m_hdc = 0; 
	m_hbm = 0; 
	m_hfont = 0; 
 
	m_fps = 25; 
 
 
/* ResX2 */ 
	m_fResX2Active = false; 
	m_pTempPicBuff = NULL; 
 
 
	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Hint"), _T("The first three are fixed, but you can add more up to ten entries.")); 
	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path0"), _T(".")); 
	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path1"), _T("c:\\subtitles")); 
	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path2"), _T(".\\subtitles")); 
 
	m_fLoading = false; 
 
	HRESULT hr = S_OK; 
	m_pTextInput.Add(new CTextInputPin(this, m_pLock, &hr)); 
 
	m_fTextStreamWorkaroundDone = false; 
 
	m_iLastSegmentPosted = -1; 
 
	m_fThreadActive = m_fBreakBuffering = false; 
	CAMThread::Create(); 
 
	m_hSystrayThread = 0; 
	m_tbid.hSystrayWnd = NULL; 
	m_tbid.graph = NULL; 
	m_tbid.fRunOnce = false; 
	m_tbid.fShowIcon = (theApp.m_AppName.Find(_T("zplayer"), 0) < 0 || !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_ENABLEZPICON), 0)); 
 
	m_frd.pFilter = this; 
	m_frd.hThread = NULL; 
	m_frd.hEndThreadEvent = CreateEvent(0, FALSE, FALSE, 0); 
	m_frd.hRefreshEvent = CreateEvent(0, FALSE, FALSE, 0); 
	DWORD tid; 
	m_frd.hThread = CreateThread(0, 0, FileReloaderThreadProc, &m_frd, 0, &tid); 
	SetThreadPriority(m_frd.hThread, THREAD_PRIORITY_LOWEST); 
/* 
	if(SUCCEEDED(hr = DirectDrawCreateEx(NULL, (VOID**)&m_pDD, IID_IDirectDraw7, NULL))) 
	{ 
		if(FAILED(m_pDD->SetCooperativeLevel(NULL, DDSCL_NORMAL))) 
			m_pDD = NULL; 
	} 
*/ 
#ifdef DEBUG 
	dwRegister = -1; 
#endif 
} 
 
CDirectVobSubFilter::~CDirectVobSubFilter() 
{ 
	CallWorker(CMD_EXIT); 
	CAMThread::Close(); 
 
	{ 
		CAutoLock lock(&m_fileLock); 
 
		if(m_file) {delete m_file; m_file = NULL;} 
 
		if(m_hfont) {DeleteObject(m_hfont); m_hfont = 0;} 
		if(m_hbm) {DeleteObject(m_hbm); m_hbm = 0;} 
		if(m_hdc) {DeleteObject(m_hdc); m_hdc = 0;} 
 
		for(int i = 0, j = m_pTextInput.GetSize(); i < j; i++) delete m_pTextInput[i]; 
	} 
 
	if(m_frd.hThread) 
	{ 
		SetThreadPriority(m_frd.hThread, THREAD_PRIORITY_NORMAL); 
 
		SetEvent(m_frd.hEndThreadEvent); 
		if(WaitForSingleObject(m_frd.hThread, 10000) != WAIT_OBJECT_0) 
		{ 
			TRACE(_T("Whatever...")); 
			TerminateThread(m_frd.hThread, 0); 
		} 
 
		m_frd.hThread = 0; 
		m_frd.files.RemoveAll(); 
	} 
 
	CloseHandle(m_frd.hEndThreadEvent); 
	CloseHandle(m_frd.hRefreshEvent); 
 
/* ResX2 */ 
	if(m_pTempPicBuff) delete [] m_pTempPicBuff; 
} 
 
CUnknown* CDirectVobSubFilter::CreateInstance(LPUNKNOWN punk, HRESULT* phr) 
{ 
    CDirectVobSubFilter* pNewObject = new CDirectVobSubFilter(NAME("DirectVobSub"), punk, phr, CLSID_DirectVobSubFilter); 
     
	if(pNewObject == NULL) *phr = E_OUTOFMEMORY; 
 
    return pNewObject; 
} 
 
STDMETHODIMP CDirectVobSubFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv) 
{ 
    CheckPointer(ppv, E_POINTER); 
 
	HRESULT hr; 
 
    hr = (riid == IID_IDirectVobSub) ? GetInterface((IDirectVobSub *)this, ppv) 
		:(riid == IID_IDirectVobSub2) ? GetInterface((IDirectVobSub2 *)this, ppv) 
		:(riid == IID_ISpecifyPropertyPages) ? GetInterface((ISpecifyPropertyPages *)this, ppv) 
		:(riid == IID_IPersistStream) ? GetInterface((IPersistStream *)this, ppv) 
		:(riid == IID_IAMStreamSelect) ? GetInterface((IAMStreamSelect *)this, ppv) 
		: CTransformFilter::NonDelegatingQueryInterface(riid, ppv); 
 
	return hr; 
} 
 
HRESULT CDirectVobSubFilter::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) 
{ 
	if(pGraph) 
	{ 
		AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
		if(!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 0)) 
		{ 
			unsigned __int64 ver = GetFileVersion(_T("divx_c32.ax")); 
			if(((ver >> 48)&0xffff) == 4 && ((ver >> 32)&0xffff) == 2) 
			{ 
				DWORD dwVersion = GetVersion(); 
				DWORD dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); 
				DWORD dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); 
				 
				if(dwVersion < 0x80000000 && dwWindowsMajorVersion >= 5) 
				{ 
					AfxMessageBox(IDS_DIVX_WARNING); 
					theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 1); 
				} 
			} 
		} 
	} 
	else 
	{ 
		if(m_hSystrayThread) 
		{ 
			SendMessage(m_tbid.hSystrayWnd, WM_CLOSE, 0, 0); 
 
			if(WaitForSingleObject(m_hSystrayThread, 10000) != WAIT_OBJECT_0) 
			{ 
				DbgLog((LOG_TRACE, 0, _T("CALL THE AMBULANCE!!!"))); 
				TerminateThread(m_hSystrayThread, (DWORD)-1); 
			} 
 
			m_hSystrayThread = 0; 
		} 
	} 
 
	return CTransformFilter::JoinFilterGraph(pGraph, pName); 
} 
 
DWORD g_dwRegister = 0; 
 
HRESULT CDirectVobSubFilter::StartStreaming() 
{ 
	m_fLoading = false; 
 
//// 
	if(m_hfont) {DeleteObject(m_hfont); m_hfont = NULL;} 
	if(m_hbm) {DeleteObject(m_hbm); m_hbm = NULL;} 
	if(m_hdc) {DeleteDC(m_hdc); m_hdc = NULL;} 
 
	if(!m_hdc) 
	{ 
		m_hdc = CreateCompatibleDC(NULL); 
 
		struct BM 
		{ 
			BITMAPINFOHEADER bih; 
			DWORD mask[3]; 
		} b; 
		 
		memset(&b, 0, sizeof(b)); 
		b.bih.biSize = sizeof(BITMAPINFOHEADER); 
		b.bih.biWidth = m_bihSub.biWidth; 
		b.bih.biHeight = -abs(m_bihSub.biHeight); 
		b.bih.biBitCount = 32; 
		b.bih.biPlanes = 1; 
		b.bih.biCompression = BI_BITFIELDS; 
		b.mask[0] = 0xFF0000; 
		b.mask[1] = 0x00FF00; 
		b.mask[2] = 0x0000FF; 
 
		m_hbm = CreateDIBSection( 
			m_hdc, 
			(BITMAPINFO *)&b, 
			DIB_RGB_COLORS, 
			NULL, 
			NULL, 
			0); 
 
		BITMAP bm; 
		GetObject(m_hbm, sizeof(bm), &bm); 
		memsetd(bm.bmBits, 0xff000000, bm.bmHeight*bm.bmWidthBytes); 
	} 
 
	if(!m_hfont) 
	{ 
		CAutoLock cAutolock(&m_propsLock); 
		 
		LOGFONT lf; 
		memset(&lf, 0, sizeof(lf)); 
		lf.lfCharSet = DEFAULT_CHARSET; 
		lf.lfOutPrecision = OUT_CHARACTER_PRECIS; 
		lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; 
		lf.lfQuality = ANTIALIASED_QUALITY; 
		HDC hdc = GetDC(NULL); 
		lf.lfHeight = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72); 
		ReleaseDC(NULL, hdc); 
		lf.lfWeight = FW_BOLD; 
		_tcscpy(lf.lfFaceName, _T("Arial")); 
		m_hfont = CreateFontIndirect(&lf); 
	} 
//// 
 
	CallWorker(CMD_RESET); 
 
    CAutoLock lock(&m_AccessLock); 
 
	HookTextStreams(); 
 
	m_iLastSegmentPosted = -1; 
 
	m_tbid.fRunOnce = true; 
 
	m_fDivxPlusFix = false; 
 
	CComPtr pFilter; 
	if(SUCCEEDED(m_pGraph->FindFilterByName(L"HPlus YUV Video Renderer", &pFilter))) 
		m_fDivxPlusFix = true; 
 
/* ResX2 */ 
	if(m_fResX2Active) {m_pTempPicBuff = new BYTE[4*m_bihIn.biWidth*m_bihIn.biHeight*4];} 
 
	put_MediaFPS(m_fMediaFPSEnabled, m_MediaFPS); 
 
/* 
if(g_dwRegister == 0) 
AddToRot(m_pGraph, &g_dwRegister); 
*/ 
	return CTransformFilter::StartStreaming(); 
} 
 
HRESULT CDirectVobSubFilter::StopStreaming() 
{ 
/* 
	RemoveFromRot(g_dwRegister); 
	g_dwRegister = 0; 
*/ 
/* ResX2 */ 
	if(m_pTempPicBuff) {delete [] m_pTempPicBuff; m_pTempPicBuff = NULL;} 
 
	CallWorker(CMD_RESET); 
 
    CAutoLock lock(&m_AccessLock); 
 
	if(m_hfont) {DeleteObject(m_hfont); m_hfont = NULL;} 
	if(m_hbm) {DeleteObject(m_hbm); m_hbm = NULL;} 
	if(m_hdc) {DeleteDC(m_hdc); m_hdc = NULL;} 
 
	return CTransformFilter::StopStreaming(); 
} 
 
HRESULT CDirectVobSubFilter::CheckConnect(PIN_DIRECTION dir, IPin* pPin) 
{ 
	if(dir == PINDIR_INPUT) 
	{ 
	} 
	else if(dir == PINDIR_OUTPUT) 
	{ 
		AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
		if(!!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_VMRZOOMENABLED), 0)) 
		{ 
			CComQIPtr pVMRFC = FindVMRFilterFromPin(pPin); 
			if(pVMRFC && pVMRFC->SetNumberOfStreams(1) == S_OK) m_fVMRFilterActive = true; 
		} 
	} 
 
	return CTransformFilter::CheckConnect(dir, pPin); 
} 
 
HRESULT CDirectVobSubFilter::CompleteConnect(PIN_DIRECTION dir, IPin* pReceivePin) 
{ 
	if(dir == PINDIR_INPUT) 
	{ 
		CComPtr pFilter; 
 
		// needed when we have a decoder with a version number of 3.x 
		if(SUCCEEDED(m_pGraph->FindFilterByName(L"DivX MPEG-4 DVD Video Decompressor ", &pFilter))  
		|| SUCCEEDED(m_pGraph->FindFilterByName(L"Microcrap MPEG-4 Video Decompressor", &pFilter)) 
		|| (SUCCEEDED(m_pGraph->FindFilterByName(L"Microsoft MPEG-4 Video Decompressor", &pFilter))  
			&& (GetFileVersion(_T("mpg4ds32.ax")) >> 48) <= 3)) 
		{ 
			m_fMSMpeg4Fix = true; 
		} 
 
		for(int i = 0, j = m_pTextInput.GetSize(), k = 0; i < j; i++) 
		{ 
			if(m_pTextInput[i]->IsConnected()) k++; 
		} 
 
		if(i == k) 
		{ 
			HRESULT hr; 
			m_pTextInput.Add(new CTextInputPin(this, m_pLock, &hr)); 
		} 
	} 
	else if(dir == PINDIR_OUTPUT) 
	{ 
		if(!m_hSystrayThread) 
		{ 
			m_tbid.graph = m_pGraph; 
			m_tbid.dvs = static_cast(this); 
 
			DWORD tid; 
			m_hSystrayThread = CreateThread(0, 0, SystrayThreadProc, &m_tbid, 0, &tid); 
		} 
 
		int w = m_bihIn.biWidth, h = m_bihIn.biHeight; 
		m_fResX2Active = AdjustFrameSize(w, h); 
		m_bihSub = m_bihIn; 
		m_bihSub.biWidth = w; 
		m_bihSub.biHeight = h; 
		m_bihSub.biSizeImage = m_bihSub.biWidth*m_bihSub.biHeight*4; 
		 
		CallWorker(CMD_INIT); 
 
		InitRTSStreams(false); 
 
		CComQIPtr pVMRFC = FindVMRFilterFromPin(pReceivePin); 
		if(pVMRFC) m_fVMRFilter = true; 
		else m_fVMRFilterActive = false; 
	} 
 
	return CTransformFilter::CompleteConnect(dir, pReceivePin); 
} 
 
HRESULT CDirectVobSubFilter::BreakConnect(PIN_DIRECTION dir) 
{ 
	if(dir == PINDIR_INPUT) 
	{ 
	} 
	else if(dir == PINDIR_OUTPUT) 
	{ 
		m_fVMRFilter = m_fVMRFilterActive = false; 
	} 
 
	return CTransformFilter::BreakConnect(dir); 
} 
 
CBasePin* CDirectVobSubFilter::GetPin(int n) 
{ 
	if(n >= 0 && n < 2) 
	{ 
		if(m_pInput == NULL) 
		{ 
			HRESULT hr = S_OK; 
 
			m_pInput = new CDirectVobSubInputPin(this, &hr); 
 
			ASSERT(SUCCEEDED(hr)); 
 
			if(m_pInput == NULL)  
				return NULL; 
 
			m_pOutput = new CDirectVobSubOutputPin(this, &hr); 
 
			ASSERT(SUCCEEDED(hr)); 
 
			if(m_pOutput == NULL) 
			{ 
				delete m_pInput; 
				m_pInput = NULL; 
			} 
		} 
 
		return (n == 0) ? (CBasePin*)m_pInput : (n == 1) ? (CBasePin*)m_pOutput : NULL; 
	} 
 
	n -= 2; 
 
	if(n >= 0 && n < m_pTextInput.GetSize()) 
	{ 
		return m_pTextInput[n];  
	} 
 
	n -= m_pTextInput.GetSize(); 
 
	return NULL; 
} 
 
int CDirectVobSubFilter::GetPinCount() 
{ 
    return(2 + m_pTextInput.GetSize()); 
} 
 
int CDirectVobSubFilter::CalcCurrentTime() 
{ 
	int t = m_pSubClock  
		? (int)(m_pSubClock->GetTime()/10000)  
		: m_tPrev.Millisecs(); 
 
	return MulDiv(t - m_SubtitleDelay, m_SubtitleSpeedMul, m_SubtitleSpeedDiv); 
} 
 
void StretchPut(BITMAP& bm, CRect dstrect, CVobSubImage& src, int fSmooth); 
 
void CDirectVobSubFilter::AddVobSub(SubImage* img, int start, int stop) 
{ 
	BITMAP bm; 
	GetObject(img->hbm, sizeof(BITMAP), &bm); 
 
	if(m_fPolygonize) 
	{ 
		uint color = m_file->m_img.ClearBorders(); 
 
		CRenderedTextSubtitle rts; 
		rts.m_fSSA = true; 
		rts.m_dstScreenSize = m_file->GetSize(); 
#ifdef UNICODE 
		rts.CreateDefaultStyle(DEFAULT_CHARSET, true); 
#else 
		rts.CreateDefaultStyle(DEFAULT_CHARSET, false); 
#endif 
		STSStyle defstyle; 
		rts.GetDefaultStyle(defstyle); 
		defstyle.colors[0] = m_TextColor;//((color>>16)&0xff)|((color>>8)&0xff00)|((color<<16)&0xff0000) 
		defstyle.outlineWidth = m_fOutline?2:0; 
		defstyle.shadowDepth = m_fShadow?2:0; 
		HDC hdc = ::GetDC(0); 
		int lfHeight = -MulDiv(m_lf.lfHeight, 72, GetDeviceCaps(hdc, LOGPIXELSY)); 
		ReleaseDC(0, hdc); 
		defstyle.fontScaleX = defstyle.fontScaleY = 100*lfHeight/defstyle.fontSize; 
		defstyle.scrAlignment = 5; 
		rts.SetDefaultStyle(defstyle); 
 
		CString str; 
		if(!m_file->m_img.Polygonize(str)) return; 
 
		CString pos; 
 
		if(m_fOverridePlacement) 
		{ 
			pos.Format(_T("{\\an2\\pos(%d,%d)}"),  
				MulDiv(m_file->GetSize().cx, m_PlacementXperc, 100),  
				MulDiv(m_file->GetSize().cy, m_PlacementYperc, 100)); 
		} 
		else 
		{ 
			pos.Format(_T("{\\pos(%d,%d)}"),  
				m_file->m_img.rect.CenterPoint().x, 
				m_file->m_img.rect.CenterPoint().y); 
		} 
		 
		rts.Add(_T(""), start, stop, pos + str, _T("Default"), _T(""), _T("")); 
 
		img->Clear(0xff000000); 
		rts.Render(bm, (start+stop)/2, m_fps); 
	} 
	else 
	{ 
		m_file->SetAlignment(m_fOverridePlacement, m_PlacementXperc, m_PlacementYperc, 1, 1); 
 
		CRect r; 
		m_file->GetDestrect(r, bm.bmWidth, bm.bmHeight); 
 
		img->Clear(0xff000000); 
		StretchPut(bm, r, m_file->m_img, m_file->m_fSmooth); 
	} 
 
	img->start = start; 
	img->stop = stop; 
 
	if(m_bihIn.biCompression > 3) 
		img->ConvertToYUY2(); 
 
	m_sic.MarkNextAsOccupied(); 
} 
 
void CDirectVobSubFilter::AddText(SubImage* img, int start, int stop, int PTS) 
{ 
	CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; 
 
	if(m_fAdvancedRenderer) 
	{ 
		if(!m_fDoPreBuffering) 
		{ 
			m_sic.RemoveAll(); 
			img = m_sic.GetNextFree(); 
			ASSERT(img); 
		} 
 
		BITMAP bm; 
		GetObject(img->hbm, sizeof(bm), &bm); 
 
		img->Clear(0xff000000); 
 
		if(m_fDoPreBuffering) 
		{ 
			if(!rts.Render(bm, (start+stop)>>1, m_fps)) return; 
 
			img->start = start; 
			img->stop = stop; 
		} 
		else 
		{ 
			if(!rts.Render(bm, PTS, m_fps)) return; 
 
			img->start = PTS; 
			img->stop = PTS + LOOKAHEADSTEPSIZE; 
		} 
	} 
	else 
	{ 
		int i = rts.SearchSub((start+stop)>>1, m_fps); 
		if(i < 0) return; 
 
		HDC hdc = m_sic.GetDC(); 
		HFONT hfont = m_sic.GetFont(); 
 
 
#ifndef UNICODE 
		// Under Win9x we have to select the font into the screen's DC first to have antialiasing, argh... 
		HDC hdcScreen = GetDC(NULL); 
		HFONT hOldScreenFont = (HFONT)SelectObject(hdcScreen, hfont); 
		SelectObject(hdcScreen, hOldScreenFont); 
		ReleaseDC(NULL, hdcScreen); 
#endif 
		 
		BITMAP bm; 
		GetObject(img->hbm, sizeof(BITMAP), &bm); 
		 
		HANDLE hbmOld = SelectObject(hdc, img->hbm); 
		HANDLE hfontOld = SelectObject(hdc, hfont); 
		 
		CString str = rts.GetStr(i); 
 
	//	int border = min(min(30, bm.bmWidth/3), bm.bmHeight/3); 
	//	CRect r = CRect(border, border, bm.bmWidth - border, bm.bmHeight - border), r2 = r; 
		CRect r = CRect(0, 0, bm.bmWidth, bm.bmHeight), r2 = r; 
		 
		DrawText(hdc, str, str.GetLength(), &r2, DT_CALCRECT|DT_EXTERNALLEADING|DT_NOPREFIX|DT_WORDBREAK); 
		 
		r2 -= r2.CenterPoint(); 
		 
		if(!m_fOverridePlacement) 
		{	 
			int h = abs((int)m_bihIn.biHeight); 
 
			CPoint p = r.CenterPoint(); 
			r2 += h < bm.bmHeight  
				? CPoint(p.x, (bm.bmHeight*3 + h) / 4)  
				: CPoint(p.x, (r.bottom*4 + p.y) / 5); 
			 
			if(r2.bottom > bm.bmHeight) r2 -= CPoint(0, r2.bottom - bm.bmHeight + 10); 
		} 
		else 
		{ 
			r2 += CPoint(MulDiv(bm.bmWidth, m_PlacementXperc, 100), MulDiv(bm.bmHeight, m_PlacementYperc, 100)); 
		} 
		 
		CRect rr = r2 + CRect(3,3,3,3); 
 
		img->Clear(0xff000000); 
 
		SetMapMode(hdc, MM_TEXT); 
		SetBkMode(hdc, TRANSPARENT); 
		 
		UINT flags = DT_CENTER|DT_VCENTER|DT_NOPREFIX|DT_WORDBREAK; 
 
		int CharSet = rts.GetCharSet(i); 
		if(CharSet == HEBREW_CHARSET || CharSet == ARABIC_CHARSET) flags |= DT_RTLREADING; 
		 
		static const char outline[][2] =  
		{ 
					{-1,-2},{ 0,-2},{+1,-2}, 
			{-2,-1},{-1,-1},{ 0,-1},{+1,-1},{+2,-1}, 
			{-2, 0},{-1, 0},		{+1, 0},{+2, 0}, 
			{-2,+1},{-1,+1},{ 0,+1},{+1,+1},{+2,+1}, 
					{-1,+2},{ 0,+2},{+1,+2}, 
		}; 
		 
		{ 
			if(m_fOutline) 
			{ 
				SetTextColor(hdc, 0); 
				for(int i = 0; i < 20; i++) 
				{ 
					CRect r3 = r2 + CPoint(outline[i][0], outline[i][1]); 
					DrawText(hdc, str, str.GetLength(), &r3, flags); 
				} 
			} 
			 
			if(m_fShadow) 
			{ 
				SetTextColor(hdc, 0); 
				CRect r3 = r2 + CPoint(2, 2); 
				if(m_fOutline) r3 += CPoint(1, 1);  
				 
				DrawText(hdc, str, str.GetLength(), &r3, flags); 
			} 
			 
			SetTextColor(hdc, m_TextColor); 
		} 
 
		DrawText(hdc, str, str.GetLength(), &r2, flags); 
		 
		r2.InflateRect(2, 2); 
		 
		r &= r2; 
		 
		SelectObject(hdc, hfontOld); 
		SelectObject(hdc, hbmOld); 
 
		GdiFlush(); 
 
		img->start = start; 
		img->stop = stop; 
	} 
 
	if(m_bihIn.biCompression > 3) 
		img->ConvertToYUY2(); 
 
	m_sic.MarkNextAsOccupied(); 
} 
 
SubImage* CDirectVobSubFilter::GetSubImage() 
{ 
	if(m_fHideSubtitles) return(NULL); 
 
	int PTS = CalcCurrentTime(); 
 
	SubImage* ret = NULL; 
 
	m_sic.Update(PTS); 
 
	if(m_fDoPreBuffering) 
	{ 
		if(!m_fThreadActive) CallWorker(CMD_SETPTS); 
	} 
 
	ret = m_sic.LookupSubImage(PTS); 
 
	if(!m_fDoPreBuffering) 
	{ 
		m_sic.MarkAsStart(PTS); 
 
		if(!ret) 
		{ 
			CAutoLock cAutolock(&m_fileLock); 
 
			if(m_mode == VOBSUB && m_file != NULL) 
			{ 
				int i = m_file->GetFrameIdxByTimeStamp(PTS); 
 
				if(i >= 0 && m_file->GetFrame(i) && (ret = m_sic.GetNextFree()) != NULL) 
				{ 
					int start = m_file->m_img.nTimeStamp; 
					int stop = m_file->m_img.nTimeStamp + m_file->m_img.nDisplayTime; 
 
					if(PTS < stop) AddVobSub(ret, start, stop); 
				} 
			} 
			else if(m_mode == TEXT && m_textidx >= 0 && m_textidx < m_textlangs.GetSize()) 
			{ 
				CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; 
 
				int segment; 
				const STSSegment* s = rts.SearchSubs(PTS, m_fps, &segment); 
 
				if(s && (ret = m_sic.GetNextFree()) != NULL) 
				{ 
					int start = rts.TranslateSegmentStart(segment, m_fps); 
					int stop = rts.TranslateSegmentEnd(segment, m_fps); 
 
					AddText(ret, start, stop, PTS); 
				} 
			} 
 
			ret = m_sic.LookupSubImage(PTS); 
		} 
	} 
 
	return(ret); 
} 
 
void CDirectVobSubFilter::Post_EC_OLE_EVENT() 
{ 
	if(m_mode != TEXT || m_textidx < 0 || m_textidx >= m_textlangs.GetSize()) 
		return; 
 
	CComQIPtr pMES = m_pGraph; 
	if(!pMES) return; 
 
	CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; 
 
//	CAutoLock cAutoLock(&rts.m_csLock); 
 
	CComBSTR bstr1("Text"), bstr2; 
 
	int segment; 
	const STSSegment* s = rts.SearchSubs(CalcCurrentTime(), m_fps, &segment); 
 
	CString str; 
 
	if(!s && m_iLastSegmentPosted >= 0) 
	{ 
		bstr2 = " "; 
 
		CComQIPtr pMES = m_pGraph; 
		if(pMES) pMES->Notify(EC_OLE_EVENT, (LONG_PTR)bstr1.Detach(), (LONG_PTR)bstr2.Detach()); 
 
		m_iLastSegmentPosted = -1; 
	} 
	else if(s && m_iLastSegmentPosted != segment) 
	{ 
		int start = rts.TranslateSegmentStart(segment, m_fps); 
		int stop = rts.TranslateSegmentEnd(segment, m_fps); 
 
		for(int i = 0, j = s->subs.GetSize(); i < j; i++) 
		{ 
			str = rts.GetMBCSStr(s->subs[i]); 
#ifdef UNICODE 
			char* buff = new char[str.GetLength()+2]; 
			for(int k = 0; k < str.GetLength(); k++) buff[k] = (char)str[k]; 
			if(i 0) 
		{ 
			CComQIPtr pMES = m_pGraph; 
			if(pMES) pMES->Notify(EC_OLE_EVENT, (LONG_PTR)bstr1.Detach(), (LONG_PTR)bstr2.Detach()); 
		} 
 
		m_iLastSegmentPosted = segment; 
	} 
} 
 
HRESULT CDirectVobSubFilter::Receive(IMediaSample* pSample) 
{ 
	HRESULT hr = CTransformFilter::Receive(pSample); 
 
	// It could have been placed into Transform(..), but if we don't  
	// deliver the sample before sending EC_OLE_EVENT, the text  
	// will appear one frame earlier (I guess because the renderer  
	// is double/triple-buffering the picture) 
	Post_EC_OLE_EVENT(); 
 
	return hr; 
} 
 
HRESULT CDirectVobSubFilter::Transform(IMediaSample* pIn, IMediaSample* pOut) 
{ 
#ifdef DEBUG 
clock_t startt = clock(); 
#endif 
 
	CRefTime tStart, tEnd; 
	pIn->GetTime(&tStart.m_time, &tEnd.m_time); 
 
	m_tPrev = m_tOff + m_pInput->CurrentRate()*tStart; 
	m_fps = (10000000.0/(tEnd-tStart)) / m_pInput->CurrentRate(); 
 
 
	bool fSkipFrame = false; 
 
	{ 
		CMediaType* pmt = NULL; 
 
		if(S_OK == pOut->GetMediaType((AM_MEDIA_TYPE**)&pmt) && pmt) 
		{ 
			CMediaType mt(*pmt); 
			DeleteMediaType(pmt); 
			pmt = NULL; 
 
			if(mt != m_pOutput->CurrentMediaType()) 
			{ 
				m_pOutput->SetMediaType(&mt); 
				fSkipFrame = true; 
			} 
		} 
	} 
 
	if(!fSkipFrame) 
	{ 
		CMediaType* pmt = NULL; 
 
		if(S_OK == pIn->GetMediaType((AM_MEDIA_TYPE**)&pmt) && pmt) 
		{ 
			CMediaType mt(*pmt); 
			DeleteMediaType(pmt); 
			pmt = NULL; 
 
			if(mt != m_pInput->CurrentMediaType()) 
			{ 
				// this is only for sending aspect ratio or palette changes  
				// downstream 
				// 
				// other format changes would already work with the VMR, but 
				// not with the older renderers, so we must not change anything  
				// serious here which would affect the color space or the  
				// picture buffer size (we can be safe about these, by default 
				// the input pin is rejecting ReceiveConnection when running) 
 
				m_pInput->SetMediaType(&mt); 
				ConvertMediaTypeInputToOutput(&mt); 
                pOut->SetMediaType(&mt); 
			} 
		} 
	} 
 
	BYTE* pInPtr; 
	pIn->GetPointer(&pInPtr); 
	BYTE* pOutPtr; 
	pOut->GetPointer(&pOutPtr); 
 
	if(fSkipFrame || m_bihSub.biHeight != abs(m_bihOut.biHeight)) 
	{ 
		DbgLog((LOG_TRACE, 0, _T("Transform: %dx%d %dbpp 0x%08x"), m_bihOut.biWidth, m_bihOut.biHeight, m_bihOut.biBitCount, m_bihOut.biCompression)); 
		if(m_bihOut.biCompression == mmioFOURCC('Y', 'V', '1', '2')) 
		{ 
			uint size = m_bihOut.biWidth*abs(m_bihOut.biHeight); 
			memset(pOutPtr, 0x10, size); 
			memset(pOutPtr+size, 0x80, size>>1); 
		} 
		else 
		{ 
			uint black = m_bihOut.biCompression <= 3 ? 0 : 0x80108010; 
			memsetd(pOutPtr, black, m_bihOut.biSizeImage); 
		} 
	} 
	else 
	{ 
		SubImage* img = GetSubImage(); 
 
		if(m_bihOut.biCompression == mmioFOURCC('Y', 'V', '1', '2')) 
		{ 
			HRESULT hr = CopyYV12(pOutPtr, pInPtr, img); 
			if(FAILED(hr)) return hr; 
		} 
		else 
		{ 
			HRESULT hr = Copy(pOutPtr, pInPtr, img); 
			if(FAILED(hr)) return hr; 
		} 
 
		PrintMessages(pOutPtr); 
	} 
 
#ifdef DEBUG 
clock_t dtt = clock() - startt; 
if(dtt > 20) DbgLog((LOG_TRACE, 0, _T("Transform: %d"), dtt)); 
#endif 
 
	return NOERROR; 
} 
 
HRESULT CDirectVobSubFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) 
{ 
//	CallWorker(CMD_BREAK); 
	m_fBreakBuffering = true; 
	m_sic.RemoveAll(); 
 
	m_tOff = m_tPrev = tStart; 
 
	Post_EC_OLE_EVENT(); 
 
    return CTransformFilter::NewSegment(tStart, tStop, dRate); 
} 
 
HRESULT CDirectVobSubFilter::CheckInputType(const CMediaType* mtIn) 
{ 
	if(CheckOutputType(mtIn)) 
		return VFW_E_TYPE_NOT_ACCEPTED; 
 
	BITMAPINFOHEADER bih; 
	ExtractBIH(mtIn, &bih); 
 
	return bih.biHeight > 0 
		? NOERROR  
		: VFW_E_TYPE_NOT_ACCEPTED; 
} 
 
HRESULT CDirectVobSubFilter::CheckOutputType(const CMediaType* mtOut) 
{ 
	if(mtOut->majortype != MEDIATYPE_Video 
	|| !(mtOut->formattype == FORMAT_VideoInfo || mtOut->formattype == FORMAT_VideoInfo2)) 
		return E_FAIL; 
 
	return (mtOut->subtype == MEDIASUBTYPE_YUY2 
		 || mtOut->subtype == MEDIASUBTYPE_YV12 
		 || mtOut->subtype == MEDIASUBTYPE_RGB555 
		 || mtOut->subtype == MEDIASUBTYPE_RGB565 
		 || mtOut->subtype == MEDIASUBTYPE_RGB24 
		 || mtOut->subtype == MEDIASUBTYPE_RGB32 
		 || mtOut->subtype == MEDIASUBTYPE_ARGB32) 
		 ? NOERROR : E_FAIL; 
} 
 
HRESULT CDirectVobSubFilter::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) 
{ 
	BITMAPINFOHEADER bihIn; 
	ExtractBIH(mtIn, &bihIn); 
	int w = bihIn.biWidth, h = abs(bihIn.biHeight); 
	AdjustFrameSize(w, h); 
	BITMAPINFOHEADER bihOut; 
	ExtractBIH(mtOut, &bihOut); 
 
	if(m_pInput && m_pInput->IsConnected()) 
	{ 
		IPin* pIn = m_pInput->GetConnected(); 
		PIN_INFO pi; 
		if(pIn && SUCCEEDED(pIn->QueryPinInfo(&pi))) 
		{ 
			pi.pFilter->Release(); 
 
			CLSID clsid; 
			CComQIPtr(pi.pFilter)->GetClassID(&clsid); 
			if(clsid == CLSID_AVIDec) 
			{ 
				if(bihIn.biBitCount != bihOut.biBitCount) 
					return VFW_E_TYPE_NOT_ACCEPTED; 
 
				if(bihIn.biCompression != bihOut.biCompression 
				&& (bihIn.biCompression > 3 || bihOut.biCompression > 3)) 
					return VFW_E_TYPE_NOT_ACCEPTED; 
			} 
		} 
	} 
 
	return (CheckInputType(mtIn) == NOERROR && CheckOutputType(mtOut) == NOERROR) 
			? NOERROR : VFW_E_TYPE_NOT_ACCEPTED; 
} 
 
HRESULT CDirectVobSubFilter::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties) 
{ 
    if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED; 
 
    ASSERT(pAlloc); 
    ASSERT(pProperties); 
    HRESULT hr = NOERROR; 
 
    pProperties->cBuffers = 1; 
	pProperties->cbBuffer = m_bihOut.biSizeImage; 
 
    ASSERT(pProperties->cbBuffer); 
 
    ALLOCATOR_PROPERTIES Actual; 
    hr = pAlloc->SetProperties(pProperties, &Actual); 
    if(FAILED(hr)) return hr; 
 
    ASSERT(Actual.cBuffers == 1); 
 
	// TODO: check if the renderer has the allocator, otherwise it won't be able to dynamically change the format 
 
    return(pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer 
		? E_FAIL 
		: NOERROR); 
} 
 
#include "OutMediaTypes.h" 
 
HRESULT CDirectVobSubFilter::GetMediaType(int iPosition, CMediaType* pMediaType) 
{ 
    if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED; 
 
	bool fVIH2 = !!(iPosition&1); 
	iPosition >>= 1; 
 
	if(m_pInput->CurrentMediaType().formattype == FORMAT_VideoInfo2) fVIH2 = !fVIH2; 
 
    if(iPosition < 0) return E_INVALIDARG; 
    if(iPosition >= VIHSIZE) return VFW_S_NO_MORE_ITEMS; 
 
	AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
	BYTE* pData = NULL; 
	UINT nSize; 
	if(theApp.GetProfileBinary(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_COLORFORMATS), &pData, &nSize) && pData) 
	{ 
		iPosition = pData[iPosition]; 
		delete [] pData; 
	} 
 
	CComPtr pEnum; 
	if(S_OK != m_pInput->GetConnected()->EnumMediaTypes(&pEnum)) return E_FAIL; 
 
	bool fFound = false; 
 
	for(CMediaType* pmt = NULL; S_OK == pEnum->Next(1, (AM_MEDIA_TYPE **)&pmt, 0); DeleteMediaType(pmt), pmt = NULL) 
	{ 
		const CMediaType mt(*pmt); 
		if(CheckInputType(&mt) == NOERROR) 
		{ 
			fFound = true; 
			break; 
		} 
	} 
 
	if(pmt) 
	{ 
		*pMediaType = *pmt; 
		DeleteMediaType(pmt); 
	} 
 
	if(!fFound || !pmt) return VFW_S_NO_MORE_ITEMS; 
 
	return ConvertMediaTypeInputToOutput(pMediaType, iPosition, fVIH2); 
/* 
	int w, h, wo, ho; 
    DWORD dwBitRate, dwBitErrorRate; 
	REFERENCE_TIME AvgTimePerFrame; 
	DWORD dwInterlaceFlags = 0, dwCopyProtectFlags = 0; 
	DWORD dwPictAspectRatioX = 0, dwPictAspectRatioY = 0; 
	DWORD dwControlFlags = 0; 
 
	if(IsEqualGUID(*pMediaType->FormatType(), FORMAT_VideoInfo)) 
	{ 
		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pMediaType->Format(); 
		w = wo = dwPictAspectRatioX = vih->bmiHeader.biWidth; 
		h = ho = dwPictAspectRatioY = vih->bmiHeader.biHeight; 
		dwBitRate = vih->dwBitRate; 
		dwBitErrorRate = vih->dwBitErrorRate; 
		AvgTimePerFrame = vih->AvgTimePerFrame; 
	} 
	else if(IsEqualGUID(*pMediaType->FormatType(), FORMAT_VideoInfo2)) 
	{ 
		VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pMediaType->Format(); 
		w = wo = vih->bmiHeader.biWidth; 
		h = ho = vih->bmiHeader.biHeight; 
		dwBitRate = vih->dwBitRate; 
		dwBitErrorRate = vih->dwBitErrorRate; 
		AvgTimePerFrame = vih->AvgTimePerFrame; 
		dwInterlaceFlags = vih->dwInterlaceFlags; 
		dwCopyProtectFlags = vih->dwCopyProtectFlags & (~AMCOPYPROTECT_RestrictDuplication); // hehe 
		dwPictAspectRatioX = vih->dwPictAspectRatioX; 
		dwPictAspectRatioY = vih->dwPictAspectRatioY; 
		dwControlFlags = vih->dwControlFlags; 
	} 
	else // dammit 
	{ 
		return VFW_S_NO_MORE_ITEMS; 
	} 
 
	ASSERT(h >= 0); 
 
	AdjustFrameSize(w, h); 
 
	if(!fVIH2) 
	{ 
		pMediaType->SetFormatType(&FORMAT_VideoInfo); 
		pMediaType->SetSubtype(vihs[iPosition].subtype); 
 
		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pMediaType->ReallocFormatBuffer(vihs[iPosition].size); 
		if(!vih) return E_FAIL; 
 
		memcpy(vih, &vihs[iPosition], pMediaType->FormatLength()); 
 
		vih->bmiHeader.biWidth = w; 
		vih->bmiHeader.biHeight = h; 
		vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; 
		vih->AvgTimePerFrame = AvgTimePerFrame; 
		vih->dwBitErrorRate = dwBitErrorRate; 
		vih->dwBitRate = dwBitRate; 
 
		pMediaType->SetSampleSize(vih->bmiHeader.biSizeImage); 
	} 
	else 
	{ 
		pMediaType->SetFormatType(&FORMAT_VideoInfo2); 
		pMediaType->SetSubtype(vih2s[iPosition].subtype); 
 
		VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pMediaType->ReallocFormatBuffer(vih2s[iPosition].size); 
		if(!vih) return E_FAIL; 
 
		memcpy(vih, &vih2s[iPosition], pMediaType->FormatLength()); 
 
		vih->bmiHeader.biWidth = w; 
		vih->bmiHeader.biHeight = h; 
		vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; 
		vih->AvgTimePerFrame = AvgTimePerFrame; 
		vih->dwBitErrorRate = dwBitErrorRate; 
		vih->dwBitRate = dwBitRate; 
		vih->dwInterlaceFlags = dwInterlaceFlags; 
		vih->dwCopyProtectFlags = dwCopyProtectFlags; 
		vih->dwPictAspectRatioX = dwPictAspectRatioX*w/wo; 
		vih->dwPictAspectRatioY = dwPictAspectRatioY*h/ho; 
		vih->dwControlFlags = dwControlFlags; 
 
		pMediaType->SetSampleSize(vih->bmiHeader.biSizeImage); 
	} 
 
	return NOERROR; 
*/ 
} 
 
bool CDirectVobSubFilter::AdjustFrameSize(int& w, int& h) 
{ 
	int horizontal, vertical, resx2, resx2minw, resx2minh; 
	get_ExtendPicture(&horizontal, &vertical, &resx2, &resx2minw, &resx2minh); 
 
	bool fRet; 
	if(fRet = (resx2 == 1) || (resx2 == 2 && w*h <= resx2minw*resx2minh)) 
	{ 
		w <<= 1;  
		h <<= 1; 
	} 
 
	int h2; 
	switch(vertical&0x7f) 
	{ 
	case 1: 
		h2 = w * 9 / 16; 
		if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; 
		break; 
	case 2: 
		h2 = w * 3 / 4; 
		if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; 
		break; 
	case 3: 
		h2 = 480; 
		if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; 
		break; 
	case 4: 
		h2 = 576; 
		if(h < h2 || !!(vertical&0x80)) h = (h2 + 3) & ~3; 
		break; 
	} 
 
	if(horizontal == 1) 
	{ 
		w = (w + 31) & ~31; 
//		h = (h + 3) & ~3; 
	} 
 
	return(fRet); 
} 
 
HRESULT CDirectVobSubFilter::ConvertMediaTypeInputToOutput(CMediaType* pmt, int iVIHTemplate, bool fVIH2) 
{ 
    if(iVIHTemplate >= VIHSIZE)  
		return VFW_S_NO_MORE_ITEMS; 
 
	if(iVIHTemplate < 0) 
	{ 
		if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo)) 
		{ 
			VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->Format(); 
 
			for(int i = 0; i < VIHSIZE; i++) 
			{ 
				if(*vihs[i].subtype == pmt->subtype 
				&& vihs[i].vih.bmiHeader.biCompression == vih->bmiHeader.biCompression 
				&& vihs[i].vih.bmiHeader.biBitCount == vih->bmiHeader.biBitCount) 
				{ 
					iVIHTemplate = i; 
					fVIH2 = false; 
					break; 
				} 
			} 
		} 
		else if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo2)) 
		{ 
			VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->Format(); 
 
			for(int i = 0; i < VIHSIZE; i++) 
			{ 
				if(*vih2s[i].subtype == pmt->subtype 
				&& vih2s[i].vih.bmiHeader.biCompression == vih->bmiHeader.biCompression 
				&& vih2s[i].vih.bmiHeader.biBitCount == vih->bmiHeader.biBitCount) 
				{ 
					iVIHTemplate = i; 
					fVIH2 = true; 
					break; 
				} 
			} 
		} 
 
		if(iVIHTemplate < 0) 
		{ 
			return VFW_S_NO_MORE_ITEMS; 
		} 
	} 
 
	int w, h, wo, ho; 
    DWORD dwBitRate, dwBitErrorRate; 
	REFERENCE_TIME AvgTimePerFrame; 
	DWORD dwInterlaceFlags = 0, dwCopyProtectFlags = 0; 
	DWORD dwPictAspectRatioX = 0, dwPictAspectRatioY = 0; 
	DWORD dwControlFlags = 0; 
 
	if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo)) 
	{ 
		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->Format(); 
		w = wo = dwPictAspectRatioX = vih->bmiHeader.biWidth; 
		h = ho = dwPictAspectRatioY = vih->bmiHeader.biHeight; 
		dwBitRate = vih->dwBitRate; 
		dwBitErrorRate = vih->dwBitErrorRate; 
		AvgTimePerFrame = vih->AvgTimePerFrame; 
	} 
	else if(IsEqualGUID(*pmt->FormatType(), FORMAT_VideoInfo2)) 
	{ 
		VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->Format(); 
		w = wo = vih->bmiHeader.biWidth; 
		h = ho = vih->bmiHeader.biHeight; 
		dwBitRate = vih->dwBitRate; 
		dwBitErrorRate = vih->dwBitErrorRate; 
		AvgTimePerFrame = vih->AvgTimePerFrame; 
		dwInterlaceFlags = vih->dwInterlaceFlags; 
		dwCopyProtectFlags = vih->dwCopyProtectFlags & (~AMCOPYPROTECT_RestrictDuplication); // hehe 
		dwPictAspectRatioX = vih->dwPictAspectRatioX; 
		dwPictAspectRatioY = vih->dwPictAspectRatioY; 
		dwControlFlags = vih->dwControlFlags; 
	} 
	else 
	{ 
		return VFW_S_NO_MORE_ITEMS; 
	} 
 
	ASSERT(h >= 0); 
 
	AdjustFrameSize(w, h); 
 
	if(!fVIH2) 
	{ 
		pmt->SetFormatType(&FORMAT_VideoInfo); 
		pmt->SetSubtype(vihs[iVIHTemplate].subtype); 
 
		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->ReallocFormatBuffer(vihs[iVIHTemplate].size); 
		if(!vih) return E_FAIL; 
 
		memcpy(vih, &vihs[iVIHTemplate], pmt->FormatLength()); 
 
		vih->bmiHeader.biWidth = w; 
		vih->bmiHeader.biHeight = h; 
		vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; 
		vih->AvgTimePerFrame = AvgTimePerFrame; 
		vih->dwBitErrorRate = dwBitErrorRate; 
		vih->dwBitRate = dwBitRate; 
 
		pmt->SetSampleSize(vih->bmiHeader.biSizeImage); 
	} 
	else 
	{ 
		pmt->SetFormatType(&FORMAT_VideoInfo2); 
		pmt->SetSubtype(vih2s[iVIHTemplate].subtype); 
 
		VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->ReallocFormatBuffer(vih2s[iVIHTemplate].size); 
		if(!vih) return E_FAIL; 
 
		memcpy(vih, &vih2s[iVIHTemplate], pmt->FormatLength()); 
 
		vih->bmiHeader.biWidth = w; 
		vih->bmiHeader.biHeight = h; 
		vih->bmiHeader.biSizeImage = w*h*vih->bmiHeader.biBitCount>>3; 
		vih->AvgTimePerFrame = AvgTimePerFrame; 
		vih->dwBitErrorRate = dwBitErrorRate; 
		vih->dwBitRate = dwBitRate; 
		vih->dwInterlaceFlags = dwInterlaceFlags; 
		vih->dwCopyProtectFlags = dwCopyProtectFlags; 
		vih->dwPictAspectRatioX = dwPictAspectRatioX*w/wo; 
		vih->dwPictAspectRatioY = dwPictAspectRatioY*h/ho; 
		vih->dwControlFlags = dwControlFlags; 
 
		pmt->SetSampleSize(vih->bmiHeader.biSizeImage); 
	} 
 
	return NOERROR; 
} 
 
HRESULT CDirectVobSubFilter::SetMediaType(PIN_DIRECTION dir, const CMediaType* pmt) 
{ 
	if(dir == PINDIR_INPUT) 
	{ 
		CAutoLock cAutoLock(&m_csReceive); 
 
		if(!m_pOutput->IsConnected()) 
			m_Allocator.NotifyMediaType(&m_pInput->CurrentMediaType()); 
 
		ExtractBIH(pmt, &m_bihIn); 
 
		REFERENCE_TIME atpf =  
			(pmt->formattype == FORMAT_VideoInfo) ? ((VIDEOINFOHEADER*)pmt->Format())->AvgTimePerFrame 
			: (pmt->formattype == FORMAT_VideoInfo2) ? ((VIDEOINFOHEADER2*)pmt->Format())->AvgTimePerFrame 
			: 0; 
 
		m_fps = atpf ? 10000000.0 / atpf : 25; 
 
		CallWorker(CMD_RESET); 
	} 
	else if(dir == PINDIR_OUTPUT) 
	{ 
		if(m_fUsingOwnAllocator && m_pInput->IsConnected()) 
		{ 
			CComPtr pIn = m_pInput->GetConnected(); 
 
			CComPtr pEnum; 
			if(SUCCEEDED(pIn->EnumMediaTypes(&pEnum))) 
			{ 
				bool fOK = false; 
 
				for(AM_MEDIA_TYPE* pmt2; !fOK && S_OK == pEnum->Next(1, &pmt2, NULL); DeleteMediaType(pmt2)) 
				{ 
					if(pmt->majortype == pmt2->majortype && pmt->subtype == pmt2->subtype && SUCCEEDED(pIn->QueryAccept(pmt2)))  
					{ 
						BITMAPINFOHEADER bih, bih2; 
						ExtractBIH(pmt, &bih); 
						ExtractBIH(pmt2, &bih2); 
						int w = bih2.biWidth, h = abs(bih2.biHeight); 
						AdjustFrameSize(w, h); 
						if(h != abs(bih.biHeight)) 
							continue; 
 
						const CMediaType mt(*pmt2); 
						fOK = SUCCEEDED(m_pInput->SetMediaType(&mt)); 
 
						if(m_pOutput->IsConnected()) 
							m_Allocator.NotifyMediaType(&m_pInput->CurrentMediaType()); 
 
/* 
						// This would work fine, but confuses graphedit in the drawing of its arrows/boxes. Also, we can do a dynamic format change later. 
						if(IsStopped()) 
						{ 
							CComQIPtr pGraph2 = m_pGraph; 
							if(pGraph2) pGraph2->ReconnectEx(pIn, pmt); 
						} 
*/ 
					} 
				} 
			} 
		} 
 
		ExtractBIH(pmt, &m_bihOut); 
	} 
 
	return CTransformFilter::SetMediaType(dir, pmt); 
} 
 
STDMETHODIMP CDirectVobSubFilter::Count(DWORD* pcStreams) 
{ 
//	CallWorker(CMD_BREAK); 
 
//	CAutoLock cAutolock(&m_fileLock); 
 
	*pcStreams = 0; 
 
	if(m_file) 
	(*pcStreams) += m_file->GetLangCountL(); 
 
	(*pcStreams) += m_textlangs.GetSize(); 
 
	if(*pcStreams) (*pcStreams) += 4; // enable ... disable | original flipped 
 
	return S_OK; 
} 
 
#define MAXPREFLANGS 5 
 
int CDirectVobSubFilter::FindPreferedLanguage(bool fHideToo) 
{ 
	AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
	int len; 
	Count((DWORD*)&len); 
	len -= 4; 
 
	if(len <= 0) return(0); 
 
	for(int i = 0, j = 0; i < MAXPREFLANGS; i++) 
	{ 
		CString tmp; 
		tmp.Format(IDS_RL_LANG, i); 
 
		CString lang = theApp.GetProfileString(ResStr(IDS_R_PREFLANGS), tmp); 
		 
		if(!lang.IsEmpty()) 
		{ 
			if(fHideToo && lang == CString(ResStr(IDS_M_HIDESUBTITLES))) return(len+1); 
 
			for(int ret = 1; ret <= len; ret++) 
			{ 
				CString l; 
				WCHAR* pNameW = NULL; 
				 
				Info(ret, 0, 0, 0, 0, &pNameW, 0, 0); 
				l = pNameW; 
 
				CoTaskMemFree(pNameW); 
				 
				if(!l.CompareNoCase(lang)) return(ret); 
			} 
		} 
	} 
 
	return(1); 
} 
 
void CDirectVobSubFilter::UpdatePreferedLanguages(CString l) 
{ 
	AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
	CString langs[MAXPREFLANGS+1]; 
 
	for(int i = 0, j = 0, k = -1; i < MAXPREFLANGS; i++) 
	{ 
		CString tmp; 
		tmp.Format(IDS_RL_LANG, i); 
 
		langs[j] = theApp.GetProfileString(ResStr(IDS_R_PREFLANGS), tmp); 
 
		if(!langs[j].IsEmpty())  
		{ 
			if(!langs[j].CompareNoCase(l)) k = j; 
			j++; 
		} 
	} 
 
	if(k == -1) 
	{ 
		langs[k = j] = l; 
		j++; 
	} 
 
	// move the selected to the top of the list 
 
	while(k > 0) 
	{ 
		CString tmp = langs[k]; langs[k] = langs[k-1]; langs[k-1] = tmp; 
		k--; 
	} 
 
	// move "Hide subtitles" to the last position if it wasn't our selection 
 
	CString hidesubs; 
	hidesubs.LoadString(IDS_M_HIDESUBTITLES); 
 
	for(k = 1; k < j; k++) 
	{ 
		if(!langs[k].CompareNoCase(hidesubs)) break; 
	} 
 
	while(k < j-1) 
	{ 
		CString tmp = langs[k]; langs[k] = langs[k+1]; langs[k+1] = tmp; 
		k++; 
	} 
 
	for(i = 0; i < j; i++) 
	{ 
		CString tmp; 
		tmp.Format(IDS_RL_LANG, i); 
 
		theApp.WriteProfileString(ResStr(IDS_R_PREFLANGS), tmp, langs[i]); 
	} 
} 
 
STDMETHODIMP CDirectVobSubFilter::Enable(long lIndex, DWORD dwFlags) 
{ 
	if(!(dwFlags & AMSTREAMSELECTENABLE_ENABLE)) 
		return E_NOTIMPL; 
	 
	if(lIndex < 0)  
	{ 
		return E_INVALIDARG; 
	} 
	else if(lIndex == 0) 
	{ 
		if(!m_fLoading)	// we need this because when loading something stupid media player pushes the first stream it founds, which is "enable" in our case 
		{ 
			m_fHideSubtitles = false; 
 
			int i = FindPreferedLanguage(false); 
			if(i > 0) Enable(i, AMSTREAMSELECTENABLE_ENABLE); 
		} 
 
		return S_OK; 
	} 
 
	do 
	{ 
		CallWorker(CMD_RESET); // this can take for a while if we are in the middle of rendering a subtitle... 
 
		CAutoLock cAutolock(&m_fileLock); 
 
		int orgidx = lIndex; 
 
		lIndex--; 
 
		if(m_file && lIndex < m_file->GetLangCountL()) 
		{ 
			m_file->SetLangIdxL(lIndex); 
			m_iSelectedLanguage = orgidx-1; 
			m_fHideSubtitles = false; 
			m_mode = VOBSUB; 
 
			break; 
		} 
		 
		if(m_file) 
			lIndex -= m_file->GetLangCountL(); 
		 
		if(lIndex < m_textlangs.GetSize()) 
		{ 
			m_textidx = lIndex; 
			m_iSelectedLanguage = orgidx-1; 
			m_fHideSubtitles = false; 
			m_mode = TEXT; 
 
			break; 
		} 
		 
		lIndex -= m_textlangs.GetSize(); 
		 
		if(lIndex == 0) 
		{ 
			m_fHideSubtitles = true; 
		} 
		else if(lIndex == 1) 
		{ 
			if(!m_fLoading)	m_fFlipPicture = false; 
		} 
		else if(lIndex == 2) 
		{ 
			m_fFlipPicture = true; 
		} 
	} 
	while(false); 
 
	if(!m_fHideSubtitles) 
	{ 
		int len; 
		Count((DWORD*)&len); 
 
		if(m_iSelectedLanguage >= 0 && m_iSelectedLanguage < len - 4) 
		{ 
			WCHAR* pNameW = NULL; 
 
			Info(m_iSelectedLanguage+1, 0, 0, 0, 0, &pNameW, 0, 0); 
			CString l = pNameW; 
 
			CoTaskMemFree(pNameW); 
 
			UpdatePreferedLanguages(l); 
		} 
	} 
 
	return S_OK; 
} 
 
STDMETHODIMP CDirectVobSubFilter::Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid, DWORD* pdwGroup, WCHAR** ppszName, IUnknown** ppObject, IUnknown** ppUnk) 
{ 
//	CallWorker(CMD_BREAK); 
 
//	CAutoLock cAutolock(&m_fileLock); 
 
	AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
	if(lIndex < 0)  
		return S_FALSE; 
 
	if(ppmt) *ppmt = CreateMediaType(&m_pInput->CurrentMediaType()); 
 
	if(pdwFlags) *pdwFlags = 0; 
 
	if(plcid) *plcid = 0; 
 
	if(pdwGroup) *pdwGroup = 0x648E51; 
 
	{ 
		CString str; 
 
		if(lIndex == 0) 
		{ 
			str.LoadString(IDS_M_SHOWSUBTITLES); 
			if(!m_fHideSubtitles && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_ENABLED; 
		} 
		else 
		{ 
			bool fFound = false; 
 
			int orgidx = lIndex; 
 
			lIndex--; 
 
			if(!fFound && m_file) 
			{ 
				if(lIndex < m_file->GetLangCountL()) 
				{ 
					m_file->GetLangInfoL(lIndex, str); 
					m_file->GetLangAltL(lIndex, str); 
					fFound = true; 
				} 
				else 
					lIndex -= m_file->GetLangCountL(); 
			} 
			 
			if(!fFound) 
			{ 
				if(lIndex < m_textlangs.GetSize()) 
				{ 
					str = m_textlangs[lIndex].m_name; 
					fFound = true; 
				} 
				else 
					lIndex -= m_textlangs.GetSize(); 
			} 
			 
			if(fFound) 
			{ 
				if(orgidx == m_iSelectedLanguage+1 && !m_fHideSubtitles && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_ENABLED; 
			} 
			else if(lIndex == 0) 
			{ 
				str.LoadString(IDS_M_HIDESUBTITLES); 
				if(m_fHideSubtitles && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_ENABLED; 
			} 
			else if(lIndex == 1) 
			{ 
				str.LoadString(IDS_M_ORIGINALPICTURE); 
				if(!m_fFlipPicture && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_EXCLUSIVE; 
				if(pdwGroup) (*pdwGroup)++; 
			} 
			else if(lIndex == 2) 
			{ 
				str.LoadString(IDS_M_FLIPPEDPICTURE); 
				if(m_fFlipPicture && pdwFlags) *pdwFlags = AMSTREAMSELECTINFO_EXCLUSIVE; 
				if(pdwGroup) (*pdwGroup)++; 
			} 
		} 
 
		if(ppszName)  
		{ 
			*ppszName = (WCHAR*)CoTaskMemAlloc((str.GetLength()+1)*sizeof(WCHAR)); 
			if(*ppszName == NULL) return S_FALSE; 
#ifdef UNICODE 
			wcscpy(*ppszName, str); 
#else 
			mbstowcs(*ppszName, str, MAX_PATH); 
#endif 
		} 
	} 
 
	if(ppObject) *ppObject = NULL; 
 
	if(ppUnk) *ppUnk = NULL; 
 
	return S_OK; 
} 
 
STDMETHODIMP CDirectVobSubFilter::GetClassID(CLSID* pClsid) 
{ 
    if(pClsid == NULL) return E_POINTER; 
 
	*pClsid = CLSID_DirectVobSubFilter; 
 
    return NOERROR; 
} 
 
STDMETHODIMP CDirectVobSubFilter::GetPages(CAUUID* pPages) 
{ 
	pPages->cElems = 7; 
	if(m_fVMRFilterActive) pPages->cElems++; 
    pPages->pElems = (GUID *)CoTaskMemAlloc(sizeof(GUID)*pPages->cElems); 
 
	if(pPages->pElems == NULL) return E_OUTOFMEMORY; 
 
	int i = 0; 
    pPages->pElems[i] = CLSID_DVSMainPPage; i++; 
    pPages->pElems[i] = CLSID_DVSGeneralPPage; i++; 
    pPages->pElems[i] = CLSID_DVSMiscPPage; i++; 
    pPages->pElems[i] = CLSID_DVSTimingPPage; i++; 
	if(m_fVMRFilterActive) {pPages->pElems[i] = CLSID_DVSZoomPPage; i++;} 
    pPages->pElems[i] = CLSID_DVSColorPPage; i++; 
    pPages->pElems[i] = CLSID_DVSPathsPPage; i++; 
    pPages->pElems[i] = CLSID_DVSAboutPPage; i++; 
 
    return NOERROR; 
} 
 
// IDirectVobSub 
 
STDMETHODIMP CDirectVobSubFilter::put_FileName(WCHAR* fn) 
{ 
	HRESULT hr = CDirectVobSub::put_FileName(fn); 
 
	if(hr == S_OK && !Open())  
	{ 
		m_FileName.Empty(); 
		hr = E_FAIL; 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::get_LanguageCount(int* nLangs) 
{ 
	HRESULT hr = CDirectVobSub::get_LanguageCount(nLangs); 
 
	if(hr == NOERROR && nLangs) 
	{ 
		*nLangs = (m_file?m_file->GetLangCountL():0) + m_textlangs.GetSize(); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::get_LanguageName(int iLanguage, WCHAR** ppName) 
{ 
	HRESULT hr = CDirectVobSub::get_LanguageName(iLanguage, ppName); 
 
	if(!ppName) return E_POINTER; 
 
	if(hr == NOERROR) 
	{ 
		if(iLanguage >= 0 && m_file) 
		{ 
			if(iLanguage < m_file->GetLangCountL()) 
			{ 
				CString str; 
				m_file->GetLangInfoL(iLanguage, str); 
				m_file->GetLangAltL(iLanguage, str); 
 
				*ppName = (WCHAR*)CoTaskMemAlloc(sizeof(WCHAR)*(str.GetLength()+1)); 
				if(!(*ppName)) E_OUTOFMEMORY; 
#ifdef UNICODE 
				wcscpy(*ppName, str); 
#else 
				mbstowcs(*ppName, str, str.GetLength()+1); 
#endif 
			} 
 
			iLanguage -= m_file->GetLangCountL(); 
		} 
 
		if(iLanguage >= 0) 
		{ 
			if(iLanguage < m_textlangs.GetSize()) 
			{ 
				*ppName = (WCHAR*)CoTaskMemAlloc(sizeof(WCHAR)*(m_textlangs[iLanguage].m_name.GetLength()+1)); 
				if(!(*ppName)) E_OUTOFMEMORY; 
#ifdef UNICODE 
				wcscpy(*ppName, m_textlangs[iLanguage].m_name); 
#else 
				mbstowcs(*ppName, m_textlangs[iLanguage].m_name, m_textlangs[iLanguage].m_name.GetLength()+1); 
#endif 
			} 
 
			iLanguage -= m_textlangs.GetSize(); 
		} 
 
		if(iLanguage >= 0) 
		{ 
			hr = E_FAIL; 
		} 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_SelectedLanguage(int iSelected) 
{ 
	HRESULT hr = CDirectVobSub::put_SelectedLanguage(iSelected); 
 
	if(hr == NOERROR) 
	{ 
		bool fTmp = m_fHideSubtitles; 
		Enable(m_iSelectedLanguage+1, AMSTREAMSELECTENABLE_ENABLE); 
		m_fHideSubtitles = fTmp; 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_PreBuffering(bool fDoPreBuffering) 
{ 
	HRESULT hr = CDirectVobSub::put_PreBuffering(fDoPreBuffering); 
 
	if(hr == NOERROR) 
	{ 
		CallWorker(CMD_RESET); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_Placement(bool fOverridePlacement, int xperc, int yperc) 
{ 
	HRESULT hr = CDirectVobSub::put_Placement(fOverridePlacement, xperc, yperc); 
 
	if(hr == NOERROR) 
	{ 
		InitRTSStreams(); 
		CallWorker(CMD_RESET); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_VobSubSettings(bool fBuffer, bool fOnlyShowForcedSubs, bool fReserved) 
{ 
	HRESULT hr = CDirectVobSub::put_VobSubSettings(fBuffer, fOnlyShowForcedSubs, fReserved); 
 
	if(hr == NOERROR && m_file) 
	{ 
		m_file->SetOnlyShowForcedSubs(m_fOnlyShowForcedVobSubs); 
		CallWorker(CMD_RESET); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_TextSettings(void* lf, int lflen, COLORREF color, bool fShadow, bool fOutline, bool fAdvancedRenderer) 
{ 
	HRESULT hr = CDirectVobSub::put_TextSettings(lf, lflen, color, fShadow, fOutline, fAdvancedRenderer); 
	 
	if(hr == NOERROR) 
	{ 
		InitRTSStreams(); 
		CallWorker(CMD_SETFONT); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_SubtitleTiming(int delay, int speedmul, int speeddiv) 
{ 
	HRESULT hr = CDirectVobSub::put_SubtitleTiming(delay, speedmul, speeddiv); 
 
	if(hr == NOERROR) 
	{ 
		CallWorker(CMD_RESET); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::get_MediaFPS(bool* fEnabled, double* fps) 
{ 
	HRESULT hr = CDirectVobSub::get_MediaFPS(fEnabled, fps); 
 
	CComQIPtr pMS = m_pGraph; 
	double rate; 
	if(pMS && SUCCEEDED(pMS->GetRate(&rate))) 
	{ 
		m_MediaFPS = rate * m_fps; 
		if(fps) *fps = m_MediaFPS; 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_MediaFPS(bool fEnabled, double fps) 
{ 
	HRESULT hr = CDirectVobSub::put_MediaFPS(fEnabled, fps); 
 
	CComQIPtr pMS = m_pGraph; 
	if(pMS) 
	{ 
		if(hr == NOERROR) 
		{ 
			hr = pMS->SetRate(m_fMediaFPSEnabled ? m_MediaFPS / m_fps : 1.0); 
TRACE(_T("SetRate\n")); 
		} 
 
		double dRate; 
		if(SUCCEEDED(pMS->GetRate(&dRate))) 
			m_MediaFPS = dRate * m_fps; 
CString str; 
str.Format(_T("%f, %f\n"), dRate, m_MediaFPS); 
TRACE(str); 
	} 
 
	return hr; 
} 
 
STDMETHODIMP CDirectVobSubFilter::get_ZoomRect(NORMALIZEDRECT* rect) 
{ 
	if(!m_fVMRFilterActive || !m_pOutput || !m_pOutput->IsConnected() || !rect) return(E_FAIL); 
 
	HRESULT hr; 
 
	CComQIPtr pVMRMC = FindVMRFilterFromPin(m_pOutput->GetConnected()); 
	if(pVMRMC && FAILED(hr = pVMRMC->GetOutputRect(0, &m_ZoomRect))) 
		return hr; 
 
	return CDirectVobSub::get_ZoomRect(rect); 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_ZoomRect(NORMALIZEDRECT* rect) 
{ 
	if(!m_fVMRFilterActive || !m_pOutput || !m_pOutput->IsConnected()) return(E_FAIL); 
 
	HRESULT hr; 
 
	CComQIPtr pVMRMC = FindVMRFilterFromPin(m_pOutput->GetConnected()); 
	if(pVMRMC && FAILED(hr = pVMRMC->SetOutputRect(0, rect))) 
		return hr; 
 
	return CDirectVobSub::put_ZoomRect(rect); 
} 
 
// IDirectVobSubFilterColor 
 
STDMETHODIMP CDirectVobSubFilter::get_ColorFormat(int* iPosition) 
{ 
	if(!m_pOutput || !m_pOutput->IsConnected() || !iPosition) return E_FAIL; 
	 
	BITMAPINFOHEADER bih, bih2; 
	ExtractBIH(&m_pOutput->CurrentMediaType(), &bih); 
	const GUID& subtype = m_pOutput->CurrentMediaType().subtype; 
 
	*iPosition = 0; 
	CMediaType mt; 
	while(SUCCEEDED(GetMediaType((*iPosition)<<1, &mt))) 
	{ 
		ExtractBIH(&mt, &bih2); 
		if(mt.subtype == subtype  
		&& bih2.biBitCount == bih.biBitCount  
		&& bih2.biCompression == bih.biCompression) 
		{ 
			return S_OK; 
		} 
		 
		(*iPosition)++; 
	} 
	 
	return E_FAIL; 
} 
 
HRESULT CDirectVobSubFilter::ChangeMediaType(int iPosition) 
{ 
	if(!m_pOutput || !m_pOutput->IsConnected() || m_State == State_Paused) return E_FAIL; 
 
	CAutoLock cAutoLock(&m_csReceive); 
 
	CComPtr pPin = m_pOutput->GetConnected(); 
	if(!pPin) return E_FAIL; 
 
	CComQIPtr pPinConnection = pPin; 
	CComQIPtr pMemImputPin = pPin; 
	if(!pPinConnection || !pMemImputPin) return E_FAIL; 
 
	CComPtr pAllocator; 
	if(FAILED(pMemImputPin->GetAllocator(&pAllocator))) return E_FAIL; 
 
	CMediaType orgmt; 
	orgmt = m_pOutput->CurrentMediaType(); 
 
	HRESULT hr; 
 
	CMediaType mt; 
	GetMediaType(iPosition<<1, &mt); 
 
	if(SUCCEEDED(hr = pPinConnection->DynamicQueryAccept(&mt))) 
	{ 
		if(SUCCEEDED(hr = pPin->ReceiveConnection(m_pOutput, &mt))) 
		{ 
			// this shouldn't be needed, but the old renderer won't attach  
			// the new media type to the next sample, despite the fact that  
			// it has just accepted it... 
			m_pOutput->SetMediaType(&mt);  
 
			return(NOERROR); 
		} 
	} 
 
	return(E_FAIL); 
} 
 
STDMETHODIMP CDirectVobSubFilter::put_ColorFormat(int iPosition) 
{ 
	if(!m_pOutput || !m_pOutput->IsConnected() /*|| !m_fVMRFilter*/) return E_FAIL; 
 
	return ChangeMediaType(iPosition); 
} 
 
STDMETHODIMP CDirectVobSubFilter::HasConfigDialog(int iSelected) 
{ 
	int nLangs; 
	if(FAILED(get_LanguageCount(&nLangs))) return E_FAIL; 
	return(nLangs >= 0 && iSelected < nLangs ? S_OK : E_FAIL); 
} 
 
STDMETHODIMP CDirectVobSubFilter::ShowConfigDialog(int iSelected, HWND hWndParent) 
{ 
	if(iSelected < 0 && FAILED(get_SelectedLanguage(&iSelected))) return E_FAIL; 
    
	if(m_file)  
	{ 
		if(iSelected < m_file->GetLangCountL())  
		{ 
			bool fRet = m_file->ConfigDlg(false, hWndParent); 
 
			if(fRet) 
			{ 
				m_file->SetLangIdxL(iSelected); 
				CallWorker(CMD_RESET); 
			} 
 
			return(fRet ? S_OK : S_FALSE); 
		} 
 
		iSelected -= m_file->GetLangCountL(); 
	} 
 
	if(iSelected < m_textlangs.GetSize())  
		return(m_textlangs[iSelected].ConfigDlg(0, hWndParent) ? CallWorker(CMD_RESET), S_OK : S_FALSE); 
 
	iSelected -= m_textlangs.GetSize(); 
 
	return(E_FAIL); 
} 
 
//////////////////////////////////////////////////////////////////////// 
 
STDMETHODIMP CDirectVobSubFilter::QueryFilterInfo(FILTER_INFO* pInfo) 
{ 
    CheckPointer(pInfo, E_POINTER); 
    ValidateReadWritePtr(pInfo, sizeof(FILTER_INFO)); 
 
	HRESULT hr = E_FAIL; 
 
	if(get_Forced()) 
	{ 
        wcscpy(pInfo->achName, L"DirectVobSub (forced auto-loading version)"); 
		if(pInfo->pGraph = m_pGraph) m_pGraph->AddRef(); 
		hr = NOERROR; 
	} 
	else 
	{ 
		hr = CTransformFilter::QueryFilterInfo(pInfo); 
	} 
 
	return(hr); 
} 
 
STDMETHODIMP CDirectVobSubFilter::SetSyncSource(IReferenceClock* pClock) 
{ 
	if(!m_fAudioReconnected && m_pGraph) 
	{ 
		CComPtr pBF; 
		if(pBF = FindAudioRenderer(m_pGraph))  
		{ 
			CComPtr pPin; 
			if(pPin = FindFirstPin(pBF)) 
			{ 
				HRESULT hr = m_pGraph->Reconnect(pPin); 
				m_fAudioReconnected = SUCCEEDED(hr); 
			} 
		} 
	} 
 
	return CTransformFilter::SetSyncSource(pClock); 
} 
 
void CDirectVobSubFilter::HookTextStreams() // nasty... 
{ 
	if(m_fTextStreamWorkaroundDone) return; 
 
	{ 
		int i = 0; 
		WCHAR buff[200] = L"Internal Script Command Renderer"; 
		 
		for(CComPtr pFilter; S_OK == m_pGraph->FindFilterByName(buff, &pFilter); pFilter = NULL) 
		{ 
			HRESULT hr = m_pGraph->RemoveFilter(pFilter); 
 
			i++; 
			swprintf(buff, L"Internal Script Command Renderer %04d", i); 
		} 
	} 
 
	CComPtr pFilter; 
	if(FAILED(m_pGraph->FindFilterByName(L"AVI Splitter", &pFilter))) return; 
 
	CComPtr pEnum; 
	if(FAILED(pFilter->EnumPins(&pEnum))) return; 
 
	for(CComPtr pPin; S_OK == pEnum->Next(1, &pPin, 0); pPin = NULL) 
	{ 
		PIN_DIRECTION pd; 
		if(FAILED(pPin->QueryDirection(&pd)) || pd != PINDIR_OUTPUT) 
			continue; 
		 
		CComPtr pPin2; 
		if(SUCCEEDED(pPin->ConnectedTo(&pPin2))) 
			continue; 
 
		CComPtr pEnum2; 
		if(FAILED(pPin->EnumMediaTypes(&pEnum2))) 
			continue; 
 
		bool fOK = false; 
 
		for(AM_MEDIA_TYPE* pmt; !fOK && S_OK == pEnum2->Next(1, &pmt, NULL); DeleteMediaType(pmt)) 
		{ 
			if(pmt->majortype == MEDIATYPE_Text) 
			{ 
				m_pGraph->ConnectDirect(pPin, GetPin(2 + m_pTextInput.GetSize()-1), pmt); 
				fOK = true; 
			} 
		} 
	} 
 
	m_fTextStreamWorkaroundDone = true; 
} 
 
/////////////////////////////////////////////////////////////////////////// 
 
CDirectVobSubFilter2::CDirectVobSubFilter2(TCHAR* tszName, LPUNKNOWN punk, HRESULT* phr, const GUID& guid) : 
	CDirectVobSubFilter(tszName, punk, phr, guid) 
{ 
 
} 
 
CUnknown* CDirectVobSubFilter2::CreateInstance(LPUNKNOWN punk, HRESULT* phr) 
{ 
	CDirectVobSubFilter2* pNewObject = new CDirectVobSubFilter2(NAME("DirectVobSub (auto-loading version)"), punk, phr, CLSID_DirectVobSubFilter2); 
 
	if(pNewObject == NULL) *phr = E_OUTOFMEMORY; 
 
	return pNewObject; 
} 
 
STDMETHODIMP CDirectVobSubFilter2::GetClassID(CLSID* pClsid) 
{ 
    if(pClsid == NULL) return E_POINTER; 
 
	*pClsid = CLSID_DirectVobSubFilter2; 
 
    return NOERROR; 
} 
 
HRESULT CDirectVobSubFilter2::CheckConnect(PIN_DIRECTION dir, IPin* pPin) 
{ 
	PIN_INFO pi; 
	if(FAILED(pPin->QueryPinInfo(&pi))) return E_FAIL; 
 
	CComPtr pFilter; 
	pFilter.Attach(pi.pFilter); 
 
	if(CComQIPtr(pFilter)) return(E_FAIL); 
 
	if(dir == PINDIR_INPUT) 
	{ 
		FILTER_INFO fi; 
		if(SUCCEEDED(pFilter->QueryFilterInfo(&fi))) 
		{ 
			if(fi.pGraph) fi.pGraph->Release(); 
			if(!wcsnicmp(fi.achName, L"Overlay Mixer", 13)) return(E_FAIL); 
		} 
	} 
	else 
	{ 
	} 
 
	return CDirectVobSubFilter::CheckConnect(dir, pPin); 
} 
 
HRESULT CDirectVobSubFilter2::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) 
{ 
	if(pGraph) 
	{ 
		if(FindMyself(pGraph) > 1)  
			return E_FAIL; 
 
		// don't look... we will do some serious graph hacking again... 
		// 
		// we will add dvs2 to the filter graph cache 
		// - if the main app has already added some kind of renderer or overlay mixer (anything which accepts video on its input) 
		// and  
		// - if we have a reason to auto-load (we don't want to make any trouble when there is no need :) 
		// 
		// This whole workaround is needed because the video stream will always be connect  
		// to the pre-added filters first, no matter how high merit we have. 
 
		if(!get_Forced()) 
		{ 
		    CComPtr pEnum; 
			if(SUCCEEDED(pGraph->EnumFilters(&pEnum))) 
			{ 
				bool fOK = false; 
 
				for(CComPtr pFilter; !fOK && (S_OK == pEnum->Next(1, &pFilter, NULL)); pFilter = NULL) 
				{ 
					if(CComQIPtr(pFilter)) 
						continue; 
 
					CComPtr pEnum2; 
					if(FAILED(pFilter->EnumPins(&pEnum2))) 
						continue; 
 
					CComPtr pPin, pInPin, pOutPin; 
 
					for(; S_OK == pEnum2->Next(1, &pPin, NULL); pPin = NULL) 
					{ 
						PIN_DIRECTION dir; 
						if(SUCCEEDED(pPin->QueryDirection(&dir))) 
						{ 
							if(dir == PINDIR_INPUT && !pInPin) 
								pInPin = pPin; 
							else if(dir == PINDIR_OUTPUT && !pOutPin) 
								pOutPin = pPin; 
						} 
					} 
 
					if(!pInPin && !pOutPin) 
						continue; 
 
					FILTER_INFO fi; 
					if(FAILED(pFilter->QueryFilterInfo(&fi))) continue; 
					if(fi.pGraph) fi.pGraph->Release(); 
 
					if(pOutPin && !wcsstr(fi.achName, L"Overlay Mixer"))  
						continue; 
 
					pPin = NULL; 
					if(pInPin && SUCCEEDED(pInPin->ConnectedTo(&pPin))) 
						continue; 
 
					pPin = NULL; 
					if(pOutPin && SUCCEEDED(pOutPin->ConnectedTo(&pPin))) 
						continue; 
 
					bool fVideoInputPin = false; 
 
					do 
					{ 
						BITMAPINFOHEADER bih; 
						memset(&bih, 0, sizeof(bih)); 
						bih.biSize = sizeof(bih); 
						bih.biWidth = 384; 
						bih.biHeight = 288; 
						bih.biBitCount = 16; 
						bih.biCompression = mmioFOURCC('Y','U','Y','2'); 
						bih.biSizeImage = 384*288*2; 
						bih.biPlanes = 1; 
 
						CMediaType cmt; 
						cmt.majortype = MEDIATYPE_Video; 
						cmt.subtype = MEDIASUBTYPE_YUY2; 
						cmt.formattype = FORMAT_VideoInfo; 
						cmt.pUnk = NULL; 
						cmt.bFixedSizeSamples = TRUE; 
						cmt.bTemporalCompression = TRUE; 
						cmt.lSampleSize = 384*288*2; 
						VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)cmt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER)); 
						memset(vih, 0, sizeof(VIDEOINFOHEADER)); 
						memcpy(&vih->bmiHeader, &bih, sizeof(bih)); 
						vih->AvgTimePerFrame = 400000; 
 
						if(SUCCEEDED(pInPin->QueryAccept(&cmt)))  
						{ 
							fVideoInputPin = true; 
							break; 
						} 
 
						VIDEOINFOHEADER2* vih2 = (VIDEOINFOHEADER2*)cmt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER2)); 
						memset(vih2, 0, sizeof(VIDEOINFOHEADER2)); 
						memcpy(&vih2->bmiHeader, &bih, sizeof(bih)); 
						vih2->AvgTimePerFrame = 400000; 
						vih2->dwPictAspectRatioX = 384; 
						vih2->dwPictAspectRatioY = 288; 
 
						if(SUCCEEDED(pInPin->QueryAccept(&cmt))) 
						{ 
							fVideoInputPin = true; 
							break; 
						} 
					} 
					while(false); 
 
					if(fVideoInputPin) 
					{ 
						CComPtr pDVS; 
						if(ShouldWeAutoload(pGraph) && SUCCEEDED(pDVS.CoCreateInstance(CLSID_DirectVobSubFilter2))) 
						{ 
							CComQIPtr(pDVS)->put_Forced(true); 
							CComQIPtr(pGraph)->AddFilterToCache(pDVS); 
						} 
 
						fOK = true; 
					} 
				} 
			} 
		} 
	} 
	else 
	{ 
	} 
 
	return CDirectVobSubFilter::JoinFilterGraph(pGraph, pName); 
} 
 
HRESULT CDirectVobSubFilter2::CheckInputType(const CMediaType* mtIn) 
{ 
    HRESULT hr = CDirectVobSubFilter::CheckInputType(mtIn); 
 
	if(FAILED(hr) || m_pInput->IsConnected()) return hr; 
 
	if(!ShouldWeAutoload(m_pGraph)) return VFW_E_TYPE_NOT_ACCEPTED; 
 
	GetRidOfInternalScriptRenderer(); 
 
	return NOERROR; 
} 
 
bool CDirectVobSubFilter2::ShouldWeAutoload(IFilterGraph* pGraph) 
{ 
	TCHAR blacklistedapps[][32] =  
	{ 
		_T("WM8EUTIL."), // wmp8 encoder's dummy renderer releases the outputted media sample after calling Receive on its input pin (yes, even when dvobsub isn't registered at all) 
		_T("explorer."), // as some users reported thumbnail preview loads dvobsub, I've never experienced this yet... 
	}; 
 
	for(int i = 0; i < sizeof(blacklistedapps)/sizeof(blacklistedapps[0]); i++) 
	{ 
		if(theApp.m_AppName.Find(blacklistedapps[i]) >= 0)  
			return(false); 
	} 
 
	int level; 
	bool m_fExternalLoad, m_fWebLoad, m_fEmbeddedLoad; 
	get_LoadSettings(&level, &m_fExternalLoad, &m_fWebLoad, &m_fEmbeddedLoad); 
 
	if(level < 0 || level >= 2) return(false); 
 
	bool fRet = false; 
 
	if(level == 1) 
		fRet = m_fExternalLoad = m_fWebLoad = m_fEmbeddedLoad = true; 
 
	fRet = (m_fEmbeddedLoad && FindTextStream(pGraph)) || fRet; 
 
	WCHAR fn[MAX_PATH] = {0}; 
	if((m_fExternalLoad || m_fWebLoad) && FindSourceFileName(pGraph, fn) 
	&& (m_fWebLoad || !(wcsstr(fn, L"http://") || wcsstr(fn, L"mms://")))) 
	{ 
		bool fTemp = m_fHideSubtitles; 
		fRet = SUCCEEDED(put_FileName(fn)) || fRet; 
		if(fTemp) m_fHideSubtitles = true; 
	} 
 
	return(fRet); 
} 
 
void CDirectVobSubFilter2::GetRidOfInternalScriptRenderer() 
{ 
	int i = 0; 
	WCHAR buff[100] = L"Internal Script Command Renderer"; 
	 
	for(CComPtr pFilter; S_OK == m_pGraph->FindFilterByName(buff, &pFilter); pFilter = NULL) 
	{ 
		i++; 
		swprintf(buff, L"Internal Script Command Renderer %04d", i); 
 
		CComPtr pEnum; 
		if(FAILED(pFilter->EnumPins(&pEnum))) 
			continue; 
 
		for(CComPtr pPin; S_OK == pEnum->Next(1, &pPin, 0); pPin = NULL) 
		{ 
			PIN_DIRECTION pd; 
			CComPtr pPin2; 
 
			if(SUCCEEDED(pPin->QueryDirection(&pd)) && pd == PINDIR_INPUT  
			&& SUCCEEDED(pPin->ConnectedTo(&pPin2))) 
			{ 
				m_pGraph->Disconnect(pPin2); 
				m_pGraph->ConnectDirect(pPin2, GetPin(2 + m_pTextInput.GetSize()-1), NULL); 
			} 
		} 
		 
		m_pGraph->RemoveFilter(pFilter); 
	} 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
 
static void PixelAtBasic(RGBQUAD& c, int x, int y, CVobSubImage& src) 
{ 
	c = src.lpPixels[(y >> 16) * src.rect.Width() + (x >> 16)]; 
} 
 
static void PixelAtBiLinear(RGBQUAD& c, int x, int y, CVobSubImage& src) 
{ 
	int w = src.rect.Width(), 
		h = src.rect.Height(); 
 
	int x1 = (x >> 16), y1 = (y >> 16) * w, 
		x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w); 
 
	RGBQUAD* ptr = src.lpPixels; 
 
	RGBQUAD c11 = ptr[y1 + x1],	c12 = ptr[y1 + x2], 
			c21 = ptr[y2 + x1],	c22 = ptr[y2 + x2]; 
 
	__int64 u2 = x & 0xffff, 
			v2 = y & 0xffff, 
			u1 = 0x10000 - u2, 
			v1 = 0x10000 - v2; 
 
	int v1u1 = int(v1*u1 >> 16) * c11.rgbReserved,  
		v1u2 = int(v1*u2 >> 16) * c12.rgbReserved, 
		v2u1 = int(v2*u1 >> 16) * c21.rgbReserved,  
		v2u2 = int(v2*u2 >> 16) * c22.rgbReserved; 
 
	c.rgbRed = (c11.rgbRed * v1u1 + c12.rgbRed * v1u2 
			  + c21.rgbRed * v2u1 + c22.rgbRed * v2u2) >> 24; 
	c.rgbGreen = (c11.rgbGreen * v1u1 + c12.rgbGreen * v1u2 
				+ c21.rgbGreen * v2u1 + c22.rgbGreen * v2u2) >> 24; 
	c.rgbBlue = (c11.rgbBlue * v1u1 + c12.rgbBlue * v1u2 
				+ c21.rgbBlue * v2u1 + c22.rgbBlue * v2u2) >> 24; 
	c.rgbReserved = (v1u1 + v1u2  
					+ v2u1 + v2u2) >> 16; 
} 
 
static void PixelAtBiCubic(RGBQUAD& c, int x, int y, CVobSubImage& src) 
{ 
	int w = src.rect.Width(), 
		h = src.rect.Height(); 
 
	int x1 = (x >> 16), y1 = (y >> 16) * w, 
		x0 = max(x1 - 1, 0), y0 = max(y1 - w, 0), 
		x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w); 
 
	RGBQUAD* ptr = src.lpPixels; 
 
	RGBQUAD c00 = ptr[y0 + x0],	c01 = ptr[y0 + x1],	c02 = ptr[y0 + x2], 
			c10 = ptr[y1 + x0],	c11 = ptr[y1 + x1],	c12 = ptr[y1 + x2], 
			c20 = ptr[y2 + x0],	c21 = ptr[y2 + x1],	c22 = ptr[y2 + x2]; 
 
	__int64 u2 = ((x & 0xffff) | 0x10000) >> 1, 
			v2 = ((y & 0xffff) | 0x10000) >> 1, 
			u1 = 0x10000 - u2, 
			v1 = 0x10000 - v2; 
 
	__int64 u11 = int(u1*u1 >> 16),	u12 = int(u1*u2 >> 15),	u22 = int(u2*u2 >> 16), 
			v11 = int(v1*v1 >> 16),	v12 = int(v1*v2 >> 15),	v22 = int(v2*v2 >> 16); 
 
	int v11u11 = int(u11*v11 >> 16) * c00.rgbReserved, 
		v11u12 = int(u12*v11 >> 16) * c01.rgbReserved, 
		v11u22 = int(u22*v11 >> 16) * c02.rgbReserved, 
		v12u11 = int(u11*v12 >> 16) * c10.rgbReserved, 
		v12u12 = int(u12*v12 >> 16) * c11.rgbReserved, 
		v12u22 = int(u22*v12 >> 16) * c12.rgbReserved, 
		v22u11 = int(u11*v22 >> 16) * c20.rgbReserved, 
		v22u12 = int(u12*v22 >> 16) * c21.rgbReserved, 
		v22u22 = int(u22*v22 >> 16) * c22.rgbReserved; 
 
	c.rgbRed = (c00.rgbRed * v11u11 + c01.rgbRed * v11u12 + c02.rgbRed * v11u22 
				+ c10.rgbRed * v12u11 + c11.rgbRed * v12u12 + c12.rgbRed * v12u22 
				+ c20.rgbRed * v22u11 + c21.rgbRed * v22u12 + c22.rgbRed * v22u22) >> 24; 
	c.rgbGreen = (c00.rgbGreen * v11u11 + c01.rgbGreen * v11u12 + c02.rgbGreen * v11u22 
				+ c10.rgbGreen * v12u11 + c11.rgbGreen * v12u12 + c12.rgbGreen * v12u22 
				+ c20.rgbGreen * v22u11 + c21.rgbGreen * v22u12 + c22.rgbGreen * v22u22) >> 24; 
	c.rgbBlue = (c00.rgbBlue * v11u11 + c01.rgbBlue * v11u12 + c02.rgbBlue * v11u22 
				+ c10.rgbBlue * v12u11 + c11.rgbBlue * v12u12 + c12.rgbBlue * v12u22 
				+ c20.rgbBlue * v22u11 + c21.rgbBlue * v22u12 + c22.rgbBlue * v22u22) >> 24; 
	c.rgbReserved = (v11u11 + v11u12 + v11u22  
					+ v12u11 + v12u12 + v12u22  
					+ v22u11 + v22u12 + v22u22) >> 16; 
 
	// Oh-hoh-hoo! See those stars? They are beautiful! Ah yes, and don't forget to buy a P4, LOL ;-) 
} 
 
void StretchPut(BITMAP& bm, CRect dstrect, CVobSubImage& src, int fSmooth) 
{ 
	if(dstrect.IsRectEmpty()) return; 
 
	if((dstrect & CRect(0, 0, bm.bmWidth, bm.bmHeight)).IsRectEmpty()) return; 
 
	int sw = src.rect.Width(), 
		sh = src.rect.Height(), 
		dw = dstrect.Width(), 
		dh = dstrect.Height(); 
 
	int srcx = 0,  
		srcy = 0, 
		srcdx = (sw << 16) / dw >> 1,  
		srcdy = (sh << 16) / dh >> 1; 
 
	if(dstrect.left < 0) {srcx = -dstrect.left * (srcdx<<1); dstrect.left = 0;} 
	if(dstrect.top < 0) {srcy = -dstrect.top * (srcdy<<1); dstrect.top = 0;} 
	if(dstrect.right > bm.bmWidth) {dstrect.right = bm.bmWidth;} 
	if(dstrect.bottom > bm.bmHeight) {dstrect.bottom = bm.bmHeight;} 
 
	if((dstrect & CRect(0, 0, bm.bmWidth, bm.bmHeight)).IsRectEmpty()) return; 
 
	dw = dstrect.Width(); 
	dh = dstrect.Height(); 
 
	if(fSmooth < 2) 
	{ 
		void (* PixelAt)(RGBQUAD&, int, int, CVobSubImage&) =  
			fSmooth == 0 ? PixelAtBiLinear : PixelAtBiCubic; 
		 
		for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1)) 
		{ 
			RGBQUAD* ptr = (RGBQUAD*)&((char*)bm.bmBits)[(bm.bmHeight-y-1)*bm.bmWidthBytes] + dstrect.left; 
			RGBQUAD* endptr = ptr + dw; 
			 
			for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++) 
			{ 
				PixelAt(*ptr,	sx,			srcy,		src); 
 
				RGBQUAD cc[4]; 
 
				PixelAt(cc[0],	sx,			srcy,		src); 
				PixelAt(cc[1],	sx+srcdx,	srcy,		src); 
				PixelAt(cc[2],	sx,			srcy+srcdy,	src); 
				PixelAt(cc[3],	sx+srcdx,	srcy+srcdy,	src); 
				 
				ptr->rgbRed = (cc[0].rgbRed + cc[1].rgbRed + cc[2].rgbRed + cc[3].rgbRed) >> 2; 
				ptr->rgbGreen = (cc[0].rgbGreen + cc[1].rgbGreen + cc[2].rgbGreen + cc[3].rgbGreen) >> 2; 
				ptr->rgbBlue = (cc[0].rgbBlue + cc[1].rgbBlue + cc[2].rgbBlue + cc[3].rgbBlue) >> 2; 
				ptr->rgbReserved = (cc[0].rgbReserved + cc[1].rgbReserved + cc[2].rgbReserved + cc[3].rgbReserved) >> 2; 
 
				ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8; 
				ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8; 
				ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8; 
				ptr->rgbReserved = ~ptr->rgbReserved; 
			} 
		} 
	} 
	else 
	{ 
		for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1)) 
		{ 
			RGBQUAD* ptr = (RGBQUAD*)&((char*)bm.bmBits)[(bm.bmHeight-y-1)*bm.bmWidthBytes] + dstrect.left; 
			RGBQUAD* endptr = ptr + dw; 
 
			for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++) 
			{ 
				PixelAtBasic(*ptr,	sx,	srcy,	src); 
 
				ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8; 
				ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8; 
				ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8; 
				ptr->rgbReserved = ~ptr->rgbReserved; 
			} 
		} 
	} 
} 
 
//////////////////////////////////////////////////////////////////////// 
 
bool CDirectVobSubFilter::Open() 
{ 
	AFX_MANAGE_STATE(AfxGetStaticModuleState()); 
 
	bool fRet = false; 
 
	{ 
		CallWorker(CMD_RESET); 
 
		CAutoLock lock(&m_AccessLock); 
 
	    CAutoLock cAutolock(&m_fileLock); 
 
		m_mode = NONE; 
 
		m_iSelectedLanguage = -1; 
 
		m_fLoading = true; 
 
		{ 
			if(m_file) {delete m_file; m_file = NULL;} 
 
			m_textlangs.RemoveAll(); 
		} 
 
		m_frd.files.RemoveAll(); 
 
		CStringArray paths; 
 
		for(int k = 0; k < 10; k++) 
		{ 
			CString path, tmp; 
 
			tmp.Format(IDS_RP_PATH, k); 
		 
			path = theApp.GetProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp); 
			if(!path.IsEmpty()) paths.Add(path); 
		} 
 
		SubFiles sf; 
		vobsubGetSubFileNames(m_FileName, paths, sf); 
 
		for(int i = 0, j = sf.GetSize(), ssa = 0; i < j; i++) 
		{ 
			SubFile& osf = sf[i]; 
 
			if(osf.ext == EXTIDX) 
			{ 
				if(m_file) continue; 
 
				m_file = new CVobSubFile(); 
				if(!m_file) continue; 
 
				CString title = osf.fn.Left(osf.fn.ReverseFind('.')); 
 
				if(!m_file->Open(title, m_fBufferVobSub)) 
				{ 
					delete m_file; 
					m_file = NULL; 
					continue; 
				} 
 
				m_file->SetOnlyShowForcedSubs(m_fOnlyShowForcedVobSubs); 
 
				m_frd.files.Add(sf[i].fn.Left(sf[i].fn.GetLength()-4) + _T(".sub")); 
			} 
			else  
			{ 
				CRenderedTextSubtitle ret; 
				if(!ret.Open(sf[i].fn, DEFAULT_CHARSET)) continue; 
 
				m_textlangs.Add(ret); 
 
				m_frd.files.Add(sf[i].fn + _T(".style")); 
			} 
 
			m_frd.files.Add(sf[i].fn); 
		} 
 
 
		SetEvent(m_frd.hRefreshEvent); 
	} 
 
	for(int i = 0; i < m_pTextInput.GetSize(); i++) 
	{ 
		if(!m_pTextInput[i] || !m_pTextInput[i]->IsConnected()) continue; 
		AddTextStream(m_pTextInput[i]->GetName()); 
	} 
 
	InitRTSStreams(false); 
 
	Enable(FindPreferedLanguage(), AMSTREAMSELECTENABLE_ENABLE); 
 
	DWORD cnt; 
	Count(&cnt); 
 
	return(cnt > 4); 
} 
 
static int intcomp(const void* i1, const void* i2) 
{ 
	return(*((int*)i1) - *((int*)i2)); 
} 
 
bool CDirectVobSubFilter::InitRTSStreams(bool fApplyDefStyle) 
{ 
	if(!m_pOutput || !m_pOutput->IsConnected()) return(false); 
 
	CallWorker(CMD_BREAK); 
 
	{ 
		CAutoLock cAutolock(&m_fileLock); 
 
		for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
		{ 
			if(!m_textlangs[i].Init(CSize(m_sic.w, m_sic.h))) return(false); 
 
			STSStyle s; 
 
			if(!m_textlangs[i].GetDefaultStyle(s) 
			|| (!fApplyDefStyle && !m_textlangs[i].m_fUsingDefaultStyle)) continue; 
 
			if(m_fOverridePlacement) 
			{ 
				s.scrAlignment = 2; 
				int w = m_textlangs[i].m_dstScreenSize.cx; 
				int h = m_textlangs[i].m_dstScreenSize.cy; 
				int mw = w - s.marginRect.left - s.marginRect.right; 
				s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100); 
				s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2; 
				s.marginRect.right = w - (s.marginRect.left + mw); 
			} 
 
			s = m_lf; 
			s.colors[0] = m_TextColor; 
			s.outlineWidth = m_fOutline?2:0; 
			s.shadowDepth = m_fShadow?2:0; 
 
			m_textlangs[i].SetDefaultStyle(s); 
		} 
	} 
 
	return(true); 
} 
 
////////////////////////////////////////////////////////////////////////////////////////// 
 
void CDirectVobSubFilter::AddTextStream(CString name) 
{ 
	CallWorker(CMD_BREAK); 
 
	{ 
		CAutoLock cAutolock(&m_fileLock); 
 
		RemoveTextStream(name); 
		 
		CRenderedTextSubtitle rts; 
 
		rts.m_name = name; 
		rts.m_mode = TIME; 
		rts.m_dstScreenSize = CSize(384, 288); 
		rts.m_fSSA = true; 
 
		rts.CreateDefaultStyle(DEFAULT_CHARSET, false); 
 
		STSStyle s; 
 
		if(rts.GetDefaultStyle(s)) 
		{ 
			if(m_fOverridePlacement) 
			{ 
				s.scrAlignment = 2; 
				int w = rts.m_dstScreenSize.cx; 
				int h = rts.m_dstScreenSize.cy; 
				int mw = w - s.marginRect.left - s.marginRect.right; 
				s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100); 
				s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2; 
				s.marginRect.right = w - (s.marginRect.left + mw); 
			} 
 
			s = m_lf; 
			s.colors[0] = m_TextColor; 
			s.outlineWidth = m_fOutline?2:0; 
			s.shadowDepth = m_fShadow?2:0; 
 
			rts.SetDefaultStyle(s); 
		} 
 
		m_textlangs.Add(rts); 
 
		m_textidx = 0; 
	} 
 
	bool fTemp = m_fHideSubtitles; 
	Enable(1, AMSTREAMSELECTENABLE_ENABLE); 
	m_fHideSubtitles = fTemp; 
 
} 
 
void CDirectVobSubFilter::AddNewEntryToTextStream(CString name, CString str, int start, int end) 
{ 
	CallWorker(CMD_BREAK); 
	 
	{ 
		CAutoLock cAutolock(&m_fileLock); 
 
		for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
		{ 
			if(m_textlangs[i].m_name == name) 
			{ 
				start = (int)(start - fmod(1.0*start, 1000.0/fabs(m_MediaFPS)) + 0.5); 
				end = (int)(end - fmod(1.0*end, 1000.0/fabs(m_MediaFPS)) + 0.5); 
				if(start == end) end = (int)(end + 1000.0/fabs(m_MediaFPS) + 0.5); 
 
				m_textlangs[i].Add(str, start, end); 
				if(m_textidx == i) m_sic.FreeExtra(start); 
 
				break; 
			} 
		} 
	} 
} 
 
void CDirectVobSubFilter::ChangeName(CString oldname, CString newname, bool fUnicode) 
{ 
/*	CallWorker(CMD_BREAK); 
 
    CAutoLock cAutolock(&m_fileLock); 
*/ 
	for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
	{ 
		if(m_textlangs[i].m_name == oldname) 
		{ 
			m_textlangs[i].m_name = newname; 
			if(m_textlangs[i].GetSize() > 0) m_textlangs[i].GetStyle(0)->fUnicode = fUnicode; 
			break; 
		} 
	} 
} 
 
void CDirectVobSubFilter::ChangeName(CString oldname, CString newname) 
{ 
	for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
	{ 
		if(m_textlangs[i].m_name == oldname) 
		{ 
			m_textlangs[i].m_name = newname; 
			break; 
		} 
	} 
} 
 
void CDirectVobSubFilter::RemoveEntriesFromTextStream(CString name) 
{ 
/*	CallWorker(CMD_RESET); 
 
	{ 
		CAutoLock cAutolock(&m_fileLock); 
 
		for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
		{ 
			if(m_textlangs[i].m_name == name) 
			{ 
				m_textlangs[i].Empty(); 
				break; 
			} 
		} 
	} 
*/ 
} 
 
void CDirectVobSubFilter::RemoveTextStream(CString name) 
{ 
	CallWorker(CMD_BREAK); 
 
	{ 
		CAutoLock cAutolock(&m_fileLock); 
		 
		for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
		{ 
			if(m_textlangs[i].m_name == name) 
			{ 
				m_textlangs.RemoveAt(i); 
				break; 
			} 
		} 
	} 
} 
 
void CDirectVobSubFilter::OpenRawTextSubtitle(CString name, BYTE* data, int len) 
{ 
	CallWorker(CMD_BREAK); 
 
	{ 
		CAutoLock cAutolock(&m_fileLock); 
 
		for(int i = 0, j = m_textlangs.GetSize(); i < j; i++) 
		{ 
			CRenderedTextSubtitle& rts = m_textlangs[i]; 
 
			if(rts.m_name == name) 
			{ 
				if(rts.GetSize() == 0) 
				{ 
					CallWorker(CMD_RESET); 
 
					rts.Open(data, len, DEFAULT_CHARSET, name); 
			 
					STSStyle s; 
					if(rts.GetDefaultStyle(s)) 
					{ 
						if(m_fOverridePlacement) 
						{ 
							s.scrAlignment = 2; 
							int w = rts.m_dstScreenSize.cx; 
							int h = rts.m_dstScreenSize.cy; 
							int mw = w - s.marginRect.left - s.marginRect.right; 
							s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100); 
							s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2; 
							s.marginRect.right = w - (s.marginRect.left + mw); 
						} 
 
						s = m_lf; 
						s.colors[0] = m_TextColor; 
						s.outlineWidth = m_fOutline?2:0; 
						s.shadowDepth = m_fShadow?2:0; 
 
						rts.SetDefaultStyle(s); 
					} 
				} 
 
				break; 
			} 
		} 
	} 
} 
 
////////////////////////////////////////////////////////////////////////////////////////// 
 
 
 
//////////////////////////////////////////////////////////////////////////// 
 
DWORD CDirectVobSubFilter::ThreadProc() 
{ 
	SetThreadPriority(m_hThread, THREAD_PRIORITY_BELOW_NORMAL); 
 
    while(1) 
	{ 
		DWORD cmd = GetRequest(); 
 
		m_fThreadActive = true; // don't call us again with CMD_SETPTS when this flag is set 
 
		switch(cmd) 
		{ 
		case CMD_EXIT: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_EXIT"))); 
 
		    Reply(S_OK); 
			return 0; 
 
		// wow, look at this, no breaks through 4 choices :) 
		case CMD_INIT: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_INIT"))); 
 
			m_sic.Init(m_bihSub.biWidth, m_bihSub.biHeight); 
 
		case CMD_SETFONT: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_SETFONT"))); 
 
			{ 
				LOGFONT tmp; 
				memcpy(&tmp, &m_lf, sizeof(tmp)); 
				HDC hdc = ::GetDC(0); 
				tmp.lfHeight = -MulDiv(tmp.lfHeight, 72 * m_bihSub.biHeight, GetDeviceCaps(hdc, LOGPIXELSY) * 288); 
				ReleaseDC(0, hdc); 
				m_sic.SetFont(&tmp); 
			} 
 
		case CMD_RESET: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_RESET"))); 
 
			m_sic.RemoveAll(); 
 
		case CMD_BREAK: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_BREAK"))); 
 
			Reply(S_OK); 
			break; 
 
		case CMD_SETPTS: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_SETPTS %d"), (int)m_tPrev)); 
 
			{ 
				Reply(S_OK); 
 
				int PTS = CalcCurrentTime();  
 
				m_sic.MarkAsStart(PTS); 
 
				if(!m_sic.CachedEnough(PTS)) 
				{ 
					PTS = m_sic.SeekToEnd(PTS); 
				 
					int i, j, endPTS = PTS + LOOKAHEADSIZE; 
					SubImage* cur = NULL; 
 
					CAutoLock cAutoLock(&m_fileLock); 
 
					if(m_mode == VOBSUB && m_file != NULL) 
					{ 
						i = m_file->GetFrameIdxByTimeStamp(PTS),  
						j = m_file->GetFrameIdxByTimeStamp(PTS + LOOKAHEADSIZE); 
 
						if(j == -1) 
						{ 
							PTS += LOOKAHEADSIZE; 
						} 
						else if(i == -1) 
						{ 
							while((i = m_file->GetFrameIdxByTimeStamp(PTS)) == -1 && PTS < endPTS) 
							{ 
								PTS += LOOKAHEADSTEPSIZE; 
							} 
						} 
 
						for(;  
							PTS < endPTS && i >= 0 && m_file->GetFrame(i) && (cur = m_sic.GetNextFree()) != NULL;  
							i++) 
						{ 
							if(CheckRequest(&cmd) || m_fBreakBuffering) break; 
							 
							int start = m_file->m_img.nTimeStamp; 
							int stop = m_file->m_img.nTimeStamp + m_file->m_img.nDisplayTime; 
 
							if(PTS < stop)  
							{ 
								AddVobSub(cur, start, stop); 
 
								PTS = stop; 
							} 
						} 
					} 
					else if(m_mode == TEXT && m_textidx >= 0 && m_textidx < m_textlangs.GetSize()) 
					{ 
						CRenderedTextSubtitle& rts = m_textlangs[m_textidx]; 
 
						int iSegment = -1, nSegments = 0; 
						rts.SearchSubs(PTS, m_fps, &iSegment, &nSegments); 
 
						if(iSegment < 0 && nSegments > 0) iSegment++; 
 
						if(iSegment >= nSegments) PTS += LOOKAHEADSIZE; 
 
						for(;  
							PTS < endPTS && iSegment >= 0 && iSegment < nSegments && (cur = m_sic.GetNextFree()) != NULL; 
							iSegment++) 
						{ 
							if(CheckRequest(&cmd) || m_fBreakBuffering) break; 
 
							if(rts.GetSegment(iSegment)->subs.GetSize() == 0) continue; 
 
							int start = rts.TranslateSegmentStart(iSegment, m_fps); 
							int stop = rts.TranslateSegmentEnd(iSegment, m_fps); 
 
							if(PTS < stop) 
							{ 
								AddText(cur, start, stop, PTS); 
 
								PTS = stop; 
							} 
						} 
					} 
 
					m_sic.MarkAsEnd(PTS); 
				} 
			} 
 
			break; 
 
		default: 
			DbgLog((LOG_CUSTOM2, 1, _T("SubImageCache: CMD_UNKNOWN"))); 
 
		    Reply(E_FAIL); 
			return -1; 
		} 
 
		m_fThreadActive = false; 
 
		if(m_fBreakBuffering)  
			m_sic.RemoveAll(); 
 
		m_fBreakBuffering = false; 
	} 
 
	return 1; 
} 
 
//////////////////////////////////////////////////////////////// 
 
CDirectVobSubFilter::SubImageCache::SubImageCache() 
{ 
	hdc = 0; 
	hfont = 0; 
	memset(imgs, 0, sizeof(SubImage)*MAXSUBIMAGECACHE); 
	pos = size = start = stop = 0; 
} 
 
CDirectVobSubFilter::SubImageCache::~SubImageCache() 
{ 
	DeInit(); 
} 
 
void CDirectVobSubFilter::SubImageCache::Init(int w, int h) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	DeInit(); 
 
	h = abs(h); 
 
	this->w = w; 
	this->h = h; 
	 
	hdc = CreateCompatibleDC(NULL); 
 
	for(int i = 0; i < MAXSUBIMAGECACHE; i++) 
	{ 
		struct BM 
		{ 
			BITMAPINFOHEADER bih; 
			DWORD mask[3]; 
		} b; 
		 
		memset(&b, 0, sizeof(b)); 
		b.bih.biSize = sizeof(BITMAPV4HEADER); 
		b.bih.biWidth = w; 
		b.bih.biHeight = h; 
		b.bih.biBitCount = 32; 
		b.bih.biPlanes = 1; 
		b.bih.biCompression = BI_BITFIELDS; 
		b.mask[0] = 0xFF0000; 
		b.mask[1] = 0x00FF00; 
		b.mask[2] = 0x0000FF; 
		 
		imgs[i].hbm = CreateDIBSection( 
			hdc, 
			(BITMAPINFO *)&b, 
			DIB_RGB_COLORS, 
			NULL, 
			NULL, 
			0); 
	} 
} 
 
void CDirectVobSubFilter::SubImageCache::DeInit() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	for(int i = 0; i < MAXSUBIMAGECACHE; i++) 
	{ 
		if(imgs[i].hbm) {DeleteObject(imgs[i].hbm); imgs[i].hbm = 0;} 
	} 
 
	if(hfont) {DeleteObject(hfont); hfont = 0;} 
 
	if(hdc) {DeleteDC(hdc); hdc = 0;} 
 
	memset(imgs, 0, sizeof(SubImage)*MAXSUBIMAGECACHE); 
	 
	RemoveAll(); 
} 
 
void CDirectVobSubFilter::SubImageCache::SetFont(LOGFONT* lf) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(hfont) DeleteObject(hfont); 
 
	hfont = CreateFontIndirect(lf); 
} 
 
void CDirectVobSubFilter::SubImageCache::RemoveOldest() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(size <= 0) return; 
 
	DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: removing no.%d"), pos)); 
 
	start = imgs[pos].stop; 
 
	pos = (pos + 1) % MAXSUBIMAGECACHE; 
	size--; 
 
	if(size <= 0) stop = start; 
} 
 
void CDirectVobSubFilter::SubImageCache::RemoveAll() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(size > 0) DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: removing all"))); 
 
	pos = size = 0; 
	start = INT_MAX; 
	stop = -1; 
} 
 
void CDirectVobSubFilter::SubImageCache::Update(int PTS) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(PTS < start) 
	{ 
		RemoveAll(); 
 
		start = stop = PTS; 
	} 
	else if(size >= MAXSUBIMAGECACHE) 
	{ 
		for(int i = pos, j = i + size; i < j; i++) 
		{ 
			int k = i % MAXSUBIMAGECACHE; 
 
			if(imgs[k].stop <= PTS) 
			{ 
				RemoveOldest(); 
break; // looks more effective this way 
			} 
		} 
	} 
} 
 
void CDirectVobSubFilter::SubImageCache::FreeExtra(int PTS) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(PTS < start) 
	{ 
		RemoveAll(); 
	} 
	else if(size >= 0) 
	{ 
		for(int i = pos, j = i + size; i < j; i++) 
		{ 
			int k = i % MAXSUBIMAGECACHE; 
 
			if(imgs[k].start >= PTS) 
			{ 
				DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: removing extra %d+ above %d"), i, PTS)); 
				size = i - pos; 
				break; 
			} 
		} 
	} 
 
	DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: adjusting end to %d"), PTS)); 
 
	start = min(start, PTS); 
	stop = min(stop, PTS); 
} 
 
SubImage* CDirectVobSubFilter::SubImageCache::GetSubImage(int i) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	return(size > 0 && i >= 0 && i < size ? &imgs[(pos + i) % MAXSUBIMAGECACHE] : NULL); 
} 
 
SubImage* CDirectVobSubFilter::SubImageCache::GetLast() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	return(size > 0 ? &imgs[(pos + size - 1) % MAXSUBIMAGECACHE] : NULL); 
} 
 
SubImage* CDirectVobSubFilter::SubImageCache::GetNextFree() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(size < MAXSUBIMAGECACHE)	DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: offering no.%d"), (pos + size) % MAXSUBIMAGECACHE)); 
 
	return(size < MAXSUBIMAGECACHE ? &imgs[(pos + size) % MAXSUBIMAGECACHE] : NULL); 
} 
 
void CDirectVobSubFilter::SubImageCache::MarkNextAsOccupied() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	if(size < MAXSUBIMAGECACHE)  
	{ 
		SubImage* next = &imgs[(pos + size) % MAXSUBIMAGECACHE]; 
 
		DbgLog((LOG_CUSTOM5, 1, _T("SubImageCache: adding no.%d (%d - %d)"), (pos + size) % MAXSUBIMAGECACHE, next->start, next->stop)); 
 
		MarkAsEnd(next->stop); 
 
		size++; 
	} 
} 
 
SubImage* CDirectVobSubFilter::SubImageCache::LookupSubImage(int PTS) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	SubImage* ret = NULL; 
 
	for(int i = pos, j = i + size; i < j; i++) 
	{ 
		int k = i % MAXSUBIMAGECACHE; 
 
		if(imgs[k].start <= PTS && PTS < imgs[k].stop) 
		{ 
			ret = &imgs[k]; 
			break; 
		} 
	} 
 
	return(ret); 
} 
 
bool CDirectVobSubFilter::SubImageCache::CachedEnough() 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	return(CachedEnough(start)); 
} 
 
bool CDirectVobSubFilter::SubImageCache::CachedEnough(int PTS) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	return((stop - PTS) > LOOKAHEADSIZE || size == MAXSUBIMAGECACHE); 
} 
 
void CDirectVobSubFilter::SubImageCache::MarkAsStart(int PTS)  
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	start = min(start, PTS); 
} 
 
void CDirectVobSubFilter::SubImageCache::MarkAsEnd(int PTS)  
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	stop = max(stop, PTS); 
} 
 
int CDirectVobSubFilter::SubImageCache::SeekToEnd(int PTS) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	return(max(stop, PTS)); 
} 
 
void CDirectVobSubFilter::SubImageCache::GetStats(int& start, int& stop, int& pos, int& size) 
{ 
	CAutoLock cAutoLock(&m_csAccessLock); 
 
	start = this->start; 
	stop = this->stop; 
	pos = this->pos; 
	size = this->size; 
} 
 
//// 
 
void SubImage::Clear(uint c) 
{ 
	BITMAP bm; 
	GetObject(hbm, sizeof(bm), &bm); 
	memsetd(bm.bmBits, c, bm.bmWidthBytes*bm.bmHeight); 
} 
 
void SubImage::ConvertToYUY2() 
{ 
	BITMAP bm; 
	GetObject(hbm, sizeof(bm), &bm); 
 
	for(int y = 0; y < bm.bmHeight; y++) 
	{ 
		uchar* sub = (uchar*)bm.bmBits + bm.bmWidthBytes*y; 
		uchar* end = sub + bm.bmWidth*4; 
 
		for(; sub < end; sub += 8) 
		{ 
			int a3 = (sub[3]+sub[7])>>1; 
 
			if(a3 < 0xff) 
			{ 
				sub[1] = (c2y_yb[sub[0]] + c2y_yg[sub[1]] + c2y_yr[sub[2]] + 0x108000) >> 16; 
				sub[5] = (c2y_yb[sub[4]] + c2y_yg[sub[5]] + c2y_yr[sub[6]] + 0x108000) >> 16; 
 
				int scaled_y = (sub[1]+sub[5]-32) * cy_cy2; 
 
				sub[0] = Clip[(((((sub[0]+sub[4])<<15) - scaled_y) >> 10) * c2y_cu + 0x800000 + 0x8000) >> 16]; 
				sub[4] = Clip[(((((sub[2]+sub[6])<<15) - scaled_y) >> 10) * c2y_cv + 0x800000 + 0x8000) >> 16]; 
			} 
			else 
			{ 
				sub[1] = sub[5] = 0x10; 
				sub[0] = sub[4] = 0x80; 
			} 
		} 
	} 
} 
 
//////////////////////////////////////////////////////////////// 
 
void CDirectVobSubFilter::SetupFRD(CFileReloaderData* frd, CStringArray& paths, CArray& handles) 
{ 
    CAutoLock cAutolock(&frd->pFilter->m_fileLock); 
 
	for(int i = 2; i < handles.GetSize(); i++) 
	{ 
		FindCloseChangeNotification(handles[i]); 
	} 
 
	paths.RemoveAll(); 
	handles.RemoveAll(); 
 
	handles.Add(frd->hEndThreadEvent); 
	handles.Add(frd->hRefreshEvent); 
 
	frd->mtime.SetSize(frd->files.GetSize()); 
 
	for(i = 0; i < frd->files.GetSize(); i++) 
	{ 
		CFileStatus status; 
		if(CFile::GetStatus(frd->files[i], status))  
			frd->mtime[i] = status.m_mtime; 
 
		CString fn = frd->files[i]; 
		fn.Replace('\\', '/'); 
		fn = fn.Left(fn.ReverseFind('/')+1); 
 
		bool fFound = false; 
 
		for(int j = 0; !fFound && j < paths.GetSize(); j++) 
		{ 
			if(paths[j] == fn) fFound = true; 
		} 
 
		if(!fFound) 
		{ 
			paths.Add(fn); 
 
			HANDLE h = FindFirstChangeNotification(fn, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);  
			if(h != INVALID_HANDLE_VALUE) handles.Add(h); 
		} 
	} 
} 
 
DWORD WINAPI CDirectVobSubFilter::FileReloaderThreadProc(LPVOID lpParameter) 
{ 
	CFileReloaderData* frd = (CFileReloaderData*)lpParameter; 
 
	CStringArray paths; 
	CArray handles; 
 
	SetupFRD(frd, paths, handles); 
 
	while(1) 
	{  
		DWORD idx = WaitForMultipleObjects(handles.GetSize(), handles.GetData(), FALSE, INFINITE); 
 
		if(idx == (WAIT_OBJECT_0 + 0)) // frd->hEndThreadEvent 
		{ 
			break; 
		} 
		if(idx == (WAIT_OBJECT_0 + 1)) // frd->hRefreshEvent 
		{ 
			SetupFRD(frd, paths, handles); 
		} 
		else if(idx >= (WAIT_OBJECT_0 + 2) && idx < (WAIT_OBJECT_0 + handles.GetSize())) 
		{ 
			bool fLocked = true; 
			frd->pFilter->IsSubtitleReloaderLocked(&fLocked); 
			if(fLocked) continue; 
 
			if(FindNextChangeNotification(handles[idx - WAIT_OBJECT_0]) == FALSE)  
				break; 
 
			int j = 0; 
 
			for(int i = 0; i < frd->files.GetSize() && j == 0; i++) 
			{ 
				CFileStatus status; 
				if(CFile::GetStatus(frd->files[i], status) && frd->mtime[i] != status.m_mtime)  
				{ 
					for(j = 0; j < 10; j++) 
					{ 
						FILE* f = _tfopen(frd->files[i], _T("rb+")); 
						if(f)  
						{ 
							fclose(f); 
							j = 0; 
 
							break; 
						} 
						else 
						{ 
							Sleep(100); 
							j++; 
						} 
					} 
				} 
			} 
 
			if(j > 0) 
			{ 
				SetupFRD(frd, paths, handles); 
			} 
			else 
			{ 
				Sleep(500); 
 
				for(i = 0; i < frd->files.GetSize(); i++) 
				{ 
					CFileStatus status; 
					if(CFile::GetStatus(frd->files[i], status) && frd->mtime[i] != status.m_mtime)  
					{ 
						bool fTemp = frd->pFilter->m_fHideSubtitles; 
						frd->pFilter->Open(); 
						frd->pFilter->m_fHideSubtitles = fTemp; 
 
						SetupFRD(frd, paths, handles); 
 
						break; 
					} 
				} 
			} 
		} 
		else  
		{ 
			break; 
		} 
	} 
 
	for(int i = 2; i < handles.GetSize(); i++) 
	{ 
		FindCloseChangeNotification(handles[i]); 
	} 
 
	return(0); 
}