1 | type
|
---|
2 | PWeightedPixel = ^TWeightedPixel;
|
---|
3 | TWeightedPixel = packed record
|
---|
4 | Coord: TPoint;
|
---|
5 | Weight: NativeInt;
|
---|
6 | PtrOfs: NativeInt;
|
---|
7 | end;
|
---|
8 |
|
---|
9 | var
|
---|
10 | maskWidth,maskHeight: integer;
|
---|
11 | blurOfs: TPoint;
|
---|
12 | ppixel: PWeightedPixel;
|
---|
13 | Pixel: array of TWeightedPixel;
|
---|
14 | PixelArrayLineStart: array of integer;
|
---|
15 | DiffPixel: array of TWeightedPixel;
|
---|
16 | DiffPixelArrayLineStart: array of integer;
|
---|
17 |
|
---|
18 | bmpWidth,bmpHeight,lineDelta: NativeInt;
|
---|
19 |
|
---|
20 | procedure LoadMask(out ABlurOfs: TPoint);
|
---|
21 | var x,y,n,i: NativeInt;
|
---|
22 | tempWeight: NativeInt;
|
---|
23 | diffMask: array of packed array of NativeInt;
|
---|
24 | p: PBGRAPixel;
|
---|
25 | begin
|
---|
26 | ABlurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1);
|
---|
27 |
|
---|
28 | //count number of non empty pixels
|
---|
29 | maskWidth := blurMask.Width;
|
---|
30 | maskHeight := blurMask.Height;
|
---|
31 | n := 0;
|
---|
32 | p := blurMask.Data;
|
---|
33 | for i := blurMask.NbPixels-1 downto 0 do
|
---|
34 | begin
|
---|
35 | if p^.red <> 0 then inc(n);
|
---|
36 | inc(p);
|
---|
37 | end;
|
---|
38 |
|
---|
39 | //initialize arrays
|
---|
40 | setlength(diffMask, maskHeight, maskWidth+1);
|
---|
41 | for y := 0 to maskHeight - 1 do
|
---|
42 | fillchar(diffMask[y,0], (maskWidth+1)*sizeof(NativeInt), 0);
|
---|
43 |
|
---|
44 | setlength(Pixel, n);
|
---|
45 | setlength(PixelArrayLineStart, maskHeight+1); //stores the first pixel of each line
|
---|
46 | n := 0;
|
---|
47 | //compute mask variations and initial mask pixel list
|
---|
48 | for y := 0 to maskHeight - 1 do
|
---|
49 | begin
|
---|
50 | PixelArrayLineStart[y] := n;
|
---|
51 | p := blurMask.ScanLine[y];
|
---|
52 | for x := 0 to maskWidth - 1 do
|
---|
53 | begin
|
---|
54 | tempWeight := p^.red;
|
---|
55 | inc(p);
|
---|
56 | diffMask[y,x] -= tempWeight;
|
---|
57 | diffMask[y,x+1] += tempWeight;
|
---|
58 |
|
---|
59 | if tempWeight <> 0 then
|
---|
60 | begin
|
---|
61 | Pixel[n].Weight := tempWeight;
|
---|
62 | Pixel[n].Coord := Point(x,y);
|
---|
63 | Pixel[n].PtrOfs := (y-ABlurOfs.Y)*lineDelta + (x-ABlurOfs.X)*sizeof(TBGRAPixel);
|
---|
64 | Inc(n);
|
---|
65 | end;
|
---|
66 | end;
|
---|
67 | end;
|
---|
68 | PixelArrayLineStart[maskHeight] := n;
|
---|
69 |
|
---|
70 | //count number of diff pixels
|
---|
71 | n := 0;
|
---|
72 | for y := 0 to maskHeight - 1 do
|
---|
73 | for x := 0 to maskWidth do
|
---|
74 | if diffMask[y,x] <> 0 then Inc(n);
|
---|
75 |
|
---|
76 | //initialize arrays
|
---|
77 | setlength(DiffPixel, n);
|
---|
78 | setlength(DiffPixelArrayLineStart, maskHeight+1); //stores the first pixel of each diff line
|
---|
79 | n := 0;
|
---|
80 | //compute diff pixel list
|
---|
81 | for y := 0 to maskHeight - 1 do
|
---|
82 | begin
|
---|
83 | DiffPixelArrayLineStart[y] := n;
|
---|
84 | for x := 0 to maskWidth do
|
---|
85 | begin
|
---|
86 | tempWeight := diffMask[y,x];
|
---|
87 | if tempWeight <> 0 then
|
---|
88 | begin
|
---|
89 | DiffPixel[n].Weight := tempWeight;
|
---|
90 | DiffPixel[n].Coord := Point(x-1,y);
|
---|
91 | DiffPixel[n].PtrOfs := (y-ABlurOfs.Y)*lineDelta + (x-ABlurOfs.X-1)*sizeof(TBGRAPixel);
|
---|
92 | Inc(n);
|
---|
93 | end;
|
---|
94 | end;
|
---|
95 | end;
|
---|
96 | DiffPixelArrayLineStart[maskHeight] := n;
|
---|
97 | end;
|
---|
98 |
|
---|
99 | function PrepareScan(AWantedBounds: TRect; out AClippedBounds: TRect): boolean;
|
---|
100 | begin
|
---|
101 | //evaluate required bounds taking blur radius into acount
|
---|
102 | AClippedBounds := bmp.GetImageBounds;
|
---|
103 | if IsRectEmpty(AClippedBounds) then
|
---|
104 | begin
|
---|
105 | result := false;
|
---|
106 | exit;
|
---|
107 | end;
|
---|
108 | AClippedBounds.Left := max(0, AClippedBounds.Left - blurOfs.X);
|
---|
109 | AClippedBounds.Top := max(0, AClippedBounds.Top - blurOfs.Y);
|
---|
110 | AClippedBounds.Right := min(bmpWidth, AClippedBounds.Right + maskWidth - 1 - blurOfs.X);
|
---|
111 | AClippedBounds.Bottom := min(bmpHeight, AClippedBounds.Bottom + maskHeight - 1 - blurOfs.Y);
|
---|
112 | if not IntersectRect(AClippedBounds, AClippedBounds, AWantedBounds) then
|
---|
113 | begin
|
---|
114 | result := false;
|
---|
115 | exit;
|
---|
116 | end;
|
---|
117 |
|
---|
118 | result := true;
|
---|
119 | end;
|
---|
120 |
|
---|
121 | var
|
---|
122 | bounds: TRect;
|
---|
123 | yb, xb: NativeInt;
|
---|
124 | mindy, maxdy, n, nStart, nCount, nDiffStart, nDiffCount: NativeInt;
|
---|
125 | bmpX,bmpXBase,bmpYBase: NativeInt;
|
---|
126 | pixMaskAlpha, maskAlpha: NativeInt;
|
---|
127 | tempPixel: TBGRAPixel;
|
---|
128 | pdest : PBGRAPixel;
|
---|
129 | psrc : PByte;
|
---|
130 |
|
---|
131 | begin
|
---|
132 | bmpWidth := bmp.Width;
|
---|
133 | bmpHeight:= bmp.Height;
|
---|
134 | if bmp.LineOrder = riloTopToBottom then
|
---|
135 | lineDelta := bmpWidth*sizeof(TBGRAPixel) else
|
---|
136 | lineDelta := -bmpWidth*sizeof(TBGRAPixel);
|
---|
137 |
|
---|
138 | if (ADestination.Width <> bmpWidth) or (ADestination.Height <> bmpHeight) then
|
---|
139 | raise exception.Create('Dimension mismatch');
|
---|
140 |
|
---|
141 | LoadMask(blurOfs);
|
---|
142 | if not PrepareScan(ABounds, bounds) then exit; //nothing to do
|
---|
143 |
|
---|
144 | bmpYBase := bounds.Top - blurOfs.Y;
|
---|
145 |
|
---|
146 | //loop through destination
|
---|
147 | for yb := bounds.Top to bounds.Bottom - 1 do
|
---|
148 | begin
|
---|
149 | if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break;
|
---|
150 | psrc := PByte(bmp.ScanLine[yb]+bounds.Left);
|
---|
151 | pdest := ADestination.ScanLine[yb] + bounds.Left;
|
---|
152 | //compute vertical range
|
---|
153 | mindy := max(-blurOfs.Y, -yb);
|
---|
154 | maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmpHeight - 1 - yb);
|
---|
155 |
|
---|
156 | sumR := 0;
|
---|
157 | sumG := 0;
|
---|
158 | sumB := 0;
|
---|
159 | sumA := 0;
|
---|
160 | Adiv := 0;
|
---|
161 | {$ifdef PARAM_MASKSHIFT}
|
---|
162 | RGBdiv := 0;
|
---|
163 | {$endif}
|
---|
164 |
|
---|
165 | bmpXBase := bounds.Left-blurOfs.X;
|
---|
166 | nStart := PixelArrayLineStart[mindy+blurOfs.Y];
|
---|
167 | nCount := PixelArrayLineStart[maxdy+blurOfs.Y+1]-nStart;
|
---|
168 | ppixel:= @Pixel[nStart];
|
---|
169 | //go through pixel list of the current vertical range
|
---|
170 | for n := nCount-1 downto 0 do
|
---|
171 | begin
|
---|
172 | bmpX := bmpXBase+ppixel^.Coord.x;
|
---|
173 | //check horizontal range
|
---|
174 | if (bmpX >= 0) and (bmpX < bmpWidth) then
|
---|
175 | begin
|
---|
176 | tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^;
|
---|
177 | maskAlpha := ppixel^.Weight;
|
---|
178 | pixMaskAlpha := maskAlpha * tempPixel.alpha;
|
---|
179 | sumA += pixMaskAlpha;
|
---|
180 | Adiv += maskAlpha;
|
---|
181 | {$ifdef PARAM_MASKSHIFT}
|
---|
182 | pixMaskAlpha := pixMaskAlpha shr maskShift;
|
---|
183 | RGBdiv += pixMaskAlpha;
|
---|
184 | {$endif}
|
---|
185 | {$hints off}
|
---|
186 | sumR += tempPixel.red * pixMaskAlpha;
|
---|
187 | sumG += tempPixel.green * pixMaskAlpha;
|
---|
188 | sumB += tempPixel.blue * pixMaskAlpha;
|
---|
189 | {$hints on}
|
---|
190 | end;
|
---|
191 | inc(ppixel);
|
---|
192 | end;
|
---|
193 |
|
---|
194 | //compute average
|
---|
195 | if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
|
---|
196 | pdest^ := BGRAPixelTransparent
|
---|
197 | else
|
---|
198 | pdest^ := computeAverage;
|
---|
199 |
|
---|
200 | nDiffStart := DiffPixelArrayLineStart[mindy+blurOfs.Y];
|
---|
201 | nDiffCount := DiffPixelArrayLineStart[maxdy+blurOfs.Y+1]-nDiffStart;
|
---|
202 |
|
---|
203 | if nDiffCount < nCount then
|
---|
204 | begin
|
---|
205 | for xb := bounds.Left+1 to Bounds.Right - 1 do
|
---|
206 | begin
|
---|
207 | Inc(pdest);
|
---|
208 | inc(bmpXBase);
|
---|
209 | inc(psrc,sizeof(TBGRAPixel));
|
---|
210 |
|
---|
211 | ppixel:= @DiffPixel[nDiffStart];
|
---|
212 | for n := nDiffCount-1 downto 0 do
|
---|
213 | begin
|
---|
214 | bmpX := bmpXBase+ppixel^.Coord.x;
|
---|
215 | if (bmpX >= 0) and (bmpX < bmpWidth) then
|
---|
216 | begin
|
---|
217 | tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^;
|
---|
218 | maskAlpha := ppixel^.Weight;
|
---|
219 | pixMaskAlpha := maskAlpha * tempPixel.alpha;
|
---|
220 | sumA += pixMaskAlpha;
|
---|
221 | Adiv += maskAlpha;
|
---|
222 | {$ifdef PARAM_MASKSHIFT}
|
---|
223 | pixMaskAlpha := (cardinal(pixMaskAlpha)+$80000000) shr maskShift - ($80000000 shr maskShift);
|
---|
224 | RGBdiv += pixMaskAlpha;
|
---|
225 | {$endif}
|
---|
226 | {$hints off}
|
---|
227 | sumR += tempPixel.red * pixMaskAlpha;
|
---|
228 | sumG += tempPixel.green * pixMaskAlpha;
|
---|
229 | sumB += tempPixel.blue * pixMaskAlpha;
|
---|
230 | {$hints on}
|
---|
231 | end;
|
---|
232 | inc(ppixel);
|
---|
233 | end;
|
---|
234 |
|
---|
235 | //compute average
|
---|
236 | if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
|
---|
237 | pdest^ := BGRAPixelTransparent
|
---|
238 | else
|
---|
239 | pdest^ := ComputeAverage;
|
---|
240 | end;
|
---|
241 | end else
|
---|
242 | begin
|
---|
243 | for xb := bounds.Left+1 to Bounds.Right - 1 do
|
---|
244 | begin
|
---|
245 | Inc(pdest);
|
---|
246 | inc(bmpXBase);
|
---|
247 | inc(psrc,sizeof(TBGRAPixel));
|
---|
248 |
|
---|
249 | sumR := 0;
|
---|
250 | sumG := 0;
|
---|
251 | sumB := 0;
|
---|
252 | sumA := 0;
|
---|
253 | Adiv := 0;
|
---|
254 | {$ifdef PARAM_MASKSHIFT}
|
---|
255 | RGBdiv := 0;
|
---|
256 | {$endif}
|
---|
257 |
|
---|
258 | ppixel:= @Pixel[nStart];
|
---|
259 | for n := nCount-1 downto 0 do
|
---|
260 | begin
|
---|
261 | bmpX := bmpXBase+ppixel^.Coord.x;
|
---|
262 | //check horizontal range
|
---|
263 | if (bmpX >= 0) and (bmpX < bmpWidth) then
|
---|
264 | begin
|
---|
265 | tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^;
|
---|
266 | maskAlpha := ppixel^.Weight;
|
---|
267 | pixMaskAlpha := maskAlpha * tempPixel.alpha;
|
---|
268 | sumA += pixMaskAlpha;
|
---|
269 | Adiv += maskAlpha;
|
---|
270 | {$ifdef PARAM_MASKSHIFT}
|
---|
271 | pixMaskAlpha := pixMaskAlpha shr maskShift;
|
---|
272 | RGBdiv += pixMaskAlpha;
|
---|
273 | {$endif}
|
---|
274 | {$hints off}
|
---|
275 | sumR += tempPixel.red * pixMaskAlpha;
|
---|
276 | sumG += tempPixel.green * pixMaskAlpha;
|
---|
277 | sumB += tempPixel.blue * pixMaskAlpha;
|
---|
278 | {$hints on}
|
---|
279 | end;
|
---|
280 | inc(ppixel);
|
---|
281 | end;
|
---|
282 |
|
---|
283 | //compute average
|
---|
284 | if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
|
---|
285 | pdest^ := BGRAPixelTransparent
|
---|
286 | else
|
---|
287 | pdest^ := computeAverage;
|
---|
288 | end;
|
---|
289 | end;
|
---|
290 |
|
---|
291 | inc(bmpYBase);
|
---|
292 | end;
|
---|
293 | ADestination.InvalidateBitmap;
|
---|
294 | end;
|
---|
295 | {$undef PARAM_MASKSHIFT}
|
---|
296 |
|
---|