source: trunk/Packages/bgrabitmap/blurfast.inc

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 8.1 KB
Line 
1
2var
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
30var
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
61var
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
77begin
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;
269end;
270
Note: See TracBrowser for help on using the repository browser.