diff --git a/examples/simple/TextSuiteTest.lpi b/examples/simple/TextSuiteTest.lpi
index 76f6590..ae42065 100644
--- a/examples/simple/TextSuiteTest.lpi
+++ b/examples/simple/TextSuiteTest.lpi
@@ -33,7 +33,7 @@
-
+
@@ -80,6 +80,11 @@
+
+
+
+
+
diff --git a/examples/simple/TextSuiteTest.lpr b/examples/simple/TextSuiteTest.lpr
index 616cd0f..dae9bf7 100644
--- a/examples/simple/TextSuiteTest.lpr
+++ b/examples/simple/TextSuiteTest.lpr
@@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, sysutils, Forms, uMainForm,
- utsFontCreatorGDI, utsUtils, utsTypes, utsTtfUtils, utsTextSuite, utsRendererOpenGL, utsCodePages;
+ utsFontCreatorGDI, utsUtils, utsTypes, utsTtfUtils, utsTextSuite, utsRendererOpenGL, utsCodePages, utsPostProcess;
{$R *.res}
diff --git a/examples/simple/TextSuiteTest.lps b/examples/simple/TextSuiteTest.lps
index 0868121..f44d1ec 100644
--- a/examples/simple/TextSuiteTest.lps
+++ b/examples/simple/TextSuiteTest.lps
@@ -4,13 +4,13 @@
-
+
-
+
@@ -20,20 +20,19 @@
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
@@ -41,65 +40,75 @@
-
-
-
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -112,240 +121,240 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
@@ -355,223 +364,254 @@
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
+
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/simple/uMainForm.pas b/examples/simple/uMainForm.pas
index ab07569..aedf488 100644
--- a/examples/simple/uMainForm.pas
+++ b/examples/simple/uMainForm.pas
@@ -8,7 +8,7 @@ interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, uglcContext, TextSuite, uglcTypes,
- utsTextSuite, utsTypes, utsFontCreatorGDI, utsRendererOpenGL;
+ utsTextSuite, utsTypes, utsFontCreatorGDI, utsRendererOpenGL, utsPostProcess;
type
TMainForm = class(TForm)
@@ -53,6 +53,18 @@ const
procedure TMainForm.FormCreate(Sender: TObject);
var
pf: TglcContextPixelFormatSettings;
+ pp: TtsPostProcessStep;
+ pa: TtsImage;
+const
+ data: array[0..63] of Byte = (
+ $FF, $AA, $88, $44, $44, $88, $AA, $FF,
+ $AA, $88, $44, $22, $22, $44, $88, $AA,
+ $88, $44, $22, $11, $11, $22, $44, $88,
+ $44, $22, $11, $00, $00, $11, $22, $44,
+ $44, $22, $11, $00, $00, $11, $22, $44,
+ $88, $44, $22, $11, $11, $22, $44, $88,
+ $AA, $88, $44, $22, $22, $44, $88, $AA,
+ $FF, $AA, $88, $44, $44, $88, $AA, $FF);
begin
pf := TglcContext.MakePF();
fContext := TglcContext.GetPlatformClass.Create(self, pf);
@@ -67,9 +79,18 @@ begin
tsFontBind(ftsFont);
{$ELSE}
ftsContext := TtsContext.Create;
- ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatAlpha8);
+ ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatRGBA8);
ftsGenerator := TtsFontGeneratorGDI.Create(ftsContext);
- ftsFont1 := ftsGenerator.GetFontByName('Calibri', ftsRenderer, 25, [tsStyleBold, tsStyleItalic], tsAANormal);
+
+ pp := TtsPostProcessFillColor.Create(tsColor4f(0.0, 0.0, 0.0, 1.0), TS_MODES_MODULATE_ALPHA, TS_CHANNELS_RGBA);
+ pp.AddUsageRange(tsUsageInclude, #$0000, #$FFFF);
+ ftsGenerator.AddPostProcessStep(pp);
+
+ pp := TtsPostProcessShadow.Create(3, 0, 2, 2, tsColor4f(1.0, 0.0, 1.0, 0.05));
+ pp.AddUsageRange(tsUsageInclude, #$0000, #$FFFF);
+ ftsGenerator.AddPostProcessStep(pp);
+
+ ftsFont1 := ftsGenerator.GetFontByName('Calibri', ftsRenderer, 100, [tsStyleBold, tsStyleItalic], tsAANormal);
ftsFont2 := ftsGenerator.GetFontByName('Calibri', ftsRenderer, 20, [], tsAANormal);
{$ENDIF}
end;
@@ -113,7 +134,7 @@ begin
fFrameTime := t;
glViewport(0, 0, ClientWidth, ClientHeight);
- glClearColor(0, 0, 0, 0);
+ glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
@@ -123,7 +144,7 @@ begin
glLoadIdentity;
glEnable(GL_BLEND);
- glcBlendFunc(TglcBlendMode.bmAdditiveAlphaBlend);
+ glcBlendFunc(TglcBlendMode.bmAlphaBlend);
{$IFDEF USE_OLD_TS}
tsTextBeginBlock(0, 0, ClientWidth, ClientHeight, TS_ALIGN_BLOCK);
@@ -135,7 +156,7 @@ begin
block.HorzAlign := tsHorzAlignJustify;
block.ChangeFont(ftsFont1);
- block.ChangeColor(tsColor4f(1.0, 0.0, 0.0, 1.0));
+ block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0));
block.TextOutW('L');
block.ChangeFont(ftsFont2);
@@ -143,7 +164,7 @@ begin
block.TextOutW(TEST_STRING + sLineBreak);
block.ChangeFont(ftsFont1);
- block.ChangeColor(tsColor4f(0.0, 1.0, 0.0, 1.0));
+ block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0));
block.TextOutW('L');
block.ChangeFont(ftsFont2);
diff --git a/utsPostProcess.pas b/utsPostProcess.pas
new file mode 100644
index 0000000..9495819
--- /dev/null
+++ b/utsPostProcess.pas
@@ -0,0 +1,261 @@
+unit utsPostProcess;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, utsTextSuite, utsTypes;
+
+type
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ TtsPostProcessFillColor = class(TtsPostProcessStep)
+ private
+ fColor: TtsColor4f;
+ fModes: TtsImageModes;
+ fChannels: TtsColorChannels;
+ protected
+ procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override;
+ public
+ constructor Create(const aColor: TtsColor4f;
+ const aModes: TtsImageModes; const aChannels: TtsColorChannels);
+ end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ TtsPostProcessFillPattern = class(TtsPostProcessStep)
+ private
+ fPattern: TtsImage;
+ fOwnsPattern: Boolean;
+ fX, fY: Integer;
+ fModes: TtsImageModes;
+ fChannels: TtsColorChannels;
+ protected
+ procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override;
+ public
+ constructor Create(const aPattern: TtsImage; const aOwnsPattern: Boolean; const X, Y: Integer;
+ const aModes: TtsImageModes; const aChannels: TtsColorChannels);
+ destructor Destroy; override;
+ end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ TtsPostProcessBorder = class(TtsPostProcessStep)
+ private
+ fKernel: TtsKernel2D;
+ fColor: TtsColor4f;
+ fUpdateCharSize: Boolean;
+ public
+ procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override;
+ public
+ constructor Create(const aWidth, aStrength: Single; const aColor: TtsColor4f;
+ const aUpdateCharSize: Boolean = false);
+ destructor Destroy; override;
+ end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ TtsPostProcessShadow = class(TtsPostProcessStep)
+ private
+ fKernel: TtsKernel1D;
+ fColor: TtsColor4f;
+ fX, fY: Integer;
+ protected
+ procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override;
+ public
+ constructor Create(const aRadius, aStrength: Single; const X, Y: Integer; const aColor: TtsColor4f);
+ destructor Destroy; override;
+ end;
+
+implementation
+
+uses
+ Math;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsPostProcessFillColor///////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsPostProcessFillColor.Execute(const aChar: TtsChar; const aCharImage: TtsImage);
+begin
+ if Assigned(aCharImage) then
+ aCharImage.FillColor(fColor, fChannels, fModes);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsPostProcessFillColor.Create(const aColor: TtsColor4f; const aModes: TtsImageModes; const aChannels: TtsColorChannels);
+begin
+ inherited Create;
+ fColor := aColor;
+ fModes := aModes;
+ fChannels := aChannels;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsPostProcessFillPattern/////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsPostProcessFillPattern.Execute(const aChar: TtsChar; const aCharImage: TtsImage);
+begin
+ if Assigned(aCharImage) then
+ aCharImage.FillPattern(fPattern, fX, fY, fChannels, fModes);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsPostProcessFillPattern.Create(const aPattern: TtsImage; const aOwnsPattern: Boolean; const X,
+ Y: Integer; const aModes: TtsImageModes; const aChannels: TtsColorChannels);
+begin
+ inherited Create;
+ fPattern := aPattern;
+ fOwnsPattern := aOwnsPattern;
+ fX := X;
+ fY := Y;
+ fModes := aModes;
+ fChannels := aChannels;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+destructor TtsPostProcessFillPattern.Destroy;
+begin
+ if fOwnsPattern then
+ FreeAndNil(fPattern);
+ inherited Destroy;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsPostProcessBorder//////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsPostProcessBorder.Execute(const aChar: TtsChar; const aCharImage: TtsImage);
+var
+ orig: TtsImage;
+ x, y: Integer;
+ dst: PByte;
+
+ function BorderLookup: TtsColor4f;
+ var
+ i: Integer;
+ c: TtsColor4f;
+ s: Single;
+ chan: TtsColorChannel;
+ mask: TtsColorChannels;
+ tmpX, tmpY: Integer;
+ begin
+ mask := TS_CHANNELS_RGBA;
+ result := tsColor4f(0, 0, 0, 0);
+ for i := 0 to fKernel.ItemCount-1 do begin
+ tmpX := x + fKernel.Items[i].OffsetX;
+ tmpY := y + fKernel.Items[i].OffsetY;
+ if (tmpX >= 0) and (tmpX < orig.Width) and
+ (tmpY >= 0) and (tmpY < orig.Height) and
+ orig.GetPixelAt(tmpX, tmpY, c) then
+ begin
+ for chan in mask do begin
+ s := c.arr[Integer(chan)] * fColor.arr[Integer(chan)] * fKernel.Items[i].Value;
+ if (s > result.arr[Integer(chan)]) then begin
+ result.arr[Integer(chan)] := s;
+ if (s >= 1.0) then begin
+ Exclude(mask, chan);
+ if (mask = []) then
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+begin
+ if not Assigned(aCharImage) then
+ exit;
+
+ aCharImage.Resize(
+ aCharImage.Width + 2 * fKernel.SizeX,
+ aCharImage.Height + 2 * fKernel.SizeY,
+ fKernel.SizeX, fKernel.SizeY);
+
+ orig := TtsImage.Create;
+ try
+ orig.Assign(aCharImage);
+ aCharImage.FillColor(fColor, TS_CHANNELS_RGBA, TS_MODES_REPLACE_ALL);
+
+ for y := 0 to orig.Height-1 do begin
+ dst := aCharImage.Scanline[y];
+ for x := 0 to orig.Width-1 do
+ tsFormatMap(aCharImage.Format, dst, BorderLookup);
+ end;
+
+ aCharImage.Blend(orig, 0, 0, @tsBlendFundAdditiveAlpha);
+ finally
+ FreeAndNil(orig);
+ end;
+
+ if fUpdateCharSize then begin
+ aChar.GlyphRect := tsRect(
+ aChar.GlyphRect.Left + fKernel.SizeX - fKernel.MidSizeX,
+ aChar.GlyphRect.Top + fKernel.SizeY - fKernel.MidSizeY,
+ aChar.GlyphRect.Right + fKernel.SizeX + fKernel.MidSizeX,
+ aChar.GlyphRect.Bottom + fKernel.SizeY + fKernel.MidSizeY);
+ aChar.GlyphOrigin := tsPosition(
+ aChar.GlyphOrigin.x + fKernel.MidSizeX,
+ aChar.GlyphOrigin.y + fKernel.MidSizeY);
+ aChar.Advance := aChar.Advance + fKernel.MidSizeX ;
+ end;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsPostProcessBorder.Create(const aWidth, aStrength: Single; const aColor: TtsColor4f;
+ const aUpdateCharSize: Boolean);
+begin
+ inherited Create;
+ fKernel := TtsKernel2D.Create(aWidth, aStrength);
+ fColor := aColor;
+ fUpdateCharSize := aUpdateCharSize;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+destructor TtsPostProcessBorder.Destroy;
+begin
+ FreeAndNil(fKernel);
+ inherited Destroy;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsPostProcessShadow//////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsPostProcessShadow.Execute(const aChar: TtsChar; const aCharImage: TtsImage);
+var
+ orig: TtsImage;
+ tmpX, tmpY: Integer;
+begin
+ orig := TtsImage.Create;
+ try
+ orig.Assign(aCharImage);
+ aCharImage.Resize(
+ aCharImage.Width + 2 * fKernel.Size,
+ aCharImage.Height + 2 * fKernel.Size,
+ fKernel.Size, fKernel.Size);
+ aCharImage.FillColor(fColor, TS_CHANNELS_RGBA, TS_MODES_MODULATE_ALPHA);
+ aCharImage.Blur(fKernel, fKernel, [tsChannelAlpha]);
+
+ tmpX := fKernel.Size - fX;
+ tmpY := fKernel.Size - fY;
+ aCharImage.Blend(orig, tmpX, tmpY, @tsBlendFundAlpha);
+ finally
+ FreeAndNil(orig);
+ end;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsPostProcessShadow.Create(const aRadius, aStrength: Single; const X, Y: Integer; const aColor: TtsColor4f);
+begin
+ inherited Create;
+ fKernel := TtsKernel1D.Create(aRadius, aStrength);
+ fX := X;
+ fY := Y;
+ fColor := aColor;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+destructor TtsPostProcessShadow.Destroy;
+begin
+ FreeAndNil(fKernel);
+ inherited Destroy;
+end;
+
+end.
+
diff --git a/utsRendererOpenGL.pas b/utsRendererOpenGL.pas
index e3095ff..8c2b8cf 100644
--- a/utsRendererOpenGL.pas
+++ b/utsRendererOpenGL.pas
@@ -107,6 +107,10 @@ const
( //tsFormatAlpha8
InternalFormat: GL_ALPHA8;
Format: GL_ALPHA;
+ DataFormat: GL_UNSIGNED_BYTE),
+ ( //tsFormatAlpha8
+ InternalFormat: GL_LUMINANCE8;
+ Format: GL_LUMINANCE;
DataFormat: GL_UNSIGNED_BYTE)
);
diff --git a/utsTextSuite.pas b/utsTextSuite.pas
index 86e18fd..879a286 100644
--- a/utsTextSuite.pas
+++ b/utsTextSuite.pas
@@ -19,7 +19,6 @@ type
TtsKernel1DItem = packed record
Offset: Integer;
Value: Single;
- DataOffset: Integer;
end;
TtsKernel1D = class
@@ -30,11 +29,36 @@ type
constructor Create(const aRadius, aStrength: Single);
end;
+ TtsKernel2DItem = packed record
+ OffsetX: Integer;
+ OffsetY: Integer;
+ Value: Double;
+ DataOffset: Integer;
+ end;
+
+ TtsKernel2D = class
+ public
+ SizeX: Integer;
+ SizeY: Integer;
+
+ MidSizeX: Integer;
+ MidSizeY: Integer;
+
+ ValueSum: Double;
+
+ Items: array of TtsKernel2DItem;
+ ItemCount: Integer;
+
+ constructor Create(const aRadius, aStrength: Single);
+ end;
+
TtsImageFunc = procedure(const aImage: TtsImage; X, Y: Integer; var aPixel: TtsColor4f; aArgs: Pointer);
TtsImage = class(TObject)
private
fWidth: Integer;
fHeight: Integer;
+ fDataSize: Integer;
+ fLineSize: Integer;
fFormat: TtsFormat;
fData: Pointer;
@@ -43,14 +67,18 @@ type
function GetScanline(const aIndex: Integer): Pointer;
function GetIsEmpty: Boolean;
- procedure SetData(const aData: Pointer; const aFormat: TtsFormat = tsFormatEmpty; const aWidth: Integer = 0; const aHeight: Integer = 0);
+ procedure SetData(const aData: Pointer; const aFormat: TtsFormat = tsFormatEmpty;
+ const aWidth: Integer = 0; const aHeight: Integer = 0;
+ const aLineSize: Integer = 0; const aDataSize: Integer = 0);
procedure UpdateScanlines;
public
- property IsEmpty: Boolean read GetIsEmpty;
- property Width: Integer read fWidth;
- property Height: Integer read fHeight;
- property Format: TtsFormat read fFormat;
- property Data: Pointer read fData;
+ property IsEmpty: Boolean read GetIsEmpty;
+ property Width: Integer read fWidth;
+ property Height: Integer read fHeight;
+ property LineSize: Integer read fLineSize;
+ property DataSize: Integer read fDataSize;
+ property Format: TtsFormat read fFormat;
+ property Data: Pointer read fData;
property Scanline[const aIndex: Integer]: Pointer read GetScanline;
function GetPixelAt(const x, y: Integer; out aColor: TtsColor4f): Boolean;
@@ -64,7 +92,7 @@ type
procedure FillColor(const aColor: TtsColor4f; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
procedure FillPattern(const aPattern: TtsImage; X, Y: Integer; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
- procedure BlendImage(const aImage: TtsImage; const X, Y: Integer);
+ procedure Blend(const aImage: TtsImage; const X, Y: Integer; const aFunc: TtsBlendFunc);
procedure Blur(const aHorzKernel, aVertKernel: TtsKernel1D; const aChannelMask: TtsColorChannels);
procedure AddResizingBorder;
@@ -159,9 +187,10 @@ type
fExcludeCharRange: TList;
procedure ClearList(const aList: TList);
+ protected
+ procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); virtual; abstract;
public
function IsInRange(const aCharCode: WideChar): Boolean;
- procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); virtual; abstract;
procedure AddUsageRange(const aUsage: TtsFontProcessStepUsage; const aStartChar, aEndChar: WideChar);
procedure AddUsageChars(const aUsage: TtsFontProcessStepUsage; aChars: PWideChar);
@@ -464,6 +493,116 @@ begin
end;
end;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsKernel2D///////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsKernel2D.Create(const aRadius, aStrength: Single);
+var
+ tmpStrenght: Double;
+ tmpRadius: Double;
+ tmpValue: Double;
+ sqrRadius: Double;
+ x, y, w, h: Integer;
+
+ function CalcValue(const aIndex: Double): Double;
+ begin
+ result := max(0, Abs(aIndex) - tmpStrenght);
+ result := Sqr(result * tmpRadius) / sqrRadius;
+ result := Exp(-result);
+ end;
+
+ procedure CalcSize(var aSize, aMidSize: Integer);
+ begin
+ aSize := 0;
+ aMidSize := 0;
+ while CalcValue(aSize) > 0.5 do begin
+ inc(aSize, 1);
+ inc(aMidSize, 1);
+ end;
+ while CalcValue(aSize) > 0.001 do
+ Inc(aSize, 1);
+ end;
+
+ procedure SetItem(const x, y: Integer);
+ begin
+ with Items[(SizeY + y) * w + (SizeX + x)] do begin
+ OffsetX := x;
+ OffsetY := y;
+ Value := tmpValue;
+ end;
+ end;
+
+ procedure QuickSort(l, r: Integer);
+ var
+ _l, _r: Integer;
+ p, t: TtsKernel2DItem;
+ begin
+ repeat
+ _l := l;
+ _r := r;
+ p := Items[(l + r) shr 1];
+
+ repeat
+ while (Items[_l].Value > p.Value) do
+ inc(_l, 1);
+
+ while (Items[_r].Value < p.Value) do
+ dec(_r, 1);
+
+ if (_l <= _r) then begin
+ t := Items[_l];
+ Items[_l] := Items[_r];
+ Items[_r] := t;
+ inc(_l, 1);
+ dec(_r, 1);
+ end;
+ until (_l > _r);
+
+ if (l < _r) then
+ QuickSort(l, _r);
+
+ l := _l;
+ until (_l >= r);
+ end;
+
+begin
+ inherited Create;
+
+ tmpStrenght := Min(aRadius - 1.0, aRadius * aStrength);
+ tmpRadius := aRadius - tmpStrenght;
+ sqrRadius := sqr(tmpRadius) * sqr(tmpRadius);
+
+ CalcSize(SizeX, MidSizeX);
+ CalcSize(SizeY, MidSizeY);
+
+ ValueSum := 0.0;
+ w := 2 * SizeX + 1;
+ h := 2 * SizeY + 1;
+ ItemCount := w * h;
+ SetLength(Items, ItemCount);
+
+ for y := 0 to SizeY do begin
+ for x := 0 to SizeX do begin
+ tmpValue := CalcValue(sqrt(Sqr(x) + Sqr(y)));
+
+ SetItem( x, y);
+ SetItem( x, -y);
+ SetItem(-x, -y);
+ SetItem(-x, y);
+
+ ValueSum := ValueSum + tmpValue;
+ if (x > 0) and (y > 0) then
+ ValueSum := ValueSum + tmpValue;
+ end;
+ end;
+
+ QuickSort(0, ItemCount-1);
+
+ while (Items[ItemCount-1].Value < 0.001) do
+ dec(ItemCount, 1);
+ SetLength(Items, ItemCount);
+end;
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TtsImage//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -485,7 +624,8 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TtsImage.SetData(const aData: Pointer; const aFormat: TtsFormat; const aWidth: Integer; const aHeight: Integer);
+procedure TtsImage.SetData(const aData: Pointer; const aFormat: TtsFormat; const aWidth: Integer;
+ const aHeight: Integer; const aLineSize: Integer; const aDataSize: Integer);
begin
fHasScanlines := false;
if Assigned(fData) then
@@ -493,28 +633,30 @@ begin
fData := aData;
if Assigned(fData) then begin
- fWidth := aWidth;
- fHeight := aHeight;
- fFormat := aFormat;
+ fWidth := aWidth;
+ fHeight := aHeight;
+ fFormat := aFormat;
+ fLineSize := aLineSize;
+ fDataSize := aDataSize;
end else begin
- fWidth := 0;
- fHeight := 0;
- fFormat := tsFormatEmpty;
+ fWidth := 0;
+ fHeight := 0;
+ fLineSize := 0;
+ fDataSize := 0;
+ fFormat := tsFormatEmpty;
end;
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TtsImage.UpdateScanlines;
var
- i, LineSize: Integer;
+ i: Integer;
tmp: PByte;
begin
- LineSize := fWidth * tsFormatSize(fFormat);
- LineSize := LineSize + ((4 - (LineSize mod 4)) mod 4);
SetLength(fScanlines, fHeight);
for i := 0 to fHeight-1 do begin
tmp := fData;
- inc(tmp, i * LineSize);
+ inc(tmp, i * fLineSize);
fScanlines[i] := tmp;
end;
fHasScanlines := true;
@@ -537,27 +679,25 @@ end;
procedure TtsImage.Assign(const aImage: TtsImage);
var
ImgData: Pointer;
- ImgSize, LineSize: Integer;
begin
- LineSize := aImage.Width * tsFormatSize(aImage.Format);
- LineSize := LineSize + ((4 - (LineSize mod 4)) mod 4);
- ImgSize := LineSize * aImage.Height;
- GetMem(ImgData, ImgSize);
+ GetMem(ImgData, aImage.DataSize);
if Assigned(ImgData) then
- Move(aImage.Data, ImgData, ImgSize);
- SetData(ImgData, aImage.Format, aImage.Width, aImage.Height);
+ Move(aImage.Data^, ImgData^, aImage.DataSize);
+ SetData(ImgData, aImage.Format, aImage.Width, aImage.Height, aImage.LineSize, aImage.DataSize);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TtsImage.CreateEmpty(const aFormat: TtsFormat; const aWidth, aHeight: Integer);
var
ImgData: PByte;
- LineSize: Integer;
+ lSize, dSize: Integer;
begin
- LineSize := aWidth * tsFormatSize(aFormat);
- LineSize := LineSize + ((4 - (LineSize mod 4)) mod 4);
- ImgData := AllocMem(aHeight * LineSize);
- SetData(ImgData, aFormat, aWidth, aHeight);
+ lSize := aWidth * tsFormatSize(aFormat);
+ lSize := lSize + ((4 - (lSize mod 4)) mod 4);
+ dSize := aHeight * lSize;
+ ImgData := AllocMem(dSize);
+ FillByte(ImgData^, dSize, 0);
+ SetData(ImgData, aFormat, aWidth, aHeight, lSize, dSize);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -582,7 +722,7 @@ end;
procedure TtsImage.Resize(const aNewWidth, aNewHeight, X, Y: Integer);
var
ImgData: PByte;
- PixSize, LineSize, ImageSize, OrgLineSize: Integer;
+ pSize, lSize, dSize: Integer;
src, dst: PByte;
YStart, YEnd, YPos, XStart, XEnd: Integer;
@@ -592,14 +732,14 @@ begin
exit;
end;
- PixSize := tsFormatSize(Format);
- LineSize := PixSize * aNewWidth;
- ImageSize := LineSize * aNewHeight;
- OrgLineSize := PixSize * Width;
+ pSize := tsFormatSize(Format);
+ lSize := pSize * aNewWidth;
+ lSize := lSize + ((4 - (lSize mod 4)) mod 4);
+ dSize := lSize * aNewHeight;
- GetMem(ImgData, ImageSize);
+ GetMem(ImgData, dSize);
try
- FillChar(ImgData^, ImageSize, 0);
+ FillChar(ImgData^, dSize, 0);
// positions
YStart := Max(0, Y);
@@ -610,16 +750,16 @@ begin
// copy data
for YPos := YStart to YEnd -1 do begin
dst := ImgData;
- Inc(dst, LineSize * YPos + PixSize * XStart);
+ Inc(dst, lSize * YPos + pSize * XStart);
src := fData;
- Inc(src, OrgLineSize * (YPos - Y) + PixSize * (XStart - X));
+ Inc(src, fLineSize * (YPos - Y) + pSize * (XStart - X));
- Move(src^, dst^, (XEnd - XStart) * PixSize);
+ Move(src^, dst^, (XEnd - XStart) * pSize);
end;
// assign
- SetData(ImgData, Format, aNewWidth, aNewHeight);
+ SetData(ImgData, Format, aNewWidth, aNewHeight, lSize, dSize);
except
FreeMem(ImgData);
end;
@@ -663,20 +803,22 @@ end;
procedure TtsImage.FillColor(const aColor: TtsColor4f; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
var
x, y: Integer;
- p: PByte;
+ rp, wp: PByte;
c: TtsColor4f;
ch: TtsColorChannel;
i: Integer;
begin
for y := 0 to Height-1 do begin
- p := Scanline[y];
+ rp := Scanline[y];
+ wp := rp;
for x := 0 to Width-1 do begin
- tsFormatUnmap(Format, p, c);
+ tsFormatUnmap(Format, rp, c);
for i := 0 to 3 do begin
ch := TtsColorChannel(i);
if (ch in aChannelMask) then
c.arr[i] := IMAGE_MODE_FUNCTIONS[aModes[ch]](aColor.arr[i], c.arr[i]);
end;
+ tsFormatMap(Format, wp, c);
end;
end;
end;
@@ -708,7 +850,7 @@ begin
end;
tmp := dst;
- tsFormatUnmap(Format, src, cSrc);
+ tsFormatUnmap(aPattern.Format, src, cSrc);
tsFormatUnmap(Format, tmp, cDst);
for i := 0 to 3 do begin
ch := TtsColorChannel(i);
@@ -722,97 +864,30 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TtsImage.BlendImage(const aImage: TtsImage; const X, Y: Integer);
+procedure TtsImage.Blend(const aImage: TtsImage; const X, Y: Integer; const aFunc: TtsBlendFunc);
var
- _x, _y, i: Integer;
- c, cOver, cUnder: TtsColor4f;
- FaqOver, FaqUnder: Single;
- UnionRect, IntersectRect: TtsRect;
- NewSize: TtsPosition;
- ImgSize: Integer;
- ImgData, dst, src, pOver, pUnder: PByte;
- tmpLines: array of Pointer;
-begin
- UnionRect := tsRect(
- Min(X, 0),
- Min(Y, 0),
- Max(X + aImage.Width, Width),
- Max(Y + aImage.Height, Height));
- IntersectRect := tsRect(
- Max(X, 0),
- Max(Y, 0),
- Min(X + aImage.Width, Width),
- Min(X + aImage.Height, Height));
- NewSize := tsPosition(
- UnionRect.Right - UnionRect.Left,
- UnionRect.Bottom - UnionRect.Top);
-
- ImgSize := NewSize.x * NewSize.y * tsFormatSize(Format);
- GetMem(ImgData, ImgSize);
- try
- FillByte(ImgData^, ImgSize, $00);
-
- // temporary scanlines
- SetLength(tmpLines, NewSize.y);
- for _y := 0 to NewSize.y-1 do begin
- tmpLines[_y] := ImgData;
- inc(tmpLines[_y], _y * NewSize.y);
- end;
-
- // copy data from underlaying image
- for _y := 0 to Height-1 do begin
- src := Scanline[_y];
- dst := tmpLines[_y - UnionRect.Top];
- dec(dst, UnionRect.Left);
- for _x := 0 to Width-1 do begin
- dst^ := src^;
- inc(src);
- inc(dst);
- end;
- end;
-
- // copy data from overlaying image
- for _y := 0 to aImage.Height-1 do begin
- src := aImage.Scanline[_y];
- dst := tmpLines[_y + y - UnionRect.Top];
- inc(dst, X - UnionRect.Left);
- for _x := 0 to Width-1 do begin
- dst^ := src^;
- inc(src);
- inc(dst);
- end;
- end;
-
- // blend overlapped
- for _y := IntersectRect.Top to IntersectRect.Bottom-1 do begin
- pOver := aImage.Scanline[_y - Min(IntersectRect.Top, UnionRect.Top)];
- inc(pOver, IntersectRect.Left - UnionRect.Left);
-
- pUnder := Scanline[_y - Min(IntersectRect.Top, 0)];
- inc(pUnder, IntersectRect.Left);
-
- dst := tmpLines[_y - Min(Y, 0)];
- inc(dst, IntersectRect.Left - Min(X, 0));
-
- for _x := IntersectRect.Left to IntersectRect.Right-1 do begin
- tsFormatUnmap(aImage.Format, pOver, cOver);
- tsFormatUnmap(Format, pUnder, cUnder);
- c.a := cOver.a + cUnder.a * (1 - cOver.a);
- if (c.a > 0) then begin
- FaqUnder := (cUnder.a * (1 - cOver.a)) / c.a;
- FaqOver := cOver.a / c.a;
- for i := 0 to 2 do
- c.arr[i] := cOver.arr[i] * FaqOver + cUnder.arr[i] * FaqUnder;
- end else begin
- c.r := 0;
- c.g := 0;
- c.b := 0;
- end;
- tsFormatMap(Format, dst, c);
- end;
+ _x, _y, x1, x2, y1, y2: Integer;
+ src, dst, tmp: PByte;
+ srcColor, dstColor: TtsColor4f;
+ srcPixelSize, dstPixelSize: Integer;
+begin
+ x1 := Max(X, 0);
+ x2 := Min(X + aImage.Width , Width);
+ y1 := Max(Y, 0);
+ y2 := Min(Y + aImage.Height, Height);
+ srcPixelSize := tsFormatSize(aImage.Format);
+ dstPixelSize := tsFormatSize(Format);
+ for _y := y1 to y2-1 do begin
+ src := aImage.Scanline[_y - min(y1, y)];
+ dst := Scanline[_y];
+ inc(src, (x1 - x) * srcPixelSize);
+ inc(dst, x1 * dstPixelSize);
+ tmp := dst;
+ for _x := x1 to x2-1 do begin
+ tsFormatUnmap(aImage.Format, src, srcColor);
+ tsFormatUnmap( Format, dst, dstColor);
+ tsFormatMap(aImage.Format, tmp, aFunc(srcColor, dstColor));
end;
- except
- FreeMem(ImgData);
end;
end;
diff --git a/utsTypes.pas b/utsTypes.pas
index 5d3efe1..69854eb 100644
--- a/utsTypes.pas
+++ b/utsTypes.pas
@@ -77,7 +77,8 @@ type
tsFormatEmpty,
tsFormatRGBA8,
tsFormatLumAlpha8,
- tsFormatAlpha8);
+ tsFormatAlpha8,
+ tsFormatLum8);
TtsAntiAliasing = (
tsAANone,
@@ -168,7 +169,18 @@ type
LineSpacing: Integer;
end;
+ TtsBlendFunc = function(const aSrc, aDst: TtsColor4f): TtsColor4f;
+
+const
+ TS_CHANNELS_RGB: TtsColorChannels = [tsChannelRed, tsChannelGreen, tsChannelBlue];
+ TS_CHANNELS_RGBA: TtsColorChannels = [tsChannelRed, tsChannelGreen, tsChannelBlue, tsChannelAlpha];
+
+ TS_MODES_REPLACE_ALL: TtsImageModes = (tsModeReplace, tsModeReplace, tsModeReplace, tsModeReplace);
+ TS_MODES_MODULATE_ALL: TtsImageModes = (tsModeModulate, tsModeModulate, tsModeModulate, tsModeModulate);
+ TS_MODES_MODULATE_ALPHA: TtsImageModes = (tsModeReplace, tsModeReplace, tsModeReplace, tsModeModulate);
+
function tsColor4f(r, g, b, a: Single): TtsColor4f;
+function tsModes(r, g, b, a: TtsImageMode): TtsImageModes;
function tsRect(const l, t, r, b: Integer): TtsRect;
function tsPosition(const x, y: Integer): TtsPosition;
@@ -180,8 +192,15 @@ function tsImageModeFuncIgnore(const aSource, aDest: Single): Single;
function tsImageModeFuncReplace(const aSource, aDest: Single): Single;
function tsImageModeFuncModulate(const aSource, aDest: Single): Single;
+function tsBlendFundAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f;
+function tsBlendFundAdditive(const aSrc, aDst: TtsColor4f): TtsColor4f;
+function tsBlendFundAdditiveAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f;
+
implementation
+uses
+ Math;
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function tsColor4f(r, g, b, a: Single): TtsColor4f;
begin
@@ -191,6 +210,15 @@ begin
result.a := a;
end;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function tsModes(r, g, b, a: TtsImageMode): TtsImageModes;
+begin
+ result[tsChannelRed] := r;
+ result[tsChannelGreen] := g;
+ result[tsChannelBlue] := b;
+ result[tsChannelAlpha] := a;
+end;
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function tsRect(const l, t, r, b: Integer): TtsRect;
begin
@@ -214,6 +242,7 @@ begin
tsFormatRGBA8: result := 4;
tsFormatLumAlpha8: result := 2;
tsFormatAlpha8: result := 1;
+ tsFormatLum8: result := 1;
else
result := 0;
end;
@@ -228,19 +257,31 @@ begin
case aFormat of
tsFormatRGBA8: begin
for i := 0 to 3 do begin
- aData^ := Trunc($FF * aColor.arr[i]);
+ aData^ := Trunc($FF * min(aColor.arr[i], 1.0));
inc(aData);
end;
end;
tsFormatLumAlpha8: begin
- s := 0.30 * aColor.r + 0.59 * aColor.g + 0.11 * aColor.b;
- aData^ := Trunc($FF * s); inc(aData);
- aData^ := Trunc($FF * aColor.a); inc(aData);
+ s := 0.30 * min(aColor.r, 1.0) +
+ 0.59 * min(aColor.g, 1.0) +
+ 0.11 * min(aColor.b, 1.0);
+ aData^ := Trunc($FF * s);
+ inc(aData);
+ aData^ := Trunc($FF * min(aColor.a, 1.0));
+ inc(aData);
end;
tsFormatAlpha8: begin
- aData^ := Trunc($FF * aColor.a);
+ aData^ := Trunc($FF * min(aColor.a, 1.0));
+ inc(aData);
+ end;
+
+ tsFormatLum8: begin
+ s := 0.30 * min(aColor.r, 1.0) +
+ 0.59 * min(aColor.g, 1.0) +
+ 0.11 * min(aColor.b, 1.0);
+ aData^ := Trunc($FF * s);
inc(aData);
end;
end;
@@ -275,6 +316,14 @@ begin
aColor.a := aData^ / $FF;
inc(aData);
end;
+
+ tsFormatLum8: begin
+ aColor.r := aData^ / $FF;
+ aColor.g := aData^ / $FF;
+ aColor.b := aData^ / $FF;
+ aColor.a := 1.0;
+ inc(aData);
+ end;
end;
end;
@@ -296,5 +345,33 @@ begin
result := aSource * aDest;
end;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function tsBlendFundAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f;
+var
+ i: Integer;
+begin
+ for i := 0 to 2 do
+ result.arr[i] := aSrc.arr[i] * aSrc.a + aDst.arr[i] * (1 - aSrc.a);
+ result.a := aSrc.a + aDst.a * (1 - aSrc.a);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function tsBlendFundAdditive(const aSrc, aDst: TtsColor4f): TtsColor4f;
+var
+ i: Integer;
+begin
+ for i := 0 to 3 do
+ result.arr[i] := aSrc.arr[i] + aDst.arr[i];
+end;
+
+function tsBlendFundAdditiveAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f;
+var
+ i: Integer;
+begin
+ for i := 0 to 2 do
+ result.arr[i] := aSrc.arr[i] * aSrc.a + aDst.arr[i];
+ result.a := aDst.a;
+end;
+
end.