Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Wie Image aus VCL-Formular-Datei (.dfm) auslesen (https://www.delphipraxis.net/179321-wie-image-aus-vcl-formular-datei-dfm-auslesen.html)

Harry Stahl 26. Feb 2014 17:59

Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Weiß jemand, in welchem Format Delphi die Images in der Formulardatei speichert? In der Textfassung sieht das ja ungefähr so aus:
Code:
object Image1: TImage
    Left = 48
    Top = 40
    Width = 105
    Height = 105
    HelpType = htKeyword
    HelpKeyword = 'g'
    Picture.Data = {
      07544269746D6170E6040000424DE60400000000000036000000280000001400
      0000140000000100180000000000B00400000000000000000000000000000000
Ich würde nun gern die Image mit einem externen Programm laden, manipulieren und wieder zurückschreiben. Kennt jemand einen einfachen Weg?

Bernhard Geyer 26. Feb 2014 18:49

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Die Daten sind Hex-Codiert.
In deinem Beispiel steht TBitmap + den Binärstream des Bitmaps drin.

himitsu 26. Feb 2014 19:05

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Mit TReader einlesen, in TWriter kopieren und dabei das Gewünschte verändern.
(man muß nur mal nachsehn, ob bei dem Textformat etwas beachtet werden muß, da das Format grundsätzlich erstmal binär ist ... eventuell gibt es auch irgendwo ein paar Konvertierungsfunktionen)

Man könnte auch ganz böse die DFM "laden", also eine Form-Insttanz erstellen (die Methodenzeiger müssen aber beachtet werden), ändert dann in der TImageInstanz das Bild und speichert die DFM danach wieder ab.

Perlsau 26. Feb 2014 19:20

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Mich würde interessieren, was der Hintergrund dieser Anforderung ist: Um an die Bitmap- bzw. Picture-Daten zu kommen, genügt doch einfach ein
Delphi-Quellcode:
Image1.Picture.SaveToFile(Dateiname);
... wozu benötigt man dann die DFM, um dort umständlich die Picture-Daten auszulesen und zu interpretieren?

Bernhard Geyer 26. Feb 2014 20:36

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Perlsau (Beitrag 1249759)
Mich würde interessieren, was der Hintergrund dieser Anforderung ist: Um an die Bitmap- bzw. Picture-Daten zu kommen, genügt doch einfach ein
Delphi-Quellcode:
Image1.Picture.SaveToFile(Dateiname);
... wozu benötigt man dann die DFM, um dort umständlich die Picture-Daten auszulesen und zu interpretieren?

Stimmt. Das wäre die Frage.
Wenn sowas austauschbar gestaltet werden soll gibt es 100 bessere und einfachere Lösungen als das im DFM zu machen.

himitsu 27. Feb 2014 02:20

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1249766)
Wenn sowas austauschbar gestaltet werden soll gibt es 100 bessere und einfachere Lösungen als das im DFM zu machen.

z.B. in die Resourcen und das Bild im OnCreate (oder so) ins Image laden. (wie man eine Resource 'ner EXE ändert, sollte sich leicht finden lassen)

Buddelfish 27. Feb 2014 07:02

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Also wenn ich eine DFM ohne PAS-Datei hätte und wollte das Bild haben, würde ich einfach ein neues Formular mit allen Komponeten der DFM-Datei erstellen, speichern, DFM austauschen, wieder laden und dann das Bild kopieren. Alles über die IDE und alles ohne Programmcode. Sollte in 1 Minute erledigt sein.

Vermutlich reicht es, ein TImage auf eine Form zu klatschen, speichern, DFM austauschen, laden, Fehler wegklicken, fertig.

Sir Rufo 27. Feb 2014 07:14

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Eventuell hilft ja dieser Link weiter

DFM Parser and Search Utility

Harry Stahl 27. Feb 2014 17:19

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von himitsu (Beitrag 1249757)
Mit TReader einlesen, in TWriter kopieren und dabei das Gewünschte verändern.

Leider habe ich mit TReader und TWriter gar keine Erfahrung (vermute mal, das verwendet man im Rahmen der Komponenten-Entwicklung?).

Ich habe mal versucht, ein Programm zu erzeugen, das zwei Images auf der Form hat. Nun wollte ich erst mal die Image2 laden und dann "kopieren" (wie Du schreibst) und in Image1 schreiben (die Image-Manipulation habe ich mir also erst einmal geschenkt).

So sieht der Source-Code aus:

Code:
procedure TForm43.FormCreate(Sender: TObject);
var
  writer: TWriter;
  reader : TReader;
  msr, msw: TMemorystream;
begin
  msr:= TMemorystream.create;
  msw := TMemorystream.create;

  reader := TReader.Create (msr,0);
  // Liest Komponente "Image2"
  reader.ReadComponent (Image2); // Hier kommt stream-lesefehler

  Writer := TWriter.Create (msw, 0);

  msr.position := 0;
  msw.CopyFrom (msr, msw.size);
  writer.WriteComponent (Image1);
end;
Bei der gekennzeichneten Zeile kommt der Fehler wie beschrieben.

Was mache ich falsch, bzw. muss ich anders machen?

Harry Stahl 27. Feb 2014 17:30

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Perlsau (Beitrag 1249759)
Mich würde interessieren, was der Hintergrund dieser Anforderung ist:...

Ich möchte diese Sache nicht in Programm selber machen, welches die DFM-Datei enthält, sondern mit einem externen Programm eine DFM-Datei laden (bzw. daraus nur die TImage-Komponente), diese dann manipulieren (z.B. von 24-Bit in ein transparentes 32-Bitmap ändern) und dann in die Form zurückschreiben (also in der textuellen Fassung).

Hintergrund ist u.a., dass z.B. bei der Konvertierung mit dem MIDA-Converter die i.d.R. 24-Bit Bitmaps auch in 24-Bit konvertiert werden. Dann werden diese aber mit den hässlichen Hintergrundfarben (z.B. Pink) angezeigt. Insofern möchte ich vorher mit einem kleinen Programm alle 24-Bit Bitmaps in einem Rutsch in 32-Bit Bitmaps ändern, die "Hintergrundfarbe" durch Transparenz ersetzen, damit diese Bitmaps dann in der FMX-Form transparent angezeigt werden können.

Wenn man z.B. 100 oder mehr TSpeedButtons in einer Form hat, mit entsprechenden Glpyhs, wäre es schon ein irrer Aufwand, hinterher manuell die Bitmaps bearbeiten zu müssen, das will ich mir halt ersparen (habe zwar den MIDA-Leuten vorgeschlagen, diese Option in deren Programm einzubauen, habe daher aber bislang noch keine Antwort erhalten [Sie wollten es prüfen]).

Harry Stahl 27. Feb 2014 17:35

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Sir Rufo (Beitrag 1249795)
Eventuell hilft ja dieser Link weiter

DFM Parser and Search Utility

Nein, leider nicht, das Programm sucht nur nach entsprechenden Komponenten, manipuliert sie aber nicht. Dennoch danke für den Link.

Bernhard Geyer 27. Feb 2014 19:00

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Harry Stahl (Beitrag 1249910)
Wenn man z.B. 100 oder mehr TSpeedButtons in einer Form hat, mit entsprechenden Glpyhs, wäre es schon ein irrer Aufwand, hinterher manuell die Bitmaps bearbeiten zu müssen, das will ich mir halt ersparen

Sind das auch 100 unterschiedliche Icons oder doch viele Icons mehrfach verwendet.
Ich würde mir ein zentralen Icon/Image-Pool aufbauen und in jedem Formular sich bei diesen bedienen.

Union 27. Feb 2014 23:48

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Leider weiß ja nur die Komponente selber, wie die binären Daten codiert sind. Es gibt da ja auch unterschiedliche Property-Namen, z.b. Picture, Bitmap, Image.Data, Glyph etc.

Ich würde das wie folgt lösen: Eine Designtime Komponente erstellen mit einem Editor, die keine Persistenz hat. Die beinhaltet dann einen Editor der über das Verb aufgerufen wird. In diesem Editor durchläufst Du alle Komponenten des Formulars und liest die Image-Objekte in eine Liste ein wobei Du Dir merkst welche Property welches Objekts das war. Die Listeneinträge kannst Du dann evtl. visualisieren, markieren und zur Konvertierung bestätigen. Daraufhin das Format entsprechend wandeln und die Komponenten-Eigenschaften aktualisieren.

Für die Umstellung eines Formulars müsstest Du zunächst die BPL installiert haben. Dann nur die Komponente fallen lassen, draufklicken und die Konvertierung bestätigen. Dann die Komponente wieder löschen und nächstes Formular. Alternativ könnte man natürlich auch einen Experten schreiben.

Harry Stahl 1. Mär 2014 15:04

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1249755)
Die Daten sind Hex-Codiert.
In deinem Beispiel steht TBitmap + den Binärstream des Bitmaps drin.

Ja, das stimmt, kann ich nun so bestätigen.

Handelt es sich z.B. um ein TSpeedButton mit einem Glyph, dann beginnt in dem Datenstream ab Offset 5 das eigentliche Bitmap. Vorher sind 4 Bytes, die Delphi anscheinend intern für die Komponente verwendet (nennen wir das mal "Komponentenheader"). So kann ich also ohne Probleme die Bitmap ab Position 5 auslesen.

Das Problem entsteht nun, wenn ich eine geänderte Bitmap zurückschreiben will. Denn die ersten 4 Bytes unterscheiden sich, je nach dem, welche Bit-Tiefe und welches Ausmaß die Bitmap hat. Eine echte Logik konnte ich da selber leider nicht erkennen.

Ist jemanden eine Beschreibung bekannt, nach welcher Systematik die ersten 4 Bytes in dem Stream ("Komponentenheader") für "Glpyh.data" (Tspeedbutton), "Picture.data" (TImage) oder "Bitmap" (TImagelist) geschrieben werden müssen?

Bernhard Geyer 1. Mär 2014 15:13

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Ich würde sagen das ist die Längenangabe:
In deinem Fall: E6040000
Intel (Little-Endian) Reihenfolge, also 000004E6 = 1254 Byte Datenlänge

Harry Stahl 1. Mär 2014 16:29

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Nein, Größenangaben können das m.E. nicht sein.

Ein 16x16 großes 24-Bit-Bitmap hat z.B. diese 4 Bytes als "Komponentenheader":

Code:
 Glyph.Data = {
    36030000...
Ein 16x16 großes 32-Bit-Bitmap hat z.B. diese 4 Bytes als Header:

Code:
 Glyph.Data = {
    36040000...
Ein 24x24 großes 24-Bit-Bitmap hat z.B. diese 4 Bytes als Header:

Code:
 Glyph.Data = {
   F6060000...

Bernhard Geyer 1. Mär 2014 16:51

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Harry Stahl (Beitrag 1250114)
Nein, Größenangaben können das m.E. nicht sein.

Ein 16x16 großes 24-Bit-Bitmap hat z.B. diese 4 Bytes als "Komponentenheader":

Code:
 Glyph.Data = {
    36030000...
Ein 16x16 großes 32-Bit-Bitmap hat z.B. diese 4 Bytes als Header:

Code:
 Glyph.Data = {
    36040000...
Ein 24x24 großes 24-Bit-Bitmap hat z.B. diese 4 Bytes als Header:

Code:
 Glyph.Data = {
   F6060000...

Wieso nicht?

16*16*24 Bit = 768 Byte <-> 0x0336 -> 822 Bildaten inkl. den 54 Byte BMP-Header
16*16*32 Bit = 1024 Byte <-> 0x0436 = 1078 Bildaten inkl. den 54 Byte BMP-Header
24*24*24 Bit = 1728 Byte <-> 0x06F6 = 1782 Bildaten inkl. den 54 Byte Bmp-Header

Harry Stahl 1. Mär 2014 17:19

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Lässt erst mal ein wenig Hoffnung aufkommen:-D

Wobei ich festgestellt habe, dass bei einem 16x16 großen 32-Bit-Bitmap auch dies dort stehen kann:
Code:
Glyph.Data = {
     36080000...
(der Bitmap-Stream ist dann auch 2102 Byte groß)

also, statt dem

Code:
Glyph.Data = {
  36040000...
Anscheinend können 32-Bit-Bitmaps, 16x16 Size auch unterschiedlichen Speicherbedarf annehmen, dachte die wären bei gleichem Ausmaß und Bit-Tiefe immer gleich groß.

Harry Stahl 1. Mär 2014 17:25

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Hast Du noch einen Tipp, wie ich die Größe des Streams (Integerwert) in den HEX-Wert, Little Endian-Reihenfolge konvertiere? Gibt es dafür eine fertige Funktion?

Bernhard Geyer 1. Mär 2014 19:05

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Harry Stahl (Beitrag 1250119)
Lässt erst mal ein wenig Hoffnung aufkommen:-D

Wobei ich festgestellt habe, dass bei einem 16x16 großen 32-Bit-Bitmap auch dies dort stehen kann:
Code:
Glyph.Data = {
     36080000...
(der Bitmap-Stream ist dann auch 2102 Byte groß)

Das müsste ich dann man mit einem eigenen Delphi und einer Vollständigen Hexwert (in einem Beispielformular) nachvollziehen.

Harry Stahl 1. Mär 2014 21:33

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Bernhard Geyer (Beitrag 1250129)
Das müsste ich dann man mit einem eigenen Delphi und einer Vollständigen Hexwert (in einem Beispielformular) nachvollziehen.

Grundsätzlich noch mal der Hinweis, dass Du recht hattest, es wird tatsächlich dort die Größe gespeichert. Was mich gerade ein wenig irritiert ist, dass die Bitmaps im .BMP-Foprmat unterschiedliche Größen haben können, bei gleichem Ausmaß und gleicher Bittiefe.

Ich habe hier mal ein Formular hochgeladen und die beiden verwendeten Bitmaps.

himitsu 1. Mär 2014 22:08

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Die Idee einfach mal in TPicture.WriteData, TPicture.ReadData, TBitmap.WriteData und TBitmap.ReadData reinzuschauen hatte noch Keiner?

Auf die Idee dort reinzugucken, wäre man gekommen, wenn man in TPicture.DefineProperties und TGraphic.DefineProperties geschaut hätte. :angel:

Harry Stahl 1. Mär 2014 22:48

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Danke, guter Hinweis.

Union 1. Mär 2014 23:42

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Liste der Anhänge anzeigen (Anzahl: 4)
Ich habe dafür eine Komponente erstellt. Die installiert sich unter Samples/TImageConverter. Die setzt man auf das Form mit den Controls, die Grafiken enthalten. Über das Kontextmenü der Komponente wählt man 'Images dieses Formulares bearbeiten' aus. Dann öffnet sich ein Dialog in dem die Komponenten angezeigt werden mit der Auflösung der Grafiken. Beim Klick auf "Speichern" werden alle Grafiken auf 32 bit Farbtiefe gesetzt. Danach ist die Komponente wieder zu entfernen und die evtl. eingefügte unit "ImageConvertEditor" aus den uses zu entfernen.

Die dpk:
Delphi-Quellcode:
package ImageConvertPackage;

{$R *.res}
{$R 'ImageConvertEditor.dcr'}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO OFF}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS OFF}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO OFF}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE RELEASE}
{$ENDIF IMPLICITBUILDING}
{$DESCRIPTION 'Image converter'}
{$DESIGNONLY}
{$IMPLICITBUILD ON}

requires
  rtl,
  vcl,
  designide;

contains
  ImageConvertEditor in 'ImageConvertEditor.pas',
  uImageConvertEditor in 'uImageConvertEditor.pas' {frmImageConvertEditor};

end.
Die Komponente, die nichts weiter macht als auf einen Doppelklick bzw, die Auswahl des Kontextmenüs zu reagieren:
Delphi-Quellcode:
unit ImageConvertEditor;


interface

uses System.Classes, DesignIntf, DesignEditors;

type

  TImageConvertEditor = class(TComponentEditor)
  public
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): string; override;
    function GetVerbCount: Integer; override;
    procedure ShowImageConvertEditor;
  private
    TheDesigner: DesignIntf.IDesigner;
  end;

  TImageConverter = class(TComponent)
  end;

procedure Register;

implementation

uses Dialogs, Forms, uImageConvertEditor;

{ TImageConvertEditor }
procedure TImageConvertEditor.ExecuteVerb(Index: Integer);
begin
  inherited;
  case Index of
  0 : ShowImageConvertEditor;
  end;
end;

function TImageConvertEditor.GetVerb(Index: Integer): string;
begin
  case Index of
  0 : result := 'Images dieses Formulares bearbeiten';
  end;
end;

function TImageConvertEditor.GetVerbCount: Integer;
begin
  result := 1;
end;

procedure TImageConvertEditor.ShowImageConvertEditor;
var
  frmConvertEditor: TFrmImageConvertEditor;
begin
  TheDesigner := Self.Designer;
  frmConvertEditor := TFrmImageConvertEditor.Create(Application, TheDesigner.CurrentParent);
  frmConvertEditor.ShowModal;
  frmConvertEditor.Free;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TImageConverter]);
  RegisterComponentEditor(TImageConverter, TImageConvertEditor);
end;

end.
Der Dialog und die Beasrbeitungslogik, die relevante Bearbeitung findet in SaveImages statt:
Delphi-Quellcode:
unit uImageConvertEditor;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.ExtCtrls;

type
  TfrmImageConvertEditor = class(TForm)
    lvImages: TListView;
    lblTitle: TLabel;
    btnSave: TButton;
    btnCancel: TButton;
    ImagePreview: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure lvImagesSelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure btnSaveClick(Sender: TObject);
  private
    { Private-Deklarationen }
    ImageInfo : TStringList;
    ValidProperties : TStringList;
    FRootComponent: TComponent;
    procedure FillValidProperties;
    function GetBitmap(AData : TObject) : TBitmap;
    procedure SaveImages;
    function IsValidProperty(APropName : string) : boolean;
    procedure ProcessComponents(AParent : TComponent; var APath : string);
    procedure GetImageProperties(AComponent : TComponent; const APath : string);
    procedure ShowImages;
  public
    constructor Create(AOwner: TComponent; ARootComponent : TComponent); reintroduce;
  end;

var
  frmImageConvertEditor: TfrmImageConvertEditor;

implementation

uses
  RTTI, TypInfo;
{$R *.dfm}

procedure TfrmImageConvertEditor.FormDestroy(Sender: TObject);
begin
  ImageInfo.Free;
  ValidProperties.Free;
end;

function TfrmImageConvertEditor.GetBitmap(AData: TObject): TBitmap;
begin
  if AData is TBitmap then
    result := TBitmap(AData)
  else
  if AData is TPicture then
  begin
    result := TPicture(AData).Bitmap;
  end
  else
    result := nil;
end;

procedure TfrmImageConvertEditor.GetImageProperties(AComponent: TComponent; const APath : string);
var
  LRtCo : TRttiContext;
  LRtTyp : TRttiType;
  LRtProp : TRttiProperty;
  LPicture : TBitmap;
begin
  LRtCo   := TRttiContext.Create;
  LRtTyp  := LRtCo.GetType(AComponent.ClassType);

  for LRtProp in LRtTyp.GetProperties do
  begin
    if LRtProp.IsReadable and LRtProp.IsWritable then
    begin
      if IsValidProperty(LRtProp.Parent.Name + '.' +LRtProp.Name) then
      begin
        LPicture := TBitmap(LRtProp.GetValue(AComponent).AsObject);

        ImageInfo.AddObject(APath +
                            AComponent.Name + '.' +
                            LRtProp.Name +
                            ' : ' + AComponent.ClassName,
                            LPicture);
      end;
    end;
  end;
  LRtCo.Free;
end;

function TfrmImageConvertEditor.IsValidProperty(APropName: string): boolean;
begin
  result := ValidProperties.IndexOf(APropName) >= 0;
end;

procedure TfrmImageConvertEditor.lvImagesSelectItem(Sender: TObject;
  Item: TListItem; Selected: Boolean);
var
  LBitmap : TBitmap;
begin
  ImagePreview.Picture.Bitmap := nil;
  if Selected then
  begin
    LBitmap := GetBitmap(Item.Data);
    if Assigned(LBitmap) then
      ImagePreview.Picture.Bitmap.Assign(LBitmap);
  end;
end;

procedure TfrmImageConvertEditor.ProcessComponents(AParent : TComponent; var APath : string);
  procedure AddPath;
  begin
    APath := APath + AParent.Name + '.';
  end;

  procedure RemovePath;
  begin
     APath := copy(APath, 1, length(APath) - length(AParent.Name)-1);
  end;
var
  i : integer;
begin
  if (AParent <> nil)
    and (AParent <> Self) then
  begin
    GetImageProperties(AParent, APath);
    AddPath;
    for i := 0 to AParent.ComponentCount-1 do
    begin
      ProcessComponents(AParent.Components[i], APath);
    end;
    RemovePath;
  end;
end;

procedure TfrmImageConvertEditor.SaveImages;
var
  LBitmap : TBitmap;
  i : integer;
begin
  for i := 0 to ImageInfo.Count -1 do
  begin
    LBitmap := GetBitmap(ImageInfo.Objects[i]);
    if Assigned(LBitmap) and (LBitmap.Width > 0) then
    begin
      LBitmap.PixelFormat := pf32bit;
      // Zur Demonstration "Durchstreichen". Hier kann man natürlich
      // beliebige Sauereien anstellen

      //      LBitmap.Canvas.Pen.Width := 2;
      //      LBitmap.Canvas.Pen.Color := clred;
      //      LBitmap.Canvas.MoveTo(0, 0);
      //      LBitmap.Canvas.LineTo(LBitmap.Width, LBitmap.Height);
    end;
  end;
end;

procedure TfrmImageConvertEditor.ShowImages;
var
  i : integer;
  LItem : TListItem;
  DataBitmap : TBitmap;
  SmallBitmap : TBitmap;
  LargeBitmap : TBitmap;
begin
  for i := 0 to ImageInfo.Count -1 do
  begin
    DataBitmap := GetBitmap(ImageInfo.Objects[i]);
    LItem := lvImages.Items.Add;
    LItem.Data   := DataBitmap;
    LItem.Caption := ImageInfo[i];
    if Assigned(DataBitmap) then
    begin
      try
        SmallBitmap := TBitmap.Create;
        SmallBitmap.Width := 16;
        SmallBitmap.Height := 16;
        SmallBitmap.Canvas.StretchDraw(Rect(0,0,16,16), DataBitmap);

        LargeBitmap := TBitmap.Create;
        LargeBitmap.Width := 64;
        LargeBitmap.Height := 64;
        LargeBitmap.Canvas.StretchDraw(Rect(0,0,64,64), DataBitmap);

        lvImages.SmallImages.AddMasked(SmallBitmap, clFuchsia);
        lvImages.LargeImages.AddMasked(LargeBitmap, clFuchsia);

        LItem.ImageIndex := lvImages.SmallImages.Count-1;

        LItem.SubItems.Add(Format('%d x %d', [DataBitmap.Width, DataBitmap.Height]));
        LItem.SubItems.Add(GetEnumName(TypeInfo(TPixelFormat), Integer(DataBitmap.PixelFormat)));
      except
      end;
    end;
  end;
end;

procedure TfrmImageConvertEditor.btnSaveClick(Sender: TObject);
begin
  SaveImages;
end;

constructor TfrmImageConvertEditor.Create(AOwner, ARootComponent: TComponent);
begin
  inherited Create(AOwner);
  FRootComponent := ARootComponent;
end;

procedure TfrmImageConvertEditor.FillValidProperties;
begin
  ValidProperties.Add('TSpeedButton.Glyph');
  ValidProperties.Add('TBitBtn.Glyph');
  ValidProperties.Add('TImage.Picture');
end;

procedure TfrmImageConvertEditor.FormCreate(Sender: TObject);
var
  ARootPath : string;
begin
  lvImages.SmallImages := TImageList.Create(nil);
  lvImages.LargeImages := TImageList.Create(nil);
  ImageInfo := TStringList.Create;
  ValidProperties := TStringList.Create;
  FillValidProperties;
  ARootPath := '';
  ProcessComponents(FRootComponent, ARootPath);
  ShowImages;
end;

end.
Das Formular dazu:
Code:
object frmImageConvertEditor: TfrmImageConvertEditor
  Left = 0
  Top = 0
  Caption = 'Image Converter'
  ClientHeight = 277
  ClientWidth = 559
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poOwnerFormCenter
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  DesignSize = (
    559
    277)
  PixelsPerInch = 96
  TextHeight = 13
  object lblTitle: TLabel
    Left = 8
    Top = 8
    Width = 82
    Height = 13
    Caption = 'Gefundene Bilder'
  end
  object ImagePreview: TImage
    Left = 407
    Top = 148
    Width = 144
    Height = 121
    Anchors = [akRight, akBottom]
    Stretch = True
  end
  object lvImages: TListView
    Left = 8
    Top = 24
    Width = 393
    Height = 245
    Anchors = [akLeft, akTop, akRight, akBottom]
    Columns = <
      item
        AutoSize = True
        Caption = 'Komponente'
      end
      item
        AutoSize = True
        Caption = 'Gr'#246#223'e'
      end
      item
        AutoSize = True
        Caption = 'Format'
      end>
    TabOrder = 0
    ViewStyle = vsReport
    OnSelectItem = lvImagesSelectItem
  end
  object btnSave: TButton
    Left = 476
    Top = 22
    Width = 75
    Height = 25
    Anchors = [akTop, akRight]
    Caption = 'Speichern'
    ModalResult = 1
    TabOrder = 1
    OnClick = btnSaveClick
  end
  object btnCancel: TButton
    Left = 476
    Top = 53
    Width = 75
    Height = 25
    Anchors = [akTop, akRight]
    Caption = 'Abbruch'
    ModalResult = 2
    TabOrder = 2
  end
end
RC (ImageConvertEditor.rc)für die Erstellung der dcr:
Code:
TIMAGECONVERTER16 BITMAP "Graphic design 16.bmp"
TIMAGECONVERTER BITMAP "Graphic design 24.bmp"
TIMAGECONVERTER32 BITMAP "Graphic design 32.bmp"
Dafür ist in den Projektoptionen als Pre-Build-Ereignis folgendes einzutragen:
Code:
brcc32 -fo"ImageConvertEditor.dcr" "ImageConvertEditor.rc"

Harry Stahl 2. Mär 2014 01:32

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Wow, Union, ich bin beeindruckt, was Du da an Arbeit reingesteckt hast. Ist ein interessanter Ansatz und demonstriert einige interessante Techniken im Rahmen der Komponentenentwicklung (wovon ich leider nur sehr wenig Ahnung habe).

Ich habe es nun so gelöst, dass ich die Informationen zu allen Grafiken, die in einer VCL-Form sind, in einen Dialog einlese und habe diesen Dialog aber in mein Bildbearbeitungsprogramm eingebunden (habe dafür nun einen extra zu aktivierenden "Entwicklermodus" integriert, denn ein normaler Mensch braucht so etwas ja nicht).

So kann bei den Bildern nicht nur die Bit-Tiefe ändern, sondern eben alles das, was man mit einem Bildbearbeitungsprogramm machen kann. Änderungen kann ich dann einzeln oder für alle geänderten Bilder zurückschreiben.

Habe mal einen Screenshot des aktuellen Zustands des Dialogs angehängt. Wenn ich das fertig habe, poste ich noch mal eine Info dazu.

Union 2. Mär 2014 10:53

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Du kannst ja in der Komponente den Aufruf Deines Dialoges einbauen. Der darf dann natürlich keine Abhängigkeiten haben. Das wäre dann der Entwickler modus.

Harry Stahl 2. Mär 2014 22:33

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Union (Beitrag 1250169)
Du kannst ja in der Komponente den Aufruf Deines Dialoges einbauen.

Danke für das Angebot. Jedoch passt das leider nicht so insgesamt in den Ablauf, den ich hier vorgesehen habe. Ich will ja von extern auf die DFM-Datei zugreifen und nicht jedes mal für diesen Prozess eine Komponente einbauen. Außerdem will ich noch viel mehr mit den Grafiken machen können.

Vielleicht wird das alles ein wenig mehr verständlich, wenn man es in einem kurzen Video sieht. Obwohl die Funktion noch nicht fertig ist, habe ich diese als BETA gekennzeichnet in die aktuelle Version 6.19 von PixPower eingebunden und in meinem PixPower-Channel ein kurzes Video dazu hinterlegt.

Das könnt Ihr Euch hier ansehen:

http://youtu.be/_xDzDkmVqM8

Harry Stahl 3. Mär 2014 15:31

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Da das Einlesen der TImageList hier noch nicht 100% funktioniert (Delphi verwendet hier wohl eine Maskengrafik, um Transparenz darzustellen), habe ich noch mal eine weitere Möglichkeit gezeigt, um an die Bilder aus der TImageList zu gelangen und ALLE enthaltenen Grafiken in einem Rutsch zu exportieren und dann als einzelne Grafiken speichern zu können. So kann man mit 5 Minuten Aufwand seine bisherige ImageList unter FMX weiterverwenden oder die Grafiken sonst anderweitig verwenden.

Hier der Link zu diesem Video:

http://youtu.be/XNJSrbLV9x8

Bernhard Geyer 3. Mär 2014 16:09

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Harry Stahl (Beitrag 1250412)
Da das Einlesen der TImageList hier noch nicht 100% funktioniert (Delphi verwendet hier wohl eine Maskengrafik, um Transparenz darzustellen), ...

Delphi verlässt sich AFAIK hier teilweise auf die WinApi so das die Problem u.U. hierher kommen könnten.

Union 3. Mär 2014 17:06

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Nicht nur teilweise: Die Imagelist kapselt ein CommonControl. Der interne Aufbau der Daten ist damit u.U. sogar abhängig von der installierten Version des IE :(

himitsu 3. Mär 2014 17:18

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Joar und obwohl die Bilder gleich bleiben, meint die Versionierung öfters mal, daß sich in der DFM (ImageList) etwas/massig geändert hat, nur weil man die Form/Datenmodul neu speichert.

Furtbichler 4. Mär 2014 07:03

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Wie sieht denn nun eigentlich der Lösungsansatz genau aus? Nach den hier vorliegenden Hinweisen würde ich einen Parser vermuten, der aus der DFM die Deklarationen einer Image/ImageList-Komponente extrahiert und isoliert um darauf den jeweiligen Componentreader loszulassen. Ich würde mir noch nicht einmal die Mühe machen, das Hexkauderwelsch zu verstehen.

Das sollte doch für alle Komponenten gleich funktionieren. Man müsste sie nur registrieren und die Bildextrahierlogik auf Basis der konkreten Properties individuell programmieren. Dafür bietet sich eine Factory an. Der Pseudocode sähe so aus:

Delphi-Quellcode:
Procedure ExtractImages (aDFM : string; aImages : TList);
var
  index : Integer;
  imageComponentDFM : string;
  classType : TComponentClass;
  definition : string;
  extractor : IComponentImageExtractor;

Begin
  repeat
    imageComponentDFM := ExtractNextPortion(aDFM, index); // get next component definition
    if imageComponentDFM='' then break; // no more components
    SplitDFMIntoClassAndDefinition(imageComponentDFM, classType, definition);
    extractor := TComponentImageExtractorFactory.Create(classType);
    if extractor = nil then continue; // the class is not registered
    extractor.ReadComponentData(definition);
    extractor.ExtractImages(aImages);
  until false;
end;

type
  IComponentImageExtractor : interface
    Procedure ReadComponentData (aDefinition : String); // Will initialize the component from the DFM definition
    Procedure ExtractImages (aImages : TList); // Will copy the images from the component to the list
  end;

  TComponentImageExtractorFactory : class
    class procedure Create (aComponentClass : TComponentClass);
    class procedure Register (aExtractor : IComponentImageExtractor; aComponentClass : TComponentClass);
  end;
Für jede Komponente, die Bildinformationen enthält, implementiert man einen 'ComponentImageExtractor', der die beiden recht banalen Methoden implementiert und registriert die Klasse zusammen mit der Komponente, für die der Extractor gedacht ist (also TImage, TImageList, TBitBtn usw.)

Die Methode 'ReadComponentData' ist vermutlich für alle Komponenten gleich. Sie erwartet einen String der Form (o.ä.)
Delphi-Quellcode:
'object ImageList: TImageList #13#10 Bitmap = {494...} end'
und liest die Daten über
Delphi-Quellcode:
TStream.ReadComponent
ein.

Über die Factory kann man das dann problemlos und beliebig erweitern. Unübersichtlich ist es auch nicht, das die Aufgaben schon komplett verteilt sind.

Harry Stahl 4. Mär 2014 17:57

AW: Wie Image aus VCL-Formular-Datei (.dfm) auslesen
 
Zitat:

Zitat von Furtbichler (Beitrag 1250460)
Wie sieht denn nun eigentlich der Lösungsansatz genau aus? Nach den hier vorliegenden Hinweisen würde ich einen Parser vermuten, der aus der DFM die Deklarationen einer Image/ImageList-Komponente extrahiert und isoliert um darauf den jeweiligen Componentreader loszulassen.

Ja, Da vermutest Du richtig. Ich gebe zu, ich habe es mir (mal wieder) einfach gemacht. Ich gestehe, dass neben Standard-Datenbanken meine zweite Schwachstelle die Komponentenprogrammierung ist. Bevor ich mich jetzt in die da üblicherweise zu verwendenden Funktionen einarbeite, mache ich es eben so. Ich brauch letztlich auch "nur" noch ein paar Informationen über die "Header" der Grafikkomponenten rausfinden und dann bin ich auch schon fertig.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:44 Uhr.

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