Autor Beitrag
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 23.07.19 11:10 
Ich hab da eine Frage, die hauptsächlich theoretischer Natur ist, aber auch so ein klein wenig eine Grundsatzfrage ist.

Ausgangspunkt ist das Problem, ein (großes) Bild "schön" zu skalieren, d.h. in ein kleineres TImage zu laden. Mit der Eigenschaft Stretch des TImage wird das Ergebnis sehr pixelig und sieht nicht gut aus.

Über StretchBlt sieht das ganze deutlich besser aus, allerdings kommt diese Methode nicht mit Transparenzen klar - stattdessen gibt es dann einen schwarzen Hintergrund. Die Eigenschaft Transparent des TImage scheidet dabei auch aus, Stichwort Alphakanal und so. Das gibt ganz, ganz hässliche Ränder.

Durch etwas Recherche bin ich dann auf Windows Imaging Component (WIC) gestoßen, z.B. hier www.delphipraxis.net/1291613-post31.html

Das funktioniert sehr gut, läuft aber erst ab XP SP3. Nun ist das sicherlich keine relevante Hürde mehr, und tatsächlich möchte ich XP nicht weiter komplett unterstützen. Aber das hier ist eine Kleinigkeit, die aber sehr oft durchgeführt werden muss. Da hätte ich gerne einen Fallback auf die "unschöne" Methode.

Kann ich da einfach mit Try..except arbeiten, oder muss ich da anders ansetzen? Da komme ich dann zur Grundsatzfrage: Wie kann ich testen, ob das System ein bestimmtes Feature bereitstellt oder nicht?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
try
    WPic := TWICImage.Create;
    try
        WPic.LoadFromFile(aFilename);
        ResizeImage(WPic, SomeNewWidth); // siehe Link zur DP, https://www.delphipraxis.net/1291613-post31.html
        TargetBitmap.Assign(WPic);  // z.B. in ein TImage-Bitmap
    finally
        WPic.Free
    end;
except
    // Alternative Methode zum laden des Bildes
    // entweder pixelig, oder mit schwarzem Hintergrund statt Transparenz
end;


Oder kommt ohnehin direkt beim Start der Anwendung ne Fehlermeldung, wenn der Code im Binary steckt? :gruebel:

In meiner alten XP-VM habe ich "leider" auch schon SP3, kann das also nicht unmittelbar testen. :lol:

_________________
We are, we were and will not be.
Steku
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 23
Erhaltene Danke: 5



BeitragVerfasst: Di 23.07.19 13:11 
Nur so eine Idee zum Testen, vielleicht kannst du dein Programm
unter Windows in einem Kompatibilätsmodus ausführen.

Hier auf meinem Win7 PC kann ich Modi runter bis Win95 wählen
(auch WinXP SP2).

Grüße
Steku
Einloggen, um Attachments anzusehen!
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1321
Erhaltene Danke: 117

Win 10
RIO, CE, Lazarus
BeitragVerfasst: Di 23.07.19 18:57 
Hallo Daniel,

Für das Transparenzproblem würde ich mir im Bild als erstes eine Farbe suchen die nicht verwendet wird. Möglichst was reines (R G B) und volle Sättigung. Dann alle Pixel im Bild die Transparent sein sollen in der Farbe zeichnen und nach BitBlt / StretchBlt die Transparenzinfo mit Hilfe der Farbe wieder herstellen.
Durch das Glätten den Bildes werden die Farbwerte verändert / verwischt und du brauchst eine Strategie zum entscheiden wie stark die Transparenz für einen Pixel werden soll in dem nur ein Teil deiner Farbe enthalten ist. Das ist zugegeben nicht ganz einfach wenn es wirklich schön werden soll. Aber recht gut mit Nachbarschaftsbeziehungen zu lösen.

try except, so pauschal blind, ohne Einschränkung auf eine bestimmte Exception Klasse würde ich es nicht machen. Du solltest einen Test schreiben der die passende Exception auslöst, falls Du sie nirgendwo in der Doku finden kannst und einen deiner alt alt alt XP Nutzer fragen ob er den test mal laufen lassen kann um rauszubekommen ob es passt.

Aber, ganz eigentlich, würde ich alles vor XP SP3 nicht mehr unterstützen. Nennt man Nutzererziehung, wenn man sie zwingt sich zu verbessern und auf Win1.0 umzusteigen.

_________________
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 23.07.19 20:54 
Das hilft mir nicht so direkt weiter. :gruebel:

Der Kompatibilitätsmodus ist ja dafür nicht gedacht. Das funktioniert dann auch nicht - bzw. es funktioniert, was es unter XP SP2 nicht tun sollte. :nut:

@Stefan: Das reicht auch nicht so ganz. Quelle der Bilder, um die es geht, sind PNGs mit alpha-Kanal. Also nicht nur Transparenz ja/nein pro Pixel, sondern auch halbtransparente Bereiche. Die sind auch nötig, wenn die Grafiken sowohl auf dunklem, als auch auf hellem Grund schön dargestellt werden sollen. Selber was bauen ginge natürlich, aber das wäre für diese Stelle Overkill. 99% der Bilder brauchen keine Transparenz und sind rechteckig. Aber ab und zu, bzw. als Fallback, wenn das "eigentliche" Bild nicht gefunden werden kann, möchte ich da ein Symbol-Bild anzeigen lassen, das nicht unbedingt rechteckig ist - und für glatte Kanten braucht man dann Transparenz.

Generell wird auch XP SP3 nicht mehr voll unterstützt von mir, alleine schon wegen des HttpClients und den "neueren"
aHttpClient.SecureProtocols (TLS 1.1, 1.2). Da knallt es eh schon, wenn der Player ins Internet will. ;-)

Ist auch eher eine Frage aus Neugier, bzw. zur Weiterbildung. Ich weiß halt nicht, wie man auf eine bestimmte API-Funktion testen kann ... Vielleicht mach ich das am Ende auch einfach über eine Option in der settings.ini, die der User zur Not umstellen kann. Oder ich liefere nur das "kleine" Bild mit, und wenn der User größer skaliert, wird das nur zentriert in dieser Größe ausgegeben ...

Ist halt auch kein Key-Feature, was unbedingt sein muss. :lol:

_________________
We are, we were and will not be.
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Di 23.07.19 21:07 
Vielleicht hilft dir das weiter: www.delphipraxis.net/1095307-post20.html

Oder:
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:
procedure ScaleImage(const SourceImage, DestImage: TImage; DestWidth, DestHeight: Integer; aColor: TColor);
var
  W, H: Double;
  L, T: Integer;
  aRect: TRect;
  aImage: TImage;
begin
 aImage := TImage.Create(nil);

 try
  aImage.Canvas.Brush.Color := aColor;
  aImage.Canvas.FloodFill(00, aColor, fsBorder);

  W := SourceImage.Picture.Width;
  H := SourceImage.Picture.Height;

  {In diesen Fällen weichen die berechneten Werte für Breite und Höhe}
  {extrem von aWidth und aHeight ab, sodass eine Berechnung nicht erfolgt}
  if (H < DestHeight) and (W > DestWidth) then exit;
  if (H > DestHeight) and (W < DestWidth) then exit;

  if (H > DestHeight) and (W > DestWidth) then
   while (W > DestWidth) or (H > DestHeight) do
   begin
    W := W / 1.01;
    H := H / 1.01;
   end
  else
   while (W < DestWidth) or (H < DestHeight) do
   begin
    W := W * 1.01;
    H := H * 1.01;
   end;

  if (SourceImage.Picture.Width > DestWidth) and (SourceImage.Picture.Height > DestHeight) then
  begin
   L := DestWidth+1;
   T := DestHeight+1;

   L := L mod Trunc(W) div 2;
   T := T mod Trunc(H) div 2;
  end
  else
  begin
   L := 0;
   T := 0;
  end;

  aRect := Rect(L, T, Trunc(W)+L, Trunc(H)+T);

  aImage.Picture.Graphic.Width := DestWidth;
  aImage.Picture.Graphic.Height := DestHeight;
  aImage.Canvas.CopyMode := cmSrcCopy;
  aImage.Canvas.StretchDraw(aRect, SourceImage.Picture.Graphic);

  DestImage.Picture.Assign(aImage.Picture);
  DestImage.Width := aRect.Right;
  DestImage.Height := aRect.Bottom;

 finally
  aImage.Free;
 end;
end;

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 24.07.19 08:18 
Leider auch nicht. In der DP ist wieder die StretchBlt-Methode (Problem mit Transparenzen), und bei deinem Code wird StretchDraw von TImage verwendet, und das erzeugt pixelige Ergebnisse.

btw.: Die Skalierung in deinem Code kann man eleganter machen, hier mal ein Auszug aus meinem Code, der Source so anpasst, dass es in Bounds hineinpasst. Eine alternative Methode dafür sieht man im DP-Code. Die Größenänderung um je 1% in der Schleife ist nicht nötig. ;-)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
xFactor:= Bounds.Width / Source.Width;
yFactor:= Bounds.Height / Source.Height;
if xFactor > yFactor then
begin
      Bounds.Width := round(Source.Width * yFactor);
      Bounds.Height := round(Source.Height * yFactor);
      Factor := yFactor;
end else
begin
      Bounds.Width := round(Source.Width * xFactor);
      Bounds.Height := round(Source.Height * xFactor);
      Factor := xFactor;
end;


Im Anhang mal ein Beispiel, was ich meine. Links jeweils die automatische Skalierung durch TImage, in der Mitte das Transparenzproblem mit StretchBlt, rechts das Ergebnis mit WIC. Letzteres funktioniert super, nur da würde ich irgendwie gerne den optionalen Fallback einbauen, falls das nicht verfügbar ist.
Einloggen, um Attachments anzusehen!
_________________
We are, we were and will not be.
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mi 24.07.19 08:31 
user profile iconGausi hat folgendes geschrieben Zum zitierten Posting springen:
Leider auch nicht. In der DP ist wieder die StretchBlt-Methode (Problem mit Transparenzen), und bei deinem Code wird StretchDraw von TImage verwendet, und das erzeugt pixelige Ergebnisse.
Und wenn du AntiAliasing verwendest, verbesserst du - rein optisch - die Pixeligkeit, aber es wird unscharf...

Ich verwende den Code, um Fotos verzerrungsfrei zu skalieren, so als eine Art Thumbnail. Auf 100%-ige Qualität kommt es mir dabei nicht an.

Ich werde mir deine Verbesserung ansehen und ggf. einbauen, danke!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mi 24.07.19 10:17 
Moin!

Ich werfe mal zwei Gedanken in den Raum (für mehr hab ich leider grade keine Zeit :?):

Suche in der Entwickler-Ecke GRAPHICS32 für das Scaling-Problem

und

im wesentlichen das hier (als Ansatz): www.delphipraxis.net...-ob-dll-geladen.html für das Kompatibilitätsproblem (Kerngedanke: rausfinden, welche API-Calls in Windows erfolgen, die DLL ermitteln, prüfen ob die im System verfügbar ist, wenn ja, prüfen, ob die gewünschte Methode verfügbar ist; bei statischer Bindung von Methoden aus den Delphi-System-Bibliotheken wird das aber sowieso egal sein, weil dann die Exe beim Start direkt mit einem Fehler rausfliegt, weil die statische Bindung an den Prozess nicht geklappt hat).

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 24.07.19 12:53 
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
bei statischer Bindung von Methoden aus den Delphi-System-Bibliotheken wird das aber sowieso egal sein, weil dann die Exe beim Start direkt mit einem Fehler rausfliegt, weil die statische Bindung an den Prozess nicht geklappt hat).


Dann hat sich das an der Stelle ohnehin erledigt, und ich kann weder mit Try..Except noch mit einer Einstellung was retten.

Aber die Graphics32 werde ich mir mal näher anschauen - das sieht recht vielversprechend aus. Nur für diese Kleinigkeit werde ich das vermutlich nicht machen. Aber wie es aussieht, ist TBitmap32 daraus threadsafe, kann also auch außerhalb des VCL-Threads verwendet werden. Und das macht das Ding für mich sehr interessant ... :zustimm:

(Auch wenn das vermutlich einen Haufen Arbeit bedeutet.)

_________________
We are, we were and will not be.