Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Speicherzugriff mit Lücken
catweasel - Sa 07.06.14 20:26
Titel: Speicherzugriff mit Lücken
Hi,
Es geht nochmal um meine TTGAImage Klasse.
Inzwischen klappt das laden/speichern problemlos. Auch neue Dateien kann ich ertellen und auch (Daten)kopien erzeugen.
Ausserdem verwende ich jetzt nicht mehr Canvas.Pixels :wink:
Was jetzt aber noch fehlt ist das elegante gezielte Bearbeiten eines einzelnen Channels.
Im Moment löse ich das mit einer Doppelschleife, aber das muss doch irgendwie besser gehen...
Ich hatte mir überlegt das irgendiw mit einer Maske zu machen aber das würde mir ja nur was bringen wenn ch dann logische Operationen auf den ganzen Speicherbreich in einem Rutsch verknüpfen könnte. Ansonsten komm ich um die Schleife(n) nicht rum.
Gibt es soetwas wie FillChar() das mt einem angegebenen Muster einen Speicherbereich füllt?
Da könnte man sich dann die MAske temporär anlegen. Mit einem AND das den Bereich abdeckt könte man das ohne Schleifen lösen.
Beim Kopieren ins Bitmap ist es ja dank CopyMemory() recht einfach. Aber ich frage mich: Kann ich nicht den Zeiger des Bitmaps entsprechend verbiegen? Blödereweise kann man TBitmap.ScanLine ja nichts zuweisen.
Muss ich hier mit doppelten Daten leben oder gibts da nen Trick?
Anbei die neuste Version :)
Cheers,
Catweasel
jaenicke - So 08.06.14 12:49
Die schnellste Variante dürfte in Assembler pxor in Kombination mit den 128-Bit MMX Registern sein.
In einer Schleife durchgehen und immer in 4-Byte Schritten einen xor auf einen PInteger zu machen sollte aber auch schnell genug sein.
Blup - Di 10.06.14 12:37
Einige konstruktiv gemeinte Hinweise:
Wenn man ein bestimmtes Grafikformat unterstützen will, genügt es im allgemeinen eine Unit einzubinden, die den passenden Nachkommen von TGraphic registriert.
TTGAImageClass ist nicht von TGraphic abgeleitet, damit wird es schwer diese in Projekte einzubinden.
Im Constructor kann man ABitDepth angeben, tatsächlich wird aber nur 32Bit unterstützt.
Alle Speicheranforderungen und Methoden rechnen mit 32Bit.
Beim LoadFromFile wird der eigene Header-Record in jedem Fall aus der Datei überschrieben.
Wird das Dateiformat nicht unterstützt wird abgebrochen, die vorherige Grafik ist damit aber zerstört.
Ein temporärer Buffer könnte dieses Problem lösen.
Damit währe die Eigenschaft Valid überflüssig, diese wird sowieso intern nirgends ausgewertet.
Es wird kein Share-Mode beim Öffnen der Datei angegeben (z.B. fmShareDenyWrite).
Für neue Daten wird an vielen Stellen GetMem aufgerufen, aber vor jedem GetMem müsste eigentlich ein FreeMem stehen, um den vorherigen Inhalt von FData freizugeben. So entstehen vermutlich Speicherlecks.
Die Formatierung (insbesondere Einrückung) des Quelltextes sollte einheitlich sein.
catweasel - So 15.06.14 23:23
Hi,
Danke für die Tipps :)
Zitat: |
Wenn man ein bestimmtes Grafikformat unterstützen will, genügt es im allgemeinen eine Unit einzubinden, die den passenden Nachkommen von TGraphic registriert.
TTGAImageClass ist nicht von TGraphic abgeleitet, damit wird es schwer diese in Projekte einzubinden. |
Hier bin ich nicht sicher was du meinst. Wenn du die Unit UTGAImageClass zu deinem Projekt hinzufügst kannst du doch dann TGAImageClass verwenden, oder nicht?
Gib mir mal ein Beispiel wie der Vorteil ein Nachfahre von TGraphic zu sein aussieht?
Zitat: |
Im Constructor kann man ABitDepth angeben, tatsächlich wird aber nur 32Bit unterstützt.
Alle Speicheranforderungen und Methoden rechnen mit 32Bit. |
Als "Unterstützung für 24 bit" ist vorgesehen intern mit 32bit zu rechnen, und den Alphakanal intern mit einer dummy null zu ergänzen.
Ein bischen bin ich aber noch unschlüssig wie ich das handhaben soll.
Zitat: |
Beim LoadFromFile wird der eigene Header-Record in jedem Fall aus der Datei überschrieben.
Wird das Dateiformat nicht unterstützt wird abgebrochen, die vorherige Grafik ist damit aber zerstört. |
In der Tat. Den Header erstmal antesten ist sicher sinvoll. Wird auch direkt umgesetzt :)
Zitat: |
Damit währe die Eigenschaft Valid überflüssig, diese wird sowieso intern nirgends ausgewertet. |
Dies soll lediglich Rückmeldung nach aussen geben, ob z.b. ein Laden erfolgreich war. Ich weiss, LoadFromFile gibt ads schon direkt als Rückgabewert, aber ich dachte es kann nichts schden das uch später nochmal abfragen zu können. Beispielsweise wenn ich selbstherrlich entscheiden sollte nur quadratische Grafiken mit einer Kantenlänge von 2^n zuzulassen. Was wohl bei OpenGL Texturen der Fall sein wird.
(Obwohl man das auch im übergeordneten Texturobjekt unterbringen könnte...)
Zitat: |
Es wird kein Share-Mode beim Öffnen der Datei angegeben (z.B. fmShareDenyWrite). |
Du meinst das mehrere Leute/Threads gleichzeitig auf die Datei zugreifen? Das hatte ich eigentlich so nicht vorgesehen. Ich gebe zu das ist dann vielleicht etwas maßgenscheidert auf meien Bedürfnisse, aber TGA Dateien werden nur von der Anwednung erzeugt und geschrieben.
Der Anwender läd zum Beispiel ein par TGAs und wählt nun für das "Zielbild" Rotkanal von TGAx Grünkanal von TGAy, etc.
(So wie man das beim Materialeditor vom UDK machen kann).
Zitat: |
Für neue Daten wird an vielen Stellen GetMem aufgerufen, aber vor jedem GetMem müsste eigentlich ein FreeMem stehen, um den vorherigen Inhalt von FData freizugeben. So entstehen vermutlich Speicherlecks. |
Neuen Speicher alloziere ich eigentlich nur andrei Stellen:
1. Der Constructor. Klar, neues Bild braucht Platz. (Wird auch im Destructor wieder freigegeben)
2. Die LoadFromFile Methode. Auch ein geladenes Bild braucht Platz
3. Die Assign Methode. Hah! Und hier mache ich tatsächlich einen Fehler. Hier leckt die alte Grafik aus dem Speicher.
Das hatte ich tatsählich übersehen :)
Zitat: |
Die Formatierung (insbesondere Einrückung) des Quelltextes sollte einheitlich sein. |
NAch welcher Einheit denn? Vielleicht machst du es anders, aber zumindest ist mein Einrückungsstil konsequent durch den ganzen Quelltext. Ich fange nur nicht mit einer Einrückung an wie es Viele tun (Ausnahme ein "inherited")
Allzu konfus sollte es aber nicht sein ?!
Sobald ich das Ein oder Andere habe einfliessen lassen, gibts die neue Version :)
Cheers,
Catweasel
Blup - Mo 16.06.14 11:28
catweasel hat folgendes geschrieben : |
Zitat: |
Wenn man ein bestimmtes Grafikformat unterstützen will, genügt es im allgemeinen eine Unit einzubinden, die den passenden Nachkommen von TGraphic registriert.
TTGAImageClass ist nicht von TGraphic abgeleitet, damit wird es schwer diese in Projekte einzubinden. |
Hier bin ich nicht sicher was du meinst. Wenn du die Unit UTGAImageClass zu deinem Projekt hinzufügst kannst du doch dann TGAImageClass verwenden, oder nicht?
Gib mir mal ein Beispiel wie der Vorteil ein Nachfahre von TGraphic zu sein aussieht?
|
Also angenommen ich habe ein supertolles Grafikanzeigeprogramm geschrieben:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtDlgs, ExtCtrls, Menus;
type TForm1 = class(TForm) Image1: TImage; OpenPictureDialog1: TOpenPictureDialog; SavePictureDialog1: TSavePictureDialog; MainMenu1: TMainMenu; mmFile: TMenuItem; mmFileOpen: TMenuItem; N1: TMenuItem; mmFileSave: TMenuItem; mmFileSaveAs: TMenuItem; mmAnsicht: TMenuItem; mmZoomIn: TMenuItem; mmZoomOut: TMenuItem; procedure mmFileOpenClick(Sender: TObject); procedure mmFileSaveClick(Sender: TObject); procedure mmFileSaveAsClick(Sender: TObject); private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.mmFileOpenClick(Sender: TObject); begin if OpenPictureDialog1.Execute(Handle) then begin Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName); SavePictureDialog1.FileName := OpenPictureDialog1.FileName; mmFileSave.Enabled := True; mmFileSaveAs.Enabled := True; end; end;
procedure TForm1.mmFileSaveAsClick(Sender: TObject); begin if SavePictureDialog1.Execute(Handle) then begin Image1.Picture.SaveToFile(SavePictureDialog1.FileName); end; end;
procedure TForm1.mmFileSaveClick(Sender: TObject); begin Image1.Picture.SaveToFile(SavePictureDialog1.FileName); end;
end. |
Jetzt wünscht sich irgend ein Anwender aber, das er neben Bitmap-Dateien auch JPGs anzeigen und speichern kann.
Also binde ich irgendwo die "jpeg.dcu" in mein Projekt ein (ich brauch nicht mal den Quelltext) und schon kann mein Programm auch diese Anforderung erfüllen.
Der Nächste möchte vieleicht auch PNG-Dateien öffnen oder eben TGA.
Das konkrete Dateiformat spielt für mein Programm überhaupt keine Rolle.
Ich arbeite mit TPicture oder objektorientiert mit TGraphic.
catweasel - Mo 16.06.14 11:51
Ah, ok. Jetzt weiss ich was du meinst.
Woher sollte ein TImage (oder TPicture) wissen wie ein TGA zu rendern ist?
Ich verwende das Bitmap von TPicture um darauf zu zeichnen.
Delphi-Quelltext
1:
| if Assigned(PicA) then PicA.DrawTo(imgdisplayA.Picture.Bitmap); |
Ich könnte jetzt noch einen Nachfahren von TImage basteln der das intern macht...
Ich gebe zu, mir ist der Mechnaismus nicht klar: unit einbinden -> schon "versteht" TImage wie die Grafikdaten zu interpretieren und anzuzeigen (im internen bitmap) sind.
Cheers,
Catweasel
Blup - Di 17.06.14 10:52
TGraphic bietet eine Reihe virtuell-abstracte Methoden, die zum Beispiel die Grafik aus einer Datei lesen, speichern, die Größe angeben oder auf einen Canvas zeichnen.
Die abgeleitete Klasse muss diese implementieren und wird bei TPicture registriert.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| type TTGAImage = class(TGraphic) procedure Draw(ACanvas: TCanvas; const Rect: TRect); override; procedure LoadFromStream(Stream: TStream); override; procedure SaveToStream(Stream: TStream); override; end;
implementation
initialization TPicture.RegisterFileFormat('TGA', 'TGA graphics', TTGAImage); finalization TPicture.UnregisterGraphicClass(TTGAImage);
end. |
Das TImage ist eigentlich nur das Fenster für die Anzeige und stellt den Canvas zur Verfügung.
Das TPicture kapselt die Aufgaben die mit dem Bild zusammenhängen. Allerdings werden die meisten Aufgabe einfach nur an einen Nachkommen von TGraphic weitergeleitet.
Beim Öffnen einer Datei erzeugt sich TPicture dafür abhängig von der Datei-Erweiterung mit Hilfe der internen Faktory die richtige Ableitung von TGraphic.
catweasel - Di 17.06.14 12:40
Ok. Das fängt nun an Sinn zu machen :D
Nur eine Sache dürfte ich so nicht umsetzen können:
Bei meiner DrawTo Variante bestimmt das TGA wie groß das Bitmap ist und passt es entsprechend an.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TTGAImageClass.DrawTo(Bmp:TBitmap); var p : TRGBAPointer; begin Bmp.Width := FHeader.ImgWidth; Bmp.Height := FHeader.ImgHeight; Bmp.PixelFormat := pf32bit; p := Bmp.ScanLine[FHeader.ImgHeight-1]; CopyMemory(p,FData,FHeader.ImgWidth*FHeader.ImgHeight*Channels); end; |
Zuerst hatte ich auch "nur" einen Canvas übergeben, aber dann kann ich die Größe eben nicht ändern...
Delphi-Quelltext
1: 2:
| Bmp.Width := FHeader.ImgWidth; Bmp.Height := FHeader.ImgHeight; |
Ein TCanvas hat nunmal keine Width/Height properties...
Gibt es da auch etwas abstaktes in TGraphic das ich implementieren muss damit die Bildgröße dann von TImage/TPicture korrekt geändert wird?
Cheers,
Catweasel
Blup - Di 17.06.14 13:02
Die Größe wird von dem bestimmt, der die Grafik anzeigen will.
Du musst die Grafik entsprechend dem Rechteck skaliert zeichnen.
Der Canvas kann dabei auch maskiert sein, so das tatsächlich nur ein Teil der Grafik dargestellt wird.
Mit AssignTo kann deine Grafik an ein anderes Grafikobjekt übergeben werden.
Ist das angegebene Objekt z.B. eine TBitmap, kannst du die Größe und Farbtiefe entsprechend deine Vorstellungen anpassen.
Edit:
TGraphic hat bereits Properties die die interne Größe der Grafik angeben, die müssen natürlich überschrieben werden.
TImage berücksichtigt diese bei der Darstellung.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!