Link to home
Start Free TrialLog in
Avatar of scrapdog
scrapdogFlag for United States of America

asked on

Accessing pixels in Canvas

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.
Avatar of scrapdog
scrapdog
Flag of United States of America image

ASKER

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.
Avatar of ZifNab
ZifNab

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.


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.
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
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?
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.
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

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
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.
Adjusted points to 30
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.
Mashdi is right
Thanx, Jacco.   :-)
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...


That's right. About what kind of canvas are we talking? A bitmap canvas or a form canvas or ...
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.
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.
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.
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.
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...)
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
Rock on!  What subdirectory is it located in?  I couldn't find it.
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...
Don't have VCL Directory, only RTL :(
What version of Delphi R U using???
2 (Gimp) don't rub it in :)
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
ASKER CERTIFIED SOLUTION
Avatar of Madshi
Madshi

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks Madshi.

Viktor already sent me the source for graphics.pas but thanks anyway.
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
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.
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