При работе с OpenGL в Delphi XE2 может возникнуть необходимость сохранить содержимое буфера кадра в файл bitmap. В этом материале мы рассмотрим решение данной задачи и способы устранения возможных проблем.
Базовый подход
Для чтения содержимого буфера кадра в Delphi XE2 можно использовать функцию glReadPixels. Ниже приведен пример кода, который демонстрирует базовый подход к решению данной задачи:
procedure TForm1.saveBtnClick(Sender: TObject);
var
srcBitmap: TBitmap;
pixels: array of GLubyte;
dimensions: array [0 .. 3] of Integer;
MS: TMemoryStream;
I: Integer;
begin
if SaveDialog1.Execute then
begin
srcBitmap := TBitmap.Create;
srcBitmap.PixelFormat := pf24bit;
MS := TMemoryStream.Create;
glGetIntegerv(GL_VIEWPORT, @dimensions);
srcBitmap.Width := dimensions[2];
srcBitmap.Height := dimensions[3];
SetLength(pixels, dimensions[2] * dimensions[3] * 3);
glReadPixels(0, 0, dimensions[2], dimensions[3], GL_RGB, GL_UNSIGNED_BYTE, @pixels);
ErrorHandler;
MS.Write(pixels, dimensions[2] * dimensions[3] * 3);
srcBitmap.LoadFromStream(MS);
Edit2.Text := SaveDialog1.FileName;
srcBitmap.SaveToFile(Edit2.Text);
MS.Free;
srcBitmap.Free;
end;
end;
Решение проблемы со Stack Overflow
При работе с большими размерамиviewport может возникнуть ошибка Stack Overflow. Это происходит из-за того, что функция glReadPixels считывает содержимое буфера кадра в оперативную память, а не в основную память. В результате, если размер буфера кадра превышает размер доступной оперативной памяти, происходит переполнение стека.
Для решения этой проблемы можно использовать буферы обмена (FBO - Framebuffer Objects) или буферы рендеринга (PBuffer). При использовании этих технологий содержимое буфера кадра можно считывать непосредственно в основную память, что позволяет работать с большими размерами viewport без риска переполнения стека.
Устранение ошибки доступа к памяти
При работе с небольшими размерами viewport может возникнуть ошибка доступа к памяти. Это происходит из-за того, что функция glReadPixels некорректно считывает содержимое буфера кадра в массив pixels.
Для устранения этой ошибки необходимо убедиться в правильности параметров, передаваемых в функцию glReadPixels. В частности, важно правильно указать формат пикселей (GL_RGB или GL_RGBA) и тип данных (GL_UNSIGNED_BYTE).
Альтернативный подход
Существует альтернативный подход к чтению содержимого буфера кадра в Delphi XE2, который использует функции GetMem и SetDIBits. Ниже приведен пример кода, демонстрирующий этот подход:
procedure GetOGL_BMP(var BMP: TBitmap);
var
Dimensions: array [0 .. 3] of Integer;
RGBBits: PRGBQuad;
Pixel: PRGBQuad;
Header: PBitmapInfo;
x, y: Integer;
Temp: Byte;
begin
glGetIntegerv(GL_VIEWPORT, @Dimensions);
GetMem(RGBBits, Dimensions[2] * Dimensions[3] * 4);
glFinish;
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
glReadPixels(0, 0, Dimensions[2], Dimensions[3], GL_RGBA, GL_UNSIGNED_BYTE, RGBBits);
if not Assigned(BMP) then
BMP := TBitmap.Create;
BMP.PixelFormat := pf32Bit;
BMP.Width := Dimensions[2];
BMP.Height := Dimensions[3];
GetMem(Header, SizeOf(TBitmapInfoHeader));
with Header^.bmiHeader do
begin
biSize := SizeOf(TBitmapInfoHeader);
biWidth := Dimensions[2];
biHeight := Dimensions[3];
biPlanes := 1;
biBitCount := 32;
biCompression := BI_RGB;
biSizeImage := Dimensions[2] * Dimensions[3] * 4;
end;
// Rot und Blau vertauschen
Pixel := RGBBits;
for x := 0 to Dimensions[2] - 1 do
for y := 0 to Dimensions[3] - 1 do
begin
Temp := Pixel.rgbRed;
Pixel.rgbRed := Pixel.rgbBlue;
Pixel.rgbBlue := Temp;
inc(Pixel);
end;
SetDIBits(BMP.Canvas.Handle, BMP.Handle, 0, Dimensions[3], RGBBits, TBitmapInfo(Header^), DIB_RGB_COLORS);
FreeMem(Header);
FreeMem(RGBBits);
end;
При использовании этого подхода важно правильно указать формат пикселей (GL_RGBA) и тип данных (GL_UNSIGNED_BYTE) в функции glReadPixels. Кроме того, необходимо учитывать, что при чтении пикселей в формате GL_RGBA порядок цветовых компонент отличается от порядка, используемого в TBitmap. Для устранения этой разницы в коде производится обмен значениями компонент красного и синего цветов.
Выводы
При чтении содержимого буфера кадра OpenGL в Delphi XE2 могут возникнуть проблемы со Stack Overflow и ошибками доступа к памяти. Для решения этих проблем можно использовать буферы обмена или буферы рендеринга, а также правильно указывать параметры в функции glReadPixels. Существует также альтернативный подход, который использует функции GetMem и SetDIBits, но требует правильной настройки параметров и учета особенностей формата GL_RGBA.
Материал описывает процесс чтения буфера кадра OpenGL в основную память в Delphi XE2 для сохранения содержимого в файл bitmap.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.