211 lines
7.0 KiB
ObjectPascal
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. |