Entwickler-Ecke

Multimedia / Grafik - Bitmap schnell skalieren


Martok - Mo 12.03.12 23:32
Titel: Bitmap schnell skalieren
Hallo!

Ich bin echt irritiert, dass dazu nichts in der Library ist - oder ich es nicht finde. ;)

Wie auch immer: ich habe ein TBitmap und ein TCanvas eines Fensters. Das Bitmap soll gestretcht werden. Das grundsätzlich in ganzzahligen Faktoren, also 1fache, doppelte, 3fache Größe etc (immer vergrößern also).
StretchDraw hat da ein Problem: es ist eben eine Allzweck-Funktion und als solche etwas langsam - das geht manuell mit Sicherheit schneller. Glaub ich :think:

Das Problem ist aber, dass die üblichen ScanLine-basierten Varianten natürlich ein Bitmap als Ziel brauchen. Ich möchte aber nicht noch ein Bitmap haben. Das Fenster ist ja im Prinzip schon eins ;)


Also: was ist schneller als StretchDraw und kann ein Bitmap fix skaliert auf ein Canvas werfen?

Viele Grüße,
Martok


bummi - Di 13.03.12 01:19

ich weiß nicht ob es schneller ist, aber man kann auch das Canvas vor dem "Malen" transformieren

http://www.delphipraxis.net/156716-grafiken-sehr-einfach-animieren-sprites-mit-drehung-und-zoom.html

unit ExCanvasTools;


jaenicke - Di 13.03.12 07:45

Am schnellsten ist da vermutlich das schnell selbst zu implementieren. Hätte ich jetzt 10 Minuten würde ich es machen, aber ich muss gleich los...
Die Idee ist simpel:
Größe passend setzen, dann die Zeilen von unten nach oben mit Scanline durchgehen.


Martok - Di 13.03.12 08:06

Danke für die Ideen!

user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
ich weiß nicht ob es schneller ist, aber man kann auch das Canvas vor dem "Malen" transformieren
Warum grade ich das übersehen hab, löse doch sonst alles mit GM_ADVANCED... ist aber exakt gleich schnell.

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Größe passend setzen, dann die Zeilen von unten nach oben mit Scanline durchgehen.
Schon klar, aber wie ich schrieb:
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Ich möchte aber nicht noch ein Bitmap haben. Das Fenster ist ja im Prinzip schon eins
Ich mein, nochmal 5MB für ein weiteres Offscreen-Bitmap wäre nicht das Problem, aber schöner würde ich das schon finden. Schon weil ich vermute, dass nochmal umkopieren dann auch ncht schneller wird ;)


jaenicke - Di 13.03.12 08:25

Wo habe ich etwas von einer zusätzlichen Bitmap geschrieben? :gruebel:

Ich meine sowas, mit den äußersten Pixeln bin ich grad nicht sicher ohne es zu testen, den Rest machst du schon:

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:
type
  TPseudoRGB = packed record
    First: Word;
    Last: Byte;
  end;
  PPseudoRGB = ^TPseudoRGB;
var
  Test: TBitmap;
  NewLine, OldLine, LastOldLine, OldWidth: Integer;
  x, x2, OldPixel, NewPixel1, NewPixel2, NewPixel3: PPseudoRGB;
begin
  Test := TBitmap.Create;
  try
    Test.LoadFromFile(...);
    Test.PixelFormat := pf24bit;
    LastOldLine := Test.Height - 1;
    OldWidth := Test.Width - 1;
    Test.SetSize(Test.Width * 3, Test.Height * 3);
    for OldLine := LastOldLine downto 0 do
    begin
      OldPixel := Test.ScanLine[OldLine];
      NewLine := OldLine * 3;
      NewPixel1 := Test.ScanLine[NewLine];
      NewPixel2 := Test.ScanLine[NewLine + 1];
      NewPixel3 := Test.ScanLine[NewLine + 2];
      for x := 1 to OldWidth do
      begin
        for x2 := 1 to 3 do
        begin
          NewPixel1.First := OldPixel.First;
          NewPixel1.Last := OldPixel.Last;
          Inc(NewPixel1);
          NewPixel2.First := OldPixel.First;
          NewPixel2.Last := OldPixel.Last;
          Inc(NewPixel2);
          NewPixel3.First := OldPixel.First;
          NewPixel3.Last := OldPixel.Last;
          Inc(NewPixel3);
        end;
        Inc(OldPixel);
      end;
    end;
    Test.SaveToFile(...);
  finally
    Test.Free;
  end;


Blup - Di 13.03.12 11:34

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Ich meine sowas, mit den äußersten Pixeln bin ich grad nicht sicher ohne es zu testen, den Rest machst du schon:

Da gibt es tatsächlich noch Probleme, aber so gehts:

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:
procedure SkaliereBMP(ABitmap: TBitmap; AFaktorX, AFaktorY: Integer);
type
  TBGR = packed record
    B: Byte;
    G: Byte;
    R: Byte;
  end;
  PBGR = ^TBGR;
var
  OldWidth, OldHeight, NewWidth, NewHeight, x1, x2, y1, y2: Integer;
  OldPixel, NewPixel: PBGR;
begin
  ABitmap.Pixelformat := pf24Bit;
  OldWidth  := ABitmap.Width;
  OldHeight := ABitmap.Height;
  NewWidth  := OldWidth  * AFaktorX;
  NewHeight := OldHeight * AFaktorY;
  {horizontal Skalieren}
  if AFaktorX > 1 then
  begin
    ABitmap.SetSize(NewWidth, OldHeight);
    for y1 := 0 to OldHeight - 1 do
    begin
      OldPixel := ABitmap.ScanLine[y1];
      NewPixel := OldPixel;
      Inc(OldPixel, OldWidth);
      Inc(NewPixel, NewWidth);
      for x1 := OldWidth - 1 downto 0 do
      begin
        Dec(OldPixel);
        for x2 := AFaktorX - 1 downto 0 do
        begin
          Dec(NewPixel);
          NewPixel^ := OldPixel^;
        end;
      end;
    end;
  end;
  {vertikal Skalieren}
  if AFaktorY > 1 then
  begin
    ABitmap.SetSize(NewWidth, NewHeight);
    for y1 := OldHeight - 1 downto 0 do
    begin
      OldPixel := ABitmap.ScanLine[y1];
      for y2 := AFaktorY - 1 downto 0 do
      begin
        NewPixel := ABitmap.ScanLine[y1 * AFaktorY + y2];
        Move(OldPixel^, NewPixel^, SizeOf(TBGR) * NewWidth);
      end;
    end;
  end;
end;


Martok - Di 13.03.12 21:58

Danke euch Beiden. Funktioniert wunderbar - ist aber noch langsamer :D

Allerdings hat mich das dazu gebracht, mal etwas zu testen, und ich hab einfach mal ein Bitmap in der Endgröße ohne irgendwelche Bearbeitung geblittet. Wie sich rausstellt, ist tatsächlich das Kopieren der Pixel an sich so langsam, die Skalierung ist schon verdammt schnell.

Wobei langsam auch relativ ist. Schnell genug ist das auch so, aber: 60FPS kosten 80% auf einem Kern, da ist nicht mehr viel Luft für Logik...

Immerhin: falls mal jemand so eine Funktion sucht, wird er jetzt eine finden :zustimm:


uall@ogc - Mi 14.03.12 22:26

StretchDraw ist ziemlich schnell. Wichtig ist, dass du nicht die Farbtiefe änderts d.h. das Pixelformat des ZielBitmaps sollte auf das des Ausgangsbitmaps gesetzt werden. Außerdem solltest du das Zielbitmap nur ein einziges mal erstellen wenn du mehrere Frames bearbeitest. Farbraumwechsel + Speicher reservieren ist langsam, wobei ersteres noch vernachlässigbar ist.


platzwart - Do 15.03.12 00:56

Schau dir mal die Graphics32 an (dort beispielsweise TBitmap32 oder so ähnlich). Die sind extrem schnell. Hatte mir bei einem Projekt gegenüber dem Standard TBitmap einen Performanceschub von über dem 50-fachen gebracht. Konnte damit auf einem Vierkernprozessor 4 Bilder gleichzeitig verarbeiten und dabei 110.000.000 Pixelzugriffe pro Sekunde realisieren...


mvollmer - Do 15.03.12 13:03

[OT]
http://graphics32.org/wiki/Main/TBitmap

Zitat:
Main /
TBitmap

WTF?

:D
[/OT]

Btw: Was ist mit StrechtBlt?