Hallo,
ich habe einen Code entwickelt, mit dem man ein Bitmap mit bilinearer Interpolation stretchen kann. Ich habe darauf geachtet, dass der Code möglichste schnell und effizient ist, daher scheint er evtl. stellenweise etwas verwirrend, daher habe ich ihn gut kommentiert:
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: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83:
| procedure ResampleBilinear(Src, Dst: TBitmap); var X10, Y10, X11, Y11: Integer; X1, Y1: Single; Fx, Fy: Single; F: Single; I: Integer; X2, Y2: Integer; W1, H1, W2, H2: Integer; SrcRow0, SrcRow1, DstRow: PByteArray; begin Src.PixelFormat := pf24Bit; Dst.PixelFormat := pf24Bit;
W1 := Src.Width - 1; H1 := Src.Height - 1; W2 := Dst.Width - 1; H2 := Dst.Height - 1;
Fx := W1 / W2; Fy := H1 / H2;
for Y2 := 0 to H2 do begin Y1 := Y2 * Fy; Y10 := Trunc(Y1); Y11 := Y10; Y1 := Y1 - Y10; if (Y1 > 0) then Y11 := Y11 + 1;
SrcRow0 := Src.ScanLine[Y10]; SrcRow1 := Src.ScanLine[Y11]; DstRow := Dst.ScanLine[Y2];
for X2 := 0 to W2 do begin X1 := X2 * Fx; X10 := Trunc(X1); X1 := X1 - X10; X10 := X10 * 3; X11 := X10; if (X1 > 0) then X11 := X11 + 3;
DstRow[X2 * 3 ] := Round( SrcRow0[X10 ] * (1 - X1) * (1 - Y1) + SrcRow0[X11 ] * X1 * (1 - Y1) + SrcRow1[X10 ] * (1 - X1) * Y1 + SrcRow1[X11 ] * X1 * Y1); DstRow[X2 * 3 + 1] := Round( SrcRow0[X10 + 1] * (1 - X1) * (1 - Y1) + SrcRow0[X11 + 1] * X1 * (1 - Y1) + SrcRow1[X10 + 1] * (1 - X1) * Y1 + SrcRow1[X11 + 1] * X1 * Y1); DstRow[X2 * 3 + 2] := Round( SrcRow0[X10 + 2] * (1 - X1) * (1 - Y1) + SrcRow0[X11 + 2] * X1 * (1 - Y1) + SrcRow1[X10 + 2] * (1 - X1) * Y1 + SrcRow1[X11 + 2] * X1 * Y1); end; end; end; |
Zur Geschwindigkeit: sie (pro Pixel) ist nahezu unabhängig von der Größe des Quell- und Zielbitmaps, auf meinem P4-1800 etwa 6MPix/s, auf meinem P1-200MMX etwa 500kPix/s.
Wie funktioniert's?
Im Bild (siehe Anhang) zu sehen sind vier Pixel (rot, gelb, blau, grün), die einen Ausschnitt des Quellbitmaps darstellen sollen. Das schwarze Quadrat ist ein Pixel des Zielbitmaps, der berechnet werden soll. Die schwarzen Punkte stellen je die Mitte der Pixel dar. Nun liegt der Mittelpunkt des zu berechnenen Pixel zwischen vier Pixeln des Quellbitmaps. Legen wir den Mittelpunkt des roten Pixels als Nullpunkt fest, und den des grünen Pixels als 100%. Dann wäre der neue Pixel auf der X-Achse bei etwa 57% und auf der Y-achse bei etwa 84%.
Nun berechnen wir erstmal die "Mischfarben" auf der X-Achse. Nehmen wir Farbe1 für die beiden oberen Pixel - sie bestünde zu 43% (100% - 57%) aus rot und zu 57% aus gelb. Farbe2 nehmen wir für die unteren Pixel, das wären dann 43% blau und 57% grün.
Mit diesen beiden Farben können wir schon die Farbe des Zielpixels berechnen - nämlich 16% (100% - 84%) von Farbe1 und 84% von Farbe2. So erhalten wir genau die Farbe, die ein bilinearer Farbverlauf (also in 2 Richtungen - X und Y) an dieser Stelle hätte. Und so verfahren wir dann mit allen Pixeln des Bitmaps.
Ich hoffe, das war einigermaßen verständlich. Auf jeden Fall tut der Code, und das relativ schnell.
Moderiert von jasocul: Bild-Link durch Anhang ersetzt