Bitmap
Как можно получить содержимое из HBITMAP, не преобразовывая его к DIB?
Чем не устраивает структура
typedef struct tagBITMAP {
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP, *PBITMAP;
Получаемая функцией
BITMAP bitmap;
HBITMAP hBitmap;
int GetObject(
(HGDIOBJ)hBitmap,
sizeof(BITMAP),
&bitmap
);
Вобщем-то, мне нужно каким-нибудь образом получить информацию о содержимом окна, причем в таком виде, чтобы ее можно было потом передать по сети и воспроизвести на другом компьтере. Насколько я понимаю, если использовать DIB, то это будет медленно, хотя может быть я ошибаюсь...
Скорее проблемы со скоростью будут из-за передачи, так 1024x768x32 примерно 4Mb, а за функции не волнуйся - будут работать нормально.
Может быть можно как-то определять изменившиеся части изображения и передавать только их? Это сэкономит трафик.
Можно, но только обработка этого полностью ложиться на программу. Ведь не зря же в Windows есть функция InvalidateRect, которая помещает прямоугольник как требующий перерисовки. Однако я не знаю как быть с программами, которые получили постояный DC - что-то типа такого:
HDC hDC = ::GetDC(GetSafeHwnd());
И потом рисует там.
Так что по идее придется писать вроде этого:
class CScreenPart
{
CRect m_rcPart;
CBitmap m_bmPart;
};
class CMyWindow : public CWnd
{
CBitmap m_bmPrevScreen;
void GetChangedParts(CBitmap& bmNewScreen, CTypedPtrList<CPtrList, CScreenPart*>& list);
void SendChangedParts(CTypedPtrList<CPtrList, CScreenPart*>& list);
HBITMAP GetScreen(void);
void OnTimer(UINT)
{
CTypedPtrList<CPtrList, CScreenPart*> list;
CBitmap bmNewScreen(GetScreen());
GetChangedParts(bmNewScreen, list);
SendChangedParts(list);
m_bmPrevScreen.DeleteObject();
m_bmPrevScreen = bmNewScreen;
}
};
Правда скорее всего отсылать придется в отдельном потоке.
HBITMAP, на сколько я понимаю можно получить с пом. CreateCompatibleBitmap только при создании контекста в памяти. Мне желательно на прямую без всяких промежуточных контекстов по контексту окна получить HBITMAP и потом соотв. BITMAP. Как это сделать?
Не обязательно создавать каждый раз новую. Достаточно предусмотреть какое-то определенное деление, например, 16x16 частей, создать для каждой части bitmap и использовать их во время работы. Функция bitblt работает быстро, например, класс CImage использует один dc на все экземпляры и не создает каждый раз контекст в памяти, так что можно воспользоваться им.
есть окно (десктоп в моем случае);
можно получить контекст этого окна;
нужно переслать содержимое этого окна по сети, его можно достать из структуры BITMAP, которую можно получить, имея HBITMAP;
соотв. HBITMAP получается только после CreateCompatibleBitmap (битовая карта для контекста в памяти);
соотв. единственный способ получить содержимое окна в готовом для передачи виде - создать контекст в памяти, создать для него битовую карту, использовать BitBlt вроде:
BitBlt(hMemDC,0,0,WND_WIDTH,WND_HEIGHT,hWndDC,0,0,..)
после этого, как вы сказали, использовать GetObject, чтобы получить BITMAP, который уже можно пересылать.
Непонятны две вещи:
1)можно ли избежать промежуточного контекста в памяти, и напрямую "выдирать" BITMAP из окна?
2)как на приемнике по имеющейся структуре BITMAP восстановить изображение в окне (какие функции)?
Благодарю за ответы, но дело в том, что я очень плохо знаю MFC, поэтому проще все делать на WinAPI (IMHO). Ваш пример я тоже не очень понял ввиду вышесказанного. На сколько я понимаю, дело обстоит так:
есть окно (десктоп в моем случае);
можно получить контекст этого окна;
нужно переслать содержимое этого окна по сети, его можно достать из структуры BITMAP, которую можно получить, имея HBITMAP;
соотв. HBITMAP получается только после CreateCompatibleBitmap (битовая карта для контекста в памяти);
соотв. единственный способ получить содержимое окна в готовом для передачи виде - создать контекст в памяти, создать для него битовую карту, использовать BitBlt вроде:
BitBlt(hMemDC,0,0,WND_WIDTH,WND_HEIGHT,hWndDC,0,0,..)
после этого, как вы сказали, использовать GetObject, чтобы получить BITMAP, который уже можно пересылать.
Непонятны две вещи:
1)можно ли избежать промежуточного контекста в памяти, и напрямую "выдирать" BITMAP из окна?
2)как на приемнике по имеющейся структуре BITMAP восстановить изображение в окне (какие функции)?
1)не знаю, тут надо покопаться
2)надо передавать не структуру
typedef struct tagBITMAP {
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP, *PBITMAP;
Так как указатель на удаленном компьютере ничего не даст, а данные из bmBits, размеры и т.д.
Создание нового DC - 64 кБайта из кучи GDI, так что не обращай внимание. Создай одну на всю программу копию экрана, передавай его изменившиеся части, затем на приемнике отображай переданные данные на экране с помощью BitBlt
Это я понимаю :)
Да, но перед BitBlt надо как-то BITMAP и все, что с ним связано поместить в содержимое промежуточной поверхности приемника, короче обратная операция по отношению к GetObject на компе-источнике.
И еще, BITMAP, на сколько я понимаю, содержит информацию в формате Device Dependent Bitmap, а он зависит от конкретного устройства. Так вот, на сколько серьезно зависит? Различается от производителя к производителю или формат может быть различным даже в пределах карт от одной фирмы? Скажем, у меня на обоих компах nVidia, будет ли изображение адекватно воспроизводиться?
Это я понимаю :)
Да, но перед BitBlt надо как-то BITMAP и все, что с ним связано поместить в содержимое промежуточной поверхности приемника, короче обратная операция по отношению к GetObject на компе-источнике.
И еще, BITMAP, на сколько я понимаю, содержит информацию в формате Device Dependent Bitmap, а он зависит от конкретного устройства. Так вот, на сколько серьезно зависит? Различается от производителя к производителю или формат может быть различным даже в пределах карт от одной фирмы? Скажем, у меня на обоих компах nVidia, будет ли изображение адекватно воспроизводиться?
Простой путь:
создаешь на приемнике одну bitmap такого же размера, при приеме данных копируешь bmBits и все.
Различия DC, влияющие на bitmap:
BITSPIXEL
PLANES
Для 24 бит это соответсвенно 8 и 3
Я обычно создаю буфер для отображения так
CImage m_iImage;
m_iImage.Create(cx, cy,
::GetDeviceCaps(hWindowDC, BITSPIXEL) * ::GetDeviceCaps(hWindowDC, PLANES))
При несовпадении форматов скорее всего будет просто аппроксимация изображения существующими цветами. Чтобы переделать в другой формат - функции GetDIBits, SetDIBits.
В общем я бы делал так:
Создаем массив
CImage m_2darrayParts[16][16];
Посылаем приемнику Image с двумя переменным i,j - индексы в массиве. Приемник вычисляет позицию на экране. С помощью функции Draw из CImage отображаем в соответствующую область.