Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Звук, графика и видео > Быстрая графика


Автор: костярик 20.7.2006, 17:12
Здравствуйте!
У меня вопрос. Я часто работаю с графикой, обычно использую класс TBitmap (ScanLine). Интересно, есть ли другой, более быстрый способ вывести изображение на экран, не используя АПИ-шки типа OpenGL или DirectX. 

Автор: Mechanic 20.7.2006, 22:46
http://www.g32.org/graphics32/index.html#Downloads, например..  smile  

Автор: Alexeis 20.7.2006, 23:02
Цитата(костярик @  20.7.2006,  17:12 Найти цитируемый пост)
Я часто работаю с графикой, обычно использую класс TBitmap (ScanLine)

Этот метод позволяет получить прямой доступ к памяти битовой карты изображения, что может быть быстрее этого? Другое дело отображение на экран! 

Автор: костярик 21.7.2006, 05:11
Цитата
Другое дело отображение на экран!


В том-то и дело. Как вывести массив цветов на экран, наиболее быстрым образом?  

Автор: Kesh 21.7.2006, 09:35
BitBlt и FastBlt 

Автор: Alexeis 21.7.2006, 12:01
PatBlt 

Автор: Alexeis 21.7.2006, 13:29
Kesh, функция FastBlt ни где не описана, если можно - ее параметры и название динамической библиотеки. (полностью ее прототип) 

Автор: Kesh 21.7.2006, 15:11
Пардон, ошибся... не FastBlt, а MaskBlt

Добавлено @ 15:15 
Хотя все-таки нет... Склероз пока еще не убил мой мозг...

Код

function BltFast(dwX: DWORD; 
  dwY: DWORD; 
  lpDDSrcSurface: IDirectDrawSurface4; 
  lpSrcRect: PRect; 
  dwTrans: DWORD): HResult;

Но работает он соответственно с DDSurface'ами... 

Автор: Alexeis 21.7.2006, 19:34
Цитата(Kesh @  21.7.2006,  15:11 Найти цитируемый пост)
Но работает он соответственно с DDSurface'ами...


костярик, просил 
Цитата(костярик @  20.7.2006,  17:12 Найти цитируемый пост)
 не используя АПИ-шки типа OpenGL или DirectX. 
 - это все таки  directDraw, я понял что он хотел выводить при помощи GDI
 

Автор: костярик 21.7.2006, 20:36
Цитата

я понял что он хотел выводить при помощи GDI

Правильно, надо вывести массив цветов при помощи GDI. Тут вот BitBlt посоветовали, он копирует из одного контекста в другой. А как загнать цвета в контекст? 

Автор: Alexeis 21.7.2006, 21:58
В него не нужно загонять просто
Код

var
bmp : tbitmap;
begin
...................
  BitBlt(bmp.canvas.handle,.....)

canvas.handle - это и есть контекст 

Автор: костярик 22.7.2006, 06:56
Цитата

canvas.handle - это и есть контекст 

Да, но у меня есть только массив из троек байтов (R, G, B) для каждого цвета. У меня еще нет канвы. Как же мне имеющийся массив загнать в контекст. TBitmap я не использую, а то, понятно, можно было б через скан лайн. Есть ли такая возможность? 

Автор: Alexeis 22.7.2006, 16:59
CreateBitmapInderect - позволяет создать битмап в памяти, затем для него можно создать свой контекст. Он называется, что-то вроде контекста для памяти. Специальный контекст, не связанный ни с каким дескриптором окна. Это чистой воды Api операции. Я попробую поискать пример - когдато делал. 

Автор: Alexeis 22.7.2006, 17:27
А вот и пример нашел
Код

var
  Form1: TForm1;
  bmp  : hBitMap;
  hBDC : HDC;
  bSize: TSize;
  r : integer;
  BInfo : ^BITMAP;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  bmp  := LoadImage(0, '049035.bmp', IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  getMem(BInfo, sizeOf(BITMAP));
  GetObject(bmp, sizeOf(BITMAP), BInfo);
  hBDC := CreateCompatibleDC(form1.Canvas.Handle);
  SelectObject(hBDC, bmp);
  BitBlt(form1.Canvas.Handle, 0, 0, BInfo.bmWidth, BInfo.bmHeight, hBDC, 0, 0, SRCCOPY);
end;


LoadImage - Загружает картинку с диска
getMem(BInfo, sizeOf(BITMAP)); - выделение памяти под структуру, которая будет использоваться для функции  получения информации о битмапе - GetObject(bmp,sizeOf(BITMAP), BInfo);

  hBDC := CreateCompatibleDC(form1.Canvas.Handle); - создает новый контекст для структур в памяти (memory device context) т.е. для битовой маски совместимый с графическим адаптером. ипользуя в качестве образца контекст формы (подойдет любое окно).

SelectObject(hBDC, bmp); - связывает дескриптор битмапа с полученным контекстом

 BitBlt(form1.Canvas.Handle, 0, 0, BInfo.bmWidth, BInfo.bmHeight, hBDC, 0, 0, SRCCOPY); - рисуем все что есть в контексте битмапа на контекст формы. 
 

Автор: Alexeis 22.7.2006, 17:47
А теперь рассмотрим как можно создать битмап в памяти не загружая его с диска
В начале заполняем информационную структуру bmiInfo: BITMAPINFO;

Код

  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота    
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1    
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель    
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компрессии    
  bmiInfo.bmiHeader.biSizeImage:=0;        // размер не знаем, ставим в ноль    
  bmiInfo.bmiHeader.biXPelsPerMeter:=2834; // пикселей на метр, гор.    
  bmiInfo.bmiHeader.biYPelsPerMeter:=2834; // пикселей на метр, верт.    
  bmiInfo.bmiHeader.biClrUsed:=0;          // палитры нет, все в ноль    
  bmiInfo.bmiHeader.biClrImportant:=0;     // то же    
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структруы    


Затем создаем собственно сам битмап
Код

  bmDIB := CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,
                            Pointer(lpBits), 0, 0);

На этом этапе нам уже нужен контекст DC (как его получить писал выше)
(lpBits : PRGBTriple;) - указатель на карту.

Однако полученный битмап является DDB - Device Dependent Bitmap (не смотря на то что функция называется CreateDIBSection), что не позволяет нам н напрямую обращатся к битам карты. Поэтому мы должны его сконвертировать в DIB. Для этого заново готовим структуру  bmiInfo
Код

  bmiInfo.bmiHeader.biWidth:=W;            // ширина    
  bmiInfo.bmiHeader.biHeight:=H;           // высота    
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1    
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель    
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компресси
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структуры


И получаем битмап в нужном нам формате DIB_RGB_COLORS
Код
 
GetDIBits(DC, Bitmap, 0, H - 1, lpBits, bmiInfo, DIB_RGB_COLORS);


В параметре  lpBits получаем - адрес первого байта заветной битовой карты куда можно записывать битовый масив с учетом выравнивания каждой строки битмапа на границу 4-х байт (как в файле bmp) 

И чтоб в этом убедится
можно протестировать пикселы выводя вручную их из памяти на форму (это медленный вариант - только для тестирования smile )
Код

  p := Pointer(lpBits); //это мой вариант с выводом на форму

  For i := 1 to 200
  do
    for j := 1 to 250
    do
     form1.Canvas.Pixels[i, 250 - j] := RGB(p^[j,i].r, p^[j,i].g, p^[j,i].b);
 

Автор: костярик 24.7.2006, 10:40
Большое спасибо вам за инфу -- как раз то, что надо. Только у меня так и не получилось обратиться к массиву битов. Ваша конструкция p^[j,i].r работать, понятно, не будет: p -- это ж у нас указатель, к тому же нетипизированный, а не массив. Тут надо хитрее поступать. Я пытался работать с массивом цветов двумя способами: как с массивом байтов и как с массивом элементов, типа TRGBTriple. Для этого пришлось объявить новый тип:

  TRGBTripleArray = array [1..10000] of TRGBTriple;
  PRGBTripleArray = ^TRGBTripleArray;

Но это все равно не помогло. На экране ничего не рисуется, даже черный квадрат. Хотя bitblt возвращает true. Вот полностью весь код:
Код

  bmiInfo.bmiHeader.biWidth:=W;
  bmiInfo.bmiHeader.biHeight:=H;
  bmiInfo.bmiHeader.biPlanes:=1;
  bmiInfo.bmiHeader.biBitCount:=24;
  bmiInfo.bmiHeader.biCompression:=BI_RGB;
  bmiInfo.bmiHeader.biSizeImage:=0;
  bmiInfo.bmiHeader.biXPelsPerMeter:=2834;
  bmiInfo.bmiHeader.biYPelsPerMeter:=2834;
  bmiInfo.bmiHeader.biClrUsed:=0;
  bmiInfo.bmiHeader.biClrImportant:=0;
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader);
  DC := CreateCompatibleDC(form1.Canvas.Handle);
  bmDIB := CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,
                            Pointer(lpBits), 0, 0);
  bmiInfo.bmiHeader.biWidth:=W;
  bmiInfo.bmiHeader.biHeight:=H;
  bmiInfo.bmiHeader.biPlanes:=1;
  bmiInfo.bmiHeader.biBitCount:=24;
  bmiInfo.bmiHeader.biCompression:=BI_RGB;
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader);
  GetDIBits(DC, bmDIB, 0, H - 1, lpBits, bmiInfo, DIB_RGB_COLORS);
  ellipse(dc, 1, 1, 5, 7); // рисуем эллипс
  bitblt(Canvas.Handle, 0, 0, w, h, dc, 0, 0, SRCCOPY); // пытаемся его отобразить
                                                        // на форме-- не работает!                
  For i := 1 to w do
    for j := 1 to h do
    begin
       // пытаемся обратиться к битам
      PByteArray(lpBits)[(j*w+i)*3+1] := i; // 1.способ
      PRGBTripleArray(lpbits)[j*w+i].rgbtRed :=   random(256); // 2 способ
      PRGBTripleArray(lpbits)[j*w+i].rgbtGreen := random(256); //
      PRGBTripleArray(lpbits)[j*w+i].rgbtBlue :=  random(256); //
    end;
  bitblt(Canvas.Handle, 10, 10, w, h, dc, 0, 0, SRCCOPY);  //  отображаем изменения -- не работает
 

Автор: Alexeis 24.7.2006, 11:12
Цитата(костярик @  24.7.2006,  10:40 Найти цитируемый пост)
Только у меня так и не получилось обратиться к массиву битов. Ваша конструкция p^[j,i].r работать, понятно, не будет: p -- это ж у нас указатель

Эта констукция не может не работать, я ее проверял, на конкретном примере, не работает что-то другое!

Добавлено @ 11:25 
Вот рабочий пример!  

Автор: Alexeis 24.7.2006, 11:43
А вот вариант почти как вам надо!
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    btn1 : TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


var
  Form1: TForm1;
  b    : TBitmap;


implementation

{$R *.dfm}
          {$O-}
type
  Trgb = packed record
                 b,g,r : byte
                 end;

  arr = array[1..250,1..200] of Trgb;

procedure TForm1.btn1Click(Sender: TObject);
var
 DC : hDC;
 Bitmap : HBITMAP;
 p : ^arr;        //можно удалить
 i, j : integer;  //можно удалить
 bmInfo: TDIBSection;           // структура BITMAP WinAPI
 W, H : Integer;                 // высота и ширина растра
 bmDIB: hBitmap;                // дискрептор независимого растра
 bmiInfo: BITMAPINFO;           // структура BITMAPINFO WinAPI
 lpBits: PRGBTriple;            // указатели на структуры RGBTRIPLE WinAPI

begin
  Bitmap := LoadImage(0,
      'IMG.bmp',
      IMAGE_BITMAP,
      0,
      0,
      LR_DEFAULTSIZE or
      LR_LOADFROMFILE);

  GetObject(Bitmap, SizeOf(bmInfo), @bmInfo);

  DC := CreateCompatibleDC(form1.Canvas.Handle);
  SelectObject(DC, Bitmap);

  W := bmInfo.dsBm.bmWidth;
  H := bmInfo.dsBm.bmHeight;

  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота    
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель    
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компрессии
  bmiInfo.bmiHeader.biSizeImage:=0;        // размер не знаем, ставим в ноль    
  bmiInfo.bmiHeader.biXPelsPerMeter:=2834; // пикселей на метр, гор.
  bmiInfo.bmiHeader.biYPelsPerMeter:=2834; // пикселей на метр, верт.    
  bmiInfo.bmiHeader.biClrUsed:=0;          // палитры нет, все в ноль
  bmiInfo.bmiHeader.biClrImportant:=0;     // то же    
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структруы
  bmDIB := CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,
  Pointer(lpBits), 0, 0);
  //создаем независимый растр WxHx24, без палитры, в указателе lpBits получаем    
  //адрес первого байта этого растра. bmDIB - дискрептор растра
  //заполняем первые шесть членов BITMAPINFO для передачи в GetDIBits    
  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота    
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель    
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компресси
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структуры    
  GetDIBits(DC, Bitmap, 0, H - 1, lpBits, bmiInfo, DIB_RGB_COLORS);

  p := Pointer(lpBits); //это мой вариант с выводом на форму

  ellipse(dc, 1, 1, 50, 17);
  BitBlt(Canvas.Handle, 0, 0, w, h, DC, 0, 0, SRCCOPY);
end;


Добавлено @ 11:53 
Вот тут вообще без загрузки изображения
+ Вывод при помощи bitblt
+ Вывод попиксельно из памяти указанной в p из того же битмапа
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    btn1 : TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


var
  Form1: TForm1;
  b    : TBitmap;


implementation

{$R *.dfm}
          {$O-}
type
  Trgb = packed record
                 b,g,r : byte
                 end;

  arr = array[1..250,1..200] of Trgb;

procedure TForm1.btn1Click(Sender: TObject);
var
 DC : hDC;
 Bitmap : HBITMAP;
 p : ^arr;        //можно удалить
 i, j : integer;  //можно удалить
 bmInfo: TDIBSection;           // структура BITMAP WinAPI
 W, H : Integer;                 // высота и ширина растра
 bmDIB: hBitmap;                // дискрептор независимого растра
 bmiInfo: BITMAPINFO;           // структура BITMAPINFO WinAPI
 lpBits: PRGBTriple;            // указатели на структуры RGBTRIPLE WinAPI

begin
  DC := CreateCompatibleDC(form1.Canvas.Handle);

  W := 200;
  H := 250;

  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компрессии
  bmiInfo.bmiHeader.biSizeImage:=0;        // размер не знаем, ставим в ноль
  bmiInfo.bmiHeader.biXPelsPerMeter:=2834; // пикселей на метр, гор.
  bmiInfo.bmiHeader.biYPelsPerMeter:=2834; // пикселей на метр, верт.
  bmiInfo.bmiHeader.biClrUsed:=0;          // палитры нет, все в ноль
  bmiInfo.bmiHeader.biClrImportant:=0;     // то же
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структруы
  bmDIB := CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,
  Pointer(lpBits), 0, 0);

  SelectObject(DC, bmDIB);

  //создаем независимый растр WxHx24, без палитры, в указателе lpBits получаем
  //адрес первого байта этого растра. bmDIB - дискрептор растра
  //заполняем первые шесть членов BITMAPINFO для передачи в GetDIBits
  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компресси
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структуры
  GetDIBits(DC, Bitmap, 0, H - 1, lpBits, bmiInfo, DIB_RGB_COLORS);

  p := Pointer(lpBits); //это мой вариант с выводом на форму

  ellipse(dc, 1, 1, 140, 170);
  BitBlt(Canvas.Handle, 220, 0, w, h, DC, 0, 0, SRCCOPY);

  For i := 1 to 200 do
  for j := 1 to 250 do
   form1.Canvas.Pixels[i, 250 - j] := RGB(p^[j,i].r, p^[j,i].g, p^[j,i].b);
end;



end.

 

Автор: welt 14.6.2009, 01:42
Alexeis, 
Ну я немного поправлю. Все-таки нехорошо, когда для разных картинок надо 
каждый раз вручную задавать границы массива
Код

 arr = array[1..250,1..200] of Trgb;


Я доработал код таким образом:

Код


type
  Trgb = packed record
   b,g,r : byte
   end;
const
  MaxPixelCountA = MaxInt div Sizeof(TRGB);
type
  arr = array [0..MaxPixelCountA - 1] of TRGB;

procedure TForm1.btn1Click(Sender: TObject);
var
 DC : hDC;
 Bitmap : HBITMAP;
 p : ^arr;        //можно удалить
 i, j : integer;  //можно удалить
 bmInfo: TDIBSection;           // структура BITMAP WinAPI
 W, H : Integer;                 // высота и ширина растра
 bmDIB: hBitmap;                // дискрептор независимого растра
 bmiInfo: BITMAPINFO;           // структура BITMAPINFO WinAPI
 lpBits: PRGBTriple;            // указатели на структуры RGBTRIPLE WinAPI

var
hBDC : HDC;

begin
  DC := Form1.Canvas.Handle; {DC := GetDC(Handle)
                              Handle - окна вывода(или любого)}
  Bitmap := LoadImage(0,
      'img.bmp',
      IMAGE_BITMAP,
      0,
      0,
      LR_DEFAULTSIZE or
      LR_LOADFROMFILE);

  GetObject(Bitmap, SizeOf(bmInfo), @bmInfo);
  W := bmInfo.dsBm.bmWidth;
  H := bmInfo.dsBm.bmHeight;
  hBDC := CreateCompatibleDC(dc);
  SelectObject(hBDC, Bitmap);
  BitBlt(dc, 0, 0, w, h, hBDC, 0, 0, SRCCOPY);

  bmiInfo.bmiHeader.biWidth:=W;            // ширина
  bmiInfo.bmiHeader.biHeight:=H;           // высота
  bmiInfo.bmiHeader.biPlanes:=1;           // всегда 1
  bmiInfo.bmiHeader.biBitCount:=24;        // три байта на пиксель
  bmiInfo.bmiHeader.biCompression:=BI_RGB; // без компрессии
  bmiInfo.bmiHeader.biSizeImage:=0;        // размер не знаем, ставим в ноль
  bmiInfo.bmiHeader.biXPelsPerMeter:=2834; // пикселей на метр, гор.
  bmiInfo.bmiHeader.biYPelsPerMeter:=2834; // пикселей на метр, верт.
  bmiInfo.bmiHeader.biClrUsed:=0;          // палитры нет, все в ноль
  bmiInfo.bmiHeader.biClrImportant:=0;     // то же
  bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); // размер структруы
  bmDIB := CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,
  Pointer(lpBits), 0, 0);

  GetMem(p, w*h*sizeof(Trgb));
  GetDIBits(DC, Bitmap, 0, H - 1, lpBits, bmiInfo, DIB_RGB_COLORS);
  p := Pointer(lpBits); //

 {
  For i := 1 to W do  //Вывод на форму
   for j := 1 to H do  //Можно удалить
    form1.Canvas.Pixels[i, H - j] := RGB(p^[i + w * j].r, p^[i + w * j].g, p^[i + w * j].b);
 }
end;


Автор: Alexeis 14.6.2009, 20:56
Цитата(welt @  14.6.2009,  00:42 Найти цитируемый пост)
Ну я немного поправлю. Все-таки нехорошо, когда для разных картинок надо 
каждый раз вручную задавать границы массива

  Главное, что ясна суть, так нагляднее видно что это матрица, а под свою задачу каждый пусть переделывает сам  smile 

Автор: AntonN 14.6.2009, 21:30
поправил спустя почти 3 года  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)