From ee6d1b615f76e733231017b3cd20db976201c928 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Wed, 18 Feb 2015 19:19:40 +0100 Subject: [PATCH] * implemented post processing * added Format Lumiance8 --- examples/simple/TextSuiteTest.lpi | 7 +- examples/simple/TextSuiteTest.lpr | 2 +- examples/simple/TextSuiteTest.lps | 450 ++++++++++++++++-------------- examples/simple/uMainForm.pas | 35 ++- utsPostProcess.pas | 261 +++++++++++++++++ utsRendererOpenGL.pas | 4 + utsTextSuite.pas | 347 ++++++++++++++--------- utsTypes.pas | 89 +++++- 8 files changed, 839 insertions(+), 356 deletions(-) create mode 100644 utsPostProcess.pas 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.