Solved

Accessing pixels in Canvas

Posted on 1998-10-20
32
710 Views
Last Modified: 2010-04-03
Maybe question can be applied particularly to the Pixels property of the TCanvas.
I understand the accessing Pixels requires Canvas to perform one of it methods, and accessing it from assembly language is not as simple as accessing a two dimensional array.
In other words:  
  mov esi,Canvas.Pixels
should not work.   In order to access the array, do I have to go through its methods, or do I have to somehow find a way to sneak into Canvas's private variables?  I want to do operations on the Canvas's pixels directly, without having to go through the overhead of the property's methods.
0
Comment
Question by:scrapdog
  • 12
  • 8
  • 8
  • +4
32 Comments
 
LVL 5

Author Comment

by:scrapdog
ID: 1343448
Man, I have been making lots of tpyos tonite.

Maybe *this* question can be applied particularly to the Pixels property of the TCanvas.
I understand *that* accessing Pixels requires Canvas to perform one of its methods, and accessing it from assembly language is not as simple as accessing a two dimensional array.
In other words:    
  mov esi,Canvas.Pixels
should not work.   In order to access the array, do I have to go through its methods, or do I have to somehow find a way to sneak into Canvas's private variables?  I want to do operations on the Canvas's pixels directly, without having to go through the overhead of the property's methods.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1343449
Hi scrapdog,

 Isn't this good enough?


{read color}
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
 CheckBox1.Font.Color := Canvas.Pixels[10, 10];
end

{change color}
procedure TForm1.Button1Click(Sender: TObject);
begin
 Canvas.Pixels[Random(ClientWidth), Random(ClientHeight)] := clRed;
end;

Regards, Zif.


0
 
LVL 20

Expert Comment

by:Madshi
ID: 1343450
Perhaps you should look at CreateDIBSection. There you can give in an array of pixels. But the pixel format depends on the color depth. It's quite difficult...

Regards, Madshi.
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343451
Hello Guys!

What Kevin(scrapdog) is trying to say is that he needs code in assembler or at least how to pass a pixel from the screen or whatever to a register and then start to work w/ that pixel in assembler....The problem is that he is trying to get the X and Y values separately since in assembler the arrays don't work the way in Pascal work... Hope all this makes any sense... Hopefully I understood the question right...If I'm missing something please correct me Kevin :->

Cheers,
Viktor
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343452
Actually, all I need to know is how to get the address of where the pixels are stored.  I want to use this in an assembly routine. Once I know the address, I can easily operate on it by calculating X and Y into an offset.

If Pixels were a field rather than a property, I could do this easily.  However that is not the case.  In order to access Pixels (through assembly), would I have to access Canvas.FPixels (if there is one), or is this just not possible?
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1343453
Definitely look at createdibsection. This does give you a nice chunk of memory full of pixels. The downside is that you have to tailor your code to deal with the pixel depth but this is no major deal.
0
 
LVL 3

Expert Comment

by:bryan7
ID: 1343454
hey scrapdog !

I have some experience in assembler, since I used it to make libraries for
 quickbasic. I'm not sure if this will work on windows too, but the video-card
 memory address is located at A000h ( hex ) , In ms-dos programming,
 ( 16 bit ) it worked like this:

     Address ( offset in the A000 segment )  =  Y * ( horizontal resolution ) + X

Maybe in 32-bit it's:      
 
Address  = 40960*65536 + Y * ( horizontal resolution ) + X

( 40960 is A000 in decimal ) ( so, if each segment is 65536 bytes, it will be 40960*65536)


and if the address was higher than 65535 then u had to toggle the memory pages
 of the video card. In 32-bit mode, I'm not sure on how to manage the memory.

I'm not sure if it will help, well, I tried.

 Since I've never used assembler in a windows program, I can't tell you if
   it's gonna work, but you should be able to read the video-card memory
 at that address ( A000 hex ) or at B000 hex or B800 hex, but those 2 last ones,
  where used by the text modes. and A000 for grapic modes.

A000 is 40960 in decimal, in the 1st Mb of memory.

bryan

0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343455
Hello Kevin!

I think that even if you access the FPixels it will be of type

type Pix = array[0..MAXVALUE, 0..MAXVALUE];

var
  FPixels : Pix;

so it will still an array that you'd have to access same way as the Canvas.Pixels

I think you need to look for FX, FY or X,Y so you could get the [x,y] value of the pixel...

Cheers,
Viktor
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343456
Thanks, but I don't want to access video memory, nor do I want to create a new bitmap.

All I want to do is access the Pixels property of a Canvas (already created) in an assembly language routine.  The only thing I need to know is how I can obtain the address of the *beginning* of the Pixels array.  If Pixels were a regular array rather than a property, I could do this:

PixelPtr := @Canvas.Pixels;

But it is a property, so I can't!!  (or is this legal AND valid???)  The reason I want to access the FPixels array with assembly language is to speed it up a bit and bypass the property's methods.
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343457
Adjusted points to 30
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1343458
The canvas.Pixels array is no array. Each and every call to this array ends in a method that extract the desired pixel from the canvas. This code is really very slow.
So if you want to have fast code (I think that's what you want) you'll have to work with the win32 API. Ok, you don't want to create a new bitmap. So don't use CreateDIBSection, but GetDIBits/SetDIBits.

Regards, Madshi.
0
 
LVL 10

Expert Comment

by:Jacco
ID: 1343459
Mashdi is right
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1343460
Thanx, Jacco.   :-)
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343461
Thanks.  I will try using SetDIBits.  One question though...what do you send as the Bitmap parameter?  I am not aware that Canvas has a Bitmap property...


0
 
LVL 20

Expert Comment

by:Madshi
ID: 1343462
That's right. About what kind of canvas are we talking? A bitmap canvas or a form canvas or ...
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343463
Any canvas that can be passed as a TCanvas parameter.

It is likely that I will be passing DrawGrid.Canvas into this parameter.  I will not likely be using it for Bitmap.Canvas though.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 20

Expert Comment

by:Madshi
ID: 1343464
Ooops, that's a little bit more difficult than a bitmap.canvas. Perhaps you should create a temporary bitmap by using CreateDIBSection and then call a BitBlt to transfer the DrawGrid's pixels to the newly created bitmap. Then you can use GetDIBits and SetDIBits for the bitmap.
Make sure that the color format of the DrawGrid.canvas and the newly created bitmap is identical.
Haven't done something like this before. Hope it works...

Regards, Madshi.
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343465
This would entail copying the entire bitmap twice (I would have to copy the temporary bitmap back).

I think using Pixels would be faster!

Does Canvas have some kind of pointer to the bitmap that it manipulates?  If it does, it is probably private so accessing it would be impossible.

Another application that I want to use this information for is do perform a fire algorithm on a Canvas (it could be on a paintbox, panel, etc.)  I want to write an assembly language fire algorithm and apply it directly to the canvas.

Thanks for your help so far, by the way.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1343466
Directly reading/writing from/to a canvas is quite easy. You can just use BitBlt with the canvas.handle. But if you want to address specific pixels, it's getting more difficult. I think, copying the whole bitmap twice is still much faster then working with the canvas.pixels property.
I didn't work with the windows drawing API that much so far. So you'll have a look at it yourself. Perhaps you should begin with CreateDIBSection and BitBlt. Perhaps there are functions to access the pixels directly with a win API. But the canvas.pixels property is probably quite slow.
Do you have to Delphi sources? Perhaps you should look how the canvas.pixels methods are implemented...

Regards, Madshi.
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343467
Madshi: for your noble attempts to help, propose an answer.

By the way, where can I get the Delphi source?  Specifically the source code to Canvas?  Is it somewhere in the source directory?  Or do I have to pay Borland $$$?

(I have the source to the Windows unit, etc...)
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343468
Here it is.... The original is in the Graphics.pas unit....
-------------------------
  TCanvas = class(TPersistent)
  private
    FHandle: HDC;
    State: TCanvasState;
    FFont: TFont;
    FPen: TPen;
    FBrush: TBrush;
    FPenPos: TPoint;
    FCopyMode: TCopyMode;
    FOnChange: TNotifyEvent;
    FOnChanging: TNotifyEvent;
    FLock: TRTLCriticalSection;
    FLockCount: Integer;
    FTextFlags: Longint;
    procedure CreateBrush;
    procedure CreateFont;
    procedure CreatePen;
    procedure BrushChanged(ABrush: TObject);
    procedure DeselectHandles;
    function GetCanvasOrientation: TCanvasOrientation;
    function GetClipRect: TRect;
    function GetHandle: HDC;
    function GetPenPos: TPoint;
    function GetPixel(X, Y: Integer): TColor;
    procedure FontChanged(AFont: TObject);
    procedure PenChanged(APen: TObject);
    procedure SetBrush(Value: TBrush);
    procedure SetFont(Value: TFont);
    procedure SetHandle(Value: HDC);
    procedure SetPen(Value: TPen);
    procedure SetPenPos(Value: TPoint);
    procedure SetPixel(X, Y: Integer; Value: TColor);
  protected
    procedure Changed; virtual;
    procedure Changing; virtual;
    procedure CreateHandle; virtual;
    procedure RequiredState(ReqState: TCanvasState);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
    procedure BrushCopy(const Dest: TRect; Bitmap: TBitmap;
      const Source: TRect; Color: TColor);
    procedure Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
    procedure CopyRect(const Dest: TRect; Canvas: TCanvas;
      const Source: TRect);
    procedure Draw(X, Y: Integer; Graphic: TGraphic);
    procedure DrawFocusRect(const Rect: TRect);
    procedure Ellipse(X1, Y1, X2, Y2: Integer);
    procedure FillRect(const Rect: TRect);
    procedure FloodFill(X, Y: Integer; Color: TColor; FillStyle: TFillStyle);
    procedure FrameRect(const Rect: TRect);
    procedure LineTo(X, Y: Integer);
    procedure Lock;
    procedure MoveTo(X, Y: Integer);
    procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
    procedure Polygon(const Points: array of TPoint);
    procedure Polyline(const Points: array of TPoint);
    procedure PolyBezier(const Points: array of TPoint);
    procedure PolyBezierTo(const Points: array of TPoint);
    procedure Rectangle(X1, Y1, X2, Y2: Integer);
    procedure Refresh;
    procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
    procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);
    function TextExtent(const Text: string): TSize;
    function TextHeight(const Text: string): Integer;
    procedure TextOut(X, Y: Integer; const Text: string);
    procedure TextRect(Rect: TRect; X, Y: Integer; const Text: string);
    function TextWidth(const Text: string): Integer;
    function TryLock: Boolean;
    procedure Unlock;
    property ClipRect: TRect read GetClipRect;
    property Handle: HDC read GetHandle write SetHandle;
    property LockCount: Integer read FLockCount;
    property CanvasOrientation: TCanvasOrientation read GetCanvasOrientation;
    property PenPos: TPoint read GetPenPos write SetPenPos;
    property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
    property TextFlags: Longint read FTextFlags write FTextFlags;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
  published
    property Brush: TBrush read FBrush write SetBrush;
    property CopyMode: TCopyMode read FCopyMode write FCopyMode default cmSrcCopy;
    property Font: TFont read FFont write SetFont;
    property Pen: TPen read FPen write SetPen;
  end;
-----------------
Cheers,
Viktor
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343469
Rock on!  What subdirectory is it located in?  I couldn't find it.
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343470
If you got D4 then there is a special feature that you just hold down Ctrl and click on TCanvas and the unit that it is in opens immediatelly, and it get's to the line where it is declared...

If you don't have D4 then here is what the path to the Graphics.pas might be...

C:\Program Files\Borland\Delphi3\Source\VCL\Graphics.pas

Take a look under that path and see what hapens...
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343471
Don't have VCL Directory, only RTL :(
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343472
What version of Delphi R U using???
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343473
2 (Gimp) don't rub it in :)
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343474
Ok, are you sure you don't have the sources for any of the VCLs...????

btw- Do you know in what unit is the code for the function Length()??? D4 shows it's in System.pas but I didn't find it there.. Any ideas???

Cheers,
Viktor
0
 
LVL 20

Accepted Solution

by:
Madshi earned 40 total points
ID: 1343475
scrapdog,

the sources are included in professional and client/server version only, not in standard version.
If you've professional or client/server version and the sources are not there, please try to reinstall Delphi.
If you've standard edition, give me your email and I'll send you the canvas codes (in "graphics.pas", like Viktor said).

Viktor,

in system.pas you'll find this type:

type StrRec = record
                allocSiz:       Longint;
                refCnt: Longint;
                length: Longint;
              end;

For each and every string Delphi allocates memory for the string data AND for this StrRec record that stands before the string data. You will not find the function "Length", because this function is no "real" function. In the hint of length you'll find no "overload", but nevertheless length can handle ansiStrings, wideStrings and dynamical arrays. So it's a Delphi trick function. I think in every location you use length Delphi is just giving back the length field of the StrRec type that stands before the string.

Regards, Madshi.
0
 
LVL 5

Author Comment

by:scrapdog
ID: 1343476
Thanks Madshi.

Viktor already sent me the source for graphics.pas but thanks anyway.
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343477
10x Madshi.... The reason why I'm looking for the Length() function is that I wanted to see how it is created.. I mean I need the code if there is one.... I tried a few attempts to accomplish to built the same function but no joy.... If you want to see a few codes I found for Pascal(not Delphi) that determine the lenght of a string just check the question "To All of you" or something like that by Zif... The code is in ASsembler and that's what I need... I need to create it with assembler in Delphi though... When I try to transfer the code from Pascal to Delphi, and even make a few changes it still doesn't work.. PLease help.....

Cheers,
Viktor
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1343478
Viktor,

here comes my enhanced PosStr assembler function. You should be able to get from it how to get the string length in assembler.

function FindStr(const subStr,str: string; fromPos,toPos: cardinal) : cardinal; assembler
asm      //            EAX    EDX          ECX     [ESP+8]              EAX
        TEST    EAX,EAX                  // subStr empty ?
        JE      @@noWork
        TEST    EDX,EDX                  // str empty ?
        JE      @@fail4
        TEST    ECX,ECX                  // fromPos = 0 ?
        JE      @@fail4
        PUSH    EBX
        MOV     EBX,ECX                  // EBX = fromPos
        MOV     ECX,[ESP+12]             // ECX = toPos  (+4 w/ PUSH EBX)
        TEST    ECX,ECX                  // toPos = 0 ?
        JE      @@fail3
        PUSH    ESI
        PUSH    EDI
        MOV     ESI,EAX                  // ESI = substr
        MOV     EDI,EDX                  // EDI = str
        CMP     EBX,ECX                  // fromPos > toPos ?
        JA      @@backwards
@@forwards:
        MOV     EDX,[EDI-4]              // EDX = Length(substr)
        CMP     EBX,EDX                  // fromPos > Length(str) ?
        JA      @@fail2
        CMP     ECX,EDX                  // toPos <= Length(str) ?
        JNA     @@toPosOk
        MOV     ECX,EDX                  // toPos = Length(str)
@@toPosOk:
        MOV     EDX,[ESI-4]              // EDX = Length(substr)
        DEC     EDX                      // EDX = Length(substr) - 1
        JS      @@fail2                  // EDX < 0 ?
        PUSH    EDI                      // remember str position to calculate index
        DEC     EBX                      // dec(fromPos)
        ADD     EDI,EBX                  // "Delete (str, 1, fromPos - 1)"
        SUB     ECX,EBX                  // toPos := toPos - fromPos + 1
        SUB     ECX,EDX                  // #positions in str to look at = Length(str) - Length(substr) + 1
        JBE     @@fail1                  // #positions <= 0 ?
        MOV     AL,[ESI]                 // AL = first char of substr
        INC     ESI                      // Point ESI to 2'nd char of substr
@@fwLoop:
        REPNE   SCASB
        JNE     @@fail1
        MOV     EBX,ECX                  // save outer loop counter
        PUSH    ESI                      // save outer loop substr pointer
        PUSH    EDI                      // save outer loop str pointer
        MOV     ECX,EDX
        REPE    CMPSB
        POP     EDI                      // restore outer loop str pointer
        POP     ESI                      // restore outer loop substr pointer
        JE      @@fwFound
        MOV     ECX,EBX                  // restore outer loop counter
        JMP     @@fwLoop
@@fwFound:
        POP     EDX                      // restore pointer to first char of str
        MOV     EAX,EDI                  // EDI points of char after match
        SUB     EAX,EDX                  // the difference is the correct index
        POP     EDI
        POP     ESI
        POP     EBX
        JMP     @@noWork
@@backwards:
        MOV     EDX,[EDI-4]              // EDX = Length(substr)
        CMP     ECX,EDX                  // toPos > Length(str) ?
        JA      @@fail2
        CMP     EBX,EDX                  // fromPos <= Length(str) ?
        JNA     @@fromPosOk
        MOV     EBX,EDX                  // fromPos = Length(str)
@@fromPosOk:
        MOV     EDX,[ESI-4]              // EDX = Length(substr)
        DEC     EDX                      // EDX = Length(substr) - 1
        JS      @@fail2                  // EDX < 0 ?
        MOV     EAX,EDI                  // remember str position to calculate index
        ADD     EAX,EDX                  // add backwards calculation
        SUB     EAX,2
        PUSH    EAX
        DEC     ECX                      // dec(toPos)
        ADD     EDI,ECX                  // "Delete (str, 1, toPos - 1)"
        SUB     EBX,ECX                  // fromPos := fromPos - toPos + 1
        MOV     ECX,EBX                  // swap (fromPos, lastPos)
        ADD     EDI,ECX
        DEC     EDI
        ADD     ESI,EDX
        SUB     ECX,EDX                  // #positions in str to look at = Length(str) - Length(substr) + 1
        JBE     @@fail1                  // #positions <= 0 ?
        MOV     AL,[ESI]                 // AL = first char of substr
        DEC     ESI                      // Point ESI to 2'nd char of substr
        STD
@@bwLoop:
        REPNE   SCASB
        JNE     @@fail0
        MOV     EBX,ECX                  // save outer loop counter
        PUSH    ESI                      // save outer loop substr pointer
        PUSH    EDI                      // save outer loop str pointer
        MOV     ECX,EDX
        REPE    CMPSB
        POP     EDI                      // restore outer loop str pointer
        POP     ESI                      // restore outer loop substr pointer
        JE      @@bwFound
        MOV     ECX,EBX                  // restore outer loop counter
        JMP     @@bwLoop
@@bwFound:
        POP     EDX                      // restore pointer to first char of str + backwards calculation
        MOV     EAX,EDI                  // EDI points of char after match
        SUB     EAX,EDX                  // the difference is the correct index
        CLD
        POP     EDI
        POP     ESI
        POP     EBX
        JMP     @@noWork
@@fail0:
        CLD
@@fail1:
        POP     EDX                      // get rid of saved str pointer
@@fail2:
        POP     EDI
        POP     ESI
@@fail3:
        POP     EBX
@@fail4:
        XOR     EAX,EAX
@@noWork:
end;

Regards, Madshi.
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1343479
10x Madshi...but John helped me a whole lot.... Here is what he gave me and oit works perfectly...

function Str_Len(Str : String):Integer;
asm
  MOV EAX, [EAX - 4]
end;

Cheers,
Viktor
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This video discusses moving either the default database or any database to a new volume.
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

760 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

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now