1 |
|
---|
2 | var
|
---|
3 | blurRowY,blurRowX: packed array of NativeUInt;
|
---|
4 | iRadiusX,iRadiusY: NativeInt;
|
---|
5 | weightFactor: NativeUInt;
|
---|
6 |
|
---|
7 | { Compute weights of pixels in a row }
|
---|
8 | procedure ComputeBlurRow;
|
---|
9 | var
|
---|
10 | i: NativeInt;
|
---|
11 | ofs: single;
|
---|
12 | begin
|
---|
13 | SetLength(blurRowX, 2*iRadiusX+1);
|
---|
14 | if frac(radiusX)=0 then ofs := 1 else ofs := frac(radiusX);
|
---|
15 | for i := 0 to iRadiusX do
|
---|
16 | begin
|
---|
17 | blurRowX[i] := round((i+ofs)*weightFactor);
|
---|
18 | blurRowX[high(blurRowX)-i] := blurRowX[i];
|
---|
19 | end;
|
---|
20 | SetLength(blurRowY, 2*iRadiusY+1);
|
---|
21 | if frac(radiusY)=0 then ofs := 1 else ofs := frac(radiusY);
|
---|
22 | for i := 0 to iRadiusY do
|
---|
23 | begin
|
---|
24 | blurRowY[i] := round((i+ofs)*weightFactor);
|
---|
25 | blurRowY[high(blurRowY)-i] := blurRowY[i];
|
---|
26 | end;
|
---|
27 | end;
|
---|
28 |
|
---|
29 |
|
---|
30 | var
|
---|
31 | srcDelta,
|
---|
32 | verticalWeightShift, horizontalWeightShift: NativeInt;
|
---|
33 | ys1,ys2: NativeInt;
|
---|
34 |
|
---|
35 | { Compute blur result in a vertical direction }
|
---|
36 | procedure ComputeVerticalRow(psrc: PBGRAPixel; var sums: TRowSum; pw: PNativeUInt; count: NativeInt);
|
---|
37 | var w: NativeUInt;
|
---|
38 | c: DWord;
|
---|
39 | begin
|
---|
40 | while count > 0 do
|
---|
41 | with sums do
|
---|
42 | begin
|
---|
43 | dec(count);
|
---|
44 | w := pw^; //apply pixel weight
|
---|
45 | inc(pw);
|
---|
46 | c := PDWord(psrc)^;
|
---|
47 | inc(PByte(psrc),srcDelta);
|
---|
48 | aDiv += w;
|
---|
49 | w *= ((c shr TBGRAPixel_AlphaShift) and $ff);
|
---|
50 | sumA += w;
|
---|
51 | w := w shr verticalWeightShift;
|
---|
52 | rgbDiv += w;
|
---|
53 | {$hints off}
|
---|
54 | sumR += ((c shr TBGRAPixel_RedShift) and $ff)*w;
|
---|
55 | sumG += ((c shr TBGRAPixel_GreenShift) and $ff)*w;
|
---|
56 | sumB += ((c shr TBGRAPixel_BlueShift) and $ff)*w;
|
---|
57 | {$hints on}
|
---|
58 | end;
|
---|
59 | end;
|
---|
60 |
|
---|
61 | var
|
---|
62 | psum, psumEnd: PRowSum;
|
---|
63 | sums: packed array of TRowSum;
|
---|
64 | sumStartIndex: NativeInt;
|
---|
65 | total: TRowSum;
|
---|
66 | extendedTotal: TExtendedRowSum;
|
---|
67 | yb,xb,xs,x,xEnd: NativeInt;
|
---|
68 | w: NativeUInt;
|
---|
69 | pw: PNativeUInt;
|
---|
70 | psrc,pdest: PBGRAPixel;
|
---|
71 | bmpWidth,bmpHeight : NativeInt;
|
---|
72 | accumulationFactor: double;
|
---|
73 | bounds: TRect;
|
---|
74 | highSum: NativeInt;
|
---|
75 | tempDest: TBGRACustomBitmap;
|
---|
76 |
|
---|
77 | begin
|
---|
78 | radiusX := round(radiusX*10)*0.1;
|
---|
79 | radiusY := round(radiusY*10)*0.1;
|
---|
80 | if (radiusX <= 0) and (radiusY <= 0) then
|
---|
81 | begin
|
---|
82 | ADestination.PutImage(0,0,bmp,dmSet);
|
---|
83 | exit;
|
---|
84 | end;
|
---|
85 | iRadiusX := ceil(radiusX);
|
---|
86 | iRadiusY := ceil(radiusY);
|
---|
87 | if (frac(radiusX)=0) and (frac(radiusY)=0) then
|
---|
88 | weightFactor:= 1
|
---|
89 | else
|
---|
90 | weightFactor:= 10;
|
---|
91 | bmpWidth := bmp.Width;
|
---|
92 | bmpHeight := bmp.Height;
|
---|
93 | //create output
|
---|
94 | if (ADestination.Width <> bmp.Width) or (ADestination.Height <> bmp.Height) then
|
---|
95 | raise exception.Create('Dimension mismatch');
|
---|
96 | bounds := bmp.GetImageBounds;
|
---|
97 | if IsRectEmpty(bounds) then exit;
|
---|
98 | bounds.Left := max(0, bounds.Left - iRadiusX);
|
---|
99 | bounds.Top := max(0, bounds.Top - iRadiusY);
|
---|
100 | bounds.Right := min(bmp.Width, bounds.Right + iRadiusX);
|
---|
101 | bounds.Bottom := min(bmp.Height, bounds.Bottom + iRadiusY);
|
---|
102 | if not IntersectRect(bounds,bounds,ABounds) then exit;
|
---|
103 |
|
---|
104 | if radiusX*radiusY >= 100 then
|
---|
105 | begin
|
---|
106 | tempDest := ADestination.NewBitmap(ADestination.Width,ADestination.Height);
|
---|
107 | FilterBlurBox(bmp,bounds,radiusX/3.2,radiusY/3.2,tempDest);
|
---|
108 | FilterBlurBox(tempDest,bounds,radiusX/2.9,radiusY/2.9,ADestination);
|
---|
109 | FilterBlurBox(ADestination,bounds,radiusX/3.2,radiusY/3.2,tempDest);
|
---|
110 | FilterBlurBox(tempDest,bounds,radiusX/2.3,radiusY/2.3,ADestination, ACheckShouldStop);
|
---|
111 | tempDest.Free;
|
---|
112 | exit;
|
---|
113 | end;
|
---|
114 |
|
---|
115 | accumulationFactor := (iRadiusY+2)*(iRadiusY+1) div 2 + (iRadiusY+1)*iRadiusY div 2;
|
---|
116 | accumulationFactor *= sqr(weightFactor);
|
---|
117 | verticalWeightShift := 0;
|
---|
118 | while accumulationFactor > (high(NativeUInt) shr 16) + 1 do
|
---|
119 | begin
|
---|
120 | inc(verticalWeightShift);
|
---|
121 | accumulationFactor *= 0.5;
|
---|
122 | end;
|
---|
123 | horizontalWeightShift:= 0;
|
---|
124 | accumulationFactor *= ((iRadiusX+2)*(iRadiusX+1) div 2 + (iRadiusX+1)*iRadiusX div 2);
|
---|
125 | accumulationFactor *= sqr(weightFactor);
|
---|
126 | while accumulationFactor > (high(NativeUInt) shr 16) + 1 do
|
---|
127 | begin
|
---|
128 | inc(horizontalWeightShift);
|
---|
129 | accumulationFactor *= 0.5;
|
---|
130 | end;
|
---|
131 | ComputeBlurRow;
|
---|
132 | //current vertical sums
|
---|
133 | setlength(sums, 2*iRadiusX+1);
|
---|
134 | highSum := high(Sums);
|
---|
135 | psumEnd := @sums[highSum];
|
---|
136 | inc(psumEnd);
|
---|
137 | if bmp.LineOrder = riloTopToBottom then
|
---|
138 | srcDelta := bmpWidth*sizeof(TBGRAPixel) else
|
---|
139 | srcDelta := -bmpWidth*sizeof(TBGRAPixel);
|
---|
140 |
|
---|
141 | xEnd := bounds.left-iRadiusX+highSum;
|
---|
142 | if xEnd >= bmpWidth then xEnd := bmpWidth-1;
|
---|
143 | //loop through destination bitmap
|
---|
144 | for yb := bounds.top to bounds.bottom-1 do
|
---|
145 | begin
|
---|
146 | if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break;
|
---|
147 | //evalute available vertical range
|
---|
148 | if yb - iRadiusY < 0 then
|
---|
149 | ys1 := iRadiusY - yb
|
---|
150 | else
|
---|
151 | ys1 := 0;
|
---|
152 | if yb + iRadiusY >= bmpHeight then
|
---|
153 | ys2 := bmpHeight-1 - yb + iRadiusY
|
---|
154 | else
|
---|
155 | ys2 := 2*iRadiusY;
|
---|
156 |
|
---|
157 | { initial vertical rows are computed here. Later,
|
---|
158 | for each pixel, vertical sums are shifted, so there
|
---|
159 | is only one vertical sum to calculate }
|
---|
160 | fillchar(sums[0],sizeof(TRowSum)*length(sums),0);
|
---|
161 | x := bounds.left-iRadiusX;
|
---|
162 | if x < 0 then
|
---|
163 | begin
|
---|
164 | xs := -x;
|
---|
165 | x := 0;
|
---|
166 | end else
|
---|
167 | xs := 0;
|
---|
168 | psrc := bmp.ScanLine[yb-iRadiusY+ys1]+x;
|
---|
169 | psum := @sums[xs];
|
---|
170 | pw := @blurRowY[ys1];
|
---|
171 | while true do
|
---|
172 | begin
|
---|
173 | ComputeVerticalRow(psrc,psum^,pw,ys2-ys1+1);
|
---|
174 | inc(x);
|
---|
175 | inc(psrc);
|
---|
176 | if x > xEnd then break;
|
---|
177 | inc(psum);
|
---|
178 | end;
|
---|
179 | sumStartIndex := 0;
|
---|
180 |
|
---|
181 | pdest := ADestination.scanline[yb]+bounds.left;
|
---|
182 | for xb := bounds.left to bounds.right-1 do
|
---|
183 | begin
|
---|
184 | //add vertical rows
|
---|
185 | pw := @blurRowX[0];
|
---|
186 | psum := @sums[sumStartIndex];
|
---|
187 | if horizontalWeightShift > 4 then
|
---|
188 | begin //we don't want to loose too much precision
|
---|
189 | fillchar({%H-}extendedTotal,sizeof(extendedTotal),0);
|
---|
190 | for xs := highSum downto 0 do
|
---|
191 | with psum^ do
|
---|
192 | begin
|
---|
193 | w := pw^;
|
---|
194 | inc(pw);
|
---|
195 | extendedTotal.sumA += TExtendedRowValue(sumA)*w;
|
---|
196 | extendedTotal.aDiv += TExtendedRowValue(aDiv)*w;
|
---|
197 | extendedTotal.sumR += TExtendedRowValue(sumR)*w;
|
---|
198 | extendedTotal.sumG += TExtendedRowValue(sumG)*w;
|
---|
199 | extendedTotal.sumB += TExtendedRowValue(sumB)*w;
|
---|
200 | extendedTotal.rgbDiv += TExtendedRowValue(rgbDiv)*w;
|
---|
201 | inc(psum);
|
---|
202 | if psum >= psumEnd then pSum := @sums[0];
|
---|
203 | end;
|
---|
204 | if (extendedTotal.aDiv > 0) and (extendedTotal.rgbDiv > 0) then
|
---|
205 | pdest^:= ComputeExtendedAverage(extendedTotal)
|
---|
206 | else
|
---|
207 | pdest^:= BGRAPixelTransparent;
|
---|
208 | end else
|
---|
209 | if horizontalWeightShift > 0 then
|
---|
210 | begin //lossy but efficient way
|
---|
211 | fillchar({%H-}total,sizeof(total),0);
|
---|
212 | for xs := highSum downto 0 do
|
---|
213 | with psum^ do
|
---|
214 | begin
|
---|
215 | w := pw^;
|
---|
216 | inc(pw);
|
---|
217 | total.sumA += sumA*w shr horizontalWeightShift;
|
---|
218 | total.aDiv += aDiv*w shr horizontalWeightShift;
|
---|
219 | total.sumR += sumR*w shr horizontalWeightShift;
|
---|
220 | total.sumG += sumG*w shr horizontalWeightShift;
|
---|
221 | total.sumB += sumB*w shr horizontalWeightShift;
|
---|
222 | total.rgbDiv += rgbDiv*w shr horizontalWeightShift;
|
---|
223 | inc(psum);
|
---|
224 | if psum >= psumEnd then pSum := @sums[0];
|
---|
225 | end;
|
---|
226 | if (total.aDiv > 0) and (total.rgbDiv > 0) then
|
---|
227 | pdest^:= ComputeClampedAverage(total)
|
---|
228 | else
|
---|
229 | pdest^:= BGRAPixelTransparent;
|
---|
230 | end else
|
---|
231 | begin //normal way
|
---|
232 | {$hints off}
|
---|
233 | fillchar(total,sizeof(total),0);
|
---|
234 | {$hints on}
|
---|
235 | for xs := highSum downto 0 do
|
---|
236 | with psum^ do
|
---|
237 | begin
|
---|
238 | w := pw^;
|
---|
239 | inc(pw);
|
---|
240 | total.sumA += sumA*w;
|
---|
241 | total.aDiv += aDiv*w;
|
---|
242 | total.sumR += sumR*w;
|
---|
243 | total.sumG += sumG*w;
|
---|
244 | total.sumB += sumB*w;
|
---|
245 | total.rgbDiv += rgbDiv*w;
|
---|
246 | inc(psum);
|
---|
247 | if psum >= psumEnd then pSum := @sums[0];
|
---|
248 | end;
|
---|
249 | if (total.aDiv > 0) and (total.rgbDiv > 0) then
|
---|
250 | pdest^ := ComputeAverage(total)
|
---|
251 | else
|
---|
252 | pdest^:= BGRAPixelTransparent;
|
---|
253 | end;
|
---|
254 | inc(pdest);
|
---|
255 | //shift vertical rows
|
---|
256 | psum := @sums[sumStartIndex];
|
---|
257 | fillchar(psum^,sizeof(TRowSum),0);
|
---|
258 | if x < bmpWidth then
|
---|
259 | begin
|
---|
260 | ComputeVerticalRow(psrc,psum^,@blurRowY[ys1],ys2-ys1+1);
|
---|
261 | inc(x);
|
---|
262 | inc(psrc);
|
---|
263 | end;
|
---|
264 | inc(sumStartIndex);
|
---|
265 | if sumStartIndex > highSum then sumStartIndex := 0;
|
---|
266 | end;
|
---|
267 | end;
|
---|
268 | ADestination.InvalidateBitmap;
|
---|
269 | end;
|
---|
270 |
|
---|