Solved

Pixel-by-Pixel drawing

Posted on 2004-09-21
11
1,385 Views
Last Modified: 2012-08-13
Is there any fast and efficient way to draw pixel by pixel images ??
I need to make a program which extracts some image data from a binary file and draw them on a window. The image data format is several bits per pixel, so I'm thinking on grouping bits and draw pixel by pixel according to its meaning.

Any advice?? thanks
0
Comment
Question by:patroclus02
11 Comments
 
LVL 4

Expert Comment

by:MrJul
ID: 12113662
The faster way to achieve this is to use TBitmap.ScanLine();
You pass the row as parameter and you have a pointer to the row, so each X bits you have a new pixel of the row, where X is the image color depth (16 bits, 24 bits, etc...).
Setting TBitmap.PixelFormat = pf24bits; will help a lot since red for the pixel will be 1 byte, blue and red to.

0
 
LVL 33

Expert Comment

by:Slick812
ID: 12113796
hello patroclus02 , , you ask - -
 Is there any fast and efficient way to draw pixel by pixel images ??

I guess that would depend on what you are drawing on, if you are drawing on a windows Control DC (canvas in Delphi) then the answer may be NO, as with a window DC, I would think that  the   SetPixel( )  function would have to be used, a SLOW method if you need to change alot of pixels. . . but with a system Bitmap you can get the pixel definition memory and change it, that is usually called a  ScanLine operation, which is Very Much faster than the DC  SetPixel( )  function . . .

you do not really say what kind of image data you get your pixel data from, using a scanline operation is faster but can get complicated, with input pixel data that does not match the scanline pixel data. . . . ., do not know what you mean by -
  "thinking on grouping bits and draw pixel by pixel according to its meaning"

maybe more info about what kind of image data you have to get would help?
0
 
LVL 6

Expert Comment

by:gwalkeriq
ID: 12118563
Everything is relative. You can draw a boatload of pixels in one second on a modern PC. Problem is, images require boatloads of pixels. However, it may be "good enough" for your purposes.

Using nothing fancy at all in terms of hardware (2 GHz Celeron, and whatever video card came with the Dell) , I just ran the code below (using a timer interval of 5000 msec and a form defined ClientWidth & ClientHeight of 500 each).

With full form displayed on screen (without clipping) its takes me about 1.9 seconds to draw a million pixels. With another form overlaying the entire client region of the form, it takes about 0.8 second to draw a million pixels. Replacing the call to Pixel with a simple noop drops to the to under 0.01 seconds. Interestingly, I if replace the pixel call with sleep(0), I get the same execution times.

Since modern video cards are 24/32 bits, you probably won't be able to optimize based on bits per pixel (without putting user into a low-color graphics mode)

procedure TForm1.Timer1Timer(Sender: TObject);
var
  StartTime: TDateTime;
  EndTime: TDateTime;
  ii: integer;
  jj: integer;
  ColorLoop: integer;
  mycolor: TColor;
  can: TCanvas;
begin
  StartTime := now;
  with Canvas do
  begin
    for ColorLoop := 0 to 3 do
    begin
      // making drawing obvious by changing color
      if (ColorLoop mod 2  = 0) then MyColor := clBlack else MyColor := clWhite;
      for ii := 0 to 499 do
      begin
        // Application.ProcessMessages;
        for jj := 0 to 499 do
        begin
          Pixels [ii, jj] := MyColor;
        end;
      end;
    end;
  end;
  EndTime := now;
  Caption := format('pixels in %.3f seconds', [(EndTime - StartTime) * SecsPerDay]);
end;

Drawing to a TCanvas using the Pixel[x,y] = Color

 
0
Are your AD admin tools letting you down?

Managing Active Directory can get complicated.  Often, the native tools for managing AD are just not up to the task.  The largest Active Directory installations in the world have relied on one tool to manage their day-to-day administration tasks: Hyena. Start your trial today.

 

Author Comment

by:patroclus02
ID: 12120567
What I want to do is extract tile images from a binary file. Tile images are small images (8x8 pixels) which you use to make bigger images. I need to display a tile image set, and then, several objects made from different tiles.

"thinking on grouping bits and draw pixel by pixel according to its meaning"
This means that I take 2 bits at a time (image information is 2 bits color depth. Later on, you can add other 2 bits to get up to 16 diferent colours, but inside the binary file, only 2 bits are stored). So each 2 bits, I draw a pixel depending on if they are 00, 01, 10 or 11.
So far I'm using a DirectX libraries for drawing routines, and a method called "Point", which just takes a sparameters X, Y and colour of pixel to draw. So far, it works fine it seems... Usually I don't need to draw more than 300.000-400.000 pixels at a time.
0
 
LVL 33

Expert Comment

by:Slick812
ID: 12126237
you really should have mentioned that you are using  DirectX methods, that makes an extream difference in the code methods. . .

I do NOT know the relative performance values for the  "Point" method, since ALL performance values are relative (compare the speed of one method to another method), you would need anouther method to compare it to to make a faster or slower judgement. . . .

if the "Point" method is working for you (you do not have some timing errors, or fram rate drop), you may want to continue to use it. . .
if it is not working for you,  you may look at another method (I have not used any directX in a number of years) for faster results, , I can remember a function that would draw a bitmap to the directX DC, perhaps a you can get the 8x8 pixel 2 bit info and place it on a 8x8 bitmap (of the needed color depth) with a scanline and then draw the 8x8 bitmap on the DirectX output DC. . .
as I said, I do not have the knowledge to give much for  DirectX,

 if you do not get any helpful comments here, you may want to ask this question AGAIN, and be sure to specificly ask about DirectX methods
0
 

Author Comment

by:patroclus02
ID: 12158947
Well, I'm not using DirectX directly. I'm using some libraries that interface with it.
The "point" method IS working for me so far.
But, would any non-DirectX method be faster? more adecuate maybe?
0
 
LVL 33

Expert Comment

by:Slick812
ID: 12162882
?????
As I have tried to say before, IF you want to use a bitmap then the ScanLine method is fast compared to SetPixel( ) or Canvas.Pixel(x,y), but I DO NOT KNOW any performace values for the  "point" method, so I can not compare,

if you like I can do a 8x8 pixel TBitmap scanLine using 4 colors input (as in a 2 bit color depth you describe) and then you will need a method to "Paint" this bitmap onto whatever you are drawing on. . .
you may can test the relative performance of the two methods?
0
 

Author Comment

by:patroclus02
ID: 12206662
That would be fine, so I can compare both my solution and that one you talk about.
0
 
LVL 33

Accepted Solution

by:
Slick812 earned 60 total points
ID: 12208715
button click to do Bit by Bit test of 16 byte memory block and a scanline of an 8x8 bitmap =

procedure TForm1.sbut_Scan8BmpClick(Sender: TObject);
type
 PAryCard = ^TAryCard;
 TAryCard = Array[Word] of Cardinal;

var
p2BitMem: Pointer;
Bmp8x8: TBitmap;
y, x, PosX, PosY, index: Integer;
{aryColor is where I place the 4 color values of 2 bit color used
 for the output in Bmp8x8}
aryColor: Array[0..3] of Cardinal;
pScan: PAryCard;
begin
p2BitMem := AllocMem(16);
{I use a 16 byte memory block for my 2 bit color depth pixel definitions}

PDWORD(p2BitMem)^ := 1987654321;
{I just add values to my mem block for 2 bit values}
PInt(Cardinal(p2BitMem)+4)^ := 0;
PInt(Cardinal(p2BitMem)+8)^ := MAXDWORD;
PInt(Cardinal(p2BitMem)+12)^ := 123456789;

{for these "Color" values you will need to reverse the normal TColor
Red and Blue bytes, so the "Normal" Red $FF need to be changed to $FF0000
but green is still $FF00}
aryColor[0] := $FF0000; // red color placed if bit value 00
aryColor[1] := $FF00; // green bit value 01
aryColor[2] := $FF; // blue bit value 10
aryColor[3] := $FFFF00; // yellow bit value 11

Bmp8x8 := TBitmap.Create;
try
  Bmp8x8.Height := 8;
  Bmp8x8.Width := 8;
  Bmp8x8.PixelFormat := pf32Bit; // MUST BE 32 BIT for this Scanline op
 
  PosY := 0;
  PosX := 0;
  for y := 0 to Bmp8x8.Height - 1 do
    begin
    pScan := Bmp8x8.ScanLine[y];

    for x := 0 To Bmp8x8.Width-1 do
      begin
      {I use  1 shl PosX   to test for individual BITS}
      if PByte(PosY + Cardinal(p2BitMem))^ and (1 shl PosX) <> 0 then index := 2
        else index := 0;
      Inc(PosX);
      if PByte(PosY + Cardinal(p2BitMem))^ and (1 shl PosX) <> 0 then Inc(Index);
      Inc(PosX);
      if (x = 3) or (x=7) then
        begin
        Inc(PosY);
        PosX := 0;
        end;
      {the tests above get two BIT values from each tested Byte in an X run}
      PScan[x] := aryColor[index];
      {index will be 0, 1, 2 or 3}
      end;
    end;

  Canvas.Draw(820, 260, Bmp8x8);
  finally
  FreeAndNil(Bmp8x8);
  FreeMem(p2BitMem);
  end;
end;
0
 

Author Comment

by:patroclus02
ID: 12215286
Thank you
0
 
LVL 33

Expert Comment

by:Slick812
ID: 12219783
I think this code will go in the correct direction for the bit tests

PosY := 0;
  PosX := 7;
  for y := 0 to Bmp8x8.Height - 1 do
    begin
    pScan := Bmp8x8.ScanLine[y];

    for x := 0 To Bmp8x8.Width-1 do
      begin
      {I use  1 shl PosX   to test for individual BITS}
      if PByte(PosY + Cardinal(p2BitMem))^ and (1 shl PosX) <> 0 then index := 2
        else index := 0;
      Dec(PosX);
      if PByte(PosY + Cardinal(p2BitMem))^ and (1 shl PosX) <> 0 then Inc(index);
      Dec(PosX);
      if (x = 3) or (x=7) then
        begin
        Inc(PosY);
        PosX := 7;
        end;
      {the tests above get two BIT values from each tested Byte in an X run}
      PScan[x] := aryColor[index];
      {index will be 0, 1, 2 or 3}
      end;
    end;

I think that I needed to start with the High bit (7) and decrease the bit position, I do not have much time to test this, so you may test both versions to see which matches the bits. . . .
0

Featured Post

Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
This tutorial gives a high-level tour of the interface of Marketo (a marketing automation tool to help businesses track and engage prospective customers and drive them to purchase). You will see the main areas including Marketing Activities, Design …

773 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question