Пишем свою оболочку для FP под Windows. Шаг 4.
Без окон, без дверей....
Прозрачный (transparent) режим работы плеера по другому еще называется windowless («без-оконный»). В обычном «оконном» режиме плеер создает себе окошко и рисует себе.. уютно так, по-домашнему. В без-оконном режиме плеер пытается рисовать на всем, что ему дадут. Разницу улавливаете? Т.е. Нам самим придется позаботиться о том, чтобы предоставить плееру окно для отрисовки графики.
Это может быть обычное окно. Давайте попробуем, что выйдет, прямо в проекте с предыдущего шага:
// MyFlash.cpp void MyFlash::init() { WCHAR ver[16]={0}; GetInstallVersion(ver); CreateHostWindow(); SetWindowSize(550,600); CenterWindow(); SetWindowText(ver); CreateInstance(ID_OCX,L"BIN"); SetWMODE(WMODE_TRANSPARENT); AttachControl(); LoadMovie(ID_SWF2,L"BIN"); }
WMODE_TRANSPARENT исключает из отрисовки фон мувика, каким бы он ни был, стало быть, нам нужно дать плееру такое окно для рисования, которое само по-себе не обладает фоном, т.е. является прозрачным. На прозрачном окне плеер отрисует контент без фона..и получим то что надо! Только где его взять, такое окно?
Слоеное тесто: Layered Window
Начиная с XP ( могу ошибиться, но в 2000 вроде не было еще этого) Windows заимела особую часть графической подсистемы, благодаря которой стало возможно рисовать не просто окна, а полупрозрачные окна. Эта фича была задумана для такой, вобщем-то, не самой ходовой операции, как перетаскивание иконок файлов и папок. Помните? Да, при драге иконка “полурастворяется”. Красота! Но раз иконку можно “растворить”, то можно то же самое сделать и с обычным окном. Это пол-дела. MS пошел дальше. Был сделан специальный тип окна, не имеющий собственного фона, рамки, но на нем можно рисовать, накладывая один слой графики на другой, и при этом еще используется pixel-blending с учетом альфы. И все это делает сама ось, без дополнительного программинга!
Слоенеое окно действительно отличается от обычных окон. Ко всему прочему, оно не получает от системы сообщения WM_PAINT, которое “заставляет” обычные окна немедленно отрисовывать свой контент. Стало быть, отрисовкой в слоеном окне придется заниматься вручную, например – по таймеру. Теперь об алгоритме отрисовки.
Windows GDI (Graphical Device Interface) зиждется на таком фундаментальном понятии как контекст устройства (Device Context, DC). Условно говоря, под DC можно понимать все, на чем можно рисовать – экран монитора, принтер... э...что там еще? Ну, вы поняли.
Для работы с DC в WinAPI есть рад функций, которые, собственно, и составляют основу GDI, это так называемые “могучие” BLT-функции (они правда могучие, это не сарказм).
Например, BitBlt(). Она позволяет скопировать графику из одного контекста в другой.
Понимаете к чему я клоню? Да! В Windows GDI можно рисовать не только на “реальных” DC, но и на виртуальных. Представьте, мы спокойно отрисовываем в память все что надо, слой за слоем, а потом, когда все будет готово, отправляем рисунок на экран монитора. Здорово!
Для работы с Layered Window существует специальная функция UpdateLayeredWindow()
Она-то и сделает за нас всю работу-отрисует виртуальный контекст в реальный на монитор. Итак, вот что выходит. Создаем слоеное окно, заводим таймер и на каждом его тике делаем следующее:
- создаем виртуальный контекст
- даем плееру в него отрисоваться
- переносим все на контекст окна
- зачищаемся (удаляем виртуальный контекст)
Хм.. а почему не дать плееру рисовать прямо в окно? Хорошо-бы.. но технически это невозможно. Системе надо потом еще контент окна как-то на десктопе расположить с учетом прозрачности. Плееру этого не потянуть. Потому отрисовка идет через буфер.
Постойте, а как мы получим графику от плеера? Все опять же несложно. У плеера есть свой графический интерфейс – IViewObjectEx. Заполучив его, мы сможем как раз заставит плеер рисовать там, где нам надо!
Дополняем базовый класс.
// создаем окно-хост с флагом dwExStyle = WS_EX_LAYERED (default) HWND CFlashWnd::CreateHostWindowEx( LPCWSTR lpWindowName, DWORD dwStyle, DWORD dwExStyle, int nCmdShow, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, LPVOID lpParam ) { if(hWndHost != NULL) return hWndHost; ATOM reg = RegisterHostWindowClass(); if( reg == 0) return NULL; HMODULE hModule = ::LoadLibrary(L"atl"); if(!hModule) return NULL; LPAtlAxWinInit pAtlAxWinInit = (LPAtlAxWinInit)::GetProcAddress(hModule,"AtlAxWinInit"); if(!pAtlAxWinInit) { ::FreeLibrary(hModule); return NULL; } BOOL res = pAtlAxWinInit(); if(res == FALSE) return NULL; hWndHost = ::CreateWindowEx(dwExStyle,FLHOSTWINDOW_CLASS, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInst, lpParam); if(hWndHost == NULL) return NULL; ::SetWindowLongPtr(hWndHost,GWL_USERDATA,(int)this); isTransparent = TRUE; ::ShowWindow(hWndHost, nCmdShow); ::UpdateWindow(hWndHost); return hWndHost; }
// устанавливаем параметры отрисовки HRESULT CFlashWnd::SetTransparentModeParam(SIZE viewSize, POINT viewPoint, UINT rate) { if(!isInit) return S_FALSE; if(!isTransparent) return S_FALSE; // получаем графический интерфейс плеера HRESULT hr = flash->QueryInterface(__uuidof(IViewObjectEx), (void**)&view) ; if(hr != S_OK) return hr; // размер окна рисования drawingSize = viewSize; // позиция окна рисования drawingPoint = viewPoint; // заводим таймер с коллбэеком DrawingTimerProc ::SetTimer(hWndHost, FLDRAWTIMER_ID, rate, (TIMERPROC)DrawingTimerProc); return hr; }
// коллбэк таймера отрисовки (static) VOID CALLBACK CFlashWnd::DrawingTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { if(idEvent == FLDRAWTIMER_ID) { // позиция области отрисовки в окне. POINT layerPt = {0,0}; CFlashWnd* f=(CFlashWnd*)GetWindowLongPtr(hwnd, GWL_USERDATA); if(f != NULL && f->hWndHost == hwnd) f->DrawView(f->drawingSize, f->drawingPoint, layerPt); } }
// отрисовка HRESULT CFlashWnd::DrawView(SIZE windowSize, POINT windowPos, POINT layerPos) { if(view == NULL) return S_FALSE; HDC hdcWindow=GetDC(hWndHost); // создаем виртуальный DC HDC hdcMemory = CreateCompatibleDC(hdcWindow); HBITMAP hBitmap = CreateCompatibleBitmap(hdcWindow, windowSize.cx, windowSize.cy); HBITMAP hOldObject = (HBITMAP)SelectObject(hdcMemory, hBitmap); // даем плееру отрисоваться и переносим все в окно HRESULT hr = view->Draw(DVASPECT_TRANSPARENT, 0, 0, 0, 0, hdcMemory, 0, 0, 0, 0); BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; UpdateLayeredWindow(hWndHost, hdcWindow, &windowPos, &windowSize, hdcMemory, &layerPos, 0, &bf, ULW_ALPHA); SetWindowPos(hWndHost, HWND_TOP, windowPos.x, windowPos.y, windowSize.cx, windowSize.cy, SWP_SHOWWINDOW | SWP_FRAMECHANGED); // зачистка SelectObject(hdcMemory, hOldObject); DeleteObject(hBitmap); ReleaseDC(hWndHost, hdcMemory); DeleteDC(hdcMemory); ReleaseDC(hWndHost, hdcWindow); return hr; }
void MyFlash::init() { WCHAR ver[16]={0}; GetInstallVersion(ver); CreateHostWindowEx(); SetWindowText(ver); CreateInstance(); //CreateInstance(L"C:\\Flash10a.ocx"); //CreateInstance(ID_OCX,L"BIN"); SetWMODE(WMODE_TRANSPARENT); POINT drwPt = {380,200}; SIZE drwSz = {500,600}; SetTransparentModeParam(drwSz, drwPt, 20); SetWMODE(WMODE_TRANSPARENT); AttachControl(); LoadMovie(ID_SWF,L"BIN"); }
На следующем шаге попробуем взаимодействие ролика и оболочки через FSCommand и ExternalInterface.
Всего комментариев 2
Комментарии
08.12.2011 09:15 | |
Плеер 32-битный, следует иметь это в виду, если хотите делать на х64.
Впрочем, сейчас и 64-битный плеер доступен. PS. давненько не заглядывал сюда, пропустил пост. |
Последние записи от alexcon314
- Пишем свою оболочку для FP под Windows. Шаг 6. (19.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 5. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 4. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 3. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 2. (17.05.2011)