Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi TMetaFile zuschneiden (https://www.delphipraxis.net/204208-tmetafile-zuschneiden.html)

Der schöne Günther 6. Mai 2020 20:47

TMetaFile zuschneiden
 
Liste der Anhänge anzeigen (Anzahl: 2)
Ja, ich habe die Suche zu "EMF" und "TMetaFile" bemüht.

Ich benutze den Steema TeeChart um mir eine Vektorgrafik erstellen zu lassen. Eine entsprechende Routine gibt ein
Delphi-Quellcode:
TMetaFile
zurück. Ich habe Null Erfahrung mit der Klasse. Ich stehe vor dem Problem dass das TMetaFile rechts und unten leeren Raum enthält wenn die Anwendung vom Betriebssystem hochskaliert wird. Also nicht "DPI-Aware" ist, Windows aber mit > 100 % Skalierung läuft.

Im Anhang einmal die EMF-Datei als PNG und einmal in einer ZIP-Datei.

Ich möchte dieses Bild zuschneiden. Ich habe keine Ahnung wie. Kann mir jemand einen Tipp geben?

PS: Wenn man das Manifest der Anwendung erweitert und sie selbst die Skalierung übernimmt stimmt die Grafik wieder. Die Anwendung "dpi aware" zu machen haben wir auf der Roadmap, aber eine schnelle Lösung für dieses TMetaFile wäre natürlich trotzdem schön.

TiGü 7. Mai 2020 09:12

AW: TMetaFile zuschneiden
 
Hm, tricky. Hatte ich mir einfacher und weniger aufwendig vorgestellt.
Ich habe es nur über den Umweg über ein TBitmap "geschafft".
Und die neue Datei ist auch 744 KB groß, gegenüber dem Original mit 47,8 KB. Da steht die Befürchtung im Raum, dass nicht wirklich die GDI-Befehle platzsparend in der neuen EMF-Datei stehen, sondern einfach nur das Bitmap (also als eine Ansammlung von Bytes) drin steckt.

Delphi-Quellcode:
procedure TForm1.btn1Click(Sender: TObject);
var
  Metafile: TMetafile;
  Metafile2: TMetafile;
  MetafileCanvas: TMetafileCanvas;
  OldSize, NewSize: TRect;
  Bitmap: TBitmap;
begin
  Metafile := TMetafile.Create;
  try
    Metafile.LoadFromFile('C:\Temp\metafile\metafile.emf');

    OldSize := TRect.Create(0, 0, Metafile.Width, Metafile.Height);
    NewSize := TRect.Create(0, 0, 680, 280);

    Bitmap := TBitmap.Create;
    try
      Bitmap.SetSize(OldSize.Width, OldSize.Height);
      Bitmap.Canvas.StretchDraw(OldSize, Metafile);
      Bitmap.SetSize(NewSize.Width, NewSize.Height);
      Bitmap.SaveToFile('C:\Temp\metafile\metafile2.bmp');

      Metafile2 := TMetafile.Create;
      try
        Metafile2.SetSize(NewSize.Width, NewSize.Height);

        MetafileCanvas := TMetafileCanvas.Create(Metafile2, 0);
        try
          MetafileCanvas.StretchDraw(NewSize, Bitmap);
        finally
          MetafileCanvas.Free;
        end;

        Metafile2.SaveToFile('C:\Temp\metafile\metafile2.emf');
      finally
        Metafile2.Free;
      end;
    finally
      Bitmap.Free;
    end;
  finally
    Metafile.Free;
  end;
end;

TiGü 7. Mai 2020 09:21

AW: TMetaFile zuschneiden
 
Schuß ins Blaue:
Über die Draw-Commmands/GDI-Befehle iterieren und die Befehle ignorieren, die den weißen Rand zeichnen und das auf ein neues Metafile umlenken.

https://docs.microsoft.com/de-de/win...numenhmetafile
https://docs.microsoft.com/de-de/pre...162606(v=vs.85)

Aufwand zu nutzen Verhältnis steht in Frage?
Gegenfalls einfach mal Steema anschreiben, ob man das beim Export einfach weg bekommt?

Der schöne Günther 7. Mai 2020 09:50

AW: TMetaFile zuschneiden
 
Bei Steema habe ich ehrlich gesagt die Hoffnung aufgegeben dass die noch etwas in die Delphi-Welt investieren. Vor ein paar Jahren war das noch besser, aber der Fehler ist denen seit 2015 bekannt. Er wurde 2018 nochmal ausgekramt und hat es anscheinend nicht mal im zweiten Anlauf in den Bugtracker geschafft.

Klar, das Problem lässt sich beheben indem man die Anwendung "Dpi aware" macht, aber das auch nicht an einem Nachmittag gemacht. Alternativ könnte ich in den Steema Source code einsteigen aber die Zeit investiere ich lieber um die VCL-Oberfläche mit DPI-Skalierung vertraut zu machen.

Redeemer 7. Mai 2020 10:37

AW: TMetaFile zuschneiden
 
Man kann auch den PENHMETAHEADER benutzen. Hier ein Beispiel aus einem anderen Kontext:
Delphi-Quellcode:
procedure TSVGMetafile.FixRDPBug(const Handle: HDC);
var
  Temp: TMemoryStream;
  Header: PENHMETAHEADER;
begin
  // Workaround für einen Fehler in RDP, durch den szlMillimeters und rclFrame falsch sind, wenn der Hauptbildschirm des verbindenden Rechners nicht das Seitenverhältnis 4:3 hat
  // vgl. https://stackoverflow.com/a/1533053
  // Kann leider nicht beim geladenen Bild geändert werden, da man an szlDevice (HORZRES und VERTRES bei GetDeviceCaps) nicht im Speicher des vorhandenen Bildes ran kommt
  // Bug scheint in Windows 10 nicht mehr zu existieren.

  // Besteht KEINE Remote-Desktop-Verbindung? Dann Abbruch.
  // Keine Ahnung, ob das Anfang August funktioniert hat, aber ich habe jetzt Ende Oktober keinen Self.Handle mehr, die Methode gibt daher 0 zurück, was anders als 320 bzw. 240 ist.
  // Daher wird der Handle von TMetafileCanvas übergeben.
  if GetDeviceCaps(Handle, HORZSIZE) <> 320 then Exit;
  if GetDeviceCaps(Handle, VERTSIZE) <> 240 then Exit;
  // Theoretisch können diese Werte auch ohne RDP auftreten, wenn der Nutzer einen 4:3-Bildschirm besitzt.
  // FixRDPBug macht korrekte Bilder nicht kaputt, daher geht es hier ausschließlich um die Performance, die bei einem Durchlauf des folgenden Codes verloren geht.

  Temp := TMemoryStream.Create();
  try
    Self.SaveToStream(Temp);
    if Temp.Size > 0 then
    begin
      Temp.Position := 0;
      Header := Temp.Memory;

      Header^.rclFrame.Right := (Header^.rclBounds.Right + 1) * 100;
      Header^.rclFrame.Bottom := (Header^.rclBounds.Bottom + 1) * 100;
      Header^.szlDevice.cx := 320; // gleiche Werte für beide szl-Felder machen es einfacher, Rundungsfehler hierüber zu vermeiden (man kann einfach mit 100 multiplizieren)
      Header^.szlDevice.cy := 240;
      Header^.szlMillimeters.cx := 320;
      Header^.szlMillimeters.cy := 240;

      inherited LoadFromStream(Temp);
    end;
  finally
    Temp.Free();
  end;
end;

Der schöne Günther 15. Mai 2020 19:44

AW: TMetaFile zuschneiden
 
Ich habe noch einen Workaround... naja eher Trick entdeckt der einem das Leben hier vielleicht etwas einfacher macht:
  1. Mache die Anwendung HighDPI-aware
  2. Setze in der DPR noch vor Application.Initialize() die Thread-DPI-Awareness auf "UNAWARE"
  3. Führe das Erstellen der Vektorgrafik in einem separaten Thread aus. Der Thread muss zu Beginn separat auf High-DPI-Aware gestellt werden.
  4. ???
  5. Gewinn!

Ein einzelner Thread kann mittels SetThreadDpiAwarenessContext seine DPI-Awareness ändern.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:43 Uhr.

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