unit utsPostProcess; {$IFDEF FPC} {$mode delphi}{$H+} {$ENDIF} 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; fKeepCharSize: Boolean; public procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override; public constructor Create(const aWidth, aStrength: Single; const aColor: TtsColor4f; const aKeepCharSize: 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 {$IFDEF FPC} for chan in mask do begin {$ELSE} for chan := low(TtsColorChannel) to high(TtsColorChannel) do if (chan in mask) then begin {$ENDIF} 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; aChar.GlyphRect := tsRect( aChar.GlyphRect.Left, aChar.GlyphRect.Top, aChar.GlyphRect.Right + 2 * fKernel.SizeX, aChar.GlyphRect.Bottom + 2 * fKernel.SizeY); if fKeepCharSize then begin aChar.GlyphOrigin := tsPosition( aChar.GlyphOrigin.x - fKernel.SizeX, aChar.GlyphOrigin.y + fKernel.SizeY); end else begin aChar.Advance := aChar.Advance + 2 * fKernel.SizeX; aChar.GlyphOrigin := tsPosition( aChar.GlyphOrigin.x, aChar.GlyphOrigin.y + fKernel.SizeY); end; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TtsPostProcessBorder.Create(const aWidth, aStrength: Single; const aColor: TtsColor4f; const aKeepCharSize: Boolean); begin inherited Create; fKernel := TtsKernel2D.Create(aWidth, aStrength); fColor := aColor; fKeepCharSize := aKeepCharSize; 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); aChar.GlyphRect := tsRect( aChar.GlyphRect.Left, aChar.GlyphRect.Top, aChar.GlyphRect.Right + 2 * fKernel.Size, aChar.GlyphRect.Bottom + 2 * fKernel.Size); aChar.GlyphOrigin := tsPosition( aChar.GlyphOrigin.x - tmpX, aChar.GlyphOrigin.y + tmpX); 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.