1097 lines
40 KiB
ObjectPascal
1097 lines
40 KiB
ObjectPascal
unit BGRAGradients;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
{$i bgrasse.inc}
|
|
|
|
interface
|
|
|
|
{ Here are various functions that draw gradients, shadow and lighting }
|
|
|
|
uses
|
|
Classes, Graphics, BGRABitmapTypes, BGRABitmap, BGRABlend, BGRAPhongTypes, BGRASSE;
|
|
|
|
{ Creates a bitmap with the specified text horizontally centered and with a shadow }
|
|
function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
|
|
AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True): TBGRABitmap;
|
|
|
|
{----------------------------------------------------------------------}
|
|
{ Functions to draw multiple gradients.
|
|
See : http://wiki.lazarus.freepascal.org/Double_Gradient#nGradient }
|
|
type
|
|
TnGradientInfo = record
|
|
StartColor,StopColor: TBGRAPixel;
|
|
Direction: TGradientDirection;
|
|
EndPercent : single; // Position from 0 to 1
|
|
end;
|
|
|
|
function nGradientInfo(StartColor, StopColor: TBGRAPixel; Direction: TGradientDirection; EndPercent: Single): TnGradientInfo;
|
|
|
|
function nGradientAlphaFill(ARect: TRect; ADir: TGradientDirection; const AGradient: array of TnGradientInfo): TBGRABitmap;
|
|
function nGradientAlphaFill(AWidth, AHeight: Integer; ADir: TGradientDirection; const AGradient: array of TnGradientInfo): TBGRABitmap;
|
|
procedure nGradientAlphaFill(ACanvas: TCanvas; ARect: TRect; ADir: TGradientDirection; const AGradient: array of TnGradientInfo);
|
|
procedure nGradientAlphaFill(ABitmap: TBGRABitmap; ARect: TRect; ADir: TGradientDirection; const AGradient: array of TnGradientInfo);
|
|
|
|
function DoubleGradientAlphaFill(ARect: TRect; AStart1,AStop1,AStart2,AStop2: TBGRAPixel;
|
|
ADirection1,ADirection2,ADir: TGradientDirection; AValue: Single): TBGRABitmap;
|
|
function DoubleGradientAlphaFill(AWidth,AHeight: Integer; AStart1,AStop1,AStart2,AStop2: TBGRAPixel;
|
|
ADirection1,ADirection2,ADir: TGradientDirection; AValue: Single): TBGRABitmap;
|
|
procedure DoubleGradientAlphaFill(ACanvas: TCanvas; ARect: TRect; AStart1,AStop1,AStart2,AStop2: TBGRAPixel;
|
|
ADirection1,ADirection2,ADir: TGradientDirection; AValue: Single);
|
|
procedure DoubleGradientAlphaFill(ABitmap: TBGRABitmap; ARect: TRect; AStart1,AStop1,AStart2,AStop2: TBGRAPixel;
|
|
ADirection1,ADirection2,ADir: TGradientDirection; AValue: Single);
|
|
|
|
{----------------------------------------------------------------------}
|
|
{ Phong shading functions. Use a height map (grayscale image or a precise map filled with MapHeightToBGRA)
|
|
to determine orientation and position of the surface.
|
|
|
|
Phong shading consist in adding an ambiant light, a diffuse light (angle between light and object),
|
|
and a specular light (angle between light, object and observer, i.e. reflected light) }
|
|
|
|
type
|
|
TRectangleMapOption = (rmoNoLeftBorder,rmoNoTopBorder,rmoNoRightBorder,rmoNoBottomBorder,rmoLinearBorder);
|
|
TRectangleMapOptions = set of TRectangleMapOption;
|
|
|
|
{ TPhongShading }
|
|
|
|
TPhongShading = class(TCustomPhongShading)
|
|
LightSourceIntensity : Single; //global intensity of the light
|
|
|
|
LightSourceDistanceTerm, //minimum distance always added (positive value)
|
|
LightSourceDistanceFactor, //how much actual distance is taken into account (usually 0 or 1)
|
|
LightDestFactor : Single; //how much the location of the lightened pixel is taken into account (usually 0 or 1)
|
|
|
|
LightPositionZ : Integer;
|
|
LightColor: TBGRAPixel; //color of the light reflection
|
|
|
|
SpecularFactor, //how much light is reflected (0..1)
|
|
SpecularIndex : Single; //how concentrated reflected light is (positive value)
|
|
|
|
AmbientFactor, //ambiant lighting whereever the point is (0..1)
|
|
DiffusionFactor, //diffusion, i.e. how much pixels are lightened by light source (0..1)
|
|
NegativeDiffusionFactor : Single; //how much hidden surface are darkened (0..1)
|
|
DiffuseSaturation: Boolean; //when diffusion saturates, use light color to show it
|
|
|
|
constructor Create;
|
|
|
|
{ Render the specified map on the destination bitmap with one solid color. Map altitude
|
|
indicate the global height of the map. }
|
|
procedure Draw(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
Color : TBGRAPixel); override;
|
|
|
|
{ Render with a color map of the same size as the height map. Map altitude
|
|
indicate the global height of the map. }
|
|
procedure Draw(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
ColorMap : TBGRACustomBitmap); override;
|
|
|
|
{ Render with a color scanner. Map altitude
|
|
indicate the global height of the map. }
|
|
procedure DrawScan(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
ColorScan : IBGRAScanner); override;
|
|
|
|
{ Draw a cone of the specified color }
|
|
procedure DrawCone(dest: TBGRACustomBitmap; X,Y,Size,Altitude: Integer; Color: TBGRAPixel); overload;
|
|
procedure DrawCone(dest: TBGRACustomBitmap; bounds: TRect; Altitude: Integer; Color: TBGRAPixel); overload;
|
|
|
|
{ Draw a vertical cone of the specified color }
|
|
procedure DrawVerticalCone(dest: TBGRACustomBitmap; bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
|
|
{ Draw an horizontal cylinder of the specified color }
|
|
procedure DrawHorizontalCylinder(dest: TBGRACustomBitmap; bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
|
|
{ Draw a vertical cylinder of the specified color }
|
|
procedure DrawVerticalCylinder(dest: TBGRACustomBitmap; bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
|
|
{ Draw a hemisphere of the specified color }
|
|
procedure DrawSphere(dest: TBGRACustomBitmap; bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
|
|
{ Draw a rectangle of the specified color }
|
|
procedure DrawRectangle(dest: TBGRACustomBitmap; bounds: TRect; Border,Altitude: Integer; Color: TBGRAPixel; RoundCorners: Boolean; Options: TRectangleMapOptions);
|
|
protected
|
|
|
|
procedure DrawMapNormal(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
ColorMap : TBGRACustomBitmap);
|
|
procedure DrawScannerNormal(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
ColorScan : IBGRAScanner);
|
|
procedure DrawColorNormal(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
Color : TBGRAPixel);
|
|
|
|
{$ifdef BGRASSE_AVAILABLE}
|
|
procedure DrawMapSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
ColorMap : TBGRACustomBitmap);
|
|
procedure DrawScannerSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
ColorScan : IBGRAScanner);
|
|
procedure DrawColorSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
Color : TBGRAPixel);
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
{ Create a grayscale height map for a cone (may not be precise enough) }
|
|
function CreateConeMap(size: integer): TBGRABitmap;
|
|
|
|
{ Create a precise height map for a cone (not grayscale anymore but more precise) }
|
|
function CreateConePreciseMap(width,height: integer): TBGRABitmap;
|
|
|
|
{ Create a precise height map for a vertical cone (not grayscale anymore but more precise) }
|
|
function CreateVerticalConePreciseMap(width,height: integer): TBGRABitmap;
|
|
|
|
{ Create a precise height map for a vertical cylinder (not grayscale anymore but more precise) }
|
|
function CreateVerticalCylinderPreciseMap(width,height: integer): TBGRABitmap;
|
|
|
|
{ Create a precise height map for an horizontal cylinder (not grayscale anymore but more precise) }
|
|
function CreateHorizontalCylinderPreciseMap(width,height: integer): TBGRABitmap;
|
|
|
|
{ Create a grayscale height map for a sphere (may not be precise enough) }
|
|
function CreateSphereMap(width,height: integer): TBGRABitmap;
|
|
|
|
{ Create a precise height map for a sphere (not grayscale anymore but more precise) }
|
|
function CreateSpherePreciseMap(width,height: integer): TBGRABitmap;
|
|
|
|
{ Create a rectangle height map with a border }
|
|
function CreateRectangleMap(width,height,border: integer; options: TRectangleMapOptions = []): TBGRABitmap;
|
|
|
|
{ Create a precise height map for a rectangle height map with a border (not grayscale anymore but more precise) }
|
|
function CreateRectanglePreciseMap(width,height,border: integer; options: TRectangleMapOptions = []): TBGRABitmap;
|
|
|
|
{ Create a round rectangle height map with a border }
|
|
function CreateRoundRectangleMap(width,height,border: integer; options: TRectangleMapOptions = []): TBGRABitmap;
|
|
|
|
{ Create a precise height map for a round rectangle height map with a border (not grayscale anymore but more precise) }
|
|
function CreateRoundRectanglePreciseMap(width,height,border: integer; options: TRectangleMapOptions = []): TBGRABitmap;
|
|
|
|
{---------- Perlin Noise -------------}
|
|
{ Random image using a superposition of interpolated random values.
|
|
See : http://wiki.lazarus.freepascal.org/Perlin_Noise
|
|
http://freespace.virgin.net/hugo.elias/models/m_perlin.htm }
|
|
|
|
{ Creates a non-tilable random grayscale image }
|
|
function CreatePerlinNoiseMap(AWidth, AHeight: integer; HorizontalPeriod: Single = 1;
|
|
VerticalPeriod: Single = 1; Exponent: Double = 1; ResampleFilter: TResampleFilter = rfCosine): TBGRABitmap;
|
|
|
|
{ Creates a tilable random grayscale image }
|
|
function CreateCyclicPerlinNoiseMap(AWidth, AHeight: integer; HorizontalPeriod: Single = 1;
|
|
VerticalPeriod: Single = 1; Exponent: Double = 1; ResampleFilter: TResampleFilter = rfCosine): TBGRABitmap;
|
|
|
|
implementation
|
|
|
|
uses GraphType, Types, SysUtils, BGRATextFX; {GraphType unit used by phongdraw.inc}
|
|
|
|
function TextShadow(AWidth, AHeight: Integer; AText: String;
|
|
AFontHeight: Integer; ATextColor, AShadowColor: TBGRAPixel; AOffSetX,
|
|
AOffSetY: Integer; ARadius: Integer; AFontStyle: TFontStyles;
|
|
AFontName: String; AShowText: Boolean): TBGRABitmap;
|
|
begin
|
|
result := BGRATextFX.TextShadow(AWidth,AHeight,AText,AFontHeight,ATextColor,AShadowColor,AOffsetX,AOffsetY,ARadius,AFontStyle,AFontName,AShowText) as TBGRABitmap;
|
|
end;
|
|
|
|
function nGradientInfo(StartColor, StopColor: TBGRAPixel;
|
|
Direction: TGradientDirection; EndPercent: Single): TnGradientInfo;
|
|
begin
|
|
result.StartColor := StartColor;
|
|
result.StopColor := StopColor;
|
|
result.Direction := Direction;
|
|
result.EndPercent := EndPercent;
|
|
end;
|
|
|
|
function DoubleGradientAlphaFill(ARect: TRect; AStart1,AStop1,AStart2,AStop2: TBGRAPixel;
|
|
ADirection1,ADirection2,ADir: TGradientDirection; AValue: Single): TBGRABitmap;
|
|
var
|
|
ABitmap: TBGRABitmap;
|
|
ARect1,ARect2: TRect;
|
|
APoint1,APoint2,APoint3,APoint4: TPointF;
|
|
begin
|
|
Dec(ARect.Right, ARect.Left);
|
|
ARect.Left := 0;
|
|
Dec(ARect.Bottom,ARect.Top);
|
|
ARect.Top := 0;
|
|
|
|
ABitmap := TBGRABitmap.Create(ARect.Right,ARect.Bottom);
|
|
|
|
if AValue <> 0 then ARect1:=ARect;
|
|
if AValue <> 1 then ARect2:=ARect;
|
|
|
|
if ADir = gdVertical then begin
|
|
ARect1.Bottom:=Round(ARect1.Bottom * AValue);
|
|
ARect2.Top:=ARect1.Bottom;
|
|
end
|
|
else if ADir = gdHorizontal then begin
|
|
ARect1.Right:=Round(ARect1.Right * AValue);
|
|
ARect2.Left:=ARect1.Right;
|
|
end;
|
|
if ADirection1 = gdVertical then begin
|
|
APoint1:=PointF(ARect1.Left,ARect1.Top);
|
|
APoint2:=PointF(ARect1.Left,ARect1.Bottom);
|
|
end
|
|
else if ADirection1 = gdHorizontal then begin
|
|
APoint1:=PointF(ARect1.Left,ARect1.Top);
|
|
APoint2:=PointF(ARect1.Right,ARect1.Top);
|
|
end;
|
|
if ADirection2 = gdVertical then begin
|
|
APoint3:=PointF(ARect2.Left,ARect2.Top);
|
|
APoint4:=PointF(ARect2.Left,ARect2.Bottom);
|
|
end
|
|
else if ADirection2 = gdHorizontal then begin
|
|
APoint3:=PointF(ARect2.Left,ARect2.Top);
|
|
APoint4:=PointF(ARect2.Right,ARect2.Top);
|
|
end;
|
|
|
|
if AValue <> 0 then
|
|
ABitmap.GradientFill(ARect1.Left,ARect1.Top,ARect1.Right,ARect1.Bottom,
|
|
AStart1,AStop1,gtLinear,APoint1,APoint2,dmSet,True);
|
|
if AValue <> 1 then
|
|
ABitmap.GradientFill( ARect2.Left,ARect2.Top,ARect2.Right,ARect2.Bottom,
|
|
AStart2,AStop2,gtLinear,APoint3,APoint4,dmSet,True);
|
|
|
|
Result:=ABitmap;
|
|
end;
|
|
|
|
function DoubleGradientAlphaFill(AWidth, AHeight: Integer; AStart1, AStop1,
|
|
AStart2, AStop2: TBGRAPixel; ADirection1, ADirection2,
|
|
ADir: TGradientDirection; AValue: Single): TBGRABitmap;
|
|
begin
|
|
result := DoubleGradientAlphaFill(Rect(0,0,AWidth,AHeight),
|
|
AStart1,AStop1,AStart2,AStop2,
|
|
ADirection1,ADirection2, ADir, AValue);
|
|
end;
|
|
|
|
procedure DoubleGradientAlphaFill(ACanvas: TCanvas; ARect: TRect; AStart1,
|
|
AStop1, AStart2, AStop2: TBGRAPixel; ADirection1, ADirection2,
|
|
ADir: TGradientDirection; AValue: Single);
|
|
var
|
|
bmp: TBGRABitmap;
|
|
begin
|
|
bmp := DoubleGradientAlphaFill(ARect,AStart1,AStop1,AStart2,AStop2,ADirection1,ADirection2,ADir,AValue);
|
|
bmp.Draw(ACanvas,ARect.Left,ARect.Top,not bmp.HasTransparentPixels);
|
|
bmp.Free;
|
|
end;
|
|
|
|
procedure DoubleGradientAlphaFill(ABitmap: TBGRABitmap; ARect: TRect; AStart1,
|
|
AStop1, AStart2, AStop2: TBGRAPixel; ADirection1, ADirection2,
|
|
ADir: TGradientDirection; AValue: Single);
|
|
var
|
|
bmp: TBGRABitmap;
|
|
begin
|
|
bmp := DoubleGradientAlphaFill(ARect,AStart1,AStop1,AStart2,AStop2,ADirection1,ADirection2,ADir,AValue);
|
|
ABitmap.PutImage(ARect.Left,ARect.Top,bmp,dmDrawWithTransparency);
|
|
bmp.Free;
|
|
end;
|
|
|
|
function nGradientAlphaFill(ARect: TRect; ADir: TGradientDirection;
|
|
const AGradient: array of TnGradientInfo): TBGRABitmap;
|
|
var
|
|
i:integer;
|
|
AnRect, OldRect: TRect;
|
|
Point1, Point2: TPointF;
|
|
begin
|
|
Result := TBGRABitmap.Create(ARect.Right-ARect.Left,ARect.Bottom-ARect.Top);
|
|
Dec(ARect.Right, ARect.Left);
|
|
ARect.Left := 0;
|
|
Dec(ARect.Bottom,ARect.Top);
|
|
ARect.Top := 0;
|
|
|
|
OldRect := ARect;
|
|
|
|
if ADir = gdVertical then
|
|
OldRect.Bottom := ARect.Top
|
|
else
|
|
OldRect.Right := ARect.Left;
|
|
|
|
for i := 0 to high(AGradient) do
|
|
begin
|
|
AnRect:=OldRect;
|
|
if ADir = gdVertical then
|
|
begin
|
|
AnRect.Bottom:=Round((ARect.Bottom-ARect.Top) * AGradient[i].endPercent + ARect.Top);
|
|
AnRect.Top:=OldRect.Bottom;
|
|
Point1:=PointF(AnRect.Left,AnRect.Top);
|
|
Point2:=PointF(AnRect.Left,AnRect.Bottom);
|
|
end
|
|
else
|
|
begin
|
|
AnRect.Right:=Round((ARect.Right-ARect.Left) * AGradient[i].endPercent + ARect.Left);
|
|
AnRect.Left:=OldRect.Right;
|
|
Point1:=PointF(AnRect.Left,AnRect.Top);
|
|
Point2:=PointF(AnRect.Right,AnRect.Top);
|
|
end;
|
|
Result.GradientFill(AnRect.Left,AnRect.Top,AnRect.Right,AnRect.Bottom,
|
|
AGradient[i].StartColor,AGradient[i].StopColor,gtLinear,Point1,Point2,dmSet,True);
|
|
OldRect := AnRect;
|
|
end;
|
|
end;
|
|
|
|
function nGradientAlphaFill(AWidth, AHeight: Integer; ADir: TGradientDirection;
|
|
const AGradient: array of TnGradientInfo): TBGRABitmap;
|
|
begin
|
|
result := nGradientAlphaFill(Rect(0,0,AWidth,AHeight),ADir,AGradient);
|
|
end;
|
|
|
|
procedure nGradientAlphaFill(ACanvas: TCanvas; ARect: TRect;
|
|
ADir: TGradientDirection; const AGradient: array of TnGradientInfo);
|
|
var
|
|
bmp: TBGRABitmap;
|
|
begin
|
|
bmp := nGradientAlphaFill(ARect, ADir, AGradient);
|
|
bmp.Draw(ACanvas,ARect.Left,ARect.Top,not bmp.HasTransparentPixels);
|
|
bmp.Free;
|
|
end;
|
|
|
|
procedure nGradientAlphaFill(ABitmap: TBGRABitmap; ARect: TRect;
|
|
ADir: TGradientDirection; const AGradient: array of TnGradientInfo);
|
|
var
|
|
bmp: TBGRABitmap;
|
|
begin
|
|
bmp := nGradientAlphaFill(ARect, ADir, AGradient);
|
|
ABitmap.PutImage(ARect.Left,ARect.Top,bmp,dmDrawWithTransparency);
|
|
bmp.Free;
|
|
end;
|
|
|
|
{ TPhongShading }
|
|
|
|
constructor TPhongShading.Create;
|
|
begin
|
|
//set default values
|
|
LightSourceIntensity := 500;
|
|
LightSourceDistanceTerm := 150;
|
|
LightSourceDistanceFactor := 1;
|
|
LightDestFactor := 1;
|
|
LightColor := BGRAWhite;
|
|
AmbientFactor := 0.3;
|
|
DiffusionFactor := 0.9;
|
|
DiffuseSaturation:= False;
|
|
NegativeDiffusionFactor := 0.1;
|
|
SpecularFactor := 0.6;
|
|
SpecularIndex := 10;
|
|
LightPosition := Point(-100,-100);
|
|
LightPositionZ := 100;
|
|
end;
|
|
|
|
Const
|
|
PhongLightPrecisionSh = 12;
|
|
PhongLightPrecision = 1 shl PhongLightPrecisionSh;
|
|
PhongLightPrecisionDiv2 = PhongLightPrecision shr 1;
|
|
|
|
{------------------ Phong drawing ----------------}
|
|
{ Look for the fastest method available }
|
|
procedure TPhongShading.Draw(dest: TBGRACustomBitmap; map: TBGRACustomBitmap; mapAltitude: integer; ofsX,ofsY: integer;
|
|
Color : TBGRAPixel);
|
|
begin
|
|
{$ifdef BGRASSE_AVAILABLE}
|
|
if UseSSE then
|
|
DrawColorSSE(dest,map,mapAltitude,ofsX,ofsY,Color)
|
|
else
|
|
{$endif}
|
|
DrawColorNormal(dest,map,mapAltitude,ofsX,ofsY,Color);
|
|
end;
|
|
|
|
procedure TPhongShading.Draw(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
|
|
mapAltitude: integer; ofsX, ofsY: integer; ColorMap: TBGRACustomBitmap);
|
|
begin
|
|
{$ifdef BGRASSE_AVAILABLE}
|
|
if UseSSE then
|
|
DrawMapSSE(dest,map,mapAltitude,ofsX,ofsY,ColorMap)
|
|
else
|
|
{$endif}
|
|
DrawMapNormal(dest,map,mapAltitude,ofsX,ofsY,ColorMap);
|
|
end;
|
|
|
|
procedure TPhongShading.DrawScan(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
|
|
mapAltitude: integer; ofsX, ofsY: integer; ColorScan: IBGRAScanner);
|
|
begin
|
|
{$ifdef BGRASSE_AVAILABLE}
|
|
if UseSSE then
|
|
DrawScannerSSE(dest,map,mapAltitude,ofsX,ofsY,ColorScan)
|
|
else
|
|
{$endif}
|
|
DrawScannerNormal(dest,map,mapAltitude,ofsX,ofsY,ColorScan);
|
|
end;
|
|
|
|
{------------------ End of phong drawing ----------------}
|
|
|
|
procedure TPhongShading.DrawCone(dest: TBGRACustomBitmap; X, Y, Size,
|
|
Altitude: Integer; Color: TBGRAPixel);
|
|
var map: TBGRABitmap;
|
|
begin
|
|
map := CreateConePreciseMap(Size,Size);
|
|
Draw(dest,map,Altitude,X,Y,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawCone(dest: TBGRACustomBitmap; bounds: TRect;
|
|
Altitude: Integer; Color: TBGRAPixel);
|
|
var map: TBGRABitmap;
|
|
temp: integer;
|
|
begin
|
|
if Bounds.Right < Bounds.Left then
|
|
begin
|
|
temp := Bounds.Left;
|
|
bounds.Left := bounds.Right;
|
|
Bounds.Right := temp;
|
|
end;
|
|
if Bounds.Bottom < Bounds.Top then
|
|
begin
|
|
temp := Bounds.Bottom;
|
|
bounds.Bottom := bounds.Top;
|
|
Bounds.Top := temp;
|
|
end;
|
|
map := CreateConePreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top);
|
|
Draw(dest,map,Altitude,bounds.Left,bounds.Top,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawVerticalCone(dest: TBGRACustomBitmap;
|
|
bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
var map: TBGRABitmap;
|
|
temp: integer;
|
|
begin
|
|
if Bounds.Right < Bounds.Left then
|
|
begin
|
|
temp := Bounds.Left;
|
|
bounds.Left := bounds.Right;
|
|
Bounds.Right := temp;
|
|
end;
|
|
if Bounds.Bottom < Bounds.Top then
|
|
begin
|
|
temp := Bounds.Bottom;
|
|
bounds.Bottom := bounds.Top;
|
|
Bounds.Top := temp;
|
|
end;
|
|
map := CreateVerticalConePreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top);
|
|
Draw(dest,map,Altitude,bounds.Left,bounds.Top,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawHorizontalCylinder(dest: TBGRACustomBitmap;
|
|
bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
var map: TBGRABitmap;
|
|
temp: integer;
|
|
begin
|
|
if Bounds.Right < Bounds.Left then
|
|
begin
|
|
temp := Bounds.Left;
|
|
bounds.Left := bounds.Right;
|
|
Bounds.Right := temp;
|
|
end;
|
|
if Bounds.Bottom < Bounds.Top then
|
|
begin
|
|
temp := Bounds.Bottom;
|
|
bounds.Bottom := bounds.Top;
|
|
Bounds.Top := temp;
|
|
end;
|
|
map := CreateHorizontalCylinderPreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top);
|
|
Draw(dest,map,Altitude,bounds.Left,bounds.Top,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawVerticalCylinder(dest: TBGRACustomBitmap;
|
|
bounds: TRect; Altitude: Integer; Color: TBGRAPixel);
|
|
var map: TBGRABitmap;
|
|
temp: integer;
|
|
begin
|
|
if Bounds.Right < Bounds.Left then
|
|
begin
|
|
temp := Bounds.Left;
|
|
bounds.Left := bounds.Right;
|
|
Bounds.Right := temp;
|
|
end;
|
|
if Bounds.Bottom < Bounds.Top then
|
|
begin
|
|
temp := Bounds.Bottom;
|
|
bounds.Bottom := bounds.Top;
|
|
Bounds.Top := temp;
|
|
end;
|
|
map := CreateVerticalCylinderPreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top);
|
|
Draw(dest,map,Altitude,bounds.Left,bounds.Top,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawSphere(dest: TBGRACustomBitmap; bounds: TRect;
|
|
Altitude: Integer; Color: TBGRAPixel);
|
|
var map: TBGRABitmap;
|
|
temp: integer;
|
|
begin
|
|
if Bounds.Right < Bounds.Left then
|
|
begin
|
|
temp := Bounds.Left;
|
|
bounds.Left := bounds.Right;
|
|
Bounds.Right := temp;
|
|
end;
|
|
if Bounds.Bottom < Bounds.Top then
|
|
begin
|
|
temp := Bounds.Bottom;
|
|
bounds.Bottom := bounds.Top;
|
|
Bounds.Top := temp;
|
|
end;
|
|
map := CreateSpherePreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top);
|
|
Draw(dest,map,Altitude,bounds.Left,bounds.Top,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawRectangle(dest: TBGRACustomBitmap; bounds: TRect;
|
|
Border,Altitude: Integer; Color: TBGRAPixel; RoundCorners: Boolean; Options: TRectangleMapOptions);
|
|
var map: TBGRABitmap;
|
|
temp: integer;
|
|
begin
|
|
if Bounds.Right < Bounds.Left then
|
|
begin
|
|
temp := Bounds.Left;
|
|
bounds.Left := bounds.Right;
|
|
Bounds.Right := temp;
|
|
end;
|
|
if Bounds.Bottom < Bounds.Top then
|
|
begin
|
|
temp := Bounds.Bottom;
|
|
bounds.Bottom := bounds.Top;
|
|
Bounds.Top := temp;
|
|
end;
|
|
if border > 10 then
|
|
begin
|
|
if RoundCorners then
|
|
map := CreateRoundRectanglePreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top,Border,Options)
|
|
else
|
|
map := CreateRectanglePreciseMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top,Border,Options);
|
|
end else
|
|
begin
|
|
if RoundCorners then
|
|
map := CreateRoundRectangleMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top,Border,Options)
|
|
else
|
|
map := CreateRectangleMap(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top,Border,Options);
|
|
end;
|
|
Draw(dest,map,Altitude,bounds.Left,bounds.Top,Color);
|
|
map.Free;
|
|
end;
|
|
|
|
procedure TPhongShading.DrawMapNormal(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
|
|
mapAltitude: integer; ofsX, ofsY: integer; ColorMap: TBGRACustomBitmap);
|
|
{$I phongdraw.inc }
|
|
|
|
procedure TPhongShading.DrawColorNormal(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
|
|
mapAltitude: integer; ofsX, ofsY: integer; Color: TBGRAPixel);
|
|
{$define PARAM_SIMPLECOLOR}
|
|
{$I phongdraw.inc }
|
|
|
|
procedure TPhongShading.DrawScannerNormal(dest: TBGRACustomBitmap;
|
|
map: TBGRACustomBitmap; mapAltitude: integer; ofsX, ofsY: integer;
|
|
ColorScan: IBGRAScanner);
|
|
{$define PARAM_SCANNER}
|
|
{$I phongdraw.inc }
|
|
|
|
{$ifdef BGRASSE_AVAILABLE}
|
|
procedure TPhongShading.DrawMapSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
|
|
mapAltitude: integer; ofsX, ofsY: integer; ColorMap: TBGRACustomBitmap);
|
|
{$define PARAM_PHONGSSE}
|
|
{$I phongdraw.inc }
|
|
|
|
procedure TPhongShading.DrawColorSSE(dest: TBGRACustomBitmap; map: TBGRACustomBitmap;
|
|
mapAltitude: integer; ofsX, ofsY: integer; Color: TBGRAPixel);
|
|
{$define PARAM_PHONGSSE}
|
|
{$define PARAM_SIMPLECOLOR}
|
|
{$I phongdraw.inc }
|
|
|
|
procedure TPhongShading.DrawScannerSSE(dest: TBGRACustomBitmap;
|
|
map: TBGRACustomBitmap; mapAltitude: integer; ofsX, ofsY: integer;
|
|
ColorScan: IBGRAScanner);
|
|
{$define PARAM_PHONGSSE}
|
|
{$define PARAM_SCANNER}
|
|
{$I phongdraw.inc }
|
|
|
|
{$endif}
|
|
|
|
{************************ maps ***********************************}
|
|
|
|
function CreateConeMap(size: integer): TBGRABitmap;
|
|
var cx,cy,r: single;
|
|
mask: TBGRABitmap;
|
|
begin
|
|
cx := (size-1)/2;
|
|
cy := (size-1)/2;
|
|
r := (size-1)/2;
|
|
result := TBGRABitmap.Create(size,size);
|
|
result.GradientFill(0,0,size,size,BGRAWhite,BGRABlack,gtRadial,PointF(cx,cy),PointF(cx+r,cy),dmSet,False);
|
|
|
|
mask := TBGRABitmap.Create(size,size,BGRABlack);
|
|
mask.FillEllipseAntialias(cx,cy,r,r,BGRAWhite);
|
|
result.ApplyMask(mask);
|
|
mask.Free;
|
|
end;
|
|
|
|
function CreateConePreciseMap(width,height: integer): TBGRABitmap;
|
|
var cx,cy,rx,ry,d: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
mask: TBGRABitmap;
|
|
begin
|
|
result := TBGRABitmap.Create(width,height);
|
|
cx := (width-1)/2;
|
|
cy := (height-1)/2;
|
|
rx := (width-1)/2;
|
|
ry := (height-1)/2;
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
d := sqr((xb-cx)/(rx+1))+sqr((yb-cy)/(ry+1));
|
|
if d >= 1 then
|
|
p^ := BGRAPixelTransparent else
|
|
p^ := MapHeightToBGRA(1-sqrt(d),255);
|
|
inc(p);
|
|
end;
|
|
end;
|
|
//antialiased border
|
|
mask := TBGRABitmap.Create(width,height,BGRABlack);
|
|
mask.FillEllipseAntialias(cx,cy,rx,ry,BGRAWhite);
|
|
result.ApplyMask(mask);
|
|
mask.Free;
|
|
end;
|
|
|
|
function CreateVerticalConePreciseMap(width, height: integer): TBGRABitmap;
|
|
var cx,rx,d,vpos: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
mask: TBGRABitmap;
|
|
begin
|
|
result := TBGRABitmap.Create(width,height);
|
|
if (height=0) or (width=0) then exit;
|
|
cx := (width-1)/2;
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
vpos := (yb+1)/height;
|
|
rx := width/2*vpos;
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
d := sqr((xb-cx)/(rx+1));
|
|
if d >= 1 then
|
|
p^ := BGRAPixelTransparent else
|
|
p^ := MapHeightToBGRA(sqrt(1-d)*vpos,255);
|
|
inc(p);
|
|
end;
|
|
end;
|
|
//antialiased border
|
|
mask := TBGRABitmap.Create(width,height,BGRABlack);
|
|
mask.FillPolyAntialias([PointF(rx,-0.5),PointF(0,height-0.5),PointF(width-0.5,height-0.5)],BGRAWhite);
|
|
result.ApplyMask(mask);
|
|
mask.Free;
|
|
end;
|
|
|
|
function CreateVerticalCylinderPreciseMap(width, height: integer): TBGRABitmap;
|
|
var cx,rx,d: single;
|
|
xb: integer;
|
|
begin
|
|
result := TBGRABitmap.Create(width,height);
|
|
if (height=0) or (width=0) then exit;
|
|
rx := width/2;
|
|
cx := (width-1)/2;
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
d := sqr((xb-cx)/(rx+1));
|
|
result.SetVertLine(xb,0,height-1,MapHeightToBGRA(sqrt(1-d),255));
|
|
end;
|
|
end;
|
|
|
|
function CreateHorizontalCylinderPreciseMap(width, height: integer
|
|
): TBGRABitmap;
|
|
var cy,ry,d: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
c: TBGRAPixel;
|
|
begin
|
|
result := TBGRABitmap.Create(width,height);
|
|
if (height=0) or (width=0) then exit;
|
|
ry := height/2;
|
|
cy := (height-1)/2;
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
d := sqr((yb-cy)/(ry+1));
|
|
c := MapHeightToBGRA(sqrt(1-d),255);
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
p^ := c;
|
|
inc(p);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CreateSphereMap(width,height: integer): TBGRABitmap;
|
|
var cx,cy,rx,ry,d: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
h: integer;
|
|
mask: TBGRABitmap;
|
|
begin
|
|
result := TBGRABitmap.Create(width,height);
|
|
cx := (width-1)/2;
|
|
cy := (height-1)/2;
|
|
rx := (width-1)/2;
|
|
ry := (height-1)/2;
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
d := sqr((xb-cx)/(rx+1))+sqr((yb-cy)/(ry+1));
|
|
if d >= 1 then
|
|
p^ := BGRAPixelTransparent else
|
|
begin
|
|
h := round(sqrt(1-d)*255);
|
|
p^.red := h;
|
|
p^.green := h;
|
|
p^.blue := h;
|
|
p^.alpha := 255;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
end;
|
|
//antialiased border
|
|
mask := TBGRABitmap.Create(width,height,BGRABlack);
|
|
mask.FillEllipseAntialias(cx,cy,rx,ry,BGRAWhite);
|
|
result.ApplyMask(mask);
|
|
mask.Free;
|
|
end;
|
|
|
|
procedure MapBorderLimit(width,height: integer; options: TRectangleMapOptions; var border: integer);
|
|
var maxHoriz,maxVert: integer;
|
|
begin
|
|
if [rmoNoLeftBorder,rmoNoRightBorder] <= options then maxHoriz := border else
|
|
if [rmoNoLeftBorder,rmoNoRightBorder] * options = [] then maxHoriz := width div 2 else
|
|
maxHoriz := width;
|
|
if border > maxHoriz then border := maxHoriz;
|
|
|
|
if [rmoNoTopBorder,rmoNoBottomBorder] <= options then maxVert := border else
|
|
if [rmoNoTopBorder,rmoNoBottomBorder] * options = [] then maxVert := height div 2 else
|
|
maxVert := height;
|
|
if border > maxVert then border := maxVert;
|
|
end;
|
|
|
|
function CreateSpherePreciseMap(width, height: integer): TBGRABitmap;
|
|
var cx,cy,rx,ry,d: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
mask: TBGRABitmap;
|
|
begin
|
|
result := TBGRABitmap.Create(width,height);
|
|
cx := (width-1)/2;
|
|
cy := (height-1)/2;
|
|
rx := (width-1)/2;
|
|
ry := (height-1)/2;
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
d := sqr((xb-cx)/(rx+1))+sqr((yb-cy)/(ry+1));
|
|
if d >= 1 then
|
|
p^ := BGRAPixelTransparent else
|
|
p^ := MapHeightToBGRA(sqrt(1-d),255);
|
|
inc(p);
|
|
end;
|
|
end;
|
|
//antialiased border
|
|
mask := TBGRABitmap.Create(width,height,BGRABlack);
|
|
mask.FillEllipseAntialias(cx,cy,rx,ry,BGRAWhite);
|
|
result.ApplyMask(mask);
|
|
mask.Free;
|
|
end;
|
|
|
|
procedure RectangleMapRemoveCorners(dest: TBGRABitmap; options: TRectangleMapOptions);
|
|
begin
|
|
if [rmoNoLeftBorder,rmoNoTopBorder]*Options = [] then
|
|
begin
|
|
dest.SetPixel(0,0,BGRAPixelTransparent);
|
|
dest.ErasePixel(1,0,128);
|
|
dest.ErasePixel(0,1,128);
|
|
end;
|
|
|
|
if [rmoNoRightBorder,rmoNoTopBorder]*Options = [] then
|
|
begin
|
|
dest.SetPixel(dest.width-1,0,BGRAPixelTransparent);
|
|
dest.ErasePixel(dest.width-2,0,128);
|
|
dest.ErasePixel(dest.width-1,1,128);
|
|
end;
|
|
|
|
if [rmoNoRightBorder,rmoNoBottomBorder]*Options = [] then
|
|
begin
|
|
dest.SetPixel(dest.width-1,dest.height-1,BGRAPixelTransparent);
|
|
dest.ErasePixel(dest.width-2,dest.height-1,128);
|
|
dest.ErasePixel(dest.width-1,dest.height-2,128);
|
|
end;
|
|
|
|
if [rmoNoLeftBorder,rmoNoBottomBorder]*Options = [] then
|
|
begin
|
|
dest.SetPixel(0,dest.height-1,BGRAPixelTransparent);
|
|
dest.ErasePixel(1,dest.height-1,128);
|
|
dest.ErasePixel(0,dest.height-2,128);
|
|
end;
|
|
end;
|
|
|
|
function CreateRectangleMap(width,height,border: integer; options: TRectangleMapOptions = []): TBGRABitmap;
|
|
var xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
h: integer;
|
|
begin
|
|
MapBorderLimit(width,height,options,border);
|
|
|
|
result := TBGRABitmap.Create(width,height);
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
if not (rmoNoLeftBorder in options) and (xb < border) and (yb > xb) and (yb < height-1-xb) then h := xb else
|
|
if not (rmoNoRightBorder in options) and (xb > width-1-border) and (yb > width-1-xb) and (yb < height-1-(width-1-xb)) then h := width-1-xb else
|
|
if not (rmoNoTopBorder in options) and (yb < border) and (xb > yb) and (xb < width-1-yb) then h := yb else
|
|
if not (rmoNoBottomBorder in options) and (yb > height-1-border) and (xb > height-1-yb) and (xb < width-1-(height-1-yb)) then h := height-1-yb else
|
|
if not (rmoNoLeftBorder in options) and (xb < border) then h := xb else
|
|
if not (rmoNoRightBorder in options) and (xb > width-1-border) then h := width-1-xb else
|
|
if not (rmoNoTopBorder in options) and (yb < border) then h := yb else
|
|
if not (rmoNoBottomBorder in options) and (yb > height-1-border) then h := height-1-yb else
|
|
begin
|
|
p^ := BGRAWhite;
|
|
inc(p);
|
|
Continue;
|
|
end;
|
|
|
|
if rmoLinearBorder in options then h := h*256 div border else
|
|
h := round(sin((h+1/2)/border*Pi/2)*255);
|
|
p^.red := h;
|
|
p^.green := h;
|
|
p^.blue := h;
|
|
p^.alpha := 255;
|
|
inc(p);
|
|
end;
|
|
end;
|
|
|
|
RectangleMapRemoveCorners(result,options);
|
|
end;
|
|
|
|
function CreateRectanglePreciseMap(width, height, border: integer;
|
|
options: TRectangleMapOptions): TBGRABitmap;
|
|
var xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
h: single;
|
|
begin
|
|
MapBorderLimit(width,height,options,border);
|
|
|
|
result := TBGRABitmap.Create(width,height);
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
if not (rmoNoLeftBorder in options) and (xb < border) and (yb > xb) and (yb < height-1-xb) then h := xb else
|
|
if not (rmoNoRightBorder in options) and (xb > width-1-border) and (yb > width-1-xb) and (yb < height-1-(width-1-xb)) then h := width-1-xb else
|
|
if not (rmoNoTopBorder in options) and (yb < border) and (xb > yb) and (xb < width-1-yb) then h := yb else
|
|
if not (rmoNoBottomBorder in options) and (yb > height-1-border) and (xb > height-1-yb) and (xb < width-1-(height-1-yb)) then h := height-1-yb else
|
|
if not (rmoNoLeftBorder in options) and (xb < border) then h := xb else
|
|
if not (rmoNoRightBorder in options) and (xb > width-1-border) then h := width-1-xb else
|
|
if not (rmoNoTopBorder in options) and (yb < border) then h := yb else
|
|
if not (rmoNoBottomBorder in options) and (yb > height-1-border) then h := height-1-yb else
|
|
begin
|
|
p^ := BGRAWhite;
|
|
inc(p);
|
|
Continue;
|
|
end;
|
|
|
|
if rmoLinearBorder in options then h := h/border else
|
|
h := sin((h+1/2)/border*Pi/2);
|
|
|
|
p^ := MapHeightToBGRA(h,255);
|
|
|
|
inc(p);
|
|
end;
|
|
end;
|
|
|
|
RectangleMapRemoveCorners(result,options);
|
|
end;
|
|
|
|
function CreateRoundRectangleMap(width,height,border: integer; options: TRectangleMapOptions = []): TBGRABitmap;
|
|
var d: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
h: integer;
|
|
begin
|
|
MapBorderLimit(width,height,options,border);
|
|
|
|
result := TBGRABitmap.Create(width,height);
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
if not (rmoNoLeftBorder in options) and not (rmoNoTopBorder in options) and (xb < border) and (yb < border) then d := border-sqrt(sqr(border-xb)+sqr(border-yb)) else
|
|
if not (rmoNoLeftBorder in options) and not (rmoNoBottomBorder in options) and (xb < border) and (yb > height-1-border) then d := border-sqrt(sqr(border-xb)+sqr(border-(height-1-yb))) else
|
|
if not (rmoNoRightBorder in options) and not (rmoNoTopBorder in options) and (xb > width-1-border) and (yb < border) then d := border-sqrt(sqr(border-(width-1-xb))+sqr(border-yb)) else
|
|
if not (rmoNoRightBorder in options) and not (rmoNoBottomBorder in options) and (xb > width-1-border) and (yb > height-1-border) then d := border-sqrt(sqr(border-(width-1-xb))+sqr(border-(height-1-yb))) else
|
|
if not (rmoNoLeftBorder in options) and (xb < border) then d := xb else
|
|
if not (rmoNoRightBorder in options) and (xb > width-1-border) then d := width-1-xb else
|
|
if not (rmoNoTopBorder in options) and (yb < border) then d := yb else
|
|
if not (rmoNoBottomBorder in options) and (yb > height-1-border) then d := height-1-yb else
|
|
begin
|
|
p^ := BGRAWhite;
|
|
inc(p);
|
|
Continue;
|
|
end;
|
|
|
|
d := (d+1)*border/(border+1);
|
|
|
|
if d < 0 then
|
|
p^ := BGRAPixelTransparent else
|
|
begin
|
|
if rmoLinearBorder in options then h := trunc(d*256/border) else
|
|
h := round(sin((d+1/2)/border*Pi/2)*255);
|
|
|
|
p^.red := h;
|
|
p^.green := h;
|
|
p^.blue := h;
|
|
if d < 1 then p^.alpha := round(d*255) else
|
|
p^.alpha := 255;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CreatePerlinNoiseMap(AWidth, AHeight: integer; HorizontalPeriod: Single;
|
|
VerticalPeriod: Single; Exponent: Double = 1; ResampleFilter: TResampleFilter = rfCosine): TBGRABitmap;
|
|
|
|
procedure AddNoise(frequencyH, frequencyV: integer; amplitude: byte; dest: TBGRABitmap);
|
|
var small,resampled: TBGRABitmap;
|
|
p: PBGRAPixel;
|
|
i: Integer;
|
|
begin
|
|
if (frequencyH = 0) or (frequencyV = 0) then exit;
|
|
small := TBGRABitmap.Create(frequencyH,frequencyV);
|
|
p := small.data;
|
|
for i := 0 to small.NbPixels-1 do
|
|
begin
|
|
p^.red := random(amplitude);
|
|
p^.green := p^.red;
|
|
p^.blue := p^.green;
|
|
p^.alpha := 255;
|
|
inc(p);
|
|
end;
|
|
small.ResampleFilter := ResampleFilter;
|
|
resampled := small.Resample(dest.Width,dest.Height) as TBGRABitmap;
|
|
dest.BlendImage(0,0,resampled,boAdditive);
|
|
resampled.Free;
|
|
small.Free;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
temp: TBGRABitmap;
|
|
|
|
begin
|
|
result := TBGRABitmap.Create(AWidth,AHeight);
|
|
for i := 0 to 5 do
|
|
AddNoise(round(AWidth / HorizontalPeriod / (32 shr i)),round(AHeight / VerticalPeriod / (32 shr i)), round(exp(ln((128 shr i)/128)*Exponent)*128),result);
|
|
|
|
temp := result.FilterNormalize(False) as TBGRABitmap;
|
|
result.Free;
|
|
result := temp;
|
|
|
|
temp := result.FilterBlurRadial(1,rbNormal) as TBGRABitmap;
|
|
result.Free;
|
|
result := temp;
|
|
end;
|
|
|
|
function CreateCyclicPerlinNoiseMap(AWidth, AHeight: integer; HorizontalPeriod: Single = 1;
|
|
VerticalPeriod: Single = 1; Exponent: Double = 1; ResampleFilter: TResampleFilter = rfCosine): TBGRABitmap;
|
|
|
|
procedure AddNoise(frequencyH, frequencyV: integer; amplitude: byte; dest: TBGRABitmap);
|
|
var small,cycled,resampled: TBGRABitmap;
|
|
p: PBGRAPixel;
|
|
i: Integer;
|
|
begin
|
|
if (frequencyH = 0) or (frequencyV = 0) then exit;
|
|
small := TBGRABitmap.Create(frequencyH,frequencyV);
|
|
p := small.data;
|
|
for i := 0 to small.NbPixels-1 do
|
|
begin
|
|
p^.red := random(amplitude);
|
|
p^.green := p^.red;
|
|
p^.blue := p^.green;
|
|
p^.alpha := 255;
|
|
inc(p);
|
|
end;
|
|
cycled := small.GetPart(rect(-2,-2,small.Width+2,small.Height+2)) as TBGRABitmap;
|
|
cycled.ResampleFilter := ResampleFilter;
|
|
resampled := cycled.Resample(round((cycled.Width-1)*(dest.Width/frequencyH)),round((cycled.Height-1)*(dest.Height/frequencyV))) as TBGRABitmap;
|
|
dest.BlendImage(round(-2*(dest.Width/frequencyH)),round(-2*(dest.Height/frequencyV)),resampled,boAdditive);
|
|
resampled.Free;
|
|
cycled.Free;
|
|
small.Free;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
temp: TBGRABitmap;
|
|
|
|
begin
|
|
result := TBGRABitmap.Create(AWidth,AHeight);
|
|
for i := 0 to 5 do
|
|
AddNoise(round(AWidth / HorizontalPeriod / (32 shr i)),round(AHeight / VerticalPeriod / (32 shr i)), round(exp(ln((128 shr i)/128)*Exponent)*128),result);
|
|
|
|
temp := result.FilterNormalize(False) as TBGRABitmap;
|
|
result.Free;
|
|
result := temp;
|
|
|
|
temp := result.FilterBlurRadial(1,rbNormal) as TBGRABitmap;
|
|
result.Free;
|
|
result := temp;
|
|
end;
|
|
|
|
function CreateRoundRectanglePreciseMap(width, height, border: integer;
|
|
options: TRectangleMapOptions): TBGRABitmap;
|
|
var d: single;
|
|
xb,yb: integer;
|
|
p: PBGRAPixel;
|
|
h: single;
|
|
begin
|
|
MapBorderLimit(width,height,options,border);
|
|
|
|
result := TBGRABitmap.Create(width,height);
|
|
for yb := 0 to height-1 do
|
|
begin
|
|
p := result.scanline[yb];
|
|
for xb := 0 to width-1 do
|
|
begin
|
|
if not (rmoNoLeftBorder in options) and not (rmoNoTopBorder in options) and (xb < border) and (yb < border) then d := border-sqrt(sqr(border-xb)+sqr(border-yb)) else
|
|
if not (rmoNoLeftBorder in options) and not (rmoNoBottomBorder in options) and (xb < border) and (yb > height-1-border) then d := border-sqrt(sqr(border-xb)+sqr(border-(height-1-yb))) else
|
|
if not (rmoNoRightBorder in options) and not (rmoNoTopBorder in options) and (xb > width-1-border) and (yb < border) then d := border-sqrt(sqr(border-(width-1-xb))+sqr(border-yb)) else
|
|
if not (rmoNoRightBorder in options) and not (rmoNoBottomBorder in options) and (xb > width-1-border) and (yb > height-1-border) then d := border-sqrt(sqr(border-(width-1-xb))+sqr(border-(height-1-yb))) else
|
|
if not (rmoNoLeftBorder in options) and (xb < border) then d := xb else
|
|
if not (rmoNoRightBorder in options) and (xb > width-1-border) then d := width-1-xb else
|
|
if not (rmoNoTopBorder in options) and (yb < border) then d := yb else
|
|
if not (rmoNoBottomBorder in options) and (yb > height-1-border) then d := height-1-yb else
|
|
begin
|
|
p^ := BGRAWhite;
|
|
inc(p);
|
|
Continue;
|
|
end;
|
|
|
|
d := (d+1)*border/(border+1);
|
|
|
|
if d < 0 then
|
|
p^ := BGRAPixelTransparent else
|
|
begin
|
|
if rmoLinearBorder in options then h := d/border else
|
|
h := sin((d+1/2)/border*Pi/2);
|
|
|
|
if d < 1 then p^:= MapHeightToBGRA(h,round(d*255)) else
|
|
p^ := MapHeightToBGRA(h,255);
|
|
end;
|
|
inc(p);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
|
|
Randomize;
|
|
|
|
end.
|
|
|