-
-
Notifications
You must be signed in to change notification settings - Fork 924
/
Resample.pas
211 lines (202 loc) · 7.03 KB
/
Resample.pas
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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.