![]() |
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:
in einer Procedure wird ein Zeiger auf einen Speicherbereich vom obigen Typ erstellt:
PixArray = Array [1..3] of Byte;
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:
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. |
Re: ScanLine anwenden, bzw die Hilfe verstehen
Eigentlich ganz simpel :wink:
Die Deklaration
Delphi-Quellcode:
macht aus deinem P erstmal einen Pointer. Nichts weiter.
P: ^PixArray;
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:
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.
P^[1]:=0;
Bis dahin ist ein Pointer einfach nur ein Pointer. HTH, Olaf |
Re: ScanLine anwenden, bzw die Hilfe verstehen
Hi Olaf,
Zitat:
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:
Dann ist ja p ein typisierter Pointer vom Typ Array. Wobei ein Array ja praktisch mehrere Pointer zusammengefaßt ist.
p: ^PixArray
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? |
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?
|
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. |
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:
Jetzt hast du ein einmal dein Typ, ein Array [1..3] of Byte, also Platz für genau 3 Byte.
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
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: |
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.
|
Re: ScanLine anwenden, bzw die Hilfe verstehen
Hoppala. Das kann sein, Sorry. Darum kümmert sich zum Glück ScanLine
|
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:
difiniere ich einen Typ, der aus 3 Elementen besteht zu jeweils einem Byte
type
PixArray = Array [1..3] of Byte; mit
Delphi-Quellcode:
difiniere ich p als ein Array, das 3 Speicheradressen zu Speichern von jeweils einem Byte aufnehmen kann.
p: ^PixArray;
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? |
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:
aus, dann wird in P nur eine Adresse gespeichert. Mehr nicht. Aber: Dein P "zeigt" jetzt auf ein Stück Speicher.
P:=Img.Scanline[h];
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:
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 :)
i:=P^[1];
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:
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.
P:=Pointer(2);
P^[1]:=37; 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: ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:09 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz