Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi ScanLine anwenden, bzw die Hilfe verstehen (https://www.delphipraxis.net/96137-scanline-anwenden-bzw-die-hilfe-verstehen.html)

tapete 19. Jul 2007 11:01


ScanLine anwenden, bzw die Hilfe verstehen
 
Ich habe folgendes Problem. Ich will in einer Bitmap die Farbwerte jedes Pixels auslesen lassen. Das funktioniert auch mittels der Codeteile, die ich in 2 Tutorials gefunden habe. Ich verstehe aber nicht, warum es funktioniert und wie man mit der Hilfe von Delphi, die das ja ermöglichen sollte, alleine darauf kommt.


Also in meinem Programm habe ich einen Typ:
Delphi-Quellcode:
PixArray = Array [1..3] of Byte;
in einer Procedure wird ein Zeiger auf einen Speicherbereich vom obigen Typ erstellt:
Delphi-Quellcode:
  var
  p: ^PixArray;

Weiter unten dann das Arbeiten mit ScanLine:

Delphi-Quellcode:
p:= Bild.ScanLine[h];

Mein Problem ist, woher weiß ich, was p für ein "Variablentyp" sein muß, also ein Zeiger auf einen Speicherbereich der Art: Array [1..3] of Byte


In der Hilfe finde ich nur:

Zitat:

TBitmap.ScanLine Eigenschaft

Ermöglicht einen indizierten, zeilenweisen Zugriff auf Pixel.

Klasse
TBitmap

Syntax


[ Delphi] property ScanLine: Pointer read GetScanline;


Beschreibung
ScanLine wird nur bei DIBs (Device Independent Bitmaps) für Bildbearbeitungsprogramme verwendet, die Low-Level-Operationen auf Pixelebene durchführen.

Daraus kann ich das nicht schlußfolgern. Und sich den Code immer aus Tutorials zusammensuchen, ist nicht sehr elegant.

Sorry für möglicherweise vorhandene Dummheit.

OlafSt 19. Jul 2007 11:15

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
Eigentlich ganz simpel :wink:

Die Deklaration
Delphi-Quellcode:
P: ^PixArray;
macht aus deinem P erstmal einen Pointer. Nichts weiter.
Das Property ScanLine liefert einen Pointer zurück - also paßt das prima zusammen.

Erst, wenn du beginnst, im Code dein P zu dereferenzieren, also z.B. bei
Delphi-Quellcode:
P^[1]:=0;
wird die Tatsache, das dein P ein Pointer auf ein PixArray ist, interessant. Denn erst hier muß der Compiler anfangen, Adressen im Speicher zu errechnen.

Bis dahin ist ein Pointer einfach nur ein Pointer.

HTH,

Olaf

tapete 19. Jul 2007 11:57

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
Hi Olaf,

Zitat:

Erst, wenn du beginnst, im Code dein P zu dereferenzieren, also z.B. bei
p^[1]:=0;
wird die Tatsache, das dein P ein Pointer auf ein PixArray ist, interessant.


Ok, aber spätestens dann muß ich doch den typ wissen (Array [1..3] of Byte)

Also irgendwie ist mir das suspekt, ich bin da Neuling.


Ein Pointer ist ja eine Adresse auf einen Speicher.

Wenn ich schreibe:

Delphi-Quellcode:
p: ^PixArray
Dann ist ja p ein typisierter Pointer vom Typ Array. Wobei ein Array ja praktisch mehrere Pointer zusammengefaßt ist.

Liefert dann
Delphi-Quellcode:
p:= Bild.ScanLine[h];

ein Array aus 3 Speicheradressen zurück, die bei mir in p unter p[1],p[2] und p[3] gespeichert sind?

SirThornberry 19. Jul 2007 12:10

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
willst du wissen woher du hättest wissen sollen das man da "array[1..3] of Byte" nehmen muss?

tapete 19. Jul 2007 12:13

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
ja :)


Und was ScanLine genau zurück gibt. Müssen ja irgendwie mehrere Zeiger sein.

Vielleicht fehlt mir auch irgendwas Grundverständnismäßiges.

inherited 19. Jul 2007 12:14

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
Also, vereinfacht läuft das so ab: das Bild besteht ja aus Pixeln, wobei jedes dieser Pixel aus im normalfall 3 Byte besteht.
Ein Byte jeweils für Rot, Blau und Grünanteil. Und diese Bytes liegen hintereinander im Speicher.
bei einem 3x3-Bild dessen erste Zeile Schwarz ist, die anderen weiß ergibt sich folgendes Speicherbild:

Code:
00,00,00 00,00,00 00,00,00   FF,FF,FF FF,FF,FF FF,FF,FF  FF,FF,FF FF,FF,FF FF,FF,FF
Jetzt hast du ein einmal dein Typ, ein Array [1..3] of Byte, also Platz für genau 3 Byte.
Dann machst du daraus einen Pointer, mit ^Typname und weist der Variable das Ergebnis von Scanline zu, was nichts anderes ist,
als der Anfang dieser oben ersten genannten Bytekette, die erste "Zeile" des Bildes.

Wenn du dann auf p^[1] Zugreifst, bekommst du das erste Byte. Ich weiß jetzt nicht wierum das da drin liegt, entweder ist das der Rot- oder der Blauanteil des ersten Pixels. Mit p^[2] bekommst du den Grünanteil und mit p^[3] das 3. Byte, den übrig bleibenden anteil.
Jetzt musst du den Pointer um eins erhöhen, damit du an die nächsten 3 Bytes kommst, also das nächste Pixel.
Das machst du sooft bis du an das Ende der Zeile angekommen bist. Dann machst du das für die nächste Zeile usw.

Fertig :drunken:

SirThornberry 19. Jul 2007 12:15

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
@inherited: Fast richtig, die Zeilen liegen in umgekehrter Reihenfolge im Speicher. Erst die letzte zeile, dann die vorletzte etc.

inherited 19. Jul 2007 12:17

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
Hoppala. Das kann sein, Sorry. Darum kümmert sich zum Glück ScanLine

tapete 19. Jul 2007 12:51

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
Ah ok, das ist sehr schön, danke.


00,00,00 00,00,00 00,00,00
FF,FF,FF FF,FF,FF FF,FF,FF
FF,FF,FF FF,FF,FF FF,FF,FF <- Zeile 0
_______




Also ScanLine[0] gibt mir die Adresse von dem mit ______ unterstrichenen Speicher wieder, der 3 Byte beträgt. Um an das Pixel rechts davon zu kommen, erhöhe ich die Adresse einfach um 1.

Mit

Delphi-Quellcode:
type
PixArray = Array [1..3] of Byte;
difiniere ich einen Typ, der aus 3 Elementen besteht zu jeweils einem Byte

mit
Delphi-Quellcode:
  p: ^PixArray;
difiniere ich p als ein Array, das 3 Speicheradressen zu Speichern von jeweils einem Byte aufnehmen kann.


das folgende ist mir aber noch nicht so ganz verständlich
Delphi-Quellcode:
    p:= xBMPImage.ScanLine[0];

Wenn ScanLine[0] die Adresse von dem oben _____ unterstrichenen Speicher liefert, dann muß das bei der Zuweisung p:= ... diese Adresse ja in 3 Adressen unterteilt werden.


Praktisch liefert ScandLine die Adresse von:
FF,FF,FF

und bei p:=..
wird diese in die Adressen von FF, FF und FF unterteilt. Kann man sich das so vorstellen?

OlafSt 19. Jul 2007 12:54

Re: ScanLine anwenden, bzw die Hilfe verstehen
 
Erklärt nur alles dem TE nicht, was es mit dem Pointer auf sich hat und warum das alles Zuweisungskompatibel ist...

Im Endeffekt wird die Frage "ist mein P ein Pointer oder ein ^PixArray" erst zu Laufzeit interessant. Pointer unter sich sind allesamt Zuweisungskompatibel, denn eigentlich speichern sie alle NUR ADRESSEN. Daraus folgt: Pointer sind nur "indirekte" Variablen - ich hoffe, du verstehst, was ich meine...

Führst du also ein
Delphi-Quellcode:
P:=Img.Scanline[h];
aus, dann wird in P nur eine Adresse gespeichert. Mehr nicht. Aber: Dein P "zeigt" jetzt auf ein Stück Speicher.

Erst, wenn du mit dem P anfängst "zu arbeiten", wird es interessant, das das kein normaler Pointer ist, sondern ein typisierter Pointer.

Mit der Zeile
Delphi-Quellcode:
i:=P^[1];
muß Delphi anfangen, dein P wie ein Array zu behandeln. Das Stück Speicher, auf das dein P nun zeigt, wird erst jetzt als ein PixArray interpretiert :)

Ich bin sicher, irgendwo findet sich hier ein Tutorial zum Thema Zeiger/Pointer. Hat man das mit den Pointern begriffen, erkennt man die ungeheuer Mächtigkeit von Zeigern - aber auch die ziemlich gräßlichen Nebenwirkungen, die Zeiger haben können. Ein Beispiel (P sei wieder ein ^PixArray):

Delphi-Quellcode:
P:=Pointer(2);
P^[1]:=37;
Das ist absolut gültiger Code. Den wird Delphi auch klaglos übersetzen. Schließlich weist du einem Pointer einen anderen Pointer zu (das P bekommt also die Adresse 2 als Inhalt). Und dann greift du auf das erste Byte zu, auf das P zeigt. Nichts schlimmes also.

Läßt man das laufen, bekommt man was an die Ohren (Schutzverletzung an Adresse [bla], Schreiben von Adresse $00000002) ;)

Pointer gehören nicht umsonst zum am meisten gefürchteten Konstrukt in Coderkreisen, denn zahllose Einsteiger verstehen das Konzept erst nach einer Weile - und etliche raffen es nie.

Noch ein Tip: Wenn du im Code mit Zeigern arbeitest, dann benutze das Dach zum dereferenzieren. Ich weiß, das Delphi das nicht benötigt (war mal anders). Aber es hilft ungemein bei der Fehlersuche, wenn man sieht, das man nicht den Zeiger beschreibt, sondern das, auf das er zeigt ;) Spätestens, wenn man sich irgendwann mal mit C beschäftigt und dort praktisch sekündlich über Zeiger stolpert und sie dereferenzieren muß, freut man sich über diese Angewohnheit :D

[Edit]Hab ein Tutorial gefunden:
Zeiger-Tutorial


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:12 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz