This repository has been archived on 2024-02-26. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
MyPresenter1.0/bgrabitmap/bgraresample.pas
2015-02-08 16:52:18 -08:00

1504 lines
49 KiB
ObjectPascal

unit BGRAResample;
{$mode objfpc}{$H+}
interface
{ This unit provides resampling functions, i.e. resizing of bitmaps with or
without interpolation filters.
SimpleStretch does a boxed resample with limited antialiasing.
FineResample uses floating point coordinates to get an antialiased resample.
It can use minimal interpolation (4 pixels when upsizing) for simple interpolation
filters (linear and cosine-like) or wide kernel resample for complex interpolation.
In this cas, it calls WideKernelResample.
WideKernelResample can be called by custom filter kernel, derived
from TWideKernelFilter. It is slower of course than simple interpolation. }
uses
Types, SysUtils, BGRABitmapTypes;
{------------------------------- Simple stretch ------------------------------------}
function SimpleStretch(bmp: TBGRACustomBitmap;
NewWidth, NewHeight: integer): TBGRACustomBitmap;
procedure StretchPutImage(bmp: TBGRACustomBitmap;
NewWidth, NewHeight: integer; dest: TBGRACustomBitmap; OffsetX,OffsetY: Integer; ADrawMode: TDrawMode; AOpacity: byte);
procedure DownSamplePutImage(source: TBGRACustomBitmap; factorX,factorY: integer; dest: TBGRACustomBitmap; OffsetX,OffsetY: Integer; ADrawMode: TDrawMode);
function DownSample(source: TBGRACustomBitmap; factorX,factorY: integer): TBGRACustomBitmap;
{---------------------------- Interpolation filters --------------------------------}
function FineInterpolation(t: single; ResampleFilter: TResampleFilter): single;
function FineInterpolation256(t256: integer; ResampleFilter: TResampleFilter): integer;
type
TWideKernelFilter = class
function Interpolation(t: single): single; virtual; abstract;
function ShouldCheckRange: boolean; virtual; abstract;
function KernelWidth: single; virtual; abstract;
end;
TMitchellKernel = class(TWideKernelFilter)
function Interpolation(t: single): single; override;
function ShouldCheckRange: boolean; override;
function KernelWidth: single; override;
end;
{ TSplineKernel }
TSplineKernel = class(TWideKernelFilter)
public
Coeff: single;
constructor Create;
constructor Create(ACoeff: single);
function Interpolation(t: single): single; override;
function ShouldCheckRange: boolean; override;
function KernelWidth: single; override;
end;
{ TCubicKernel }
TCubicKernel = class(TWideKernelFilter)
function pow3(x: single): single; inline;
function Interpolation(t: single): single; override;
function ShouldCheckRange: boolean; override;
function KernelWidth: single; override;
end;
{ TLanczosKernel }
TLanczosKernel = class(TWideKernelFilter)
private
FNumberOfLobes: integer;
FFactor: ValReal;
procedure SetNumberOfLobes(AValue: integer);
public
constructor Create(ANumberOfLobes: integer);
function Interpolation(t: single): single; override;
function ShouldCheckRange: boolean; override;
function KernelWidth: single; override;
property NumberOfLobes : integer read FNumberOfLobes write SetNumberOfLobes;
end;
function CreateInterpolator(style: TSplineStyle): TWideKernelFilter;
{-------------------------------- Fine resample ------------------------------------}
function FineResample(bmp: TBGRACustomBitmap;
NewWidth, NewHeight: integer; ResampleFilter: TResampleFilter): TBGRACustomBitmap;
function WideKernelResample(bmp: TBGRACustomBitmap;
NewWidth, NewHeight: integer; ResampleFilterSmaller, ResampleFilterLarger: TWideKernelFilter): TBGRACustomBitmap;
implementation
uses GraphType, Math, BGRABlend;
function SimpleStretch(bmp: TBGRACustomBitmap;
newWidth, newHeight: integer): TBGRACustomBitmap;
begin
if (NewWidth = bmp.Width) and (NewHeight = bmp.Height) then
begin
Result := bmp.Duplicate;
exit;
end;
Result := bmp.NewBitmap(NewWidth, NewHeight);
StretchPutImage(bmp, newWidth,newHeight, result, 0,0, dmSet, 255);
end;
procedure StretchPutImage(bmp: TBGRACustomBitmap; NewWidth, NewHeight: integer;
dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode; AOpacity: byte);
type
TTransitionState = (tsNone, tsPlain, tsLeft, tsMiddle, tsRight);
var
x_src,y_src, y_src2, prev_y_src, prev_y_src2: NativeInt;
inc_x_src, mod_x_src, acc_x_src, inc_y_src, mod_y_src, acc_y_src,
acc_x_src2, acc_y_src2: NativeInt;
x_dest, y_dest: NativeInt;
PDest, PSrc1, PSrc2: PBGRAPixel;
vertColors: packed array[1..2] of TBGRAPixel;
DeltaSrcX: NativeInt;
targetRect: TRect;
tempData: PBGRAPixel;
prevHorizTransition,horizTransition,prevVertTransition,vertTransition: TTransitionState;
horizSlightlyDifferent,vertSlightlyDifferent: boolean;
procedure LinearMix(PSrc: PBGRAPixel; DeltaSrc: integer; AccSrcQuarter: boolean;
PDest: PBGRAPixel; slightlyDifferent: boolean; var transition: TTransitionState);
var
asum: NativeInt;
a1,a2: NativeInt;
newTransition: TTransitionState;
begin
if DeltaSrc=0 then
begin
PDest^ := PSrc^;
transition:= tsPlain;
end
else
begin
if slightlyDifferent then
begin
if AccSrcQuarter then newTransition:= tsRight else
newTransition:= tsLeft;
end else
newTransition:= tsMiddle;
if (newTransition = tsMiddle) or ((newTransition = tsRight) and (transition = tsLeft)) or
((newTransition = tsLeft) and (transition = tsRight)) then
begin
transition:= tsMiddle;
asum := psrc^.alpha + (psrc+DeltaSrc)^.alpha;
if asum = 0 then
pdest^ := BGRAPixelTransparent
else if asum = 510 then
begin
pdest^.alpha := 255;
pdest^.red := (psrc^.red + (psrc+DeltaSrc)^.red + 1) shr 1;
pdest^.green := (psrc^.green + (psrc+DeltaSrc)^.green + 1) shr 1;
pdest^.blue := (psrc^.blue + (psrc+DeltaSrc)^.blue + 1) shr 1;
end else
begin
pdest^.alpha := asum shr 1;
a1 := psrc^.alpha;
a2 := (psrc+DeltaSrc)^.alpha;
pdest^.red := (psrc^.red*a1 + (psrc+DeltaSrc)^.red*a2 + (asum shr 1)) div asum;
pdest^.green := (psrc^.green*a1 + (psrc+DeltaSrc)^.green*a2 + (asum shr 1)) div asum;
pdest^.blue := (psrc^.blue*a1 + (psrc+DeltaSrc)^.blue*a2 + (asum shr 1)) div asum;
end;
end else
if newTransition = tsRight then
begin
transition := tsRight;
asum := psrc^.alpha + (psrc+DeltaSrc)^.alpha*3;
if asum = 0 then
pdest^ := BGRAPixelTransparent
else if asum = 1020 then
begin
pdest^.alpha := 255;
pdest^.red := (psrc^.red + (psrc+DeltaSrc)^.red*3 + 2) shr 2;
pdest^.green := (psrc^.green + (psrc+DeltaSrc)^.green*3 + 2) shr 2;
pdest^.blue := (psrc^.blue + (psrc+DeltaSrc)^.blue*3 + 2) shr 2;
end else
begin
pdest^.alpha := asum shr 2;
a1 := psrc^.alpha;
a2 := (psrc+DeltaSrc)^.alpha;
pdest^.red := (psrc^.red*a1 + (psrc+DeltaSrc)^.red*a2*3 + (asum shr 1)) div asum;
pdest^.green := (psrc^.green*a1 + (psrc+DeltaSrc)^.green*a2*3 + (asum shr 1)) div asum;
pdest^.blue := (psrc^.blue*a1 + (psrc+DeltaSrc)^.blue*a2*3 + (asum shr 1)) div asum;
end;
end else
begin
transition:= tsLeft;
asum := psrc^.alpha*3 + (psrc+DeltaSrc)^.alpha;
if asum = 0 then
pdest^ := BGRAPixelTransparent
else if asum = 1020 then
begin
pdest^.alpha := 255;
pdest^.red := (psrc^.red*3 + (psrc+DeltaSrc)^.red + 2) shr 2;
pdest^.green := (psrc^.green*3 + (psrc+DeltaSrc)^.green + 2) shr 2;
pdest^.blue := (psrc^.blue*3 + (psrc+DeltaSrc)^.blue + 2) shr 2;
end else
begin
pdest^.alpha := asum shr 2;
a1 := psrc^.alpha;
a2 := (psrc+DeltaSrc)^.alpha;
pdest^.red := (psrc^.red*a1*3 + (psrc+DeltaSrc)^.red*a2 + (asum shr 1)) div asum;
pdest^.green := (psrc^.green*a1*3 + (psrc+DeltaSrc)^.green*a2 + (asum shr 1)) div asum;
pdest^.blue := (psrc^.blue*a1*3 + (psrc+DeltaSrc)^.blue*a2 + (asum shr 1)) div asum;
end;
end;
end;
end;
begin
if (newWidth <= 0) or (newHeight <= 0) or (bmp.Width <= 0)
or (bmp.Height <= 0) then
exit;
targetRect := rect(0,0,NewWidth,NewHeight);
if OffsetX < dest.ClipRect.Left then targetRect.Left:= dest.ClipRect.Left-OffsetX;
if OffsetY < dest.ClipRect.Top then targetRect.Top:= dest.ClipRect.Top-OffsetY;
if OffsetX+NewWidth > dest.ClipRect.Right then targetRect.Right := dest.ClipRect.Right-OffsetX;
if OffsetY+NewHeight > dest.ClipRect.Bottom then targetRect.Bottom := dest.ClipRect.Bottom-OffsetY;
if (targetRect.Right <= targetRect.Left) or (targetRect.Bottom <= targetRect.Top) then exit;
bmp.LoadFromBitmapIfNeeded;
if (ADrawMode <> dmSet) or (AOpacity <> 255) then
getmem(tempData, (targetRect.Right-targetRect.Left)*sizeof(TBGRAPixel) )
else
tempData := nil;
inc_x_src := bmp.Width div newwidth;
mod_x_src := bmp.Width mod newwidth;
inc_y_src := bmp.Height div newheight;
mod_y_src := bmp.Height mod newheight;
prev_y_src := -1;
prev_y_src2 := -1;
acc_y_src := targetRect.Top*mod_y_src;
y_src := targetRect.Top*inc_y_src + (acc_y_src div NewHeight);
acc_y_src := acc_y_src mod NewHeight;
y_src := y_src+ (bmp.Height div 4) div newheight;
acc_y_src := acc_y_src+ (bmp.Height div 4) mod newheight;
y_src2 := y_src+ (bmp.Height div 2) div newheight;
acc_y_src2 := acc_y_src+ (bmp.Height div 2) mod newheight;
if acc_y_src2 > NewHeight then
begin
dec(acc_y_src2, NewHeight);
inc(y_src2);
end;
horizSlightlyDifferent := (NewWidth > bmp.Width*2 div 3) and (NewWidth < bmp.Width*4 div 3);
prevVertTransition:= tsNone;
vertSlightlyDifferent := (NewHeight > bmp.Height*2 div 3) and (NewHeight < bmp.Height*4 div 3);
for y_dest := targetRect.Top to targetRect.Bottom - 1 do
begin
if (y_src = prev_y_src) and (y_src2 = prev_y_src2) and not vertSlightlyDifferent then
begin
if tempData = nil then
move((dest.ScanLine[y_dest-1+OffsetY]+OffsetX+targetRect.Left)^,(dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left)^,(targetRect.Right-targetRect.Left)*sizeof(TBGRAPixel))
else
PutPixels(dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left,tempData,targetRect.right-targetRect.left,ADrawMode,AOpacity);
end else
begin
if tempData = nil then
PDest := dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left
else
PDest := tempData;
PSrc1 := bmp.Scanline[y_src];
acc_x_src := targetRect.Left*mod_x_src;
x_src := targetRect.Left*inc_x_src + (acc_x_src div NewWidth);
acc_x_src := acc_x_src mod NewWidth;
x_src := x_src+ (bmp.Width div 4) div NewWidth;
acc_x_src := acc_x_src+ (bmp.Width div 4) mod NewWidth;
DeltaSrcX := (bmp.Width div 2) div NewWidth;
acc_x_src2 := acc_x_src+ (bmp.Width div 2) mod NewWidth;
if acc_x_src2 > NewWidth then
begin
dec(acc_x_src2, NewWidth);
inc(DeltaSrcX);
end;
inc(Psrc1, x_src);
prevHorizTransition := tsNone;
if y_src2=y_src then
begin
horizTransition:= prevHorizTransition;
for x_dest := targetRect.left to targetRect.right - 1 do
begin
LinearMix(psrc1, DeltaSrcX, acc_x_src2 >= NewWidth shr 2, PDest, horizSlightlyDifferent, horizTransition);
Inc(PSrc1, inc_x_src);
Inc(acc_x_src, mod_x_src);
if acc_x_src >= newWidth then
begin
Dec(acc_x_src, newWidth);
Inc(PSrc1);
dec(DeltaSrcX);
end;
Inc(acc_x_src2, mod_x_src);
if acc_x_src2 >= newWidth then
begin
Dec(acc_x_src2, newWidth);
Inc(DeltaSrcX);
end;
inc(PDest);
end;
prevVertTransition:= tsPlain;
end else
begin
PSrc2 := bmp.Scanline[y_src2]+x_src;
for x_dest := targetRect.left to targetRect.right - 1 do
begin
horizTransition:= prevHorizTransition;
LinearMix(psrc1, DeltaSrcX, acc_x_src2 >= NewWidth shr 2, @vertColors[1], horizSlightlyDifferent, horizTransition);
horizTransition:= prevHorizTransition;
LinearMix(psrc2, DeltaSrcX, acc_x_src2 >= NewWidth shr 2, @vertColors[2], horizSlightlyDifferent, horizTransition);
prevHorizTransition:= horizTransition;
vertTransition:= prevVertTransition;
LinearMix(@vertColors[1],1,acc_y_src2 >= NewHeight shr 2,PDest,vertSlightlyDifferent,vertTransition);
Inc(PSrc1, inc_x_src);
Inc(PSrc2, inc_x_src);
Inc(acc_x_src, mod_x_src);
if acc_x_src >= newWidth then
begin
Dec(acc_x_src, newWidth);
Inc(PSrc1);
Inc(PSrc2);
dec(DeltaSrcX);
end;
Inc(acc_x_src2, mod_x_src);
if acc_x_src2 >= newWidth then
begin
Dec(acc_x_src2, newWidth);
Inc(DeltaSrcX);
end;
inc(PDest);
end;
prevVertTransition:= vertTransition;
end;
if tempData <> nil then
PutPixels(dest.ScanLine[y_dest+OffsetY]+OffsetX+targetRect.Left,tempData,targetRect.right-targetRect.left,ADrawMode,AOpacity);
end;
prev_y_src := y_src;
prev_y_src2 := y_src2;
Inc(y_src, inc_y_src);
Inc(acc_y_src, mod_y_src);
if acc_y_src >= newheight then
begin
Dec(acc_y_src, newheight);
Inc(y_src);
end;
Inc(y_src2, inc_y_src);
Inc(acc_y_src2, mod_y_src);
if acc_y_src2 >= newheight then
begin
Dec(acc_y_src2, newheight);
Inc(y_src2);
end;
end;
dest.InvalidateBitmap;
if Assigned(tempData) then FreeMem(tempData);
end;
procedure DownSamplePutImage2(source: TBGRACustomBitmap;
dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode);
const factorX = 2; factorY = 2; nbi= factorX*factorY;
var xb,yb,ys: NativeInt;
pdest: PBGRAPixel;
psrc1,psrc2: PBGRAPixel;
asum,maxsum: NativeUInt;
newWidth,newHeight: NativeInt;
r,g,b: NativeUInt;
begin
if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
raise exception.Create('Source size must be a multiple of factorX and factorY');
newWidth := source.Width div factorX;
newHeight := source.Height div factorY;
ys := 0;
maxsum := 255*NativeInt(factorX)*NativeInt(factorY);
for yb := 0 to newHeight-1 do
begin
pdest := dest.ScanLine[yb+OffsetY]+OffsetX;
psrc1 := source.Scanline[ys]; inc(ys);
psrc2 := source.Scanline[ys]; inc(ys);
for xb := newWidth-1 downto 0 do
begin
asum := 0;
asum := psrc1^.alpha + psrc2^.alpha + (psrc1+1)^.alpha + (psrc2+1)^.alpha;
if asum = maxsum then
begin
pdest^.alpha := 255;
r := psrc1^.red + psrc2^.red + (psrc1+1)^.red + (psrc2+1)^.red;
g := psrc1^.green + psrc2^.green + (psrc1+1)^.green + (psrc2+1)^.green;
b := psrc1^.blue + psrc2^.blue + (psrc1+1)^.blue + (psrc2+1)^.blue;
inc(psrc1,factorX); inc(psrc2,factorX);
pdest^.red := (r + (nbi shr 1)) shr 2;
pdest^.green := (g + (nbi shr 1)) shr 2;
pdest^.blue := (b + (nbi shr 1)) shr 2;
end else
if ADrawMode <> dmSetExceptTransparent then
begin
if asum = 0 then
begin
if ADrawMode = dmSet then
pdest^ := BGRAPixelTransparent;
inc(psrc1,factorX); inc(psrc2,factorX);
end
else
begin
r := psrc1^.red*psrc1^.alpha + psrc2^.red*psrc2^.alpha + (psrc1+1)^.red*(psrc1+1)^.alpha + (psrc2+1)^.red*(psrc2+1)^.alpha;
g := psrc1^.green*psrc1^.alpha + psrc2^.green*psrc2^.alpha + (psrc1+1)^.green*(psrc1+1)^.alpha + (psrc2+1)^.green*(psrc2+1)^.alpha;
b := psrc1^.blue*psrc1^.alpha + psrc2^.blue*psrc2^.alpha + (psrc1+1)^.blue*(psrc1+1)^.alpha + (psrc2+1)^.blue*(psrc2+1)^.alpha;
inc(psrc1,factorX); inc(psrc2,factorX);
if ADrawMode = dmSet then
begin
pdest^.alpha := (asum + (nbi shr 1)) shr 2;
pdest^.red := (r + (asum shr 1)) div asum;
pdest^.green := (g + (asum shr 1)) div asum;
pdest^.blue := (b + (asum shr 1)) div asum;
end
else
begin
if ADrawMode = dmDrawWithTransparency then
DrawPixelInlineWithAlphaCheck(pdest,BGRA((r + (asum shr 1)) div asum,
(g + (asum shr 1)) div asum,
(b + (asum shr 1)) div asum,
(asum + (nbi shr 1)) shr 2)) else
if ADrawMode = dmFastBlend then
FastBlendPixelInline(pdest,BGRA((r + (asum shr 1)) div asum,
(g + (asum shr 1)) div asum,
(b + (asum shr 1)) div asum,
(asum + (nbi shr 1)) shr 2));
end;
end;
end;
inc(pdest);
end;
end;
end;
procedure DownSamplePutImage3(source: TBGRACustomBitmap;
dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode);
const factorX = 3; factorY = 3; nbi= factorX*factorY;
var xb,yb,ys: NativeInt;
pdest: PBGRAPixel;
psrc1,psrc2,psrc3: PBGRAPixel;
asum,maxsum: NativeUInt;
newWidth,newHeight: NativeInt;
r,g,b: NativeUInt;
begin
if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
raise exception.Create('Source size must be a multiple of factorX and factorY');
newWidth := source.Width div factorX;
newHeight := source.Height div factorY;
ys := 0;
maxsum := 255*NativeInt(factorX)*NativeInt(factorY);
for yb := 0 to newHeight-1 do
begin
pdest := dest.ScanLine[yb+OffsetY]+OffsetX;
psrc1 := source.Scanline[ys]; inc(ys);
psrc2 := source.Scanline[ys]; inc(ys);
psrc3 := source.Scanline[ys]; inc(ys);
for xb := newWidth-1 downto 0 do
begin
asum := 0;
asum := psrc1^.alpha + psrc2^.alpha + psrc3^.alpha
+ (psrc1+1)^.alpha + (psrc2+1)^.alpha + (psrc3+1)^.alpha
+ (psrc1+2)^.alpha + (psrc2+2)^.alpha + (psrc3+2)^.alpha;
if asum = maxsum then
begin
pdest^.alpha := 255;
r := psrc1^.red + psrc2^.red + psrc3^.red
+ (psrc1+1)^.red + (psrc2+1)^.red + (psrc3+1)^.red
+ (psrc1+2)^.red + (psrc2+2)^.red + (psrc3+2)^.red;
g := psrc1^.green + psrc2^.green + psrc3^.green
+ (psrc1+1)^.green + (psrc2+1)^.green + (psrc3+1)^.green
+ (psrc1+2)^.green + (psrc2+2)^.green + (psrc3+2)^.green;
b := psrc1^.blue + psrc2^.blue + psrc3^.blue
+ (psrc1+1)^.blue + (psrc2+1)^.blue + (psrc3+1)^.blue
+ (psrc1+2)^.blue + (psrc2+2)^.blue + (psrc3+2)^.blue;
inc(psrc1,factorX); inc(psrc2,factorX); inc(psrc3,factorX);
pdest^.red := (r + (nbi shr 1)) div 9;
pdest^.green := (g + (nbi shr 1)) div 9;
pdest^.blue := (b + (nbi shr 1)) div 9;
end else
if ADrawMode <> dmSetExceptTransparent then
begin
if asum = 0 then
begin
if ADrawMode = dmSet then
pdest^ := BGRAPixelTransparent;
inc(psrc1,factorX); inc(psrc2,factorX); inc(psrc3,factorX);
end
else
begin
r := psrc1^.red*psrc1^.alpha + psrc2^.red*psrc2^.alpha + psrc3^.red*psrc3^.alpha
+ (psrc1+1)^.red*(psrc1+1)^.alpha + (psrc2+1)^.red*(psrc2+1)^.alpha + (psrc3+1)^.red*(psrc3+1)^.alpha
+ (psrc1+2)^.red*(psrc1+2)^.alpha + (psrc2+2)^.red*(psrc2+2)^.alpha + (psrc3+2)^.red*(psrc3+2)^.alpha;
g := psrc1^.green*psrc1^.alpha + psrc2^.green*psrc2^.alpha + psrc3^.green*psrc3^.alpha
+ (psrc1+1)^.green*(psrc1+1)^.alpha + (psrc2+1)^.green*(psrc2+1)^.alpha + (psrc3+1)^.green*(psrc3+1)^.alpha
+ (psrc1+2)^.green*(psrc1+2)^.alpha + (psrc2+2)^.green*(psrc2+2)^.alpha + (psrc3+2)^.green*(psrc3+2)^.alpha;
b := psrc1^.blue*psrc1^.alpha + psrc2^.blue*psrc2^.alpha + psrc3^.blue*psrc3^.alpha
+ (psrc1+1)^.blue*(psrc1+1)^.alpha + (psrc2+1)^.blue*(psrc2+1)^.alpha + (psrc3+1)^.blue*(psrc3+1)^.alpha
+ (psrc1+2)^.blue*(psrc1+2)^.alpha + (psrc2+2)^.blue*(psrc2+2)^.alpha + (psrc3+2)^.blue*(psrc3+2)^.alpha;
inc(psrc1,factorX); inc(psrc2,factorX); inc(psrc3,factorX);
if ADrawMode = dmSet then
begin
pdest^.alpha := (asum + (nbi shr 1)) div 9;
pdest^.red := (r + (asum shr 1)) div asum;
pdest^.green := (g + (asum shr 1)) div asum;
pdest^.blue := (b + (asum shr 1)) div asum;
end
else
begin
if ADrawMode = dmDrawWithTransparency then
DrawPixelInlineWithAlphaCheck(pdest,BGRA((r + (asum shr 1)) div asum,
(g + (asum shr 1)) div asum,
(b + (asum shr 1)) div asum,
(asum + (nbi shr 1)) div 9)) else
if ADrawMode = dmFastBlend then
FastBlendPixelInline(pdest,BGRA((r + (asum shr 1)) div asum,
(g + (asum shr 1)) div asum,
(b + (asum shr 1)) div asum,
(asum + (nbi shr 1)) div 9));
end;
end;
end;
inc(pdest);
end;
end;
end;
procedure DownSamplePutImage(source: TBGRACustomBitmap; factorX, factorY: integer;
dest: TBGRACustomBitmap; OffsetX, OffsetY: Integer; ADrawMode: TDrawMode);
var xb,yb,ys,iy,ix: NativeInt;
pdest,psrci: PBGRAPixel;
psrc: array of PBGRAPixel;
asum,maxsum: NativeUInt;
newWidth,newHeight: NativeInt;
r,g,b,nbi: NativeUInt;
begin
if ADrawMode = dmXor then raise exception.Create('dmXor drawmode not supported');
if (factorX = 2) and (factorY = 2) then
begin
DownSamplePutImage2(source,dest,OffsetX,OffsetY,ADrawMode);
exit;
end;
if (factorX = 3) and (factorY = 3) then
begin
DownSamplePutImage3(source,dest,OffsetX,OffsetY,ADrawMode);
exit;
end;
if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
raise exception.Create('Source size must be a multiple of factorX and factorY');
newWidth := source.Width div factorX;
newHeight := source.Height div factorY;
ys := 0;
maxsum := 255*NativeInt(factorX)*NativeInt(factorY);
nbi := factorX*factorY;
setlength(psrc, factorY);
for yb := 0 to newHeight-1 do
begin
pdest := dest.ScanLine[yb+OffsetY]+OffsetX;
for iy := factorY-1 downto 0 do
begin
psrc[iy] := source.Scanline[ys];
inc(ys);
end;
for xb := newWidth-1 downto 0 do
begin
asum := 0;
for iy := factorY-1 downto 0 do
begin
psrci := psrc[iy];
for ix := factorX-1 downto 0 do
asum += (psrci+ix)^.alpha;
end;
if asum = maxsum then
begin
pdest^.alpha := 255;
r := 0;
g := 0;
b := 0;
for iy := factorY-1 downto 0 do
for ix := factorX-1 downto 0 do
begin
with psrc[iy]^ do
begin
r += red;
g += green;
b += blue;
end;
inc(psrc[iy]);
end;
pdest^.red := (r + (nbi shr 1)) div nbi;
pdest^.green := (g + (nbi shr 1)) div nbi;
pdest^.blue := (b + (nbi shr 1)) div nbi;
end else
if ADrawMode <> dmSetExceptTransparent then
begin
if asum = 0 then
begin
if ADrawMode = dmSet then
pdest^ := BGRAPixelTransparent;
for iy := factorY-1 downto 0 do
inc(psrc[iy],factorX);
end
else
begin
r := 0;
g := 0;
b := 0;
for iy := factorY-1 downto 0 do
for ix := factorX-1 downto 0 do
begin
with psrc[iy]^ do
begin
r += red*alpha;
g += green*alpha;
b += blue*alpha;
end;
inc(psrc[iy]);
end;
if ADrawMode = dmSet then
begin
pdest^.alpha := (asum + (nbi shr 1)) div nbi;
pdest^.red := (r + (asum shr 1)) div asum;
pdest^.green := (g + (asum shr 1)) div asum;
pdest^.blue := (b + (asum shr 1)) div asum;
end
else
begin
if ADrawMode = dmDrawWithTransparency then
DrawPixelInlineWithAlphaCheck(pdest,BGRA((r + (asum shr 1)) div asum,
(g + (asum shr 1)) div asum,
(b + (asum shr 1)) div asum,
(asum + (nbi shr 1)) div nbi)) else
if ADrawMode = dmFastBlend then
FastBlendPixelInline(pdest,BGRA((r + (asum shr 1)) div asum,
(g + (asum shr 1)) div asum,
(b + (asum shr 1)) div asum,
(asum + (nbi shr 1)) div nbi));
end;
end;
end;
inc(pdest);
end;
end;
end;
function DownSample(source: TBGRACustomBitmap; factorX, factorY: integer): TBGRACustomBitmap;
begin
if (source.Width mod factorX <> 0) or (source.Height mod factorY <> 0) then
raise exception.Create('Source size must be a multiple of factorX and factorY');
result := source.NewBitmap(source.Width div factorX, source.Height div factorY);
DownSamplePutImage(source,factorX,factorY,result,0,0,dmSet);
end;
{---------------------------- Interpolation filters ----------------------------------------}
function FineInterpolation(t: single; ResampleFilter: TResampleFilter): single;
begin
if ResampleFilter <= rfLinear then
begin
if ResampleFilter = rfBox then
begin
result := round(t);
end else
result := t;
end else
begin
if t <= 0.5 then
result := t*t*2 else
result := 1-(1-t)*(1-t)*2;
if ResampleFilter <> rfCosine then result := (result+t)*0.5;
end;
end;
function FineInterpolation256(t256: integer; ResampleFilter: TResampleFilter): integer;
begin
if ResampleFilter <= rfLinear then
begin
if ResampleFilter = rfBox then
begin
if t256 < 128 then
result := 0
else
result := 256;
end
else
result := t256;
end else
begin
if t256 <= 128 then
result := (t256*t256) shr 7 else
result := 256 - (((256-t256)*(256-t256)) shr 7);
if ResampleFilter <> rfCosine then result := (result+t256) shr 1;
end;
end;
{ TCubicKernel }
function TCubicKernel.pow3(x: single): single;
begin
if x <= 0.0 then
result:=0.0
else
result:=x * x * x;
end;
function TCubicKernel.Interpolation(t: single): single;
const globalfactor = 1/6;
begin
if t > 2 then
result := 0
else
result:= globalfactor *
(pow3(t + 2 ) - 4 * pow3(t + 1 ) + 6 * pow3(t ) - 4 * pow3(t - 1 ) );
end;
function TCubicKernel.ShouldCheckRange: boolean;
begin
Result:= false;
end;
function TCubicKernel.KernelWidth: single;
begin
Result:= 2;
end;
{ TMitchellKernel }
function TMitchellKernel.Interpolation(t: single): single;
var
tt, ttt: single;
const OneEighteenth = 1 / 18;
begin
t := Abs(t);
tt := Sqr(t);
ttt := tt * t;
if t < 1 then Result := (21 * ttt - 36 * tt + 16 ) * OneEighteenth
else if t < 2 then Result := (- 7 * ttt + 36 * tt - 60 * t + 32) * OneEighteenth
else Result := 0;
end;
function TMitchellKernel.ShouldCheckRange: Boolean;
begin
Result := True;
end;
function TMitchellKernel.KernelWidth: single;
begin
Result := 2;
end;
{ TSplineKernel }
constructor TSplineKernel.Create;
begin
coeff := 0.5;
end;
constructor TSplineKernel.Create(ACoeff: single);
begin
Coeff := ACoeff;
end;
function TSplineKernel.Interpolation(t: single): single;
var
tt, ttt: single;
begin
t := Abs(t);
tt := Sqr(t);
ttt := tt * t;
if t < 1 then
Result := (2 - Coeff) * ttt - (3 - Coeff) * tt + 1
else if t < 2 then
Result := -Coeff * (ttt - 5 * tt + 8 * t - 4)
else
Result := 0;
end;
function TSplineKernel.ShouldCheckRange: Boolean;
begin
Result := True;
end;
function TSplineKernel.KernelWidth: single;
begin
Result := 2;
end;
{ TLanczosKernel }
{ by stab }
procedure TLanczosKernel.SetNumberOfLobes(AValue: integer);
begin
if AValue < 1 then AValue := 1;
if FNumberOfLobes=AValue then Exit;
FNumberOfLobes:=AValue;
if AValue = 1 then FFactor := 1.5 else FFactor := AValue;
end;
constructor TLanczosKernel.Create(ANumberOfLobes: integer);
begin
NumberOfLobes:= ANumberOfLobes;
end;
function TLanczosKernel.Interpolation(t: single): single;
var Pi_t: ValReal;
begin
if t = 0 then
Result := 1
else if t < FNumberOfLobes then
begin
Pi_t := pi * t;
Result := FFactor * sin(Pi_t) * sin(Pi_t / FNumberOfLobes) /
(Pi_t * Pi_t)
end
else
Result := 0;
end;
function TLanczosKernel.ShouldCheckRange: boolean;
begin
Result := True;
end;
function TLanczosKernel.KernelWidth: single;
begin
Result := FNumberOfLobes;
end;
{--------------------------------------------- Fine resample ------------------------------------------------}
function FineResampleLarger(bmp: TBGRACustomBitmap;
newWidth, newHeight: integer; ResampleFilter: TResampleFilter): TBGRACustomBitmap;
type
TInterpolationEntry = record
isrc1,isrc2,factCorr: integer;
end;
var
yb, xb: integer;
pdest,psrc1,psrc2: PBGRAPixel;
xsrc, ysrc, xfactor, yfactor: double;
xTab,yTab: array of TInterpolationEntry;
xInfo,yInfo: TInterpolationEntry;
cUpLeft, cUpRight, cLowLeft, cLowRight: TBGRAPixel;
factHoriz, factVert: single;
fUpLeft, fUpRight, fLowLeft, fLowRight: integer;
faUpLeft, faUpRight, faLowLeft, faLowRight: integer;
rSum, gSum, bSum, aSum: integer;
temp: TBGRACustomBitmap;
begin
if (newWidth < bmp.Width) or (newHeight < bmp.Height) then
raise ERangeError.Create('FineResampleLarger: New dimensions must be greater or equal ('+IntToStr(bmp.Width)+'x'+IntToStr(bmp.Height)+'->'+IntToStr(newWidth)+'x'+IntToStr(newHeight)+')');
if (newWidth = 0) or (newHeight = 0) then
begin
Result := bmp.NewBitmap(NewWidth, NewHeight);
exit;
end;
bmp.LoadFromBitmapIfNeeded;
if (bmp.Width = 1) and (bmp.Height = 1) then
begin
Result := bmp.NewBitmap(NewWidth, NewHeight);
Result.Fill(bmp.GetPixel(0, 0));
exit;
end
else
if bmp.Width = 1 then
begin
temp := bmp.NewBitmap(2, bmp.Height);
temp.PutImage(0, 0, bmp, dmSet);
temp.PutImage(1, 0, bmp, dmSet);
Result := FineResampleLarger(temp, 2, newHeight, ResampleFilter);
temp.Free;
temp := Result;
Result := SimpleStretch(temp, newWidth,temp.Height);
temp.Free;
exit;
end
else
if bmp.Height = 1 then
begin
temp := bmp.NewBitmap(bmp.Width, 2);
temp.PutImage(0, 0, bmp, dmSet);
temp.PutImage(0, 1, bmp, dmSet);
Result := FineResampleLarger(temp, newWidth, 2, ResampleFilter);
temp.Free;
temp := Result;
Result := SimpleStretch(temp, temp.Width,newHeight);
temp.Free;
exit;
end;
Result := bmp.NewBitmap(NewWidth, NewHeight);
yfactor := (bmp.Height - 1) / (newHeight - 1);
xfactor := (bmp.Width - 1) / (newWidth - 1);
setlength(yTab, newHeight);
for yb := 0 to newHeight - 1 do
begin
ysrc := yb * yfactor;
factVert := frac(ysrc);
yTab[yb].isrc1 := floor(ysrc);
yTab[yb].isrc2 := min(bmp.Height-1, ceil(ysrc));
yTab[yb].factCorr := round(FineInterpolation(factVert,ResampleFilter)*256);
end;
setlength(xTab, newWidth);
for xb := 0 to newWidth - 1 do
begin
xsrc := xb * xfactor;
factHoriz := frac(xsrc);
xTab[xb].isrc1 := floor(xsrc);
xTab[xb].isrc2 := min(bmp.Width-1,ceil(xsrc));
xTab[xb].factCorr := round(FineInterpolation(factHoriz,ResampleFilter)*256);
end;
for yb := 0 to newHeight - 1 do
begin
pdest := Result.Scanline[yb];
yInfo := yTab[yb];
psrc1 := bmp.scanline[yInfo.isrc1];
psrc2 := bmp.scanline[yInfo.isrc2];
for xb := 0 to newWidth - 1 do
begin
xInfo := xTab[xb];
cUpLeft := (psrc1 + xInfo.isrc1)^;
cUpRight := (psrc1 + xInfo.isrc2)^;
cLowLeft := (psrc2 + xInfo.isrc1)^;
cLowRight := (psrc2 + xInfo.isrc2)^;
fLowRight := (xInfo.factCorr * yInfo.factCorr + 128) shr 8;
fLowLeft := yInfo.factCorr - fLowRight;
fUpRight := xInfo.factCorr - fLowRight;
fUpLeft := (256 - xInfo.factCorr) - fLowLeft;
faUpLeft := fUpLeft * cUpLeft.alpha;
faUpRight := fUpRight * cUpRight.alpha;
faLowLeft := fLowLeft * cLowLeft.alpha;
faLowRight := fLowRight * cLowRight.alpha;
rSum := cUpLeft.red * faUpLeft + cUpRight.red * faUpRight +
cLowLeft.red * faLowLeft + cLowRight.red * faLowRight;
gSum := cUpLeft.green * faUpLeft + cUpRight.green * faUpRight +
cLowLeft.green * faLowLeft + cLowRight.green * faLowRight;
bSum := cUpLeft.blue * faUpLeft + cUpRight.blue * faUpRight +
cLowLeft.blue * faLowLeft + cLowRight.blue * faLowRight;
aSum := cUpLeft.alpha * fUpLeft + cUpRight.alpha * fUpRight +
cLowLeft.alpha * fLowLeft + cLowRight.alpha * fLowRight;
if aSum = 0 then
pdest^ := BGRAPixelTransparent
else
pdest^ := BGRA((rSum + aSum shr 1) div aSum, (gSum + aSum shr 1) div aSum,
(bSum + aSum shr 1) div aSum, (aSum + 128) shr 8);
Inc(pdest);
end;
end;
end;
function FineResampleSmaller(bmp: TBGRACustomBitmap;
newWidth, newHeight: integer): TBGRACustomBitmap;
var
yb, xb, yb2, xb2: integer;
pdest, psrc: PBGRAPixel;
lineDelta, delta: integer;
xsrc1, ysrc1, xsrc2, ysrc2, xfactor, yfactor: double;
ixsrc1, ixsrc2, iysrc1, iysrc2, ixsrc1p1, ixsrc2m1, iysrc1p1, iysrc2m1: integer;
cBorder, cFull, cUpLeft, cUpRight, cLowLeft, cLowRight: TBGRAPixel;
factHoriz1, factHoriz2, factVert1, factVert2, Sum, fUpLeft, fUpRight,
fLowLeft, fLowRight, faUpLeft, faUpRight, faLowLeft, faLowRight: single;
rSum, gSum, bSum, aSum: double;
begin
if (newWidth > bmp.Width) or (newHeight > bmp.Height) then
raise ERangeError.Create('FineResampleSmaller: New dimensions must be smaller or equal ('+IntToStr(bmp.Width)+'x'+IntToStr(bmp.Height)+'->'+IntToStr(newWidth)+'x'+IntToStr(newHeight)+')');
Result := bmp.NewBitmap(NewWidth, NewHeight);
if (newWidth = 0) or (newHeight = 0) or (bmp.Width = 0) or (bmp.Height = 0) then
exit;
bmp.LoadFromBitmapIfNeeded;
if bmp.lineOrder = riloTopToBottom then
lineDelta := bmp.Width
else
lineDelta := -bmp.Width;
yfactor := bmp.Height / newHeight;
xfactor := bmp.Width / newWidth;
for yb := 0 to newHeight - 1 do
begin
pdest := Result.Scanline[yb];
ysrc1 := yb * yfactor;
ysrc2 := (yb + 1) * yfactor;
iysrc1 := trunc(ysrc1);
if (int(ysrc2) = int(ysrc1)) or (ysrc2 = iysrc1 + 1) then
begin
iysrc2 := iysrc1;
factVert1 := 1;
factVert2 := 0;
end
else
begin
iysrc2 := trunc(ysrc2);
factVert1 := 1 - frac(ysrc1);
factVert2 := frac(ysrc2);
end;
for xb := 0 to newWidth - 1 do
begin
xsrc1 := xb * xfactor;
xsrc2 := (xb + 1) * xfactor;
ixsrc1 := trunc(xsrc1);
if (int(xsrc2) = int(xsrc1)) or (xsrc2 = ixsrc1 + 1) then
begin
ixsrc2 := ixsrc1;
factHoriz1 := 1;
factHoriz2 := 0;
end
else
begin
ixsrc2 := trunc(xsrc2);
factHoriz1 := 1 - frac(xsrc1);
factHoriz2 := frac(xsrc2);
end;
cUpLeft := bmp.GetPixel(ixsrc1, iysrc1);
cUpRight := bmp.GetPixel(ixsrc2, iysrc1);
cLowLeft := bmp.GetPixel(ixsrc1, iysrc2);
cLowRight := bmp.GetPixel(ixsrc2, iysrc2);
fUpLeft := factHoriz1 * factVert1;
fUpRight := factHoriz2 * factVert1;
fLowLeft := factHoriz1 * factVert2;
fLowRight := factHoriz2 * factVert2;
faUpLeft := fUpLeft * cUpLeft.alpha;
faUpRight := fUpRight * cUpRight.alpha;
faLowLeft := fLowLeft * cLowLeft.alpha;
faLowRight := fLowRight * cLowRight.alpha;
Sum := fUpLeft + fUpRight + fLowLeft + fLowRight;
rSum := cUpLeft.red * faUpLeft + cUpRight.red * faUpRight +
cLowLeft.red * faLowLeft + cLowRight.red * faLowRight;
gSum := cUpLeft.green * faUpLeft + cUpRight.green * faUpRight +
cLowLeft.green * faLowLeft + cLowRight.green * faLowRight;
bSum := cUpLeft.blue * faUpLeft + cUpRight.blue * faUpRight +
cLowLeft.blue * faLowLeft + cLowRight.blue * faLowRight;
aSum := cUpLeft.alpha * fUpLeft + cUpRight.alpha * fUpRight +
cLowLeft.alpha * fLowLeft + cLowRight.alpha * fLowRight;
ixsrc1p1 := ixsrc1 + 1;
ixsrc2m1 := ixsrc2 - 1;
iysrc1p1 := iysrc1 + 1;
iysrc2m1 := iysrc2 - 1;
if ixsrc2m1 >= ixsrc1p1 then
begin
psrc := bmp.scanline[iysrc1] + ixsrc1p1;
for xb2 := ixsrc1p1 to ixsrc2m1 do
begin
cBorder := psrc^;
Inc(psrc);
rSum += cBorder.red * cBorder.alpha * factVert1;
gSum += cBorder.green * cBorder.alpha * factVert1;
bSum += cBorder.blue * cBorder.alpha * factVert1;
aSum += cBorder.alpha * factVert1;
Sum += factVert1;
end;
if (factVert2 <> 0) and (iysrc2 < bmp.Height) then
begin
psrc := bmp.scanline[iysrc2] + ixsrc1p1;
for xb2 := ixsrc1p1 to ixsrc2m1 do
begin
cBorder := psrc^;
Inc(psrc);
rSum += cBorder.red * cBorder.alpha * factVert2;
gSum += cBorder.green * cBorder.alpha * factVert2;
bSum += cBorder.blue * cBorder.alpha * factVert2;
aSum += cBorder.alpha * factVert2;
Sum += factVert2;
end;
end;
end;
if iysrc2m1 >= iysrc1p1 then
begin
psrc := bmp.scanline[iysrc1p1] + ixsrc1;
for yb2 := iysrc1p1 to iysrc2m1 do
begin
cBorder := psrc^;
Inc(psrc, lineDelta);
rSum += cBorder.red * cBorder.alpha * factHoriz1;
gSum += cBorder.green * cBorder.alpha * factHoriz1;
bSum += cBorder.blue * cBorder.alpha * factHoriz1;
aSum += cBorder.alpha * factHoriz1;
Sum += factHoriz1;
end;
if (factHoriz2 <> 0) and (ixsrc2 < bmp.Width) then
begin
psrc := bmp.scanline[iysrc1p1] + ixsrc2;
for yb2 := iysrc1p1 to iysrc2m1 do
begin
cBorder := psrc^;
Inc(psrc, lineDelta);
rSum += cBorder.red * cBorder.alpha * factHoriz2;
gSum += cBorder.green * cBorder.alpha * factHoriz2;
bSum += cBorder.blue * cBorder.alpha * factHoriz2;
aSum += cBorder.alpha * factHoriz2;
Sum += factHoriz2;
end;
end;
end;
if (ixsrc2m1 >= ixsrc1p1) and (iysrc2m1 >= iysrc1p1) then
begin
delta := lineDelta - (ixsrc2m1 - ixsrc1p1 + 1);
psrc := bmp.scanline[iysrc1p1] + ixsrc1p1;
for yb2 := iysrc1p1 to iysrc2m1 do
begin
for xb2 := ixsrc1p1 to ixsrc2m1 do
begin
cFull := psrc^;
rSum += cFull.red * cFull.alpha;
gSum += cFull.green * cFull.alpha;
bSum += cFull.blue * cFull.alpha;
aSum += cFull.alpha;
Sum += 1;
Inc(psrc);
end;
Inc(psrc, delta);
end;
end;
if aSum = 0 then
pdest^ := BGRAPixelTransparent
else
pdest^ := BGRA(round(rSum / aSum), round(gSum / aSum),
round(bSum / aSum), round(aSum / Sum));
Inc(pdest);
end;
end;
end;
function CreateInterpolator(style: TSplineStyle): TWideKernelFilter;
begin
case Style of
ssInside, ssInsideWithEnds: result := TCubicKernel.Create;
ssCrossing, ssCrossingWithEnds: result := TMitchellKernel.Create;
ssOutside: result := TSplineKernel.Create(0.5);
ssRoundOutside: result := TSplineKernel.Create(0.75);
ssVertexToSide: result := TSplineKernel.Create(1);
else
raise Exception.Create('Unknown spline style');
end;
end;
function FineResample(bmp: TBGRACustomBitmap;
NewWidth, NewHeight: integer; ResampleFilter: TResampleFilter): TBGRACustomBitmap;
var
temp, newtemp: TBGRACustomBitmap;
tempFilter1,tempFilter2: TWideKernelFilter;
begin
if (NewWidth = bmp.Width) and (NewHeight = bmp.Height) then
begin
Result := bmp.Duplicate;
exit;
end;
case ResampleFilter of
rfBicubic: //blur
begin
tempFilter1 := TCubicKernel.Create;
result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter1,tempFilter1);
tempFilter1.Free;
exit;
end;
rfMitchell:
begin
tempFilter1 := TMitchellKernel.Create;
result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter1,tempFilter1);
tempFilter1.Free;
exit;
end;
rfSpline:
begin
tempFilter1 := TSplineKernel.Create;
result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter1,tempFilter1);
tempFilter1.Free;
exit;
end;
rfLanczos2,rfLanczos3,rfLanczos4:
begin
tempFilter1 := TLanczosKernel.Create(ord(ResampleFilter)-ord(rfLanczos2)+2);
result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter1,tempFilter1);
tempFilter1.Free;
exit;
end;
rfBestQuality:
begin
tempFilter1 := TSplineKernel.Create;
tempFilter2 := TMitchellKernel.Create;
result := WideKernelResample(bmp,NewWidth,NewHeight,tempFilter2,tempFilter1);
tempFilter1.Free;
tempFilter2.Free;
exit;
end;
end;
if (NewWidth >= bmp.Width) and (NewHeight >= bmp.Height) then
Result := FineResampleLarger(bmp, NewWidth, NewHeight, ResampleFilter)
else
if (NewWidth <= bmp.Width) and (NewHeight <= bmp.Height) then
Result := FineResampleSmaller(bmp, NewWidth, NewHeight)
else
begin
temp := bmp;
if NewWidth < bmp.Width then
begin
newtemp := FineResampleSmaller(temp, NewWidth, temp.Height);
if (temp <> bmp) then
temp.Free;
temp := newtemp;
end;
if NewHeight < bmp.Height then
begin
newtemp := FineResampleSmaller(temp, temp.Width, NewHeight);
if (temp <> bmp) then
temp.Free;
temp := newtemp;
end;
if NewWidth > bmp.Width then
begin
newtemp := FineResampleLarger(temp, NewWidth, temp.Height, ResampleFilter);
if (temp <> bmp) then
temp.Free;
temp := newtemp;
end;
if NewHeight > bmp.Height then
begin
newtemp := FineResampleLarger(temp, temp.Width, NewHeight, ResampleFilter);
if (temp <> bmp) then
temp.Free;
temp := newtemp;
end;
if temp <> bmp then
Result := temp
else
Result := bmp.Duplicate;
end;
end;
{------------------------ Wide kernel filtering adapted from Graphics32 ---------------------------}
function Constrain(const Value, Lo, Hi: Integer): Integer;
begin
if Value < Lo then
Result := Lo
else if Value > Hi then
Result := Hi
else
Result := Value;
end;
type
TPointRec = record
Pos: Integer;
Weight: Single;
end;
TCluster = array of TPointRec;
TMappingTable = array of TCluster;
{$warnings off}
function BuildMappingTable(
DstLo, DstHi: Integer;
ClipLo, ClipHi: Integer;
SrcLo, SrcHi: Integer;
KernelSmaller,KernelLarger: TWideKernelFilter): TMappingTable;
Const FullEdge = false;
var
SrcW, DstW, ClipW: Integer;
FilterWidth: Single;
Scale, OldScale: Single;
Center: Single;
Left, Right: Integer;
I, J, K: Integer;
Weight: Single;
begin
SrcW := SrcHi - SrcLo;
DstW := DstHi - DstLo;
ClipW := ClipHi - ClipLo;
if SrcW = 0 then
begin
Result := nil;
Exit;
end
else if SrcW = 1 then
begin
SetLength(Result, ClipW);
for I := 0 to ClipW - 1 do
begin
SetLength(Result[I], 1);
Result[I][0].Pos := 0;
Result[I][0].Weight := 1;
end;
Exit;
end;
SetLength(Result, ClipW);
if ClipW = 0 then Exit;
if FullEdge then Scale := DstW / SrcW
else Scale := (DstW - 1) / (SrcW - 1);
K := 0;
if Scale = 0 then
begin
SetLength(Result[0], 1);
Result[0][0].Pos := (SrcLo + SrcHi) div 2;
Result[0][0].Weight := 1;
end
else if Scale < 1 then
begin
FilterWidth := KernelSmaller.KernelWidth;
OldScale := Scale;
Scale := 1 / Scale;
FilterWidth := FilterWidth * Scale;
for I := 0 to ClipW - 1 do
begin
if FullEdge then
Center := SrcLo - 0.5 + (I - DstLo + ClipLo + 0.5) * Scale
else
Center := SrcLo + (I - DstLo + ClipLo) * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
for J := Left to Right do
begin
Weight := KernelSmaller.Interpolation((Center - J) * OldScale) * OldScale;
if Weight <> 0 then
begin
K := Length(Result[I]);
SetLength(Result[I], K + 1);
Result[I][K].Pos := Constrain(J, SrcLo, SrcHi - 1);
Result[I][K].Weight := Weight;
end;
end;
if Length(Result[I]) = 0 then
begin
SetLength(Result[I], 1);
Result[I][0].Pos := Floor(Center);
Result[I][0].Weight := 1;
end;
end;
end
else // scale > 1
begin
FilterWidth := KernelLarger.KernelWidth;
Scale := 1 / Scale;
for I := 0 to ClipW - 1 do
begin
if FullEdge then
Center := SrcLo - 0.5 + (I - DstLo + ClipLo + 0.5) * Scale
else
Center := SrcLo + (I - DstLo + ClipLo) * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
for J := Left to Right do
begin
Weight := KernelLarger.Interpolation(Center - j);
if Weight <> 0 then
begin
K := Length(Result[I]);
SetLength(Result[I], k + 1);
Result[I][K].Pos := Constrain(j, SrcLo, SrcHi - 1);
Result[I][K].Weight := Weight;
end;
end;
end;
end;
end;
{$warnings on}
function WideKernelResample(bmp: TBGRACustomBitmap;
NewWidth, NewHeight: integer; ResampleFilterSmaller, ResampleFilterLarger: TWideKernelFilter): TBGRACustomBitmap;
type
TSum = record
sumR,sumG,sumB,sumA: single;
end;
var
mapX,mapY: TMappingTable;
xb,yb,xc,yc,MapXLoPos,MapXHiPos: integer;
clusterX,clusterY: TCluster;
verticalSum: array of TSum;
scanlinesSrc: array of PBGRAPixel;
sum: TSum;
c: TBGRAPixel;
w,wa: single;
pdest: PBGRAPixel;
begin
result := bmp.NewBitmap(NewWidth,NewHeight);
if (NewWidth=0) or (NewHeight=0) then exit;
mapX := BuildMappingTable(0,NewWidth,0,NewWidth,0,bmp.Width,ResampleFilterSmaller,ResampleFilterLarger);
mapY := BuildMappingTable(0,NewHeight,0,NewHeight,0,bmp.Height,ResampleFilterSmaller,ResampleFilterLarger);
MapXLoPos := MapX[0][0].Pos;
MapXHiPos := MapX[NewWidth - 1][High(MapX[NewWidth - 1])].Pos;
setlength(verticalSum, MapXHiPos-MapXLoPos+1);
setlength(scanlinesSrc, bmp.Height);
for yb := 0 to bmp.Height-1 do
scanlinesSrc[yb] := bmp.ScanLine[yb];
for yb := 0 to NewHeight-1 do
begin
clusterY := mapY[yb];
for xb := MapXLoPos to MapXHiPos do
begin
fillchar(verticalSum[xb - MapXLoPos],sizeof(verticalSum[xb - MapXLoPos]),0);
for yc := 0 to high(clusterY) do
with verticalSum[xb - MapXLoPos] do
begin
c := (scanlinesSrc[clusterY[yc].Pos]+xb)^;
w := clusterY[yc].Weight;
wa := w * c.alpha;
sumA += wa;
sumR += c.red * wa;
sumG += c.green * wa;
sumB += c.blue * wa;
end;
end;
pdest := result.Scanline[yb];
for xb := 0 to NewWidth-1 do
begin
clusterX := mapX[xb];
{$hints off}
fillchar(sum,sizeof(sum),0);
{$hints on}
for xc := 0 to high(clusterX) do
begin
w := clusterX[xc].Weight;
with verticalSum[ClusterX[xc].Pos - MapXLoPos] do
begin
sum.sumA += sumA*w;
sum.sumR += sumR*w;
sum.sumG += sumG*w;
sum.sumB += sumB*w;
end;
end;
if sum.sumA < 0.5 then
pdest^ := BGRAPixelTransparent else
begin
c.red := constrain(round(sum.sumR/sum.sumA),0,255);
c.green := constrain(round(sum.sumG/sum.sumA),0,255);
c.blue := constrain(round(sum.sumB/sum.sumA),0,255);
if sum.sumA > 255 then
c.alpha := 255 else
c.alpha := round(sum.sumA);
pdest^ := c;
end;
inc(pdest);
end;
end;
end;
end.