Changeset 317 for GraphicTest/BGRABitmap/bgrafilters.pas
- Timestamp:
- Feb 1, 2012, 3:02:33 PM (13 years ago)
- Location:
- GraphicTest/BGRABitmap
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
GraphicTest/BGRABitmap
-
Property svn:ignore
set to
lib
-
Property svn:ignore
set to
-
GraphicTest/BGRABitmap/bgrafilters.pas
r210 r317 5 5 interface 6 6 7 { Here are some filters that can be applied to a bitmap. The filters 8 take a source image as a parameter and gives a filtered image as 9 a result. } 10 7 11 uses 8 Classes, SysUtils, BGRADefaultBitmap, BGRABitmapTypes; 9 10 function FilterMedian(bmp: TBGRADefaultBitmap; 11 Option: TMedianOption): TBGRADefaultBitmap; 12 function FilterSmartZoom3(bmp: TBGRADefaultBitmap; 13 Option: TMedianOption): TBGRADefaultBitmap; 14 function FilterSharpen(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 15 function FilterBlurRadialPrecise(bmp: TBGRADefaultBitmap; 16 radius: single): TBGRADefaultBitmap; 17 function FilterBlurRadial(bmp: TBGRADefaultBitmap; radius: integer; 18 blurType: TRadialBlurType): TBGRADefaultBitmap; 19 function FilterBlurMotion(bmp: TBGRADefaultBitmap; distance: single; 20 angle: single; oriented: boolean): TBGRADefaultBitmap; 21 function FilterBlur(bmp: TBGRADefaultBitmap; 22 blurMask: TBGRADefaultBitmap): TBGRADefaultBitmap; 23 function FilterEmboss(bmp: TBGRADefaultBitmap; angle: single): TBGRADefaultBitmap; 24 function FilterEmbossHighlight(bmp: TBGRADefaultBitmap; 25 FillSelection: boolean): TBGRADefaultBitmap; 26 function FilterNormalize(bmp: TBGRADefaultBitmap; 27 eachChannel: boolean = True): TBGRADefaultBitmap; 28 function FilterRotate(bmp: TBGRADefaultBitmap; origin: TPointF; 29 angle: single): TBGRADefaultBitmap; 30 function FilterGrayscale(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 31 function FilterContour(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 32 function FilterSphere(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 33 function FilterCylinder(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 34 function FilterPlane(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 12 Classes, SysUtils, BGRABitmapTypes; 13 14 { The median filter consist in calculating the median value of pixels. Here 15 a square of 9x9 pixel is considered. The median allow to select the most 16 representative colors. The option parameter allow to choose to smooth the 17 result or not. } 18 function FilterMedian(bmp: TBGRACustomBitmap; 19 Option: TMedianOption): TBGRACustomBitmap; 20 21 { SmartZoom x3 is a filter that upsizes 3 times the picture and add 22 pixels that could be logically expected (horizontal, vertical, diagonal lines) } 23 function FilterSmartZoom3(bmp: TBGRACustomBitmap; 24 Option: TMedianOption): TBGRACustomBitmap; 25 26 { Sharpen filter add more contrast between pixels } 27 function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 28 29 { A radial blur applies a blur with a circular influence, i.e, each pixel 30 is merged with pixels within the specified radius. There is an exception 31 with rbFast blur, the optimization entails an hyperbolic shape. } 32 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer; 33 blurType: TRadialBlurType): TBGRACustomBitmap; 34 35 { The precise blur allow to specify the blur radius with subpixel accuracy } 36 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; 37 radius: single): TBGRACustomBitmap; 38 39 { Motion blur merge pixels in a direction. The oriented parameter specifies 40 if the weights of the pixels are the same along the line or not. } 41 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; 42 angle: single; oriented: boolean): TBGRACustomBitmap; 43 44 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; 45 46 { General purpose blur filter, with a blur mask as parameter to describe 47 how pixels influence each other } 48 function FilterBlur(bmp: TBGRACustomBitmap; 49 blurMask: TBGRACustomBitmap): TBGRACustomBitmap; 50 51 { Emboss filter compute a color difference in the angle direction } 52 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap; 53 54 { Emboss highlight computes a sort of emboss with 45 degrees angle and 55 with standard selection color (white/black and filled with blue) } 56 function FilterEmbossHighlight(bmp: TBGRACustomBitmap; 57 FillSelection: boolean): TBGRACustomBitmap; 58 59 { Normalize use the whole available range of values, making dark colors darkest possible 60 and light colors lightest possible } 61 function FilterNormalize(bmp: TBGRACustomBitmap; 62 eachChannel: boolean = True): TBGRACustomBitmap; 63 64 { Rotate filter rotate the image and clip it in the bounding rectangle } 65 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF; 66 angle: single): TBGRACustomBitmap; 67 68 { Grayscale converts colored pixel into grayscale with same luminosity } 69 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 70 71 { Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil } 72 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 73 74 { Distort the image as if it were on a sphere } 75 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 76 77 { Twirl distortion, i.e. a progressive rotation } 78 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 79 80 { Distort the image as if it were on a vertical cylinder } 81 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 82 83 { Compute a plane projection towards infinity (SLOW) } 84 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 35 85 36 86 implementation 37 87 38 uses Math ;39 40 function FilterSmartZoom3(bmp: TBGRA DefaultBitmap;41 Option: TMedianOption): TBGRA DefaultBitmap;88 uses Math, GraphType, Dialogs, BGRATransform; 89 90 function FilterSmartZoom3(bmp: TBGRACustomBitmap; 91 Option: TMedianOption): TBGRACustomBitmap; 42 92 type 43 93 TSmartDiff = record … … 48 98 xb, yb: integer; 49 99 diag1, diag2, h1, h2, v1, v2: TSmartDiff; 50 c : TBGRAPixel;51 temp, median: TBGRA DefaultBitmap;100 c,c1,c2: TBGRAPixel; 101 temp, median: TBGRACustomBitmap; 52 102 53 103 function ColorDiff(c1, c2: TBGRAPixel): single; … … 156 206 if diag1.cd < 0.3 then 157 207 begin 158 c := MergeBGRA(bmp.GetPixel(xb, yb), bmp.GetPixel(xb + 1, yb + 1)); 208 c1 := bmp.GetPixel(xb, yb); 209 c2 := bmp.GetPixel(integer(xb + 1), integer(yb + 1)); 210 c := MergeBGRA(c1, c2); 159 211 //restore 160 212 Result.SetPixel(xb * 3 + 2, yb * 3 + 2, bmp.GetPixel(xb, yb)); 161 Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel( xb + 1, yb + 1));213 Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(integer(xb + 1), integer(yb + 1))); 162 214 163 215 if (diag1.sd < h1.sd) and (diag1.sd < v2.sd) then … … 173 225 if diag2.cd < 0.3 then 174 226 begin 175 c := MergeBGRA(bmp.GetPixel(xb, yb + 1), bmp.GetPixel(xb + 1, yb)); 227 c1 := bmp.GetPixel(xb, yb + 1); 228 c2 := bmp.GetPixel(xb + 1, yb); 229 c := MergeBGRA(c1, c2); 176 230 //restore 177 231 Result.SetPixel(xb * 3 + 3, yb * 3 + 2, bmp.GetPixel(xb + 1, yb)); … … 190 244 end; 191 245 192 function FilterSharpen(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 246 { This filter compute for each pixel the mean of the eight surrounding pixels, 247 then the difference between this average pixel and the pixel at the center 248 of the square. Finally the difference is added to the new pixel, exagerating 249 its difference with its neighbours. } 250 function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 193 251 const 194 252 nbpix = 8; … … 204 262 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 205 263 264 //determine where pixels are in the bitmap 206 265 bounds := bmp.GetImageBounds; 207 266 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then … … 212 271 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1); 213 272 273 //loop through the destination bitmap 214 274 for yb := bounds.Top to bounds.Bottom - 1 do 215 275 begin … … 217 277 for xb := bounds.Left to bounds.Right - 1 do 218 278 begin 279 //for each pixel, read eight surrounding pixels in the source bitmap 219 280 n := 0; 220 281 for dy := -1 to 1 do … … 222 283 if (dx <> 0) or (dy <> 0) then 223 284 begin 224 a_pixels[n] := bmp.GetPixel( xb + dx, yb + dy);285 a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy)); 225 286 Inc(n); 226 287 end; 227 288 289 //compute sum 228 290 sumR := 0; 229 291 sumG := 0; … … 246 308 {$hints on} 247 309 310 //we finally have an average pixel 248 311 if (RGBdiv = 0) then 249 312 refPixel := BGRAPixelTransparent … … 256 319 end; 257 320 321 //read the pixel at the center of the square 258 322 tempPixel := bmp.GetPixel(xb, yb); 259 323 if refPixel <> BGRAPixelTransparent then 260 324 begin 325 //compute sharpened pixel by adding the difference 261 326 tempPixel.red := max(0, min(255, tempPixel.red + 262 327 integer(tempPixel.red - refPixel.red))); … … 275 340 end; 276 341 277 function FilterBlurRadialPrecise(bmp: TBGRADefaultBitmap; 278 radius: single): TBGRADefaultBitmap; 279 var 280 blurShape: TBGRADefaultBitmap; 342 { Precise blur builds a blur mask with a gradient fill and use 343 general purpose blur } 344 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; 345 radius: single): TBGRACustomBitmap; 346 var 347 blurShape: TBGRACustomBitmap; 281 348 intRadius: integer; 282 349 begin 350 if radius = 0 then 351 begin 352 result := bmp.Duplicate; 353 exit; 354 end; 283 355 intRadius := ceil(radius); 284 blurShape := TBGRADefaultBitmap.Create(2 * intRadius + 1, 2 * intRadius + 1);356 blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1); 285 357 blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite, 286 358 BGRABlack, gtRadial, pointF(intRadius, intRadius), pointF( … … 290 362 end; 291 363 292 function FilterBlurRadialNormal(bmp: TBGRADefaultBitmap; 293 radius: integer): TBGRADefaultBitmap; 294 var 295 blurShape: TBGRADefaultBitmap; 296 begin 297 blurShape := TBGRADefaultBitmap.Create(2 * radius + 1, 2 * radius + 1); 364 { This is a clever solution for fast computing of the blur 365 effect : it stores an array of vertical sums forming a square 366 around the pixel which moves with it. For each new pixel, 367 the vertical sums are kept except for the last column of 368 the square } 369 function FilterBlurFast(bmp: TBGRACustomBitmap; 370 radius: integer): TBGRACustomBitmap; 371 372 type 373 TRowSum = record 374 sumR,sumG,sumB,rgbDiv,sumA,aDiv: cardinal; 375 end; 376 377 function ComputeAverage(sum: TRowSum): TBGRAPixel; 378 begin 379 result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv; 380 result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv; 381 result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv; 382 result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv; 383 end; 384 385 {$I blurfast.inc} 386 387 { Normal radial blur compute a blur mask with a GradientFill and 388 then posterize to optimize general purpose blur } 389 function FilterBlurRadialNormal(bmp: TBGRACustomBitmap; 390 radius: integer): TBGRACustomBitmap; 391 var 392 blurShape: TBGRACustomBitmap; 393 n: Integer; 394 p: PBGRAPixel; 395 begin 396 blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1); 298 397 blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite, 299 398 BGRABlack, gtRadial, pointF(radius, radius), pointF(-0.5, radius), dmSet); 399 p := blurShape.Data; 400 for n := 0 to blurShape.NbPixels-1 do 401 begin 402 p^.red := p^.red and $F0; 403 p^.green := p^.red; 404 p^.blue := p^.red; 405 inc(p); 406 end; 300 407 Result := FilterBlur(bmp, blurShape); 301 408 blurShape.Free; 302 409 end; 303 410 304 function FilterBlurDisk(bmp: TBGRADefaultBitmap; radius: integer): TBGRADefaultBitmap; 305 var 306 blurShape: TBGRADefaultBitmap; 307 begin 308 blurShape := TBGRADefaultBitmap.Create(2 * radius + 1, 2 * radius + 1); 411 { Blur disk creates a disk mask with a FillEllipse } 412 function FilterBlurDisk(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap; 413 var 414 blurShape: TBGRACustomBitmap; 415 begin 416 blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1); 309 417 blurShape.Fill(BGRABlack); 310 418 blurShape.FillEllipseAntialias(radius, radius, radius + 0.5, radius + 0.5, BGRAWhite); … … 313 421 end; 314 422 315 function FilterBlurCorona(bmp: TBGRADefaultBitmap; radius: integer): TBGRADefaultBitmap; 316 var 317 blurShape: TBGRADefaultBitmap; 318 begin 319 blurShape := TBGRADefaultBitmap.Create(2 * radius + 1, 2 * radius + 1); 423 { Corona blur use a circle as mask } 424 function FilterBlurCorona(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap; 425 var 426 blurShape: TBGRACustomBitmap; 427 begin 428 blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1); 320 429 blurShape.Fill(BGRABlack); 321 430 blurShape.EllipseAntialias(radius, radius, radius, radius, BGRAWhite, 1); … … 324 433 end; 325 434 326 function FilterBlurRadial(bmp: TBGRADefaultBitmap; radius: integer; 327 blurType: TRadialBlurType): TBGRADefaultBitmap; 328 begin 435 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer; 436 blurType: TRadialBlurType): TBGRACustomBitmap; 437 begin 438 if radius = 0 then 439 begin 440 result := bmp.Duplicate; 441 exit; 442 end; 329 443 case blurType of 330 444 rbCorona: Result := FilterBlurCorona(bmp, radius); 331 445 rbDisk: Result := FilterBlurDisk(bmp, radius); 332 446 rbNormal: Result := FilterBlurRadialNormal(bmp, radius); 447 rbFast: Result := FilterBlurFast(bmp, radius); 333 448 rbPrecise: Result := FilterBlurRadialPrecise(bmp, radius / 10); 334 449 else … … 337 452 end; 338 453 339 function FilterBlurMotion(bmp: TBGRADefaultBitmap; distance: single; 340 angle: single; oriented: boolean): TBGRADefaultBitmap; 341 var 342 blurShape: TBGRADefaultBitmap; 454 { This filter draws an antialiased line to make the mask, and 455 if the motion blur is oriented, does a GradientFill to orient it } 456 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; 457 angle: single; oriented: boolean): TBGRACustomBitmap; 458 var 459 blurShape: TBGRACustomBitmap; 343 460 intRadius: integer; 344 461 dx, dy, d: single; 345 462 begin 463 if distance = 0 then 464 begin 465 result := bmp.Duplicate; 466 exit; 467 end; 346 468 intRadius := ceil(distance / 2); 347 blurShape := TBGRADefaultBitmap.Create(2 * intRadius + 1, 2 * intRadius + 1);469 blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1); 348 470 d := distance / 2; 349 471 dx := cos(angle * Pi / 180); … … 362 484 end; 363 485 364 function FilterBlur(bmp: TBGRADefaultBitmap; 365 blurMask: TBGRADefaultBitmap): TBGRADefaultBitmap; 366 var 367 yb, xb: integer; 368 dx, dy, mindx, maxdx, mindy, maxdy, n, j: integer; 369 a_pixels: array of TBGRAPixel; 370 weights: array of integer; 371 sumR, sumG, sumB, sumA, Adiv, RGBdiv: cardinal; 372 RGBweight: byte; 373 tempPixel, refPixel: TBGRAPixel; 374 shapeMatrix: array of array of byte; 375 pdest, psrc: PBGRAPixel; 376 blurOfs: TPoint; 377 bounds: TRect; 378 begin 379 blurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1); 380 381 setlength(shapeMatrix, blurMask.Width, blurMask.Height); 382 n := 0; 383 for yb := 0 to blurMask.Height - 1 do 384 for xb := 0 to blurMask.Width - 1 do 385 begin 386 shapeMatrix[yb, xb] := blurMask.GetPixel(xb, yb).red; 387 if shapeMatrix[yb, xb] <> 0 then 388 Inc(n); 389 end; 390 391 setlength(a_pixels, n); 392 setlength(weights, n); 393 394 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 395 bounds := bmp.GetImageBounds; 396 if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then 486 { General purpose blur : compute pixel sum according to the mask and then 487 compute only difference while scanning from the left to the right } 488 function FilterBlurSmallMask(bmp: TBGRACustomBitmap; 489 blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward; 490 function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap; 491 blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap; forward; 492 function FilterBlurBigMask(bmp: TBGRACustomBitmap; 493 blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward; 494 495 //make sure value is in the range 0..255 496 function clampByte(value: integer): byte; inline; 497 begin 498 if value < 0 then result := 0 else 499 if value > 255 then result := 255 else 500 result := value; 501 end; 502 503 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; 504 useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap; 505 var yb,xb, xs,ys, tx,ty: integer; 506 psrc,pdest: PBGRAPixel; 507 temp,stretched: TBGRACustomBitmap; 508 oldfilter: TResampleFilter; 509 begin 510 if pixelSize < 1 then 511 begin 512 result := bmp.Duplicate; 397 513 exit; 398 bounds.Left := max(0, bounds.Left - blurOfs.X); 399 bounds.Top := max(0, bounds.Top - blurOfs.Y); 400 bounds.Right := min(bmp.Width, bounds.Right + blurMask.Width - 1 - blurOfs.X); 401 bounds.Bottom := min(bmp.Height, bounds.Bottom + blurMask.Height - 1 - blurOfs.Y); 402 403 for yb := bounds.Top to bounds.Bottom - 1 do 404 begin 405 pdest := Result.ScanLine[yb] + bounds.Left; 406 for xb := bounds.Left to Bounds.Right - 1 do 407 begin 408 n := 0; 409 mindx := max(-blurOfs.X, -xb); 410 mindy := max(-blurOfs.Y, -yb); 411 maxdx := min(blurMask.Width - 1 - blurOfs.X, bmp.Width - 1 - xb); 412 maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmp.Height - 1 - yb); 413 for dy := mindy to maxdy do 414 begin 415 psrc := bmp.scanline[yb + dy] + (xb + mindx); 416 for dx := mindx to maxdx do 417 begin 418 j := shapeMatrix[dy + blurOfs.Y, dx + blurOfs.X]; 419 if j <> 0 then 420 begin 421 a_pixels[n] := psrc^; 422 weights[n] := (a_pixels[n].alpha * j + 127) shr 8; 423 Inc(n); 424 end; 425 Inc(psrc); 426 end; 514 end; 515 result := bmp.NewBitmap(bmp.Width,bmp.Height); 516 517 tx := (bmp.Width+pixelSize-1) div pixelSize; 518 ty := (bmp.Height+pixelSize-1) div pixelSize; 519 if not useResample then 520 begin 521 temp := bmp.NewBitmap(tx,ty); 522 523 xs := (bmp.Width mod pixelSize) div 2; 524 ys := (bmp.Height mod pixelSize) div 2; 525 526 for yb := 0 to temp.height-1 do 527 begin 528 pdest := temp.ScanLine[yb]; 529 psrc := bmp.scanline[ys]+xs; 530 inc(ys,pixelSize); 531 for xb := 0 to temp.width-1 do 532 begin 533 pdest^ := psrc^; 534 inc(pdest); 535 inc(psrc,pixelSize); 427 536 end; 428 sumR := 0; 429 sumG := 0; 430 sumB := 0; 431 sumA := 0; 432 Adiv := 0; 433 RGBdiv := 0; 434 435 {$hints off} 436 for j := 0 to n - 1 do 437 begin 438 tempPixel := a_pixels[j]; 439 RGBweight := (weights[j] * tempPixel.alpha + 128) div 255; 440 sumR += tempPixel.red * RGBweight; 441 sumG += tempPixel.green * RGBweight; 442 sumB += tempPixel.blue * RGBweight; 443 RGBdiv += RGBweight; 444 sumA += tempPixel.alpha; 445 Adiv += 1; 446 end; 447 {$hints on} 448 449 if (Adiv = 0) or (RGBdiv = 0) then 450 refPixel := BGRAPixelTransparent 451 else 452 begin 453 refPixel.alpha := (sumA + Adiv shr 1) div Adiv; 454 if refPixel.alpha = 0 then 455 refPixel := BGRAPixelTransparent 456 else 457 begin 458 refPixel.red := (sumR + RGBdiv shr 1) div RGBdiv; 459 refPixel.green := (sumG + RGBdiv shr 1) div RGBdiv; 460 refPixel.blue := (sumB + RGBdiv shr 1) div RGBdiv; 461 end; 462 end; 463 464 pdest^ := refPixel; 465 Inc(pdest); 466 end; 467 end; 468 Result.InvalidateBitmap; 469 end; 470 471 function FilterEmboss(bmp: TBGRADefaultBitmap; angle: single): TBGRADefaultBitmap; 537 end; 538 temp.InvalidateBitmap; 539 end else 540 begin 541 oldfilter := bmp.ResampleFilter; 542 bmp.ResampleFilter := filter; 543 temp := bmp.Resample(tx,ty,rmFineResample); 544 bmp.ResampleFilter := oldfilter; 545 end; 546 stretched := temp.Resample(temp.Width*pixelSize,temp.Height*pixelSize,rmSimpleStretch); 547 temp.free; 548 if bmp.Width mod pixelSize = 0 then 549 xs := 0 550 else 551 xs := (-pixelSize+(bmp.Width mod pixelSize)) div 2; 552 if bmp.Height mod pixelSize = 0 then 553 ys := 0 554 else 555 ys := (-pixelSize+(bmp.Height mod pixelSize)) div 2; 556 result.PutImage(xs,ys,stretched,dmSet); 557 stretched.Free; 558 end; 559 560 function FilterBlur(bmp: TBGRACustomBitmap; 561 blurMask: TBGRACustomBitmap): TBGRACustomBitmap; 562 var 563 maskSum: int64; 564 i: Integer; 565 p: PBGRAPixel; 566 maskShift: integer; 567 begin 568 maskSum := 0; 569 p := blurMask.data; 570 for i := 0 to blurMask.NbPixels-1 do 571 begin 572 inc(maskSum,p^.red); 573 inc(p); 574 end; 575 maskShift := 0; 576 while maskSum > 32768 do 577 begin 578 inc(maskShift); 579 maskSum := maskSum shr 1; 580 end; 581 //check if sum can be stored in a 32-bit signed integer 582 if maskShift = 0 then 583 result := FilterBlurSmallMask(bmp,blurMask) else 584 if maskShift < 8 then 585 result := FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift) else 586 result := FilterBlurBigMask(bmp,blurMask); 587 end; 588 589 //32-bit blur with shift 590 function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap; 591 blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap; 592 593 var 594 sumR, sumG, sumB, sumA, Adiv, RGBdiv : integer; 595 596 function ComputeAverage: TBGRAPixel; inline; 597 begin 598 result.alpha := (sumA + Adiv shr 1) div Adiv; 599 if result.alpha = 0 then 600 result := BGRAPixelTransparent 601 else 602 begin 603 result.red := clampByte((sumR + RGBdiv shr 1) div RGBdiv); 604 result.green := clampByte((sumG + RGBdiv shr 1) div RGBdiv); 605 result.blue := clampByte((sumB + RGBdiv shr 1) div RGBdiv); 606 end; 607 end; 608 609 {$define PARAM_MASKSHIFT} 610 {$I blurnormal.inc} 611 612 //32-bit blur 613 function FilterBlurSmallMask(bmp: TBGRACustomBitmap; 614 blurMask: TBGRACustomBitmap): TBGRACustomBitmap; 615 616 var 617 sumR, sumG, sumB, sumA, Adiv : integer; 618 619 function ComputeAverage: TBGRAPixel; inline; 620 begin 621 result.alpha := (sumA + Adiv shr 1) div Adiv; 622 if result.alpha = 0 then 623 result := BGRAPixelTransparent 624 else 625 begin 626 result.red := clampByte((sumR + sumA shr 1) div sumA); 627 result.green := clampByte((sumG + sumA shr 1) div sumA); 628 result.blue := clampByte((sumB + sumA shr 1) div sumA); 629 end; 630 end; 631 632 {$I blurnormal.inc} 633 634 //floating point blur 635 function FilterBlurBigMask(bmp: TBGRACustomBitmap; 636 blurMask: TBGRACustomBitmap): TBGRACustomBitmap; 637 638 var 639 sumR, sumG, sumB, sumA, Adiv : single; 640 641 function ComputeAverage: TBGRAPixel; inline; 642 begin 643 result.alpha := round(sumA/Adiv); 644 if result.alpha = 0 then 645 result := BGRAPixelTransparent 646 else 647 begin 648 result.red := clampByte(round(sumR/sumA)); 649 result.green := clampByte(round(sumG/sumA)); 650 result.blue := clampByte(round(sumB/sumA)); 651 end; 652 end; 653 654 {$I blurnormal.inc} 655 656 { Emboss filter computes the difference between each pixel and the surrounding pixels 657 in the specified direction. } 658 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap; 472 659 var 473 660 yb, xb: integer; … … 475 662 idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: integer; 476 663 w: array[1..4] of single; 477 iw: integer;664 iw: cardinal; 478 665 c: array[0..4] of TBGRAPixel; 479 666 … … 485 672 bounds: TRect; 486 673 begin 674 //compute pixel position and weight 487 675 dx := cos(angle * Pi / 180); 488 676 dy := sin(angle * Pi / 180); … … 501 689 w[4] := (1 - abs(idx4 - dx)) * (1 - abs(idy4 - dy)); 502 690 691 //fill with gray 503 692 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 504 693 Result.Fill(BGRA(128, 128, 128, 255)); … … 512 701 bounds.Bottom := min(bmp.Height, bounds.Bottom + 1); 513 702 703 //loop through destination 514 704 for yb := bounds.Top to bounds.bottom - 1 do 515 705 begin … … 518 708 begin 519 709 c[0] := bmp.getPixel(xb, yb); 520 c[1] := bmp.getPixel( xb + idx1, yb + idy1);521 c[2] := bmp.getPixel( xb + idx2, yb + idy2);522 c[3] := bmp.getPixel( xb + idx3, yb + idy3);523 c[4] := bmp.getPixel( xb + idx4, yb + idy4);710 c[1] := bmp.getPixel(integer(xb + idx1), integer(yb + idy1)); 711 c[2] := bmp.getPixel(integer(xb + idx2), integer(yb + idy2)); 712 c[3] := bmp.getPixel(integer(xb + idx3), integer(yb + idy3)); 713 c[4] := bmp.getPixel(integer(xb + idx4), integer(yb + idy4)); 524 714 525 715 sumR := 0; … … 530 720 RGBdiv := 0; 531 721 722 //compute sum 532 723 {$hints off} 533 724 for i := 1 to 4 do … … 546 737 {$hints on} 547 738 739 //average 548 740 if (Adiv = 0) or (RGBdiv = 0) then 549 741 refPixel := c[0] … … 555 747 refPixel.alpha := (sumA * 255 + Adiv shr 1) div Adiv; 556 748 end; 749 750 //difference with center pixel 557 751 {$hints off} 558 752 tempPixel.red := max(0, min(512 * 255, 65536 + refPixel.red * … … 571 765 end; 572 766 573 function FilterEmbossHighlight(bmp: TBGRADefaultBitmap; 574 FillSelection: boolean): TBGRADefaultBitmap; 767 { Like general emboss, but with fixed direction and automatic color with transparency } 768 function FilterEmbossHighlight(bmp: TBGRACustomBitmap; 769 FillSelection: boolean): TBGRACustomBitmap; 575 770 var 576 771 yb, xb: integer; … … 710 905 end; 711 906 712 function FilterNormalize(bmp: TBGRADefaultBitmap; 713 eachChannel: boolean = True): TBGRADefaultBitmap; 907 { Normalize compute min-max of specified channel and apply an affine transformation 908 to make it use the full range of values } 909 function FilterNormalize(bmp: TBGRACustomBitmap; 910 eachChannel: boolean = True): TBGRACustomBitmap; 714 911 var 715 912 psrc, pdest: PBGRAPixel; … … 831 1028 end; 832 1029 833 function FilterRotate(bmp: TBGRADefaultBitmap; origin: TPointF; 834 angle: single): TBGRADefaultBitmap; 1030 { Rotates the image. To do this, loop through the destination and 1031 calculates the position in the source bitmap with an affine transformation } 1032 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF; 1033 angle: single): TBGRACustomBitmap; 835 1034 var 836 1035 bounds: TRect; … … 926 1125 end; 927 1126 928 function FilterGrayscale(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 1127 { Filter grayscale applies BGRAToGrayscale function to all pixels } 1128 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 929 1129 var 930 1130 bounds: TRect; … … 952 1152 end; 953 1153 954 function FilterContour(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 1154 { Filter contour compute a grayscale image, then for each pixel 1155 calculates the difference with surrounding pixels (in intensity and alpha) 1156 and draw black pixels when there is a difference } 1157 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 955 1158 var 956 1159 yb, xb: integer; … … 964 1167 965 1168 bounds: TRect; 966 gray: TBGRA DefaultBitmap;1169 gray: TBGRACustomBitmap; 967 1170 begin 968 1171 bmpWidth := bmp.Width; … … 1066 1269 end; 1067 1270 1068 function FilterSphere(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 1271 { Compute the distance for each pixel to the center of the bitmap, 1272 calculate the corresponding angle with arcsin, use this angle 1273 to determine a distance from the center in the source bitmap } 1274 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 1069 1275 var 1070 1276 cx, cy, x, y, len, fact: single; 1071 1277 xb, yb: integer; 1072 mask: TBGRA DefaultBitmap;1278 mask: TBGRACustomBitmap; 1073 1279 begin 1074 1280 Result := bmp.NewBitmap(bmp.Width, bmp.Height); … … 1099 1305 end; 1100 1306 1101 function FilterCylinder(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap; 1307 { Applies twirl scanner. See TBGRATwirlScanner } 1308 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; 1309 var twirl: TBGRATwirlScanner; 1310 begin 1311 twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent); 1312 Result := bmp.NewBitmap(bmp.Width, bmp.Height); 1313 result.Fill(twirl); 1314 twirl.free; 1315 end; 1316 1317 { Compute the distance for each pixel to the vertical axis of the bitmap, 1318 calculate the corresponding angle with arcsin, use this angle 1319 to determine a distance from the vertical axis in the source bitmap } 1320 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 1102 1321 var 1103 1322 cx, cy, x, y, len, fact: single; … … 1125 1344 end; 1126 1345 1127 function FilterPlane(bmp: TBGRA DefaultBitmap): TBGRADefaultBitmap;1346 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap; 1128 1347 const resampleGap=0.6; 1129 1348 var 1130 1349 cy, x1, x2, y1, y2, z1, z2, h: single; 1131 1350 yb: integer; 1132 resampledBmp: TBGRA DefaultBitmap;1351 resampledBmp: TBGRACustomBitmap; 1133 1352 resampledBmpWidth: integer; 1134 1353 resampledFactor,newResampleFactor: single; 1135 sub,resampledSub: TBGRA DefaultBitmap;1354 sub,resampledSub: TBGRACustomBitmap; 1136 1355 partRect: TRect; 1137 1356 resampleSizeY : integer; … … 1191 1410 end; 1192 1411 1193 function FilterMedian(bmp: TBGRADefaultBitmap; 1194 Option: TMedianOption): TBGRADefaultBitmap; 1412 { For each component, sort values to get the median } 1413 function FilterMedian(bmp: TBGRACustomBitmap; 1414 Option: TMedianOption): TBGRACustomBitmap; 1195 1415 1196 1416 function ComparePixLt(p1, p2: TBGRAPixel): boolean; … … 1236 1456 for dx := -1 to 1 do 1237 1457 begin 1238 a_pixels[n] := bmp.GetPixel( xb + dx, yb + dy);1458 a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy)); 1239 1459 if a_pixels[n].alpha = 0 then 1240 1460 a_pixels[n] := BGRAPixelTransparent;
Note:
See TracChangeset
for help on using the changeset viewer.