Sign in Join
Wiki copytemporary

Fast direct pixel access: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Fast direct pixel access

OldNewLine
1+# Introduction
2+
3+Standard graphical LCL components provides Canvas object for common drawing. But most of available graphic routines have some overhead given by universality, platform independence and safety. To achieve best drawing speed it can be useful to use specialized bitmap structures and routines. You can also use existing [[Graphics libraries]].
4+
5+Direct pixel access in libraries is generally slowed-down by more factors:
6+
7+- Coordinate limits checking
8+- Cropping
9+- Facility for automatic image updating and redrawing (notifications and mass operation optimization)
10+- Abstract program constructions as property, static and virtual methods, dynamic two dimensional arrays
11+- Support for multiple pixel formats
12+- Support for multiple platforms
13+- Support for bit-level pixel size and addressing
14+
15+This articles shows how to achieve a custom bitmap structure that is then copied to a TBitmap to render it on the screen.
16+
17+# Pixel format
18+
19+Pixel type can be written in various ways to define its size and internal channel structure. Here you can found some known ways to define pixel type.
20+
21+## Simple ordinal type
22+
23+We can use simple integer pixels which would be faster on 32-bit platform:
24+
25+``` pascal
26+TFastBitmapPixel = Integer;
27+```
28+
29+## Record
30+
31+Or more abstract pixels with separated components:
32+
33+``` pascal
34+TFastBitmapPixelComponents = packed record
35+ Blue: Byte;
36+ Green: Byte;
37+ Red: Byte;
38+ Alpha: Byte;
39+end;
40+```
41+
42+## Subrange
43+
44+It is possible even go further to bit level and define 16-bit RGB pixel used for some LCD displays:
45+
46+``` pascal
47+TFastBitmapPixelComponents16Bit = packed record
48+ Blue: 0..31; // 5 bits
49+ Green: 0..63; // 6 bits
50+ Red: 0..31; // 5 bits
51+end;
52+```
53+
54+## Pointer
55+
56+Pixel can be pointer which would be useful for cases where pixel value itself is rather large or can be compressed somehow.
57+
58+``` pascal
59+TFastBitmapPixelComponentsValue = packed record
60+ Blue: Word;
61+ Green: Word;
62+ Red: Word;
63+ Alpha: Word;
64+end;
65+
66+TFastBitmapPixelComponents = ^TFastBitmapPixelComponentsValue;
67+```
68+
69+## Class
70+
71+Another situation is use of polymorphism of classes. But this would require calling Create and Destroy for each pixel instance and further background processing caused by allocation and deallocation memory on a heap.
72+
73+``` pascal
74+TFastBitmapPixel = class
75+ procedure Clear; virtual;
76+end;
77+
78+TFastBitmapPixelComponents = class(TFastBitmapPixel)
79+ Blue: Word;
80+ Green: Word;
81+ Red: Word;
82+ Alpha: Word;
83+ procedure Clear; override;
84+end;
85+
86+TFastBitmapPixelByte = class(TFastBitmapPixel)
87+ Value: Byte;
88+ procedure Clear; override;
89+end;
90+```
91+
92+## Variant parts in record
93+
94+Notable implementation of the pixel type is Graphics32 library which define pixel using composed record type with case construction to union access with different methods to the same pixel data. There four ways to access pixel data and its internal structure defined by this type:
95+
96+``` pascal
97+TColor32 = type Cardinal;
98+
99+TColor32Component = (ccBlue, ccGreen, ccRed, ccAlpha);
100+
101+TColor32Entry = packed record
102+ case Integer of
103+ 0: (B, G, R, A: Byte);
104+ 1: (ARGB: TColor32);
105+ 2: (Planes: array[0..3] of Byte);
106+ 3: (Components: array[TColor32Component] of Byte);
107+end;
108+```
109+
110+# Bitmap structure
111+
112+Bitmap class should provide direct pixel access given by X, Y coordinate. But some graphic operation could be further optimized by not doing coordinate calculations for every pixel and rather do pixel pointer shifting by simple memory pointer addition. Some mass operation as filling rectangular region could be optimized using Move and FillChar functions.
113+
114+## Two dimensional dynamic array
115+
116+This is native way to express two dimensional array in pascal. Internal structure is implemented as pointer to array of pointers to data because dynamic array is in fact pointer to array data. Then calculation of pixel position is matter of fetching pointer for rows and add horizontal position to it.
117+
118+``` pascal
119+interface
120+
121+type
122+ TFastBitmap = class
123+ private
124+ function GetSize: TPoint;
125+ procedure SetSize(const AValue: TPoint);
126+ public
127+ Pixels: array of array of TFastBitmapPixel;
128+ property Size: TPoint read GetSize write SetSize;
129+ end;
130+
131+implementation
132+
133+{ TFastBitmap }
134+
135+function TFastBitmap.GetSize: TPoint;
136+begin
137+ Result.X := Length(Pixels);
138+ if Result.X > 0 then
139+ Result.Y := Length(Pixels[0])
140+ else
141+ Result.Y := 0;
142+end;
143+
144+procedure TFastBitmap.SetSize(const AValue: TPoint);
145+begin
146+ SetLength(Pixels, AValue.X, AValue.Y);
147+end;
148+```
149+
150+## Raw dynamic memory
151+
152+It is good to have whole bitmap in one compact memory area. Such memory block behave as video memory of video card. Position of pixels have to be calculated by using equation Y * Width + X with use of instructions for addition and multiplication. Access to pixels is pretty fast thanks to GetPixel and SetPixel methods inlining. But more instruction have to be used than in case of two dimensional dynamic array.
153+
154+``` pascal
155+interface
156+
157+type
158+ PFastBitmapPixel = ^TFastBitmapPixel;
159+ TFastBitmap = class
160+ private
161+ FPixelsData: PByte;
162+ FSize: TPoint;
163+ function GetPixel(X, Y: Integer): TFastBitmapPixel; inline;
164+ procedure SetPixel(X, Y: Integer; const AValue: TFastBitmapPixel); inline;
165+ procedure SetSize(const AValue: TPoint);
166+ public
167+ constructor Create;
168+ destructor Destroy; override;
169+ property Size: TPoint read FSize write SetSize;
170+ property Pixels[X, Y: Integer]: TFastBitmapPixel read GetPixel write SetPixel;
171+ end;
172+
173+implementation
174+
175+{ TFastBitmap }
176+
177+function TFastBitmap.GetPixel(X, Y: Integer): TFastBitmapPixel;
178+begin
179+ Result := PFastBitmapPixel(FPixelsData + (Y * FSize.X + X) * SizeOf(TFastBitmapPixel))^;
180+end;
181+
182+procedure TFastBitmap.SetPixel(X, Y: Integer; const AValue: TFastBitmapPixel);
183+begin
184+ PFastBitmapPixel(FPixelsData + (Y * FSize.X + X) * SizeOf(TFastBitmapPixel))^ := AValue;
185+end;
186+
187+procedure TFastBitmap.SetSize(const AValue: TPoint);
188+begin
189+ if (FSize.X = AValue.X) and (FSize.Y = AValue.X) then
190+ Exit;
191+ FSize := AValue;
192+ FPixelsData := ReAllocMem(FPixelsData, FSize.X * FSize.Y * SizeOf(TFastBitmapPixel));
193+end;
194+
195+constructor TFastBitmap.Create;
196+begin
197+ Size := Point(0, 0);
198+end;
199+
200+destructor TFastBitmap.Destroy;
201+begin
202+ FreeMem(FPixelsData);
203+ inherited Destroy;
204+end;
205+```
206+
207+## Strict Pointer pixel access
208+
209+We are able eliminate some of coordinate multiplications with low level pixel access using pointers only. Then only addition(incrementation) is necessary to change current pixel position.
210+
211+``` pascal
212+interface
213+
214+type
215+ TFastBitmap = class
216+ private
217+ FPixelsData: PByte;
218+ FSize: TPoint;
219+ procedure SetSize(const AValue: TPoint);
220+ public
221+ constructor Create;
222+ destructor Destroy; override;
223+ procedure RandomImage;
224+ property Size: TPoint read FSize write SetSize;
225+ function GetPixelAddress(X, Y: Integer): PFastBitmapPixel; inline;
226+ function GetPixelSize: Integer; inline;
227+ end;
228+
229+implementation
230+
231+{ TFastBitmap }
232+
233+procedure TFastBitmap.SetSize(const AValue: TPoint);
234+begin
235+ if (FSize.X = AValue.X) and (FSize.Y = AValue.X) then Exit;
236+ FSize := AValue;
237+ FPixelsData := ReAllocMem(FPixelsData, FSize.X * FSize.Y * SizeOf(TFastBitmapPixel));
238+end;
239+
240+constructor TFastBitmap.Create;
241+begin
242+ Size := Point(0, 0);
243+end;
244+
245+destructor TFastBitmap.Destroy;
246+begin
247+ FreeData(FPixelData);
248+ inherited Destroy;
249+end;
250+
251+function TFastBitmap.GetPixelAddress(X, Y: Integer): PFastBitmapPixel;
252+begin
253+ Result := PFastBitmapPixel(FPixelsData) + Y * FSize.X + X;
254+end;
255+
256+function TFastBitmap.GetPixelSize: Integer;
257+begin
258+ Result := SizeOf(TFastBitmapPixel);
259+end;
260+```
261+
262+In this case drawing pixels is less readable:
263+
264+``` pascal
265+procedure RandomImage(FastBitmap: TFastBitmap);
266+var
267+ X, Y: Integer;
268+ PRow: PFastBitmapPixel;
269+ PPixel: PFastBitmapPixel;
270+begin
271+ with FastBitmap do begin
272+ PRow := GetPixelAddress(0, Size.Y div 2);
273+ for Y := 0 to Size.Y - 1 do begin
274+ PPixel := PRow;
275+ for X := 0 to Size.X - 1 do begin
276+ PPixel^ := Random(256) or (Random(256) shl 16) or (Random(256) shl 8);
277+ Inc(PPixel);
278+ end;
279+ Inc(PRow, Size.X);
280+ end;
281+ end;
282+end;
283+```
284+
285+# Pixel operation optimization
286+
287+## Basic line algorithm
288+
289+This is naive form which is readable but with price of slower processing.
290+
291+``` pascal
292+procedure TFastBitmap.HorizontalLine(X, Y, Length: Integer; Color: TFastBitmapPixel);
293+var
294+ I: Integer;
295+begin
296+ for I := 0 to Length - 1 do
297+ Pixels[X + I, Y] := Color;
298+end;
299+```
300+
301+## Pointers
302+
303+With use of pointers we can eliminate much of pixel address addition and multiplication by Pixels property access. Only fast increment operation is performed.
304+
305+``` pascal
306+procedure TFastBitmap.HorizontalLine(X, Y, Length: Integer; Color: TFastBitmapPixel);
307+var
308+ I: Integer;
309+ P: PFastBitmapPixel;
310+begin
311+ P := PFastBitmapPixel(FPixelData + (Y * Size.X + X) * SizeOf(TFastBitmapPixel));
312+ for I := 0 to Length - 1 do begin
313+ P^ := Color;
314+ Inc(P);
315+ end;
316+end;
317+```
318+
319+## Mass fill using FillDWord
320+
321+Access using pointers and incrementation is fastest possible using conventional single operations. But most of todays CPU offer instructions for mass operations like MOVS, STOS for x86 architecture. Pixel size should be 1, 2 or 4 bytes to be able to use this optimization.
322+
323+``` pascal
324+procedure TFastBitmap.HorizontalLine(X, Y, Length: Integer; Color: TFastBitmapPixel);
325+var
326+ I: Integer;
327+ P: PFastBitmapPixel;
328+begin
329+ P := PFastBitmapPixel(FPixelData + (Y * Size.X + X) * SizeOf(TFastBitmapPixel));
330+ FillDWord(P^, Length, Color);
331+end;
332+```
333+
334+## Inlining
335+
336+If code is notably smaller like SetPixel and GetPixel methods it is better to inline instructions rather than do push and pop operations on stact with execution of call and ret instruction. This optimization will be even significant if such operation is executed many times as pixel operations do.
337+
338+``` pascal
339+procedure TFastBitmap.HorizontalLine(X, Y, Length: Integer; Color: TFastBitmapPixel); inline;
340+var
341+ I: Integer;
342+ P: PFastBitmapPixel;
343+begin
344+ P := PFastBitmapPixel(FPixelData + (Y * Size.X + X) * SizeOf(TFastBitmapPixel));
345+ FillDWord(P^, Length, Color);
346+end;
347+```
348+
349+## DMA
350+
351+If memory block have to be copied to another memory place or device memory DMA(Direct Memory Access) can be used. CPU doesn't have to be involved in copy operations and can do further processing. This kind of optimization can be used in OpenGL for copying data to video card memory.
352+
353+# Drawing bitmap on screen
354+
355+In this test let assume that we have simple bitmap structure designed as two dimensional byte array where each pixel have 256 possible colors. This could be gray image or some palette mapped image. All image manipulation will be done with custom functions with direct pixel access. Thanks to custom data structure functions could be optimized for faster block memory operations if necessary.
356+
357+To be able to display image on Form custom bitmap have to be copied to some TWinControl canvas area. Image have to be copied repeatedly if motion image is generated. Every bitmap copy in memory take some time. Then our aim is to do as low as possible copy operations and rather copy our bitmap to screen directly if possible.
358+
359+You can draw image as fast as possible in simple loop:
360+
361+``` pascal
362+repeat
363+ FastBitmapToBitmap(FastBitmap, Image1.Picture.Bitmap);
364+ Application.ProcessMessages;
365+until Terminated;
366+```
367+
368+Or draw image for example using Timer with defined drawing interval. Even if nothing is changed on bitmap there is no need to copy bitmap to screen so RedrawPending simple flag could be used. Thanks to delayed draw execution with calling Redraw method drawing of frames could be skipped.
369+
370+``` pascal
371+TForm1 = class(TForm)
372+published
373+ procedure Timer1Execute(Sender: TObject);
374+ ...
375+public
376+ RedrawPending: Boolean;
377+ Drawing: Boolean;
378+ FastBitmap: TFastBitmap;
379+ procedure Redraw;
380+ ...
381+end;
382+
383+procedure TForm1.Redraw;
384+begin
385+ RedrawPending := True;
386+end;
387+
388+procedure TForm1.Timer1Execute(Sender: TObject);
389+begin
390+ if (not Drawing) and RedrawPending then
391+ try
392+ Drawing := True;
393+ CustomProcessing(FastBitmap);
394+ FastBitmapToBitmap(FastBitmap, Image1.Picture.Bitmap);
395+ finally
396+ RedrawPending := False;
397+ Drawing := False;
398+ end;
399+end;
400+```
401+
402+# Draw methods
403+
404+- These methods show various ways how to draw bitmap.
405+- FastBitmap needs to be already initialized to desired size. Target Bitmap(Canvas) should have same size as FastBitmap. Use something like Bitmap.SetSize(FastBitmap.X, FastBitmap.Y)
406+- These drawing methods don't assume exact pixel color format. They rather use abstract function called FastPixelToTColor for illustration purpose which needs to be implemented according real chosen color format.
407+- These methods assume for simplicity Bitmap structure accessible by Pixels two-dimensional array usually implemented as property. For even better performance various further optimization could be used. See #Pixel operation optimization and #Strict Pointer pixel access.
408+
409+## TBitmap.Canvas.Pixels
410+
411+This is most straighforward but slowest method:
412+
413+``` pascal
414+function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap);
415+var
416+ X, Y: Integer;
417+begin
418+ for Y := 0 to FastBitmap.Size.Y - 1 do
419+ for X := 0 to FastBitmap.Size.X - 1 do
420+ Bitmap.Canvas.Pixels[X, Y] := FastPixelToTColor(FastBitmap.Pixels[X, Y]);
421+end;
422+```
423+
424+## TBitmap.Canvas.Pixels with Update locking
425+
426+Previous method could be speeded up by update locking and thus reducing per pixel update and event signaling.
427+
428+``` pascal
429+function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap);
430+var
431+ X, Y: Integer;
432+begin
433+ try
434+ Bitmap.BeginUpdate(True);
435+ for Y := 0 to FastBitmap.Size.Y - 1 do
436+ for X := 0 to FastBitmap.Size.X - 1 do
437+ Bitmap.Canvas.Pixels[X, Y] := FastPixelToTColor(FastBitmap.Pixels[X, Y]);
438+ finally
439+ Bitmap.EndUpdate(False);
440+ end;
441+end;
442+```
443+
444+## TLazIntfImage
445+
446+TLazIntfImage is a memory image. I can store transparency and 16-bit values for each channel. TBitmap is compatible with Delphi and use TColor type for pixels which do not contain alpha information. So TLazIntfImage is better suited for image processing. This component provide faster access to pixels because it is an array in memory like our TFastBitmap.
447+
448+Here we copy TFastBitmap grayscale pixels into a TLazIntfImage to convert it into a TBitmap.
449+
450+``` pascal
451+uses
452+ ..., LCLType, LCLProc, LCLIntf, IntfGraphics;
453+
454+function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap);
455+var
456+ X, Y: Integer;
457+ TempIntfImage: TLazIntfImage;
458+begin
459+ try
460+ TempIntfImage := Bitmap.CreateIntfImage; // Temp image could be pre-created and help by owner class to avoid new creation in each frame
461+ for Y := 0 to FastBitmap.Size.Y - 1 do
462+ for X := 0 to FastBitmap.Size.X - 1 do begin
463+ TempIntfImage.Colors[X, Y] := TColorToFPColor(FastPixelToTColor(FastBitmap.Pixels[X, Y]));
464+ end;
465+ Bitmap.LoadFromIntfImage(TempIntfImage);
466+ finally
467+ TempIntfImage.Free;
468+ end;
469+end;
470+```
471+
472+We can also work directly with TLazIntfImage pixels, which can well serve our purpose. To do this, create a TLazInfImage, set the pixel format and then use GetDataLineStart to access scanlines. This is still an indirect method, because a TLazIntfImage need to be copied to a TBitmap to be drawn.
473+
474+## TBGRABitmap.ScanLine
475+
476+There is graphic library BGRABitmap which allow access to scan lines. Overall speed of this method is pretty good. Drawing is done directly to Canvas of some TWinControl components like TForm of TPaintBox. The pixel format is 32-bit color with alpha channel, i.e. 8-bit for each channel.
477+
478+Using TBitmap.ScanLine was a method used frequently on Delphi. But TBitmap.ScanLine is not supported by LCL. ScanLine property give access to memory starting point for each row raw data. Then direct manipulation with pixels is much faster than using Pixels property as no additional events is fired.
479+
480+We can copy our grayscale FastBitmap data to a TBGRABitmap to render it on the screen.
481+
482+``` pascal
483+uses
484+ ..., BGRABitmap, BGRABitmapTypes;
485+
486+procedure FastBitmapToCanvas(FastBitmap: TFastBitmap; Canvas: TCanvas);
487+var
488+ X, Y: Integer;
489+ P: PBGRAPixel;
490+ bgra: TBGRABitmap;
491+begin
492+ bgra := TBGRABitmap.Create(FastBitmap.Size.X, FastBitmap.Size.Y);
493+ with FastBitmap do
494+ for Y := 0 to Size.Y - 1 do begin
495+ P := PInteger(bgra.ScanLine[Y]);
496+ for X := 0 to Size.X - 1 do begin
497+ PInteger(P)^ := FastPixelToTColor(Pixels[X, Y]) or $ff000000;
498+ Inc(P);
499+ end;
500+ end;
501+ bgra.InvalidateBitmap; // Changed by direct access
502+ bgra.Draw(Canvas, 0, 0, False);
503+ bgra.Free;
504+end;
505+```
506+
507+We can also use TBGRABitmap only. This library works if possible with device independent bitmaps of the operating system, so it is generally a direct pixel access or quasi-direct pixel access.
508+
509+[[BGRABitmap tutorial 4|BGRABitmap tutorial shows how to access directly to pixels]]
510+
511+## TBitmap.RawImage
512+
513+This method is so far fastest in comparing to previous ones but more complicated as special care have to be given to bitmap data structure. Example assume that bitmap PixelFormat is pf24bit. Accessed raw data may differs across platforms.
514+
515+``` pascal
516+uses
517+ ..., GraphType;
518+
519+function FastBitmapToBitmap(FastBitmap: TFastBitmap; Bitmap: TBitmap);
520+var
521+ X, Y: Integer;
522+ PixelPtr: PInteger;
523+ PixelRowPtr: PInteger;
524+ P: TPixelFormat;
525+ RawImage: TRawImage;
526+ BytePerPixel: Integer;
527+begin
528+ try
529+ Bitmap.BeginUpdate(False);
530+ RawImage := Bitmap.RawImage;
531+ PixelRowPtr := PInteger(RawImage.Data);
532+ BytePerPixel := RawImage.Description.BitsPerPixel div 8;
533+ for Y := 0 to Size.Y - 1 do begin
534+ PixelPtr := PixelRowPtr;
535+ for X := 0 to Size.X - 1 do begin
536+ PixelPtr^ := FastPixelToTColor(Pixels[X, Y]);
537+ Inc(PByte(PixelPtr), BytePerPixel);
538+ end;
539+ Inc(PByte(PixelRowPtr), RawImage.Description.BytesPerLine);
540+ end;
541+ finally
542+ Bitmap.EndUpdate(False);
543+ end;
544+end;
545+```
546+
547+**RawImage.Description values examples on various platforms:**
548+
549+| Platform | Format | Depth | BitsPerPixel | BitOrder | ByteOrder | LineOrder | LineEnd | RedPrec | RedShift | GreenPrec | GreenShift | BluePrec | BlueShift | AlphaPrec | AlphaShift |
550+|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|
551+| Windows | RGBA | 24 | 24 | ReverseBits | LSBFirst | TopToBottom | DWordBoundary | 8 | 16 | 8 | 8 | 8 | 0 | 0 | 0 |
552+| Windows | RGBA | 15 | 16 | ReverseBits | LSBFirst | TopToBottom | DWordBoundary | 5 | 10 | 5 | 5 | 5 | 0 | 0 | 0 |
553+| Linux GTK2 | RGBA | 24 | 32 | BitsInOrder | LSBFirst | TopToBottom | DWordBoundary | 8 | 16 | 8 | 8 | 8 | 0 | 0 | 0 |
554+
555+## OpenGL
556+
557+OpenGL is mainly used for 3D complex modeling but it can be used for simple 2D accelerated graphics. We need initialized OpenGL, create one textured rectangle with screen resolution and set orthogonal view. Then we will able to fill texture by our custom converted bitmap data. But this method is not significantly faster then RawImage.Data method because all image data are copied with glTexImage2D function which mean slow copy using CPU.
558+
559+``` pascal
560+uses
561+ ..., GL, OpenGLContext;
562+
563+var
564+ TextureId: GLuint;
565+ TextureData: Pointer;
566+ OpenGLControl1: TOpenGLControl;
567+
568+procedure InitGL;
569+begin
570+ glMatrixMode(GL_PROJECTION);
571+ glLoadIdentity;
572+ glOrtho(0, OpenGLControl1.Width, OpenGLControl1.Height, 0, 0, 1);
573+ glMatrixMode(GL_MODELVIEW);
574+ glLoadIdentity();
575+ glDisable(GL_DEPTH_TEST);
576+ glViewport(0, 0, OpenGLControl1.Width, OpenGLControl1.Height);
577+ glGenTextures(1, @TextureId);
578+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
579+end;
580+
581+function FastBitmapToBitmap(FastBitmap: TFastBitmap; OpenGLControl: TOpenGLControl);
582+var
583+ X, Y: Integer;
584+ P: PInteger;
585+ R: PInteger;
586+const
587+ GL_CLAMP_TO_EDGE = $812F;
588+begin
589+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
590+
591+ P := OpenGLBitmap;
592+ with FastBitmap do
593+ for Y := 0 to Size.Y - 1 do begin
594+ R := P;
595+ for X := 0 to Size.X - 1 do begin
596+ R^ := FastPixelToTColor(Pixels[X, Y]) or $ff000000;
597+ Inc(R);
598+ end;
599+ Inc(P, Size.X);
600+ end;
601+
602+ glLoadIdentity;
603+ glTranslatef(-OpenGLControl.Width div 2, -OpenGLControl.Height div 2, 0.0);
604+
605+ glEnable(GL_TEXTURE_2D);
606+ glBindTexture(GL_TEXTURE_2D, TextureId);
607+ //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
608+ //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
609+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
610+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
611+ glTexImage2D(GL_TEXTURE_2D, 0, 4, OpenGLControl.Width, OpenGLControl.Height,
612+ 0, GL_RGBA, GL_UNSIGNED_BYTE, OpenGLBitmap);
613+
614+ glBegin(GL_QUADS);
615+ glColor3ub(255, 255, 255);
616+ glTexCoord2f(0, 0);
617+ glVertex3f(0, 0, 0);
618+ glTexCoord2f(OpenGLControl.Width div 2, 0);
619+ glVertex3f(OpenGLControl.Width, 0, 0);
620+ glTexCoord2f(OpenGLControl.Width div 2, OpenGLControl.Height div 2);
621+ glVertex3f(OpenGLControl.Width, OpenGLControl.Height, 0);
622+ glTexCoord2f(0, OpenGLControl.Height div 2);
623+ glVertex3f(0, OpenGLControl.Height, 0);
624+ glEnd();
625+
626+ OpenGLControl.SwapBuffers;
627+end;
628+```
629+
630+## OpenGL PBO
631+
632+This method use asynchronous DMA transfer to copy texture data thus CPU is free to do further computations. It also eliminate one additional copy operation which is done by glTexImage2D in previous method. Method require GL_ARB_pixel_buffer_object extension.
633+
634+- [OpenGL Pixel Buffer Object (PBO)](http://www.songho.ca/opengl/gl_pbo.html)
635+
636+``` pascal
637+uses
638+ ..., GL, OpenGLContext;
639+
640+var
641+ TextureId: GLuint;
642+ TextureData: Pointer;
643+ OpenGLControl1: TOpenGLControl;
644+ pboIds: array[0..1] of GLuint;
645+
646+procedure InitGL;
647+var
648+ DataSize: Integer;
649+begin
650+ glMatrixMode(GL_PROJECTION);
651+ glLoadIdentity;
652+ glOrtho(0, OpenGLControl1.Width, OpenGLControl1.Height, 0, 0, 1);
653+ glMatrixMode(GL_MODELVIEW);
654+ glLoadIdentity();
655+ glDisable(GL_DEPTH_TEST);
656+ glViewport(0, 0, OpenGLControl1.Width, OpenGLControl1.Height);
657+ glGenTextures(1, @TextureId);
658+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
659+
660+ OpenGLControl1.MakeCurrent;
661+ DataSize := OpenGLControl1.Width * OpenGLControl1.Height * SizeOf(Integer);
662+ if Load_GL_ARB_vertex_buffer_object then begin
663+ glGenBuffersARB(2, @pboIds);
664+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[0]);
665+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DataSize, Pointer(0), GL_STREAM_READ_ARB);
666+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[1]);
667+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DataSize, Pointer(0), GL_STREAM_READ_ARB);
668+
669+ end else raise Exception.Create('GL_ARB_pixel_buffer_object not supported');
670+
671+ glEnable(GL_TEXTURE_2D);
672+ glBindTexture(GL_TEXTURE_2D, TextureId);
673+ //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
674+ //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
675+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
676+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
677+ glTexImage2D(GL_TEXTURE_2D, 0, 4, OpenGLControl1.Width, OpenGLControl1.Height,
678+ 0, GL_RGBA, GL_UNSIGNED_BYTE, OpenGLBitmap);
679+end;
680+
681+function FastBitmapToBitmap(FastBitmap: TFastBitmap; OpenGLControl: TOpenGLControl);
682+var
683+ X, Y: Integer;
684+ P: PInteger;
685+ R: PInteger;
686+ Ptr: ^GLubyte;
687+ TextureShift: TPoint;
688+ TextureShift2: TPoint;
689+const
690+ GL_CLAMP_TO_EDGE = $812F;
691+begin
692+ // "index" is used to read pixels from framebuffer to a PBO
693+ // "nextIndex" is used to update pixels in the other PBO
694+ Index := (Index + 1) mod 2;
695+ NextIndex := (Index + 1) mod 2;
696+
697+ glLoadIdentity;
698+
699+ // bind the texture and PBO
700+ glBindTexture(GL_TEXTURE_2D, TextureId);
701+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[index]);
702+
703+ // copy pixels from PBO to texture object
704+ // Use offset instead of ponter.
705+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, OpenGLControl.Width, OpenGLControl.Height,
706+ GL_BGRA, GL_UNSIGNED_BYTE, Pointer(0));
707+
708+ // bind PBO to update texture source
709+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[nextIndex]);
710+
711+ // Note that glMapBufferARB() causes sync issue.
712+ // If GPU is working with this buffer, glMapBufferARB() will wait(stall)
713+ // until GPU to finish its job. To avoid waiting (idle), you can call
714+ // first glBufferDataARB() with NULL pointer before glMapBufferARB().
715+ // If you do that, the previous data in PBO will be discarded and
716+ // glMapBufferARB() returns a new allocated pointer immediately
717+ // even if GPU is still working with the previous data.
718+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, OpenGLControl.Width * OpenGLControl.Height * SizeOf(Integer), Pointer(0), GL_STREAM_DRAW_ARB);
719+
720+ // map the buffer object into client's memory
721+ ptr := glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB);
722+ if Assigned(ptr) then begin
723+ // update data directly on the mapped buffer
724+ P := PInteger(Ptr);
725+ with FastBitmap do
726+ for Y := 0 to Size.Y - 2 do begin
727+ R := P;
728+ for X := 0 to Size.X - 1 do begin
729+ R^ := NoSwapBRComponent(FastPixelToTColor(Pixels[X, Y]) or $ff000000);
730+ Inc(R);
731+ end;
732+ Inc(P, Size.X);
733+ end;
734+ glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
735+ end;
736+
737+ // it is good idea to release PBOs with ID 0 after use.
738+ // Once bound with 0, all pixel operations are back to normal ways.
739+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
740+
741+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
742+ glTranslatef(-OpenGLControl.Width / 2, -OpenGLControl.Height / 2, 0.0);
743+ glBindTexture(GL_TEXTURE_2D, TextureId);
744+
745+ glBegin(GL_QUADS);
746+ glColor3ub(255, 255, 255);
747+ glTexCoord2f(0, 0);
748+ glVertex3f(0, TextureShift2.Y, 0);
749+ glTexCoord2f(OpenGLControl.Width div 2, 0);
750+ glVertex3f(OpenGLControl.Width, 0, 0);
751+ glTexCoord2f(OpenGLControl.Width div 2, OpenGLControl.Height div 2);
752+ glVertex3f(OpenGLControl.Width, OpenGLControl.Height, 0);
753+ glTexCoord2f(0, OpenGLControl.Height div 2);
754+ glVertex3f(0, OpenGLControl.Height, 0);
755+ glEnd();
756+
757+ OpenGLControl.SwapBuffers;
758+end;
759+```
760+
761+# See also
762+
763+- [[Developing with Graphics]] especially the Direct pixel access section
764+- [[Accessing the Interfaces directly]]
765+- [[GraphicTest]] - speed comparison of various direct draw methods
766+
767+---
768+*From the Free Pascal wiki, page "Fast direct pixel access", as it stood on 2020-02-11 after a change by Andry, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
769+Category: BGRABitmap
770+Category: Graphics

Back to history