211 lines
7.0 KiB
ObjectPascal

unit Resample;
interface
uses
Windows, Math, Graphics;
function StretchBmp(SrcBitmap, DstBitmap: TBitmap;
DstWidth, DstHeight: Integer; Is32bit: Boolean): Boolean;
implementation
const
FixedBits = 16;
FixedOne = 1 shl FixedBits;
FixedOneHalf = FixedOne shr 1;
type
TWeight = packed record
Offset: Integer; //Byte offset to pixel data
case Integer of
0: (Weight: Integer); //Pixel weight in Q16.16 fixed point format
1: (Temp: Single); //same thing in float format
end;
TWeightArray = array [0..MaxInt div SizeOf(TWeight) - 1] of TWeight;
TPutPixelProc = procedure(const Weights: array of TWeight; Bits, Pixel: Pointer);
procedure ResampleBits(DstSize, SrcSize: Integer; SrcLine, DstLine: Pointer;
PixelSize, LineCount, SrcLineSize, DstLineSize: Integer; PutPixelProc: TPutPixelProc);
var
I, J, Count: Integer;
Limit, Scale, X, Y, Center, Sup, Sum: Single;
Weights: ^TWeightArray;
Src, Dst: Pointer;
const
FilterWidth = 2.0;
begin
Scale := SrcSize / DstSize;
if Scale < 1.0 then
Limit := 1.0
else
Limit := 1.0 / Scale;
Sup := FilterWidth / Limit;
GetMem(Weights, Trunc(Sup * 2.0 + 2.0) * SizeOf(TWeight));
try
for I := 0 to DstSize - 1 do begin
Count := 0;
Sum := 0;
Center := (I + 0.5) * Scale;
for J := Floor(Center - Sup) to Ceil(Center + Sup) do begin
X := Abs(J - Center + 0.5);
if X > Sup then Continue;
X := X * Limit;
{Resampling filter}
if X < 1.0 then //SPLINE16
Y := Sqr(X) * (X - 9 / 5) - 1 / 5 * X + 1
else
Y := Sqr(X - 1) * (-1 / 3 * (X - 1) + 4 / 5) - 7 / 15 * (X - 1);
{The code from above must be kept in sync with FilterWidth value}
if (Y = 0) or (J < 0) or (J >= SrcSize) then Continue;
with Weights[Count] do begin
Temp := Y;
Offset := J * PixelSize;
end;
Sum := Sum + Y;
Inc(Count);
end;
if Sum <> 0 then begin
Sum := FixedOne / Sum;
for J := 0 to Count - 1 do
with Weights[J] do
Weight := Round(Temp * Sum);
end else
Count := 0;
Src := SrcLine;
Dst := DstLine;
for J := 0 to LineCount - 1 do begin
PutPixelProc(Slice(Weights^, Count), Src, Dst);
Inc(PByte(Src), SrcLineSize);
Inc(PByte(Dst), DstLineSize);
end;
Inc(PByte(DstLine), PixelSize);
end;
finally
FreeMem(Weights);
end;
end;
//Process pixel in BGR format
procedure PutPixel24(const Weights: array of TWeight; Bits, Pixel: Pointer);
type
PRGBTriple = ^TRGBTriple;
var
I, R, G, B: Integer;
begin
R := FixedOneHalf;
G := FixedOneHalf;
B := FixedOneHalf;
for I := 0 to High(Weights) do
with Weights[I], PRGBTriple(PAnsiChar(Bits) + Offset)^ do begin
Inc(R, rgbtRed * Weight);
Inc(G, rgbtGreen * Weight);
Inc(B, rgbtBlue * Weight);
end;
with PRGBTriple(Pixel)^ do begin
//Clamps all channels to values between 0 and 255
if R > 0 then if R < 255 shl FixedBits then rgbtRed := R shr FixedBits else rgbtRed := 255 else rgbtRed := 0;
if G > 0 then if G < 255 shl FixedBits then rgbtGreen := G shr FixedBits else rgbtGreen := 255 else rgbtGreen := 0;
if B > 0 then if B < 255 shl FixedBits then rgbtBlue := B shr FixedBits else rgbtBlue := 255 else rgbtBlue := 0;
end;
end;
//Process pixel in BGRA premultiplied alpha format
procedure PutPixel32P(const Weights: array of TWeight; Bits, Pixel: Pointer);
var
I, R, G, B, A: Integer;
AByte: Byte;
begin
R := FixedOneHalf;
G := FixedOneHalf;
B := FixedOneHalf;
A := FixedOneHalf;
for I := 0 to High(Weights) do
with Weights[I], PRGBQuad(PAnsiChar(Bits) + Offset)^ do begin
Inc(R, rgbRed * Weight);
Inc(G, rgbGreen * Weight);
Inc(B, rgbBlue * Weight);
Inc(A, rgbReserved * Weight);
end;
//Clamps alpha channel to values between 0 and 255
if A > 0 then if A < 255 shl FixedBits then AByte := A shr FixedBits else AByte := 255 else AByte := 0;
with PRGBQuad(Pixel)^ do begin
rgbReserved := AByte;
I := AByte shl FixedBits;
//Clamps other channels to values between 0 and Alpha
if R > 0 then if R < I then rgbRed := R shr FixedBits else rgbRed := AByte else rgbRed := 0;
if G > 0 then if G < I then rgbGreen := G shr FixedBits else rgbGreen := AByte else rgbGreen := 0;
if B > 0 then if B < I then rgbBlue := B shr FixedBits else rgbBlue := AByte else rgbBlue := 0;
end;
end;
function StretchBmp(SrcBitmap, DstBitmap: TBitmap;
DstWidth, DstHeight: Integer; Is32bit: Boolean): Boolean;
var
SrcWidth, SrcHeight, SrcLineSize, DstLineSize, PixelSize: Integer;
SrcBits, DstBits, TmpBits: Pointer;
PixelFormat: TPixelFormat;
Proc: TPutPixelProc;
begin
Result := False;
try
if (DstWidth <= 0) or (DstHeight <= 0) then Exit;
SrcWidth := SrcBitmap.Width;
SrcHeight := SrcBitmap.Height;
if (SrcWidth <= 0) or (SrcHeight <= 0) then Exit;
if Is32bit then begin
PixelFormat := pf32bit;
PixelSize := 4;
Proc := PutPixel32P;
end else begin
PixelFormat := pf24bit;
PixelSize := 3;
Proc := PutPixel24;
end;
//NOTE: Irreversible change of SrcBitmap pixel format
SrcBitmap.PixelFormat := PixelFormat;
SrcLineSize := WPARAM(SrcBitmap.ScanLine[0]) - WPARAM(SrcBitmap.ScanLine[1]);
if SrcLineSize >= 0 then
SrcBits := SrcBitmap.ScanLine[SrcHeight - 1]
else begin
SrcLineSize := -SrcLineSize;
SrcBits := SrcBitmap.ScanLine[0];
end;
DstBitmap.PixelFormat := PixelFormat;
DstBitmap.AlphaFormat := SrcBitmap.AlphaFormat;
DstBitmap.Width := DstWidth;
DstBitmap.Height := DstHeight;
DstLineSize := WPARAM(DstBitmap.ScanLine[0]) - WPARAM(DstBitmap.ScanLine[1]);
if DstLineSize >= 0 then
DstBits := DstBitmap.ScanLine[DstHeight - 1]
else begin
DstLineSize := -DstLineSize;
DstBits := DstBitmap.ScanLine[0];
end;
TmpBits := nil;
try
//Minimize temporary allocations by choosing right stretch order
if DstWidth * SrcHeight < DstHeight * SrcWidth then begin
GetMem(TmpBits, SrcHeight * DstLineSize);
//Stretch horizontally
ResampleBits(DstWidth, SrcWidth, SrcBits, TmpBits, PixelSize,
SrcHeight, SrcLineSize, DstLineSize, Proc);
//Stretch vertically
ResampleBits(DstHeight, SrcHeight, TmpBits, DstBits, DstLineSize,
DstWidth, PixelSize, PixelSize, Proc);
end else begin
GetMem(TmpBits, DstHeight * SrcLineSize);
//Stretch vertically
ResampleBits(DstHeight, SrcHeight, SrcBits, TmpBits, SrcLineSize,
SrcWidth, PixelSize, PixelSize, Proc);
//Stretch horizontally
ResampleBits(DstWidth, SrcWidth, TmpBits, DstBits, PixelSize,
DstHeight, SrcLineSize, DstLineSize, Proc);
end;
Result := True;
finally
FreeMem(TmpBits);
end;
except
end;
end;
end.