123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- 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.
|