|
Вопрос # 1 716/ вопрос открыт / |
|
Здравствуйте, эксперты!
Подскажите пожалуйста как сохранить в файл .bmp то, что нарисовано в окне формы при помощи OpenGL. Другими словами - воспроизводится некая анимация посредством OpenGL и в определенный момент нужно захватить то, что отрисовано в окне в графический файл. Стандартными средствами решить вопрос не получилось, поэтому очень расчитываю на Вашу помощь.
 |
Вопрос задал: Сергей П. (статус: Посетитель)
Вопрос отправлен: 23 июня 2008, 13:20
Состояние вопроса: открыт, ответов: 2.
|
Ответ #1. Отвечает эксперт: Feniks
Здравствуйте, Сергей П.!
Попробуйте такой вот простой пример:
// gr - объект, на канве которого рисуется с помощью OpenGL
bt := TBitmap.Create;
with bt do
begin
Width := gr.Width;
Height := gr.Height;
Canvas.CopyRect(ClientRect, gr.Canvas, gr.ClientRect);
SaveToFile('с:\bt.bmp');
Free;
end;
Желаю удачи.
 |
Ответ отправил: Feniks (статус: Бакалавр)
Время отправки: 23 июня 2008, 14:43
|
Ответ #2. Отвечает эксперт: Вадим К
Здравствуйте, Сергей П.!
Результат конвертирования в приложении. Вроде должно работать. Но помните, что это будет работать только если выставлен 24 битный цвет при инициализации OpenGl.
Приложение: Переключить в обычный режим- procedure Screens(contr:TWinControl);
- var
- rect :TRect;
- cx, cy:integer;
- bitmap:HBITMAP;
- pDC, MemDC:HDC;
- NbBytes:integer;
- pPixelData:^byte;
- header:BITMAPINFOHEADER;
- _handle:THandle;
- pData:PChar;
- begin
-
- rect := contr.ClientRect;
- cx := rect.Right;
- cy := rect.Bottom;
-
-
-
- cx := cx - cx mod 4;
-
- pDC := GetDC(contr.Handle);
-
- MemDC := CreateCompatibleDC(pDC);
- bitmap := CreateCompatibleBitmap(pDC,cx,cy);
- SelectObject(MemDC, bitmap);
-
-
- NbBytes := 3 * cx * cy;
- GetMem(pPixelData, NbBytes);
-
-
- glReadPixels(0,0,cx,cy,GL_RGB,GL_UNSIGNED_BYTE,pPixelData);
-
-
- header.biWidth := cx;
- header.biHeight := cy;
- header.biSizeImage := NbBytes;
- header.biSize := 40;
- header.biPlanes := 1;
- header.biBitCount := 3 * 8; // RGB
- header.biCompression := 0;
- header.biXPelsPerMeter := 0;
- header.biYPelsPerMeter := 0;
- header.biClrUsed := 0;
- header.biClrImportant := 0;
-
-
- _handle := GlobalAlloc (GHND,sizeof(BITMAPINFOHEADER) + NbBytes);
- if(_handle <> 0) then
- begin
-
- pData := GlobalLock(_handle);
-
- CopyMemory(pData,@header,sizeof(BITMAPINFOHEADER));
- CopyMemory(pData+sizeof(BITMAPINFOHEADER),pPixelData,NbBytes);
-
- GlobalUnlock(_handle);
-
-
- OpenClipboard(Application.Handle);
- EmptyClipboard();
- SetClipboardData(CF_DIB,_handle);
- CloseClipboard();
- end;
-
-
- DeleteDC(MemDC);
- DeleteObject(Bitmap);
- FreeMem(pPixelData);
- end;
- procedure TForm3.Button1Click(Sender: TObject);
- begin
-
- Screens(Panel1);
- end;
 |
Ответ отправил: Вадим К (статус: Академик)
Время отправки: 23 июня 2008, 22:21
Оценка за ответ: 5
|
Мини-форум вопроса
Всего сообщений: 12; последнее сообщение — 26 июня 2008, 01:49; участников в обсуждении: 2.
|
Сергей П. (статус: Посетитель), 23 июня 2008, 15:22 [#1]:
Спасибо за отклик, но не захватывает кадр такая конструкция. Уже весь инет облазил решения не могу найти. Хотел попробывать то, которое на http://sources.ru/cpp/cpp_3d.shtml , но там не на Delphi.
|
|
Сергей П. (статус: Посетитель), 23 июня 2008, 17:23 [#2]:
Или может кто знает как на Delphi следующие 3 фрагмента перевести:
// Создаём bitmap и устанавливаем его в контексте устройства
CBitmap bitmap;
CDC *pDC = GetDC();
CDC MemDC;
ASSERT(MemDC.CreateCompatibleDC(NULL));
ASSERT(bitmap.CreateCompatibleBitmap(pDC,size.cx,size.cy));
MemDC.SelectObject(&bitmap);
// Рассчитываем кол-во необходимых байт для изображения
int NbBytes = 3 * size.cx * size.cy;
unsigned char *pPixelData = new unsigned char[NbBytes];
char *pData = (char *)
|
|
Сергей П. (статус: Посетитель), 23 июня 2008, 23:27 [#3]:
Огромное спасибо ! Заработало с 1-го раза. Единственный нюанс - цвета изменились, красный стал похож на что-то ближе к синему и .т.п. Буду добивать дальше.
|
|
Вадим К (статус: Академик), 24 июня 2008, 10:01 [#4]:
Ну с цветами я предупреждал. Покажите код, как инициализируете OpenGl. Вполне возможно, что вы используете 16битный цвет или другой.этот код будет работать нормально только для 24битного с правильной последовательностью бит.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Сергей П. (статус: Посетитель), 24 июня 2008, 11:03 [#5]:
procedure TForm1.SetDCPixelFormat;
var
PixelFormat: integer;
PFD: TPixelFormatDescriptor;
begin
FillChar(PFD,SizeOf(PFD),0);
with PFD do
begin
nSize:=SizeOf(PFD);
nVersion:=1;
dwFlags:=PFD_DRAW_TO_WINDOW or
PFD_SUPPORT_OPENGL or
PFD_DOUBLEBUFFER;
iPixelType:=PFD_TYPE_RGBA;
cColorBits:=24;
cDepthBits:=32;
iLayerType:=PFD_MAIN_PLANE;
end;
PixelFormat:=ChoosePixelFormat(DC,@PFD);
if PixelFormat=0 then
Raise
Exception.Create('Формат пикселей не поддерживается');
SetPixelFormat(DC,PixelFormat,@PFD);
DescribePixelFormat(DC,PixelFormat,SizeOf(TPixelFormatDescriptor),PFD);
with PFD do
if ((dwFlags and PFD_DRAW_TO_WINDOW)=0) or
((dwFlags and PFD_SUPPORT_OPENGL)=0) or
((dwFlags and PFD_DOUBLEBUFFER)=0) or
(iPixelType<>PFD_TYPE_RGBA) or
(cColorBits<16) then
Raise
Exception.Create('Выбран не подходящий формат пикселей');
end;
|
|
Сергей П. (статус: Посетитель), 24 июня 2008, 12:57 [#6]:
Насчет 24 бит. С этим всё в порядке помоему. А вот кадр захватывается в формате BGR, а отображается в RGB, вот и получается искажение цвета.
|
|
Вадим К (статус: Академик), 24 июня 2008, 23:31 [#7]:
Ну тогда, если с битностью нет проблем, то никто не мешает переставить байты при записи. пробежаться циклом и всё. Хотя вроде можно флажок для HBITMAP выставить и указать порядок цветов.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Сергей П. (статус: Посетитель), 25 июня 2008, 17:54 [#8]:
Да, если б ещё знать где этот флажок выставить, а то все перестановки очень процесс замедляют. В любом случае ещё раз спасибо, а то бы дальше не смог двинуться.
|
|
Сергей П. (статус: Посетитель), 25 июня 2008, 20:08 [#9]:
А вот здесь не подскажете ?
Получил я свой файл в конце концов.
Btm.LoadFromClipBoardFormat(CF_BITMAP,Clipboard.GetAsHandle(CF_BITMAP), 0);
Хотел BRG в RGB так переделать:
procedure TForm1.BGRtoRGB();
var
Line : pByteArray;
begin
Line:=PByteArray(Btm.Scanline[0]);
asm
pushad
mov edx, 3BFh // 960-1 Высота рисунка
@repy:
mov ecx, 4AFh // 1200-1 Ширина рисунка
@repx:
mov al, byte ptr[Line]
mov ah, byte ptr[Line+2]
mov byte ptr[Line+2], al
mov byte ptr[Line], ah
add Line,3
dec ecx
jge @repx
dec edx
jge @repy
popad
end;
end;
Но в результате ничего не меняется, подозреваю что указатель (Line) использую и передаю неправильно.
Заранее извиняюсь если вопрос наивен, давно этим не занимался (особенно вставками на ассемблере).
|
|
Вадим К (статус: Академик), 25 июня 2008, 21:39 [#10]:
Вы решили, что используя асемблер, сделаете быстрее и качественее? Напишите для начала это всё на самом обычном паскале. Это раз.
Во вторых, использовать Scanline незачем. у вас ведь есть массив пикселей. Вот его и надо проработать перед записью.
Опять пишу на глаз, но должно работать и принимайте код как идею.
//pPixelData:^byte; <-- было
pPixelData:Pbyte;
...
p1,p2:Pbyte; te:byte;
c:DWORD;
begin
....
// Копируем из OpenGL
glReadPixels(0,0,cx,cy,GL_RGB,GL_UNSIGNED_BYTE,pPixelData);
//Вставленный код
p1 := pPixelData;//указатель на начало данных
p2 := p1; inc(p2,2);//сдвиним на 2 байта
for c := 0 to cx * cy - 1 do //пробежимся по всем пикселям в массиве
begin
//обмен
te := p1^;
p1^ := p2^;
p2^ := te;
//сдвинем указатели на 3 байта вперёд.
inc(p1,3);
inc(p2,3);
end;
// Заполняем заголовок
....
Галочка "подтверждения прочтения" - вселенское зло.
|
|
Сергей П. (статус: Посетитель), 25 июня 2008, 23:47 [#11]:
Всё работает , спасибо. На ассеммблере вставочку решил сделать потому что весь процесс захвата кадра занимает в моём случае около 0.5 сек. По моему это долго (есть же программы которые видео захватывают и пользователь этого даже не замечает). Я думал что почти всё это время уходит на перестановку R<->B, но оказывается что это выполняется почти мгновенно, а оно (0.45 сек.) уходит на одну строчку -
glReadPixels(0,0,cx,cy,GL_RGB,GL_UNSIGNED_BYTE,pPixelData);
В данной задаче скорость выполнения мне не важна, поэтому в принципе вопрос можно считать закрытым (а можно и пообсуждать ещё). Если возможно, подскажите в каком направлении поискать чтоб сорость уменьшить. Может ссылочка на материал какой-нибудь по этой теме есть ?
P.S. Очень рад что зашел на этот сайт, и получил от Вас ответы. Спасибо.
|
|
Вадим К (статус: Академик), 26 июня 2008, 01:49 [#12]:
Вот вот. перед тем, как начинать что то оптимизировать и переписывать на ассемблере, вначале надо пройтись профайлером и посмотреть, что тормозит. Для простых эксперементов обычно хватает функции GetTickCount + запись в файл/массив.
То, что тормозит функция чтения пикселей - я даже не сомневаюсь, так как попиксельный доступ - самая медленная операция в данном контексте. С DirectX еще хуже - там нет операции доступа к пикселям в явном виде, но там другие методы.
P.S. Заходите ещё, задавайте умные вопросы, приводите других.
Галочка "подтверждения прочтения" - вселенское зло.
|
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|