Autor Beitrag
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: Sa 07.06.14 20:26 
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
Einloggen, um Attachments anzusehen!
_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19313
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 174
Erhaltene Danke: 43



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: 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

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 174
Erhaltene Danke: 43



BeitragVerfasst: Mo 16.06.14 11:28 
user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
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:
ausblenden volle Höhe 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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  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.
ausblenden Delphi-Quelltext
1:
2:
uses
  jpeg;

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: 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.

ausblenden 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

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 174
Erhaltene Danke: 43



BeitragVerfasst: 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.
ausblenden 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;
    {usw.}
  end;

implementation

  {...}

initialization
  TPicture.RegisterFileFormat('TGA''TGA graphics', TTGAImage);  // Do not localize

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: 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.

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

ausblenden 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

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 174
Erhaltene Danke: 43



BeitragVerfasst: 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.