Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

2264 rindas
70 KiB

  1. unit utsTextSuite;
  2. {$IFDEF FPC}
  3. {$mode delphi}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils, contnrs, math, syncobjs,
  8. utsTypes, utsUtils;
  9. type
  10. TtsImage = class;
  11. TtsFont = class;
  12. TtsFontGenerator = class;
  13. TtsRenderer = class;
  14. TtsContext = class;
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  16. TtsKernel1DItem = packed record
  17. Offset: Integer;
  18. Value: Single;
  19. end;
  20. TtsKernel1D = class
  21. public
  22. Size: Integer;
  23. Items: array of TtsKernel1DItem;
  24. ItemCount: Integer;
  25. constructor Create(const aRadius, aStrength: Single);
  26. end;
  27. TtsKernel2DItem = packed record
  28. OffsetX: Integer;
  29. OffsetY: Integer;
  30. Value: Double;
  31. DataOffset: Integer;
  32. end;
  33. TtsKernel2D = class
  34. public
  35. SizeX: Integer;
  36. SizeY: Integer;
  37. MidSizeX: Integer;
  38. MidSizeY: Integer;
  39. ValueSum: Double;
  40. Items: array of TtsKernel2DItem;
  41. ItemCount: Integer;
  42. constructor Create(const aRadius, aStrength: Single);
  43. end;
  44. TtsImageFunc = procedure(const aImage: TtsImage; X, Y: Integer; var aPixel: TtsColor4f; aArgs: Pointer);
  45. TtsImage = class(TObject)
  46. private
  47. fWidth: Integer;
  48. fHeight: Integer;
  49. fDataSize: Integer;
  50. fLineSize: Integer;
  51. fFormat: TtsFormat;
  52. fData: Pointer;
  53. fHasScanlines: Boolean;
  54. fScanlines: array of Pointer;
  55. function GetScanline(const aIndex: Integer): Pointer;
  56. function GetIsEmpty: Boolean;
  57. procedure SetData(const aData: Pointer; const aFormat: TtsFormat = tsFormatEmpty;
  58. const aWidth: Integer = 0; const aHeight: Integer = 0;
  59. const aLineSize: Integer = 0; const aDataSize: Integer = 0);
  60. procedure UpdateScanlines;
  61. public
  62. property IsEmpty: Boolean read GetIsEmpty;
  63. property Width: Integer read fWidth;
  64. property Height: Integer read fHeight;
  65. property LineSize: Integer read fLineSize;
  66. property DataSize: Integer read fDataSize;
  67. property Format: TtsFormat read fFormat;
  68. property Data: Pointer read fData;
  69. property Scanline[const aIndex: Integer]: Pointer read GetScanline;
  70. function GetPixelAt(const x, y: Integer; out aColor: TtsColor4f): Boolean;
  71. procedure Assign(const aImage: TtsImage);
  72. procedure CreateEmpty(const aFormat: TtsFormat; const aWidth, aHeight: Integer);
  73. procedure LoadFromFunc(const aFunc: TtsImageFunc; const aArgs: Pointer);
  74. procedure Resize(const aNewWidth, aNewHeight, X, Y: Integer);
  75. procedure FindMinMax(out aRect: TtsRect);
  76. procedure FillColor(const aColor: TtsColor4f; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
  77. procedure FillPattern(const aPattern: TtsImage; X, Y: Integer; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
  78. procedure Blend(const aImage: TtsImage; const X, Y: Integer; const aFunc: TtsBlendFunc);
  79. procedure Blur(const aHorzKernel, aVertKernel: TtsKernel1D; const aChannelMask: TtsColorChannels);
  80. constructor Create;
  81. destructor Destroy; override;
  82. end;
  83. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  84. TtsCharRenderRef = class(TObject);
  85. TtsChar = class(TObject)
  86. private
  87. fCharCode: WideChar;
  88. fGlyphOrigin: TtsPosition;
  89. fGlyphRect: TtsRect;
  90. fAdvance: Integer;
  91. fRenderRef: TtsCharRenderRef;
  92. public
  93. property CharCode: WideChar read fCharCode;
  94. property GlyphOrigin: TtsPosition read fGlyphOrigin write fGlyphOrigin;
  95. property GlyphRect: TtsRect read fGlyphRect write fGlyphRect;
  96. property Advance: Integer read fAdvance write fAdvance;
  97. property RenderRef: TtsCharRenderRef read fRenderRef write fRenderRef;
  98. constructor Create(const aCharCode: WideChar);
  99. end;
  100. TtsFontCharArray = packed record
  101. Chars: array [Byte] of TtsChar;
  102. CharCount: Byte;
  103. end;
  104. PtsFontCharArray = ^TtsFontCharArray;
  105. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  106. TtsFont = class(TObject)
  107. private
  108. fRenderer: TtsRenderer;
  109. fGenerator: TtsFontGenerator;
  110. fProperties: TtsFontProperties;
  111. fCharSpacing: Integer;
  112. fTabWidth: Integer;
  113. fLineSpacing: Single;
  114. fChars: array[Byte] of PtsFontCharArray;
  115. fCreateChars: Boolean;
  116. function GetChar(const aCharCode: WideChar): TtsChar;
  117. procedure AddChar(const aCharCode: WideChar; const aChar: TtsChar); overload;
  118. protected
  119. {%H-}constructor Create(const aRenderer: TtsRenderer; const aGenerator: TtsFontGenerator; const aProperties: TtsFontProperties);
  120. public
  121. property CreateChars: Boolean read fCreateChars write fCreateChars;
  122. property Char[const aCharCode: WideChar]: TtsChar read GetChar;
  123. property Renderer: TtsRenderer read fRenderer;
  124. property Generator: TtsFontGenerator read fGenerator;
  125. property Properties: TtsFontProperties read fProperties;
  126. property CharSpacing: Integer read fCharSpacing write fCharSpacing;
  127. property TabWidth: Integer read fTabWidth write fTabWidth;
  128. property LineSpacing: Single read fLineSpacing write fLineSpacing;
  129. function AddChar(const aCharCode: WideChar): TtsChar; overload;
  130. procedure AddCharRange(const aCharCodeBeg, aCharCodeEnd: WideChar);
  131. procedure RemoveChar(const aCharCode: WideChar);
  132. procedure ClearChars;
  133. function GetTextWidthW(aText: PWideChar): Integer;
  134. function GetTextWidthA(aText: PAnsiChar): Integer;
  135. procedure GetTextMetric(out aMetric: TtsTextMetric);
  136. destructor Destroy; override;
  137. end;
  138. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  139. TtsPostProcessStepRange = record
  140. StartChar: WideChar;
  141. EndChar: WideChar;
  142. end;
  143. PtsPostProcessStepRange = ^TtsPostProcessStepRange;
  144. TtsFontProcessStepUsage = (
  145. tsUsageInclude,
  146. tsUsageExclude);
  147. TtsPostProcessStep = class(TObject)
  148. private
  149. fIncludeCharRange: TList;
  150. fExcludeCharRange: TList;
  151. procedure ClearList(const aList: TList);
  152. protected
  153. procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); virtual; abstract;
  154. public
  155. function IsInRange(const aCharCode: WideChar): Boolean;
  156. procedure AddUsageRange(const aUsage: TtsFontProcessStepUsage; const aStartChar, aEndChar: WideChar);
  157. procedure AddUsageChars(const aUsage: TtsFontProcessStepUsage; aChars: PWideChar);
  158. procedure ClearIncludeRange;
  159. procedure ClearExcludeRange;
  160. constructor Create;
  161. destructor Destroy; override;
  162. end;
  163. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  164. TtsFontGenerator = class(TObject)
  165. private
  166. fContext: TtsContext;
  167. fFonts: TObjectList;
  168. fPostProcessSteps: TObjectList;
  169. function GetPostProcessStepCount: Integer;
  170. function GetPostProcessStep(const aIndex: Integer): TtsPostProcessStep;
  171. procedure DrawLine(const aChar: TtsChar; const aCharImage: TtsImage; aLinePosition, aLineSize: Integer);
  172. procedure DoPostProcess(const aChar: TtsChar; const aCharImage: TtsImage);
  173. protected
  174. procedure RegisterFont(const aFont: TtsFont);
  175. procedure UnregisterFont(const aFont: TtsFont);
  176. function GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; virtual; abstract;
  177. procedure GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage); virtual; abstract;
  178. public
  179. property Context: TtsContext read fContext;
  180. property PostProcessStepCount: Integer read GetPostProcessStepCount;
  181. property PostProcessStep[const aIndex: Integer]: TtsPostProcessStep read GetPostProcessStep;
  182. function GenerateChar(const aCharCode: WideChar; const aFont: TtsFont; const aRenderer: TtsRenderer): TtsChar;
  183. function AddPostProcessStep(const aStep: TtsPostProcessStep): TtsPostProcessStep;
  184. function InsertPostProcessStep(const aIndex: Integer; const aStep: TtsPostProcessStep): TtsPostProcessStep;
  185. procedure DeletePostProcessStep(const aIndex: Integer);
  186. procedure ClearPostProcessSteps;
  187. constructor Create(const aContext: TtsContext);
  188. destructor Destroy; override;
  189. end;
  190. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  191. TtsLineItemType = (
  192. tsItemTypeUnknown,
  193. tsItemTypeFont,
  194. tsItemTypeColor,
  195. tsItemTypeText,
  196. tsItemTypeSpace,
  197. tsItemTypeLineBreak,
  198. tsItemTypeTab,
  199. tsItemTypeSpacing);
  200. PtsLineItem = ^TtsLineItem;
  201. TtsLineItem = packed record
  202. Next: PtsLineItem;
  203. Prev: PtsLineItem;
  204. ItemType: TtsLineItemType;
  205. case TtsLineItemType of
  206. tsItemTypeFont: (
  207. Font: TtsFont
  208. );
  209. tsItemTypeColor: (
  210. Color: TtsColor4f;
  211. );
  212. tsItemTypeText, tsItemTypeSpace: (
  213. Text: PWideChar; // text of this item
  214. TextWidth: Integer; // width of text (in pixel)
  215. );
  216. tsItemTypeSpacing: (
  217. Spacing: Integer;
  218. );
  219. tsItemTypeTab: (
  220. TabWidth: Integer; // with of tab (in pixel)
  221. );
  222. end;
  223. TtsLineFlag = (
  224. tsLastItemIsSpace, // is set if the last item was a space item
  225. tsMetaValid, // is set if the line meta data is valid
  226. tsAutoLineBreak // is set if the linebreak was set automatically
  227. );
  228. TtsLineFlags = set of TtsLineFlag;
  229. PtsBlockLine = ^TtsBlockLine;
  230. TtsBlockLine = packed record
  231. Next: PtsBlockLine;
  232. First: PtsLineItem;
  233. Last: PtsLineItem;
  234. Flags: TtsLineFlags;
  235. meta: packed record
  236. Width: Integer; // absolut width of this line
  237. Height: Integer; // absolute height of this line
  238. Spacing: Integer; // spacing between lines
  239. Ascent: Integer; // text ascent
  240. SpaceCount: Integer; // number of words in this line
  241. end;
  242. end;
  243. TtsBlockFlag = (
  244. tsBlockFlagWordWrap
  245. );
  246. TtsBlockFlags = set of TtsBlockFlag;
  247. TtsClipping = (
  248. tsClipNone, // no clipping
  249. tsClipWordBorder, // draw all words that have at least one pixel inside the box
  250. tsClipCharBorder, // draw all chars that have at least one pixel inside the box
  251. tsClipWordComplete, // draw all words that are completly inside the box
  252. tsClipCharComplete // draw all chars that are completly inside the box
  253. );
  254. TtsTextBlock = class(TObject)
  255. private
  256. fRenderer: TtsRenderer;
  257. fTop: Integer;
  258. fLeft: Integer;
  259. fWidth: Integer;
  260. fHeight: Integer;
  261. fFlags: TtsBlockFlags;
  262. fVertAlign: TtsVertAlignment;
  263. fHorzAlign: TtsHorzAlignment;
  264. fClipping: TtsClipping;
  265. fCurrentColor: TtsColor4f;
  266. fCurrentFont: TtsFont;
  267. fFirstLine: PtsBlockLine;
  268. fLastLine: PtsBlockLine;
  269. function GetRect: TtsRect;
  270. function PushLineItem(const aItem: PtsLineItem): Boolean;
  271. procedure PushSpacing(const aWidth: Integer);
  272. procedure FreeLineItem(var aItem: PtsLineItem);
  273. procedure FreeLineItems(var aItem: PtsLineItem);
  274. procedure FreeLines(var aItem: PtsBlockLine);
  275. function SplitText(aText: PWideChar): PtsLineItem;
  276. function SplitIntoLines(aItem: PtsLineItem): Boolean;
  277. procedure TrimSpaces(const aLine: PtsBlockLine);
  278. procedure UpdateLineMeta(const aLine: PtsBlockLine);
  279. protected
  280. property Lines: PtsBlockLine read fFirstLine;
  281. procedure PushNewLine;
  282. {%H-}constructor Create(const aRenderer: TtsRenderer; const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags);
  283. public
  284. property Renderer: TtsRenderer read fRenderer;
  285. property CurrentColor: TtsColor4f read fCurrentColor;
  286. property CurrentFont: TtsFont read fCurrentFont;
  287. property Rect: TtsRect read GetRect;
  288. property Width: Integer read fWidth;
  289. property Height: Integer read fHeight;
  290. property Flags: TtsBlockFlags read fFlags;
  291. property Top: Integer read fTop write fTop;
  292. property Left: Integer read fLeft write fLeft;
  293. property VertAlign: TtsVertAlignment read fVertAlign write fVertAlign;
  294. property HorzAlign: TtsHorzAlignment read fHorzAlign write fHorzAlign;
  295. property Clipping: TtsClipping read fClipping write fClipping;
  296. procedure ChangeFont(const aFont: TtsFont);
  297. procedure ChangeColor(const aColor: TtsColor4f);
  298. function GetActualBlockHeight: Integer;
  299. procedure TextOutA(const aText: PAnsiChar);
  300. procedure TextOutW(const aText: PWideChar);
  301. destructor Destroy; override;
  302. end;
  303. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  304. TtsRenderer = class(TObject)
  305. private
  306. fContext: TtsContext;
  307. fFormat: TtsFormat;
  308. fSaveImages: Boolean;
  309. fRenderCS: TCriticalSection;
  310. fBlocks: TObjectList;
  311. procedure RegisterBlock(const aBlock: TtsTextBlock);
  312. procedure UnregisterBlock(const aBlock: TtsTextBlock);
  313. protected
  314. function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; virtual; abstract;
  315. procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); virtual; abstract;
  316. procedure BeginRender; virtual;
  317. procedure EndRender; virtual;
  318. procedure SetDrawPos(const X, Y: Integer); virtual; abstract;
  319. function GetDrawPos: TtsPosition; virtual; abstract;
  320. procedure MoveDrawPos(const X, Y: Integer); virtual; abstract;
  321. procedure SetColor(const aColor: TtsColor4f); virtual; abstract;
  322. procedure Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer = 0); virtual; abstract;
  323. public
  324. property Context: TtsContext read fContext;
  325. property Format: TtsFormat read fFormat;
  326. property SaveImages: Boolean read fSaveImages write fSaveImages;
  327. function BeginBlock(const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags): TtsTextBlock;
  328. procedure EndBlock(var aBlock: TtsTextBlock);
  329. constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
  330. destructor Destroy; override;
  331. end;
  332. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  333. TtsContext = class(TObject)
  334. private
  335. fCodePage: TtsCodePage;
  336. fCodePageDefault: WideChar;
  337. protected
  338. fRenderers: TObjectList;
  339. fGenerators: TObjectList;
  340. private
  341. procedure RegisterRenderer(const aRenderer: TtsRenderer);
  342. procedure UnregisterRenderer(const aRenderer: TtsRenderer);
  343. procedure RegisterGenerator(const aGenerator: TtsFontGenerator);
  344. procedure UnregisterGenerator(const aGenerator: TtsFontGenerator);
  345. public
  346. property CodePage: TtsCodePage read fCodePage write fCodePage;
  347. property CodePageDefault: WideChar read fCodePageDefault write fCodePageDefault;
  348. function AnsiToWide(const aText: PAnsiChar): PWideChar;
  349. constructor Create;
  350. destructor Destroy; override;
  351. end;
  352. EtsException = class(Exception);
  353. EtsRenderer = class(EtsException);
  354. EtsOutOfRange = class(EtsException)
  355. public
  356. constructor Create(const aMin, aMax, aIndex: Integer);
  357. end;
  358. const
  359. IMAGE_MODES_REPLACE: TtsImageModes = (tsModeReplace, tsModeReplace, tsModeReplace, tsModeReplace);
  360. IMAGE_MODES_NORMAL: TtsImageModes = (tsModeReplace, tsModeReplace, tsModeReplace, tsModeModulate);
  361. COLOR_CHANNELS_RGB: TtsColorChannels = [tsChannelRed, tsChannelGreen, tsChannelBlue];
  362. COLOR_CHANNELS_RGBA: TtsColorChannels = [tsChannelRed, tsChannelGreen, tsChannelBlue, tsChannelAlpha];
  363. implementation
  364. const
  365. IMAGE_MODE_FUNCTIONS: array[TtsImageMode] of TtsImageModeFunc = (
  366. tsImageModeFuncIgnore,
  367. tsImageModeFuncReplace,
  368. tsImageModeFuncModulate);
  369. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  370. //TtsKernel1D///////////////////////////////////////////////////////////////////////////////////////////////////////////
  371. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  372. constructor TtsKernel1D.Create(const aRadius, aStrength: Single);
  373. var
  374. TempRadius, SQRRadius, TempStrength, TempValue: Double;
  375. Idx: Integer;
  376. function CalcValue(const aIndex: Integer): Single;
  377. var
  378. Temp: Double;
  379. begin
  380. Temp := Max(0, Abs(aIndex) - TempStrength);
  381. Temp := Sqr(Temp * TempRadius) / SQRRadius;
  382. result := Exp(-Temp);
  383. end;
  384. begin
  385. inherited Create;
  386. // calculate new radius and strength
  387. TempStrength := Min(aRadius - 1, aRadius * aStrength);
  388. TempRadius := aRadius - TempStrength;
  389. SQRRadius := sqr(TempRadius) * sqr(TempRadius);
  390. // caluculating size of the kernel
  391. Size := Round(TempRadius);
  392. while CalcValue(Size) > 0.001 do
  393. Inc(Size);
  394. Size := Size -1;
  395. ItemCount := Size * 2 +1;
  396. SetLength(Items, ItemCount);
  397. // calculate Value (yes thats right. there is no -1)
  398. for Idx := 0 to Size do begin
  399. TempValue := CalcValue(Idx);
  400. with Items[Size + Idx] do begin
  401. Offset := Idx;
  402. Value := TempValue;
  403. end;
  404. with Items[Size - Idx] do begin
  405. Offset := -Idx;
  406. Value := TempValue;
  407. end;
  408. end;
  409. end;
  410. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  411. //TtsKernel2D///////////////////////////////////////////////////////////////////////////////////////////////////////////
  412. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  413. constructor TtsKernel2D.Create(const aRadius, aStrength: Single);
  414. var
  415. tmpStrenght: Double;
  416. tmpRadius: Double;
  417. tmpValue: Double;
  418. sqrRadius: Double;
  419. x, y, w, h: Integer;
  420. function CalcValue(const aIndex: Double): Double;
  421. begin
  422. result := max(0, Abs(aIndex) - tmpStrenght);
  423. result := Sqr(result * tmpRadius) / sqrRadius;
  424. result := Exp(-result);
  425. end;
  426. procedure CalcSize(var aSize, aMidSize: Integer);
  427. begin
  428. aSize := 0;
  429. aMidSize := 0;
  430. while CalcValue(aSize) > 0.5 do begin
  431. inc(aSize, 1);
  432. inc(aMidSize, 1);
  433. end;
  434. while CalcValue(aSize) > 0.001 do
  435. Inc(aSize, 1);
  436. end;
  437. procedure SetItem(const x, y: Integer);
  438. begin
  439. with Items[(SizeY + y) * w + (SizeX + x)] do begin
  440. OffsetX := x;
  441. OffsetY := y;
  442. Value := tmpValue;
  443. end;
  444. end;
  445. procedure QuickSort(l, r: Integer);
  446. var
  447. _l, _r: Integer;
  448. p, t: TtsKernel2DItem;
  449. begin
  450. repeat
  451. _l := l;
  452. _r := r;
  453. p := Items[(l + r) shr 1];
  454. repeat
  455. while (Items[_l].Value > p.Value) do
  456. inc(_l, 1);
  457. while (Items[_r].Value < p.Value) do
  458. dec(_r, 1);
  459. if (_l <= _r) then begin
  460. t := Items[_l];
  461. Items[_l] := Items[_r];
  462. Items[_r] := t;
  463. inc(_l, 1);
  464. dec(_r, 1);
  465. end;
  466. until (_l > _r);
  467. if (l < _r) then
  468. QuickSort(l, _r);
  469. l := _l;
  470. until (_l >= r);
  471. end;
  472. begin
  473. inherited Create;
  474. tmpStrenght := Min(aRadius - 1.0, aRadius * aStrength);
  475. tmpRadius := aRadius - tmpStrenght;
  476. sqrRadius := sqr(tmpRadius) * sqr(tmpRadius);
  477. CalcSize(SizeX, MidSizeX);
  478. CalcSize(SizeY, MidSizeY);
  479. ValueSum := 0.0;
  480. w := 2 * SizeX + 1;
  481. h := 2 * SizeY + 1;
  482. ItemCount := w * h;
  483. SetLength(Items, ItemCount);
  484. for y := 0 to SizeY do begin
  485. for x := 0 to SizeX do begin
  486. tmpValue := CalcValue(sqrt(Sqr(x) + Sqr(y)));
  487. SetItem( x, y);
  488. SetItem( x, -y);
  489. SetItem(-x, -y);
  490. SetItem(-x, y);
  491. ValueSum := ValueSum + tmpValue;
  492. if (x > 0) and (y > 0) then
  493. ValueSum := ValueSum + tmpValue;
  494. end;
  495. end;
  496. QuickSort(0, ItemCount-1);
  497. while (Items[ItemCount-1].Value < 0.001) do
  498. dec(ItemCount, 1);
  499. SetLength(Items, ItemCount);
  500. end;
  501. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  502. //TtsImage//////////////////////////////////////////////////////////////////////////////////////////////////////////////
  503. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  504. function TtsImage.GetScanline(const aIndex: Integer): Pointer;
  505. begin
  506. if not fHasScanlines then
  507. UpdateScanlines;
  508. if fHasScanlines and (aIndex >= 0) and (aIndex <= High(fScanlines)) then
  509. result := fScanlines[aIndex]
  510. else
  511. result := nil;
  512. end;
  513. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  514. function TtsImage.GetIsEmpty: Boolean;
  515. begin
  516. result := not Assigned(fData);
  517. end;
  518. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  519. procedure TtsImage.SetData(const aData: Pointer; const aFormat: TtsFormat; const aWidth: Integer;
  520. const aHeight: Integer; const aLineSize: Integer; const aDataSize: Integer);
  521. begin
  522. fHasScanlines := false;
  523. if Assigned(fData) then
  524. FreeMemory(fData);
  525. fData := aData;
  526. if Assigned(fData) then begin
  527. fWidth := aWidth;
  528. fHeight := aHeight;
  529. fFormat := aFormat;
  530. fLineSize := aLineSize;
  531. fDataSize := aDataSize;
  532. end else begin
  533. fWidth := 0;
  534. fHeight := 0;
  535. fLineSize := 0;
  536. fDataSize := 0;
  537. fFormat := tsFormatEmpty;
  538. end;
  539. end;
  540. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  541. procedure TtsImage.UpdateScanlines;
  542. var
  543. i: Integer;
  544. tmp: PByte;
  545. begin
  546. SetLength(fScanlines, fHeight);
  547. for i := 0 to fHeight-1 do begin
  548. tmp := fData;
  549. inc(tmp, i * fLineSize);
  550. fScanlines[i] := tmp;
  551. end;
  552. fHasScanlines := true;
  553. end;
  554. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  555. function TtsImage.GetPixelAt(const x, y: Integer; out aColor: TtsColor4f): Boolean;
  556. var
  557. p: PByte;
  558. begin
  559. result := (x >= 0) and (x < Width) and (y >= 0) and (y < Height);
  560. if result then begin
  561. p := Scanline[y];
  562. inc(p, x * tsFormatSize(Format));
  563. tsFormatUnmap(Format, p, aColor);
  564. end;
  565. end;
  566. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  567. procedure TtsImage.Assign(const aImage: TtsImage);
  568. var
  569. ImgData: Pointer;
  570. begin
  571. GetMem(ImgData, aImage.DataSize);
  572. if Assigned(ImgData) then
  573. Move(aImage.Data^, ImgData^, aImage.DataSize);
  574. SetData(ImgData, aImage.Format, aImage.Width, aImage.Height, aImage.LineSize, aImage.DataSize);
  575. end;
  576. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  577. procedure TtsImage.CreateEmpty(const aFormat: TtsFormat; const aWidth, aHeight: Integer);
  578. var
  579. ImgData: PByte;
  580. lSize, dSize: Integer;
  581. begin
  582. lSize := aWidth * tsFormatSize(aFormat);
  583. lSize := lSize + ((4 - (lSize mod 4)) mod 4);
  584. dSize := aHeight * lSize;
  585. ImgData := AllocMem(dSize);
  586. FillChar(ImgData^, dSize, #0);
  587. SetData(ImgData, aFormat, aWidth, aHeight, lSize, dSize);
  588. end;
  589. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  590. procedure TtsImage.LoadFromFunc(const aFunc: TtsImageFunc; const aArgs: Pointer);
  591. var
  592. X, Y: Integer;
  593. c: TtsColor4f;
  594. p, tmp: PByte;
  595. begin
  596. for Y := 0 to Height - 1 do begin
  597. p := ScanLine[Y];
  598. for X := 0 to Width - 1 do begin
  599. tmp := p;
  600. tsFormatUnmap(fFormat, tmp, c);
  601. aFunc(Self, X, Y, c, aArgs);
  602. tsFormatMap(fFormat, p, c);
  603. end;
  604. end;
  605. end;
  606. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  607. procedure TtsImage.Resize(const aNewWidth, aNewHeight, X, Y: Integer);
  608. var
  609. ImgData: PByte;
  610. pSize, lSize, dSize: Integer;
  611. src, dst: PByte;
  612. YStart, YEnd, YPos, XStart, XEnd: Integer;
  613. begin
  614. if (aNewHeight = 0) or (aNewWidth = 0) then begin
  615. SetData(nil);
  616. exit;
  617. end;
  618. pSize := tsFormatSize(Format);
  619. lSize := pSize * aNewWidth;
  620. lSize := lSize + ((4 - (lSize mod 4)) mod 4);
  621. dSize := lSize * aNewHeight;
  622. GetMem(ImgData, dSize);
  623. try
  624. FillChar(ImgData^, dSize, 0);
  625. // positions
  626. YStart := Max(0, Y);
  627. YEnd := Min(aNewHeight, Y + Height);
  628. XStart := Max(0, X);
  629. XEnd := Min(aNewWidth, X + Width);
  630. // copy data
  631. for YPos := YStart to YEnd -1 do begin
  632. dst := ImgData;
  633. Inc(dst, lSize * YPos + pSize * XStart);
  634. src := fData;
  635. Inc(src, fLineSize * (YPos - Y) + pSize * (XStart - X));
  636. Move(src^, dst^, (XEnd - XStart) * pSize);
  637. end;
  638. // assign
  639. SetData(ImgData, Format, aNewWidth, aNewHeight, lSize, dSize);
  640. except
  641. FreeMem(ImgData);
  642. end;
  643. end;
  644. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  645. procedure TtsImage.FindMinMax(out aRect: TtsRect);
  646. var
  647. X, Y: Integer;
  648. c: TtsColor4f;
  649. p: PByte;
  650. begin
  651. aRect.Top := -1;
  652. aRect.Left := -1;
  653. aRect.Right := -1;
  654. aRect.Bottom := -1;
  655. // Search for MinMax
  656. for Y := 0 to Height-1 do begin
  657. p := ScanLine[Y];
  658. for X := 0 to Width-1 do begin
  659. tsFormatUnmap(Format, p, c);
  660. if c.a > 0 then begin
  661. if (X < aRect.Left) or (aRect.Left = -1) then
  662. aRect.Left := X;
  663. if (X+1 > aRect.Right) or (aRect.Right = -1) then
  664. aRect.Right := X+1;
  665. if (Y < aRect.Top) or (aRect.Top = -1) then
  666. aRect.Top := Y;
  667. if (Y+1 > aRect.Bottom) or (aRect.Bottom = -1) then
  668. aRect.Bottom := Y+1;
  669. end;
  670. end;
  671. end;
  672. end;
  673. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  674. procedure TtsImage.FillColor(const aColor: TtsColor4f; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
  675. var
  676. x, y: Integer;
  677. rp, wp: PByte;
  678. c: TtsColor4f;
  679. ch: TtsColorChannel;
  680. i: Integer;
  681. begin
  682. for y := 0 to Height-1 do begin
  683. rp := Scanline[y];
  684. wp := rp;
  685. for x := 0 to Width-1 do begin
  686. tsFormatUnmap(Format, rp, c);
  687. for i := 0 to 3 do begin
  688. ch := TtsColorChannel(i);
  689. if (ch in aChannelMask) then
  690. c.arr[i] := IMAGE_MODE_FUNCTIONS[aModes[ch]](aColor.arr[i], c.arr[i]);
  691. end;
  692. tsFormatMap(Format, wp, c);
  693. end;
  694. end;
  695. end;
  696. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  697. procedure TtsImage.FillPattern(const aPattern: TtsImage; X, Y: Integer; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes);
  698. var
  699. _x, _y, posX, i: Integer;
  700. src, dst, tmp: PByte;
  701. cSrc, cDst: TtsColor4f;
  702. ch: TtsColorChannel;
  703. begin
  704. if x < 0 then
  705. x := Random(aPattern.Width);
  706. if y < 0 then
  707. y := Random(aPattern.Height);
  708. for _y := 0 to Height-1 do begin
  709. src := aPattern.Scanline[(y + _y) mod aPattern.Height];
  710. dst := Scanline[_y];
  711. inc(src, x);
  712. posX := x;
  713. for _x := 0 to Width-1 do begin
  714. if (posX >= aPattern.Width) then begin
  715. src := aPattern.Scanline[(y + _y) mod aPattern.Height];
  716. posX := 0;
  717. end;
  718. tmp := dst;
  719. tsFormatUnmap(aPattern.Format, src, cSrc);
  720. tsFormatUnmap(Format, tmp, cDst);
  721. for i := 0 to 3 do begin
  722. ch := TtsColorChannel(i);
  723. if (ch in aChannelMask) then
  724. cDst.arr[i] := IMAGE_MODE_FUNCTIONS[aModes[ch]](cSrc.arr[i], cDst.arr[i]);
  725. end;
  726. tsFormatMap(Format, dst, cDst);
  727. inc(posX);
  728. end;
  729. end;
  730. end;
  731. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  732. procedure TtsImage.Blend(const aImage: TtsImage; const X, Y: Integer; const aFunc: TtsBlendFunc);
  733. var
  734. _x, _y, x1, x2, y1, y2: Integer;
  735. src, dst, tmp: PByte;
  736. srcColor, dstColor: TtsColor4f;
  737. srcPixelSize, dstPixelSize: Integer;
  738. begin
  739. x1 := Max(X, 0);
  740. x2 := Min(X + aImage.Width , Width);
  741. y1 := Max(Y, 0);
  742. y2 := Min(Y + aImage.Height, Height);
  743. srcPixelSize := tsFormatSize(aImage.Format);
  744. dstPixelSize := tsFormatSize(Format);
  745. for _y := y1 to y2-1 do begin
  746. src := aImage.Scanline[_y - min(y1, y)];
  747. dst := Scanline[_y];
  748. inc(src, (x1 - x) * srcPixelSize);
  749. inc(dst, x1 * dstPixelSize);
  750. tmp := dst;
  751. for _x := x1 to x2-1 do begin
  752. tsFormatUnmap(aImage.Format, src, srcColor);
  753. tsFormatUnmap( Format, dst, dstColor);
  754. tsFormatMap(aImage.Format, tmp, aFunc(srcColor, dstColor));
  755. end;
  756. end;
  757. end;
  758. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  759. procedure TtsImage.Blur(const aHorzKernel, aVertKernel: TtsKernel1D; const aChannelMask: TtsColorChannels);
  760. var
  761. tmpImage: TtsImage;
  762. procedure DoBlur(const aSrc, aDst: TtsImage; const aKernel: TtsKernel1D; const ShiftX, ShiftY: Integer);
  763. var
  764. x, y, i, j: Integer;
  765. src, dst: PByte;
  766. v: Single;
  767. c, tmp: TtsColor4f;
  768. begin
  769. for y := 0 to Height-1 do begin
  770. src := aSrc.Scanline[y];
  771. dst := aDst.Scanline[y];
  772. for x := 0 to Width-1 do begin
  773. // read color and clear channels
  774. v := 0;
  775. tsFormatUnmap(aSrc.Format, src, c);
  776. for i := 0 to 3 do
  777. if (TtsColorChannel(i) in aChannelMask) then
  778. c.arr[i] := 0;
  779. // do blur
  780. for i := 0 to aKernel.ItemCount-1 do with aKernel.Items[i] do begin
  781. if aSrc.GetPixelAt(x + Offset * ShiftX, y + Offset * ShiftY, tmp) then begin
  782. for j := 0 to 3 do begin
  783. if (TtsColorChannel(j) in aChannelMask) then
  784. c.arr[j] := c.arr[j] + tmp.arr[j] * Value;
  785. end;
  786. v := v + Value;
  787. end;
  788. end;
  789. // calc final color and write
  790. for i := 0 to 3 do
  791. if (TtsColorChannel(i) in aChannelMask) then
  792. c.arr[i] := c.arr[i] / v;
  793. tsFormatMap(aDst.Format, dst, c);
  794. end;
  795. end;
  796. end;
  797. begin
  798. tmpImage := TtsImage.Create;
  799. try
  800. tmpImage.CreateEmpty(Format, Width, Height);
  801. tmpImage.FillColor(tsColor4f(1, 1, 1, 0), COLOR_CHANNELS_RGBA, IMAGE_MODES_REPLACE);
  802. DoBlur(self, tmpImage, aHorzKernel, 1, 0);
  803. DoBlur(tmpImage, self, aVertKernel, 0, 1);
  804. finally
  805. FreeAndNil(tmpImage);
  806. end;
  807. end;
  808. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  809. constructor TtsImage.Create;
  810. begin
  811. inherited Create;
  812. SetData(nil);
  813. end;
  814. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  815. destructor TtsImage.Destroy;
  816. begin
  817. SetData(nil);
  818. inherited Destroy;
  819. end;
  820. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  821. //TtsChar///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  822. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  823. constructor TtsChar.Create(const aCharCode: WideChar);
  824. begin
  825. inherited Create;
  826. fCharCode := aCharCode;
  827. fGlyphOrigin := tsPosition(0, 0);
  828. fGlyphRect := tsRect(0, 0, 0, 0);
  829. fAdvance := 0;
  830. fRenderRef := nil;
  831. end;
  832. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  833. //TtsFont///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  834. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  835. function TtsFont.GetChar(const aCharCode: WideChar): TtsChar;
  836. var
  837. Chars: PtsFontCharArray;
  838. begin
  839. Chars := fChars[(Ord(aCharCode) shr 8) and $FF];
  840. if Assigned(Chars) then
  841. result := Chars^.Chars[Ord(aCharCode) and $FF]
  842. else
  843. result := nil;
  844. end;
  845. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  846. procedure TtsFont.AddChar(const aCharCode: WideChar; const aChar: TtsChar);
  847. var
  848. h, l: Integer;
  849. Chars: PtsFontCharArray;
  850. begin
  851. h := (Ord(aCharCode) shr 8) and $FF;
  852. Chars := fChars[h];
  853. if not Assigned(Chars) then begin
  854. New(Chars);
  855. FillChar(Chars^, SizeOf(Chars^), 0);
  856. fChars[h] := Chars;
  857. end;
  858. if Assigned(Chars) then begin
  859. l := Ord(aCharCode) and $FF;
  860. Chars^.Chars[l] := aChar;
  861. inc(Chars^.CharCount);
  862. end;
  863. end;
  864. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  865. constructor TtsFont.Create(const aRenderer: TtsRenderer; const aGenerator: TtsFontGenerator; const aProperties: TtsFontProperties);
  866. begin
  867. inherited Create;
  868. fRenderer := aRenderer;
  869. fGenerator := aGenerator;
  870. fProperties := aProperties;
  871. fCharSpacing := 0;
  872. fTabWidth := 1;
  873. fLineSpacing := 0.0;
  874. fCreateChars := true;
  875. fGenerator.RegisterFont(self);
  876. end;
  877. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  878. function TtsFont.AddChar(const aCharCode: WideChar): TtsChar;
  879. begin
  880. result := GetChar(aCharCode);
  881. if not Assigned(result) and fCreateChars then begin
  882. result := fGenerator.GenerateChar(aCharCode, self, fRenderer);
  883. if Assigned(result) then
  884. AddChar(aCharCode, result);
  885. end;
  886. end;
  887. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  888. procedure TtsFont.AddCharRange(const aCharCodeBeg, aCharCodeEnd: WideChar);
  889. var
  890. c: WideChar;
  891. begin
  892. for c := aCharCodeBeg to aCharCodeEnd do
  893. AddChar(c);
  894. end;
  895. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  896. procedure TtsFont.RemoveChar(const aCharCode: WideChar);
  897. var
  898. h, l: Integer;
  899. Chars: PtsFontCharArray;
  900. c: TtsChar;
  901. begin
  902. // find char array
  903. h := (Ord(aCharCode) shr 8) and $FF;
  904. Chars := fChars[h];
  905. if not Assigned(Chars) then
  906. exit;
  907. // find char
  908. l := Ord(aCharCode) and $FF;
  909. c := Chars^.Chars[l];
  910. if not Assigned(c) then
  911. exit;
  912. // remove char
  913. Chars^.Chars[l] := nil;
  914. dec(Chars^.CharCount);
  915. if (Chars^.CharCount <= 0) then begin
  916. fChars[h] := nil;
  917. Dispose(Chars);
  918. end;
  919. if Assigned(c.RenderRef) then begin
  920. fRenderer.FreeRenderRef(c.RenderRef);
  921. c.RenderRef := nil;
  922. end;
  923. FreeAndNil(c);
  924. end;
  925. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  926. procedure TtsFont.ClearChars;
  927. var
  928. h, l: Integer;
  929. Chars: PtsFontCharArray;
  930. c: TtsChar;
  931. begin
  932. for h := Low(fChars) to High(fChars) do begin
  933. Chars := fChars[h];
  934. if Assigned(Chars) then begin
  935. for l := Low(Chars^.Chars) to High(Chars^.Chars) do begin
  936. c := Chars^.Chars[l];
  937. if Assigned(c) then begin
  938. if Assigned(c.RenderRef) then
  939. fRenderer.FreeRenderRef(c.RenderRef);
  940. FreeAndNil(c);
  941. end;
  942. end;
  943. Dispose(Chars);
  944. fChars[h] := nil;
  945. end;
  946. end;
  947. end;
  948. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  949. function TtsFont.GetTextWidthW(aText: PWideChar): Integer;
  950. var
  951. c: TtsChar;
  952. begin
  953. result := 0;
  954. if not Assigned(aText) then
  955. exit;
  956. while (aText^ <> #0) do begin
  957. c := AddChar(aText^);
  958. if not Assigned(c) then
  959. c := AddChar(fProperties.DefaultChar);
  960. if Assigned(c) then begin
  961. if (result > 0) then
  962. result := result + CharSpacing;
  963. result := result + c.Advance;
  964. end;
  965. inc(aText);
  966. end;
  967. end;
  968. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  969. function TtsFont.GetTextWidthA(aText: PAnsiChar): Integer;
  970. var
  971. tmp: PWideChar;
  972. begin
  973. tmp := fGenerator.Context.AnsiToWide(aText);
  974. try
  975. result := GetTextWidthW(tmp);
  976. finally
  977. tsStrDispose(tmp);
  978. end;
  979. end;
  980. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  981. procedure TtsFont.GetTextMetric(out aMetric: TtsTextMetric);
  982. begin
  983. aMetric.Ascent := fProperties.Ascent;
  984. aMetric.Descent := fProperties.Descent;
  985. aMetric.ExternalLeading := fProperties.ExternalLeading;
  986. aMetric.BaseLineOffset := fProperties.BaseLineOffset;
  987. aMetric.CharSpacing := CharSpacing;
  988. aMetric.LineHeight := fProperties.Ascent + fProperties.Descent + fProperties.ExternalLeading;
  989. aMetric.LineSpacing := Trunc(fProperties.Size * fLineSpacing);
  990. end;
  991. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  992. destructor TtsFont.Destroy;
  993. begin
  994. fGenerator.UnregisterFont(self);
  995. ClearChars;
  996. inherited Destroy;
  997. end;
  998. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  999. //TtsPostProcessStep////////////////////////////////////////////////////////////////////////////////////////////////////
  1000. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1001. procedure TtsPostProcessStep.ClearList(const aList: TList);
  1002. var
  1003. i: Integer;
  1004. p: PtsPostProcessStepRange;
  1005. begin
  1006. for i := 0 to aList.Count-1 do begin
  1007. p := aList[i];
  1008. Dispose(p);
  1009. end;
  1010. aList.Clear;
  1011. end;
  1012. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1013. function TtsPostProcessStep.IsInRange(const aCharCode: WideChar): Boolean;
  1014. var
  1015. i: Integer;
  1016. p: PtsPostProcessStepRange;
  1017. begin
  1018. result := (fIncludeCharRange.Count = 0);
  1019. if not result then for i := 0 to fIncludeCharRange.Count-1 do begin
  1020. p := fIncludeCharRange[i];
  1021. if (aCharCode >= p^.StartChar) and (aCharCode <= p^.EndChar) then begin
  1022. result := true;
  1023. break;
  1024. end;
  1025. end;
  1026. if result then for i := 0 to fExcludeCharRange.Count-1 do begin
  1027. p := fExcludeCharRange[i];
  1028. if (aCharCode >= p^.StartChar) and (aCharCode <= p^.EndChar) then begin
  1029. result := false;
  1030. break;
  1031. end;
  1032. end;
  1033. end;
  1034. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1035. procedure TtsPostProcessStep.AddUsageRange(const aUsage: TtsFontProcessStepUsage; const aStartChar, aEndChar: WideChar);
  1036. var
  1037. p: PtsPostProcessStepRange;
  1038. begin
  1039. New(p);
  1040. p^.StartChar := aStartChar;
  1041. p^.EndChar := aEndChar;
  1042. case aUsage of
  1043. tsUsageInclude:
  1044. fIncludeCharRange.Add(p);
  1045. tsUsageExclude:
  1046. fExcludeCharRange.Add(p);
  1047. end;
  1048. end;
  1049. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1050. procedure TtsPostProcessStep.AddUsageChars(const aUsage: TtsFontProcessStepUsage; aChars: PWideChar);
  1051. begin
  1052. if Assigned(aChars) then
  1053. while (aChars^ <> #0) do begin
  1054. AddUsageRange(aUsage, aChars^, aChars^);
  1055. inc(aChars);
  1056. end;
  1057. end;
  1058. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1059. procedure TtsPostProcessStep.ClearIncludeRange;
  1060. begin
  1061. ClearList(fIncludeCharRange);
  1062. end;
  1063. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1064. procedure TtsPostProcessStep.ClearExcludeRange;
  1065. begin
  1066. ClearList(fExcludeCharRange);
  1067. end;
  1068. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1069. constructor TtsPostProcessStep.Create;
  1070. begin
  1071. inherited Create;
  1072. fIncludeCharRange := TList.Create;
  1073. fExcludeCharRange := TList.Create;
  1074. end;
  1075. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1076. destructor TtsPostProcessStep.Destroy;
  1077. begin
  1078. ClearList(fIncludeCharRange);
  1079. ClearList(fExcludeCharRange);
  1080. FreeAndNil(fIncludeCharRange);
  1081. FreeAndNil(fExcludeCharRange);
  1082. inherited Destroy;
  1083. end;
  1084. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1085. //TtsFontGenerator//////////////////////////////////////////////////////////////////////////////////////////////////////////
  1086. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1087. function TtsFontGenerator.GetPostProcessStepCount: Integer;
  1088. begin
  1089. result := fPostProcessSteps.Count;
  1090. end;
  1091. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1092. function TtsFontGenerator.GetPostProcessStep(const aIndex: Integer): TtsPostProcessStep;
  1093. begin
  1094. if (aIndex >= 0) and (aIndex < fPostProcessSteps.Count) then
  1095. Result := TtsPostProcessStep(fPostProcessSteps[aIndex])
  1096. else
  1097. raise EtsOutOfRange.Create(0, fPostProcessSteps.Count-1, aIndex);
  1098. end;
  1099. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1100. procedure TtsFontGenerator.DrawLine(const aChar: TtsChar; const aCharImage: TtsImage; aLinePosition, aLineSize: Integer);
  1101. var
  1102. ImgSize, ImgPos, Origin: TtsPosition;
  1103. Rect: TtsRect;
  1104. YOffset, y: Integer;
  1105. procedure FillLine(aData: PByte);
  1106. var
  1107. w, i: Integer;
  1108. c: TtsColor4f;
  1109. tmp: PByte;
  1110. begin
  1111. w := aCharImage.Width;
  1112. while (w > 0) do begin
  1113. tmp := aData;
  1114. tsFormatUnmap(aCharImage.Format, tmp, c);
  1115. for i := 0 to 3 do
  1116. c.arr[i] := 1.0;
  1117. tsFormatMap(aCharImage.Format, aData, c);
  1118. dec(w);
  1119. end;
  1120. end;
  1121. begin
  1122. if aLineSize <= 0 then
  1123. exit;
  1124. aLinePosition := aLinePosition - aLineSize;
  1125. // calculate width and height
  1126. ImgPos := tsPosition(0, 0);
  1127. ImgSize := tsPosition(aCharImage.Width, aCharImage.Height);
  1128. Origin := aChar.GlyphOrigin;
  1129. Rect := aChar.GlyphRect;
  1130. // expand left rect border to origin
  1131. if (Origin.x > 0) then begin
  1132. dec(Rect.Left, Origin.x);
  1133. Origin.x := 0;
  1134. end;
  1135. // expand right rect border to advanced
  1136. if (Rect.Right - Rect.Left < aChar.Advance) then begin
  1137. Rect.Right := Rect.Left + aChar.Advance;
  1138. end;
  1139. // expand bottom rect border
  1140. if (Origin.y - aLinePosition > Rect.Bottom) then begin
  1141. Rect.Bottom := Origin.y - aLinePosition;
  1142. end;
  1143. // expand top rect border
  1144. if (Origin.y - aLinePosition - aLineSize < Rect.Top) then begin
  1145. Rect.Top := Origin.y - aLinePosition - aLineSize;
  1146. Origin.y := aLinePosition + aLineSize;
  1147. end;
  1148. // update image size
  1149. if (Rect.Right - Rect.Left > ImgSize.x) then begin
  1150. ImgSize.x := Rect.Right - Rect.Left;
  1151. ImgPos.x := Max(-Rect.Left, 0);
  1152. inc(Rect.Left, ImgPos.x);
  1153. inc(Rect.Right, ImgPos.x);
  1154. end;
  1155. if (Rect.Bottom - Rect.Top > ImgSize.y) then begin
  1156. ImgSize.y := Rect.Bottom - Rect.Top;
  1157. ImgPos.y := Max(-Rect.Top, 0);
  1158. inc(Rect.Top, ImgPos.y);
  1159. inc(Rect.Bottom, ImgPos.y);
  1160. end;
  1161. aCharImage.Resize(ImgSize.x, ImgSize.y, ImgPos.x, ImgPos.y);
  1162. // draw lines
  1163. YOffset := Rect.Top + Origin.y - aLinePosition;
  1164. for y := 1 to aLineSize do
  1165. FillLine(aCharImage.ScanLine[YOffset - y]);
  1166. // move glyph rect
  1167. aChar.GlyphOrigin := Origin;
  1168. aChar.GlyphRect := Rect;
  1169. end;
  1170. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1171. procedure TtsFontGenerator.DoPostProcess(const aChar: TtsChar; const aCharImage: TtsImage);
  1172. var
  1173. i: Integer;
  1174. step: TtsPostProcessStep;
  1175. begin
  1176. if not aCharImage.IsEmpty then begin
  1177. for i := 0 to fPostProcessSteps.Count-1 do begin
  1178. step := TtsPostProcessStep(fPostProcessSteps[i]);
  1179. if step.IsInRange(aChar.CharCode) then
  1180. step.Execute(aChar, aCharImage);
  1181. end;
  1182. end;
  1183. end;
  1184. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1185. procedure TtsFontGenerator.RegisterFont(const aFont: TtsFont);
  1186. begin
  1187. fFonts.Add(aFont);
  1188. end;
  1189. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1190. procedure TtsFontGenerator.UnregisterFont(const aFont: TtsFont);
  1191. begin
  1192. if Assigned(fFonts) then
  1193. fFonts.Remove(aFont);
  1194. end;
  1195. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1196. function TtsFontGenerator.GenerateChar(const aCharCode: WideChar; const aFont: TtsFont; const aRenderer: TtsRenderer): TtsChar;
  1197. var
  1198. GlyphOrigin, GlyphSize: TtsPosition;
  1199. Advance: Integer;
  1200. CharImage: TtsImage;
  1201. begin
  1202. result := nil;
  1203. if (aCharCode <> #0) and
  1204. (not GetGlyphMetrics(aFont, aCharCode, GlyphOrigin, GlyphSize, Advance) or
  1205. not ((GlyphOrigin.x <> 0) or (GlyphOrigin.y <> 0) or (GlyphSize.x <> 0) or (GlyphSize.y <> 0) or (Advance <> 0))) then
  1206. exit;
  1207. CharImage := TtsImage.Create;
  1208. try
  1209. if aRenderer.SaveImages then begin
  1210. if (aCharCode = #0) then begin
  1211. CharImage.CreateEmpty(aRenderer.Format, 3, 1);
  1212. GlyphOrigin := tsPosition(0, 1);
  1213. Advance := 1;
  1214. end else if (GlyphSize.x > 0) and (GlyphSize.y > 0) then
  1215. GetCharImage(aFont, aCharCode, CharImage);
  1216. if CharImage.IsEmpty and ([tsStyleUnderline, tsStyleStrikeout] * aFont.Properties.Style <> []) then begin
  1217. CharImage.CreateEmpty(aRenderer.Format, max(Advance, 1), 1);
  1218. GlyphOrigin.y := 1;
  1219. end;
  1220. end;
  1221. result := TtsChar.Create(aCharCode);
  1222. try
  1223. result.GlyphOrigin := GlyphOrigin;
  1224. result.Advance := Advance;
  1225. if (aCharCode = #0) then
  1226. result.GlyphRect := tsRect(1, 0, 2, 1)
  1227. else
  1228. result.GlyphRect := tsRect(0, 0, CharImage.Width, CharImage.Height);
  1229. if (aRenderer.SaveImages) then begin
  1230. try
  1231. if (tsStyleUnderline in aFont.Properties.Style) then
  1232. DrawLine(result, CharImage, aFont.Properties.UnderlinePos, aFont.Properties.UnderlineSize);
  1233. if (tsStyleStrikeout in aFont.Properties.Style) then
  1234. DrawLine(result, CharImage, aFont.Properties.StrikeoutPos, aFont.Properties.StrikeoutSize);
  1235. except
  1236. CharImage.FillColor(tsColor4f(1, 0, 0, 0), COLOR_CHANNELS_RGB, IMAGE_MODES_NORMAL);
  1237. end;
  1238. DoPostProcess(result, CharImage);
  1239. result.RenderRef := aRenderer.CreateRenderRef(result, CharImage);
  1240. end;
  1241. except
  1242. FreeAndNil(result);
  1243. end;
  1244. finally
  1245. FreeAndNil(CharImage);
  1246. end;
  1247. end;
  1248. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1249. function TtsFontGenerator.AddPostProcessStep(const aStep: TtsPostProcessStep): TtsPostProcessStep;
  1250. begin
  1251. result := aStep;
  1252. fPostProcessSteps.Add(aStep);
  1253. end;
  1254. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1255. function TtsFontGenerator.InsertPostProcessStep(const aIndex: Integer; const aStep: TtsPostProcessStep): TtsPostProcessStep;
  1256. begin
  1257. result := aStep;
  1258. fPostProcessSteps.Insert(aIndex, aStep);
  1259. end;
  1260. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1261. procedure TtsFontGenerator.DeletePostProcessStep(const aIndex: Integer);
  1262. begin
  1263. if (aIndex >= 0) and (aIndex < fPostProcessSteps.Count) then
  1264. fPostProcessSteps.Delete(aIndex)
  1265. else
  1266. raise EtsOutOfRange.Create(0, fPostProcessSteps.Count-1, aIndex);
  1267. end;
  1268. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1269. procedure TtsFontGenerator.ClearPostProcessSteps;
  1270. begin
  1271. fPostProcessSteps.Clear;
  1272. end;
  1273. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1274. constructor TtsFontGenerator.Create(const aContext: TtsContext);
  1275. begin
  1276. inherited Create;
  1277. fContext := aContext;
  1278. fFonts := TObjectList.Create(false);
  1279. fPostProcessSteps := TObjectList.Create(true);
  1280. fContext.RegisterGenerator(self);
  1281. end;
  1282. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1283. destructor TtsFontGenerator.Destroy;
  1284. begin
  1285. ClearPostProcessSteps;
  1286. fContext.UnregisterGenerator(self);
  1287. fFonts.OwnsObjects := true;
  1288. FreeAndNil(fFonts);
  1289. FreeAndNil(fPostProcessSteps);
  1290. inherited Destroy;
  1291. end;
  1292. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1293. //TtsTextBlock//////////////////////////////////////////////////////////////////////////////////////////////////////////
  1294. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1295. function TtsTextBlock.GetRect: TtsRect;
  1296. begin
  1297. result.Left := fLeft;
  1298. result.Top := fTop;
  1299. result.Right := fLeft + fWidth;
  1300. result.Bottom := fTop + fHeight;
  1301. end;
  1302. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1303. function TtsTextBlock.PushLineItem(const aItem: PtsLineItem): Boolean;
  1304. begin
  1305. result := false;
  1306. if not Assigned(fLastLine) then
  1307. PushNewLine;
  1308. if not Assigned(fLastLine^.First) and
  1309. (aItem^.ItemType in [tsItemTypeSpace, tsItemTypeSpacing]) then
  1310. exit; // di not add line space or line spacing if line is empty
  1311. if Assigned(fLastLine^.Last) then begin
  1312. aItem^.Prev := fLastLine^.Last;
  1313. aItem^.Next := nil;
  1314. fLastLine^.Last^.Next := aItem;
  1315. fLastLine^.Last := aItem;
  1316. end;
  1317. if not Assigned(fLastLine^.First) then begin
  1318. fLastLine^.First := aItem;
  1319. fLastLine^.Last := aItem;
  1320. end;
  1321. case aItem^.ItemType of
  1322. tsItemTypeSpace, tsItemTypeText:
  1323. fLastLine^.meta.Width := fLastLine^.meta.Width + aItem^.TextWidth;
  1324. tsItemTypeSpacing:
  1325. fLastLine^.meta.Width := fLastLine^.meta.Width + aItem^.Spacing;
  1326. tsItemTypeTab:
  1327. fLastLine^.meta.Width := fLastLine^.meta.Width + aItem^.TabWidth;
  1328. end;
  1329. result := true;
  1330. end;
  1331. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1332. procedure TtsTextBlock.PushSpacing(const aWidth: Integer);
  1333. var
  1334. p: PtsLineItem;
  1335. begin
  1336. if (aWidth <= 0) then
  1337. exit;
  1338. new(p);
  1339. FillChar(p^, SizeOf(p^), #0);
  1340. p^.ItemType := tsItemTypeSpacing;
  1341. p^.Spacing := aWidth;
  1342. PushLineItem(p);
  1343. end;
  1344. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1345. procedure TtsTextBlock.FreeLineItem(var aItem: PtsLineItem);
  1346. begin
  1347. if Assigned(aItem^.Prev) then
  1348. aItem^.Prev^.Next := aItem^.Next;
  1349. if Assigned(aItem^.Next) then
  1350. aItem^.Next^.Prev := aItem^.Prev;
  1351. case aItem^.ItemType of
  1352. tsItemTypeText, tsItemTypeSpace:
  1353. tsStrDispose(aItem^.Text);
  1354. end;
  1355. Dispose(aItem);
  1356. aItem := nil;
  1357. end;
  1358. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1359. procedure TtsTextBlock.FreeLineItems(var aItem: PtsLineItem);
  1360. var
  1361. p: PtsLineItem;
  1362. begin
  1363. while Assigned(aItem) do begin
  1364. p := aItem;
  1365. aItem := aItem^.Next;
  1366. FreeLineItem(p);
  1367. end;
  1368. end;
  1369. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1370. procedure TtsTextBlock.FreeLines(var aItem: PtsBlockLine);
  1371. var
  1372. p: PtsBlockLine;
  1373. begin
  1374. while Assigned(aItem) do begin
  1375. p := aItem;
  1376. aItem := aItem^.Next;
  1377. FreeLineItems(p^.First);
  1378. p^.Last := nil;
  1379. Dispose(p);
  1380. end;
  1381. end;
  1382. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1383. function TtsTextBlock.SplitText(aText: PWideChar): PtsLineItem;
  1384. var
  1385. TextBegin: PWideChar;
  1386. TextLength: Integer;
  1387. State: TtsLineItemType;
  1388. LastItem: PtsLineItem;
  1389. procedure AddItem(const aItem: PtsLineItem);
  1390. begin
  1391. if Assigned(result) then begin
  1392. LastItem^.Next := aItem;
  1393. aItem^.Prev := LastItem;
  1394. aItem^.Next := nil;
  1395. LastItem := aItem;
  1396. end;
  1397. if not Assigned(result) then begin
  1398. result := aItem;
  1399. LastItem := aItem;
  1400. end;
  1401. end;
  1402. procedure ExtractWord;
  1403. var
  1404. p: PtsLineItem;
  1405. Text: PWideChar;
  1406. begin
  1407. if (State = tsItemTypeUnknown) then
  1408. exit;
  1409. new(p);
  1410. FillChar(p^, SizeOf(p^), #0);
  1411. p^.ItemType := State;
  1412. case State of
  1413. tsItemTypeText, tsItemTypeSpace: begin
  1414. p^.Text := tsStrAlloc(TextLength);
  1415. Text := p^.Text;
  1416. while (TextBegin <> aText) do begin
  1417. Text^ := TextBegin^;
  1418. inc(Text, 1);
  1419. inc(TextBegin, 1);
  1420. end;
  1421. AddItem(p);
  1422. end;
  1423. tsItemTypeLineBreak, tsItemTypeTab: begin
  1424. AddItem(p);
  1425. end;
  1426. else
  1427. Dispose(p);
  1428. end;
  1429. TextBegin := aText;
  1430. TextLength := 0;
  1431. end;
  1432. begin
  1433. result := nil;
  1434. LastItem := nil;
  1435. TextBegin := aText;
  1436. TextLength := 0;
  1437. State := tsItemTypeUnknown;
  1438. if not Assigned(aText) then
  1439. exit;
  1440. while (aText^ <> #0) do begin
  1441. case aText^ of
  1442. // line breaks
  1443. #$000D, #$000A: begin
  1444. if (State <> tsItemTypeLineBreak) then begin
  1445. ExtractWord;
  1446. State := tsItemTypeLineBreak;
  1447. end else if (TextBegin^ <> #13) or (aText^ <> #10) or (TextBegin + 1 < aText) then
  1448. ExtractWord;
  1449. end;
  1450. // spaces
  1451. #$0020: begin
  1452. if (State <> tsItemTypeSpace) then
  1453. ExtractWord;
  1454. State := tsItemTypeSpace;
  1455. end;
  1456. // tabulator
  1457. #$0009: begin
  1458. ExtractWord;
  1459. State := tsItemTypeTab;
  1460. end;
  1461. else
  1462. if (State <> tsItemTypeText) then
  1463. ExtractWord;
  1464. State := tsItemTypeText;
  1465. end;
  1466. inc(aText, 1);
  1467. inc(TextLength, 1);
  1468. end;
  1469. if (TextBegin <> aText) then
  1470. ExtractWord;
  1471. end;
  1472. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1473. function TtsTextBlock.SplitIntoLines(aItem: PtsLineItem): Boolean;
  1474. var
  1475. p: PtsLineItem;
  1476. tab: Integer;
  1477. begin
  1478. result := false;
  1479. if not Assigned(fCurrentFont) then
  1480. exit;
  1481. result := true;
  1482. while Assigned(aItem) do begin
  1483. p := aItem;
  1484. aItem := aItem^.Next;
  1485. p^.Next := nil;
  1486. p^.Prev := nil;
  1487. if not Assigned(fLastLine) then
  1488. PushNewLine;
  1489. case p^.ItemType of
  1490. tsItemTypeText, tsItemTypeSpace: begin
  1491. // increment word counter
  1492. if (p^.ItemType = tsItemTypeSpace) then begin
  1493. if not (tsLastItemIsSpace in fLastLine^.Flags) then
  1494. inc(fLastLine^.meta.SpaceCount, 1);
  1495. Include(fLastLine^.Flags, tsLastItemIsSpace);
  1496. end else
  1497. Exclude(fLastLine^.Flags, tsLastItemIsSpace);
  1498. // update and check line width
  1499. p^.TextWidth := fCurrentFont.GetTextWidthW(p^.Text);
  1500. if (tsBlockFlagWordWrap in fFlags) and
  1501. (fLastLine^.meta.Width + p^.TextWidth > fWidth) then
  1502. begin
  1503. if (fLastLine^.meta.Width = 0) then begin
  1504. if not PushLineItem(p) then // if is first word, than add anyway
  1505. FreeLineItem(p);
  1506. p := nil;
  1507. end;
  1508. include(fLastLine^.Flags, tsAutoLineBreak);
  1509. PushNewLine;
  1510. end;
  1511. // add item
  1512. if Assigned(p) then begin
  1513. if not PushLineItem(p) then
  1514. FreeLineItem(p);
  1515. PushSpacing(fCurrentFont.CharSpacing);
  1516. end;
  1517. end;
  1518. tsItemTypeLineBreak: begin
  1519. if not PushLineItem(p) then
  1520. FreeLineItem(p);
  1521. PushNewLine;
  1522. end;
  1523. tsItemTypeTab: begin
  1524. tab := fCurrentFont.TabWidth * fCurrentFont.Properties.Size;
  1525. p^.TabWidth := (1 + fLastLine^.meta.Width div tab) * tab - fLastLine^.meta.Width;
  1526. if not PushLineItem(p) then
  1527. FreeLineItem(p);
  1528. end;
  1529. else
  1530. raise EtsException.Create('unexpected line item');
  1531. end;
  1532. end;
  1533. end;
  1534. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1535. procedure TtsTextBlock.TrimSpaces(const aLine: PtsBlockLine);
  1536. procedure Trim(var aItem: PtsLineItem; const aMoveNext: Boolean);
  1537. var
  1538. tmp, p: PtsLineItem;
  1539. IsFirst: Boolean;
  1540. begin
  1541. IsFirst := true;
  1542. p := aItem;
  1543. while Assigned(p) do begin
  1544. tmp := p;
  1545. if aMoveNext then
  1546. p := p^.Next
  1547. else
  1548. p := p^.Prev;
  1549. case tmp^.ItemType of
  1550. tsItemTypeText: begin //done
  1551. break;
  1552. end;
  1553. tsItemTypeSpace,
  1554. tsItemTypeSpacing: begin
  1555. // update line meta
  1556. if (tmp^.ItemType = tsItemTypeSpace) then begin
  1557. aLine^.meta.Width := aLine^.meta.Width - tmp^.TextWidth;
  1558. dec(aLine^.meta.SpaceCount, 1);
  1559. end else
  1560. aLine^.meta.Width := aLine^.meta.Width - tmp^.Spacing;
  1561. FreeLineItem(tmp);
  1562. if IsFirst then
  1563. aItem := p;
  1564. end;
  1565. else
  1566. IsFirst := false;
  1567. end;
  1568. end;
  1569. end;
  1570. begin
  1571. if not Assigned(aLine) then
  1572. exit;
  1573. Trim(aLine^.First, true);
  1574. Trim(aLine^.Last, false);
  1575. end;
  1576. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1577. procedure TtsTextBlock.UpdateLineMeta(const aLine: PtsBlockLine);
  1578. var
  1579. metric: TtsTextMetric;
  1580. begin
  1581. if not Assigned(fCurrentFont) or
  1582. not Assigned(aLine) then
  1583. exit;
  1584. fCurrentFont.GetTextMetric(metric);
  1585. if (tsMetaValid in aLine^.Flags) then begin
  1586. aLine^.meta.Height := max(
  1587. aLine^.meta.Height,
  1588. metric.LineHeight);
  1589. aLine^.meta.Spacing := max(
  1590. aLine^.meta.Spacing,
  1591. metric.LineSpacing);
  1592. aLine^.meta.Ascent := max(
  1593. aLine^.meta.Ascent,
  1594. metric.Ascent);
  1595. end else begin
  1596. Include(aLine^.Flags, tsMetaValid);
  1597. aLine^.meta.Height := metric.LineHeight;
  1598. aLine^.meta.Spacing := metric.LineSpacing;
  1599. aLine^.meta.Ascent := metric.Ascent;
  1600. end;
  1601. end;
  1602. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1603. procedure TtsTextBlock.PushNewLine;
  1604. var
  1605. p: PtsBlockLine;
  1606. begin
  1607. TrimSpaces(fLastLine);
  1608. new(p);
  1609. FillChar(p^, SizeOf(p^), #0);
  1610. UpdateLineMeta(p);
  1611. if Assigned(fLastLine) then begin
  1612. fLastLine^.Next := p;
  1613. fLastLine := p;
  1614. end;
  1615. if not Assigned(fFirstLine) then begin
  1616. fFirstLine := p;
  1617. fLastLine := p;
  1618. end;
  1619. end;
  1620. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1621. constructor TtsTextBlock.Create(const aRenderer: TtsRenderer; const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags);
  1622. begin
  1623. inherited Create;
  1624. fRenderer := aRenderer;
  1625. fTop := aTop;
  1626. fLeft := aLeft;
  1627. fWidth := aWidth;
  1628. fHeight := aHeight;
  1629. fFlags := aFlags;
  1630. fVertAlign := tsVertAlignTop;
  1631. fHorzAlign := tsHorzAlignLeft;
  1632. fRenderer.RegisterBlock(self);
  1633. PushNewLine;
  1634. end;
  1635. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1636. procedure TtsTextBlock.ChangeFont(const aFont: TtsFont);
  1637. var
  1638. p: PtsLineItem;
  1639. begin
  1640. if not Assigned(aFont) then
  1641. exit;
  1642. New(p);
  1643. FillChar(p^, SizeOf(p^), #0);
  1644. fCurrentFont := aFont;
  1645. p^.ItemType := tsItemTypeFont;
  1646. p^.Font := fCurrentFont;
  1647. PushLineItem(p);
  1648. UpdateLineMeta(fLastLine);
  1649. fRenderer.UnregisterBlock(self);
  1650. end;
  1651. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1652. procedure TtsTextBlock.ChangeColor(const aColor: TtsColor4f);
  1653. var
  1654. p: PtsLineItem;
  1655. begin
  1656. New(p);
  1657. FillChar(p^, SizeOf(p^), #0);
  1658. p^.ItemType := tsItemTypeColor;
  1659. p^.Color := aColor;
  1660. PushLineItem(p);
  1661. fCurrentColor := aColor;
  1662. end;
  1663. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1664. function TtsTextBlock.GetActualBlockHeight: Integer;
  1665. var
  1666. line: PtsBlockLine;
  1667. begin
  1668. result := 0;
  1669. line := fFirstLine;
  1670. while Assigned(line) do begin
  1671. result := result + line^.meta.Height;
  1672. line := line^.Next;
  1673. end;
  1674. end;
  1675. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1676. procedure TtsTextBlock.TextOutA(const aText: PAnsiChar);
  1677. var
  1678. tmp: PWideChar;
  1679. begin
  1680. tmp := Renderer.Context.AnsiToWide(aText);
  1681. try
  1682. TextOutW(tmp);
  1683. finally
  1684. tsStrDispose(tmp);
  1685. end;
  1686. end;
  1687. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1688. procedure TtsTextBlock.TextOutW(const aText: PWideChar);
  1689. var
  1690. p: PtsLineItem;
  1691. begin
  1692. p := SplitText(aText);
  1693. if not SplitIntoLines(p) then
  1694. FreeLineItems(p);
  1695. end;
  1696. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1697. destructor TtsTextBlock.Destroy;
  1698. begin
  1699. FreeLines(fFirstLine);
  1700. fLastLine := nil;
  1701. inherited Destroy;
  1702. end;
  1703. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1704. //TtsRenderer///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1705. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1706. procedure TtsRenderer.RegisterBlock(const aBlock: TtsTextBlock);
  1707. begin
  1708. fBlocks.Add(aBlock);
  1709. end;
  1710. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1711. procedure TtsRenderer.UnregisterBlock(const aBlock: TtsTextBlock);
  1712. begin
  1713. if Assigned(fBlocks) then
  1714. fBlocks.Remove(aBlock);
  1715. end;
  1716. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1717. procedure TtsRenderer.BeginRender;
  1718. begin
  1719. fRenderCS.Enter;
  1720. end;
  1721. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1722. procedure TtsRenderer.EndRender;
  1723. begin
  1724. fRenderCS.Leave;
  1725. end;
  1726. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1727. function TtsRenderer.BeginBlock(const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags): TtsTextBlock;
  1728. begin
  1729. result := TtsTextBlock.Create(self, aTop, aLeft, aWidth, aHeight, aFlags);
  1730. end;
  1731. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1732. procedure TtsRenderer.EndBlock(var aBlock: TtsTextBlock);
  1733. var
  1734. c: PWideChar;
  1735. pos: TtsPosition;
  1736. x, y, tmp, tab: Integer;
  1737. ExtraSpaceTotal, ExtraSpaceActual: Single;
  1738. rect: TtsRect;
  1739. line: PtsBlockLine;
  1740. item: PtsLineItem;
  1741. font: TtsFont;
  1742. char: TtsChar;
  1743. metric: TtsTextMetric;
  1744. draw: Boolean;
  1745. function GetChar(const aCharCode: WideChar): TtsChar;
  1746. begin
  1747. result := font.AddChar(aCharCode);
  1748. if not Assigned(result) then
  1749. result := font.AddChar(font.Properties.DefaultChar);
  1750. end;
  1751. procedure DrawItem;
  1752. begin
  1753. case item^.ItemType of
  1754. tsItemTypeFont: begin
  1755. font := item^.Font;
  1756. font.GetTextMetric(metric);
  1757. end;
  1758. tsItemTypeColor: begin
  1759. SetColor(item^.Color);
  1760. end;
  1761. tsItemTypeText: begin
  1762. if draw and Assigned(font) then begin
  1763. c := item^.Text;
  1764. while (c^ <> #0) do begin
  1765. char := GetChar(c^);
  1766. if Assigned(char) then begin
  1767. MoveDrawPos(0, -metric.BaseLineOffset);
  1768. Render(char.RenderRef);
  1769. MoveDrawPos(char.Advance + font.CharSpacing, metric.BaseLineOffset);
  1770. end;
  1771. inc(c);
  1772. end;
  1773. end;
  1774. end;
  1775. tsItemTypeSpace: begin
  1776. if draw and Assigned(font) then begin
  1777. ExtraSpaceActual := ExtraSpaceActual + ExtraSpaceTotal;
  1778. c := item^.Text;
  1779. while (c^ <> #0) do begin
  1780. char := GetChar(c^);
  1781. if Assigned(char) then begin
  1782. if (font.Properties.Style * [tsStyleUnderline, tsStyleStrikeout] <> []) then begin
  1783. MoveDrawPos(0, -metric.BaseLineOffset);
  1784. Render(char.RenderRef);
  1785. MoveDrawPos(char.Advance + font.CharSpacing, metric.BaseLineOffset);
  1786. end else begin
  1787. MoveDrawPos(char.Advance + font.CharSpacing, 0);
  1788. end;
  1789. end;
  1790. inc(c);
  1791. end;
  1792. tmp := Trunc(ExtraSpaceActual);
  1793. ExtraSpaceActual := ExtraSpaceActual - tmp;
  1794. if (font.Properties.Style * [tsStyleUnderline, tsStyleStrikeout] <> []) then begin
  1795. char := GetChar(#0);
  1796. if Assigned(char) then
  1797. Render(char.RenderRef, tmp);
  1798. // TODO draw lines; maybe with a temporary created fake char or something like an empty char?
  1799. end;
  1800. MoveDrawPos(tmp, 0);
  1801. end;
  1802. end;
  1803. tsItemTypeLineBreak: begin
  1804. // because this should be the last item in a line, we have nothing to do here
  1805. end;
  1806. tsItemTypeTab: begin
  1807. // get current x pos and round it to TabWidth
  1808. pos := GetDrawPos;
  1809. tab := font.TabWidth * font.Properties.Size;
  1810. if (tab = 0) then
  1811. tab := 1;
  1812. pos.x := aBlock.Left + (1 + (pos.x - aBlock.Left) div tab) * tab;
  1813. SetDrawPos(pos.x, pos.y);
  1814. end;
  1815. tsItemTypeSpacing: begin
  1816. MoveDrawPos(item^.Spacing, 0);
  1817. end;
  1818. end;
  1819. end;
  1820. procedure DrawLine;
  1821. begin
  1822. // check vertical clipping
  1823. case aBlock.Clipping of
  1824. tsClipCharBorder, tsClipWordBorder:
  1825. draw := (y + line^.meta.Height > rect.Top) and (y < rect.Bottom);
  1826. tsClipCharComplete, tsClipWordComplete:
  1827. draw := (y > rect.Top) and (y + line^.meta.Height < rect.Bottom);
  1828. else
  1829. draw := true;
  1830. end;
  1831. // check horizontal alignment
  1832. x := rect.Left;
  1833. ExtraSpaceTotal := 0;
  1834. ExtraSpaceActual := 0;
  1835. case aBlock.HorzAlign of
  1836. tsHorzAlignCenter:
  1837. x := rect.Left + (aBlock.Width div 2) - (line^.meta.Width div 2);
  1838. tsHorzAlignRight:
  1839. x := rect.Right - line^.meta.Width;
  1840. tsHorzAlignJustify:
  1841. if (tsAutoLineBreak in line^.Flags) and (line^.meta.SpaceCount > 0) then
  1842. ExtraSpaceTotal := (aBlock.Width - line^.meta.Width) / line^.meta.SpaceCount;
  1843. end;
  1844. if draw then
  1845. SetDrawPos(x, y + line^.meta.Ascent);
  1846. inc(y, line^.meta.Height + line^.meta.Spacing);
  1847. item := line^.First;
  1848. while Assigned(item) do begin
  1849. DrawItem;
  1850. item := item^.Next;
  1851. end;
  1852. end;
  1853. begin
  1854. if (aBlock.Renderer <> self) then
  1855. EtsException.Create('text block was created by other renderer');
  1856. BeginRender;
  1857. try
  1858. // init variables
  1859. y := aBlock.Top;
  1860. font := nil;
  1861. line := aBlock.Lines;
  1862. rect := aBlock.Rect;
  1863. // check vertical alignment
  1864. case aBlock.VertAlign of
  1865. tsVertAlignCenter:
  1866. y := y + (aBlock.Height div 2 - aBlock.GetActualBlockHeight div 2);
  1867. tsVertAlignBottom:
  1868. y := y + (aBlock.Height - aBlock.GetActualBlockHeight);
  1869. end;
  1870. while Assigned(line) do begin
  1871. DrawLine;
  1872. line := line^.Next;
  1873. end;
  1874. finally
  1875. EndRender;
  1876. FreeAndNil(aBlock);
  1877. end;
  1878. end;
  1879. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1880. constructor TtsRenderer.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  1881. begin
  1882. inherited Create;
  1883. fContext := aContext;
  1884. fFormat := aFormat;
  1885. fSaveImages := true;
  1886. fBlocks := TObjectList.Create(false);
  1887. fRenderCS := TCriticalSection.Create;
  1888. fContext.RegisterRenderer(self);
  1889. end;
  1890. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1891. destructor TtsRenderer.Destroy;
  1892. begin
  1893. fContext.UnregisterRenderer(self);
  1894. fBlocks.OwnsObjects := true;
  1895. FreeAndNil(fBlocks);
  1896. FreeAndNil(fRenderCS);
  1897. inherited Destroy;
  1898. end;
  1899. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1900. //TtsContext////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1901. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1902. procedure TtsContext.RegisterRenderer(const aRenderer: TtsRenderer);
  1903. begin
  1904. fRenderers.Add(aRenderer);
  1905. end;
  1906. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1907. procedure TtsContext.UnregisterRenderer(const aRenderer: TtsRenderer);
  1908. begin
  1909. if Assigned(fRenderers) then
  1910. fRenderers.Remove(aRenderer);
  1911. end;
  1912. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1913. procedure TtsContext.RegisterGenerator(const aGenerator: TtsFontGenerator);
  1914. begin
  1915. fGenerators.Add(aGenerator);
  1916. end;
  1917. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1918. procedure TtsContext.UnregisterGenerator(const aGenerator: TtsFontGenerator);
  1919. begin
  1920. if Assigned(fGenerators) then
  1921. fGenerators.Remove(aGenerator);
  1922. end;
  1923. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1924. function TtsContext.AnsiToWide(const aText: PAnsiChar): PWideChar;
  1925. var
  1926. len: Integer;
  1927. begin
  1928. result := nil;
  1929. if not Assigned(aText) then
  1930. exit;
  1931. len := Length(aText);
  1932. result := tsStrAlloc(len);
  1933. tsAnsiToWide(result, len, aText, fCodePage, fCodePageDefault);
  1934. end;
  1935. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1936. constructor TtsContext.Create;
  1937. begin
  1938. inherited Create;
  1939. fCodePage := tsUTF8;
  1940. fCodePageDefault := WideChar('?');
  1941. fRenderers := TObjectList.Create(false);
  1942. fGenerators := TObjectList.Create(false);
  1943. end;
  1944. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1945. destructor TtsContext.Destroy;
  1946. begin
  1947. fGenerators.OwnsObjects := true;
  1948. fRenderers.OwnsObjects := true;
  1949. FreeAndNil(fGenerators);
  1950. FreeAndNil(fRenderers);
  1951. inherited Destroy;
  1952. end;
  1953. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1954. //Exceptions////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1955. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1956. constructor EtsOutOfRange.Create(const aMin, aMax, aIndex: Integer);
  1957. begin
  1958. inherited Create(Format('index (%d) is out of range (%d - %d)', [aIndex, aMin, aMax]));
  1959. end;
  1960. initialization
  1961. Randomize;
  1962. end.