Componentes.Terceros.jvcl/official/3.32/run/JvPaintFX.pas

3521 lines
95 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvPaintFX.PAS, released on 2002-06-15.
The Initial Developer of the Original Code is Jan Verhoeven [jan1 dott verhoeven att wxs dott nl]
Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
All Rights Reserved.
Contributor(s): Robert Love [rlove att slcdug dott org].
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvPaintFX.pas 10612 2006-05-19 19:04:09Z jfudickar $
unit JvPaintFX;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows, Messages, Graphics, Controls, Forms,
SysUtils, Classes;
type
// Type of a filter for use with Stretch()
TFilterProc = function(Value: Single): Single;
TLightBrush = (lbBrightness, lbContrast, lbSaturation,
lbFisheye, lbrotate, lbtwist, lbrimple,
mbHor, mbTop, mbBottom, mbDiamond, mbWaste, mbRound,
mbRound2, mbSplitRound, mbSplitWaste);
TJvPaintFX = class(TComponent)
public
class procedure Solarize(const Src: TBitmap; var Dst: TBitmap; Amount: Integer);
class procedure Posterize(const Src: TBitmap; var Dst: TBitmap; Amount: Integer);
class procedure Blend(const Src1, Src2: TBitmap; var Dst: TBitmap; Amount: Single);
class procedure Blend2(const Src1, Src2: TBitmap; var Dst: TBitmap; Amount: Single);
class procedure ExtractColor(const Dst: TBitmap; AColor: TColor);
class procedure ExcludeColor(const Dst: TBitmap; AColor: TColor);
class procedure Turn(Src, Dst: TBitmap);
class procedure TurnRight(Src, Dst: TBitmap);
class procedure HeightMap(const Dst: TBitmap; Amount: Integer);
class procedure TexturizeTile(const Dst: TBitmap; Amount: Integer);
class procedure TexturizeOverlap(const Dst: TBitmap; Amount: Integer);
class procedure RippleRandom(const Dst: TBitmap; Amount: Integer);
class procedure RippleTooth(const Dst: TBitmap; Amount: Integer);
class procedure RippleTriangle(const Dst: TBitmap; Amount: Integer);
class procedure Triangles(const Dst: TBitmap; Amount: Integer);
class procedure DrawMandelJulia(const Dst: TBitmap; x0, y0, x1, y1: Single;
Niter: Integer; Mandel: Boolean);
class procedure FilterXBlue(const Dst: TBitmap; Min, Max: Integer);
class procedure FilterXGreen(const Dst: TBitmap; Min, Max: Integer);
class procedure FilterXRed(const Dst: TBitmap; Min, Max: Integer);
class procedure FilterBlue(const Dst: TBitmap; Min, Max: Integer);
class procedure FilterGreen(const Dst: TBitmap; Min, Max: Integer);
class procedure FilterRed(const Dst: TBitmap; Min, Max: Integer);
class procedure Emboss(var Bmp: TBitmap);
class procedure Plasma(Src1, Src2, Dst: TBitmap; Scale, Turbulence: Single);
class procedure Shake(Src, Dst: TBitmap; Factor: Single);
class procedure ShakeDown(Src, Dst: TBitmap; Factor: Single);
class procedure KeepBlue(const Dst: TBitmap; Factor: Single);
class procedure KeepGreen(const Dst: TBitmap; Factor: Single);
class procedure KeepRed(const Dst: TBitmap; Factor: Single);
class procedure Mandelbrot(const Dst: TBitmap; Factor: Integer);
class procedure MaskMandelbrot(const Dst: TBitmap; Factor: Integer);
class procedure FoldRight(Src1, Src2, Dst: TBitmap; Amount: Single);
class procedure QuartoOpaque(Src, Dst: TBitmap);
class procedure SemiOpaque(Src, Dst: TBitmap);
class procedure ShadowDownLeft(const Dst: TBitmap);
class procedure ShadowDownRight(const Dst: TBitmap);
class procedure ShadowUpLeft(const Dst: TBitmap);
class procedure ShadowUpRight(const Dst: TBitmap);
class procedure Darkness(const Dst: TBitmap; Amount: Integer);
class procedure Trace(const Dst: TBitmap; Intensity: Integer);
class procedure FlipRight(const Dst: TBitmap);
class procedure FlipDown(const Dst: TBitmap);
class procedure SpotLight(const Dst: TBitmap; Amount: Integer; Spot: TRect);
class procedure SplitLight(const Dst: TBitmap; Amount: Integer);
class procedure MakeSeamlessClip(var Dst: TBitmap; Seam: Integer);
class procedure Wave(const Dst: TBitmap; Amount, Inference, Style: Integer);
class procedure Mosaic(const Bm: TBitmap; Size: Integer);
class procedure SmoothRotate(var Src, Dst: TBitmap; CX, CY: Integer; Angle: Single);
class procedure SmoothResize(var Src, Dst: TBitmap);
class procedure Twist(var Bmp, Dst: TBitmap; Amount: Integer);
class procedure SplitBlur(const Dst: TBitmap; Amount: Integer);
class procedure GaussianBlur(const Dst: TBitmap; Amount: Integer);
class procedure Smooth(const Dst: TBitmap; Weight: Integer);
class procedure GrayScale(const Dst: TBitmap);
class procedure AddColorNoise(const Dst: TBitmap; Amount: Integer);
class procedure AddMonoNoise(const Dst: TBitmap; Amount: Integer);
class procedure Contrast(const Dst: TBitmap; Amount: Integer);
class procedure Lightness(const Dst: TBitmap; Amount: Integer);
class procedure Saturation(const Dst: TBitmap; Amount: Integer);
class procedure Spray(const Dst: TBitmap; Amount: Integer);
class procedure AntiAlias(const Dst: TBitmap);
class procedure AntiAliasRect(const Dst: TBitmap; XOrigin, YOrigin, XFinal, YFinal: Integer);
class procedure SmoothPoint(const Dst: TBitmap; XK, YK: Integer);
class procedure FishEye(var Bmp, Dst: TBitmap; Amount: Single);
class procedure Marble(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble2(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble3(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble4(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble5(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble6(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble7(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure Marble8(const Src: TBitmap; var Dst: TBitmap; Scale: Single; Turbulence: Integer);
class procedure SqueezeHor(Src, Dst: TBitmap; Amount: Integer; Style: TLightBrush);
class procedure SplitRound(Src, Dst: TBitmap; Amount: Integer; Style: TLightBrush);
class procedure Tile(Src, Dst: TBitmap; Amount: Integer);
// Interpolator
// Src: Source bitmap
// Dst: Destination bitmap
// Filter: Weight calculation filter
// AWidth: Relative sample radius
class procedure Stretch(Src, Dst: TBitmap; Filter: TFilterProc; AWidth: Single);
class procedure Grow(Src1, Src2, Dst: TBitmap; Amount: Single; X, Y: Integer);
class procedure Invert(Src: TBitmap);
class procedure MirrorRight(Src: TBitmap);
class procedure MirrorDown(Src: TBitmap);
end;
// Sample filters for use with Stretch()
function SplineFilter(Value: Single): Single;
function BellFilter(Value: Single): Single;
function TriangleFilter(Value: Single): Single;
function BoxFilter(Value: Single): Single;
function HermiteFilter(Value: Single): Single;
function Lanczos3Filter(Value: Single): Single;
function MitchellFilter(Value: Single): Single;
const
ResampleFilters: array [0..6] of record
Name: string; // Filter name
Filter: TFilterProc; // Filter implementation
Width: Single; // Suggested sampling width/radius
end = (
(Name: 'Box'; Filter: BoxFilter; Width: 0.5),
(Name: 'Triangle'; Filter: TriangleFilter; Width: 1.0),
(Name: 'Hermite'; Filter: HermiteFilter; Width: 1.0),
(Name: 'Bell'; Filter: BellFilter; Width: 1.5),
(Name: 'B-Spline'; Filter: SplineFilter; Width: 2.0),
(Name: 'Lanczos3'; Filter: Lanczos3Filter; Width: 3.0),
(Name: 'Mitchell'; Filter: MitchellFilter; Width: 2.0)
);
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvPaintFX.pas $';
Revision: '$Revision: 10612 $';
Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
Math,
JvJCLUtils, JvResources, JvTypes;
const
// TJvRGBTriple = TRGBQuad for VisualCLX
bpp = SizeOf(TJvRGBTriple);
function TrimInt(N, Min, Max: Integer): Integer;
begin
if N > Max then
Result := Max
else
if N < Min then
Result := Min
else
Result := N;
end;
function IntToByte(N: Integer): Byte;
begin
if N > 255 then
Result := 255
else
if N < 0 then
Result := 0
else
Result := N;
end;
// Just a small function to map the numbers to colors
function ConvertColor(Value: Integer): TColor;
const
Colors: array [0..15] of TColor =
(
clBlack, clNavy, clGreen, clAqua, clRed, clPurple, clMaroon, clSilver,
clGray, clBlue, clLime, clOlive, clFuchsia, clTeal, clYellow, clWhite
);
begin
if (Value < 0) or (Value > High(Colors)) then
Result := clWhite
else
Result := Colors[Value];
end;
function BellFilter(Value: Single): Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 0.5 then
Result := 0.75 - Sqr(Value)
else
if Value < 1.5 then
begin
Value := Value - 1.5;
Result := 0.5 * Sqr(Value);
end
else
Result := 0.0;
end;
// a.k.a. "Nearest Neighbour" filter
// anme: I have not been able to get acceptable
// results with this filter for subsampling.
function BoxFilter(Value: Single): Single;
begin
if (Value > -0.5) and (Value <= 0.5) then
Result := 1.0
else
Result := 0.0;
end;
function HermiteFilter(Value: Single): Single;
begin
// f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
if Value < 0.0 then
Value := -Value;
if Value < 1.0 then
Result := (2.0 * Value - 3.0) * Sqr(Value) + 1.0
else
Result := 0.0;
end;
function Lanczos3Filter(Value: Single): Single;
function SinC(Value: Single): Single;
begin
if Value <> 0.0 then
begin
Value := Value * Pi;
Result := Sin(Value) / Value;
end
else
Result := 1.0;
end;
begin
if Value < 0.0 then
Value := -Value;
if Value < 3.0 then
Result := SinC(Value) * SinC(Value / 3.0)
else
Result := 0.0;
end;
function MitchellFilter(Value: Single): Single;
const
B = 1.0 / 3.0;
C = 1.0 / 3.0;
var
T: Single;
begin
if Value < 0.0 then
Value := -Value;
T := Sqr(Value);
if Value < 1.0 then
begin
Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * T)) +
((-18.0 + 12.0 * B + 6.0 * C) * T) +
(6.0 - 2 * B));
Result := Value / 6.0;
end
else
if Value < 2.0 then
begin
Value := (((-1.0 * B - 6.0 * C) * (Value * T)) +
((6.0 * B + 30.0 * C) * T) +
((-12.0 * B - 48.0 * C) * Value) +
(8.0 * B + 24 * C));
Result := Value / 6.0;
end
else
Result := 0.0;
end;
// B-spline filter
function SplineFilter(Value: Single): Single;
var
T: Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 1.0 then
begin
T := Sqr(Value);
Result := 0.5 * T * Value - T + 2.0 / 3.0;
end
else
if Value < 2.0 then
begin
Value := 2.0 - Value;
Result := 1.0 / 6.0 * Sqr(Value) * Value;
end
else
Result := 0.0;
end;
// Triangle filter
// a.k.a. "Linear" or "Bilinear" filter
function TriangleFilter(Value: Single): Single;
begin
if Value < 0.0 then
Value := -Value;
if Value < 1.0 then
Result := 1.0 - Value
else
Result := 0.0;
end;
class procedure TJvPaintFX.AddColorNoise(const Dst: TBitmap; Amount: Integer);
var
Line: PJvRGBArray;
X, Y: Integer;
OPF: TPixelFormat;
begin
Randomize;
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
Line[X].rgbRed := IntToByte(Line[X].rgbRed + (Random(Amount) - (Amount shr 1)));
Line[X].rgbGreen := IntToByte(Line[X].rgbGreen + (Random(Amount) - (Amount shr 1)));
Line[X].rgbBlue := IntToByte(Line[X].rgbBlue + (Random(Amount) - (Amount shr 1)));
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.AddMonoNoise(const Dst: TBitmap; Amount: Integer);
var
Line: PJvRGBArray;
X, Y, A: Integer;
OPF: TPixelFormat;
begin
Randomize;
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
A := Random(Amount) - (Amount shr 1);
Line[X].rgbRed := IntToByte(Line[X].rgbRed + A);
Line[X].rgbGreen := IntToByte(Line[X].rgbGreen + A);
Line[X].rgbBlue := IntToByte(Line[X].rgbBlue + A);
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.AntiAlias(const Dst: TBitmap);
begin
JvJCLUtils.AntiAlias(Dst);
end;
class procedure TJvPaintFX.AntiAliasRect(const Dst: TBitmap;
XOrigin, YOrigin, XFinal, YFinal: Integer);
begin
{$IFDEF VCL}
JvJCLUtils.AntiAliasRect(Dst, XOrigin, YOrigin, XFinal, YFinal);
{$ENDIF VCL}
{$IFDEF VisualCLX}
JvQJCLUtils.AntiAliasRect(Dst, XOrigin, YOrigin, XFinal, YFinal);
{$ENDIF VisualCLX}
end;
class procedure TJvPaintFX.Contrast(const Dst: TBitmap; Amount: Integer);
var
Line: PJvRGBArray;
RG, GG, BG, R, G, B, X, Y: Integer;
OPF: TPixelFormat;
begin
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
R := Line[X].rgbRed;
G := Line[X].rgbGreen;
B := Line[X].rgbBlue;
RG := (Abs(127 - R) * Amount) div 255;
GG := (Abs(127 - G) * Amount) div 255;
BG := (Abs(127 - B) * Amount) div 255;
if R > 127 then
R := R + RG
else
R := R - RG;
if G > 127 then
G := G + GG
else
G := G - GG;
if B > 127 then
B := B + BG
else
B := B - BG;
Line[X].rgbRed := IntToByte(R);
Line[X].rgbGreen := IntToByte(G);
Line[X].rgbBlue := IntToByte(B);
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.FishEye(var Bmp, Dst: TBitmap; Amount: Single);
var
xmid, ymid: Single;
fx, fy: Single;
r1, r2: Single;
ifx, ify: Integer;
DX, DY: Single;
rmax: Single;
ty, tx: Integer;
WeightX, WeightY: array [0..1] of Single;
Weight: Single;
new_red, new_green: Integer;
new_blue: Integer;
total_red, total_green: Single;
total_blue: Single;
ix, iy: Integer;
sli, slo: PJvRGBArray;
begin
xmid := Bmp.Width / 2;
ymid := Bmp.Height / 2;
rmax := Dst.Width * Amount;
for ty := 0 to Dst.Height - 1 do
begin
for tx := 0 to Dst.Width - 1 do
begin
DX := tx - xmid;
DY := ty - ymid;
r1 := Sqrt(DX * DX + DY * DY);
if r1 = 0 then
begin
fx := xmid;
fy := ymid;
end
else
begin
r2 := rmax / 2 * (1 / (1 - r1 / rmax) - 1);
fx := DX * r2 / r1 + xmid;
fy := DY * r2 / r1 + ymid;
end;
ify := Trunc(fy);
ifx := Trunc(fx);
// Calculate the weights.
if fy >= 0 then
begin
WeightY[1] := fy - ify;
WeightY[0] := 1 - WeightY[1];
end
else
begin
WeightY[0] := -(fy - ify);
WeightY[1] := 1 - WeightY[0];
end;
if fx >= 0 then
begin
WeightX[1] := fx - ifx;
WeightX[0] := 1 - WeightX[1];
end
else
begin
WeightX[0] := -(fx - ifx);
WeightX[1] := 1 - WeightX[0];
end;
if ifx < 0 then
ifx := Bmp.Width - 1 - (-ifx mod Bmp.Width)
else
if ifx > Bmp.Width - 1 then
ifx := ifx mod Bmp.Width;
if ify < 0 then
ify := Bmp.Height - 1 - (-ify mod Bmp.Height)
else
if ify > Bmp.Height - 1 then
ify := ify mod Bmp.Height;
total_red := 0.0;
total_green := 0.0;
total_blue := 0.0;
for ix := 0 to 1 do
begin
for iy := 0 to 1 do
begin
if ify + iy < Bmp.Height then
sli := Bmp.ScanLine[ify + iy]
else
sli := Bmp.ScanLine[Bmp.Height - ify - iy];
if ifx + ix < Bmp.Width then
begin
new_red := sli[ifx + ix].rgbRed;
new_green := sli[ifx + ix].rgbGreen;
new_blue := sli[ifx + ix].rgbBlue;
end
else
begin
new_red := sli[Bmp.Width - ifx - ix].rgbRed;
new_green := sli[Bmp.Width - ifx - ix].rgbGreen;
new_blue := sli[Bmp.Width - ifx - ix].rgbBlue;
end;
Weight := WeightX[ix] * WeightY[iy];
total_red := total_red + new_red * Weight;
total_green := total_green + new_green * Weight;
total_blue := total_blue + new_blue * Weight;
end;
end;
slo := Dst.ScanLine[ty];
slo[tx].rgbRed := Round(total_red);
slo[tx].rgbGreen := Round(total_green);
slo[tx].rgbBlue := Round(total_blue);
end;
end;
end;
class procedure TJvPaintFX.GaussianBlur(const Dst: TBitmap; Amount: Integer);
var
I: Integer;
OPF: TPixelFormat;
begin
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for I := Amount downto 0 do
SplitBlur(Dst, 3);
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.GrayScale(const Dst: TBitmap);
var
Line: PJvRGBArray;
Gray, X, Y: Integer;
OPF: TPixelFormat;
begin
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
Gray := Round(Line[X].rgbRed * 0.3 + Line[X].rgbGreen * 0.59 + Line[X].rgbBlue * 0.11);
Line[X].rgbRed := Gray;
Line[X].rgbGreen := Gray;
Line[X].rgbBlue := Gray;
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.Lightness(const Dst: TBitmap; Amount: Integer);
var
Line: PJvRGBArray;
R, G, B, X, Y: Integer;
OPF: TPixelFormat;
begin
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
R := Line[X].rgbRed;
G := Line[X].rgbGreen;
B := Line[X].rgbBlue;
Line[X].rgbRed := IntToByte(R + ((255 - R) * Amount) div 255);
Line[X].rgbGreen := IntToByte(G + ((255 - G) * Amount) div 255);
Line[X].rgbBlue := IntToByte(B + ((255 - B) * Amount) div 255);
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.Darkness(const Dst: TBitmap; Amount: Integer);
var
Line: PJvRGBArray;
R, G, B, X, Y: Integer;
OPF: TPixelFormat;
begin
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
R := Line[X].rgbRed;
G := Line[X].rgbGreen;
B := Line[X].rgbBlue;
Line[X].rgbRed := IntToByte(R - (R * Amount) div 255);
Line[X].rgbGreen := IntToByte(G - (G * Amount) div 255);
Line[X].rgbBlue := IntToByte(B - (B * Amount) div 255);
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.Marble(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
YY := Scale * Cos((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := -Scale * Sin((X mod Turbulence) / Scale);
XM := Round(Abs(X + XX + YY));
YM := Round(Abs(Y + YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble2(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
YY := Scale * Cos((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := -Scale * Sin((X mod Turbulence) / Scale);
XM := Round(Abs(X + XX - YY));
YM := Round(Abs(Y + YY - XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble3(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
YY := Scale * Cos((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := -Scale * Sin((X mod Turbulence) / Scale);
XM := Round(Abs(X - XX + YY));
YM := Round(Abs(Y - YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble4(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
YY := Scale * Sin((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := -Scale * Cos((X mod Turbulence) / Scale);
XM := Round(Abs(X + XX + YY));
YM := Round(Abs(Y + YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble5(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := H - 1 downto 0 do
begin
YY := Scale * Cos((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := W - 1 downto 0 do
begin
XX := -Scale * Sin((X mod Turbulence) / Scale);
XM := Round(Abs(X + XX + YY));
YM := Round(Abs(Y + YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble6(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
YY := Scale * Cos((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := -tan((X mod Turbulence) / Scale) / Scale;
XM := Round(Abs(X + XX + YY));
YM := Round(Abs(Y + YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble7(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
YY := Scale * Sin((Y mod Turbulence) / Scale);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := -tan((X mod Turbulence) / Scale) / (Scale * Scale);
XM := Round(Abs(X + XX + YY));
YM := Round(Abs(Y + YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Marble8(const Src: TBitmap; var Dst: TBitmap; Scale: Single;
Turbulence: Integer);
var
X, XM, Y, YM: Integer;
XX, YY: Single;
Line1, Line2: PJvRGBArray;
W, H: Integer;
ax: Single;
Source: TBitmap;
begin
if Src = nil then
Exit;
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Source := TBitmap.Create;
Source.Assign(Src);
Dst.PixelFormat := pf24bit;
Source.PixelFormat := pf24bit;
H := Src.Height;
W := Src.Width;
for Y := 0 to H - 1 do
begin
ax := (Y mod Turbulence) / Scale;
YY := Scale * Sin(ax) * Cos(1.5 * ax);
Line1 := Source.ScanLine[Y];
for X := 0 to W - 1 do
begin
ax := (X mod Turbulence) / Scale;
XX := -Scale * Sin(2 * ax) * Cos(ax);
XM := Round(Abs(X + XX + YY));
YM := Round(Abs(Y + YY + XX));
if (YM < H) and (XM < W) then
begin
Line2 := Dst.ScanLine[YM];
Line2[XM] := Line1[X];
end;
end;
end;
Source.Free;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Saturation(const Dst: TBitmap; Amount: Integer);
var
Line: PJvRGBArray;
Gray, R, G, B, X, Y: Integer;
begin
for Y := 0 to Dst.Height - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
R := Line[X].rgbRed;
G := Line[X].rgbGreen;
B := Line[X].rgbBlue;
Gray := (R + G + B) div 3;
Line[X].rgbRed := IntToByte(Gray + (((R - Gray) * Amount) div 255));
Line[X].rgbGreen := IntToByte(Gray + (((G - Gray) * Amount) div 255));
Line[X].rgbBlue := IntToByte(Gray + (((B - Gray) * Amount) div 255));
end;
end;
end;
class procedure TJvPaintFX.Smooth(const Dst: TBitmap; Weight: Integer);
var
Line, Line1, Line2, Line3: PJvRGBArray;
W, H, X, Y: Integer;
Src: TBitmap;
OPF: TPixelFormat;
begin
if (Dst.Height < 2) or (Dst.Width < 2) then
Exit;
W := Dst.Width;
H := Dst.Height;
Src := TBitmap.Create;
Src.Assign(Dst);
OPF := Dst.PixelFormat;
Src.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
for Y := 1 to H - 2 do
begin
Line := Dst.ScanLine[Y];
Line1 := Src.ScanLine[Y-1];
Line2 := Src.ScanLine[Y];
Line3 := Src.ScanLine[Y+1];
Line[0].rgbRed := (Line2[0].rgbRed + Line2[1].rgbRed + Line1[0].rgbRed + Line3[0].rgbRed) div 4;
Line[0].rgbGreen := (Line2[0].rgbGreen + Line2[1].rgbGreen + Line1[0].rgbGreen + Line3[0].rgbGreen) div 4;
Line[0].rgbBlue := (Line2[0].rgbBlue + Line2[1].rgbBlue + Line1[0].rgbBlue + Line3[0].rgbBlue) div 4;
Line[W-1].rgbRed := (Line2[W-2].rgbRed + Line2[W-1].rgbRed + Line1[W-1].rgbRed + Line3[W-1].rgbRed) div 4;
Line[W-1].rgbGreen := (Line2[W-2].rgbGreen + Line2[W-1].rgbGreen + Line1[W-1].rgbGreen + Line3[W-1].rgbGreen) div 4;
Line[W-1].rgbBlue := (Line2[W-2].rgbBlue + Line2[W-1].rgbBlue + Line1[W-1].rgbBlue + Line3[W-1].rgbBlue) div 4;
for X := 1 to W - 2 do
begin
Line[X].rgbRed := (Line2[X-1].rgbRed + Line2[X+1].rgbRed + Line1[X].rgbRed + Line3[X].rgbRed) div 4;
Line[X].rgbGreen := (Line2[X-1].rgbGreen + Line2[X+1].rgbGreen + Line1[X].rgbGreen + Line3[X].rgbGreen) div 4;
Line[X].rgbBlue := (Line2[X-1].rgbBlue + Line2[X+1].rgbBlue + Line1[X].rgbBlue + Line3[X].rgbBlue) div 4;
end;
end;
Line := Dst.ScanLine[0];
Line1 := Src.ScanLine[0];
Line2 := Src.ScanLine[0];
Line3 := Src.ScanLine[1];
for X := 1 to Dst.Width - 2 do
begin
Line[X].rgbRed := (Line2[X-1].rgbRed + Line2[X+1].rgbRed + Line1[X].rgbRed + Line3[X].rgbRed) div 4;
Line[X].rgbGreen := (Line2[X-1].rgbGreen + Line2[X+1].rgbGreen + Line1[X].rgbGreen + Line3[X].rgbGreen) div 4;
Line[X].rgbBlue := (Line2[X-1].rgbBlue + Line2[X+1].rgbBlue + Line1[X].rgbBlue + Line3[X].rgbBlue) div 4;
end;
Line := Dst.ScanLine[H-1];
Line1 := Src.ScanLine[H-2];
Line2 := Src.ScanLine[H-1];
Line3 := Src.ScanLine[H-1];
for X := 1 to Dst.Width - 2 do
begin
Line[X].rgbRed := (Line2[X-1].rgbRed + Line2[X+1].rgbRed + Line1[X].rgbRed + Line3[X].rgbRed) div 4;
Line[X].rgbGreen := (Line2[X-1].rgbGreen + Line2[X+1].rgbGreen + Line1[X].rgbGreen + Line3[X].rgbGreen) div 4;
Line[X].rgbBlue := (Line2[X-1].rgbBlue + Line2[X+1].rgbBlue + Line1[X].rgbBlue + Line3[X].rgbBlue) div 4;
end;
Src.Free;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.SmoothPoint(const Dst: TBitmap; XK, YK: Integer);
var
Pixel: TColor;
B, G, R: Cardinal;
begin
if (XK > 0) and (YK > 0) and (XK < Dst.Width - 1) and (YK < Dst.Height - 1) then
with Dst.Canvas do
begin
Pixel := ColorToRGB(Pixels[XK, YK - 1]);
R := GetRValue(Pixel);
B := GetGValue(Pixel);
G := GetBValue(Pixel);
Pixel := ColorToRGB(Pixels[XK + 1, YK]);
R := R + GetRValue(Pixel);
G := G + GetGValue(Pixel);
B := B + GetBValue(Pixel);
Pixel := ColorToRGB(Pixels[XK, YK + 1]);
R := R + GetRValue(Pixel);
G := G + GetGValue(Pixel);
B := B + GetBValue(Pixel);
Pixel := ColorToRGB(Pixels[XK - 1, YK]);
R := R + GetRValue(Pixel);
G := G + GetGValue(Pixel);
B := B + GetBValue(Pixel);
Pixels[XK, YK] := RGB(R div 4, G div 4, B div 4);
end;
end;
class procedure TJvPaintFX.SmoothResize(var Src, Dst: TBitmap);
var
X, Y, xP, yP, yP2, xP2: Integer;
Read, Read2: PByteArray;
T, z, z2, iz2: Integer;
pc: PByteArray;
w1, w2, w3, w4: Integer;
Col1r, Col1g, Col1b, Col2r, Col2g, Col2b: Byte;
begin
xP2 := ((Src.Width - 1) shl 15) div Dst.Width;
yP2 := ((Src.Height - 1) shl 15) div Dst.Height;
yP := 0;
for Y := 0 to Dst.Height - 1 do
begin
xP := 0;
Read := Src.ScanLine[yP shr 15];
if yP shr 16 < Src.Height - 1 then
Read2 := Src.ScanLine[yP shr 15 + 1]
else
Read2 := Src.ScanLine[yP shr 15];
pc := Dst.ScanLine[Y];
z2 := yP and $7FFF;
iz2 := $8000 - z2;
for X := 0 to Dst.Width - 1 do
begin
T := xP shr 15;
Col1r := Read[T * bpp];
Col1g := Read[T * bpp + 1];
Col1b := Read[T * bpp + 2];
Col2r := Read2[T * bpp];
Col2g := Read2[T * bpp + 1];
Col2b := Read2[T * bpp + 2];
z := xP and $7FFF;
w2 := (z * iz2) shr 15;
w1 := iz2 - w2;
w4 := (z * z2) shr 15;
w3 := z2 - w4;
pc[X * bpp + 2] :=
(Col1b * w1 + Read[(T + 1) * bpp + 2] * w2 +
Col2b * w3 + Read2[(T + 1) * bpp + 2] * w4) shr 15;
pc[X * bpp + 1] :=
(Col1g * w1 + Read[(T + 1) * bpp + 1] * w2 +
Col2g * w3 + Read2[(T + 1) * bpp + 1] * w4) shr 15;
pc[X * bpp] :=
(Col1r * w1 + Read2[(T + 1) * bpp] * w2 +
Col2r * w3 + Read2[(T + 1) * bpp] * w4) shr 15;
Inc(xP, xP2);
end;
Inc(yP, yP2);
end;
end;
class procedure TJvPaintFX.SmoothRotate(var Src, Dst: TBitmap; CX, CY: Integer;
Angle: Single);
type
TFColor = record
B, G, R: Byte
end;
var
Top,
Bottom,
Left,
Right,
eww, nsw,
fx, fy,
wx, wy: Single;
cAngle,
sAngle: Double;
xDiff,
yDiff,
ifx, ify,
PX, PY,
ix, iy,
X, Y: Integer;
nw, ne,
sw, se: TFColor;
P1, P2, P3: PByteArray;
begin
Angle := Angle;
Angle := -Angle * Pi / 180;
sAngle := Sin(Angle);
cAngle := Cos(Angle);
xDiff := (Dst.Width - Src.Width) div 2;
yDiff := (Dst.Height - Src.Height) div 2;
for Y := 0 to Dst.Height - 1 do
begin
P3 := Dst.ScanLine[Y];
PY := 2 * (Y - CY) + 1;
for X := 0 to Dst.Width - 1 do
begin
PX := 2 * (X - CX) + 1;
fx := (((PX * cAngle - PY * sAngle) - 1) / 2 + CX) - xDiff;
fy := (((PX * sAngle + PY * cAngle) - 1) / 2 + CY) - yDiff;
ifx := Round(fx);
ify := Round(fy);
if (ifx > -1) and (ifx < Src.Width) and (ify > -1) and (ify < Src.Height) then
begin
eww := fx - ifx;
nsw := fy - ify;
iy := TrimInt(ify + 1, 0, Src.Height - 1);
ix := TrimInt(ifx + 1, 0, Src.Width - 1);
P1 := Src.ScanLine[ify];
P2 := Src.ScanLine[iy];
nw.R := P1[ifx * bpp];
nw.G := P1[ifx * bpp + 1];
nw.B := P1[ifx * bpp + 2];
ne.R := P1[ix * bpp];
ne.G := P1[ix * bpp + 1];
ne.B := P1[ix * bpp + 2];
sw.R := P2[ifx * bpp];
sw.G := P2[ifx * bpp + 1];
sw.B := P2[ifx * bpp + 2];
se.R := P2[ix * bpp];
se.G := P2[ix * bpp + 1];
se.B := P2[ix * bpp + 2];
Top := nw.B + eww * (ne.B - nw.B);
Bottom := sw.B + eww * (se.B - sw.B);
P3[X * bpp + 2] := IntToByte(Round(Top + nsw * (Bottom - Top)));
Top := nw.G + eww * (ne.G - nw.G);
Bottom := sw.G + eww * (se.G - sw.G);
P3[X * bpp + 1] := IntToByte(Round(Top + nsw * (Bottom - Top)));
Top := nw.R + eww * (ne.R - nw.R);
Bottom := sw.R + eww * (se.R - sw.R);
P3[X * bpp] := IntToByte(Round(Top + nsw * (Bottom - Top)));
end;
end;
end;
end;
class procedure TJvPaintFX.SplitBlur(const Dst: TBitmap; Amount: Integer);
var
p0, P1, P2: PByteArray;
CX, X, Y: Integer;
Buf: array [0..3, 0..2] of Byte;
begin
if Amount = 0 then
Exit;
for Y := 0 to Dst.Height - 1 do
begin
p0 := Dst.ScanLine[Y];
if Y - Amount < 0 then
P1 := Dst.ScanLine[Y]
else {Y-Amount>0}
P1 := Dst.ScanLine[Y - Amount];
if Y + Amount < Dst.Height then
P2 := Dst.ScanLine[Y + Amount]
else {Y+Amount>=Height}
P2 := Dst.ScanLine[Dst.Height - Y];
for X := 0 to Dst.Width - 1 do
begin
if X - Amount < 0 then
CX := X
else {X-Amount>0}
CX := X - Amount;
Buf[0, 0] := P1[CX * bpp];
Buf[0, 1] := P1[CX * bpp + 1];
Buf[0, 2] := P1[CX * bpp + 2];
Buf[1, 0] := P2[CX * bpp];
Buf[1, 1] := P2[CX * bpp + 1];
Buf[1, 2] := P2[CX * bpp + 2];
if X + Amount < Dst.Width then
CX := X + Amount
else {X+Amount>=Width}
CX := Dst.Width - X;
Buf[2, 0] := P1[CX * bpp];
Buf[2, 1] := P1[CX * bpp + 1];
Buf[2, 2] := P1[CX * bpp + 2];
Buf[3, 0] := P2[CX * bpp];
Buf[3, 1] := P2[CX * bpp + 1];
Buf[3, 2] := P2[CX * bpp + 2];
p0[X * bpp] := (Buf[0, 0] + Buf[1, 0] + Buf[2, 0] + Buf[3, 0]) shr 2;
p0[X * bpp + 1] := (Buf[0, 1] + Buf[1, 1] + Buf[2, 1] + Buf[3, 1]) shr 2;
p0[X * bpp + 2] := (Buf[0, 2] + Buf[1, 2] + Buf[2, 2] + Buf[3, 2]) shr 2;
end;
end;
end;
class procedure TJvPaintFX.Spray(const Dst: TBitmap; Amount: Integer);
var
I, J, X, Y, W, H, Val: Integer;
begin
H := Dst.Height;
W := Dst.Width;
for I := 0 to W - 1 do
for J := 0 to H - 1 do
begin
Val := Random(Amount);
X := I + Val - Random(Val * 2);
Y := J + Val - Random(Val * 2);
if (X > -1) and (X < W) and (Y > -1) and (Y < H) then
Dst.Canvas.Pixels[I, J] := Dst.Canvas.Pixels[X, Y];
end;
end;
class procedure TJvPaintFX.Mosaic(const Bm: TBitmap; Size: Integer);
var
X, Y, I, J: Integer;
P1, P2: PJvRGBArray;
P1Val: TJvRGBTriple;
begin
Y := 0;
repeat
P1 := Bm.ScanLine[Y];
repeat
J := 1;
repeat
P2 := Bm.ScanLine[Y];
X := 0;
repeat
P1Val := P1[X];
I := 1;
repeat
P2[X] := P1Val;
Inc(X);
Inc(I);
until (I > Size) or (X >= Bm.Width);
until X >= Bm.Width;
Inc(J);
Inc(Y);
until (J > Size) or (Y >= Bm.Height);
until (Y >= Bm.Height) or (X >= Bm.Width);
until Y >= Bm.Height;
end;
class procedure TJvPaintFX.Twist(var Bmp, Dst: TBitmap; Amount: Integer);
var
fxmid, fymid: Single;
txmid, tymid: Single;
fx, fy: Single;
tx2, ty2: Single;
R: Single;
theta: Single;
ifx, ify: Integer;
DX, DY: Single;
OFFSET: Single;
ty, tx: Integer;
WeightX, WeightY: array [0..1] of Single;
Weight: Single;
new_red, new_green: Integer;
new_blue: Integer;
total_red, total_green: Single;
total_blue: Single;
ix, iy: Integer;
sli, slo: PByteArray;
function ArcTan2(xt, yt: Single): Single;
begin
if xt = 0 then
if yt > 0 then
Result := Pi / 2
else
Result := -(Pi / 2)
else
begin
Result := ArcTan(yt / xt);
if xt < 0 then
Result := Pi + ArcTan(yt / xt);
end;
end;
begin
OFFSET := -(Pi / 2);
DX := Bmp.Width - 1;
DY := Bmp.Height - 1;
R := Sqrt(DX * DX + DY * DY);
tx2 := R;
ty2 := R;
txmid := (Bmp.Width - 1) / 2; //Adjust these to move center of rotation
tymid := (Bmp.Height - 1) / 2; //Adjust these to move ......
fxmid := (Bmp.Width - 1) / 2;
fymid := (Bmp.Height - 1) / 2;
if tx2 >= Bmp.Width then
tx2 := Bmp.Width - 1;
if ty2 >= Bmp.Height then
ty2 := Bmp.Height - 1;
for ty := 0 to Round(ty2) do
begin
for tx := 0 to Round(tx2) do
begin
DX := tx - txmid;
DY := ty - tymid;
R := Sqrt(DX * DX + DY * DY);
if R = 0 then
begin
fx := 0;
fy := 0;
end
else
begin
theta := ArcTan2(DX, DY) - R / Amount - OFFSET;
fx := R * Cos(theta);
fy := R * Sin(theta);
end;
fx := fx + fxmid;
fy := fy + fymid;
ify := Trunc(fy);
ifx := Trunc(fx);
// Calculate the weights.
if fy >= 0 then
begin
WeightY[1] := fy - ify;
WeightY[0] := 1 - WeightY[1];
end
else
begin
WeightY[0] := -(fy - ify);
WeightY[1] := 1 - WeightY[0];
end;
if fx >= 0 then
begin
WeightX[1] := fx - ifx;
WeightX[0] := 1 - WeightX[1];
end
else
begin
WeightX[0] := -(fx - ifx);
WeightX[1] := 1 - WeightX[0];
end;
if ifx < 0 then
ifx := Bmp.Width - 1 - (-ifx mod Bmp.Width)
else
if ifx > Bmp.Width - 1 then
ifx := ifx mod Bmp.Width;
if ify < 0 then
ify := Bmp.Height - 1 - (-ify mod Bmp.Height)
else
if ify > Bmp.Height - 1 then
ify := ify mod Bmp.Height;
total_red := 0.0;
total_green := 0.0;
total_blue := 0.0;
for ix := 0 to 1 do
begin
for iy := 0 to 1 do
begin
if ify + iy < Bmp.Height then
sli := Bmp.ScanLine[ify + iy]
else
sli := Bmp.ScanLine[Bmp.Height - ify - iy];
if ifx + ix < Bmp.Width then
begin
new_red := sli[(ifx + ix) * bpp];
new_green := sli[(ifx + ix) * bpp + 1];
new_blue := sli[(ifx + ix) * bpp + 2];
end
else
begin
new_red := sli[(Bmp.Width - ifx - ix) * bpp];
new_green := sli[(Bmp.Width - ifx - ix) * bpp + 1];
new_blue := sli[(Bmp.Width - ifx - ix) * bpp + 2];
end;
Weight := WeightX[ix] * WeightY[iy];
total_red := total_red + new_red * Weight;
total_green := total_green + new_green * Weight;
total_blue := total_blue + new_blue * Weight;
end;
end;
slo := Dst.ScanLine[ty];
slo[tx * bpp] := Round(total_red);
slo[tx * bpp + 1] := Round(total_green);
slo[tx * bpp + 2] := Round(total_blue);
end;
end;
end;
class procedure TJvPaintFX.Wave(const Dst: TBitmap; Amount, Inference, Style: Integer);
var
X, Y: Integer;
Bitmap: TBitmap;
P1, P2: PByteArray;
B: Integer;
Angle: Extended;
wavex: Integer;
begin
Bitmap := TBitmap.Create;
Bitmap.Assign(Dst);
wavex := Style;
Angle := Pi / 2 / Amount;
for Y := Bitmap.Height - 1 - (2 * Amount) downto Amount do
begin
P1 := Bitmap.ScanLine[Y];
B := 0;
for X := 0 to Bitmap.Width - 1 do
begin
P2 := Dst.ScanLine[Y + Amount + B];
P2[X * bpp] := P1[X * bpp];
P2[X * bpp + 1] := P1[X * bpp + 1];
P2[X * bpp + 2] := P1[X * bpp + 2];
case wavex of
0:
B := Amount * Variant(Sin(Angle * X));
1:
B := Amount * Variant(Sin(Angle * X) * Cos(Angle * X));
2:
B := Amount * Variant(Sin(Angle * X) * Sin(Inference * Angle * X));
end;
end;
end;
Bitmap.Free;
end;
class procedure TJvPaintFX.MakeSeamlessClip(var Dst: TBitmap; Seam: Integer);
var
p0, P1, P2: PByteArray;
H, W, I, J, sv, sh: Integer;
f0, f1, f2: real;
begin
H := Dst.Height;
W := Dst.Width;
sv := H div Seam;
sh := W div Seam;
P1 := Dst.ScanLine[0];
P2 := Dst.ScanLine[H - 1];
for I := 0 to W - 1 do
begin
P1[I * bpp] := P2[I * bpp];
P1[I * bpp + 1] := P2[I * bpp + 1];
P1[I * bpp + 2] := P2[I * bpp + 2];
end;
p0 := Dst.ScanLine[0];
P2 := Dst.ScanLine[sv];
for J := 1 to sv - 1 do
begin
P1 := Dst.ScanLine[J];
for I := 0 to W - 1 do
begin
f0 := (P2[I * bpp] - p0[I * bpp]) / sv * J + p0[I * bpp];
P1[I * bpp] := Round(f0);
f1 := (P2[I * bpp + 1] - p0[I * bpp + 1]) / sv * J + p0[I * bpp + 1];
P1[I * bpp + 1] := Round(f1);
f2 := (P2[I * bpp + 2] - p0[I * bpp + 2]) / sv * J + p0[I * bpp + 2];
P1[I * bpp + 2] := Round(f2);
end;
end;
for J := 0 to H - 1 do
begin
P1 := Dst.ScanLine[J];
P1[(W - 1) * bpp] := P1[0];
P1[(W - 1) * bpp + 1] := P1[1];
P1[(W - 1) * bpp + 2] := P1[2];
for I := 1 to sh - 1 do
begin
f0 := (P1[(W - sh) * bpp] - P1[(W - 1) * bpp]) / sh * I + P1[(W - 1) * bpp];
P1[(W - 1 - I) * bpp] := Round(f0);
f1 := (P1[(W - sh) * bpp + 1] - P1[(W - 1) * bpp + 1]) / sh * I + P1[(W - 1) * bpp + 1];
P1[(W - 1 - I) * bpp + 1] := Round(f1);
f2 := (P1[(W - sh) * bpp + 2] - P1[(W - 1) * bpp + 2]) / sh * I + P1[(W - 1) * bpp + 2];
P1[(W - 1 - I) * bpp + 2] := Round(f2);
end;
end;
end;
class procedure TJvPaintFX.SplitLight(const Dst: TBitmap; Amount: Integer);
var
X, Y, I: Integer;
P: PJvRGBArray;
OPF: TPixelFormat;
function Sinus(A: Integer): Integer;
begin
Result := Round(Sin(A / 255 * Pi / 2) * 255);
end;
begin
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
for I := 1 to Amount do
for Y := 0 to Dst.Height - 1 do
begin
P := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
P[X].rgbBlue := Sinus(P[X].rgbBlue);
P[X].rgbGreen := Sinus(P[X].rgbGreen);
P[X].rgbRed := Sinus(P[X].rgbRed);
end;
end;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.SqueezeHor(Src, Dst: TBitmap; Amount: Integer; Style: TLightBrush);
var
DX, X, Y, C, CX: Integer;
R: TRect;
Bm: TBitmap;
p0, P1: PByteArray;
begin
if Amount > (Src.Width div 2) then
Amount := Src.Width div 2;
Bm := TBitmap.Create;
Bm.PixelFormat := pf24bit;
Bm.Height := 1;
Bm.Width := Src.Width;
CX := Src.Width div 2;
p0 := Bm.ScanLine[0];
for Y := 0 to Src.Height - 1 do
begin
P1 := Src.ScanLine[Y];
for X := 0 to Src.Width - 1 do
begin
C := X * bpp;
p0[C] := P1[C];
p0[C + 1] := P1[C + 1];
p0[C + 2] := P1[C + 2];
end;
case Style of
mbHor:
begin
DX := Amount;
R := Rect(DX, Y, Src.Width - DX, Y + 1);
end;
mbTop:
begin
DX := Round((Src.Height - 1 - Y) / Src.Height * Amount);
R := Rect(DX, Y, Src.Width - DX, Y + 1);
end;
mbBottom:
begin
DX := Round(Y / Src.Height * Amount);
R := Rect(DX, Y, Src.Width - DX, Y + 1);
end;
mbDiamond:
begin
DX := Round(Amount * Abs(Cos(Y / (Src.Height - 1) * Pi)));
R := Rect(DX, Y, Src.Width - DX, Y + 1);
end;
mbWaste:
begin
DX := Round(Amount * Abs(Sin(Y / (Src.Height - 1) * Pi)));
R := Rect(DX, Y, Src.Width - DX, Y + 1);
end;
mbRound:
begin
DX := Round(Amount * Abs(Sin(Y / (Src.Height - 1) * Pi)));
R := Rect(CX - DX, Y, CX + DX, Y + 1);
end;
mbRound2:
begin
DX := Round(Amount * Abs(Sin(Y / (Src.Height - 1) * Pi * 2)));
R := Rect(CX - DX, Y, CX + DX, Y + 1);
end;
end;
Dst.Canvas.StretchDraw(R, Bm);
end;
Bm.Free;
end;
class procedure TJvPaintFX.Tile(Src, Dst: TBitmap; Amount: Integer);
var
w2, h2, I, J: Integer;
Bmp: TBitmap;
begin
Dst.Assign(Src);
if (Amount <= 0) or ((Src.Width div Amount) < 5) or ((Src.Height div Amount) < 5) then
Exit;
h2 := Src.Width div Amount;
w2 := Src.Height div Amount;
Bmp := TBitmap.Create;
Bmp.Width := w2;
Bmp.Height := h2;
Bmp.PixelFormat := pf24bit;
SmoothResize(Src, Bmp);
for J := 0 to Amount - 1 do
for I := 0 to Amount - 1 do
Dst.Canvas.Draw(I * w2, J * h2, Bmp);
Bmp.Free;
end;
// ---------------------------------------------------------------------------
// Interpolator
// ---------------------------------------------------------------------------
type
// Contributor for a pixel
TContributor = record
Pixel: Integer; // Source pixel
Weight: Single; // Pixel Weight
end;
TContributorList = array [0..0] of TContributor;
PContributorList = ^TContributorList;
// List of source pixels contributing to a destination pixel
TCList = record
N: Integer;
P: PContributorList;
end;
TCListList = array [0..0] of TCList;
PCListList = ^TCListList;
TRGB = packed record
R: Single;
G: Single;
B: Single;
end;
// Physical bitmap pixel
TColorRGB = packed record
R: Byte;
G: Byte;
B: Byte;
end;
PColorRGB = ^TColorRGB;
// Physical bitmap ScanLine (row)
TRGBList = packed array [0..0] of TColorRGB;
PRGBList = ^TRGBList;
class procedure TJvPaintFX.Stretch(Src, Dst: TBitmap; Filter: TFilterProc;
AWidth: Single);
var
xscale, yscale: Single; // Zoom Scale factors
I, J, k: Integer; // Loop variables
Center: Single; // Filter calculation variables
Width, fscale, Weight: Single; // Filter calculation variables
Left, Right: Integer; // Filter calculation variables
N: Integer; // Pixel number
Work: TBitmap;
Contrib: PCListList;
RGB: TRGB;
Color: TColorRGB;
SourceLine, DestLine: PRGBList;
SourcePixel, DestPixel: PColorRGB;
Delta, DestDelta: Integer;
SrcWidth, SrcHeight, DstWidth, DstHeight: Integer;
function Color2RGB(Color: TColor): TColorRGB;
begin
Result.R := Color and $000000FF;
Result.G := (Color and $0000FF00) shr 8;
Result.B := (Color and $00FF0000) shr 16;
end;
function RGB2Color(Color: TColorRGB): TColor;
begin
Result := Color.R or (Color.G shl 8) or (Color.B shl 16);
end;
begin
DstWidth := Dst.Width;
DstHeight := Dst.Height;
SrcWidth := Src.Width;
SrcHeight := Src.Height;
if (SrcWidth < 1) or (SrcHeight < 1) then
raise Exception.CreateRes(@RsESourceBitmapTooSmall);
// Create intermediate image to hold horizontal zoom
Work := TBitmap.Create;
try
Work.Height := SrcHeight;
Work.Width := DstWidth;
// xscale := DstWidth / SrcWidth;
// yscale := DstHeight / SrcHeight;
// Improvement suggested by David Ullrich:
if (SrcWidth = 1) then
xscale := DstWidth / SrcWidth
else
xscale := (DstWidth - 1) / (SrcWidth - 1);
if (SrcHeight = 1) then
yscale := DstHeight / SrcHeight
else
yscale := (DstHeight - 1) / (SrcHeight - 1);
// This implementation only works on 24-bit images because it uses
// TBitmap.ScanLine
Src.PixelFormat := pf24bit;
Dst.PixelFormat := Src.PixelFormat;
Work.PixelFormat := Src.PixelFormat;
// --------------------------------------------
// Pre-calculate filter contributions for a row
// -----------------------------------------------
GetMem(Contrib, DstWidth * SizeOf(TCList));
// Horizontal sub-sampling
// Scales from bigger to smaller Width
if (xscale < 1.0) then
begin
Width := AWidth / xscale;
fscale := 1.0 / xscale;
for I := 0 to DstWidth - 1 do
begin
Contrib^[I].N := 0;
GetMem(Contrib^[I].P, Trunc(Width * 2.0 + 1) * SizeOf(TContributor));
Center := I / xscale;
// Original code:
// Left := Ceil(Center - Width);
// Right := Floor(Center + Width);
Left := Floor(Center - Width);
Right := Ceil(Center + Width);
for J := Left to Right do
begin
Weight := Filter((Center - J) / fscale) / fscale;
if (Weight = 0.0) then
Continue;
if (J < 0) then
N := -J
else
if (J >= SrcWidth) then
N := SrcWidth - J + SrcWidth - 1
else
N := J;
k := Contrib^[I].N;
Contrib^[I].N := Contrib^[I].N + 1;
Contrib^[I].P^[k].Pixel := N;
Contrib^[I].P^[k].Weight := Weight;
end;
end;
end
else
// Horizontal super-sampling
// Scales from smaller to bigger Width
begin
for I := 0 to DstWidth - 1 do
begin
Contrib^[I].N := 0;
GetMem(Contrib^[I].P, Trunc(AWidth * 2.0 + 1) * SizeOf(TContributor));
Center := I / xscale;
// Original code:
// Left := Ceil(Center - AWidth);
// Right := Floor(Center + AWidth);
Left := Floor(Center - AWidth);
Right := Ceil(Center + AWidth);
for J := Left to Right do
begin
Weight := Filter(Center - J);
if (Weight = 0.0) then
Continue;
if J < 0 then
N := -J
else
if J >= SrcWidth then
N := SrcWidth - J + SrcWidth - 1
else
N := J;
if N < 0 then
N := -N;
k := Contrib^[I].N;
Contrib^[I].N := Contrib^[I].N + 1;
Contrib^[I].P^[k].Pixel := N;
Contrib^[I].P^[k].Weight := Weight;
end;
end;
end;
// ----------------------------------------------------
// Apply filter to sample horizontally from Src to Work
// ----------------------------------------------------
for k := 0 to SrcHeight - 1 do
begin
SourceLine := Src.ScanLine[k];
DestPixel := Work.ScanLine[k];
for I := 0 to DstWidth - 1 do
begin
RGB.R := 0.0;
RGB.G := 0.0;
RGB.B := 0.0;
for J := 0 to Contrib^[I].N - 1 do
begin
Color := SourceLine^[Contrib^[I].P^[J].Pixel];
Weight := Contrib^[I].P^[J].Weight;
if (Weight = 0.0) then
Continue;
RGB.R := RGB.R + Color.R * Weight;
RGB.G := RGB.G + Color.G * Weight;
RGB.B := RGB.B + Color.B * Weight;
end;
if RGB.R > 255.0 then
Color.R := 255
else
if RGB.R < 0.0 then
Color.R := 0
else
Color.R := Round(RGB.R);
if RGB.G > 255.0 then
Color.G := 255
else
if RGB.G < 0.0 then
Color.G := 0
else
Color.G := Round(RGB.G);
if RGB.B > 255.0 then
Color.B := 255
else
if RGB.B < 0.0 then
Color.B := 0
else
Color.B := Round(RGB.B);
// Set new Pixel value
DestPixel^ := Color;
// Move on to next column
Inc(DestPixel);
end;
end;
// Free the memory allocated for horizontal filter weights
for I := 0 to DstWidth - 1 do
FreeMem(Contrib^[I].P);
FreeMem(Contrib);
// -----------------------------------------------
// Pre-calculate filter contributions for a column
// -----------------------------------------------
GetMem(Contrib, DstHeight * SizeOf(TCList));
// Vertical sub-sampling
// Scales from bigger to smaller Height
if (yscale < 1.0) then
begin
Width := AWidth / yscale;
fscale := 1.0 / yscale;
for I := 0 to DstHeight - 1 do
begin
Contrib^[I].N := 0;
GetMem(Contrib^[I].P, Trunc(Width * 2.0 + 1) * SizeOf(TContributor));
Center := I / yscale;
// Original code:
// Left := Ceil(Center - Width);
// Right := Floor(Center + Width);
Left := Floor(Center - Width);
Right := Ceil(Center + Width);
for J := Left to Right do
begin
Weight := Filter((Center - J) / fscale) / fscale;
if Weight = 0.0 then
Continue;
if J < 0 then
N := -J
else
if J >= SrcHeight then
N := SrcHeight - J + SrcHeight - 1
else
N := J;
k := Contrib^[I].N;
Contrib^[I].N := Contrib^[I].N + 1;
Contrib^[I].P^[k].Pixel := N;
Contrib^[I].P^[k].Weight := Weight;
end;
end
end
else
// Vertical super-sampling
// Scales from smaller to bigger Height
begin
for I := 0 to DstHeight - 1 do
begin
Contrib^[I].N := 0;
GetMem(Contrib^[I].P, Trunc(AWidth * 2.0 + 1) * SizeOf(TContributor));
Center := I / yscale;
// Original code:
// Left := Ceil(Center - AWidth);
// Right := Floor(Center + AWidth);
Left := Floor(Center - AWidth);
Right := Ceil(Center + AWidth);
for J := Left to Right do
begin
Weight := Filter(Center - J);
if Weight = 0.0 then
Continue;
if J < 0 then
N := -J
else
if J >= SrcHeight then
N := SrcHeight - J + SrcHeight - 1
else
N := J;
k := Contrib^[I].N;
Contrib^[I].N := Contrib^[I].N + 1;
Contrib^[I].P^[k].Pixel := N;
Contrib^[I].P^[k].Weight := Weight;
end;
end;
end;
// --------------------------------------------------
// Apply filter to sample vertically from Work to Dst
// --------------------------------------------------
SourceLine := Work.ScanLine[0];
if Work.Height > 1 then
Delta := Integer(Work.ScanLine[1]) - Integer(SourceLine)
else
Delta := 0;
DestLine := Dst.ScanLine[0];
if Dst.Height > 1 then
DestDelta := Integer(Dst.ScanLine[1]) - Integer(DestLine)
else
DestDelta := 0;
for k := 0 to DstWidth - 1 do
begin
DestPixel := pointer(DestLine);
for I := 0 to DstHeight - 1 do
begin
RGB.R := 0;
RGB.G := 0;
RGB.B := 0;
// Weight := 0.0;
for J := 0 to Contrib^[I].N - 1 do
begin
Color := PColorRGB(Integer(SourceLine) + Contrib^[I].P^[J].Pixel * Delta)^;
Weight := Contrib^[I].P^[J].Weight;
if (Weight = 0.0) then
Continue;
RGB.R := RGB.R + Color.R * Weight;
RGB.G := RGB.G + Color.G * Weight;
RGB.B := RGB.B + Color.B * Weight;
end;
if RGB.R > 255.0 then
Color.R := 255
else
if RGB.R < 0.0 then
Color.R := 0
else
Color.R := Round(RGB.R);
if RGB.G > 255.0 then
Color.G := 255
else
if RGB.G < 0.0 then
Color.G := 0
else
Color.G := Round(RGB.G);
if RGB.B > 255.0 then
Color.B := 255
else
if RGB.B < 0.0 then
Color.B := 0
else
Color.B := Round(RGB.B);
DestPixel^ := Color;
Inc(Integer(DestPixel), DestDelta);
end;
Inc(SourceLine, 1);
Inc(DestLine, 1);
end;
// Free the memory allocated for vertical filter weights
for I := 0 to DstHeight - 1 do
FreeMem(Contrib^[I].P);
FreeMem(Contrib);
finally
Work.Free;
end;
end;
class procedure TJvPaintFX.Grow(Src1, Src2, Dst: TBitmap; Amount: Single; X, Y: Integer);
var
Bmp: TBitmap;
begin
Dst.Assign(Src1);
Bmp := TBitmap.Create;
Bmp.Width := Round(Amount * Src1.Width);
Bmp.Height := Round(Amount * Src1.Height);
Stretch(Src2, Bmp, ResampleFilters[4].Filter, ResampleFilters[4].Width);
Dst.Canvas.Draw(X, Y, Bmp);
Bmp.Free;
end;
class procedure TJvPaintFX.SpotLight(const Dst: TBitmap; Amount: Integer; Spot: TRect);
var
Bmp: TBitmap;
begin
Darkness(Dst, Amount);
Bmp := TBitmap.Create;
Bmp.Width := Dst.Width;
Bmp.Height := Dst.Height;
Bmp.Canvas.Brush.Color := clBlack;
Bmp.Canvas.FillRect(Rect(0, 0, Dst.Width, Dst.Height));
Bmp.Canvas.Brush.Color := clWhite;
Bmp.Canvas.Ellipse(Spot.Left, Spot.Top, Spot.Right, Spot.Bottom);
Bmp.Transparent := True;
Bmp.TransparentColor := clWhite;
Dst.Canvas.Draw(0, 0, Bmp);
Bmp.Free;
end;
class procedure TJvPaintFX.FlipDown(const Dst: TBitmap);
var
Bmp: TBitmap;
W, H, X, Y: Integer;
PD, PS: PByteArray;
begin
W := Dst.Width;
H := Dst.Height;
Bmp := TBitmap.Create;
Bmp.Width := W;
Bmp.Height := H;
Bmp.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
for Y := 0 to H - 1 do
begin
PD := Bmp.ScanLine[Y];
PS := Dst.ScanLine[H - 1 - Y];
for X := 0 to W - 1 do
begin
PD[X * bpp] := PS[X * bpp];
PD[X * bpp + 1] := PS[X * bpp + 1];
PD[X * bpp + 2] := PS[X * bpp + 2];
end;
end;
Dst.Assign(Bmp);
Bmp.Free;
end;
class procedure TJvPaintFX.FlipRight(const Dst: TBitmap);
var
dest: TBitmap;
W, H, X, Y: Integer;
PD, PS: PByteArray;
begin
W := Dst.Width;
H := Dst.Height;
dest := TBitmap.Create;
dest.Width := W;
dest.Height := H;
dest.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
for Y := 0 to H - 1 do
begin
PD := dest.ScanLine[Y];
PS := Dst.ScanLine[Y];
for X := 0 to W - 1 do
begin
PD[X * bpp] := PS[(W - 1 - X) * bpp];
PD[X * bpp + 1] := PS[(W - 1 - X) * bpp + 1];
PD[X * bpp + 2] := PS[(W - 1 - X) * bpp + 2];
end;
end;
Dst.Assign(dest);
dest.Free;
end;
class procedure TJvPaintFX.Trace(const Dst: TBitmap; Intensity: Integer);
var
X, Y, I: Integer;
P1, P2, P3, P4: PByteArray;
tb, TraceB: Byte;
hasb: Boolean;
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
Bitmap.Width := Dst.Width;
Bitmap.Height := Dst.Height;
Bitmap.Canvas.Draw(0, 0, Dst);
Bitmap.PixelFormat := pf8bit;
Dst.PixelFormat := pf24bit;
hasb := False;
TraceB := $00;
tb := 0;
for I := 1 to Intensity do
begin
for Y := 0 to Bitmap.Height - 2 do
begin
P1 := Bitmap.ScanLine[Y];
P2 := Bitmap.ScanLine[Y + 1];
P3 := Dst.ScanLine[Y];
P4 := Dst.ScanLine[Y + 1];
X := 0;
repeat
if P1[X] <> P1[X + 1] then
begin
if not hasb then
begin
tb := P1[X + 1];
hasb := True;
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
if P1[X] <> tb then
begin
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
P3[(X + 1) * bpp] := TraceB;
P3[(X + 1) * bpp + 1] := TraceB;
P3[(X + 1) * bpp + 1] := TraceB;
end;
end;
end;
if P1[X] <> P2[X] then
begin
if not hasb then
begin
tb := P2[X];
hasb := True;
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
if P1[X] <> tb then
begin
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
P4[X * bpp] := TraceB;
P4[X * bpp + 1] := TraceB;
P4[X * bpp + 2] := TraceB;
end;
end;
end;
Inc(X);
until X >= (Bitmap.Width - 2);
end;
// do the same in the opposite direction
// only when Intensity > 1
if I > 1 then
for Y := Bitmap.Height - 1 downto 1 do
begin
P1 := Bitmap.ScanLine[Y];
P2 := Bitmap.ScanLine[Y - 1];
P3 := Dst.ScanLine[Y];
P4 := Dst.ScanLine[Y - 1];
X := Bitmap.Width - 1;
repeat
if P1[X] <> P1[X - 1] then
begin
if not hasb then
begin
tb := P1[X - 1];
hasb := True;
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
if P1[X] <> tb then
begin
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
P3[(X - 1) * bpp] := TraceB;
P3[(X - 1) * bpp + 1] := TraceB;
P3[(X - 1) * bpp + 2] := TraceB;
end;
end;
end;
if P1[X] <> P2[X] then
begin
if not hasb then
begin
tb := P2[X];
hasb := True;
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
if P1[X] <> tb then
begin
P3[X * bpp] := TraceB;
P3[X * bpp + 1] := TraceB;
P3[X * bpp + 2] := TraceB;
end
else
begin
P4[X * bpp] := TraceB;
P4[X * bpp + 1] := TraceB;
P4[X * bpp + 2] := TraceB;
end;
end;
end;
Dec(X);
until X <= 1;
end;
end;
Bitmap.Free;
end;
class procedure TJvPaintFX.ShadowUpLeft(const Dst: TBitmap);
var
X, Y: Integer;
Bitmap: TBitmap;
P1, P2: PByteArray;
begin
Bitmap := TBitmap.Create;
Bitmap.Width := Dst.Width;
Bitmap.Height := Dst.Height;
Bitmap.PixelFormat := pf24bit;
Bitmap.Canvas.Draw(0, 0, Dst);
for Y := 0 to Bitmap.Height - 5 do
begin
P1 := Bitmap.ScanLine[Y];
P2 := Bitmap.ScanLine[Y + 4];
for X := 0 to Bitmap.Width - 5 do
if P1[X * bpp] > P2[(X + 4) * bpp] then
begin
P1[X * bpp] := P2[(X + 4) * bpp] + 1;
P1[X * bpp + 1] := P2[(X + 4) * bpp + 1] + 1;
P1[X * bpp + 2] := P2[(X + 4) * bpp + 2] + 1;
end;
end;
Dst.Assign(Bitmap);
Bitmap.Free;
end;
class procedure TJvPaintFX.ShadowUpRight(const Dst: TBitmap);
var
X, Y: Integer;
Bitmap: TBitmap;
P1, P2: PByteArray;
begin
Bitmap := TBitmap.Create;
Bitmap.Width := Dst.Width;
Bitmap.Height := Dst.Height;
Bitmap.PixelFormat := pf24bit;
Bitmap.Canvas.Draw(0, 0, Dst);
for Y := 0 to Bitmap.Height - 5 do
begin
P1 := Bitmap.ScanLine[Y];
P2 := Bitmap.ScanLine[Y + 4];
for X := Bitmap.Width - 1 downto 4 do
if P1[X * bpp] > P2[(X - 4) * bpp] then
begin
P1[X * bpp] := P2[(X - 4) * bpp] + 1;
P1[X * bpp + 1] := P2[(X - 4) * bpp + 1] + 1;
P1[X * bpp + 2] := P2[(X - 4) * bpp + 2] + 1;
end;
end;
Dst.Assign(Bitmap);
Bitmap.Free;
end;
class procedure TJvPaintFX.ShadowDownLeft(const Dst: TBitmap);
var
X, Y: Integer;
Bitmap: TBitmap;
P1, P2: PByteArray;
begin
Bitmap := TBitmap.Create;
Bitmap.Width := Dst.Width;
Bitmap.Height := Dst.Height;
Bitmap.PixelFormat := pf24bit;
Bitmap.Canvas.Draw(0, 0, Dst);
for Y := Bitmap.Height - 1 downto 4 do
begin
P1 := Bitmap.ScanLine[Y];
P2 := Bitmap.ScanLine[Y - 4];
for X := 0 to Bitmap.Width - 5 do
if P1[X * bpp] > P2[(X + 4) * bpp] then
begin
P1[X * bpp] := P2[(X + 4) * bpp] + 1;
P1[X * bpp + 1] := P2[(X + 4) * bpp + 1] + 1;
P1[X * bpp + 2] := P2[(X + 4) * bpp + 2] + 1;
end;
end;
Dst.Assign(Bitmap);
Bitmap.Free;
end;
class procedure TJvPaintFX.ShadowDownRight(const Dst: TBitmap);
var
X, Y: Integer;
Bitmap: TBitmap;
P1, P2: PByteArray;
begin
Bitmap := TBitmap.Create;
Bitmap.Width := Dst.Width;
Bitmap.Height := Dst.Height;
Bitmap.PixelFormat := pf24bit;
Bitmap.Canvas.Draw(0, 0, Dst);
for Y := Bitmap.Height - 1 downto 4 do
begin
P1 := Bitmap.ScanLine[Y];
P2 := Bitmap.ScanLine[Y - 4];
for X := Bitmap.Width - 1 downto 4 do
if P1[X * bpp] > P2[(X - 4) * bpp] then
begin
P1[X * bpp] := P2[(X - 4) * bpp] + 1;
P1[X * bpp + 1] := P2[(X - 4) * bpp + 1] + 1;
P1[X * bpp + 2] := P2[(X - 4) * bpp + 2] + 1;
end;
end;
Dst.Assign(Bitmap);
Bitmap.Free;
end;
class procedure TJvPaintFX.SemiOpaque(Src, Dst: TBitmap);
var
B: TBitmap;
P: PByteArray;
X, Y: Integer;
begin
B := TBitmap.Create;
B.Width := Src.Width;
B.Height := Src.Height;
B.PixelFormat := pf24bit;
B.Canvas.Draw(0, 0, Src);
for Y := 0 to B.Height - 1 do
begin
P := B.ScanLine[Y];
if (Y mod 2) = 0 then
begin
for X := 0 to B.Width - 1 do
if (X mod 2) = 0 then
begin
P[X * bpp] := $FF;
P[X * bpp + 1] := $FF;
P[X * bpp + 2] := $FF;
end;
end
else
begin
for X := 0 to B.Width - 1 do
if ((X + 1) mod 2) = 0 then
begin
P[X * bpp] := $FF;
P[X * bpp + 1] := $FF;
P[X * bpp + 2] := $FF;
end;
end;
end;
B.Transparent := True;
B.TransparentColor := clWhite;
Dst.Canvas.Draw(0, 0, B);
B.Free;
end;
class procedure TJvPaintFX.QuartoOpaque(Src, Dst: TBitmap);
var
B: TBitmap;
P: PByteArray;
X, Y: Integer;
begin
B := TBitmap.Create;
B.Width := Src.Width;
B.Height := Src.Height;
B.PixelFormat := pf24bit;
B.Canvas.Draw(0, 0, Src);
for Y := 0 to B.Height - 1 do
begin
P := B.ScanLine[Y];
if (Y mod 2) = 0 then
begin
for X := 0 to B.Width - 1 do
if (X mod 2) = 0 then
begin
P[X * bpp] := $FF;
P[X * bpp + 1] := $FF;
P[X * bpp + 2] := $FF;
end;
end
else
begin
for X := 0 to B.Width - 1 do
begin
P[X * bpp] := $FF;
P[X * bpp + 1] := $FF;
P[X * bpp + 2] := $FF;
end;
end;
end;
B.Transparent := True;
B.TransparentColor := clWhite;
Dst.Canvas.Draw(0, 0, B);
B.Free;
end;
class procedure TJvPaintFX.FoldRight(Src1, Src2, Dst: TBitmap; Amount: Single);
var
W, H, X, Y, xf, xf0: Integer;
PS1, PS2, PD: PByteArray;
begin
Src1.PixelFormat := pf24bit;
Src2.PixelFormat := pf24bit;
W := Src1.Width;
H := Src2.Height;
Dst.Width := W;
Dst.Height := H;
Dst.PixelFormat := pf24bit;
xf := Round(Amount * W);
for Y := 0 to H - 1 do
begin
PS1 := Src1.ScanLine[Y];
PS2 := Src2.ScanLine[Y];
PD := Dst.ScanLine[Y];
for X := 0 to xf do
begin
xf0 := xf + (xf - X);
if xf0 < W then
begin
PD[xf0 * bpp] := PS1[X * bpp];
PD[xf0 * bpp + 1] := PS1[X * bpp + 1];
PD[xf0 * bpp + 2] := PS1[X * bpp + 2];
PD[X * bpp] := PS2[X * bpp];
PD[X * bpp + 1] := PS2[X * bpp + 1];
PD[X * bpp + 2] := PS2[X * bpp + 2];
end;
end;
if (2 * xf) < W - 1 then
for X := 2 * xf + 1 to W - 1 do
begin
PD[X * bpp] := PS1[X * bpp];
PD[X * bpp + 1] := PS1[X * bpp + 1];
PD[X * bpp + 2] := PS1[X * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.Mandelbrot(const Dst: TBitmap; Factor: Integer);
const
MaxX = 1.25;
MinX = -2;
MaxY = 1.25;
MinY = -1.25;
var
W, H, X, Y: Integer;
DX, DY: Extended;
Line: PByteArray;
Color: Integer;
function IsMandel(CA, CBi: Extended): Integer;
const
MAX_ITERATION = 64;
var
OldA: Extended; {just a variable to keep 'a' from being destroyed}
A, B: Extended; {function Z divided in real and imaginary parts}
LengthZ: Extended; {length of Z, sqrt(length_z)>2 => Z->infinity}
Iteration: Integer;
begin
A := 0; {initialize Z(0) = 0}
B := 0;
Iteration := 0; {initialize Iteration}
repeat
OldA := A; {saves the 'a' (Will be destroyed in next line}
A := A * A - B * B + CA;
B := 2 * OldA * B + CBi;
Iteration := Iteration + 1;
LengthZ := A * A + B * B;
until (LengthZ >= 4) or (Iteration > MAX_ITERATION);
Result := Iteration;
end;
begin
W := Dst.Width;
H := Dst.Height;
Dst.PixelFormat := pf24bit;
DX := (MaxX - MinX) / W;
DY := (MaxY - MinY) / H;
for Y := 0 to H - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to W - 1 do
begin
Color := IsMandel(MinX + X * DX, MinY + Y * DY);
if Color > Factor then
Color := $FF
else
Color := $00;
Line[X * bpp] := Color;
Line[X * bpp + 1] := Color;
Line[X * bpp + 2] := Color;
end;
end;
end;
class procedure TJvPaintFX.MaskMandelbrot(const Dst: TBitmap; Factor: Integer);
var
Bm: TBitmap;
begin
Bm := TBitmap.Create;
Bm.Width := Dst.Width;
Bm.Height := Dst.Height;
Mandelbrot(Bm, Factor);
Bm.Transparent := True;
Bm.TransparentColor := clWhite;
Dst.Canvas.Draw(0, 0, Bm);
Bm.Free;
end;
class procedure TJvPaintFX.KeepBlue(const Dst: TBitmap; Factor: Single);
var
X, Y, W, H: Integer;
Line: PByteArray;
begin
Dst.PixelFormat := pf24bit;
W := Dst.Width;
H := Dst.Height;
for Y := 0 to H - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to W - 1 do
begin
Line[X * bpp] := Round(Factor * Line[X * bpp]);
Line[X * bpp + 1] := 0;
Line[X * bpp + 2] := 0;
end;
end;
end;
class procedure TJvPaintFX.KeepGreen(const Dst: TBitmap; Factor: Single);
var
X, Y, W, H: Integer;
Line: PByteArray;
begin
Dst.PixelFormat := pf24bit;
W := Dst.Width;
H := Dst.Height;
for Y := 0 to H - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to W - 1 do
begin
Line[X * bpp + 1] := Round(Factor * Line[X * bpp + 1]);
Line[X * bpp] := 0;
Line[X * bpp + 2] := 0;
end;
end;
end;
class procedure TJvPaintFX.KeepRed(const Dst: TBitmap; Factor: Single);
var
X, Y, W, H: Integer;
Line: PByteArray;
begin
Dst.PixelFormat := pf24bit;
W := Dst.Width;
H := Dst.Height;
for Y := 0 to H - 1 do
begin
Line := Dst.ScanLine[Y];
for X := 0 to W - 1 do
begin
Line[X * bpp + 2] := Round(Factor * Line[X * bpp + 2]);
Line[X * bpp + 1] := 0;
Line[X * bpp] := 0;
end;
end;
end;
class procedure TJvPaintFX.Shake(Src, Dst: TBitmap; Factor: Single);
var
X, Y, H, W, DX: Integer;
P: PByteArray;
begin
Dst.Canvas.Draw(0, 0, Src);
Dst.PixelFormat := pf24bit;
W := Dst.Width;
H := Dst.Height;
DX := Round(Factor * W);
if DX = 0 then
Exit;
if DX > (W div 2) then
Exit;
for Y := 0 to H - 1 do
begin
P := Dst.ScanLine[Y];
if (Y mod 2) = 0 then
for X := DX to W - 1 do
begin
P[(X - DX) * bpp] := P[X * bpp];
P[(X - DX) * bpp + 1] := P[X * bpp + 1];
P[(X - DX) * bpp + 2] := P[X * bpp + 2];
end
else
for X := W - 1 downto DX do
begin
P[X * bpp] := P[(X - DX) * bpp];
P[X * bpp + 1] := P[(X - DX) * bpp + 1];
P[X * bpp + 2] := P[(X - DX) * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.ShakeDown(Src, Dst: TBitmap; Factor: Single);
var
X, Y, H, W, DY: Integer;
P, P2, P3: PByteArray;
begin
Dst.Canvas.Draw(0, 0, Src);
Dst.PixelFormat := pf24bit;
W := Dst.Width;
H := Dst.Height;
DY := Round(Factor * H);
if DY = 0 then
Exit;
if DY > (H div 2) then
Exit;
for Y := DY to H - 1 do
begin
P := Dst.ScanLine[Y];
P2 := Dst.ScanLine[Y - DY];
for X := 0 to W - 1 do
if (X mod 2) = 0 then
begin
P2[X * bpp] := P[X * bpp];
P2[X * bpp + 1] := P[X * bpp + 1];
P2[X * bpp + 2] := P[X * bpp + 2];
end;
end;
for Y := H - 1 - DY downto 0 do
begin
P := Dst.ScanLine[Y];
P3 := Dst.ScanLine[Y + DY];
for X := 0 to W - 1 do
if (X mod 2) <> 0 then
begin
P3[X * bpp] := P[X * bpp];
P3[X * bpp + 1] := P[X * bpp + 1];
P3[X * bpp + 2] := P[X * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.Plasma(Src1, Src2, Dst: TBitmap; Scale, Turbulence: Single);
var
cval, sval: array [0..255] of Integer;
I, X, Y, W, H, XX, YY: Integer;
Asin, Acos: Extended;
PS1, PS2, PD: PByteArray;
begin
W := Src1.Width;
H := Src1.Height;
if Turbulence < 10 then
Turbulence := 10;
if Scale < 5 then
Scale := 5;
for I := 0 to 255 do
begin
sincos(I / Turbulence, Asin, Acos);
sval[I] := Round(-Scale * Asin);
cval[I] := Round(Scale * Acos);
end;
for Y := 0 to H - 1 do
begin
PD := Dst.ScanLine[Y];
PS2 := Src2.ScanLine[Y];
for X := 0 to W - 1 do
begin
XX := X + sval[PS2[X * bpp]];
YY := Y + cval[PS2[X * bpp]];
if (XX >= 0) and (XX < W) and (YY >= 0) and (YY < H) then
begin
PS1 := Src1.ScanLine[YY];
PD[X * bpp] := PS1[XX * bpp];
PD[X * bpp + 1] := PS1[XX * bpp + 1];
PD[X * bpp + 2] := PS1[XX * bpp + 2];
end;
end;
end;
;
end;
class procedure TJvPaintFX.SplitRound(Src, Dst: TBitmap; Amount: Integer; Style: TLightBrush);
var
X, Y, W, C, c00, DX, CX: Integer;
R, R00: TRect;
Bm, bm2: TBitmap;
p0, p00, P1: PByteArray;
begin
if Amount = 0 then
begin
Dst.Canvas.Draw(0, 0, Src);
Exit;
end;
CX := Src.Width div 2;
if Amount > CX then
Amount := CX;
W := Src.Width;
Bm := TBitmap.Create;
Bm.PixelFormat := pf24bit;
Bm.Height := 1;
Bm.Width := CX;
bm2 := TBitmap.Create;
bm2.PixelFormat := pf24bit;
bm2.Height := 1;
bm2.Width := CX;
p0 := Bm.ScanLine[0];
p00 := bm2.ScanLine[0];
DX := 0;
for Y := 0 to Src.Height - 1 do
begin
P1 := Src.ScanLine[Y];
for X := 0 to CX - 1 do
begin
C := X * bpp;
c00 := (CX + X) * bpp;
p0[C] := P1[C];
p0[C + 1] := P1[C + 1];
p0[C + 2] := P1[C + 2];
p00[C] := P1[c00];
p00[C + 1] := P1[c00 + 1];
p00[C + 2] := P1[c00 + 2];
end;
case Style of
mbSplitRound:
DX := Round(Amount * Abs(Sin(Y / (Src.Height - 1) * Pi)));
mbSplitWaste:
DX := Round(Amount * Abs(Cos(Y / (Src.Height - 1) * Pi)));
end;
R := Rect(0, Y, DX, Y + 1);
Dst.Canvas.StretchDraw(R, Bm);
R00 := Rect(W - 1 - DX, Y, W - 1, Y + 1);
Dst.Canvas.StretchDraw(R00, bm2);
end;
Bm.Free;
bm2.Free;
end;
class procedure TJvPaintFX.Emboss(var Bmp: TBitmap);
var
X, Y: Integer;
P1, P2: PByteArray;
begin
for Y := 0 to Bmp.Height - 2 do
begin
P1 := Bmp.ScanLine[Y];
P2 := Bmp.ScanLine[Y + 1];
for X := 0 to Bmp.Width - 4 do
begin
P1[X * bpp] := (P1[X * bpp] + (P2[(X + bpp) * bpp] xor $FF)) shr 1;
P1[X * bpp + 1] := (P1[X * bpp + 1] + (P2[(X + bpp) * bpp + 1] xor $FF)) shr 1;
P1[X * bpp + 2] := (P1[X * bpp + 2] + (P2[(X + bpp) * bpp + 2] xor $FF)) shr 1;
end;
end;
end;
class procedure TJvPaintFX.FilterRed(const Dst: TBitmap; Min, Max: Integer);
var
C, X, Y: Integer;
P1: PByteArray;
begin
for Y := 0 to Dst.Height - 1 do
begin
P1 := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := X * bpp;
if (P1[C + 2] > Min) and (P1[C + 2] < Max) then
P1[C + 2] := $FF
else
P1[C + 2] := 0;
P1[C] := 0;
P1[C + 1] := 0;
end;
end;
end;
class procedure TJvPaintFX.FilterGreen(const Dst: TBitmap; Min, Max: Integer);
var
C, X, Y: Integer;
P1: PByteArray;
begin
for Y := 0 to Dst.Height - 1 do
begin
P1 := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := X * bpp;
if (P1[C + 1] > Min) and (P1[C + 1] < Max) then
P1[C + 1] := $FF
else
P1[C + 1] := 0;
P1[C] := 0;
P1[C + 2] := 0;
end;
end;
end;
class procedure TJvPaintFX.FilterBlue(const Dst: TBitmap; Min, Max: Integer);
var
C, X, Y: Integer;
P1: PByteArray;
begin
for Y := 0 to Dst.Height - 1 do
begin
P1 := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := X * bpp;
if (P1[C] > Min) and (P1[C] < Max) then
P1[C] := $FF
else
P1[C] := 0;
P1[C + 1] := 0;
P1[C + 2] := 0;
end;
end;
end;
class procedure TJvPaintFX.FilterXRed(const Dst: TBitmap; Min, Max: Integer);
var
C, X, Y: Integer;
P1: PByteArray;
begin
for Y := 0 to Dst.Height - 1 do
begin
P1 := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := X * bpp;
if (P1[C + 2] > Min) and (P1[C + 2] < Max) then
P1[C + 2] := $FF
else
P1[C + 2] := 0;
end;
end;
end;
class procedure TJvPaintFX.FilterXGreen(const Dst: TBitmap; Min, Max: Integer);
var
C, X, Y: Integer;
P1: PByteArray;
begin
for Y := 0 to Dst.Height - 1 do
begin
P1 := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := X * bpp;
if (P1[C + 1] > Min) and (P1[C + 1] < Max) then
P1[C + 1] := $FF
else
P1[C + 1] := 0;
end;
end;
end;
class procedure TJvPaintFX.FilterXBlue(const Dst: TBitmap; Min, Max: Integer);
var
C, X, Y: Integer;
P1: PByteArray;
begin
for Y := 0 to Dst.Height - 1 do
begin
P1 := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := X * bpp;
if (P1[C] > Min) and (P1[C] < Max) then
P1[C] := $FF
else
P1[C] := 0;
end;
end;
end;
class procedure TJvPaintFX.DrawMandelJulia(const Dst: TBitmap; x0, y0, x1, y1: Single; Niter: Integer; Mandel: Boolean);
const
//Number if colors. If this is changed, the number of mapped colors must also be changed
nc = 16;
type
TJvRGBTriplet = record
R: Byte;
G: Byte;
B: Byte;
end;
var
X, XX, Y, YY, CX, CY, DX, DY, XSquared, YSquared: Double;
NX, NY, PY, PX, I: Integer;
Line: PByteArray;
cc: array [0..15] of TJvRGBTriplet;
AColor: TColor;
begin
Dst.PixelFormat := pf24bit;
for I := 0 to 15 do
begin
AColor := ConvertColor(I);
cc[I].B := GetBValue(ColorToRGB(AColor));
cc[I].G := GetGValue(ColorToRGB(AColor));
cc[I].R := GetRValue(ColorToRGB(AColor));
end;
if Niter < nc then
Niter := nc;
try
NX := Dst.Width;
NY := Dst.Height;
CX := 0;
CY := 1;
DX := (x1 - x0) / NX;
DY := (y1 - y0) / NY;
PY := 0;
while PY < NY do
begin
Line := Dst.ScanLine[PY];
PX := 0;
while (PX < NX) do
begin
X := x0 + PX * DX;
Y := y0 + PY * DY;
if Mandel then
begin
CX := X;
CY := Y;
X := 0;
Y := 0;
end;
XSquared := 0;
YSquared := 0;
I := 0;
while (I <= Niter) and (XSquared + YSquared < (4)) do
begin
XSquared := X * X;
YSquared := Y * Y;
XX := XSquared - YSquared + CX;
YY := (2 * X * Y) + CY;
X := XX;
Y := YY;
I := I + 1;
end;
I := I - 1;
if (I = Niter) then
I := 0
else
I := Round(I / (Niter / nc));
// Canvas.Pixels[PX,PY] := ConvertColor(I);
Line[PX * 3] := cc[I].B;
Line[PX * 3 + 1] := cc[I].G;
Line[PX * 3 + 2] := cc[I].R;
PX := PX + 1;
end;
PY := PY + 1;
end;
finally
end;
end;
class procedure TJvPaintFX.Invert(Src: TBitmap);
var
W, H, X, Y: Integer;
P: PByteArray;
begin
W := Src.Width;
H := Src.Height;
Src.PixelFormat := pf24bit;
for Y := 0 to H - 1 do
begin
P := Src.ScanLine[Y];
for X := 0 to W - 1 do
begin
P[X * bpp] := not P[X * bpp];
P[X * bpp + 1] := not P[X * bpp + 1];
P[X * bpp + 2] := not P[X * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.MirrorRight(Src: TBitmap);
var
W, H, X, Y: Integer;
P: PByteArray;
begin
W := Src.Width;
H := Src.Height;
Src.PixelFormat := pf24bit;
for Y := 0 to H - 1 do
begin
P := Src.ScanLine[Y];
for X := 0 to W div 2 do
begin
P[(W - 1 - X) * bpp] := P[X * bpp];
P[(W - 1 - X) * bpp + 1] := P[X * bpp + 1];
P[(W - 1 - X) * bpp + 2] := P[X * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.MirrorDown(Src: TBitmap);
var
W, H, X, Y: Integer;
P1, P2: PByteArray;
begin
W := Src.Width;
H := Src.Height;
Src.PixelFormat := pf24bit;
for Y := 0 to H div 2 do
begin
P1 := Src.ScanLine[Y];
P2 := Src.ScanLine[H - 1 - Y];
for X := 0 to W - 1 do
begin
P2[X * bpp] := P1[X * bpp];
P2[X * bpp + 1] := P1[X * bpp + 1];
P2[X * bpp + 2] := P1[X * bpp + 2];
end;
end;
end;
// resample image as triangles
class procedure TJvPaintFX.Triangles(const Dst: TBitmap; Amount: Integer);
type
TTriplet = record
R: Byte;
G: Byte;
B: Byte;
end;
var
W, H, X, Y, tb, tm, te: Integer;
PS: PByteArray;
T: TTriplet;
begin
W := Dst.Width;
H := Dst.Height;
Dst.PixelFormat := pf24bit;
if Amount < 5 then
Amount := 5;
Amount := (Amount div 2) * 2 + 1;
tm := Amount div 2;
for Y := 0 to H - 1 do
begin
PS := Dst.ScanLine[Y];
T.R := PS[0];
T.G := PS[1];
T.B := PS[2];
tb := Y mod (Amount - 1);
if tb > tm then
tb := 2 * tm - tb;
if tb = 0 then
tb := Amount;
te := tm + Abs(tm - (Y mod Amount));
for X := 0 to W - 1 do
begin
if (X mod tb) = 0 then
begin
T.R := PS[X * bpp];
T.G := PS[X * bpp + 1];
T.B := PS[X * bpp + 2];
end;
if ((X mod te) = 1) and (tb <> 0) then
begin
T.R := PS[X * bpp];
T.G := PS[X * bpp + 1];
T.B := PS[X * bpp + 2];
end;
PS[X * bpp] := T.R;
PS[X * bpp + 1] := T.G;
PS[X * bpp + 2] := T.B;
end;
end;
end;
class procedure TJvPaintFX.RippleTooth(const Dst: TBitmap; Amount: Integer);
var
X, Y: Integer;
P1, P2: PByteArray;
B: Byte;
begin
Dst.PixelFormat := pf24bit;
Amount := Min(Dst.Height div 2, Amount);
for Y := Dst.Height - 1 - Amount downto 0 do
begin
P1 := Dst.ScanLine[Y];
B := 0;
for X := 0 to Dst.Width - 1 do
begin
P2 := Dst.ScanLine[Y + B];
P2[X * bpp] := P1[X * bpp];
P2[X * bpp + 1] := P1[X * bpp + 1];
P2[X * bpp + 2] := P1[X * bpp + 2];
Inc(B);
if B > Amount then
B := 0;
end;
end;
end;
class procedure TJvPaintFX.RippleTriangle(const Dst: TBitmap; Amount: Integer);
var
X, Y: Integer;
P1, P2: PByteArray;
B: Byte;
doinc: Boolean;
begin
Amount := Min(Dst.Height div 2, Amount);
for Y := Dst.Height - 1 - Amount downto 0 do
begin
P1 := Dst.ScanLine[Y];
B := 0;
doinc := True;
for X := 0 to Dst.Width - 1 do
begin
P2 := Dst.ScanLine[Y + B];
P2[X * bpp] := P1[X * bpp];
P2[X * bpp + 1] := P1[X * bpp + 1];
P2[X * bpp + 2] := P1[X * bpp + 2];
if doinc then
begin
Inc(B);
if B > Amount then
begin
doinc := False;
B := Amount - 1;
end;
end
else
begin
if B = 0 then
begin
doinc := True;
B := 2;
end;
Dec(B);
end;
end;
end;
end;
class procedure TJvPaintFX.RippleRandom(const Dst: TBitmap; Amount: Integer);
var
X, Y: Integer;
P1, P2: PByteArray;
B: Byte;
begin
Amount := Min(Dst.Height div 2, Amount);
Dst.PixelFormat := pf24bit;
Randomize;
for Y := Dst.Height - 1 - Amount downto 0 do
begin
P1 := Dst.ScanLine[Y];
B := 0;
for X := 0 to Dst.Width - 1 do
begin
P2 := Dst.ScanLine[Y + B];
P2[X * bpp] := P1[X * bpp];
P2[X * bpp + 1] := P1[X * bpp + 1];
P2[X * bpp + 2] := P1[X * bpp + 2];
B := Random(Amount);
end;
end;
end;
class procedure TJvPaintFX.TexturizeOverlap(const Dst: TBitmap; Amount: Integer);
var
W, H, X, Y, xo: Integer;
Bm: TBitmap;
ARect: TRect;
begin
Bm := TBitmap.Create;
Amount := Min(Dst.Width div 2, Amount);
Amount := Min(Dst.Height div 2, Amount);
xo := Round(Amount * 2 / 3);
Bm.Width := Amount;
Bm.Height := Amount;
W := Dst.Width;
H := Dst.Height;
ARect := Rect(0, 0, Amount, Amount);
Bm.Canvas.StretchDraw(ARect, Dst);
Y := 0;
repeat
X := 0;
repeat
Dst.Canvas.Draw(X, Y, Bm);
X := X + xo;
until X >= W;
Y := Y + xo;
until Y >= H;
Bm.Free;
end;
class procedure TJvPaintFX.TexturizeTile(const Dst: TBitmap; Amount: Integer);
var
W, H, X, Y: Integer;
Bm: TBitmap;
ARect: TRect;
begin
Bm := TBitmap.Create;
Amount := Min(Dst.Width div 2, Amount);
Amount := Min(Dst.Height div 2, Amount);
Bm.Width := Amount;
Bm.Height := Amount;
W := Dst.Width;
H := Dst.Height;
ARect := Rect(0, 0, Amount, Amount);
Bm.Canvas.StretchDraw(ARect, Dst);
Y := 0;
repeat
X := 0;
repeat
Dst.Canvas.Draw(X, Y, Bm);
X := X + Bm.Width;
until X >= W;
Y := Y + Bm.Height;
until Y >= H;
Bm.Free;
end;
class procedure TJvPaintFX.HeightMap(const Dst: TBitmap; Amount: Integer);
var
Bm: TBitmap;
W, H, X, Y: Integer;
pb, PS: PByteArray;
C: Integer;
begin
H := Dst.Height;
W := Dst.Width;
Bm := TBitmap.Create;
Bm.Width := W;
Bm.Height := H;
Bm.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
Bm.Canvas.Draw(0, 0, Dst);
for Y := 0 to H - 1 do
begin
pb := Bm.ScanLine[Y];
for X := 0 to W - 1 do
begin
C := Round((pb[X * bpp] + pb[X * bpp + 1] + pb[X * bpp + 2]) / 3 / 255 * Amount);
if (Y - C) >= 0 then
begin
PS := Dst.ScanLine[Y - C];
PS[X * bpp] := pb[X * bpp];
PS[X * bpp + 1] := pb[X * bpp + 1];
PS[X * bpp + 2] := pb[X * bpp + 2];
end;
end;
end;
Bm.Free;
end;
class procedure TJvPaintFX.Turn(Src, Dst: TBitmap);
var
W, H, X, Y: Integer;
PS, PD: PByteArray;
begin
H := Src.Height;
W := Src.Width;
Src.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
Dst.Height := W;
Dst.Width := H;
for Y := 0 to H - 1 do
begin
PS := Src.ScanLine[Y];
for X := 0 to W - 1 do
begin
PD := Dst.ScanLine[W - 1 - X];
PD[Y * bpp] := PS[X * bpp];
PD[Y * bpp + 1] := PS[X * bpp + 1];
PD[Y * bpp + 2] := PS[X * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.TurnRight(Src, Dst: TBitmap);
var
W, H, X, Y: Integer;
PS, PD: PByteArray;
begin
H := Src.Height;
W := Src.Width;
Src.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
Dst.Height := W;
Dst.Width := H;
for Y := 0 to H - 1 do
begin
PS := Src.ScanLine[Y];
for X := 0 to W - 1 do
begin
PD := Dst.ScanLine[X];
PD[(H - 1 - Y) * bpp] := PS[X * bpp];
PD[(H - 1 - Y) * bpp + 1] := PS[X * bpp + 1];
PD[(H - 1 - Y) * bpp + 2] := PS[X * bpp + 2];
end;
end;
end;
class procedure TJvPaintFX.ExtractColor(const Dst: TBitmap; AColor: TColor);
var
X, Y: Integer;
P: PJvRGBArray;
EColor: TColor;
R, G, B: Byte;
OPF: TPixelFormat;
Val: Byte;
begin
EColor := ColorToRGB(AColor);
R := GetRValue(EColor);
G := GetGValue(EColor);
B := GetBValue(EColor);
OPF := Dst.PixelFormat;
Dst.PixelFormat := pf24bit;
if EColor = 0 then
Val := $FF
else
Val := 0;
for Y := 0 to Dst.Height - 1 do
begin
P := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
if ((P[X].rgbBlue <> B) or (P[X].rgbGreen <> G) or (P[X].rgbRed <> R)) then
begin
P[X].rgbBlue := Val;
P[X].rgbGreen := Val;
P[X].rgbRed := Val;
end;
end
end;
if AColor = clBlack then
Dst.TransparentColor := clWhite
else
Dst.TransparentColor := clBlack;
Dst.Transparent := True;
Dst.PixelFormat := OPF;
end;
class procedure TJvPaintFX.ExcludeColor(const Dst: TBitmap; AColor: TColor);
begin
Dst.TransparentColor := AColor;
Dst.Transparent := True;
end;
class procedure TJvPaintFX.Blend(const Src1, Src2: TBitmap; var Dst: TBitmap; Amount: Single);
var
W, H, X, Y: Integer;
PS1, PS2, PD: PByteArray;
begin
W := Src1.Width;
H := Src1.Height;
Dst.Width := W;
Dst.Height := H;
Src1.PixelFormat := pf24bit;
Src2.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
for Y := 0 to H - 1 do
begin
PS1 := Src1.ScanLine[Y];
PS2 := Src2.ScanLine[Y];
PD := Dst.ScanLine[Y];
for X := 0 to W - 1 do
begin
PD[X * bpp] := Round((1 - Amount) * PS1[X * bpp] + Amount * PS2[X * bpp]);
PD[X * bpp + 1] := Round((1 - Amount) * PS1[X * bpp + 1] + Amount * PS2[X * bpp + 1]);
PD[X * bpp + 2] := Round((1 - Amount) * PS1[X * bpp + 2] + Amount * PS2[X * bpp + 2]);
end;
end;
end;
class procedure TJvPaintFX.Blend2(const Src1, Src2: TBitmap; var Dst: TBitmap; Amount: Single);
var
W, H, X, Y: Integer;
PS1, PS2, PD: PByteArray;
begin
W := Src1.Width;
H := Src1.Height;
Dst.Width := W;
Dst.Height := H;
Src1.PixelFormat := pf24bit;
Src2.PixelFormat := pf24bit;
Dst.PixelFormat := pf24bit;
for Y := 0 to H - 1 do
begin
PS1 := Src1.ScanLine[Y];
PS2 := Src2.ScanLine[Y];
PD := Dst.ScanLine[Y];
for X := 0 to W - 1 do
if ((PS2[X * bpp] = $FF) and (PS2[X * bpp + 1] = $FF) and (PS2[X * bpp + 2] = $FF)) then
begin
PD[X * bpp] := $FF;
PD[X * bpp + 2] := $FF;
PD[X * bpp + 2] := $FF;
end
else
begin
PD[X * bpp] := Round((1 - Amount) * PS1[X * bpp] + Amount * PS2[X * bpp]);
PD[X * bpp + 1] := Round((1 - Amount) * PS1[X * bpp + 1] + Amount * PS2[X * bpp + 1]);
PD[X * bpp + 2] := Round((1 - Amount) * PS1[X * bpp + 2] + Amount * PS2[X * bpp + 2]);
end;
end;
end;
class procedure TJvPaintFX.Solarize(const Src: TBitmap; var Dst: TBitmap; Amount: Integer);
var
X, Y: Integer;
P: PJvRGBArray;
C: Integer;
begin
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
P := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
C := (P[X].rgbBlue + P[X].rgbGreen + P[X].rgbRed) div 3;
if C > Amount then
begin
P[X].rgbBlue := 255 - P[X].rgbBlue;
P[X].rgbGreen := 255 - P[X].rgbGreen;
P[X].rgbRed := 255 - P[X].rgbRed;
end;
end;
end;
Dst.PixelFormat := Src.PixelFormat;
end;
class procedure TJvPaintFX.Posterize(const Src: TBitmap; var Dst: TBitmap; Amount: Integer);
var
X, Y: Integer;
PD: PJvRGBArray;
begin
if Dst = nil then
Dst := TBitmap.Create;
Dst.Assign(Src);
Dst.PixelFormat := pf24bit;
for Y := 0 to Dst.Height - 1 do
begin
PD := Dst.ScanLine[Y];
for X := 0 to Dst.Width - 1 do
begin
PD[X].rgbBlue := Round(PD[X].rgbBlue / Amount) * Amount;
PD[X].rgbGreen := Round(PD[X].rgbGreen / Amount) * Amount;
PD[X].rgbRed := Round(PD[X].rgbRed / Amount) * Amount;
end;
end;
Dst.PixelFormat := Src.PixelFormat;
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.