Mega Code Archive

 
Categories / Delphi / VCL
 

TEffects component

Title: TEffects component Question: To download a number of necessary components just go to http://www.pisarev.net. There you can find a sample for each effect. Answer: { *********************************************************************** } { } { Effects } { } { Copyright (c) 2003-2004 Pisarev Yuriy (mail@pisarev.net) } { } { *********************************************************************** } unit Effects; {$B-} {$WARN SYMBOL_PLATFORM OFF} interface uses Windows, SysUtils, Classes, Graphics, Math, Shape, GrayBitmap, MemUtils; type TChannel = (chBlue, chGreen, chRed, chAlpha); TChannels = set of TChannel; TPixel = array[TChannel] of Byte; PPixel = ^TPixel; TPixelRef = array[TChannel] of PByte; TSelection = array[TChannel] of Boolean; TLines = array of Pointer; TRangeType = (rtRect, rtEllipse, rtFrame, rtStar5, rtStar6, rtTriangle0, rtTriangle1, rtTriangle2, rtTriangle3, rtRhomb, rtCross); TPairData = record Lines: TLines; Data: Pointer; end; PPairData = ^TPairData; TRangeData = record Size: TSize; Data: Pointer; end; PRangeData = ^TRangeData; TScanEvent = procedure(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Data: Pointer; var Continue: Boolean) of object; TScanPairEvent = procedure(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; Data: Pointer; var Continue: Boolean) of object; TScanRangeEvent = procedure(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; RangeIndex: Integer; Rect: TRect; Data: Pointer; var Continue: Boolean) of object; TScanType = (stImport, stExport); TDistributionType = (dtEven, dtCustom); TFactor = 1..High(Integer); EStreamError = class(Exception); EBitmapError = class(Exception); TImportEvent = procedure(var Data: Byte; P: Pointer; Index, BitIndex: Integer) of object; TExportEvent = procedure(Data: Byte; P: Pointer; Index, BitIndex: Integer) of object; TCustomEffects = class(TComponent) private FMasked: Boolean; FTransparentRange: Boolean; FMaskBitmap: TBitmap; FRangeBitmap: TBitmap; FOnExport: TExportEvent; FOnImport: TImportEvent; FPositivePixel: TPixel; FNegativePixel: TPixel; FRangeType: TRangeType; FOnScan: TScanEvent; FOnScanPair: TScanPairEvent; FOnScanRange: TScanRangeEvent; FShapePoints: TShapePoints; protected procedure DoImport(var Data: Byte; P: Pointer; BitIndex: Integer); dynamic; procedure DoExport(Data: Byte; P: Pointer; BitIndex: Integer); dynamic; procedure DoScan(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Data: Pointer; var Continue: Boolean; out Modified: Boolean); dynamic; procedure ScanPairProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; PairData: Pointer; var Continue: Boolean); dynamic; procedure ScanRangeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RangeData: Pointer; var Continue: Boolean); dynamic; property RangeBitmap: TBitmap read FRangeBitmap write FRangeBitmap; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure ScanBitmap(Stream: TMemoryStream; Bitmap: TBitmap; ScanType: TScanType; DistributionType: TDistributionType; Factor: TFactor = 1; Start: Integer = 0); overload; virtual; function ScanBitmap(Bitmap: TBitmap; Data: Pointer = nil; Mask: TBitmap = nil): Boolean; overload; virtual; function ScanPair(SourceBitmap, TargetBitmap: TBitmap; Data: Pointer = nil; Mask: TBitmap = nil): Boolean; virtual; function ScanRange(Bitmap: TBitmap; ASize: TSize; AData: Pointer = nil; Range: TBitmap = nil; Mask: TBitmap = nil): Boolean; virtual; function Fill(Bitmap: TBitmap; Channel: TChannel; Value: Byte): Boolean; overload; virtual; abstract; function Fill(Bitmap: TBitmap; ASelection: TSelection; APixel: TPixel): Boolean; overload; virtual; abstract; function AdjustSize(SourceSize: TSize; var TargetSize: TSize): Boolean; virtual; function ComputeBitmap(SourceBitmap, TargetBitmap: TBitmap; TargetSize: TSize; var X, Y: Integer): Boolean; virtual; function ComputeSize(var SourceSize: TSize; TargetSize: TSize; var X, Y: Integer): Boolean; virtual; function CopyBitmap(SourceBitmap, TargetBitmap: TBitmap; TargetSize: TSize): Boolean; virtual; function GetBitmapCapacity(Bitmap: TBitmap): Integer; function ScaleBitmap(var Bitmap: TBitmap; Size: TSize): Boolean; virtual; procedure ComputeMask(Mask: TBitmap; SourceSize, TargetSize: TSize; X, Y: Integer); overload; virtual; procedure ComputeMask(TargetBitmap: TBitmap; TargetSize: TSize; Scale: Boolean = True); overload; virtual; procedure CorrectMask(Size: TSize; Scale: Boolean = True); virtual; property PositivePixel: TPixel read FPositivePixel write FPositivePixel; property NegativePixel: TPixel read FNegativePixel write FNegativePixel; published property Masked: Boolean read FMasked write FMasked default False; property MaskBitmap: TBitmap read FMaskBitmap write FMaskBitmap; property ShapePoints: TShapePoints read FShapePoints write FShapePoints; property RangeType: TRangeType read FRangeType write FRangeType default rtRect; property TransparentRange: Boolean read FTransparentRange write FTransparentRange default True; property OnImport: TImportEvent read FOnImport write FOnImport; property OnExport: TExportEvent read FOnExport write FOnExport; property OnScan: TScanEvent read FOnScan write FOnScan; property OnScanPair: TScanPairEvent read FOnScanPair write FOnScanPair; property OnScanRange: TScanRangeEvent read FOnScanRange write FOnScanRange; end; TPosition = record X, Y: Extended; end; TQuarterType = (qt1, qt2, qt3, qt4); TQuarterRange = record Min, Max: Extended; end; TIncrement = -High(Byte)..High(Byte); TIncrements = array[TChannel] of TIncrement; TChangeType = (ctEqual, ctNotEqual); TChangeTypes = array[TChannel] of TChangeType; TSum = array[TChannel] of Integer; TRadius = 1..5; TSharpenType = (stBrightness, stContrast); TRowData = array[TChannel] of TIntArray; TPixelateType = (ptGet, ptSet); TGrayBitmaps = array[TChannel] of TGrayBitmap; TRotateData = record Size: TSize; end; PRotateData = ^TRotateData; TMirrorData = TRotateData; PMirrorData = ^TMirrorData; TRotateAndMirrorData = TRotateData; PRotateAndMirrorData = ^TRotateAndMirrorData; TFillData = record Selection: TSelection; Pixel: TPixel; end; PFillData = ^TFillData; TInvertData = record Selection: TSelection; end; PInvertData = ^TInvertData; TRotateCustomData = record QuarterType: TQuarterType; Radians: Extended; Size: TSize; SourceCenter, TargetCenter: TPoint; end; PRotateCustomData = ^TRotateCustomData; TIncreaseData = record Selection, IgnoreBorders: TSelection; Increments: TIncrements; end; PIncreaseData = ^TIncreaseData; TContrastData = record Selection: TSelection; Increments: TIncrements; end; PContrastData = ^TContrastData; TSaturationData = record Selection: TSelection; Factor: Byte; end; PSaturationData = ^TSaturationData; TSolorizeData = record Selection: TSelection; Factor: Byte; end; PSolorizeData = ^TSolorizeData; TPosterizeData = record Selection: TSelection; Factor: Byte; end; PPosterizeData = ^TPosterizeData; TBlurData = record Selection: TSelection; Radius: TRadius; Size: TSize; end; PBlurData = ^TBlurData; TNoiseData = record Selection: TSelection; Factor: Byte; end; PNoiseData = ^TNoiseData; TChangeData = record Channel: TChannel; Min, Max: Byte; Pixel: TPixel; ChangeType: TChangeType; end; PChangeData = ^TChangeData; TChangeRangeData = record Min, Max, Pixel: TPixel; ChangeTypes: TChangeTypes; ChangeType: TChangeType; Alpha: Boolean; end; PChangeRangeData = ^TChangeRangeData; TSharpenData = record Selection: TSelection; Radius: TRadius; Difference: Byte; Percent: Extended; SharpenType: TSharpenType; Size: TSize; end; PSharpenData = ^TSharpenData; TPixelateData = record Selection: TSelection; PixelateType: TPixelateType; RowData: TRowData; Count: TIntArray; end; PPixelateData = ^TPixelateData; TRoughBlurData = record Selection: TSelection; Size: TSize; Radius: TRadius; Fast: Boolean; PixelateType: TPixelateType; RowData: TRowData; RangeCount: Integer; Count: TIntArray; end; PRoughBlurData = ^TRoughBlurData; TInsertData = record X, Y: Integer; Transparent: Boolean; end; PInsertData = ^TInsertData; TSeparateData = record GrayBitmaps: TGrayBitmaps; end; PSeparateData = ^TSeparateData; TAssembleData = record GrayBitmaps: TGrayBitmaps; end; PAssembleData = ^TAssembleData; TEffects = class(TCustomEffects) protected { Cryptography } procedure ImportProc(var Data: Byte; P: Pointer; Index, BitIndex: Integer); dynamic; procedure ExportProc(Data: Byte; P: Pointer; Index, BitIndex: Integer); dynamic; { Rotation } procedure RotateCustomProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateCustomData: Pointer; var Continue: Boolean); dynamic; procedure RotateLeftProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateData: Pointer; var Continue: Boolean); dynamic; procedure RotateProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateData: Pointer; var Continue: Boolean); dynamic; procedure RotateRightProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateData: Pointer; var Continue: Boolean); dynamic; procedure MirrorProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; MirrorData: Pointer; var Continue: Boolean); dynamic; procedure RotateAndMirrorProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateAndMirrorData: Pointer; var Continue: Boolean); dynamic; { Fill } procedure FillProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; FillData: Pointer; var Continue: Boolean); dynamic; { Increase } procedure IncreaseProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; IncreaseData: Pointer; var Continue: Boolean); dynamic; { Contrast } procedure ContrastProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; ContrastData: Pointer; var Continue: Boolean); dynamic; { Saturation } procedure SaturationProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; SaturationData: Pointer; var Continue: Boolean); dynamic; { Solorize } procedure SolorizeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; SolorizeData: Pointer; var Continue: Boolean); dynamic; { Posterize } procedure PosterizeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; PosterizeData: Pointer; var Continue: Boolean); dynamic; { Blur } procedure BlurProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; BlurData: Pointer; var Continue: Boolean); dynamic; { Noise } procedure NoiseProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; NoiseData: Pointer; var Continue: Boolean); dynamic; { Invert } procedure InvertProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; InvertData: Pointer; var Continue: Boolean); dynamic; { Change } procedure ChangeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; ChangeData: Pointer; var Continue: Boolean); dynamic; { ChangeRange } procedure ChangeRangeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; ChangeRangeData: Pointer; var Continue: Boolean); dynamic; { Sharpen } procedure SharpenProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; SharpenData: Pointer; var Continue: Boolean); dynamic; { Pixelate } procedure PixelateProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; RangeIndex: Integer; Rect: TRect; PixelateData: Pointer; var Continue: Boolean); dynamic; { RoughBlur } procedure RoughBlurProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; RangeIndex: Integer; Rect: TRect; RoughBlurData: Pointer; var Continue: Boolean); dynamic; { Insert } procedure InsertProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; InsertData: Pointer; var Continue: Boolean); dynamic; { Separate } procedure SeparateProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; SeparateData: Pointer; var Continue: Boolean); dynamic; { Assemble } procedure AssembleProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; AssembleData: Pointer; var Continue: Boolean); dynamic; { Miscellaneous } function Simplify(var Radians: Extended): TQuarterType; dynamic; procedure CorrectRadians(var Radians: Extended); dynamic; function ComputeRotatedSize(SourceSize: TSize; Radians: Extended; out TargetSize: TSize): Boolean; dynamic; function ComputePoint(TargetPoint, SourceCenter, TargetCenter: TPoint; QuarterType: TQuarterType; Radians: Extended): TPoint; dynamic; function CheckSelection(Selection: TSelection; SkipChannels: TChannels = []): Boolean; dynamic; function CheckValue(Value, Min, Max: Byte; ChangeType: TChangeType): Boolean; dynamic; function CheckPixel(PixelRef: TPixelRef; Min, Max: TPixel; ChangeTypes: TChangeTypes; ChangeType: TChangeType; Alpha: Boolean): Boolean; dynamic; function CreateRowData(var RowData: TRowData; Length: Integer; Selection: TSelection): Boolean; dynamic; procedure DeleteRowData(var RowData: TRowData); dynamic; procedure IncreaseContrast(var Value: Byte; Increment: TIncrement); dynamic; public { Cryptography } procedure Import(Stream: TMemoryStream; Bitmap: TBitmap; DistributionType: TDistributionType; Factor: TFactor = 1; Start: Integer = 0); virtual; procedure Export(Stream: TMemoryStream; Bitmap: TBitmap; DistributionType: TDistributionType; Size: Integer = -1; Factor: TFactor = 1; Start: Integer = 0); virtual; { Rotation } function RotateCustom(SourceBitmap, TargetBitmap: TBitmap; ARadians: Extended; Background: PPixel = nil): Boolean; virtual; function RotateLeft(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel = nil): Boolean; virtual; function Rotate(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel = nil): Boolean; virtual; function RotateRight(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel = nil): Boolean; virtual; function Mirror(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel = nil): Boolean; virtual; function RotateAndMirror(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel = nil): Boolean; virtual; { Fill } function Fill(Bitmap: TBitmap; Channel: TChannel; Value: Byte): Boolean; overload; override; function Fill(Bitmap: TBitmap; ASelection: TSelection; APixel: TPixel): Boolean; overload; override; { Invert } function Invert(Bitmap: TBitmap; Channel: TChannel): Boolean; overload; virtual; function Invert(Bitmap: TBitmap; ASelection: TSelection): Boolean; overload; virtual; { Increase } function Increase(Bitmap: TBitmap; Channel: TChannel; Increment: TIncrement = 1; IgnoreBorder: Boolean = False): Boolean; overload; virtual; function Increase(Bitmap: TBitmap; ASelection, AIgnoreBorders: TSelection; AIncrements: TIncrements): Boolean; overload; virtual; { Contrast } function Contrast(Bitmap: TBitmap; Channel: TChannel; Increment: TIncrement): Boolean; overload; virtual; function Contrast(Bitmap: TBitmap; ASelection: TSelection; AIncrements: TIncrements): Boolean; overload; virtual; { Saturation } function Saturation(Bitmap: TBitmap; Channel: TChannel; Factor: Byte): Boolean; overload; virtual; function Saturation(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; overload; virtual; { Solorize } function Solorize(Bitmap: TBitmap; Channel: TChannel; Factor: Byte): Boolean; overload; virtual; function Solorize(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; overload; virtual; { Posterize } function Posterize(Bitmap: TBitmap; Channel: TChannel; Factor: Byte): Boolean; overload; virtual; function Posterize(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; overload; virtual; { Blur } function Blur(SourceBitmap, TargetBitmap: TBitmap; Radius: TRadius; Channel: TChannel): Boolean; overload; virtual; function Blur(SourceBitmap, TargetBitmap: TBitmap; ASelection: TSelection; ARadius: TRadius): Boolean; overload; virtual; { Noise } function Noise(Bitmap: TBitmap; Factor: Byte; Channel: TChannel): Boolean; overload; virtual; function Noise(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; overload; virtual; { Change } function Change(Bitmap: TBitmap; AChannel: TChannel; AMin, AMax: Byte; APixel: TPixel; AChangeType: TChangeType): Boolean; virtual; { ChangeRange } function ChangeRange(Bitmap: TBitmap; AMin, AMax, APixel: TPixel; AChangeTypes: TChangeTypes; AChangeType: TChangeType; AAlpha: Boolean = False): Boolean; virtual; { Sharpen } function Sharpen(SourceBitmap, TargetBitmap: TBitmap; Radius: TRadius; Difference: Byte; Percent: Extended; SharpenType: TSharpenType; Channel: TChannel): Boolean; overload; virtual; function Sharpen(SourceBitmap, TargetBitmap: TBitmap; ASelection: TSelection; ARadius: TRadius; ADifference: Byte; APercent: Extended; ASharpenType: TSharpenType): Boolean; overload; virtual; { Pixelate } function Pixelate(Bitmap: TBitmap; Size: TSize; Channel: TChannel): Boolean; overload; virtual; function Pixelate(Bitmap: TBitmap; ASelection: TSelection; ASize: TSize): Boolean; overload; virtual; { RoughBlur } function RoughBlur(Bitmap: TBitmap; Size: TSize; Radius: TRadius; Fast: Boolean; Channel: TChannel): Boolean; overload; virtual; function RoughBlur(Bitmap: TBitmap; ASelection: TSelection; ASize: TSize; ARadius: TRadius; AFast: Boolean): Boolean; overload; virtual; { Insert } function Insert(SourceBitmap: TBitmap; TargetBitmap: TBitmap; AX, AY: Integer; ATransparent: Boolean = False): Boolean; virtual; { Separate } function Separate(SourceBitmap: TBitmap; TargetBitmap: TGrayBitmap; Channel: TChannel): Boolean; overload; virtual; function Separate(Bitmap: TBitmap; AGrayBitmaps: TGrayBitmaps): Boolean; overload; virtual; { Assemble } function Assemble(SourceBitmap: TGrayBitmap; TargetBitmap: TBitmap; Channel: TChannel): Boolean; overload; virtual; function Assemble(AGrayBitmaps: TGrayBitmaps; Bitmap: TBitmap): Boolean; overload; virtual; end; const PositiveSel: TSelection = (True, True, True, True); NegativeSel: TSelection = (False, False, False, False); MaxByte = High(Byte); MinByte = 0; Average = High(Byte) div 2; PositivePixel: TPixel = (MaxByte, MaxByte, MaxByte, MaxByte); NegativePixel: TPixel = (MinByte, MinByte, MinByte, MinByte); BitConst1 = $07; BitConst2 = $FE; BitConst3 = $01; BitConsts: array[0..7] of Byte = ($FE, $FD, $FB, $F7, $EF, $DF, $BF, $7F); ByteCounts: array[pf8Bit..pf32Bit] of Byte = (1, 2, 2, 3, 4); DefaultPixelFormat = pf32bit; DefaultRangePixelFormat = pf24bit; EmptyColor = clWhite; Quarter1Range: TQuarterRange = (Min: 0; Max: Pi / 2); Quarter2Range: TQuarterRange = (Min: Pi / 2; Max: Pi); Quarter3Range: TQuarterRange = (Min: Pi; Max: 3 * Pi / 2); Quarter4Range: TQuarterRange = (Min: 3 * Pi / 2; Max: 2 * Pi); Pi2 = 2 * Pi; _100Percent = 100; var DefaultByteCount: Integer; resourcestring sNoData = 'No data'; sOutOfSpace = 'Out of bitmap space'; function Pixel(R, G, B: Byte; A: Byte = 0): TPixel; function Selection(R, G, B: Boolean; A: Boolean = False): TSelection; function Increments(R, G, B: TIncrement; A: TIncrement = 0): TIncrements; function ChangeTypes(R, G, B: TChangeType; A: TChangeType = ctEqual): TChangeTypes; function Size(cx, cy: Integer): TSize; procedure Register; implementation uses Types; procedure Register; begin RegisterComponents('Samples', [TEffects]); end; function Pixel(R, G, B: Byte; A: Byte = 0): TPixel; begin Result[chBlue] := B; Result[chGreen] := G; Result[chRed] := R; Result[chAlpha] := A; end; function Selection(R, G, B: Boolean; A: Boolean = False): TSelection; begin Result[chBlue] := B; Result[chGreen] := G; Result[chRed] := R; Result[chAlpha] := A; end; function Increments(R, G, B: TIncrement; A: TIncrement = 0): TIncrements; begin Result[chBlue] := B; Result[chGreen] := G; Result[chRed] := R; Result[chAlpha] := A; end; function ChangeTypes(R, G, B: TChangeType; A: TChangeType = ctEqual): TChangeTypes; begin Result[chBlue] := B; Result[chGreen] := G; Result[chRed] := R; Result[chAlpha] := A; end; function Size(cx, cy: Integer): TSize; begin Result.cx := cx; Result.cy := cy; end; { TCustomEffects } function TCustomEffects.AdjustSize(SourceSize: TSize; var TargetSize: TSize): Boolean; begin Result := (TargetSize.cx 0) and (TargetSize.cy 0); if not Result then Exit; if SourceSize.cx / TargetSize.cx TargetSize.cx := SourceSize.cx * TargetSize.cy div SourceSize.cy else TargetSize.cy := SourceSize.cy * TargetSize.cx div SourceSize.cx; Result := (TargetSize.cx 0) and (TargetSize.cy 0); end; function TCustomEffects.ComputeBitmap(SourceBitmap, TargetBitmap: TBitmap; TargetSize: TSize; var X, Y: Integer): Boolean; var AX, AY: Integer; SourceSize: TSize; ARect: TRect; begin AX := X; AY := Y; SourceSize := Size(SourceBitmap.Width, SourceBitmap.Height); Result := ComputeSize(SourceSize, TargetSize, X, Y); if not Result then Exit; with TargetBitmap do begin PixelFormat := SourceBitmap.PixelFormat; Width := SourceSize.cx; Height := SourceSize.cy; end; with ARect do begin Left := IfThen(AX Right := Left + SourceSize.cx; Top := IfThen(AY Bottom := Top + SourceSize.cy; end; TargetBitmap.Canvas.CopyRect(Rect(0, 0, SourceSize.cx, SourceSize.cy), SourceBitmap.Canvas, ARect); end; procedure TCustomEffects.ComputeMask(TargetBitmap: TBitmap; TargetSize: TSize; Scale: Boolean); var SourceSize: TSize; Flags: array[0..1] of Boolean; SourceRect, TargetRect: TRect; AMasked: Boolean; begin SourceSize := Effects.Size(FMaskBitmap.Width, FMaskBitmap.Height); with TargetBitmap do begin Width := TargetSize.cx; Height := TargetSize.cy; end; Flags[0] := TargetSize.cx SourceSize.cx; Flags[1] := TargetSize.cy SourceSize.cy; if Flags[0] or Flags[1] then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, FNegativePixel); finally FMasked := AMasked; end; end; if Scale then begin with SourceRect do begin Left := 0; Right := SourceSize.cx; Top := 0; Bottom := SourceSize.cy; end; with TargetRect do begin Left := 0; Right := TargetSize.cx; Top := 0; Bottom := TargetSize.cy; end; end else begin with SourceRect, SourceSize do begin Left := EnsureRange((cx - TargetSize.cx) div 2, 0, cx); Right := EnsureRange(Left + TargetSize.cx, 0, cx); Top := EnsureRange((cy - TargetSize.cy) div 2, 0, cy); Bottom := EnsureRange(Top + TargetSize.cy, 0, cy); end; with TargetRect, TargetSize do begin Left := IfThen(Flags[0], (cx - SourceSize.cx) div 2, 0); Right := IfThen(Flags[0], Left + SourceSize.cx, cx); Top := IfThen(Flags[1], (cy - SourceSize.cy) div 2, 0); Bottom := IfThen(Flags[1], Top + SourceSize.cy, cy); end; end; TargetBitmap.Canvas.CopyRect(TargetRect, FMaskBitmap.Canvas, SourceRect); end; procedure TCustomEffects.ComputeMask(Mask: TBitmap; SourceSize, TargetSize: TSize; X, Y: Integer); begin ComputeSize(SourceSize, TargetSize, X, Y); with Mask do begin Width := SourceSize.cx; Height := SourceSize.cy; end; Mask.Canvas.CopyRect(Rect(0, 0, SourceSize.cx, SourceSize.cy), FMaskBitmap.Canvas, Rect(X, Y, X + SourceSize.cx, Y + SourceSize.cy)); end; function TCustomEffects.ComputeSize(var SourceSize: TSize; TargetSize: TSize; var X, Y: Integer): Boolean; begin if X begin SourceSize.cx := X + SourceSize.cx; X := 0; if SourceSize.cx end; if Y begin SourceSize.cy := Y + SourceSize.cy; Y := 0; if SourceSize.cy end; Result := (TargetSize.cx 0) and (TargetSize.cy 0); if not Result then Exit; if X + SourceSize.cx TargetSize.cx then begin SourceSize.cx := TargetSize.cx - X; if SourceSize.cx end; if Y + SourceSize.cy TargetSize.cy then begin SourceSize.cy := TargetSize.cy - Y; if SourceSize.cy end; Result := (TargetSize.cx 0) and (TargetSize.cy 0); end; function TCustomEffects.CopyBitmap(SourceBitmap, TargetBitmap: TBitmap; TargetSize: TSize): Boolean; var SourceSize: TSize; begin Result := not SourceBitmap.Empty; if not Result then Exit; SourceSize := Size(SourceBitmap.Width, SourceBitmap.Height); Result := AdjustSize(SourceSize, TargetSize); with TargetBitmap do begin PixelFormat := SourceBitmap.PixelFormat; Width := TargetSize.cx; Height := TargetSize.cy; Canvas.StretchDraw(Rect(0, 0, TargetSize.cx, TargetSize.cy), SourceBitmap); { Canvas.CopyRect(Rect(0, 0, TargetSize.cx, TargetSize.cy), SourceBitmap.Canvas, Rect(0, 0, SourceSize.cx, SourceSize.cy)); } end; end; procedure TCustomEffects.CorrectMask(Size: TSize; Scale: Boolean); var New: TBitmap; begin New := TBitmap.Create; try ComputeMask(New, Size, Scale); FMaskBitmap.Free; FMaskBitmap := New; except New.Free; raise; end; end; constructor TCustomEffects.Create(AOwner: TComponent); begin inherited; FMaskBitmap := TBitmap.Create; FShapePoints := TShapePoints.Create; FRangeType := rtRect; FTransparentRange := True; FRangeBitmap := TBitmap.Create; FPositivePixel := Effects.PositivePixel; FNegativePixel := Effects.NegativePixel; end; destructor TCustomEffects.Destroy; begin FMaskBitmap.Free; FShapePoints.Free; FRangeBitmap.Free; inherited; end; procedure TCustomEffects.DoExport(Data: Byte; P: Pointer; BitIndex: Integer); var Index: Integer; begin if not Assigned(FOnExport) then Exit; Index := BitIndex div 8; BitIndex := BitIndex and BitConst1; FOnExport(Data, P, Index, BitIndex); end; procedure TCustomEffects.DoImport(var Data: Byte; P: Pointer; BitIndex: Integer); var Index: Integer; begin if not Assigned(FOnImport) then Exit; Index := BitIndex div 8; BitIndex := BitIndex and BitConst1; FOnImport(Data, P, Index, BitIndex); end; procedure TCustomEffects.DoScan(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Data: Pointer; var Continue: Boolean; out Modified: Boolean); var AHorzIndex, AVertIndex: Integer; begin if Assigned(FOnScan) then begin AHorzIndex := HorzIndex; AVertIndex := VertIndex; FOnScan(PixelRef, HorzIndex, VertIndex, Data, Continue); Modified := (AHorzIndex HorzIndex) or (AVertIndex VertIndex); end else Continue := False; end; function TCustomEffects.GetBitmapCapacity(Bitmap: TBitmap): Integer; begin with Bitmap do Result := Width * Height * ByteCounts[PixelFormat] div 8; end; function TCustomEffects.ScaleBitmap(var Bitmap: TBitmap; Size: TSize): Boolean; var New: TBitmap; begin Result := not Bitmap.Empty and (Size.cx 0) and (Size.cy 0); if not Result or (Bitmap.Width = Size.cx) and (Bitmap.Height = Size.cy) then Exit; New := TBitmap.Create; try CopyBitmap(Bitmap, New, Size); Bitmap.Free; Bitmap := New; except New.Free; raise; end; end; function TCustomEffects.ScanBitmap(Bitmap: TBitmap; Data: Pointer; Mask: TBitmap): Boolean; var I, J, K: Integer; L: TChannel; AMasked, Continue, Positive, Modified: Boolean; P1, P2: Pointer; PixelRef: TPixelRef; begin Result := not Bitmap.Empty; if not Result then Exit; with Bitmap do begin PixelFormat := DefaultPixelFormat; K := Width * DefaultByteCount; end; AMasked := FMasked; if AMasked then begin if (Mask = nil) and not FMaskBitmap.Empty then Mask := FMaskBitmap; AMasked := Assigned(Mask); if AMasked and ((Mask.Width if Mask = FMaskBitmap then AMasked := False else ComputeMask(Mask, Size(Bitmap.Width, Bitmap.Height)); end; if AMasked then Mask.PixelFormat := DefaultPixelFormat; P2 := nil; I := 0; while I begin P1 := Bitmap.ScanLine[I]; if AMasked then P2 := Mask.ScanLine[I]; J := 0; while J begin for L := Low(TChannel) to High(TChannel) do PixelRef[L] := PByte(Integer(P1) + J + Ord(L)); Continue := True; if AMasked then begin for L := Low(TChannel) to High(TChannel) do begin Positive := FPositivePixel[L] = PByte(Integer(P2) + J + Ord(L))^; if not Positive then Break; end; if Positive then DoScan(PixelRef, J, I, Data, Continue, Modified) else Modified := False; end else DoScan(PixelRef, J, I, Data, Continue, Modified); if not Continue then Exit else if Modified then if I begin P1 := Bitmap.ScanLine[I]; if AMasked then P2 := Mask.ScanLine[I]; end else Exit else Inc(J, DefaultByteCount); end; if not Modified then Inc(I); end; end; procedure TCustomEffects.ScanBitmap(Stream: TMemoryStream; Bitmap: TBitmap; ScanType: TScanType; DistributionType: TDistributionType; Factor: TFactor; Start: Integer); var I, J, K, L, Size1, Size2: Integer; P1, P2: Pointer; begin if Stream.Size = 0 then raise EStreamError.Create(sNoData); if Bitmap.PixelFormat Size1 := Stream.Size * 8; I := IfThen(DistributionType = dtEven, Size1, Size1 * Factor); with Bitmap do begin Size2 := Width * ByteCounts[PixelFormat]; J := Size2 * Height - Start; end; if I J then raise EBitmapError.Create(sOutOfSpace); if DistributionType = dtEven then Factor := J div I; L := Start div Size2; P1 := Stream.Memory; J := 0; K := 0; for I := L to Bitmap.Height - 1 do begin P2 := Bitmap.ScanLine[I]; if J = Size2 then Dec(J, Size2); while J begin // I * Size2 - if I * Size2 + J = Start then begin // Size1 - if K = Size1 then Exit; if ScanType = stImport then DoImport(PByte(Integer(P2) + J)^, P1, K) else DoExport(PByte(Integer(P2) + J)^, P1, K); Inc(K); end; Inc(J, Factor); end; end; end; function TCustomEffects.ScanPair(SourceBitmap, TargetBitmap: TBitmap; Data: Pointer; Mask: TBitmap): Boolean; var I: Integer; PairData: TPairData; ScanEvent: TScanEvent; begin Result := not SourceBitmap.Empty and not TargetBitmap.Empty; if not Result then Exit; TargetBitmap.PixelFormat := DefaultPixelFormat; SetLength(PairData.Lines, TargetBitmap.Height); try for I := 0 to TargetBitmap.Height - 1 do PairData.Lines[I] := TargetBitmap.ScanLine[I]; PairData.Data := Data; ScanEvent := FOnScan; FOnScan := ScanPairProc; try Result := ScanBitmap(SourceBitmap, @PairData, Mask); finally FOnScan := ScanEvent; end; finally PairData.Lines := nil; end; end; procedure TCustomEffects.ScanPairProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; PairData: Pointer; var Continue: Boolean); begin if Assigned(FOnScanPair) then FOnScanPair(PixelRef, HorzIndex, VertIndex, PPairData(PairData).Lines, PPairData(PairData).Data, Continue); end; function TCustomEffects.ScanRange(Bitmap: TBitmap; ASize: TSize; AData: Pointer; Range, Mask: TBitmap): Boolean; var I, J, K, L, M, N: Integer; R, G, B: Byte; RangeData: TRangeData; ScanPairEvent: TScanPairEvent; begin Result := not Bitmap.Empty; if not Result then Exit; if Assigned(Range) and ((Range.Width Bitmap.Width) or (Range.Height Bitmap.Height)) then Range := nil; if not Assigned(Range) then begin Range := FRangeBitmap; with Range do begin PixelFormat := DefaultRangePixelFormat; Width := Bitmap.Width; Height := Bitmap.Height; with Canvas do begin if FTransparentRange then Brush.Color := EmptyColor else Brush.Color := 0; FillRect(Rect(0, 0, Width, Height)); end; end; I := 0; if FTransparentRange then K := 0 else K := 1; while I begin J := 0; while J begin B := GetBValue(K); G := GetGValue(K); R := GetRValue(K); with Range.Canvas do begin Brush.Color := RGB(R, G, B); Pen.Color := RGB(R, G, B); case FRangeType of rtRect: FillRect(Rect(J, I, J + ASize.cx, I + ASize.cy)); rtEllipse: Ellipse(Rect(J, I, J + ASize.cx, I + ASize.cy)); rtFrame: FrameRect(Rect(J, I, J + ASize.cx, I + ASize.cy)); rtStar5: begin L := ASize.cx div 2; M := ASize.cy div 4; N := ASize.cx div 8; Polygon([Point(J + L, I), Point(J + ASize.cx - N, I + ASize.cy), Point(J + L, I + ASize.cy - M), Point(J + N, I + ASize.cy)]); N := ASize.cy div 3; Polygon([Point(J, I + N), Point(J + ASize.cx, I + N), Point(J + L, I + ASize.cy - M)]); end; rtStar6: begin L := ASize.cx div 2; M := ASize.cy div 4; Polygon([Point(J + L, I), Point(J + ASize.cx, I + ASize.cy - M), Point(J, I + ASize.cy - M)]); Polygon([Point(J, I + M), Point(J + ASize.cx, I + M), Point(J + L, I + ASize.cy)]); end; else with FShapePoints do begin Range := Shape.Range(J, I, ASize.cx, ASize.cy); ShapeType := TShapeType(Ord(FRangeType) - Ord(rtStar6) - 1); Polygon(Points); end; end; end; Inc(K); Inc(J, ASize.cx); end; Inc(I, ASize.cy); end; end; with RangeData do begin Size := ASize; Data := AData; end; ScanPairEvent := FOnScanPair; FOnScanPair := ScanRangeProc; try Result := ScanPair(Bitmap, Range, @RangeData, Mask); finally FOnScanPair := ScanPairEvent; end; end; procedure TCustomEffects.ScanRangeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RangeData: Pointer; var Continue: Boolean); var I, J, K: Integer; Rect: TRect; begin if not Assigned(FOnScanRange) then Exit; I := HorzIndex div DefaultByteCount; J := Integer(Lines[VertIndex]) + HorzIndex; // K - K := RGB(PByte(J + 2)^, PByte(J + 1)^, PByte(J)^); if K = EmptyColor then Exit; with PRangeData(RangeData)^ do begin Rect.Left := I - I mod Size.cx; Rect.Right := Rect.Left + Size.cx; Rect.Top := VertIndex - VertIndex mod Size.cy; Rect.Bottom := Rect.Top + Size.cy; FOnScanRange(PixelRef, HorzIndex, VertIndex, K, Rect, Data, Continue); end; end; { TEffects } function TEffects.Assemble(AGrayBitmaps: TGrayBitmaps; Bitmap: TBitmap): Boolean; var I: TChannel; Size: TSize; AssembleData: TAssembleData; ScanEvent: TScanEvent; begin Size.cx := MaxInt; Size.cy := MaxInt; for I := Low(TChannel) to High(TChannel) do if Assigned(AGrayBitmaps[I]) then begin if Size.cx AGrayBitmaps[I].Width then Size.cx := AGrayBitmaps[I].Width; if Size.cy AGrayBitmaps[I].Height then Size.cy := AGrayBitmaps[I].Height; AGrayBitmaps[I].UpdatePalette; end; with Bitmap do begin Width := Size.cx; Height := Size.cy; end; AssembleData.GrayBitmaps := AGrayBitmaps; ScanEvent := FOnScan; FOnScan := AssembleProc; try Result := ScanBitmap(Bitmap, @AssembleData); finally FOnScan := ScanEvent; end; end; function TEffects.Assemble(SourceBitmap: TGrayBitmap; TargetBitmap: TBitmap; Channel: TChannel): Boolean; var GrayBitmaps: TGrayBitmaps; begin FillChar(GrayBitmaps, SizeOf(TGrayBitmaps), 0); GrayBitmaps[Channel] := SourceBitmap; Result := Assemble(GrayBitmaps, TargetBitmap); end; procedure TEffects.AssembleProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; AssembleData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; P: PByte; begin J := HorzIndex div DefaultByteCount; for I := Low(TChannel) to High(TChannel) do with PSeparateData(AssembleData)^ do if Assigned(GrayBitmaps[I]) then begin P := Pointer(Integer(GrayBitmaps[I].ScanLine[VertIndex]) + J); PixelRef[I]^ := P^; end; end; function TEffects.Blur(SourceBitmap, TargetBitmap: TBitmap; Radius: TRadius; Channel: TChannel): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Blur(SourceBitmap, TargetBitmap, Selection, Radius); end; function TEffects.Blur(SourceBitmap, TargetBitmap: TBitmap; ASelection: TSelection; ARadius: TRadius): Boolean; var BlurData: TBlurData; ScanPairEvent: TScanPairEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; TargetBitmap.Assign(SourceBitmap); with BlurData do begin Selection := ASelection; Radius := ARadius; Size.cx := SourceBitmap.Width - 1; Size.cy := SourceBitmap.Height - 1; end; ScanPairEvent := FOnScanPair; FOnScanPair := BlurProc; try Result := ScanPair(TargetBitmap, SourceBitmap, @BlurData); finally FOnScanPair := ScanPairEvent; end; end; procedure TEffects.BlurProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; BlurData: Pointer; var Continue: Boolean); var I: TChannel; J, K, L, M: Integer; Rect: TRect; Sum: TSum; begin J := HorzIndex div DefaultByteCount; with PBlurData(BlurData)^ do begin with Rect do begin Left := IfThen(J Radius, J - Radius, 0); Right := IfThen(J + Radius Top := IfThen(VertIndex Radius, VertIndex - Radius, 0); Bottom := IfThen(VertIndex + Radius end; FillChar(Sum, SizeOf(Sum), 0); M := 0; for J := Rect.Top to Rect.Bottom do for K := Rect.Left to Rect.Right do begin L := Integer(Lines[J]) + K * DefaultByteCount; for I := Low(TChannel) to High(TChannel) do if Selection[I] then Inc(Sum[I], PByte(L + Ord(I))^); Inc(M); end; for I := Low(TChannel) to High(TChannel) do if Selection[I] then PixelRef[I]^ := Sum[I] div M; end; end; function TEffects.Change(Bitmap: TBitmap; AChannel: TChannel; AMin, AMax: Byte; APixel: TPixel; AChangeType: TChangeType): Boolean; var ChangeData: TChangeData; ScanEvent: TScanEvent; begin with ChangeData do begin Channel := AChannel; Min := AMin; Max := AMax; Pixel := APixel; ChangeType := AChangeType; end; ScanEvent := FOnScan; FOnScan := ChangeProc; try Result := ScanBitmap(Bitmap, @ChangeData); finally FOnScan := ScanEvent; end; end; procedure TEffects.ChangeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; ChangeData: Pointer; var Continue: Boolean); var I: TChannel; begin with PChangeData(ChangeData)^ do if CheckValue(PixelRef[Channel]^, Min, Max, ChangeType) then for I := Low(TChannel) to High(TChannel) do PixelRef[I]^ := Pixel[I]; end; function TEffects.ChangeRange(Bitmap: TBitmap; AMin, AMax, APixel: TPixel; AChangeTypes: TChangeTypes; AChangeType: TChangeType; AAlpha: Boolean): Boolean; var ChangeRangeData: TChangeRangeData; ScanEvent: TScanEvent; begin with ChangeRangeData do begin Min := AMin; Max := AMax; Pixel := APixel; ChangeTypes := AChangeTypes; ChangeType := AChangeType; Alpha := AAlpha; end; ScanEvent := FOnScan; FOnScan := ChangeRangeProc; try Result := ScanBitmap(Bitmap, @ChangeRangeData); finally FOnScan := ScanEvent; end; end; procedure TEffects.ChangeRangeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; ChangeRangeData: Pointer; var Continue: Boolean); var I: TChannel; begin with PChangeRangeData(ChangeRangeData)^ do if CheckPixel(PixelRef, Min, Max, ChangeTypes, ChangeType, Alpha) then for I := Low(TChannel) to High(TChannel) do PixelRef[I]^ := Pixel[I]; end; function TEffects.CheckPixel(PixelRef: TPixelRef; Min, Max: TPixel; ChangeTypes: TChangeTypes; ChangeType: TChangeType; Alpha: Boolean): Boolean; var I: TChannel; Selection: TSelection; begin for I := Low(TChannel) to High(TChannel) do Selection[I] := (PixelRef[I]^ = Min[I]) and (PixelRef[I]^ (ChangeTypes[I] = ctNotEqual); if Alpha then Result := CheckSelection(Selection) xor (ChangeType = ctNotEqual) else Result := CheckSelection(Selection, [chAlpha]) xor (ChangeType = ctNotEqual); end; function TEffects.CheckSelection(Selection: TSelection; SkipChannels: TChannels): Boolean; var I: TChannel; begin for I := Low(TChannel) to High(TChannel) do if not (I in SkipChannels) and Selection[I] then begin Result := True; Exit; end; Result := False; end; function TEffects.CheckValue(Value, Min, Max: Byte; ChangeType: TChangeType): Boolean; begin Result := (Value = Min) and (Value end; function TEffects.ComputePoint(TargetPoint, SourceCenter, TargetCenter: TPoint; QuarterType: TQuarterType; Radians: Extended): TPoint; var SourcePoint: TPosition; begin { TargetPoint.X := SourcePoint.X * Cos(Radians) - SourcePoint.Y * Sin(Radians); TargetPoint.Y := SourcePoint.X * Sin(Radians) + SourcePoint.Y * Cos(Radians); } with TargetPoint do if QuarterType = qt1 then begin X := X - TargetCenter.X; Y := TargetCenter.Y - Y; SourcePoint.X := Y * Csc(Radians) + (X - Y * Cotan(Radians)) * Cos(Radians); SourcePoint.Y := -(X - Y * Cotan(Radians)) * Sin(Radians); end else if QuarterType = qt2 then begin X := TargetCenter.X - X; Y := TargetCenter.Y - Y; SourcePoint.X := Y * Csc(Radians) + (X - Y * Cotan(Radians)) * Cos(Radians); SourcePoint.Y := (X - Y * Cotan(Radians)) * Sin(Radians); end else if QuarterType = qt3 then begin X := X - TargetCenter.X; Y := TargetCenter.Y - Y; SourcePoint.X := - Y * Csc(Radians) - (X - Y * Cotan(Radians)) * Cos(Radians); SourcePoint.Y := (X - Y * Cotan(Radians)) * Sin(Radians); end else if QuarterType = qt4 then begin X := TargetCenter.X - X; Y := TargetCenter.Y - Y; SourcePoint.X := - Y * Csc(Radians) - (X - Y * Cotan(Radians)) * Cos(Radians); SourcePoint.Y := -(X - Y * Cotan(Radians)) * Sin(Radians); end; Result.X := Round(SourcePoint.X + SourceCenter.X); Result.Y := Round(SourceCenter.Y - SourcePoint.Y); end; function TEffects.ComputeRotatedSize(SourceSize: TSize; Radians: Extended; out TargetSize: TSize): Boolean; var RoundMode: TFPURoundingMode; Center: TPosition; begin Result := (SourceSize.cx 0) and (SourceSize.cy 0); if not Result then Exit; RoundMode := GetRoundMode; SetRoundMode(rmNearest); try with Center do begin X := SourceSize.cx / 2; Y := SourceSize.cy / 2; end; TargetSize.cx := Round((Center.X * Cos(Radians) + Center.Y * Sin(Radians)) * 2); TargetSize.cy := Round((Center.X * Sin(Radians) + Center.Y * Cos(Radians)) * 2); finally SetRoundMode(RoundMode); end; end; function TEffects.Contrast(Bitmap: TBitmap; Channel: TChannel; Increment: TIncrement): Boolean; var Selection: TSelection; Increments: TIncrements; begin Selection := NegativeSel; Selection[Channel] := True; Increments[Channel] := Increment; Result := Contrast(Bitmap, Selection, Increments); end; function TEffects.Contrast(Bitmap: TBitmap; ASelection: TSelection; AIncrements: TIncrements): Boolean; var ContrastData: TContrastData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with ContrastData do begin Selection := ASelection; Increments := AIncrements; end; ScanEvent := FOnScan; FOnScan := ContrastProc; try Result := ScanBitmap(Bitmap, @ContrastData); finally FOnScan := ScanEvent; end; end; procedure TEffects.ContrastProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; ContrastData: Pointer; var Continue: Boolean); var I: TChannel; begin with PContrastData(ContrastData)^ do for I := Low(TChannel) to High(TChannel) do if Selection[I] then IncreaseContrast(PixelRef[I]^, Increments[I]); end; procedure TEffects.CorrectRadians(var Radians: Extended); begin if Radians else if Radians = Pi2 then while Radians = Pi2 do Radians := Radians - Pi2; end; function TEffects.CreateRowData(var RowData: TRowData; Length: Integer; Selection: TSelection): Boolean; var I: TChannel; begin Result := Length 0; if not Result then Exit; for I := Low(TChannel) to High(TChannel) do if Selection[I] then begin SetLength(RowData[I], Length); FillChar(RowData[I][0], Length * SizeOf(Integer), 0); end; end; procedure TEffects.DeleteRowData(var RowData: TRowData); var I: TChannel; begin for I := Low(TChannel) to High(TChannel) do RowData[I] := nil; end; procedure TEffects.Export(Stream: TMemoryStream; Bitmap: TBitmap; DistributionType: TDistributionType; Size: Integer; Factor: TFactor; Start: Integer); var ExportEvent: TExportEvent; begin ExportEvent := FOnExport; FOnExport := ExportProc; try Stream.SetSize(IfThen(Size ScanBitmap(Stream, Bitmap, stExport, DistributionType, Factor, Start); finally FOnExport := ExportEvent; end; end; procedure TEffects.ExportProc(Data: Byte; P: Pointer; Index, BitIndex: Integer); var I: Byte; J: PByte; begin I := Data and BitConst3; J := PByte(Integer(P) + Index); J^ := (BitConsts[BitIndex] and J^) or (I shl BitIndex); end; function TEffects.Fill(Bitmap: TBitmap; Channel: TChannel; Value: Byte): Boolean; var Selection: TSelection; Pixel: TPixel; begin Selection := NegativeSel; Selection[Channel] := True; Pixel[Channel] := Value; Result := Fill(Bitmap, Selection, Pixel); end; function TEffects.Fill(Bitmap: TBitmap; ASelection: TSelection; APixel: TPixel): Boolean; var FillData: TFillData; ScanEvent: TScanEvent; begin with FillData do begin Selection := ASelection; Pixel := APixel; end; ScanEvent := FOnScan; FOnScan := FillProc; try Result := ScanBitmap(Bitmap, @FillData); finally FOnScan := ScanEvent; end; end; procedure TEffects.FillProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; FillData: Pointer; var Continue: Boolean); var I: TChannel; begin for I := Low(TChannel) to High(TChannel) do if PFillData(FillData)^.Selection[I] then PixelRef[I]^ := PFillData(FillData)^.Pixel[I]; end; procedure TEffects.Import(Stream: TMemoryStream; Bitmap: TBitmap; DistributionType: TDistributionType; Factor: TFactor; Start: Integer); var ImportEvent: TImportEvent; begin ImportEvent := FOnImport; FOnImport := ImportProc; try ScanBitmap(Stream, Bitmap, stImport, DistributionType, Factor, Start); finally FOnImport := ImportEvent; end; end; procedure TEffects.ImportProc(var Data: Byte; P: Pointer; Index, BitIndex: Integer); var I: Byte; begin I := (PByte(Integer(P) + Index)^ shr BitIndex) and BitConst3; Data := (Data and BitConst2) or I; end; function TEffects.Increase(Bitmap: TBitmap; Channel: TChannel; Increment: TIncrement; IgnoreBorder: Boolean): Boolean; var Selection, IgnoreBorders: TSelection; Increments: TIncrements; begin Selection := NegativeSel; Selection[Channel] := True; Increments[Channel] := Increment; IgnoreBorders[Channel] := IgnoreBorder; Result := Increase(Bitmap, Selection, IgnoreBorders, Increments); end; function TEffects.Increase(Bitmap: TBitmap; ASelection, AIgnoreBorders: TSelection; AIncrements: TIncrements): Boolean; var IncreaseData: TIncreaseData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with IncreaseData do begin Selection := ASelection; IgnoreBorders := AIgnoreBorders; Increments := AIncrements; end; ScanEvent := FOnScan; FOnScan := IncreaseProc; try Result := ScanBitmap(Bitmap, @IncreaseData); finally FOnScan := ScanEvent; end; end; procedure TEffects.IncreaseContrast(var Value: Byte; Increment: TIncrement); var I: Integer; begin I := (Average - Value) * Increment div MaxByte; Value := EnsureRange(Value - I, MinByte, MaxByte); end; procedure TEffects.IncreaseProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; IncreaseData: Pointer; var Continue: Boolean); var I: TChannel; begin with PIncreaseData(IncreaseData)^ do for I := Low(TChannel) to High(TChannel) do if Selection[I] then if IgnoreBorders[I] then PixelRef[I]^ := PixelRef[I]^ + Increments[I] else PixelRef[I]^ := EnsureRange(PixelRef[I]^ + Increments[I], MinByte, MaxByte); end; function TEffects.Insert(SourceBitmap, TargetBitmap: TBitmap; AX, AY: Integer; ATransparent: Boolean): Boolean; var InsertData: TInsertData; Bitmap, Mask: TBitmap; ScanPairEvent: TScanPairEvent; begin Bitmap := TBitmap.Create; try Result := ComputeBitmap(SourceBitmap, Bitmap, Size(TargetBitmap.Width, TargetBitmap.Height), AX, AY); if not Result then Exit; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) then ComputeMask(Mask, Size(Bitmap.Width, Bitmap.Height), Size(TargetBitmap.Width, TargetBitmap.Height), AX, AY); with InsertData do begin X := AX * DefaultByteCount; Y := AY; Transparent := ATransparent; end; ScanPairEvent := FOnScanPair; FOnScanPair := InsertProc; try Result := ScanPair(Bitmap, TargetBitmap, @InsertData, Mask); finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; finally Bitmap.Free; end; end; procedure TEffects.InsertProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; InsertData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; P: PByte; begin with PInsertData(InsertData)^ do begin J := Integer(Lines[Y + VertIndex]) + X + HorzIndex; if Transparent then for I := Low(TChannel) to High(TChannel) do begin { A = A * F / MaxByte + B * (F / MaxByte - 1) A = (A * F + B * (MaxByte - F)) / MaxByte } P := PByte(J + Ord(I)); if I = chAlpha then P^ := PixelRef[I]^ else P^ := (P^ * PixelRef[chAlpha]^ + PixelRef[I]^ * (MaxByte - PixelRef[chAlpha]^)) div MaxByte; end else for I := Low(TChannel) to High(TChannel) do PByte(J + Ord(I))^ := PixelRef[I]^; end; end; function TEffects.Invert(Bitmap: TBitmap; Channel: TChannel): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Invert(Bitmap, Selection); end; function TEffects.Invert(Bitmap: TBitmap; ASelection: TSelection): Boolean; var InvertData: TInvertData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; InvertData.Selection := ASelection; ScanEvent := FOnScan; FOnScan := InvertProc; try Result := ScanBitmap(Bitmap, @InvertData); finally FOnScan := ScanEvent; end; end; procedure TEffects.InvertProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; InvertData: Pointer; var Continue: Boolean); var I: TChannel; begin with PInvertData(InvertData)^ do for I := Low(TChannel) to High(TChannel) do if Selection[I] then PixelRef[I]^ := MaxByte - PixelRef[I]^; end; function TEffects.Mirror(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel): Boolean; var MirrorData: TMirrorData; Mask: TBitmap; AMasked: Boolean; ScanPairEvent: TScanPairEvent; begin with TargetBitmap do begin Width := SourceBitmap.Width; Height := SourceBitmap.Height; end; MirrorData.Size.cx := TargetBitmap.Width * DefaultByteCount - DefaultByteCount; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) and Assigned(Background) then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; end; ScanPairEvent := FOnScanPair; FOnScanPair := MirrorProc; try Result := ScanPair(SourceBitmap, TargetBitmap, @MirrorData, Mask); finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; end; procedure TEffects.MirrorProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; MirrorData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; begin J := Integer(Lines[VertIndex]) + PMirrorData(MirrorData).Size.cx - HorzIndex; for I := Low(TChannel) to High(TChannel) do PByte(J + Ord(I))^ := PixelRef[I]^; end; function TEffects.Noise(Bitmap: TBitmap; Factor: Byte; Channel: TChannel): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Noise(Bitmap, Selection, Factor); end; function TEffects.Noise(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; var NoiseData: TNoiseData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with NoiseData do begin Selection := ASelection; Factor := AFactor; end; ScanEvent := FOnScan; FOnScan := NoiseProc; try Result := ScanBitmap(Bitmap, @NoiseData); finally FOnScan := ScanEvent; end; end; procedure TEffects.NoiseProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; NoiseData: Pointer; var Continue: Boolean); var I: TChannel; begin with PNoiseData(NoiseData)^ do for I := Low(TChannel) to High(TChannel) do if Selection[I] then PixelRef[I]^ := EnsureRange(PixelRef[I]^ + Random(Factor) - Random(Factor), MinByte, MaxByte); end; function TEffects.Pixelate(Bitmap: TBitmap; Size: TSize; Channel: TChannel): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Pixelate(Bitmap, Selection, Size); end; function TEffects.Pixelate(Bitmap: TBitmap; ASelection: TSelection; ASize: TSize): Boolean; var I: Integer; PixelateData: TPixelateData; ScanRangeEvent: TScanRangeEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; I := (Bitmap.Width div ASize.cx + Integer(Bitmap.Width mod ASize.cx 0)) * (Bitmap.Height div ASize.cy + Integer(Bitmap.Height mod ASize.cy 0)) + Ord(not FTransparentRange); Result := I Ord(not FTransparentRange); if not Result then Exit; try with PixelateData do begin Selection := ASelection; PixelateType := ptGet; CreateRowData(RowData, I, Selection); SetLength(Count, I); FillChar(Count[0], I * SizeOf(Integer), 0); end; ScanRangeEvent := FOnScanRange; FOnScanRange := PixelateProc; try Result := ScanRange(Bitmap, ASize, @PixelateData); if not Result then Exit; PixelateData.PixelateType := ptSet; Result := ScanRange(Bitmap, ASize, @PixelateData, FRangeBitmap); finally FOnScanRange := ScanRangeEvent; end; finally with PixelateData do begin DeleteRowData(RowData); Count := nil; end; end; end; procedure TEffects.PixelateProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; RangeIndex: Integer; Rect: TRect; PixelateData: Pointer; var Continue: Boolean); var I: TChannel; begin with PPixelateData(PixelateData)^ do if PixelateType = ptGet then begin for I := Low(TChannel) to High(TChannel) do if Selection[I] and (RangeIndex Inc(RowData[I][RangeIndex], PixelRef[I]^); Inc(Count[RangeIndex]); end else for I := Low(TChannel) to High(TChannel) do if Selection[I] and (RangeIndex PixelRef[I]^ := RowData[I][RangeIndex] div Count[RangeIndex]; end; function TEffects.Posterize(Bitmap: TBitmap; Channel: TChannel; Factor: Byte): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Posterize(Bitmap, Selection, Factor); end; function TEffects.Posterize(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; var PosterizeData: TPosterizeData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with PosterizeData do begin Selection := ASelection; Factor := IfThen(AFactor = 0, 1, AFactor); end; ScanEvent := FOnScan; FOnScan := PosterizeProc; try Result := ScanBitmap(Bitmap, @PosterizeData); finally FOnScan := ScanEvent; end; end; procedure TEffects.PosterizeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; PosterizeData: Pointer; var Continue: Boolean); var I: TChannel; begin with PPosterizeData(PosterizeData)^ do for I := Low(TChannel) to High(TChannel) do PixelRef[I]^ := EnsureRange(Round(PixelRef[I]^ / Factor) * Factor, MinByte, MaxByte); end; function TEffects.Rotate(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel): Boolean; var RotateData: TRotateData; Mask: TBitmap; AMasked: Boolean; ScanPairEvent: TScanPairEvent; begin with TargetBitmap do begin Width := SourceBitmap.Width; Height := SourceBitmap.Height; end; RotateData.Size.cy := TargetBitmap.Height - 1; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) and Assigned(Background) then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; end; ScanPairEvent := FOnScanPair; FOnScanPair := RotateProc; try Result := ScanPair(SourceBitmap, TargetBitmap, @RotateData, Mask); finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; end; function TEffects.RotateAndMirror(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel): Boolean; var RotateAndMirrorData: TRotateAndMirrorData; Mask: TBitmap; AMasked: Boolean; ScanPairEvent: TScanPairEvent; begin with TargetBitmap do begin Width := SourceBitmap.Width; Height := SourceBitmap.Height; end; with RotateAndMirrorData do begin Size.cx := TargetBitmap.Width - 1; Size.cy := TargetBitmap.Height - 1; end; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) and Assigned(Background) then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; end; ScanPairEvent := FOnScanPair; FOnScanPair := RotateAndMirrorProc; try Result := ScanPair(SourceBitmap, TargetBitmap, @RotateAndMirrorData, Mask); finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; end; procedure TEffects.RotateAndMirrorProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateAndMirrorData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; begin with PMirrorData(RotateAndMirrorData)^ do J := Integer(Lines[Size.cy - VertIndex]) + Size.cx * DefaultByteCount - HorzIndex; for I := Low(TChannel) to High(TChannel) do PByte(J + Ord(I))^ := PixelRef[I]^; end; function TEffects.RotateCustom(SourceBitmap, TargetBitmap: TBitmap; ARadians: Extended; Background: PPixel): Boolean; var E: Extended; AMasked: Boolean; RotateCustomData: TRotateCustomData; ASize: TSize; Mask: TBitmap; ScanPairEvent: TScanPairEvent; RoundMode: TFPURoundingMode; begin E := ARadians; CorrectRadians(ARadians); if IsZero(ARadians) then begin with TargetBitmap do begin Width := SourceBitmap.Width; Height := SourceBitmap.Height; end; AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; Result := Insert(SourceBitmap, TargetBitmap, 0, 0, False); Exit; end else if SameValue(ARadians, Pi / 2) then begin Result := RotateLeft(SourceBitmap, TargetBitmap, Background); Exit; end else if SameValue(ARadians, Pi) then begin Result := RotateAndMirror(SourceBitmap, TargetBitmap, Background); Exit; end else if SameValue(ARadians, 3 * Pi / 2) then begin Result := RotateRight(SourceBitmap, TargetBitmap, Background); Exit; end; with RotateCustomData do begin QuarterType := Simplify(ARadians); Radians := ARadians; Size := Effects.Size(SourceBitmap.Width, SourceBitmap.Height); end; ComputeRotatedSize(RotateCustomData.Size, ARadians, ASize); with TargetBitmap do begin PixelFormat := DefaultPixelFormat; Width := ASize.cx; Height := ASize.cy; end; if Assigned(Background) then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; end; with RotateCustomData do begin SourceCenter.X := Size.cx div 2; SourceCenter.Y := Size.cy div 2; TargetCenter.X := ASize.cx div 2; TargetCenter.Y := ASize.cy div 2; end; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) then begin AMasked := FMasked; FMasked := False; try RotateCustom(FMaskBitmap, Mask, E, @FNegativePixel); finally FMasked := AMasked; end; end; ScanPairEvent := FOnScanPair; FOnScanPair := RotateCustomProc; try RoundMode := GetRoundMode; SetRoundMode(rmNearest); try Result := ScanPair(TargetBitmap, SourceBitmap, @RotateCustomData, Mask); finally SetRoundMode(RoundMode); end; finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; end; procedure TEffects.RotateCustomProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateCustomData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; APoint: TPoint; begin with PRotateCustomData(RotateCustomData)^ do begin APoint := ComputePoint(Point(HorzIndex div DefaultByteCount, VertIndex), SourceCenter, TargetCenter, QuarterType, Radians); if (APoint.X = Size.cx) or (APoint.Y (APoint.Y = Size.cy) then Exit; end; J := Integer(Lines[APoint.Y]) + APoint.X * DefaultByteCount; for I := Low(TChannel) to High(TChannel) do PixelRef[I]^ := PByte(J + Ord(I))^; end; function TEffects.RotateLeft(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel): Boolean; var RotateData: TRotateData; Mask: TBitmap; AMasked: Boolean; ScanPairEvent: TScanPairEvent; begin with TargetBitmap do begin Width := SourceBitmap.Height; Height := SourceBitmap.Width; end; RotateData.Size.cy := TargetBitmap.Height - 1; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) and Assigned(Background) then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; end; ScanPairEvent := FOnScanPair; FOnScanPair := RotateLeftProc; try Result := ScanPair(SourceBitmap, TargetBitmap, @RotateData, Mask); finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; end; procedure TEffects.RotateLeftProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; begin J := Integer(Lines[PRotateData(RotateData).Size.cy - HorzIndex div DefaultByteCount]) + VertIndex * DefaultByteCount; for I := Low(TChannel) to High(TChannel) do PByte(J + Ord(I))^ := PixelRef[I]^; end; procedure TEffects.RotateProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; begin J := Integer(Lines[PRotateData(RotateData)^.Size.cy - VertIndex]) + HorzIndex; for I := Low(TChannel) to High(TChannel) do PByte(J + Ord(I))^ := PixelRef[I]^; end; function TEffects.RotateRight(SourceBitmap, TargetBitmap: TBitmap; Background: PPixel): Boolean; var RotateData: TRotateData; Mask: TBitmap; AMasked: Boolean; ScanPairEvent: TScanPairEvent; begin with TargetBitmap do begin Width := SourceBitmap.Height; Height := SourceBitmap.Width; end; RotateData.Size.cx := TargetBitmap.Width * DefaultByteCount - DefaultByteCount; if FMasked and not FMaskBitmap.Empty then Mask := TBitmap.Create else Mask := nil; try if Assigned(Mask) and Assigned(Background) then begin AMasked := FMasked; FMasked := False; try Fill(TargetBitmap, PositiveSel, Background^); finally FMasked := AMasked; end; end; ScanPairEvent := FOnScanPair; FOnScanPair := RotateRightProc; try Result := ScanPair(SourceBitmap, TargetBitmap, @RotateData, Mask); finally FOnScanPair := ScanPairEvent; end; finally Mask.Free; end; end; procedure TEffects.RotateRightProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; RotateData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; begin J := Integer(Lines[HorzIndex div DefaultByteCount]) + PRotateData(RotateData).Size.cx - VertIndex * DefaultByteCount; for I := Low(TChannel) to High(TChannel) do PByte(J + Ord(I))^ := PixelRef[I]^; end; function TEffects.RoughBlur(Bitmap: TBitmap; Size: TSize; Radius: TRadius; Fast: Boolean; Channel: TChannel): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := RoughBlur(Bitmap, Selection, Size, Radius, Fast); end; function TEffects.RoughBlur(Bitmap: TBitmap; ASelection: TSelection; ASize: TSize; ARadius: TRadius; AFast: Boolean): Boolean; var RoughBlurData: TRoughBlurData; ScanRangeEvent: TScanRangeEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with RoughBlurData do begin Size := Effects.Size(Bitmap.Width div ASize.cx + Integer(Bitmap.Width mod ASize.cx 0), Bitmap.Height div ASize.cy + Integer(Bitmap.Height mod ASize.cy 0)); RangeCount := Size.cx * Size.cy + Ord(not FTransparentRange); end; Result := RoughBlurData.RangeCount Ord(not FTransparentRange); if not Result then Exit; try with RoughBlurData do begin Selection := ASelection; Radius := ARadius; Fast := AFast; PixelateType := ptGet; CreateRowData(RowData, RangeCount, Selection); SetLength(Count, RangeCount); FillChar(Count[0], RangeCount * SizeOf(Integer), 0); end; ScanRangeEvent := FOnScanRange; FOnScanRange := RoughBlurProc; try Result := ScanRange(Bitmap, ASize, @RoughBlurData); if not Result then Exit; RoughBlurData.PixelateType := ptSet; Result := ScanRange(Bitmap, ASize, @RoughBlurData, FRangeBitmap); finally FOnScanRange := ScanRangeEvent; end; finally with RoughBlurData do begin DeleteRowData(RowData); Count := nil; end; end; end; procedure TEffects.RoughBlurProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; RangeIndex: Integer; Rect: TRect; RoughBlurData: Pointer; var Continue: Boolean); var I: TChannel; J, K, L, Step, Top, Bottom, Left, Right: Integer; begin with PRoughBlurData(RoughBlurData)^ do if PixelateType = ptGet then begin for I := Low(TChannel) to High(TChannel) do if Selection[I] and (RangeIndex Inc(RowData[I][RangeIndex], PixelRef[I]^); Inc(Count[RangeIndex]); end else for I := Low(TChannel) to High(TChannel) do if Selection[I] and (RangeIndex begin J := Radius * Size.cx; Top := RangeIndex - J; Bottom := RangeIndex + J; if Fast then Step := J // low-quality else Step := Size.cx; // high-quality K := 0; // total L := 0; // count while Top begin Left := Top - Radius; Right := Top + Radius; for J := Left to Right do if J = 0 then begin if J = RangeCount then Break; if Count[J] = 0 then System.Continue; Inc(K, RowData[I][J] div Count[J]); Inc(L); end; Inc(Top, Step); end; PixelRef[I]^ := K div L; end; end; function TEffects.Saturation(Bitmap: TBitmap; Channel: TChannel; Factor: Byte): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Saturation(Bitmap, Selection, Factor); end; function TEffects.Saturation(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; var SaturationData: TSaturationData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with SaturationData do begin Selection := ASelection; Factor := AFactor; end; ScanEvent := FOnScan; FOnScan := SaturationProc; try Result := ScanBitmap(Bitmap, @SaturationData); finally FOnScan := ScanEvent; end; end; procedure TEffects.SaturationProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; SaturationData: Pointer; var Continue: Boolean); var I: TChannel; J, K: Integer; begin J := 0; K := 0; with PSaturationData(SaturationData)^ do begin for I := Low(TChannel) to High(TChannel) do if Selection[I] then begin Inc(J, PixelRef[I]^); Inc(K) end; J := J div K; for I := Low(TChannel) to High(TChannel) do if Selection[I] then PixelRef[I]^ := J + ((PixelRef[I]^ - J) * Factor) div MaxByte; end; end; function TEffects.Separate(SourceBitmap: TBitmap; TargetBitmap: TGrayBitmap; Channel: TChannel): Boolean; var GrayBitmaps: TGrayBitmaps; begin FillChar(GrayBitmaps, SizeOf(TGrayBitmaps), 0); GrayBitmaps[Channel] := TargetBitmap; Result := Separate(SourceBitmap, GrayBitmaps); end; function TEffects.Separate(Bitmap: TBitmap; AGrayBitmaps: TGrayBitmaps): Boolean; var I: TChannel; SeparateData: TSeparateData; ScanEvent: TScanEvent; begin for I := Low(TChannel) to High(TChannel) do if Assigned(AGrayBitmaps[I]) then begin AGrayBitmaps[I].Width := Bitmap.Width; AGrayBitmaps[I].Height := Bitmap.Height; AGrayBitmaps[I].UpdatePalette; end; SeparateData.GrayBitmaps := AGrayBitmaps; ScanEvent := FOnScan; FOnScan := SeparateProc; try Result := ScanBitmap(Bitmap, @SeparateData); finally FOnScan := ScanEvent; end; end; procedure TEffects.SeparateProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; SeparateData: Pointer; var Continue: Boolean); var I: TChannel; J: Integer; P: PByte; begin J := HorzIndex div DefaultByteCount; for I := Low(TChannel) to High(TChannel) do with PSeparateData(SeparateData)^ do if Assigned(GrayBitmaps[I]) then begin P := Pointer(Integer(GrayBitmaps[I].ScanLine[VertIndex]) + J); P^ := PixelRef[I]^; end; end; function TEffects.Sharpen(SourceBitmap, TargetBitmap: TBitmap; Radius: TRadius; Difference: Byte; Percent: Extended; SharpenType: TSharpenType; Channel: TChannel): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Sharpen(SourceBitmap, TargetBitmap, Selection, Radius, Difference, Percent, SharpenType); end; function TEffects.Sharpen(SourceBitmap, TargetBitmap: TBitmap; ASelection: TSelection; ARadius: TRadius; ADifference: Byte; APercent: Extended; ASharpenType: TSharpenType): Boolean; var SharpenData: TSharpenData; ScanPairEvent: TScanPairEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; TargetBitmap.Assign(SourceBitmap); with SharpenData do begin Selection := ASelection; Radius := ARadius; Difference := ADifference; Percent := APercent; SharpenType := ASharpenType; Size.cx := SourceBitmap.Width - 1; Size.cy := SourceBitmap.Height - 1; end; ScanPairEvent := FOnScanPair; FOnScanPair := SharpenProc; try Result := ScanPair(TargetBitmap, SourceBitmap, @SharpenData); finally FOnScanPair := ScanPairEvent; end; end; procedure TEffects.SharpenProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; Lines: TLines; SharpenData: Pointer; var Continue: Boolean); var I: TChannel; J, K, L, M, N: Integer; Min, Max: Byte; Rect: TRect; Sum: TSum; begin J := HorzIndex div DefaultByteCount; with PSharpenData(SharpenData)^ do begin with Rect do begin Left := IfThen(J Radius, J - Radius, 0); Right := IfThen(J + Radius Top := IfThen(VertIndex Radius, VertIndex - Radius, 0); Bottom := IfThen(VertIndex + Radius end; FillChar(Sum, SizeOf(TSum), 0); N := 0; for K := Rect.Top to Rect.Bottom do for L := Rect.Left to Rect.Right do if (K VertIndex) or (L J) then begin M := Integer(Lines[K]) + L * DefaultByteCount; for I := Low(TChannel) to High(TChannel) do if Selection[I] then Inc(Sum[I], PByte(M + Ord(I))^); Inc(N); end; for I := Low(TChannel) to High(TChannel) do if Selection[I] then begin Sum[I] := Sum[I] div N; Min := EnsureRange(Sum[I] - Difference, 0, MaxByte); Max := EnsureRange(Sum[I] + Difference, 0, MaxByte); if (PixelRef[I]^ Max) then case SharpenType of stBrightness: if PixelRef[I]^ Sum[I] then PixelRef[I]^ := EnsureRange(PixelRef[I]^ + Round(PixelRef[I]^ * Percent / _100Percent), MinByte, MaxByte) else PixelRef[I]^ := EnsureRange(PixelRef[I]^ - Round(PixelRef[I]^ * Percent / _100Percent), MinByte, MaxByte); stContrast: IncreaseContrast(PixelRef[I]^, Round(MaxByte * Percent / _100Percent)); end; end; end; end; function TEffects.Simplify(var Radians: Extended): TQuarterType; begin if (Radians = Quarter2Range.Min) and (Radians begin Radians := Pi - Radians; Result := qt2; end else if (Radians = Quarter3Range.Min) and (Radians begin Radians := Radians - Pi; Result := qt3; end else if (Radians = Quarter4Range.Min) and (Radians begin Radians := Pi2 - Radians; Result := qt4 end else Result := qt1 end; function TEffects.Solorize(Bitmap: TBitmap; Channel: TChannel; Factor: Byte): Boolean; var Selection: TSelection; begin Selection := NegativeSel; Selection[Channel] := True; Result := Solorize(Bitmap, Selection, Factor); end; function TEffects.Solorize(Bitmap: TBitmap; ASelection: TSelection; AFactor: Byte): Boolean; var SolorizeData: TSolorizeData; ScanEvent: TScanEvent; begin Result := CheckSelection(ASelection); if not Result then Exit; with SolorizeData do begin Selection := ASelection; Factor := AFactor; end; ScanEvent := FOnScan; FOnScan := SolorizeProc; try Result := ScanBitmap(Bitmap, @SolorizeData); finally FOnScan := ScanEvent; end; end; procedure TEffects.SolorizeProc(PixelRef: TPixelRef; var HorzIndex, VertIndex: Integer; SolorizeData: Pointer; var Continue: Boolean); var I: TChannel; J, K: Integer; begin J := 0; K := 0; with PSolorizeData(SolorizeData)^ do begin for I := Low(TChannel) to High(TChannel) do if Selection[I] then begin Inc(J, PixelRef[I]^); Inc(K) end; J := J div K; if J Factor then for I := Low(TChannel) to High(TChannel) do PixelRef[I]^ := MaxByte - PixelRef[I]^ end; end; initialization DefaultByteCount := ByteCounts[DefaultPixelFormat]; end.