You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5763 line
137 KiB

  1. {
  2. TextSuite (C) Steffen Xonna (aka Lossy eX)
  3. http://www.opengl24.de/
  4. -----------------------------------------------------------------------
  5. For copyright informations see file copyright.txt.
  6. }
  7. {$WARNINGS OFF}
  8. {$HINTS OFF}
  9. {$I TextSuiteOptions.inc}
  10. unit TextSuiteClasses;
  11. interface
  12. uses
  13. Classes,
  14. TextSuite,
  15. TextSuiteWideUtils,
  16. TextSuiteImports;
  17. { intern types for Renderer }
  18. const
  19. TS_BLOCK_FONT = $1;
  20. TS_BLOCK_COLOR = $2;
  21. TS_BLOCK_WORD = $3;
  22. TS_BLOCK_SPACE = $4;
  23. TS_BLOCK_LINEBREAK = $5;
  24. TS_BLOCK_TAB = $6;
  25. type
  26. TtsFontStyle = (tsStyleBold, tsStyleItalic, tsStyleUnderline, tsStyleStrikeout);
  27. TtsFontStyles = set of TtsFontStyle;
  28. TtsAntiAliasing = (tsAANone, tsAANormal);
  29. TtsFormat = (tsFormatEmpty, tsFormatRGBA8);
  30. TtsImageMode = (tsModeRed, tsModeGreen, tsModeBlue, tsModeAlpha, tsModeLuminance);
  31. TtsImageModes = array [TtsImageMode] of tsEnum;
  32. tsQuad = array[0..3] of tsPoint;
  33. tsPointFloat = packed record
  34. X: Single;
  35. Y: Single;
  36. end;
  37. tsQuadFloat = array[0..3] of tsPointFloat;
  38. const
  39. cModesReplace : TtsImageModes = (TS_MODE_REPLACE, TS_MODE_REPLACE, TS_MODE_REPLACE, TS_MODE_REPLACE, TS_MODE_REPLACE);
  40. cModesNormal : TtsImageModes = (TS_MODE_REPLACE, TS_MODE_REPLACE, TS_MODE_REPLACE, TS_MODE_MODULATE, TS_MODE_REPLACE);
  41. type
  42. PtsHashEntry = ^TtsHashEntry;
  43. TtsHashEntry = record
  44. Name: Integer;
  45. Value: Pointer;
  46. Next: PtsHashEntry;
  47. end;
  48. TtsHash = class(TObject)
  49. private
  50. fHashArray: array of PtsHashEntry;
  51. fHashEntrys: Integer;
  52. fCount: Integer;
  53. function IntToPos(Name: Integer): Integer;
  54. public
  55. property Count: Integer read fCount;
  56. constructor Create(HashEntrys: Integer);
  57. destructor Destroy; override;
  58. procedure Add(Name: Integer; Value: Pointer);
  59. procedure Delete(Name: Integer);
  60. procedure Clear;
  61. function Get(Name: Integer): Pointer;
  62. procedure GetNames(const NameList: TList);
  63. procedure GetValues(const ValueList: TList);
  64. end;
  65. PtsStringHashEntry = ^TtsStringHashEntry;
  66. TtsStringHashEntry = record
  67. pString: pWideChar;
  68. Next: PtsStringHashEntry;
  69. end;
  70. TtsStringHash = class(TObject)
  71. private
  72. fHashArray: array of PtsStringHashEntry;
  73. fHashEntrys: Cardinal;
  74. public
  75. constructor Create(HashEntrys: Integer);
  76. destructor Destroy; override;
  77. procedure Add(pString: pWideChar);
  78. function Delete(pString: pWideChar): Boolean;
  79. end;
  80. TtsKernel1DItem = packed record
  81. Offset: Integer;
  82. Value: Single;
  83. DataOffset: Integer;
  84. end;
  85. TtsKernel1D = class
  86. public
  87. Size: Integer;
  88. ValueSum: Double;
  89. Items: array of TtsKernel1DItem;
  90. ItemCount: Integer;
  91. constructor Create(Radius, Strength: Single);
  92. procedure UpdateDataOffset(DataSize: Integer);
  93. end;
  94. TtsKernel2DItem = packed record
  95. OffsetX: Integer;
  96. OffsetY: Integer;
  97. Value: Single;
  98. DataOffset: Integer;
  99. end;
  100. TtsKernel2D = class
  101. public
  102. SizeX: Integer;
  103. SizeY: Integer;
  104. MidSizeX: Integer;
  105. MidSizeY: Integer;
  106. ValueSum: Double;
  107. Items: array of TtsKernel2DItem;
  108. ItemCount: Integer;
  109. constructor Create(Radius, Strength: Single);
  110. procedure UpdateDataOffset(DataSizeX, DataSizeY: Integer);
  111. end;
  112. TtsImage = class;
  113. TtsRenderer = class;
  114. TtsRendererImageReference = class;
  115. TtsContext = class;
  116. TtsChar = class;
  117. TtsImageFunc = procedure(Image: TtsImage; X, Y: Integer; var Pixel: tsColor; Data: Pointer);
  118. TtsImage = class
  119. private
  120. fWidth: Integer;
  121. fHeight: Integer;
  122. fFormat: TtsFormat;
  123. fData: Pointer;
  124. fScanLinesValid: Boolean;
  125. fScanLines: array of Pointer;
  126. procedure SetDataPtr(aData: Pointer; aFormat: TtsFormat = tsFormatEmpty; aWidth: Integer = 0; aHeight: Integer = 0);
  127. function GetFormatSize(Format: TtsFormat): Integer;
  128. procedure UpdateScanLines;
  129. function GetScanLine(Index: Integer): pointer;
  130. function GetEmpty: Boolean;
  131. public
  132. procedure BeforeDestruction; override;
  133. procedure AssignFrom(Image: TtsImage);
  134. procedure CreateEmpty(Format: TtsFormat; aWidth, aHeight: Integer);
  135. procedure LoadFromFile(FileName: PAnsiChar);
  136. procedure Resize(NewWidth, NewHeight, X, Y: Integer);
  137. procedure FindMinMax(var MinMaxInfo: tsRect);
  138. procedure AddFunc(Func: TtsImageFunc; Data: Pointer);
  139. procedure FillColor(Red, Green, Blue, Alpha: Single; ChannelMask: tsBitmask; Modes: TtsImageModes);
  140. procedure FillPattern(Pattern: TtsImage; X, Y: Integer; ChannelMask: tsBitmask; Modes: TtsImageModes);
  141. procedure BlendImage(Image: TtsImage; X, Y: Integer; AutoExpand: Boolean = True);
  142. procedure Blur(HorzKernel, VertKernel: TtsKernel1D; ChannelMask: tsBitmask);
  143. procedure AddResizingBorder(tsChar: TtsChar);
  144. property Empty: Boolean read GetEmpty;
  145. property Data: Pointer read fData;
  146. property Width: Integer read fWidth;
  147. property Height: Integer read fHeight;
  148. property Format: TtsFormat read fFormat;
  149. property ScanLine[Index: Integer]: pointer read GetScanline;
  150. end;
  151. TtsChar = class
  152. protected
  153. // CharCode
  154. fCharCode: WideChar;
  155. public
  156. // Position of char
  157. GlyphOriginX: Smallint;
  158. GlyphOriginY: Smallint;
  159. Advance: SmallInt;
  160. GlyphRect: tsRect;
  161. HasResizingBorder: Boolean;
  162. // Kerning
  163. // KerningValuesLeft: array of WORD;
  164. // KerningValuesRight: array of WORD;
  165. // Renderer data for Imagehandling
  166. RendererImageReference: TtsRendererImageReference;
  167. constructor Create(CharCode: WideChar);
  168. destructor Destroy; override;
  169. procedure ExpandRect(Left, Top, Right, Bottom: Integer);
  170. // Kerning
  171. // procedure CalculateKerningData(CharImage: TtsImage);
  172. // function CalculateKerningValue(LastChar: TtsChar): Smallint;
  173. // CharCode
  174. property CharCode: WideChar read fCharCode;
  175. end;
  176. PtsFontCharArray = ^TtsFontCharArray;
  177. TtsFontCharArray = packed record
  178. Chars: array [Byte] of TtsChar;
  179. CharCount: Byte;
  180. end;
  181. TtsTextMetric = record
  182. Ascent: Integer;
  183. Descent: Integer;
  184. LineSkip: Integer;
  185. LineSkip_with_LineSpace: Integer;
  186. end;
  187. TtsFont = class
  188. private
  189. // Strings
  190. fCopyright: AnsiString;
  191. fFaceName: AnsiString;
  192. fStyleName: AnsiString;
  193. fFullName: AnsiString;
  194. // font styles
  195. fSize: Integer;
  196. fStyle: TtsFontStyles;
  197. fFormat: TtsFormat;
  198. fAntiAliasing: TtsAntiAliasing;
  199. // font settings
  200. fAscent: Integer;
  201. fDescent: Integer;
  202. fExternalLeading: Integer;
  203. fBaselineOffset: Integer;
  204. fDefaultChar: WideChar;
  205. fFontFileStyle: Integer;
  206. fFixedWidth: Boolean;
  207. fCharSpacing: Integer;
  208. fLineSpacing: Integer;
  209. fUnderlinePosition: Integer;
  210. fUnderlineSize: Integer;
  211. fStrikeoutPosition: Integer;
  212. fStrikeoutSize: Integer;
  213. // chars
  214. fChars: array [Byte] of PtsFontCharArray;
  215. protected
  216. fRenderer: TtsRenderer;
  217. function Validate(CharCode: WideChar): Boolean; virtual;
  218. procedure AddChar(CharCode: WideChar; Char: TtsChar);
  219. function GetChar(CharCode: WideChar): TtsChar;
  220. public
  221. // chars
  222. property Char[CharCode: WideChar]: TtsChar read GetChar;
  223. // strings
  224. property Copyright: AnsiString read fCopyright write fCopyright;
  225. property FaceName: AnsiString read fFaceName write fFaceName;
  226. property StyleName: AnsiString read fStyleName write fStyleName;
  227. property FullName: AnsiString read fFullName write fFullName;
  228. property Size: Integer read fSize write fSize;
  229. property Style: TtsFontStyles read fStyle write fStyle;
  230. property Format: TtsFormat read fFormat write fFormat;
  231. property AntiAliasing: TtsAntiAliasing read fAntiAliasing write fAntiAliasing;
  232. // Font propertys
  233. property Ascent: Integer read fAscent write fAscent;
  234. property Descent: Integer read fDescent write fDescent;
  235. property ExternalLeading: Integer read fExternalLeading write fExternalLeading;
  236. property BaselineOffset: Integer read fBaselineOffset write fBaselineOffset;
  237. property DefaultChar: WideChar read fDefaultChar write fDefaultChar;
  238. property FontFileStyle: Integer read fFontFileStyle write fFontFileStyle;
  239. property FixedWidth: Boolean read fFixedWidth write fFixedWidth;
  240. property CharSpacing: Integer read fCharSpacing write fCharSpacing;
  241. property LineSpacing: Integer read fLineSpacing write fLineSpacing;
  242. property UnderlinePosition: Integer read fUnderlinePosition write fUnderlinePosition;
  243. property UnderlineSize: Integer read fUnderlineSize write fUnderlineSize;
  244. property StrikeoutPosition: Integer read fStrikeoutPosition write fStrikeoutPosition;
  245. property StrikeoutSize: Integer read fStrikeoutSize write fStrikeoutSize;
  246. constructor Create(Renderer: TtsRenderer; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  247. destructor Destroy; override;
  248. procedure ClearChars;
  249. procedure DeleteChar(CharCode: WideChar);
  250. procedure GetTextMetric(var Metric: TtsTextMetric);
  251. end;
  252. PtsPostProcessStepRange = ^TtsPostProcessStepRange;
  253. TtsPostProcessStepRange = record
  254. StartChar: WideChar;
  255. EndChar: WideChar;
  256. end;
  257. TtsFontProcessStepUsage = (tsUInclude, tsUExclude);
  258. TtsPostProcessStep = class
  259. protected
  260. fIncludeCharRange: TList;
  261. fExcludeCharRange: TList;
  262. procedure ClearList(List: TList);
  263. procedure PostProcess(const CharImage: TtsImage; const Char: TtsChar); virtual; abstract;
  264. public
  265. constructor Create;
  266. destructor Destroy; override;
  267. function IsInRange(CharCode: WideChar): Boolean;
  268. procedure AddUsageRange(Usage: TtsFontProcessStepUsage; StartChar, EndChar: WideChar);
  269. procedure AddUsageChars(Usage: TtsFontProcessStepUsage; Chars: pWideChar);
  270. procedure ClearIncludeRange;
  271. procedure ClearExcludeRange;
  272. end;
  273. TtsFontCreator = class(TtsFont)
  274. private
  275. fPostProcessSteps: TList;
  276. function GetPostProcessStepCount: Integer;
  277. function GetPostProcessStep(Index: Integer): TtsPostProcessStep;
  278. protected
  279. fCreateChars: Boolean;
  280. fAddResizingBorder: Boolean;
  281. function Validate(CharCode: WideChar): Boolean; override;
  282. function GetGlyphMetrics(CharCode: WideChar; var GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance: Integer): Boolean; virtual; abstract;
  283. procedure GetCharImage(CharCode: WideChar; const CharImage: TtsImage); virtual; abstract;
  284. procedure DrawLine(Char: TtsChar; CharImage: TtsImage; LinePosition, LineSize: Integer);
  285. procedure DoPostProcess(var CharImage: TtsImage; const tsChar: TtsChar);
  286. public
  287. property CreateChars: Boolean read fCreateChars write fCreateChars;
  288. property AddResizingBorder: Boolean read fAddResizingBorder write fAddResizingBorder;
  289. constructor Create(Renderer: TtsRenderer; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  290. destructor Destroy; override;
  291. procedure AddChar(CharCode: WideChar); overload;
  292. function AddPostProcessStep(PostProcessStep: TtsPostProcessStep): TtsPostProcessStep;
  293. procedure DeletePostProcessStep(Index: Integer);
  294. procedure ClearPostProcessSteps;
  295. property PostProcessStepCount: Integer read GetPostProcessStepCount;
  296. property PostProcessStep[Index: Integer]: TtsPostProcessStep read GetPostProcessStep;
  297. end;
  298. TtsFontCreatorSDL = class(TtsFontCreator)
  299. protected
  300. fSDLFont: PTTF_Font;
  301. function GetGlyphMetrics(CharCode: WideChar; var GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance: Integer): Boolean; override;
  302. procedure GetCharImage(CharCode: WideChar; const CharImage: TtsImage); override;
  303. public
  304. constructor Create(Renderer: TtsRenderer; const Filename: AnsiString; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  305. destructor Destroy; override;
  306. end;
  307. TtsFontCreatorGDIFontFace = class(TtsFontCreator)
  308. protected
  309. fFontHandle: THandle;
  310. fMat2: TMat2;
  311. fFontname: AnsiString;
  312. function GetGlyphIndex(CharCode: WideChar): Integer;
  313. function GetGlyphMetrics(CharCode: WideChar; var GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance: Integer): Boolean; override;
  314. procedure GetCharImageAntialiased(DC: HDC; CharCode: WideChar; const CharImage: TtsImage);
  315. procedure GetCharImageNone(DC: HDC; CharCode: WideChar; const CharImage: TtsImage);
  316. procedure GetCharImage(CharCode: WideChar; const CharImage: TtsImage); override;
  317. public
  318. constructor Create(Renderer: TtsRenderer; const Fontname: AnsiString; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  319. destructor Destroy; override;
  320. end;
  321. TtsFontCreatorGDIFile = class(TtsFontCreatorGDIFontFace)
  322. protected
  323. fFilename: pAnsiChar;
  324. fFontRegistred: Boolean;
  325. function RegisterFont(Filename: pAnsiChar; RegisterPublic: Boolean): boolean;
  326. function UnRegisterFont(Filename: pAnsiChar; RegisterPublic: Boolean): boolean;
  327. function GetFaceName(Filename: pAnsiChar; var Face: AnsiString): boolean;
  328. public
  329. constructor Create(Renderer: TtsRenderer; const Filename: AnsiString; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  330. destructor Destroy; override;
  331. end;
  332. TtsFontCreatorGDIStream = class(TtsFontCreatorGDIFontFace)
  333. protected
  334. fFontRegistred: Boolean;
  335. fHandle: THandle;
  336. function RegisterFont(Data: TStream): boolean;
  337. function UnRegisterFont(): boolean;
  338. function GetFaceName(Stream: TStream; var Face: AnsiString): boolean;
  339. public
  340. constructor Create(Renderer: TtsRenderer; const Source: TStream; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  341. destructor Destroy; override;
  342. end;
  343. PtsLineItem = ^TtsLineItem;
  344. TtsLineItem = record
  345. NextItem: PtsLineItem;
  346. PrevItem: PtsLineItem;
  347. ItemType: Integer;
  348. case Integer of
  349. TS_BLOCK_FONT: (
  350. Font: TtsFont;
  351. FontID: tsFontID;
  352. );
  353. TS_BLOCK_COLOR: (
  354. Red: Single;
  355. Green: Single;
  356. Blue: Single;
  357. Alpha: Single;
  358. );
  359. TS_BLOCK_WORD, TS_BLOCK_SPACE: (
  360. Word: PWideChar;
  361. WordLength: Integer;
  362. );
  363. end;
  364. PtsLinesItem = ^TtsLinesItem;
  365. TtsLinesItem = record
  366. NextLine: PtsLinesItem;
  367. LineItemFirst: PtsLineItem;
  368. LineItemLast: PtsLineItem;
  369. LineLength: Integer;
  370. LineAutoBreak: Boolean;
  371. end;
  372. TtsTempLines = record
  373. Lines: PtsLinesItem;
  374. Empty: Boolean;
  375. end;
  376. { *** *** }
  377. TtsRendererImageReference = class
  378. end;
  379. TtsRenderer = class
  380. private
  381. fContext: TtsContext;
  382. fSaveImages: Boolean;
  383. fisBlock: Boolean;
  384. fBlockLeft: Integer;
  385. fBlockTop: Integer;
  386. fBlockWidth: Integer;
  387. fBlockHeight: Integer;
  388. fFlags: Integer;
  389. fWordWrap: Boolean;
  390. // fSingleLine: Boolean;
  391. fActiveFont: TtsFont;
  392. fActiveFontID: Cardinal;
  393. fLastActiveFont: TtsFont;
  394. fLastActiveFontID: Cardinal;
  395. fLinesFirst: PtsLinesItem;
  396. fLinesLast: PtsLinesItem;
  397. fLinesTemp: TtsTempLines;
  398. // drawings
  399. fLineTop: Integer;
  400. fTextOffsetY: Integer;
  401. fTextOffsetX: Integer;
  402. function GetActiveFont: TtsFont;
  403. function GetActiveFontID: Cardinal;
  404. function SplitText(pText: PWideChar): PtsLineItem;
  405. procedure CalculateWordLength(Font: TtsFont; pWord: PtsLineItem);
  406. procedure SplitIntoLines(pItemList: PtsLineItem);
  407. procedure DrawLine(pLine: PtsLineItem; LineLength: Integer; LineBreak: Boolean);
  408. procedure DrawLines(pLinesItem: PtsLinesItem);
  409. function CalculateLinesHeight(pLinesItem: PtsLinesItem): Integer;
  410. procedure GetLineMetric(pLine: PtsLineItem; var Metric: TtsTextMetric);
  411. procedure PushLineItem(pLine: PtsLineItem);
  412. procedure FreeLineItems(var pLine: PtsLineItem);
  413. procedure PushTempLines;
  414. procedure FreeLines(var pLinesItem: PtsLinesItem);
  415. procedure TrimSpaces(pLinesItem: PtsLinesItem);
  416. protected
  417. procedure DrawChar(Font: TtsFont; Char: TtsChar); virtual; abstract;
  418. procedure DrawSetPosition(X, Y: Integer); virtual; abstract;
  419. procedure DrawSetPositionRelative(X, Y: Integer); virtual; abstract;
  420. procedure DrawSetColor(Red, Green, Blue, Alpha: Single); virtual; abstract;
  421. function AddImage(Char: TtsChar; CharImage: TtsImage): TtsRendererImageReference; virtual; abstract;
  422. procedure RemoveImageReference(ImageReference: TtsRendererImageReference); virtual; abstract;
  423. public
  424. property ActiveFont: TtsFont read GetActiveFont;
  425. property ActiveFontID: Cardinal read GetActiveFontID;
  426. property SaveImages: Boolean read fSaveImages write fSaveImages;
  427. property isBlock: Boolean read FisBlock;
  428. constructor Create(Context: TtsContext);
  429. destructor Destroy; override;
  430. procedure BeginBlock(Left, Top, Width, Height: Integer; Flags: tsBitmask); virtual;
  431. procedure EndBlock;
  432. procedure FontActivate(FontID: Cardinal);
  433. procedure Color(Red, Green, Blue, Alpha: Single);
  434. procedure TextOut(pText: pWideChar);
  435. function TextGetWidth(pText: pWideChar): Integer;
  436. function TextGetDrawWidth: Integer;
  437. function TextGetDrawHeight: Integer;
  438. procedure CharOut(CharCode: WideChar);
  439. end;
  440. TtsRendererNULLImageReference = class(TtsRendererImageReference)
  441. Image: TtsImage;
  442. end;
  443. TtsRendererNULL = class(TtsRenderer)
  444. protected
  445. procedure DrawChar(Font: TtsFont; Char: TtsChar); override;
  446. procedure DrawSetPosition(X, Y: Integer); override;
  447. procedure DrawSetPositionRelative(X, Y: Integer); override;
  448. procedure DrawSetColor(Red, Green, Blue, Alpha: Single); override;
  449. function AddImage(Char: TtsChar; CharImage: TtsImage): TtsRendererImageReference; override;
  450. procedure RemoveImageReference(ImageReference: TtsRendererImageReference); override;
  451. end;
  452. TtsRendererOpenGLImageReference = class(TtsRendererImageReference)
  453. TexID: Integer;
  454. Coordinates: tsRect;
  455. TexCoords: tsQuadFloat;
  456. Vertex: tsQuadFloat;
  457. end;
  458. PtsRendererOpenGLTexture = ^TtsRendererOpenGLTexture;
  459. TtsRendererOpenGLTexture = record
  460. glTextureID: Cardinal;
  461. Width: Integer;
  462. Height: Integer;
  463. end;
  464. PtsRendererOpenGLManagedEntry = ^TtsRendererOpenGLManagedEntry;
  465. TtsRendererOpenGLManagedEntry = record
  466. Start: Word;
  467. Count: Word;
  468. NextEntry: PtsRendererOpenGLManagedEntry;
  469. end;
  470. PtsRendererOpenGLTextureEntry = ^TtsRendererOpenGLTextureEntry;
  471. TtsRendererOpenGLTextureEntry = record
  472. ID: Integer;
  473. Texture: PtsRendererOpenGLTexture;
  474. Lines: array of PtsRendererOpenGLManagedEntry;
  475. Usage: Integer;
  476. end;
  477. TtsRendererOpenGL = class(TtsRenderer)
  478. private
  479. fPos: tsPoint;
  480. fTextureSize: Integer;
  481. // Texture
  482. fTextures: TList;
  483. procedure AllocSpace(var FirstManaged: PtsRendererOpenGLManagedEntry; Start, Count: Word);
  484. procedure FreeSpace(var FirstManaged: PtsRendererOpenGLManagedEntry; Start, Count: Word);
  485. function GetTextureByID(ID: Integer): PtsRendererOpenGLTexture;
  486. function AddImageToTexture(Texture: PtsRendererOpenGLTextureEntry; Image: TtsImage; var TextureID: Integer; var Coordinates: tsRect): boolean;
  487. function CreateNewTexture: PtsRendererOpenGLTextureEntry;
  488. procedure DeleteTexture(Idx: Integer);
  489. procedure ClearTextures;
  490. protected
  491. procedure DrawChar(Font: TtsFont; Char: TtsChar); override;
  492. procedure DrawSetPosition(X, Y: Integer); override;
  493. procedure DrawSetPositionRelative(X, Y: Integer); override;
  494. procedure DrawSetColor(Red, Green, Blue, Alpha: Single); override;
  495. function AddImage(Char: TtsChar; CharImage: TtsImage): TtsRendererImageReference; override;
  496. procedure RemoveImageReference(ImageReference: TtsRendererImageReference); override;
  497. public
  498. property TextureSize: Integer read fTextureSize write fTextureSize;
  499. procedure BeginBlock(Left, Top, Width, Height: Integer; Flags: tsBitmask); override;
  500. procedure AfterConstruction; override;
  501. procedure BeforeDestruction; override;
  502. end;
  503. // context structures/types for use in unit TextSuite
  504. TtsContext = class
  505. private
  506. fContextID: Cardinal;
  507. // Fonts
  508. fFonts: TtsHash;
  509. fLastFontID: Cardinal;
  510. // Images
  511. fImages: TtsHash;
  512. fLastImageID: Cardinal;
  513. function GetIsLocked: boolean;
  514. procedure ClearFonts;
  515. procedure ClearImages;
  516. function GetActiveFont: TtsFont;
  517. public
  518. // ThreadID
  519. gBoundThreadID: Cardinal;
  520. // error
  521. Error: Cardinal;
  522. // globals settings
  523. Renderer: TtsRenderer;
  524. gCreator: tsEnum;
  525. gGlobalFormat: tsEnum;
  526. gGlobalAntiAliasing: tsEnum;
  527. gDebugDrawCharRects: Boolean;
  528. gEmptyCodePageEntry: tsEnum;
  529. gCodePage: tsEnum;
  530. gCodePagePtr: Pointer;
  531. gCodePageFunc: TtsAnsiToWideCharFunc;
  532. gSingleLine: tsEnum;
  533. gAlign: tsEnum;
  534. gVAlign: tsEnum;
  535. gClip: tsEnum;
  536. gBlockOffsetX: tsInt;
  537. gBlockOffsetY: tsInt;
  538. gImageMode: TtsImageModes;
  539. gImageLibrary: tsEnum;
  540. { Tab: tsEnum;
  541. TabWidth: tsInt; }
  542. // context specific / helper
  543. property ContextID: Cardinal read fContextID;
  544. property IsLocked: boolean read GetIsLocked;
  545. property ActiveFont: TtsFont read GetActiveFont;
  546. // helper functions
  547. function ImageAdd(Image: TtsImage): Cardinal;
  548. function ImageGet(Image: Cardinal): TtsImage;
  549. procedure ImageDelete(Image: Cardinal);
  550. function ImageCount: Cardinal;
  551. function FontAdd(Font: TtsFont): Cardinal;
  552. function FontGet(Font: Cardinal): TtsFont;
  553. procedure FontDelete(Font: Cardinal);
  554. function FontCount: Cardinal;
  555. function AnsiToWide(pText: pAnsiChar): pWideChar;
  556. constructor Create;
  557. destructor Destroy; override;
  558. end;
  559. PtsContextFontEntry = ^TtsContextFontEntry;
  560. TtsContextFontEntry = record
  561. FontID: tsFontID;
  562. Font: TtsFont;
  563. end;
  564. PtsContextImageEntry = ^TtsContextImageEntry;
  565. TtsContextImageEntry = record
  566. ImageID: tsImageID;
  567. Image: TtsImage;
  568. end;
  569. // Helper
  570. function MakeColor(Red: Byte = 0; Green: Byte = 0; Blue: Byte = 0; Alpha: Byte = 0): tsColor;
  571. implementation
  572. uses
  573. Math,
  574. SysUtils,
  575. SyncObjs,
  576. TextSuitePostProcess,
  577. TextSuiteTTFUtils;
  578. var
  579. gLastContextID: Cardinal;
  580. // Helper
  581. function MakeColor(Red, Green, Blue, Alpha: Byte): tsColor;
  582. begin
  583. Result.Red := Red;
  584. Result.Green := Green;
  585. Result.Blue := Blue;
  586. Result.Alpha := Alpha;
  587. end;
  588. procedure TranslateQuad(var Dest: tsQuadFloat; const Source: tsQuadFloat; const Translate: tsPoint);
  589. begin
  590. Dest[0].X := Source[0].X + Translate.X;
  591. Dest[0].Y := Source[0].Y + Translate.Y;
  592. Dest[1].X := Source[1].X + Translate.X;
  593. Dest[1].Y := Source[1].Y + Translate.Y;
  594. Dest[2].X := Source[2].X + Translate.X;
  595. Dest[2].Y := Source[2].Y + Translate.Y;
  596. Dest[3].X := Source[3].X + Translate.X;
  597. Dest[3].Y := Source[3].Y + Translate.Y;
  598. end;
  599. { TtsHash }
  600. procedure TtsHash.Add(Name: Integer; Value: Pointer);
  601. var
  602. Pos: Integer;
  603. Entry, HashEntry: PtsHashEntry;
  604. begin
  605. if Name <> 0 then begin
  606. Pos := IntToPos(Name);
  607. HashEntry := fHashArray[Pos];
  608. Entry := fHashArray[Pos];
  609. if (HashEntry = nil) then begin
  610. if (Value = nil) then
  611. Exit;
  612. New(HashEntry);
  613. HashEntry^.Name := Name;
  614. HashEntry^.Value := Value;
  615. HashEntry^.Next := nil;
  616. fHashArray[Pos] := HashEntry;
  617. Inc(fCount);
  618. Exit;
  619. end;
  620. while HashEntry <> nil do begin
  621. if Name = HashEntry^.Name then begin
  622. if Value = nil then begin
  623. if (HashEntry = fHashArray[Pos]) then
  624. fHashArray[Pos] := fHashArray[Pos]^.Next
  625. else
  626. Entry^.Next := HashEntry^.Next;
  627. Dispose(HashEntry);
  628. Dec(fCount);
  629. Exit;
  630. end;
  631. HashEntry^.Value := Value;
  632. Exit;
  633. end;
  634. if HashEntry^.Next = nil
  635. then break;
  636. Entry := HashEntry;
  637. HashEntry := HashEntry^.Next;
  638. end;
  639. if (Value = nil)
  640. then Exit;
  641. New(Entry);
  642. Entry^.Name := Name;
  643. Entry^.Value := Value;
  644. Entry^.Next := nil;
  645. Inc(fCount);
  646. HashEntry^.Next := Entry;
  647. end;
  648. end;
  649. procedure TtsHash.Clear;
  650. var
  651. Idx: Integer;
  652. TempEntry, Entry: PtsHashEntry;
  653. begin
  654. for Idx := Low(fHashArray) to High(fHashArray) do begin
  655. Entry := fHashArray[Idx];
  656. while Entry <> nil do begin
  657. TempEntry := Entry;
  658. Entry := Entry^.Next;
  659. Dispose(TempEntry);
  660. end;
  661. fHashArray[Idx] := nil;
  662. end;
  663. fCount := 0;
  664. end;
  665. constructor TtsHash.Create(HashEntrys: Integer);
  666. begin
  667. inherited Create;
  668. fHashEntrys := Max(1, HashEntrys);
  669. SetLength(fHashArray, fHashEntrys);
  670. end;
  671. procedure TtsHash.Delete(Name: Integer);
  672. begin
  673. // Add with an empty value is enough
  674. Add(Name, nil);
  675. end;
  676. destructor TtsHash.Destroy;
  677. begin
  678. Clear;
  679. inherited;
  680. end;
  681. function TtsHash.Get(Name: Integer): Pointer;
  682. var
  683. Pos: Integer;
  684. Entry: PtsHashEntry;
  685. begin
  686. Result := nil;
  687. if Name <> 0 then begin
  688. Pos := IntToPos(Name);
  689. Entry := fHashArray[Pos];
  690. if Entry <> nil then begin
  691. while Entry <> nil do begin
  692. if Name = Entry^.Name then begin
  693. Result := Entry^.Value;
  694. Break;
  695. end;
  696. Entry := Entry^.Next;
  697. end;
  698. end;
  699. end;
  700. end;
  701. procedure TtsHash.GetNames(const NameList: TList);
  702. var
  703. Idx: Integer;
  704. Entry: PtsHashEntry;
  705. begin
  706. Assert(NameList <> nil, 'TtsHash.GetNames - NameList is undefined');
  707. NameList.Clear;
  708. for Idx := Low(fHashArray) to High(fHashArray) do begin
  709. Entry := fHashArray[Idx];
  710. while Entry <> nil do begin
  711. NameList.Add({%H-}Pointer(Entry^.Name));
  712. Entry := Entry^.Next;
  713. end;
  714. end;
  715. end;
  716. procedure TtsHash.GetValues(const ValueList: TList);
  717. var
  718. Idx: Integer;
  719. Entry: PtsHashEntry;
  720. begin
  721. Assert(ValueList <> nil, 'TtsHash.GetValues - ValuesList is undefined');
  722. ValueList.Clear;
  723. for Idx := Low(fHashArray) to High(fHashArray) do begin
  724. Entry := fHashArray[Idx];
  725. while Entry <> nil do begin
  726. ValueList.Add(Entry^.Value);
  727. Entry := Entry^.Next;
  728. end;
  729. end;
  730. end;
  731. function TtsHash.IntToPos(Name: Integer): Integer;
  732. begin
  733. if Name < 0 then
  734. Result := -Name
  735. else
  736. Result := Name;
  737. Result := Result mod fHashEntrys;
  738. end;
  739. { TtsStringHash }
  740. procedure TtsStringHash.Add(pString: pWideChar);
  741. var
  742. Pos: Integer;
  743. Entry, HashEntry: PtsStringHashEntry;
  744. begin
  745. if pString <> nil then begin
  746. Pos := {%H-}Cardinal(pString) mod fHashEntrys;
  747. Entry := fHashArray[Pos];
  748. HashEntry := Entry;
  749. // is empty field
  750. if (Entry = nil) then begin
  751. New(Entry);
  752. Entry^.pString := pString;
  753. Entry^.Next := nil;
  754. fHashArray[Pos] := Entry;
  755. Exit;
  756. end;
  757. // search last
  758. while HashEntry <> nil do begin
  759. if HashEntry^.Next = nil
  760. then break;
  761. HashEntry := HashEntry^.Next;
  762. end;
  763. New(Entry);
  764. Entry^.pString := pString;
  765. Entry^.Next := nil;
  766. HashEntry^.Next := Entry;
  767. end;
  768. end;
  769. constructor TtsStringHash.Create(HashEntrys: Integer);
  770. begin
  771. inherited Create;
  772. fHashEntrys := Max(1, HashEntrys);
  773. SetLength(fHashArray, fHashEntrys);
  774. end;
  775. function TtsStringHash.Delete(pString: pWideChar) : Boolean;
  776. var
  777. Pos: Integer;
  778. Entry, HashEntry: PtsStringHashEntry;
  779. begin
  780. Result := False;
  781. if pString <> nil then begin
  782. Pos := {%H-}Cardinal(pString) mod fHashEntrys;
  783. HashEntry := fHashArray[Pos];
  784. Entry := nil;
  785. while HashEntry <> nil do begin
  786. if pString = HashEntry^.pString then begin
  787. if (HashEntry = fHashArray[Pos]) then
  788. fHashArray[Pos] := fHashArray[Pos]^.Next
  789. else
  790. Entry^.Next := HashEntry^.Next;
  791. Dispose(HashEntry);
  792. Result := True;
  793. Exit;
  794. end;
  795. Entry := HashEntry;
  796. HashEntry := HashEntry^.Next;
  797. end;
  798. end;
  799. end;
  800. destructor TtsStringHash.Destroy;
  801. var
  802. Idx: Integer;
  803. Temp: PtsStringHashEntry;
  804. begin
  805. for Idx := Low(fHashArray) to High(fHashArray) do begin
  806. while fHashArray[Idx] <> nil do begin
  807. Temp := fHashArray[Idx];
  808. fHashArray[Idx] := fHashArray[Idx]^.Next;
  809. tsStrDispose(Temp^.pString);
  810. Dispose(Temp);
  811. end;
  812. end;
  813. SetLength(fHashArray, 0);
  814. inherited;
  815. end;
  816. { TtsKernel1D }
  817. constructor TtsKernel1D.Create(Radius, Strength: Single);
  818. var
  819. TempRadius, SQRRadius, TempStrength, TempValue: Double;
  820. Idx: Integer;
  821. function CalcValue(Index: Integer): Single;
  822. var
  823. Temp: Double;
  824. begin
  825. Temp := Max(0, Abs(Index) - TempStrength);
  826. Temp := Sqr(Temp * TempRadius) / SQRRadius;
  827. Result := Exp(-Temp);
  828. end;
  829. begin
  830. inherited Create;
  831. // calculate new radius and strength
  832. TempStrength := Min(Radius - 1, Radius * Strength);
  833. TempRadius := Radius - TempStrength;
  834. SQRRadius := sqr(TempRadius) * sqr(TempRadius);
  835. // caluculating size of the kernel
  836. Size := Round(TempRadius);
  837. while CalcValue(Size) > 0.001 do
  838. Inc(Size);
  839. Size := Size -1;
  840. ValueSum := 0;
  841. ItemCount := Size * 2 +1;
  842. SetLength(Items, ItemCount);
  843. // calculate Value (yes thats right. there is no -1)
  844. for Idx := 0 to Size do begin
  845. TempValue := CalcValue(Idx);
  846. with Items[Size + Idx] do begin
  847. Offset := Idx;
  848. Value := TempValue;
  849. end;
  850. with Items[Size - Idx] do begin
  851. Offset := -Idx;
  852. Value := TempValue;
  853. end;
  854. // sum
  855. ValueSum := ValueSum + TempValue;
  856. if Idx > 0 then
  857. ValueSum := ValueSum + TempValue;
  858. end;
  859. end;
  860. procedure TtsKernel1D.UpdateDataOffset(DataSize: Integer);
  861. var
  862. Idx: Integer;
  863. begin
  864. for Idx := 0 to ItemCount -1 do
  865. with Items[Idx] do
  866. DataOffset := Offset * DataSize;
  867. end;
  868. { TtsKernel2D }
  869. constructor TtsKernel2D.Create(Radius, Strength: Single);
  870. var
  871. TempRadius, SQRRadius, TempStrength, TempValue: Double;
  872. X, Y, Height, Width: Integer;
  873. function CalcValue(Index: Single): Single;
  874. var
  875. Temp: Double;
  876. begin
  877. Temp := Max(0, Abs(Index) - TempStrength);
  878. Temp := Sqr(Temp * TempRadius) / SQRRadius;
  879. Result := Exp(-Temp);
  880. end;
  881. procedure QuickSort(L, R: Integer);
  882. var
  883. I, J: Integer;
  884. P, T: TtsKernel2DItem;
  885. function Compare(const Item1, Item2: TtsKernel2DItem): Integer;
  886. begin
  887. if Item1.Value = Item2.Value then
  888. Result := 0
  889. else
  890. if Item1.Value > Item2.Value then
  891. Result := -1
  892. else
  893. Result := 1;
  894. end;
  895. begin
  896. repeat
  897. I := L;
  898. J := R;
  899. P := Items[(L + R) shr 1];
  900. repeat
  901. while Compare(Items[I], P) < 0 do
  902. Inc(I);
  903. while Compare(Items[J], P) > 0 do
  904. Dec(J);
  905. if I <= J then begin
  906. T := Items[I];
  907. Items[I] := Items[J];
  908. Items[J] := T;
  909. Inc(I);
  910. Dec(J);
  911. end;
  912. until I > J;
  913. if L < J then
  914. QuickSort(L, J);
  915. L := I;
  916. until I >= R;
  917. end;
  918. begin
  919. inherited Create;
  920. // calculate new radius and strength
  921. TempStrength := Min(Radius - 1, Radius * Strength);
  922. TempRadius := Radius - TempStrength;
  923. SQRRadius := sqr(TempRadius) * sqr(TempRadius);
  924. // caluculating X size of the kernel
  925. SizeX := 0;
  926. MidSizeX := SizeX;
  927. while CalcValue(SizeX) > 0.5 do begin
  928. Inc(SizeX);
  929. Inc(MidSizeX);
  930. end;
  931. while CalcValue(SizeX) > 0.001 do
  932. Inc(SizeX);
  933. // caluculating Y size of the kernel
  934. SizeY := 0;
  935. MidSizeY := SizeY;
  936. while CalcValue(SizeY) > 0.5 do begin
  937. Inc(SizeY);
  938. Inc(MidSizeY);
  939. end;
  940. while CalcValue(SizeY) > 0.001 do
  941. Inc(SizeY);
  942. ValueSum := 0;
  943. Width := SizeX * 2 + 1;
  944. Height := SizeY * 2 + 1;
  945. ItemCount := Height * Width;
  946. SetLength(Items, ItemCount);
  947. Width := SizeX * 2 + 1;
  948. Height := SizeY * 2 + 1;
  949. ItemCount := Height * Width;
  950. SetLength(Items, ItemCount);
  951. // calculate Value (yes thats right. there is no -1)
  952. for Y := 0 to SizeY do begin
  953. for X := 0 to SizeX do begin
  954. TempValue := CalcValue(Sqrt(Sqr(X) + Sqr(Y)));
  955. with Items[(SizeY + Y) * Width + (SizeX + X)] do begin
  956. OffsetX := X;
  957. OffsetY := Y;
  958. Value := TempValue;
  959. end;
  960. with Items[(SizeY + Y) * Width + (SizeX - X)] do begin
  961. OffsetX := -X;
  962. OffsetY := Y;
  963. Value := TempValue;
  964. end;
  965. with Items[(SizeY - Y) * Width + (SizeX + X)] do begin
  966. OffsetX := X;
  967. OffsetY := -Y;
  968. Value := TempValue;
  969. end;
  970. with Items[(SizeY - Y) * Width + (SizeX - X)] do begin
  971. OffsetX := -X;
  972. OffsetY := -Y;
  973. Value := TempValue;
  974. end;
  975. // sum
  976. ValueSum := ValueSum + TempValue;
  977. if (X > 0) and (Y > 0) then
  978. ValueSum := ValueSum + TempValue;
  979. end;
  980. end;
  981. // sort
  982. QuickSort(0, ItemCount -1);
  983. // cut small items
  984. while Items[ItemCount -1].Value < 0.001 do
  985. Dec(ItemCount);
  986. SetLength(Items, ItemCount);
  987. end;
  988. procedure TtsKernel2D.UpdateDataOffset(DataSizeX, DataSizeY: Integer);
  989. var
  990. Idx: Integer;
  991. begin
  992. for Idx := 0 to ItemCount -1 do
  993. with Items[Idx] do
  994. DataOffset := OffsetX * DataSizeX + OffsetY * DataSizeY;
  995. end;
  996. { TtsChar }
  997. (*
  998. procedure TtsChar.CalculateKerningData(CharImage: TtsImage);
  999. var
  1000. Y: Integer;
  1001. pLeft, pRight: PtsColor;
  1002. function GetFirstPixel(pData: PtsColor; MinOpaque: Byte; IncValue, MaxSteps: Integer) : Integer;
  1003. var
  1004. CurStep: Integer;
  1005. begin
  1006. Result := MaxSteps;
  1007. CurStep := 0;
  1008. while CurStep < MaxSteps do begin
  1009. if pData^.Alpha >= MinOpaque then begin
  1010. Result := CurStep;
  1011. Break;
  1012. end;
  1013. Inc(CurStep);
  1014. Inc(pData, IncValue);
  1015. end;
  1016. end;
  1017. begin
  1018. SetLength(KerningValuesLeft, CharImage.Height);
  1019. SetLength(KerningValuesRight, CharImage.Height);
  1020. for Y := 0 to CharImage.Height - 1 do begin
  1021. pRight := CharImage.ScanLine[Y];
  1022. Inc(pRight, CharImage.Width -1);
  1023. KerningValuesRight[Y] := GetFirstPixel(pRight, $40, -1, CharImage.Width);
  1024. pLeft:= CharImage.ScanLine[Y];
  1025. KerningValuesLeft[Y] := GetFirstPixel(pLeft, $40, 1, CharImage.Width);
  1026. end;
  1027. end;
  1028. *)
  1029. //function TtsChar.CalculateKerningValue(LastChar: TtsChar): Smallint;
  1030. //begin
  1031. // Result := 0;
  1032. //var
  1033. // TempHeight, TempLastHeight: Integer;
  1034. // Y, YMin, YMax: Integer;
  1035. // LeftYMin, LeftYMax, RightYMin, RightYMax: Integer;
  1036. //
  1037. // Dist, TempDist: Integer;
  1038. //
  1039. // function GetMinDistance(Row: Integer): Integer;
  1040. // begin
  1041. //// Result :=
  1042. //// Self.KerningValuesLeft[Self.BaseLine - Self.GlyphRect.Top + Row] +
  1043. //// LastChar.KerningValuesRight[LastChar.BaseLine - LastChar.GlyphRect.Top + Row];
  1044. // end;
  1045. //
  1046. //begin
  1047. // Result := 0;
  1048. //
  1049. // if Assigned(LastChar) then begin
  1050. // TempLastHeight := Length(LastChar.KerningValuesRight);
  1051. // TempHeight := Length(Self.KerningValuesLeft);
  1052. //
  1053. // if (TempLastHeight > 0) and (TempHeight > 0) then begin
  1054. // LeftYMin := Self.GlyphRect.Bottom - Self.BaseLine;
  1055. // LeftYMax := Self.GlyphRect.Top - Self.BaseLine;
  1056. //
  1057. // RightYMin := LastChar.GlyphRect.Bottom - LastChar.BaseLine;
  1058. // RightYMax := LastChar.GlyphRect.Top - LastChar.BaseLine;
  1059. //
  1060. // YMin := Min(LeftYMin, RightYMin);
  1061. // YMax := Max(LeftYMax, RightYMax);
  1062. //
  1063. // Dist := -1;
  1064. //
  1065. // for Y := YMax to YMin -1 do begin
  1066. // TempDist := GetMinDistance(Y);
  1067. //
  1068. // if (Dist = -1) then
  1069. // Dist := TempDist
  1070. // else
  1071. //
  1072. // if TempDist < Dist then
  1073. // Dist := TempDist;
  1074. // end;
  1075. //
  1076. // // calculate advance of last char to diff
  1077. // Dist := Dist + LastChar.Advance - (LastChar.GlyphRect.Right - LastChar.GlyphRect.Left);
  1078. //
  1079. // Result := -Dist +3;
  1080. // end;
  1081. // end;
  1082. //end;
  1083. constructor TtsChar.Create(CharCode: WideChar);
  1084. begin
  1085. inherited Create;
  1086. fCharCode := CharCode;
  1087. end;
  1088. destructor TtsChar.Destroy;
  1089. begin
  1090. // SetLength(KerningValuesLeft, 0);
  1091. // SetLength(KerningValuesRight, 0);
  1092. inherited;
  1093. end;
  1094. procedure TtsChar.ExpandRect(Left, Top, Right, Bottom: Integer);
  1095. begin
  1096. Advance := Advance + Left + Right;
  1097. GlyphOriginY := GlyphOriginY + Top + Bottom;
  1098. GlyphRect.Right := GlyphRect.Right + Left + Right;
  1099. GlyphRect.Bottom := GlyphRect.Bottom + Top + Bottom;
  1100. end;
  1101. { TtsImage }
  1102. type
  1103. TtsModeFunc = function(Source, Dest: Byte): Byte; register;
  1104. function ModeFuncIgnore(Source, Dest: Byte): Byte; register;
  1105. {$ifdef TS_PURE_PASCAL}
  1106. begin
  1107. Result := Dest;
  1108. {$else}
  1109. asm
  1110. mov al, dl
  1111. {$endif}
  1112. end;
  1113. function ModeFuncReplace(Source, Dest: Byte): Byte; register;
  1114. {$ifdef TS_PURE_PASCAL}
  1115. begin
  1116. Result := Source;
  1117. {$else}
  1118. asm
  1119. {$endif}
  1120. end;
  1121. function ModeFuncModulate(Source, Dest: Byte): Byte; register;
  1122. {$ifdef TS_PURE_PASCAL}
  1123. begin
  1124. Result := (Source * Dest) div $FF
  1125. {$else}
  1126. asm
  1127. // inc ax
  1128. // inc dx
  1129. mul dl
  1130. shr eax, 8
  1131. {$endif}
  1132. end;
  1133. procedure TtsImage.AddFunc(Func: TtsImageFunc; Data: Pointer);
  1134. var
  1135. X, Y: Integer;
  1136. pPix: PtsColor;
  1137. begin
  1138. for Y := 0 to Height - 1 do begin
  1139. pPix := ScanLine[Y];
  1140. for X := 0 to Width - 1 do begin
  1141. Func(Self, X, Y, pPix^, Data);
  1142. Inc(pPix);
  1143. end;
  1144. end;
  1145. end;
  1146. procedure TtsImage.AddResizingBorder(tsChar: TtsChar);
  1147. var
  1148. X, Y: Integer;
  1149. pPix: PtsColor;
  1150. pTemp: PtsColor;
  1151. SumCount: Integer;
  1152. SumColor: array [0..2] of integer;
  1153. begin
  1154. SumColor[0] := 0;
  1155. SumColor[1] := 0;
  1156. SumColor[2] := 0;
  1157. SumCount := 0;
  1158. // settings of char
  1159. tsChar.GlyphRect.Top := tsChar.GlyphRect.Top + 1;
  1160. tsChar.GlyphRect.Left := tsChar.GlyphRect.Left + 1;
  1161. tsChar.GlyphRect.Right := tsChar.GlyphRect.Right + 1;
  1162. tsChar.GlyphRect.Bottom := tsChar.GlyphRect.Bottom + 1;
  1163. // resize image
  1164. Resize(Width + 4, Height + 4, 2, 2);
  1165. // calculate color of invisible pixels
  1166. for Y := 0 to Height -1 do begin
  1167. pPix := ScanLine[Y];
  1168. for X := 0 to Width -1 do begin
  1169. if pPix^.Alpha = 0 then begin
  1170. // row -1
  1171. if Y > 0 then begin
  1172. pTemp := pPix;
  1173. Dec(pTemp, fWidth);
  1174. // row -1 / col
  1175. if pTemp^.Alpha > 0 then begin
  1176. Inc(SumCount);
  1177. Inc(SumColor[0], pTemp^.Red);
  1178. Inc(SumColor[1], pTemp^.Green);
  1179. Inc(SumColor[2], pTemp^.Blue);
  1180. end;
  1181. // row -1 / col -1
  1182. if X > 0 then begin
  1183. Dec(pTemp);
  1184. if pTemp^.Alpha > 0 then begin
  1185. Inc(SumCount);
  1186. Inc(SumColor[0], pTemp^.Red);
  1187. Inc(SumColor[1], pTemp^.Green);
  1188. Inc(SumColor[2], pTemp^.Blue);
  1189. end;
  1190. Inc(pTemp);
  1191. end;
  1192. // row -1 / col +1
  1193. if X < fWidth -1 then begin
  1194. Inc(pTemp);
  1195. if pTemp^.Alpha > 0 then begin
  1196. Inc(SumCount);
  1197. Inc(SumColor[0], pTemp^.Red);
  1198. Inc(SumColor[1], pTemp^.Green);
  1199. Inc(SumColor[2], pTemp^.Blue);
  1200. end;
  1201. end;
  1202. end;
  1203. // row +1
  1204. if Y < fHeight -1 then begin
  1205. pTemp := pPix;
  1206. Inc(pTemp, fWidth);
  1207. // row +1 / col
  1208. if pTemp^.Alpha > 0 then begin
  1209. Inc(SumCount);
  1210. Inc(SumColor[0], pTemp^.Red);
  1211. Inc(SumColor[1], pTemp^.Green);
  1212. Inc(SumColor[2], pTemp^.Blue);
  1213. end;
  1214. // row +1 / col -1
  1215. if X > 0 then begin
  1216. Dec(pTemp);
  1217. if pTemp^.Alpha > 0 then begin
  1218. Inc(SumCount);
  1219. Inc(SumColor[0], pTemp^.Red);
  1220. Inc(SumColor[1], pTemp^.Green);
  1221. Inc(SumColor[2], pTemp^.Blue);
  1222. end;
  1223. Inc(pTemp);
  1224. end;
  1225. // row +1 / col +1
  1226. if X < fWidth -1 then begin
  1227. Inc(pTemp);
  1228. if pTemp^.Alpha > 0 then begin
  1229. Inc(SumCount);
  1230. Inc(SumColor[0], pTemp^.Red);
  1231. Inc(SumColor[1], pTemp^.Green);
  1232. Inc(SumColor[2], pTemp^.Blue);
  1233. end;
  1234. end;
  1235. end;
  1236. // row / col -1
  1237. if X > 0 then begin
  1238. pTemp := pPix;
  1239. Dec(pTemp);
  1240. if pTemp^.Alpha > 0 then begin
  1241. Inc(SumCount);
  1242. Inc(SumColor[0], pTemp^.Red);
  1243. Inc(SumColor[1], pTemp^.Green);
  1244. Inc(SumColor[2], pTemp^.Blue);
  1245. end;
  1246. end;
  1247. // row / col +1
  1248. if X < fWidth -1 then begin
  1249. pTemp := pPix;
  1250. Inc(pTemp);
  1251. if pTemp^.Alpha > 0 then begin
  1252. Inc(SumCount);
  1253. Inc(SumColor[0], pTemp^.Red);
  1254. Inc(SumColor[1], pTemp^.Green);
  1255. Inc(SumColor[2], pTemp^.Blue);
  1256. end;
  1257. end;
  1258. // any pixel next to the transparent pixel they are opaque?
  1259. if SumCount > 0 then begin
  1260. // calculate resulting pixel color
  1261. pPix^.Red := SumColor[0] div SumCount;
  1262. pPix^.Green := SumColor[1] div SumCount;
  1263. pPix^.Blue := SumColor[2] div SumCount;
  1264. // clearing values
  1265. SumColor[0] := 0;
  1266. SumColor[1] := 0;
  1267. SumColor[2] := 0;
  1268. SumCount := 0;
  1269. end;
  1270. end;
  1271. Inc(pPix);
  1272. end;
  1273. end;
  1274. end;
  1275. procedure TtsImage.AssignFrom(Image: TtsImage);
  1276. var
  1277. pImage: Pointer;
  1278. ImageSize: Integer;
  1279. begin
  1280. ImageSize := Image.Width * Image.Height * GetFormatSize(Image.Format);
  1281. GetMem(pImage, ImageSize);
  1282. if pImage <> nil then
  1283. Move(Image.Data^, pImage^, ImageSize);
  1284. SetDataPtr(pImage, Image.Format, Image.Width, Image.Height);
  1285. end;
  1286. procedure TtsImage.BeforeDestruction;
  1287. begin
  1288. SetDataPtr(nil);
  1289. inherited;
  1290. end;
  1291. procedure TtsImage.BlendImage(Image: TtsImage; X, Y: Integer; AutoExpand: Boolean);
  1292. var
  1293. pImage, pDest: PtsColor;
  1294. X1, X2, Y1, Y2, BX1, BX2, BY1, BY2, NewWidth, NewHeight: Integer;
  1295. TempX, TempY: Integer;
  1296. TempLines: array of PtsColor;
  1297. pSource: PtsColor;
  1298. // Blending
  1299. pUnder, pOver: PtsColor;
  1300. ResultAlpha, FaqUnder, FaqOver: Byte;
  1301. begin
  1302. // Calculate new size
  1303. X1 := Min(X, 0);
  1304. X2 := Max(X + Image.Width, Width);
  1305. Y1 := Min(Y, 0);
  1306. Y2 := Max(Y + Image.Height, Height);
  1307. BX1 := Max(X, 0);
  1308. BX2 := Min(X + Image.Width, Width);
  1309. BY1 := Max(Y, 0);
  1310. BY2 := Min(Y + Image.Height, Height);
  1311. NewWidth := X2 - X1;
  1312. NewHeight := Y2 - Y1;
  1313. // Allocate new image
  1314. GetMem(pImage, NewWidth * NewHeight * GetFormatSize(Format));
  1315. try
  1316. FillChar(pImage^, NewWidth * NewHeight * GetFormatSize(Format), #$00);
  1317. // ScanLines
  1318. SetLength(TempLines, NewHeight);
  1319. for TempY := 0 to NewHeight - 1 do begin
  1320. TempLines[TempY] := pImage;
  1321. Inc(TempLines[TempY], NewWidth * TempY);
  1322. end;
  1323. // copy non overlapping data from underlaying Image
  1324. for TempY := 0 to Height -1 do begin
  1325. pDest := TempLines[TempY - Y1];
  1326. Inc(pDest, - X1);
  1327. pSource := ScanLine[TempY];
  1328. for TempX := 0 to Width -1 do begin
  1329. pDest^ := pSource^;
  1330. Inc(pDest);
  1331. Inc(pSource);
  1332. end;
  1333. end;
  1334. // copy non overlapping data from overlaying Image
  1335. for TempY := 0 to Image.Height -1 do begin
  1336. pDest := TempLines[TempY + Y - Y1];
  1337. Inc(pDest, X - X1);
  1338. pSource := Image.ScanLine[TempY];
  1339. for TempX := 0 to Image.Width -1 do begin
  1340. pDest^ := pSource^;
  1341. Inc(pDest);
  1342. Inc(pSource);
  1343. end;
  1344. end;
  1345. // Blend overlapped
  1346. for TempY := BY1 to BY2 - 1 do begin
  1347. pOver := Image.ScanLine[TempY - Min(BY1, Y)];
  1348. Inc(pOver, BX1 - X);
  1349. pUnder := ScanLine[TempY - Min(BY1, 0)];
  1350. Inc(pUnder, BX1);
  1351. pDest := TempLines[TempY - Min(Y, 0)];
  1352. Inc(pDest, BX1 - Min(X, 0));
  1353. for TempX := BX1 to BX2 - 1 do begin
  1354. ResultAlpha := pOver^.Alpha + pUnder^.Alpha * ($FF - pOver^.Alpha) div $FF;
  1355. if ResultAlpha > 0 then begin
  1356. FaqUnder := (pUnder^.Alpha * ($FF - pOver^.Alpha) div $FF) * $FF div ResultAlpha;
  1357. FaqOver := pOver^.Alpha * $FF div ResultAlpha;
  1358. pDest^.Red := (pOver^.Red * FaqOver + pUnder^.Red * FaqUnder) div $FF;
  1359. pDest^.Green := (pOver^.Green * FaqOver + pUnder^.Green * FaqUnder) div $FF;
  1360. pDest^.Blue := (pOver^.Blue * FaqOver + pUnder^.Blue * FaqUnder) div $FF;
  1361. end else begin
  1362. pDest^.Red := 0;
  1363. pDest^.Green := 0;
  1364. pDest^.Blue := 0;
  1365. end;
  1366. pDest^.Alpha := ResultAlpha;
  1367. Inc(pOver);
  1368. Inc(pUnder);
  1369. Inc(pDest);
  1370. end;
  1371. end;
  1372. // Set new image
  1373. SetDataPtr(pImage, Format, NewWidth, NewHeight);
  1374. except
  1375. FreeMem(pImage);
  1376. end;
  1377. end;
  1378. type
  1379. TtsImageBlurFuncData = packed record
  1380. Kernel: TtsKernel1D;
  1381. Pos, MaxPos: Integer;
  1382. end;
  1383. TBlurFunc = function(pSource: pByte; var Data: TtsImageBlurFuncData): Byte; register;
  1384. function BlurFuncKernel(pSource: pByte; var Data: TtsImageBlurFuncData): Byte; register;
  1385. var
  1386. Idx: Integer;
  1387. pTemp: pByte;
  1388. TempSum, TempMax: Double;
  1389. begin
  1390. TempSum := 0;
  1391. TempMax := 0;
  1392. with Data do begin
  1393. for Idx := 0 to Kernel.ItemCount -1 do begin
  1394. with Kernel.Items[Idx] do begin
  1395. if (Pos + Offset >= 0) and (Pos + Offset < MaxPos) then begin
  1396. pTemp := pSource;
  1397. Inc(pTemp, DataOffset);
  1398. TempSum := TempSum + pTemp^ * Value;
  1399. TempMax := TempMax + Value;
  1400. end;
  1401. end;
  1402. end;
  1403. end;
  1404. Result := Round(TempSum / TempMax);
  1405. end;
  1406. function BlurFuncIgnore(pSource: pByte; var Data: TtsImageBlurFuncData): Byte; register;
  1407. {$ifdef TS_PURE_PASCAL}
  1408. begin
  1409. Result := pSource^;
  1410. {$else}
  1411. asm
  1412. mov al, byte ptr [eax]
  1413. {$endif}
  1414. end;
  1415. procedure TtsImage.Blur(HorzKernel, VertKernel: TtsKernel1D; ChannelMask: tsBitmask);
  1416. var
  1417. X, Y: Integer;
  1418. Temp: TtsImage;
  1419. pSource, pDest: ptsColor;
  1420. FuncData: TtsImageBlurFuncData;
  1421. RedFunc, GreenFunc, BlueFunc, AlphaFunc: TBlurFunc;
  1422. procedure AssignFunc(var Func: TBlurFunc; MaskBit: Cardinal);
  1423. begin
  1424. if MaskBit and ChannelMask > 0 then
  1425. Func := BlurFuncKernel
  1426. else
  1427. Func := BlurFuncIgnore;
  1428. end;
  1429. begin
  1430. // casing functions
  1431. AssignFunc(RedFunc, TS_CHANNEL_RED);
  1432. AssignFunc(GreenFunc, TS_CHANNEL_GREEN);
  1433. AssignFunc(BlueFunc, TS_CHANNEL_BLUE);
  1434. AssignFunc(AlphaFunc, TS_CHANNEL_ALPHA);
  1435. Temp := TtsImage.Create;
  1436. try
  1437. Temp.CreateEmpty(Format, Width, Height);
  1438. Temp.FillColor(1, 1, 1, 0, TS_CHANNELS_RGBA, cModesReplace);
  1439. // blur horz from original to temp image
  1440. HorzKernel.UpdateDataOffset(4);
  1441. FuncData.Kernel := HorzKernel;
  1442. FuncData.MaxPos := Temp.Width;
  1443. for Y := 0 to Temp.Height - 1 do begin
  1444. pSource := Self.ScanLine[Y];
  1445. pDest := Temp.ScanLine[Y];
  1446. for X := 0 to FuncData.MaxPos - 1 do begin
  1447. FuncData.Pos := X;
  1448. pDest^.Red := RedFunc(@(pSource^.Red), FuncData);
  1449. pDest^.Green := GreenFunc(@(pSource^.Green), FuncData);
  1450. pDest^.Blue := BlueFunc(@(pSource^.Blue), FuncData);
  1451. pDest^.Alpha := AlphaFunc(@(pSource^.Alpha), FuncData);
  1452. Inc(pDest);
  1453. Inc(pSource);
  1454. end;
  1455. end;
  1456. // blur vert from temp to original image
  1457. VertKernel.UpdateDataOffset(Width * 4);
  1458. FuncData.Kernel := VertKernel;
  1459. FuncData.MaxPos := Temp.Height;
  1460. for Y := 0 to Temp.Height - 1 do begin
  1461. pSource := Temp.ScanLine[Y];
  1462. pDest := Self.ScanLine[Y];
  1463. FuncData.Pos := Y;
  1464. for X := 0 to Temp.Width - 1 do begin
  1465. pDest^.Red := RedFunc(@(pSource^.Red), FuncData);
  1466. pDest^.Green := GreenFunc(@(pSource^.Green), FuncData);
  1467. pDest^.Blue := BlueFunc(@(pSource^.Blue), FuncData);
  1468. pDest^.Alpha := AlphaFunc(@(pSource^.Alpha), FuncData);
  1469. Inc(pDest);
  1470. Inc(pSource);
  1471. end;
  1472. end;
  1473. finally
  1474. Temp.Free;
  1475. end;
  1476. end;
  1477. procedure TtsImage.CreateEmpty(Format: TtsFormat; aWidth, aHeight: Integer);
  1478. var
  1479. pImage: pByte;
  1480. begin
  1481. pImage := AllocMem(aWidth * aHeight * GetFormatSize(Format));
  1482. SetDataPtr(pImage, Format, aWidth, aHeight);
  1483. end;
  1484. procedure TtsImage.FillColor(Red, Green, Blue, Alpha: Single; ChannelMask: tsBitmask; Modes: TtsImageModes);
  1485. //var
  1486. // MaskColor: TtsFillcolorData;
  1487. //begin
  1488. // // prepare mask
  1489. // FillChar(MaskColor.Mask, 4, $FF);
  1490. // if ChannelMask and TS_CHANNEL_RED = TS_CHANNEL_RED then
  1491. // MaskColor.Mask[0] := $00;
  1492. // if ChannelMask and TS_CHANNEL_GREEN = TS_CHANNEL_GREEN then
  1493. // MaskColor.Mask[1] := $00;
  1494. // if ChannelMask and TS_CHANNEL_BLUE = TS_CHANNEL_BLUE then
  1495. // MaskColor.Mask[2] := $00;
  1496. // if ChannelMask and TS_CHANNEL_ALPHA = TS_CHANNEL_ALPHA then
  1497. // MaskColor.Mask[3] := $00;
  1498. //
  1499. // pCardinal(@MaskColor.Mask[4])^ := pCardinal(@MaskColor.Mask[0])^;
  1500. // pCardinal(@MaskColor.Mask[8])^ := pCardinal(@MaskColor.Mask[0])^;
  1501. // pCardinal(@MaskColor.Mask[12])^ := pCardinal(@MaskColor.Mask[0])^;
  1502. //
  1503. // // prepare color
  1504. // MaskColor.Color[0] := Round($FF * Red);
  1505. // MaskColor.Color[1] := Round($FF * Green);
  1506. // MaskColor.Color[2] := Round($FF * Blue);
  1507. // MaskColor.Color[3] := Round($FF * Alpha);
  1508. // pCardinal(@MaskColor.Color[4])^ := pCardinal(@MaskColor.Color[0])^;
  1509. // pCardinal(@MaskColor.Color[8])^ := pCardinal(@MaskColor.Color[0])^;
  1510. // pCardinal(@MaskColor.Color[12])^ := pCardinal(@MaskColor.Color[0])^;
  1511. //
  1512. // // image mode
  1513. // FillChar(MaskColor.ModuloMask, 4, $00);
  1514. // if (Modes[tsModeRed] = TS_MODE_MODULATE) and (MaskColor.Mask[0] > 0) then
  1515. // MaskColor.ModuloMask[0] := $FF;
  1516. // if (Modes[tsModeGreen] = TS_MODE_MODULATE) and (MaskColor.Mask[1] > 0) then
  1517. // MaskColor.ModuloMask[1] := $FF;
  1518. // if (Modes[tsModeBlue] = TS_MODE_MODULATE) and (MaskColor.Mask[2] > 0) then
  1519. // MaskColor.ModuloMask[2] := $FF;
  1520. // if (Modes[tsModeAlpha] = TS_MODE_MODULATE) and (MaskColor.Mask[3] > 0) then
  1521. // MaskColor.ModuloMask[3] := $FF;
  1522. // pCardinal(@MaskColor.ModuloMask[4])^ := pCardinal(@MaskColor.ModuloMask[0])^;
  1523. //
  1524. // // fill with color
  1525. // if pCardinal(@MaskColor.ModuloMask[0])^ = 0 then
  1526. // Fillcolor_RGBA8(Data, @MaskColor, Width * Height)
  1527. // else
  1528. // Fillcolor_RGBA8_modulo(Data, @MaskColor, Width * Height);
  1529. //
  1530. // {$IFNDEF TS_PURE_PASCAL}
  1531. //// if supportSSE then
  1532. //// Fillcolor_RGBA8_SSE(Data, @MaskColor, Width * Height)
  1533. //// else
  1534. // {$ENDIF}
  1535. //
  1536. //// Fillcolor_RGBA8(Data, @MaskColor, Width * Height);
  1537. //end;
  1538. var
  1539. _Red, _Green, _Blue, _Alpha: Byte;
  1540. RedFunc, GreenFunc, BlueFunc, AlphaFunc, LuminanceFunc: TtsModeFunc;
  1541. Y, X: Integer;
  1542. pPix: PtsColor;
  1543. procedure AssignFunc(var Func: TtsModeFunc; Mask, Mode: tsEnum);
  1544. begin
  1545. if ChannelMask and Mask = Mask then begin
  1546. if Mode = TS_MODE_MODULATE then
  1547. Func := ModeFuncModulate
  1548. else
  1549. Func := ModeFuncReplace
  1550. end else
  1551. Func := ModeFuncIgnore
  1552. end;
  1553. begin
  1554. _Red := Round($FF * Red);
  1555. _Green := Round($FF * Green);
  1556. _Blue := Round($FF * Blue);
  1557. _Alpha := Round($FF * Alpha);
  1558. AssignFunc(RedFunc, TS_CHANNEL_RED, Modes[tsModeRed]);
  1559. AssignFunc(GreenFunc, TS_CHANNEL_GREEN, Modes[tsModeGreen]);
  1560. AssignFunc(BlueFunc, TS_CHANNEL_BLUE, Modes[tsModeBlue]);
  1561. AssignFunc(AlphaFunc, TS_CHANNEL_ALPHA, Modes[tsModeAlpha]);
  1562. AssignFunc(LuminanceFunc, TS_CHANNEL_LUMINANCE, Modes[tsModeLuminance]);
  1563. for Y := 0 to Height - 1 do begin
  1564. pPix := ScanLine[Y];
  1565. for X := 0 to Width - 1 do begin
  1566. pPix^.Red := RedFunc (_Red, pPix^.Red);
  1567. pPix^.Green := GreenFunc(_Green, pPix^.Green);
  1568. pPix^.Blue := BlueFunc (_Blue, pPix^.Blue);
  1569. pPix^.Alpha := AlphaFunc(_Alpha, pPix^.Alpha);
  1570. Inc(pPix);
  1571. end;
  1572. end;
  1573. end;
  1574. procedure TtsImage.FillPattern(Pattern: TtsImage; X, Y: Integer; ChannelMask: tsBitmask; Modes: TtsImageModes);
  1575. var
  1576. TempX, TempY, RandX, RandY, PosX: Integer;
  1577. RedFunc, GreenFunc, BlueFunc, AlphaFunc, LuminanceFunc: TtsModeFunc;
  1578. pSrc, pDest: PtsColor;
  1579. procedure AssignFunc(var Func: TtsModeFunc; Mask, Mode: tsEnum);
  1580. begin
  1581. if ChannelMask and Mask = Mask then begin
  1582. if Mode = TS_MODE_MODULATE then
  1583. Func := ModeFuncModulate
  1584. else
  1585. Func := ModeFuncReplace
  1586. end else
  1587. Func := ModeFuncIgnore
  1588. end;
  1589. begin
  1590. // Pattern position
  1591. if X < 0 then
  1592. RandX := Random(Pattern.Width)
  1593. else
  1594. RandX := X;
  1595. if Y < 0 then
  1596. RandY := Random(Pattern.Height)
  1597. else
  1598. RandY := Y;
  1599. AssignFunc(RedFunc, TS_CHANNEL_RED, Modes[tsModeRed]);
  1600. AssignFunc(GreenFunc, TS_CHANNEL_GREEN, Modes[tsModeGreen]);
  1601. AssignFunc(BlueFunc, TS_CHANNEL_BLUE, Modes[tsModeBlue]);
  1602. AssignFunc(AlphaFunc, TS_CHANNEL_ALPHA, Modes[tsModeAlpha]);
  1603. AssignFunc(LuminanceFunc, TS_CHANNEL_LUMINANCE, Modes[tsModeLuminance]);
  1604. // Copy data
  1605. for TempY := 0 to Height - 1 do begin
  1606. pDest := ScanLine[TempY];
  1607. pSrc := Pattern.Scanline[(TempY + RandY) mod Pattern.Height];
  1608. Inc(pSrc, RandX);
  1609. PosX := RandX;
  1610. for TempX := 0 to Width - 1 do begin
  1611. if PosX >= Pattern.Width then begin
  1612. pSrc := Pattern.Scanline[(TempY + RandY) mod Pattern.Height];
  1613. PosX := 0;
  1614. end;
  1615. pDest^.Red := RedFunc (pSrc^.Red, pDest^.Red);
  1616. pDest^.Green := GreenFunc(pSrc^.Green, pDest^.Green);
  1617. pDest^.Blue := BlueFunc (pSrc^.Blue, pDest^.Blue);
  1618. pDest^.Alpha := AlphaFunc(pSrc^.Alpha, pDest^.Alpha);
  1619. Inc(pDest);
  1620. Inc(pSrc);
  1621. Inc(PosX);
  1622. end;
  1623. end;
  1624. end;
  1625. procedure TtsImage.FindMinMax(var MinMaxInfo: tsRect);
  1626. var
  1627. X, Y: Integer;
  1628. pPix: PtsColor;
  1629. begin
  1630. MinMaxInfo.Top := -1;
  1631. MinMaxInfo.Left := -1;
  1632. MinMaxInfo.Right := -1;
  1633. MinMaxInfo.Bottom := -1;
  1634. // Search for MinMax
  1635. for Y := 0 to Height -1 do begin
  1636. pPix := ScanLine[Y];
  1637. for X := 0 to Width -1 do begin
  1638. if pPix^.Alpha > 0 then begin
  1639. if (X < MinMaxInfo.Left) or (MinMaxInfo.Left = -1) then
  1640. MinMaxInfo.Left := X;
  1641. if (X+1 > MinMaxInfo.Right) or (MinMaxInfo.Right = -1) then
  1642. MinMaxInfo.Right := X +1;
  1643. if (Y < MinMaxInfo.Top) or (MinMaxInfo.Top = -1) then
  1644. MinMaxInfo.Top := Y;
  1645. if (Y+1 > MinMaxInfo.Bottom) or (MinMaxInfo.Bottom = -1) then
  1646. MinMaxInfo.Bottom := Y +1;
  1647. end;
  1648. Inc(pPix);
  1649. end;
  1650. end;
  1651. end;
  1652. function TtsImage.GetEmpty: Boolean;
  1653. begin
  1654. Result := fData = nil;
  1655. end;
  1656. function TtsImage.GetFormatSize(Format: TtsFormat): Integer;
  1657. begin
  1658. case Format of
  1659. tsFormatRGBA8: Result := 4;
  1660. else
  1661. Result := 0;
  1662. end;
  1663. end;
  1664. function TtsImage.GetScanLine(Index: Integer): pointer;
  1665. begin
  1666. if not fScanLinesValid then
  1667. UpdateScanLines;
  1668. if (fScanLinesValid) and (Index >= 0) and (Index <= High(fScanLines)) then
  1669. Result := fScanLines[Index]
  1670. else
  1671. Result := nil;
  1672. end;
  1673. procedure TtsImage.LoadFromFile(FileName: PAnsiChar);
  1674. var
  1675. Surface, ConvSurface: PSDL_Surface;
  1676. Format: TSDL_PixelFormat;
  1677. ImageSize: Integer;
  1678. Image: pByte;
  1679. begin
  1680. Surface := IMG_Load(FileName);
  1681. if Surface <> nil then
  1682. try
  1683. FillChar(Format, SizeOf(TSDL_PixelFormat), 0);
  1684. Format.BitsPerPixel := 32;
  1685. Format.BytesPerPixel := 4;
  1686. Format.RMask := $000000FF;
  1687. Format.GMask := $0000FF00;
  1688. Format.BMask := $00FF0000;
  1689. Format.AMask := $FF000000;
  1690. Format.Rshift := 0;
  1691. Format.Gshift := 8;
  1692. Format.Bshift := 16;
  1693. Format.Ashift := 24;
  1694. ConvSurface := SDL_ConvertSurface(Surface, @Format, SDL_SWSURFACE);
  1695. if ConvSurface <> nil then
  1696. try
  1697. // Set Image Size
  1698. ImageSize := ConvSurface^.Width * ConvSurface^.Height * 4;
  1699. GetMem(Image, ImageSize);
  1700. try
  1701. // Copy image
  1702. Move(ConvSurface^.pixels^, Image^, ImageSize);
  1703. // Set new Data
  1704. SetDataPtr(Image, tsFormatRGBA8, ConvSurface^.Width, ConvSurface^.Height);
  1705. except
  1706. FreeMem(Image);
  1707. end;
  1708. finally
  1709. SDL_FreeSurface(ConvSurface);
  1710. end;
  1711. finally
  1712. SDL_FreeSurface(Surface);
  1713. end;
  1714. end;
  1715. procedure TtsImage.Resize(NewWidth, NewHeight, X, Y: Integer);
  1716. var
  1717. pImage: PByte;
  1718. PixSize, LineSize, ImageSize, OrgLineSize: Integer;
  1719. pSource, pDest: PByte;
  1720. YStart, YEnd, YPos, XStart, XEnd: Integer;
  1721. begin
  1722. if (NewHeight = 0) or (NewWidth = 0) then begin
  1723. SetDataPtr(nil);
  1724. end else begin
  1725. PixSize := GetFormatSize(Format);
  1726. LineSize := PixSize * NewWidth;
  1727. ImageSize := LineSize * NewHeight;
  1728. OrgLineSize := PixSize * Width;
  1729. GetMem(pImage, ImageSize);
  1730. try
  1731. FillChar(pImage^, ImageSize, 0);
  1732. // positions
  1733. YStart := Max(0, Y);
  1734. YEnd := Min(NewHeight, Y + Height);
  1735. XStart := Max(0, X);
  1736. XEnd := Min(NewWidth, X + Width);
  1737. // copy data
  1738. for YPos := YStart to YEnd -1 do begin
  1739. pDest := pImage;
  1740. Inc(pDest, LineSize * YPos + PixSize * XStart);
  1741. pSource := fData;
  1742. Inc(pSource, OrgLineSize * (YPos - Y) + PixSize * (XStart - X));
  1743. Move(pSource^, pDest^, (XEnd - XStart) * PixSize);
  1744. end;
  1745. // assign
  1746. SetDataPtr(pImage, Format, NewWidth, NewHeight);
  1747. except
  1748. FreeMem(pImage);
  1749. end;
  1750. end;
  1751. end;
  1752. procedure TtsImage.SetDataPtr(aData: Pointer; aFormat: TtsFormat; aWidth, aHeight: Integer);
  1753. begin
  1754. fScanLinesValid := False;
  1755. if fData <> nil then
  1756. FreeMemory(fData);
  1757. fData := aData;
  1758. if fData <> nil then begin
  1759. fWidth := aWidth;
  1760. fHeight := aHeight;
  1761. fFormat := aFormat;
  1762. end else begin
  1763. fWidth := 0;
  1764. fHeight := 0;
  1765. fFormat := tsFormatEmpty;
  1766. end;
  1767. end;
  1768. procedure TtsImage.UpdateScanLines;
  1769. var
  1770. Idx, LineSize: Integer;
  1771. Temp: pByte;
  1772. begin
  1773. LineSize := fWidth * GetFormatSize(fFormat);
  1774. SetLength(fScanLines, fHeight);
  1775. for Idx := 0 to fHeight -1 do begin
  1776. Temp := fData;
  1777. Inc(Temp, Idx * LineSize);
  1778. fScanLines[Idx] := Temp;
  1779. end;
  1780. fScanLinesValid := True;
  1781. end;
  1782. { TtsFont }
  1783. procedure TtsFont.AddChar(CharCode: WideChar; Char: TtsChar);
  1784. var
  1785. Idx1, Idx2: Integer;
  1786. Chars: PtsFontCharArray;
  1787. begin
  1788. Idx1 := Hi(Ord(CharCode));
  1789. Chars := fChars[Idx1];
  1790. if Chars = nil then begin
  1791. New(Chars);
  1792. FillChar(Chars^, SizeOf(TtsFontCharArray), 0);
  1793. fChars[Idx1] := Chars;
  1794. end;
  1795. if Chars <> nil then begin
  1796. Idx2 := Lo(Ord(CharCode));
  1797. Chars^.Chars[Idx2] := Char;
  1798. Chars^.CharCount := Chars^.CharCount + 1;
  1799. end;
  1800. end;
  1801. procedure TtsFont.ClearChars;
  1802. var
  1803. Idx1, Idx2: Integer;
  1804. Chars: PtsFontCharArray;
  1805. Char: TtsChar;
  1806. begin
  1807. // iterate first step
  1808. for Idx1 := Low(fChars) to High(fChars) do begin
  1809. Chars := fChars[Idx1];
  1810. // iterate second step
  1811. if Chars <> nil then begin
  1812. for Idx2 := Low(Chars^.Chars) to High(Chars^.Chars) do begin
  1813. Char := Chars^.Chars[Idx2];
  1814. // free char
  1815. if Char <> nil then begin
  1816. if Char.RendererImageReference <> nil then begin
  1817. if fRenderer <> nil then
  1818. fRenderer.RemoveImageReference(Char.RendererImageReference);
  1819. Char.RendererImageReference.Free;
  1820. end;
  1821. Char.Free;
  1822. end;
  1823. end;
  1824. // dispose
  1825. fChars[Idx1] := nil;
  1826. dispose(Chars);
  1827. end;
  1828. end;
  1829. end;
  1830. constructor TtsFont.Create(Renderer: TtsRenderer; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  1831. begin
  1832. inherited Create;
  1833. fRenderer := Renderer;
  1834. fSize := Size;
  1835. fStyle := Style;
  1836. fFormat := Format;
  1837. fAntiAliasing := AntiAliasing;
  1838. end;
  1839. procedure TtsFont.DeleteChar(CharCode: WideChar);
  1840. var
  1841. Idx1, Idx2: Integer;
  1842. Chars: PtsFontCharArray;
  1843. Char: TtsChar;
  1844. begin
  1845. // first step
  1846. Idx1 := Hi(Ord(CharCode));
  1847. Chars := fChars[Idx1];
  1848. if Chars <> nil then begin
  1849. // second step
  1850. Idx2 := Lo(Ord(CharCode));
  1851. Char := Chars^.Chars[Idx2];
  1852. if Char <> nil then begin
  1853. Chars^.Chars[Idx2] := nil;
  1854. Chars^.CharCount := Chars^.CharCount -1;
  1855. // no chars so delete the subpage
  1856. if Chars^.CharCount = 0 then begin
  1857. fChars[Idx1] := nil;
  1858. Dispose(Chars);
  1859. end;
  1860. if Char.RendererImageReference <> nil then begin
  1861. if fRenderer <> nil then
  1862. fRenderer.RemoveImageReference(Char.RendererImageReference);
  1863. Char.RendererImageReference.Free;
  1864. end;
  1865. Char.Free;
  1866. end;
  1867. end;
  1868. end;
  1869. destructor TtsFont.Destroy;
  1870. begin
  1871. // Chars
  1872. ClearChars;
  1873. inherited;
  1874. end;
  1875. function TtsFont.GetChar(CharCode: WideChar): TtsChar;
  1876. {$IFDEF TS_PURE_PASCAL}
  1877. var
  1878. Chars: PtsFontCharArray;
  1879. begin
  1880. // first step
  1881. Chars := fChars[Hi(Ord(CharCode))];
  1882. // second step
  1883. if Chars <> nil then
  1884. Result := Chars^.Chars[Lo(Ord(CharCode))]
  1885. else
  1886. Result := nil;
  1887. {$else}
  1888. asm
  1889. add eax, offset TtsFont.fChars // add offset of fChars to self
  1890. movzx ecx, dh // extract high byte to ecx
  1891. mov eax, dword ptr [eax + ecx * 4] // copy array element to eax
  1892. test eax, eax // subarray is empty
  1893. jz @@end
  1894. movzx edx, dl // extract lower byte to ed x
  1895. mov eax, dword ptr [eax + edx * 4] // copy array element to eax
  1896. @@end:
  1897. {$endif}
  1898. end;
  1899. procedure TtsFont.GetTextMetric(var Metric: TtsTextMetric);
  1900. begin
  1901. Metric.Ascent := Ascent;
  1902. Metric.Descent := Descent;
  1903. Metric.LineSkip := Ascent + Descent + ExternalLeading;
  1904. Metric.LineSkip_with_LineSpace := Metric.LineSkip + LineSpacing;
  1905. end;
  1906. // May be fpc has problems because it's an virtual function
  1907. function TtsFont.Validate(CharCode: WideChar): Boolean;
  1908. //{$IFDEF TS_PURE_PASCAL}
  1909. begin
  1910. Result := GetChar(CharCode) <> nil;
  1911. //{$else}
  1912. //asm
  1913. // // self is still in eax
  1914. // // charcode is still is edx
  1915. // call TtsFont.GetChar
  1916. // test eax, eax
  1917. // setnz al
  1918. //{$endif}
  1919. end;
  1920. { TtsFontCreator }
  1921. procedure TtsFontCreator.AddChar(CharCode: WideChar);
  1922. var
  1923. tsChar: TtsChar;
  1924. GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance: Integer;
  1925. CharImage: TtsImage;
  1926. begin
  1927. if fCreateChars and (Ord(CharCode) > 0) then begin
  1928. tsChar := GetChar(CharCode);
  1929. // Check if the char allready was added
  1930. if tsChar = nil then begin
  1931. // check if the Char exists in the font
  1932. if GetGlyphMetrics(CharCode, GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance) then
  1933. if (GlyphOriginX <> 0) or (GlyphOriginY <> 0) or (GlyphWidth <> 0) or (GlyphHeight <> 0) or (Advance <> 0) then begin
  1934. // Getting Image of Char
  1935. CharImage := TtsImage.Create;
  1936. try
  1937. if fRenderer.SaveImages then begin
  1938. if (GlyphWidth > 0) and (GlyphHeight > 0) then begin
  1939. // getting char image
  1940. GetCharImage(CharCode, CharImage);
  1941. end;
  1942. end;
  1943. if (tsStyleUnderline in Style) or (tsStyleStrikeout in Style) then begin
  1944. if (CharImage.Width = 0) and (CharImage.Height = 0) then begin
  1945. CharImage.CreateEmpty(tsFormatRGBA8, Advance, 1);
  1946. GlyphOriginY := 1;
  1947. end;
  1948. end;
  1949. // Create new Entry for Char
  1950. tsChar := TtsChar.Create(CharCode);
  1951. tsChar.GlyphOriginX := GlyphOriginX;
  1952. tsChar.GlyphOriginY := GlyphOriginY;
  1953. tsChar.Advance := Advance;
  1954. tsChar.GlyphRect.Left := 0;
  1955. tsChar.GlyphRect.Top := 0;
  1956. tsChar.GlyphRect.Right := CharImage.Width;
  1957. tsChar.GlyphRect.Bottom := CharImage.Height;
  1958. AddChar(CharCode, tsChar);
  1959. if fRenderer.SaveImages then begin
  1960. try
  1961. // apply underline style
  1962. if tsStyleUnderline in Style then
  1963. DrawLine(tsChar, CharImage, UnderlinePosition, UnderlineSize);
  1964. // apply strikeout stlye
  1965. if tsStyleStrikeout in Style then
  1966. DrawLine(tsChar, CharImage, StrikeoutPosition, StrikeoutSize);
  1967. except
  1968. CharImage.FillColor(1, 0, 0, 0, TS_CHANNELS_RGB, cModesNormal);
  1969. end;
  1970. // PostProcessing
  1971. DoPostProcess(CharImage, tsChar);
  1972. // Add invisible border for resizing (at last before adding)
  1973. if AddResizingBorder then begin
  1974. tsChar.HasResizingBorder := True;
  1975. CharImage.AddResizingBorder(tsChar);
  1976. end;
  1977. // Add Image to Renderer
  1978. tsChar.RendererImageReference := fRenderer.AddImage(tsChar, CharImage);
  1979. end;
  1980. finally
  1981. FreeAndNil(CharImage);
  1982. end;
  1983. end;
  1984. end;
  1985. end;
  1986. end;
  1987. function TtsFontCreator.AddPostProcessStep(PostProcessStep: TtsPostProcessStep): TtsPostProcessStep;
  1988. begin
  1989. Result := PostProcessStep;
  1990. fPostProcessSteps.Add(PostProcessStep);
  1991. end;
  1992. procedure TtsFontCreator.ClearPostProcessSteps;
  1993. var
  1994. Idx: Integer;
  1995. begin
  1996. for Idx := fPostProcessSteps.Count -1 downto 0 do
  1997. DeletePostProcessStep(Idx);
  1998. fPostProcessSteps.Clear;
  1999. end;
  2000. constructor TtsFontCreator.Create(Renderer: TtsRenderer; Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  2001. begin
  2002. inherited Create(Renderer, Size, Style, Format, AntiAliasing);
  2003. fCreateChars := True;
  2004. fPostProcessSteps := TList.Create;
  2005. end;
  2006. procedure TtsFontCreator.DeletePostProcessStep(Index: Integer);
  2007. var
  2008. Entry: TtsPostProcessStep;
  2009. begin
  2010. if (Index >= 0) and (Index < fPostProcessSteps.Count) then begin
  2011. Entry := fPostProcessSteps[Index];
  2012. Entry.Free;
  2013. fPostProcessSteps.Delete(Index);
  2014. end;
  2015. end;
  2016. destructor TtsFontCreator.Destroy;
  2017. begin
  2018. if fPostProcessSteps <> nil then begin
  2019. ClearPostProcessSteps;
  2020. FreeAndNil(fPostProcessSteps);
  2021. end;
  2022. inherited;
  2023. end;
  2024. procedure TtsFontCreator.DoPostProcess(var CharImage: TtsImage; const tsChar: TtsChar);
  2025. var
  2026. Idx: Integer;
  2027. Entry: TtsPostProcessStep;
  2028. begin
  2029. if not CharImage.Empty then begin
  2030. for Idx := 0 to fPostProcessSteps.Count - 1 do begin
  2031. Entry := fPostProcessSteps[Idx];
  2032. if Entry.IsInRange(tsChar.CharCode) then
  2033. Entry.PostProcess(CharImage, tsChar);
  2034. end;
  2035. end;
  2036. end;
  2037. procedure TtsFontCreator.DrawLine(Char: TtsChar; CharImage: TtsImage; LinePosition, LineSize: Integer);
  2038. var
  2039. NewWidth, NewHeight, NewPosX, NewPosY, YOffset, Idx: Integer;
  2040. procedure FillLine(pPix: ptsColor);
  2041. var
  2042. Idx: Integer;
  2043. begin
  2044. Idx := NewWidth;
  2045. while Idx > 0 do begin
  2046. pPix^.Red := $FF;
  2047. pPix^.Green := $FF;
  2048. pPix^.Blue := $FF;
  2049. pPix^.Alpha := $FF;
  2050. Inc(pPix);
  2051. Dec(Idx);
  2052. end;
  2053. end;
  2054. begin
  2055. if LineSize <= 0 then
  2056. Exit;
  2057. LinePosition := LinePosition - LineSize;
  2058. // calculate width and height
  2059. NewWidth := CharImage.Width;
  2060. NewPosX := 0;
  2061. NewHeight := CharImage.Height;
  2062. NewPosY := 0;
  2063. // expand image to the full advance
  2064. if Char.Advance > CharImage.Width then
  2065. NewWidth := Char.Advance;
  2066. // add glyph position to image width and set position
  2067. if Char.GlyphOriginX > Char.GlyphRect.Left then begin
  2068. NewWidth := NewWidth + Char.GlyphOriginX;
  2069. NewPosX := Char.GlyphOriginX;
  2070. end;
  2071. if Char.GlyphOriginX < 0 then
  2072. NewWidth := NewWidth - Char.GlyphOriginX;
  2073. // line is under the image
  2074. if LinePosition < (Char.GlyphOriginY - CharImage.Height) then
  2075. NewHeight := NewHeight + (Char.GlyphOriginY - CharImage.Height - LinePosition);
  2076. // line is above the image
  2077. if LinePosition + LineSize > Char.GlyphOriginY then begin
  2078. NewPosY := ((LinePosition + LineSize) - Char.GlyphOriginY);
  2079. NewHeight := NewHeight + NewPosY;
  2080. end;
  2081. // resize
  2082. CharImage.Resize(NewWidth, NewHeight, NewPosX, NewPosY);
  2083. // draw lines
  2084. YOffset := (Char.GlyphOriginY + NewPosY) - LinePosition;
  2085. for Idx := 1 to LineSize do
  2086. FillLine(CharImage.ScanLine[YOffset - Idx]);
  2087. // move glyph rect
  2088. Char.GlyphRect.Left := Char.GlyphRect.Left + NewPosX;
  2089. Char.GlyphRect.Right := Char.GlyphRect.Right + NewPosX;
  2090. Char.GlyphRect.Top := Char.GlyphRect.Top + NewPosY;
  2091. Char.GlyphRect.Bottom := Char.GlyphRect.Bottom + NewPosY;
  2092. end;
  2093. function TtsFontCreator.GetPostProcessStep(Index: Integer): TtsPostProcessStep;
  2094. begin
  2095. if (Index >= 0) and (Index < fPostProcessSteps.Count) then
  2096. Result := TtsPostProcessStep(fPostProcessSteps[Index])
  2097. else
  2098. Result := nil;
  2099. end;
  2100. function TtsFontCreator.GetPostProcessStepCount: Integer;
  2101. begin
  2102. Result := fPostProcessSteps.Count;
  2103. end;
  2104. function TtsFontCreator.Validate(CharCode: WideChar): Boolean;
  2105. begin
  2106. Result := Inherited Validate(CharCode);
  2107. // if char wasnt found then create it.
  2108. if not Result then begin
  2109. AddChar(CharCode);
  2110. // and test for creation
  2111. Result := Inherited Validate(CharCode);
  2112. end;
  2113. end;
  2114. { TtsPostProcessStep }
  2115. procedure TtsPostProcessStep.AddUsageChars(Usage: TtsFontProcessStepUsage; Chars: pWideChar);
  2116. begin
  2117. if Chars <> nil then
  2118. while Chars^ <> #0 do begin
  2119. AddUsageRange(Usage, Chars^, Chars^);
  2120. Inc(Chars);
  2121. end;
  2122. end;
  2123. procedure TtsPostProcessStep.AddUsageRange(Usage: TtsFontProcessStepUsage;
  2124. StartChar, EndChar: WideChar);
  2125. var
  2126. pItem: PtsPostProcessStepRange;
  2127. begin
  2128. New(pItem);
  2129. pItem^.StartChar := StartChar;
  2130. pItem^.EndChar := EndChar;
  2131. case Usage of
  2132. tsUInclude:
  2133. fIncludeCharRange.Add(pItem);
  2134. tsUExclude:
  2135. fExcludeCharRange.Add(pItem);
  2136. end;
  2137. end;
  2138. procedure TtsPostProcessStep.ClearExcludeRange;
  2139. begin
  2140. ClearList(fExcludeCharRange);
  2141. end;
  2142. procedure TtsPostProcessStep.ClearIncludeRange;
  2143. begin
  2144. ClearList(fIncludeCharRange);
  2145. end;
  2146. procedure TtsPostProcessStep.ClearList(List: TList);
  2147. var
  2148. Idx: Integer;
  2149. pItem: PtsPostProcessStepRange;
  2150. begin
  2151. for Idx := 0 to List.Count - 1 do begin
  2152. pItem := List[Idx];
  2153. Dispose(pItem);
  2154. end;
  2155. List.Clear;
  2156. end;
  2157. constructor TtsPostProcessStep.Create;
  2158. begin
  2159. inherited Create;
  2160. fIncludeCharRange := TList.Create;
  2161. fExcludeCharRange := TList.Create;
  2162. end;
  2163. destructor TtsPostProcessStep.Destroy;
  2164. begin
  2165. ClearIncludeRange;
  2166. ClearExcludeRange;
  2167. fIncludeCharRange.Free;
  2168. fExcludeCharRange.Free;
  2169. inherited;
  2170. end;
  2171. function TtsPostProcessStep.IsInRange(CharCode: WideChar): Boolean;
  2172. var
  2173. Idx: Integer;
  2174. pItem: PtsPostProcessStepRange;
  2175. begin
  2176. // Look in include range
  2177. if fIncludeCharRange.Count <> 0 then begin
  2178. Result := False;
  2179. for Idx := 0 to fIncludeCharRange.Count - 1 do begin
  2180. pItem := fIncludeCharRange[Idx];
  2181. if (CharCode >= pItem^.StartChar) and (CharCode <= pItem^.EndChar) then begin
  2182. Result := True;
  2183. Break;
  2184. end;
  2185. end;
  2186. end else
  2187. Result := True;
  2188. // Look in exclude range but only if its included
  2189. if Result then begin
  2190. for Idx := 0 to fExcludeCharRange.Count - 1 do begin
  2191. pItem := fExcludeCharRange[Idx];
  2192. if (CharCode >= pItem^.StartChar) and (CharCode <= pItem^.EndChar) then begin
  2193. Result := False;
  2194. Break;
  2195. end;
  2196. end;
  2197. end;
  2198. end;
  2199. { TtsFontCreatorSDL }
  2200. constructor TtsFontCreatorSDL.Create(Renderer: TtsRenderer; const Filename: AnsiString; Size: Integer;
  2201. Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  2202. var
  2203. TempStyle: Integer;
  2204. begin
  2205. inherited Create(Renderer, Size, Style, Format, AntiAliasing);
  2206. // Init SDL_ttf
  2207. if (TTF_WasInit = 0) then
  2208. if (TTF_Init < 0) then
  2209. raise Exception.Create('TtsFontCreator.Create: TTF_Init error');
  2210. // Create FFT_Font
  2211. fSDLFont := TTF_OpenFont(pAnsiChar(Filename), Size);
  2212. // Getting style - SDL_ttf dosn't support it. so we only have normal
  2213. fFontFileStyle := TS_STYLE_NORMAL;
  2214. // getting props
  2215. Ascent := TTF_FontAscent(fSDLFont);
  2216. Descent := -TTF_FontDescent(fSDLFont);
  2217. ExternalLeading := TTF_FontLineSkip(fSDLFont) - (Ascent + Descent);
  2218. // SDL_ttf dosn't support it so we must calculate it by our self
  2219. UnderlinePosition := - round(Ascent / 8);
  2220. if UnderlinePosition > -1 then
  2221. UnderlinePosition := -1;
  2222. if tsStyleBold in Style then
  2223. UnderlineSize := round(Ascent / 8)
  2224. else
  2225. UnderlineSize := round(Ascent / 13);
  2226. if UnderlineSize < 1 then
  2227. UnderlineSize := 1;
  2228. StrikeoutPosition := round(Ascent / 3.5);
  2229. if tsStyleBold in Style then
  2230. StrikeoutSize := round(Ascent / 14)
  2231. else
  2232. StrikeoutSize := round(Ascent / 19);
  2233. if StrikeoutSize < 1 then
  2234. StrikeoutSize := 1;
  2235. FixedWidth := TTF_FontFaceIsFixedWidth(fSDLFont) > 0;
  2236. Copyright := '';
  2237. FaceName := TTF_FontFaceFamilyName(fSDLFont);
  2238. StyleName := TTF_FontFaceStyleName(fSDLFont);
  2239. FullName := FaceName + #32 + StyleName;
  2240. // Set style
  2241. TempStyle := 0;
  2242. if tsStyleBold in Style then
  2243. TempStyle := TempStyle or TTF_STYLE_BOLD;
  2244. if tsStyleItalic in Style then
  2245. TempStyle := TempStyle or TTF_STYLE_ITALIC;
  2246. // if tsStyleUnderline in Style then
  2247. // TempStyle := TempStyle or TTF_STYLE_UNDERLINE;
  2248. TTF_SetFontStyle(fSDLFont, TempStyle);
  2249. end;
  2250. destructor TtsFontCreatorSDL.Destroy;
  2251. begin
  2252. // Destroy Font
  2253. TTF_CloseFont(fSDLFont);
  2254. fSDLFont := nil;
  2255. inherited;
  2256. end;
  2257. procedure TtsFontCreatorSDL.GetCharImage(CharCode: WideChar; const CharImage: TtsImage);
  2258. const
  2259. WHITE: TSDL_Color = (r: $FF; g: $FF; b: $FF; unused: 0);
  2260. BLACK: TSDL_Color = (r: $00; g: $00; b: $00; unused: 0);
  2261. var
  2262. CharSurface: PSDL_Surface;
  2263. X, Y, TempWidth: Integer;
  2264. pSource: pByte;
  2265. pDest: PtsColor;
  2266. function GetPaletteEntry(Index: Byte): Byte;
  2267. begin
  2268. Result := 0;
  2269. with CharSurface^.format^ do begin
  2270. if palette <> nil then
  2271. if (palette^.ncolors > 0) and (Index < palette^.ncolors) then
  2272. Result := palette^.colors[Index].r
  2273. end;
  2274. end;
  2275. begin
  2276. //CharCode: Needs to use an widestring because of #0 endchar
  2277. case AntiAliasing of
  2278. tsAANone:
  2279. CharSurface := TTF_RenderGlyph_Solid(fSDLFont, Ord(CharCode), WHITE);
  2280. tsAANormal:
  2281. CharSurface := TTF_RenderGlyph_Shaded(fSDLFont, Ord(CharCode), WHITE, BLACK);
  2282. end;
  2283. if CharSurface <> nil then
  2284. try
  2285. CharImage.CreateEmpty(fFormat, CharSurface^.Width, CharSurface^.Height);
  2286. try
  2287. TempWidth := CharSurface^.Width;
  2288. if TempWidth mod 4 > 0 then
  2289. TempWidth := (TempWidth div 4 + 1) * 4;
  2290. for Y := 0 to CharSurface^.Height - 1 do begin
  2291. pDest := CharImage.ScanLine[Y];
  2292. pSource := CharSurface^.Pixels;
  2293. Inc(pSource, Y * TempWidth);
  2294. for X := 0 to CharSurface^.Width - 1 do begin
  2295. pDest^.Red := $FF;
  2296. pDest^.Green := $FF;
  2297. pDest^.Blue := $FF;
  2298. pDest^.Alpha := GetPaletteEntry(pSource^);
  2299. Inc(pSource);
  2300. Inc(pDest);
  2301. end;
  2302. end;
  2303. except
  2304. CharImage.Free;
  2305. end;
  2306. finally
  2307. SDL_FreeSurface(CharSurface);
  2308. end;
  2309. end;
  2310. function TtsFontCreatorSDL.GetGlyphMetrics(CharCode: WideChar; var GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance: Integer): Boolean;
  2311. var
  2312. MinX, MaxX, MinY, MaxY: Integer;
  2313. begin
  2314. if fSDLFont <> nil then begin
  2315. Result := TTF_GlyphMetrics(fSDLFont, Ord(CharCode), MinX, MaxX, MinY, MaxY, Advance) = 0;
  2316. GlyphWidth := MaxX - MinX;
  2317. GlyphHeight := MaxY - MinY;
  2318. GlyphOriginX := MinX;
  2319. GlyphOriginY := GlyphHeight + MinY;
  2320. end
  2321. else
  2322. Result := False;
  2323. end;
  2324. { TtsFontCreatorGDIFontFace }
  2325. constructor TtsFontCreatorGDIFontFace.Create(Renderer: TtsRenderer; const Fontname: AnsiString;
  2326. Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  2327. var
  2328. Idx: Integer;
  2329. LogFont: TLogFontA;
  2330. DC: HDC;
  2331. TableName: Cardinal;
  2332. Buffer: Pointer;
  2333. BufferSize: Cardinal;
  2334. Lang: AnsiString;
  2335. TextMetric: TTextMetricW;
  2336. OutTextMetric: TOutlineTextmetricW;
  2337. begin
  2338. inherited Create (Renderer, Size, Style, Format, AntiAliasing);
  2339. // setting up matrix
  2340. FillChar(fMat2, SizeOf(TMat2), $00);
  2341. fMat2.eM11.Value := 1;
  2342. fMat2.eM22.Value := 1;
  2343. fFontname := Fontname;
  2344. // Creating Font
  2345. FillChar(LogFont, SizeOf(LogFont), 0);
  2346. // name
  2347. fFontname := Fontname;
  2348. for Idx := 1 to min(Length(Fontname), Length(LogFont.lfFaceName)) do
  2349. LogFont.lfFaceName[Idx -1] := Fontname[Idx];
  2350. // char set
  2351. LogFont.lfCharSet := DEFAULT_CHARSET;
  2352. // size
  2353. // fPointSize := PointSize;
  2354. LogFont.lfHeight := -Size; //-MulDiv(PointSize, GetDeviceCaps(Temp.Canvas.Handle, LOGPIXELSY), 72);
  2355. // style
  2356. if tsStyleBold in Style then
  2357. LogFont.lfWeight := FW_BOLD
  2358. else
  2359. LogFont.lfWeight := FW_NORMAL;
  2360. if tsStyleItalic in Style then
  2361. LogFont.lfItalic := 1;
  2362. if tsStyleUnderline in Style then
  2363. LogFont.lfUnderline := 1;
  2364. // smooth
  2365. case AntiAliasing of
  2366. tsAANone:
  2367. LogFont.lfQuality := NONANTIALIASED_QUALITY;
  2368. tsAANormal:
  2369. LogFont.lfQuality := ANTIALIASED_QUALITY;
  2370. // tsSmoothSmooth:
  2371. // begin
  2372. // if Smooth = tsSmoothSmooth then
  2373. // fMat2.eM11.Value := 3;
  2374. // end;
  2375. end;
  2376. // create font
  2377. fFontHandle := CreateFontIndirectA(LogFont);
  2378. // Getting informations about font
  2379. DC := CreateCompatibleDC(0);
  2380. try
  2381. SelectObject(DC, fFontHandle);
  2382. // find strings in text
  2383. TableName := MakeTTTableName('n', 'a', 'm', 'e');
  2384. BufferSize := GetFontData(DC, TableName, 0, nil, 0);
  2385. if BufferSize <> GDI_ERROR then begin
  2386. GetMem(Buffer, BufferSize);
  2387. try
  2388. if GetFontData(DC, TableName, 0, Buffer, BufferSize) <> GDI_ERROR then begin
  2389. SetLength(Lang, 4);
  2390. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @Lang[1], 4);
  2391. GetTTString(Buffer, BufferSize, NAME_ID_COPYRIGHT, StrToInt('$' + String(Lang)), fCopyright);
  2392. GetTTString(Buffer, BufferSize, NAME_ID_FACE_NAME, StrToInt('$' + String(Lang)), fFaceName);
  2393. GetTTString(Buffer, BufferSize, NAME_ID_STYLE_NAME, StrToInt('$' + String(Lang)), fStyleName);
  2394. GetTTString(Buffer, BufferSize, NAME_ID_FULL_NAME, StrToInt('$' + String(Lang)), fFullName);
  2395. end;
  2396. finally
  2397. FreeMem(Buffer);
  2398. end;
  2399. end;
  2400. // Text Metric
  2401. GetTextMetricsW(DC, TextMetric);
  2402. Ascent := TextMetric.tmAscent;
  2403. Descent := TextMetric.tmDescent;
  2404. ExternalLeading := TextMetric.tmExternalLeading;
  2405. DefaultChar := TextMetric.tmDefaultChar;
  2406. // inverse logic of the bit. clear then fixed pitch
  2407. FixedWidth := TextMetric.tmPitchAndFamily and TMPF_FIXED_PITCH = 0;
  2408. // style
  2409. FontFileStyle := TS_STYLE_NORMAL;
  2410. if TextMetric.tmWeight > 400 then
  2411. FontFileStyle := FontFileStyle or TS_STYLE_BOLD;
  2412. if TextMetric.tmItalic > 0 then
  2413. FontFileStyle := FontFileStyle or TS_STYLE_ITALIC;
  2414. if TextMetric.tmUnderlined > 0 then
  2415. FontFileStyle := FontFileStyle or TS_STYLE_UNDERLINE;
  2416. if TextMetric.tmStruckOut > 0 then
  2417. FontFileStyle := FontFileStyle or TS_STYLE_STRIKEOUT;
  2418. // Outline Text Metric
  2419. GetOutlineTextMetricsW(DC, SizeOf(OutTextMetric), OutTextMetric);
  2420. UnderlinePosition := OutTextMetric.otmsUnderscorePosition;
  2421. UnderlineSize := OutTextMetric.otmsUnderscoreSize;
  2422. if UnderlineSize < 1 then
  2423. UnderlineSize := 1;
  2424. StrikeoutPosition := OutTextMetric.otmsStrikeoutPosition;
  2425. StrikeoutSize := OutTextMetric.otmsStrikeoutSize;
  2426. if StrikeoutSize < 1 then
  2427. StrikeoutSize := 1;
  2428. finally
  2429. DeleteDC(DC);
  2430. end;
  2431. end;
  2432. destructor TtsFontCreatorGDIFontFace.Destroy;
  2433. begin
  2434. DeleteObject(fFontHandle);
  2435. inherited;
  2436. end;
  2437. procedure TtsFontCreatorGDIFontFace.GetCharImage(CharCode: WideChar; const CharImage: TtsImage);
  2438. var
  2439. DC: HDC;
  2440. begin
  2441. DC := CreateCompatibleDC(0);
  2442. try
  2443. SelectObject(DC, fFontHandle);
  2444. case AntiAliasing of
  2445. tsAANone:
  2446. GetCharImageNone(DC, CharCode, CharImage);
  2447. tsAANormal:
  2448. GetCharImageAntialiased(DC, CharCode, CharImage);
  2449. end;
  2450. finally
  2451. DeleteDC(DC);
  2452. end;
  2453. end;
  2454. procedure TtsFontCreatorGDIFontFace.GetCharImageAntialiased(DC: HDC; CharCode: WideChar; const CharImage: TtsImage);
  2455. var
  2456. Metric: TGlyphMetrics;
  2457. pBuffer: Pointer;
  2458. Size, OutlineResult: Cardinal;
  2459. GlyphIndex: Integer;
  2460. X, Y, Height, Width, Spacer: Integer;
  2461. pDest: PtsColor;
  2462. pSrc: pByte;
  2463. procedure CopyPixel;
  2464. var
  2465. Idx: Integer;
  2466. Temp, Count: Cardinal;
  2467. begin
  2468. Count := Min(X, fMat2.eM11.Value);
  2469. Temp := 0;
  2470. for Idx := 0 to Count -1 do begin
  2471. Temp := Temp + pSrc^;
  2472. Inc(pSrc);
  2473. end;
  2474. Dec(X, Count);
  2475. pDest^.Red := $FF;
  2476. pDest^.Green := $FF;
  2477. pDest^.Blue := $FF;
  2478. pDest^.Alpha := $FF * Temp div ($40 * Cardinal(fMat2.eM11.Value));
  2479. Inc(pDest);
  2480. end;
  2481. begin
  2482. FillChar(Metric, SizeOf(TGlyphMetrics), $00);
  2483. // Translate Glyphindex
  2484. GlyphIndex := GetGlyphIndex(CharCode);
  2485. // size
  2486. // if GlyphIndex <> 0 then
  2487. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @fMat2);
  2488. // else
  2489. // Size := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_GRAY8_BITMAP, Metric, 0, nil, fMat2);
  2490. if (Size <> GDI_ERROR) and (Size <> 0) then begin
  2491. GetMem(pBuffer, Size);
  2492. try
  2493. // glyphdata
  2494. // if GlyphIndex <> 0 then
  2495. OutlineResult := GetGlyphOutlineA(DC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, Size, pBuffer, @fMat2);
  2496. // else
  2497. // OutlineResult := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_GRAY8_BITMAP, Metric, Size, pBuffer, fMat2);
  2498. if OutlineResult <> GDI_ERROR then begin
  2499. // Image size
  2500. Height := Metric.gmBlackBoxY;
  2501. Width := Integer(Metric.gmBlackBoxX) div fMat2.eM11.Value;
  2502. if (Integer(Metric.gmBlackBoxX) mod fMat2.eM11.Value) <> 0 then
  2503. Width := Width + fMat2.eM11.Value - (Integer(Metric.gmBlackBoxX) mod fMat2.eM11.Value);
  2504. // spacer
  2505. if (Metric.gmBlackBoxX mod 4) <> 0 then
  2506. Spacer := 4 - (Metric.gmBlackBoxX mod 4)
  2507. else
  2508. Spacer := 0;
  2509. // copy image
  2510. if (Height > 0) and (Width > 0) then begin
  2511. CharImage.CreateEmpty(fFormat, Width, Height);
  2512. pSrc := pBuffer;
  2513. for Y := 0 to Height -1 do begin
  2514. pDest := CharImage.ScanLine[Y];
  2515. X := Metric.gmBlackBoxX;
  2516. while X > 0 do
  2517. CopyPixel;
  2518. if Spacer <> 0 then
  2519. Inc(pSrc, Spacer);
  2520. end;
  2521. end;
  2522. end;
  2523. finally
  2524. FreeMem(pBuffer);
  2525. end;
  2526. end;
  2527. end;
  2528. procedure TtsFontCreatorGDIFontFace.GetCharImageNone(DC: HDC; CharCode: WideChar; const CharImage: TtsImage);
  2529. var
  2530. Metric: TGlyphMetrics;
  2531. pBuffer: Pointer;
  2532. Size, OutlineResult: Cardinal;
  2533. GlyphIndex: Integer;
  2534. X, Y, Height, Width, SourceX, SourceWidth: Integer;
  2535. pDest: PtsColor;
  2536. pSrc: pByte;
  2537. procedure ExpandByte;
  2538. var
  2539. Idx, Count, SourceCount: Integer;
  2540. begin
  2541. SourceCount := Min(8, SourceX);
  2542. Count := Min(8, X);
  2543. for Idx := 1 to Count do begin
  2544. pDest^.Red := $FF;
  2545. pDest^.Green := $FF;
  2546. pDest^.Blue := $FF;
  2547. if (pSrc^ and $80) > 0 then
  2548. pDest^.Alpha := $FF
  2549. else
  2550. pDest^.Alpha := $00;
  2551. pSrc^ := (pSrc^ and not $80) shl 1;
  2552. Inc(pDest);
  2553. end;
  2554. Dec(SourceX, SourceCount);
  2555. Dec(X, Count);
  2556. end;
  2557. begin
  2558. // fMat2.eM11.Value must be 1
  2559. Assert(fMat2.eM11.Value = 1);
  2560. FillChar(Metric, SizeOf(TGlyphMetrics), $00);
  2561. // Translate Glyphindex
  2562. GlyphIndex := GetGlyphIndex(CharCode);
  2563. // size
  2564. // if GlyphIndex <> 0 then
  2565. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @fMat2);
  2566. // else
  2567. // Size := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_BITMAP, Metric, 0, nil, fMat2);
  2568. if (Size <> GDI_ERROR) and (Size <> 0) then begin
  2569. GetMem(pBuffer, Size);
  2570. try
  2571. // glyphdata
  2572. // if GlyphIndex <> 0 then
  2573. OutlineResult := GetGlyphOutlineA(DC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, Size, pBuffer, @fMat2);
  2574. // else
  2575. // OutlineResult := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_BITMAP, Metric, Size, pBuffer, fMat2);
  2576. if OutlineResult <> GDI_ERROR then begin
  2577. SourceWidth := (Size div Metric.gmBlackBoxY) * 8;
  2578. Width := Metric.gmBlackBoxX;
  2579. Height := Metric.gmBlackBoxY;
  2580. // copy image
  2581. if (Height > 0) and (Width > 0) then begin
  2582. CharImage.CreateEmpty(tsFormatRGBA8, Width, Height);
  2583. pSrc := pBuffer;
  2584. for Y := 0 to Height -1 do begin
  2585. pDest := CharImage.ScanLine[Y];
  2586. // copy data
  2587. SourceX := SourceWidth;
  2588. X := Width;
  2589. while SourceX > 0 do begin
  2590. ExpandByte;
  2591. Inc(pSrc);
  2592. end;
  2593. end;
  2594. end;
  2595. end;
  2596. finally
  2597. FreeMem(pBuffer);
  2598. end;
  2599. end;
  2600. end;
  2601. function TtsFontCreatorGDIFontFace.GetGlyphIndex(CharCode: WideChar): Integer;
  2602. var
  2603. // ReadRawData: Boolean;
  2604. DC: HDC;
  2605. GCPRes: TGCPResultsW;
  2606. begin
  2607. Result := 0;
  2608. // ReadRawData := True;
  2609. DC := CreateCompatibleDC(0);
  2610. try
  2611. SelectObject(DC, fFontHandle);
  2612. // windows nt
  2613. if Addr(GetCharacterPlacementW) <> nil then begin
  2614. FillChar(GCPRes, SizeOf(GCPRes), 0);
  2615. GetMem(GCPRes.lpGlyphs, SizeOf(Cardinal));
  2616. try
  2617. GCPRes.lStructSize := SizeOf(GCPRes);
  2618. GCPRes.lpGlyphs^ := 0;
  2619. GCPRes.nGlyphs := 1;
  2620. if GetCharacterPlacementW(DC, @CharCode, 1, GCP_MAXEXTENT, @GCPRes, 0) <> GDI_ERROR then begin
  2621. if (GCPRes.nGlyphs = 1) and (GCPRes.lpGlyphs <> nil) then begin
  2622. Result := GCPRes.lpGlyphs^;
  2623. // ReadRawData := False;
  2624. end;
  2625. end;
  2626. finally
  2627. FreeMem(GCPRes.lpGlyphs);
  2628. end;
  2629. end;
  2630. // windows 9x workaround
  2631. // ReadRawData := True;
  2632. // if ReadRawData then begin
  2633. // if GetTTUnicodeCharCount(DC) > 0 then
  2634. // Result := GetTTUnicodeGlyphIndex(DC, Ord(CharCode));
  2635. // end;
  2636. finally
  2637. DeleteDC(DC);
  2638. end;
  2639. end;
  2640. function TtsFontCreatorGDIFontFace.GetGlyphMetrics(CharCode: WideChar; var GlyphOriginX, GlyphOriginY, GlyphWidth, GlyphHeight, Advance: Integer): Boolean;
  2641. var
  2642. DC: HDC;
  2643. Metric: TGlyphMetrics;
  2644. Size: Cardinal;
  2645. GlyphIndex: Integer;
  2646. begin
  2647. Result := False;
  2648. // Set values to 0
  2649. GlyphOriginX := 0;
  2650. GlyphOriginY := 0;
  2651. GlyphWidth := 0;
  2652. GlyphHeight := 0;
  2653. Advance := 0;
  2654. // Translate Glyphindex
  2655. GlyphIndex := GetGlyphIndex(CharCode);
  2656. DC := CreateCompatibleDC(0);
  2657. try
  2658. SelectObject(DC, fFontHandle);
  2659. // get value of resulting bitmaps
  2660. case AntiAliasing of
  2661. tsAANone: begin
  2662. // if GlyphIndex <> 0 then
  2663. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @fMat2);
  2664. // else
  2665. // Size := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_BITMAP, Metric, 0, nil, fMat2);
  2666. end;
  2667. tsAANormal: begin
  2668. // if GlyphIndex <> 0 then
  2669. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_GRAY8_BITMAP or GGO_GLYPH_INDEX, @Metric, 0, nil, @fMat2);
  2670. // else
  2671. // Size := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_GRAY8_BITMAP, Metric, 0, nil, fMat2);
  2672. end;
  2673. else
  2674. Size := 0;
  2675. end;
  2676. // dosn't work so get metric value
  2677. if (Size = GDI_ERROR) or (Size = 0) then begin
  2678. // if GlyphIndex <> 0 then
  2679. Size := GetGlyphOutlineA(DC, GlyphIndex, GGO_METRICS or GGO_GLYPH_INDEX, @Metric, 0, nil, @fMat2);
  2680. // else
  2681. // Size := GetGlyphOutlineA(DC, Ord(fDefaultChar), GGO_METRICS, Metric, 0, nil, fMat2);
  2682. end;
  2683. // we have values?
  2684. if (Size <> GDI_ERROR) and (Size > 0) then begin
  2685. GlyphOriginX := Round(Metric.gmptGlyphOrigin.X / fMat2.eM11.value);
  2686. GlyphOriginY := Metric.gmptGlyphOrigin.Y;
  2687. GlyphWidth := Round(Metric.gmBlackBoxX / fMat2.eM11.value);
  2688. GlyphHeight := Metric.gmBlackBoxY;
  2689. Advance := Round(Metric.gmCellIncX / fMat2.eM11.value);
  2690. Result := True;
  2691. end;
  2692. finally
  2693. DeleteDC(DC)
  2694. end;
  2695. end;
  2696. { TtsFontCreatorGDIFile }
  2697. constructor TtsFontCreatorGDIFile.Create(Renderer: TtsRenderer; const Filename: AnsiString;
  2698. Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  2699. var
  2700. FaceName: AnsiString;
  2701. begin
  2702. // filename
  2703. fFileName := StrNew(pAnsiChar(Filename));
  2704. fFontRegistred := false;
  2705. FaceName := '';
  2706. if GetFaceName(fFilename, FaceName) then
  2707. fFontRegistred := RegisterFont(fFilename, False);
  2708. // inherited
  2709. inherited Create(Renderer, FaceName, Size, Style, Format, AntiAliasing);
  2710. end;
  2711. destructor TtsFontCreatorGDIFile.Destroy;
  2712. begin
  2713. inherited;
  2714. // unregister font
  2715. if fFontRegistred then
  2716. UnRegisterFont(fFilename, False);
  2717. StrDispose(fFileName);
  2718. end;
  2719. function TtsFontCreatorGDIFile.GetFaceName(Filename: PAnsiChar; var Face: AnsiString): boolean;
  2720. var
  2721. Lang: AnsiString;
  2722. begin
  2723. SetLength(Lang, 4);
  2724. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @Lang[1], 4);
  2725. Face := GetTTFontFullNameFromFile(Filename, StrToInt('$' + String(Lang)));
  2726. Result := Face <> '';
  2727. end;
  2728. function TtsFontCreatorGDIFile.RegisterFont(Filename: pAnsiChar; RegisterPublic: Boolean): boolean;
  2729. var
  2730. Flags: Cardinal;
  2731. begin
  2732. Result := False;
  2733. // Flags
  2734. if not RegisterPublic then
  2735. Flags := FR_PRIVATE or FR_NOT_ENUM
  2736. else
  2737. Flags := 0;
  2738. // AddFontResource
  2739. if Addr(AddFontResourceExA) <> nil then
  2740. Result := AddFontResourceExA(FileName, Flags, nil) > 0
  2741. else
  2742. if Addr(AddFontResourceA) <> nil then
  2743. Result := AddFontResourceA(FileName) > 0;
  2744. end;
  2745. function TtsFontCreatorGDIFile.UnRegisterFont(Filename: pAnsiChar; RegisterPublic: Boolean): boolean;
  2746. var
  2747. Flags: Cardinal;
  2748. begin
  2749. Result := False;
  2750. // Flags
  2751. if not RegisterPublic then
  2752. Flags := FR_PRIVATE or FR_NOT_ENUM
  2753. else
  2754. Flags := 0;
  2755. // RemoveFontResource
  2756. if Addr(RemoveFontResourceExA) <> nil then
  2757. Result := RemoveFontResourceExA(FileName, Flags, nil)
  2758. else
  2759. if Addr(RemoveFontResourceA) <> nil then
  2760. Result := RemoveFontResourceA(FileName);
  2761. end;
  2762. { TtsFontCreatorGDIFile }
  2763. constructor TtsFontCreatorGDIStream.Create(Renderer: TtsRenderer; const Source: TStream;
  2764. Size: Integer; Style: TtsFontStyles; Format: TtsFormat; AntiAliasing: TtsAntiAliasing);
  2765. var
  2766. FaceName: AnsiString;
  2767. begin
  2768. fFontRegistred := false;
  2769. FaceName := '';
  2770. if GetFaceName(Source, FaceName) then
  2771. fFontRegistred := RegisterFont(Source);
  2772. // inherited
  2773. inherited Create(Renderer, FaceName, Size, Style, Format, AntiAliasing);
  2774. end;
  2775. destructor TtsFontCreatorGDIStream.Destroy;
  2776. begin
  2777. inherited;
  2778. // unregister font
  2779. if fFontRegistred then
  2780. UnRegisterFont();
  2781. end;
  2782. function TtsFontCreatorGDIStream.GetFaceName(Stream: TStream; var Face: AnsiString): boolean;
  2783. var
  2784. Lang: AnsiString;
  2785. begin
  2786. SetLength(Lang, 4);
  2787. GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, @Lang[1], 4);
  2788. Face := GetTTFontFullNameFromStream(Stream, StrToInt('$' + String(Lang)));
  2789. Result := Face <> '';
  2790. end;
  2791. function TtsFontCreatorGDIStream.RegisterFont(Data: TStream): boolean;
  2792. var
  2793. ms: TMemoryStream;
  2794. cnt: DWORD;
  2795. begin
  2796. Result := False;
  2797. fHandle := 0;
  2798. ms:= TMemoryStream.Create;
  2799. try
  2800. ms.CopyFrom(Data, 0);
  2801. if Addr(AddFontMemResourceEx)<>nil then
  2802. fHandle:= AddFontMemResourceEx(ms.Memory, ms.Size, nil, @cnt);
  2803. Result:= fHandle > 0;
  2804. finally
  2805. ms.Free;
  2806. end;
  2807. end;
  2808. function TtsFontCreatorGDIStream.UnRegisterFont(): boolean;
  2809. begin
  2810. Result := RemoveFontMemResourceEx(fHandle);
  2811. end;
  2812. { TtsRenderer }
  2813. procedure TtsRenderer.BeginBlock(Left, Top, Width, Height: Integer; Flags: tsBitmask);
  2814. begin
  2815. fisBlock := True;
  2816. fBlockLeft := Left;
  2817. fBlockTop := Top;
  2818. fBlockWidth := Width;
  2819. fBlockHeight := Height;
  2820. fFlags := Flags;
  2821. fWordWrap := fFlags and TS_BLOCKFLAG_WORD_WRAP = TS_BLOCKFLAG_WORD_WRAP;
  2822. // fSingleLine := fFlags and TS_BLOCKFLAG_SINGLE_LINE = TS_BLOCKFLAG_SINGLE_LINE;
  2823. fLineTop := Top + tsGetParameteri(TS_BLOCK_OFFSET_Y);
  2824. fTextOffsetY := 0;
  2825. fTextOffsetX := 0;
  2826. with fLinesTemp do begin
  2827. New(Lines);
  2828. with Lines^ do begin
  2829. NextLine := nil;
  2830. LineItemFirst := nil;
  2831. LineItemLast := nil;
  2832. LineLength := 0;
  2833. LineAutoBreak := False;
  2834. end;
  2835. Empty := True;
  2836. end;
  2837. fLinesFirst := nil;
  2838. fLinesLast := nil;
  2839. // if font is active add to list
  2840. if fActiveFont <> nil then
  2841. FontActivate(fActiveFontID);
  2842. end;
  2843. function TtsRenderer.CalculateLinesHeight(pLinesItem: PtsLinesItem): Integer;
  2844. var
  2845. pLine: PtsLineItem;
  2846. Metric: TtsTextMetric;
  2847. begin
  2848. Result := 0;
  2849. while pLinesItem <> nil do begin
  2850. pLine := pLinesItem^.LineItemFirst;
  2851. GetLineMetric(pLine, Metric);
  2852. Result := Result + Metric.LineSkip_with_LineSpace;
  2853. pLinesItem := pLinesItem^.NextLine;
  2854. end;
  2855. // remove last linespace from the lines
  2856. Result := Result - (Metric.LineSkip_with_LineSpace - Metric.LineSkip);
  2857. end;
  2858. procedure TtsRenderer.CalculateWordLength(Font: TtsFont; pWord: PtsLineItem);
  2859. var
  2860. pTempWord: PWideChar;
  2861. Char: TtsChar;
  2862. CharSpacing: tsInt;
  2863. begin
  2864. if pWord^.ItemType in [TS_BLOCK_WORD, TS_BLOCK_SPACE] then begin
  2865. CharSpacing := fLastActiveFont.CharSpacing;
  2866. pTempWord := pWord^.Word;
  2867. pWord^.WordLength := 0;
  2868. while pTempWord^ <> #0 do begin
  2869. // normal char
  2870. if Font.Validate(pTempWord^) then
  2871. Char := Font.GetChar(pTempWord^)
  2872. else
  2873. // default char
  2874. if Font.Validate(Font.DefaultChar) then
  2875. Char := Font.GetChar(Font.DefaultChar)
  2876. else
  2877. Char := nil;
  2878. if Char <> nil then begin
  2879. pWord^.WordLength := pWord^.WordLength + Char.Advance + CharSpacing;
  2880. end;
  2881. Inc(pTempWord);
  2882. end;
  2883. end;
  2884. end;
  2885. procedure TtsRenderer.Color(Red, Green, Blue, Alpha: Single);
  2886. var
  2887. LineItem: PtsLineItem;
  2888. begin
  2889. if isBlock then begin
  2890. New(LineItem);
  2891. LineItem^.NextItem := nil;
  2892. LineItem^.PrevItem := nil;
  2893. LineItem^.ItemType := TS_BLOCK_COLOR;
  2894. LineItem^.Red := Red;
  2895. LineItem^.Green := Green;
  2896. LineItem^.Blue := Blue;
  2897. LineItem^.Alpha := Alpha;
  2898. PushLineItem(LineItem);
  2899. end else
  2900. begin
  2901. DrawSetColor(Red, Green, Blue, Alpha);
  2902. end;
  2903. end;
  2904. constructor TtsRenderer.Create(Context: TtsContext);
  2905. begin
  2906. inherited Create;
  2907. fContext := Context;
  2908. fSaveImages := True;
  2909. end;
  2910. destructor TtsRenderer.Destroy;
  2911. begin
  2912. if isBlock then
  2913. EndBlock;
  2914. inherited;
  2915. end;
  2916. procedure TtsRenderer.DrawLine(pLine: PtsLineItem; LineLength: Integer; LineBreak: Boolean);
  2917. var
  2918. pText: PWideChar;
  2919. Char: TtsChar;
  2920. Metric: TtsTextMetric;
  2921. TempLeft, Temp: Integer;
  2922. DrawLeft, SpaceTemp: Single;
  2923. DrawAscent, LineSkip: Integer;
  2924. DrawText: Boolean;
  2925. BlockSpaceCount: Integer;
  2926. BlockSpaceWidth: Single;
  2927. function CountSpaces(pLine: PtsLineItem): Integer;
  2928. var
  2929. pText: PWideChar;
  2930. begin
  2931. Result := 0;
  2932. while pLine <> nil do begin
  2933. case pLine^.ItemType of
  2934. TS_BLOCK_SPACE: begin
  2935. pText := pLine^.Word;
  2936. // Enumerate Text
  2937. while pText^ <> #0 do begin
  2938. Inc(Result);
  2939. Inc(pText);
  2940. end;
  2941. end;
  2942. end;
  2943. pLine := pLine^.NextItem;
  2944. end;
  2945. end;
  2946. begin
  2947. if fFlags and TS_BLOCKFLAG_CALC_SIZE > 0 then
  2948. Exit;
  2949. BlockSpaceWidth := 0;
  2950. DrawLeft := 0;
  2951. TempLeft := 0;
  2952. GetLineMetric(pLine, Metric);
  2953. // set drawposition to new baseline
  2954. DrawAscent := fLineTop + fTextOffsetY + Metric.Ascent;
  2955. // increment linetop with height of line
  2956. LineSkip := Metric.LineSkip;
  2957. fLineTop := fLineTop + LineSkip;
  2958. // clipping
  2959. DrawText := True;
  2960. if fisBlock then begin
  2961. if not (fFlags and TS_BLOCKFLAG_NO_CLIP = TS_BLOCKFLAG_NO_CLIP) then begin
  2962. case tsGetParameteri(TS_CLIP) of
  2963. TS_CLIP_COMPLETE: begin
  2964. if (fLineTop + fTextOffsetY < fBlockTop) or
  2965. ((fLineTop + fTextOffsetY - LineSkip) > (fBlockTop + fBlockHeight)) then
  2966. DrawText := False;
  2967. end;
  2968. TS_CLIP_BORDER: begin
  2969. if ((fLineTop + fTextOffsetY - LineSkip) < fBlockTop) or
  2970. (fLineTop + fTextOffsetY > (fBlockTop + fBlockHeight)) then
  2971. DrawText := False;
  2972. end;
  2973. end;
  2974. end;
  2975. end;
  2976. // TextBlock text alignment
  2977. if isBlock then begin
  2978. case tsGetParameteri(TS_ALIGN) of
  2979. TS_ALIGN_CENTER:
  2980. begin
  2981. TempLeft := (fBlockWidth div 2) - (LineLength div 2);
  2982. end;
  2983. TS_ALIGN_RIGHT:
  2984. begin
  2985. TempLeft := fBlockWidth - LineLength;
  2986. end;
  2987. TS_ALIGN_BLOCK: begin
  2988. if LineBreak then begin
  2989. BlockSpaceCount := CountSpaces(pLine);
  2990. if BlockSpaceCount > 0 then
  2991. BlockSpaceWidth := (fBlockWidth - LineLength) / BlockSpaceCount;
  2992. end;
  2993. end;
  2994. end;
  2995. DrawSetPosition(fBlockLeft + TempLeft, DrawAscent);
  2996. end else
  2997. // Normal text alignment
  2998. begin
  2999. case tsGetParameteri(TS_ALIGN) of
  3000. TS_ALIGN_CENTER:
  3001. begin
  3002. TempLeft := - (LineLength div 2);
  3003. end;
  3004. TS_ALIGN_RIGHT:
  3005. begin
  3006. TempLeft := - LineLength;
  3007. end;
  3008. end;
  3009. DrawSetPositionRelative(TempLeft, 0);
  3010. end;
  3011. DrawSetPositionRelative(tsGetParameteri(TS_BLOCK_OFFSET_X), 0);
  3012. // Enumerate LineItems
  3013. while pLine <> nil do begin
  3014. case pLine^.ItemType of
  3015. TS_BLOCK_FONT: begin
  3016. fActiveFont := pLine^.Font;
  3017. fActiveFontID := pLine^.FontID;
  3018. end;
  3019. TS_BLOCK_COLOR: begin
  3020. DrawSetColor(pLine^.Red, pLine^.Green, pLine^.Blue, pLine^.Alpha);
  3021. end;
  3022. TS_BLOCK_WORD: begin
  3023. if DrawText then begin
  3024. if fActiveFont <> nil then begin
  3025. pText := pLine^.Word;
  3026. // Enumerate Text
  3027. while pText^ <> #0 do begin
  3028. // normal char
  3029. if fActiveFont.Validate(pText^) then
  3030. Char := fActiveFont.GetChar(pText^)
  3031. else
  3032. // default char
  3033. if fActiveFont.Validate(fActiveFont.DefaultChar) then
  3034. Char := fActiveFont.GetChar(fActiveFont.DefaultChar)
  3035. else
  3036. Char := nil;
  3037. if Char <> nil then begin
  3038. DrawSetPositionRelative(Char.GlyphOriginX, -fActiveFont.fBaselineOffset);
  3039. DrawChar(fActiveFont, Char);
  3040. DrawSetPositionRelative(Char.Advance - Char.GlyphOriginX + fActiveFont.CharSpacing, fActiveFont.fBaselineOffset);
  3041. end;
  3042. Inc(pText);
  3043. end;
  3044. end;
  3045. end;
  3046. end;
  3047. TS_BLOCK_SPACE: begin
  3048. if DrawText then begin
  3049. if fActiveFont <> nil then begin
  3050. pText := pLine^.Word;
  3051. // Enumerate Text
  3052. while pText^ <> #0 do begin
  3053. // normal char
  3054. if fActiveFont.Validate(pText^) then
  3055. Char := fActiveFont.GetChar(pText^)
  3056. else
  3057. // default char
  3058. if fActiveFont.Validate(fActiveFont.DefaultChar) then
  3059. Char := fActiveFont.GetChar(fActiveFont.DefaultChar)
  3060. else
  3061. Char := nil;
  3062. if Char <> nil then begin
  3063. // We have lines so we must repeat the "empty" space
  3064. if (tsStyleUnderline in fActiveFont.Style) or (tsStyleStrikeout in fActiveFont.Style) then begin
  3065. // width we need to draw
  3066. SpaceTemp := Char.Advance + fActiveFont.CharSpacing + BlockSpaceWidth;
  3067. // set the position to the normal end. Following we decrease
  3068. // these value by the width of the drawn chars. So we get the
  3069. // difference of the last drawn space.
  3070. DrawLeft := DrawLeft + Char.Advance + fActiveFont.CharSpacing + BlockSpaceWidth;
  3071. Temp := Char.Advance - Char.GlyphOriginX + fActiveFont.CharSpacing;
  3072. while SpaceTemp > 0 do begin
  3073. // draw the char
  3074. DrawSetPositionRelative(Char.GlyphOriginX, 0);
  3075. DrawChar(fActiveFont, Char);
  3076. // set the position inside the drawer
  3077. DrawSetPositionRelative(Temp, 0);
  3078. // decrease need to draw width
  3079. SpaceTemp := SpaceTemp - Temp;
  3080. // decrease the drawwidth with the width of the char.
  3081. DrawLeft := DrawLeft - Temp;
  3082. end;
  3083. end else
  3084. // no lines so only set the position
  3085. DrawLeft := DrawLeft + Char.Advance + fActiveFont.CharSpacing + BlockSpaceWidth;
  3086. end;
  3087. Inc(pText);
  3088. end;
  3089. DrawSetPositionRelative(Round(DrawLeft), 0);
  3090. DrawLeft := DrawLeft - Round(DrawLeft);
  3091. end;
  3092. end;
  3093. end;
  3094. TS_BLOCK_LINEBREAK: begin
  3095. end;
  3096. // TS_BLOCK_TAB: begin
  3097. // case tsGetParameteri(TS_TAB) of
  3098. // TS_TAB_FIXED:
  3099. // begin
  3100. // Temp := tsGetParameteri(TS_TAB_FIXED_WIDTH);
  3101. //
  3102. //// if (DrawLeft - fBlockLeft) mod Temp > 0 then
  3103. // DrawLeft := (Round(DrawLeft) mod Temp) + Temp;
  3104. // end;
  3105. // TS_TAB_ABSOLUTE:
  3106. // begin
  3107. //
  3108. // end;
  3109. // end;
  3110. // end;
  3111. end;
  3112. pLine := pLine^.NextItem;
  3113. end;
  3114. end;
  3115. procedure TtsRenderer.DrawLines(pLinesItem: PtsLinesItem);
  3116. begin
  3117. if fFlags and TS_BLOCKFLAG_CALC_SIZE = 0 then begin
  3118. while pLinesItem <> nil do begin
  3119. DrawLine(pLinesItem^.LineItemFirst, pLinesItem^.LineLength, pLinesItem^.LineAutoBreak);
  3120. pLinesItem := pLinesItem^.NextLine;
  3121. end;
  3122. end;
  3123. end;
  3124. procedure TtsRenderer.EndBlock;
  3125. var
  3126. LinesHeight: Integer;
  3127. VerticalAlign: tsEnum;
  3128. begin
  3129. // if temp line exist then push them
  3130. with fLinesTemp do begin
  3131. if Lines <> nil then
  3132. if Lines^.LineItemFirst <> nil then
  3133. PushTempLines;
  3134. FreeLines(Lines);
  3135. end;
  3136. // if vertical align isn't top
  3137. VerticalAlign := tsGetParameteri(TS_VALIGN);
  3138. if (VerticalAlign = TS_VALIGN_CENTER) or
  3139. (VerticalAlign = TS_VALIGN_BOTTOM) then begin
  3140. // calculating height
  3141. LinesHeight := CalculateLinesHeight(fLinesFirst);
  3142. // setting offset
  3143. case VerticalAlign of
  3144. TS_VALIGN_CENTER:
  3145. fTextOffsetY := fTextOffsetY + (fBlockHeight div 2 - LinesHeight div 2);
  3146. TS_VALIGN_BOTTOM:
  3147. fTextOffsetY := fTextOffsetY + (fBlockHeight - LinesHeight);
  3148. end;
  3149. // drawing lines
  3150. DrawLines(fLinesFirst);
  3151. end;
  3152. // Free all lines
  3153. FreeLines(fLinesFirst);
  3154. fLinesLast := nil;
  3155. fisBlock := False;
  3156. end;
  3157. procedure TtsRenderer.FontActivate(FontID: Cardinal);
  3158. var
  3159. pLine: PtsLineItem;
  3160. begin
  3161. if FontID <> 0 then begin
  3162. fLastActiveFont := fContext.FontGet(FontID);
  3163. fLastActiveFontID := FontID;
  3164. end else
  3165. fLastActiveFont := nil;
  3166. // if in block then add blockitem
  3167. if isBlock then begin
  3168. New(pLine);
  3169. pLine^.NextItem := nil;
  3170. pLine^.PrevItem := nil;
  3171. pLine^.ItemType := TS_BLOCK_FONT;
  3172. pLine^.FontID := FontID;
  3173. pLine^.Font := fLastActiveFont;
  3174. if pLine^.Font <> nil then
  3175. PushLineItem(pLine)
  3176. else
  3177. Dispose(pLine);
  3178. end else
  3179. // activate font
  3180. begin
  3181. fActiveFontID := FontID;
  3182. fActiveFont := fLastActiveFont;
  3183. end;
  3184. end;
  3185. procedure TtsRenderer.FreeLines(var pLinesItem: PtsLinesItem);
  3186. var
  3187. pTemp: PtsLinesItem;
  3188. begin
  3189. while pLinesItem <> nil do begin
  3190. pTemp := pLinesItem;
  3191. FreeLineItems(pLinesItem^.LineItemFirst);
  3192. pLinesItem^.LineItemLast := pLinesItem^.LineItemFirst;
  3193. pLinesItem := pLinesItem^.NextLine;
  3194. Dispose(pTemp);
  3195. end;
  3196. end;
  3197. procedure TtsRenderer.FreeLineItems(var pLine: PtsLineItem);
  3198. var
  3199. pTemp: PtsLineItem;
  3200. begin
  3201. while pLine <> nil do begin
  3202. pTemp := pLine;
  3203. case pLine^.ItemType of
  3204. TS_BLOCK_WORD, TS_BLOCK_SPACE:
  3205. tsStrDispose(pLine^.Word);
  3206. end;
  3207. pLine := pLine^.NextItem;
  3208. Dispose(pTemp);
  3209. end;
  3210. end;
  3211. function TtsRenderer.GetActiveFont: TtsFont;
  3212. begin
  3213. if fisBlock then
  3214. Result := fLastActiveFont
  3215. else
  3216. Result := fActiveFont;
  3217. end;
  3218. function TtsRenderer.GetActiveFontID: Cardinal;
  3219. begin
  3220. if fisBlock then
  3221. Result := fLastActiveFontID
  3222. else
  3223. Result := fActiveFontID;
  3224. end;
  3225. procedure TtsRenderer.GetLineMetric(pLine: PtsLineItem; var Metric: TtsTextMetric);
  3226. var
  3227. Font: TtsFont;
  3228. Temp: TtsTextMetric;
  3229. begin
  3230. // Defaults
  3231. Metric.Ascent := 0;
  3232. Metric.Descent := 0;
  3233. Metric.LineSkip := 0;
  3234. Metric.LineSkip_with_LineSpace := 0;
  3235. // calculating lines
  3236. Font := fActiveFont;
  3237. while pLine <> nil do begin
  3238. case pLine^.ItemType of
  3239. TS_BLOCK_FONT: begin
  3240. Font := pLine^.Font;
  3241. end;
  3242. TS_BLOCK_WORD, TS_BLOCK_SPACE, TS_BLOCK_LINEBREAK: begin
  3243. if Font <> nil then begin
  3244. Font.GetTextMetric(Temp);
  3245. if Temp.Ascent > Metric.Ascent then
  3246. Metric.Ascent := Temp.Ascent;
  3247. if Temp.Descent > Metric.Descent then
  3248. Metric.Descent := Temp.Descent;
  3249. if Temp.LineSkip > Metric.LineSkip then
  3250. Metric.LineSkip := Temp.LineSkip;
  3251. if Temp.LineSkip_with_LineSpace > Metric.LineSkip_with_LineSpace then
  3252. Metric.LineSkip_with_LineSpace := Temp.LineSkip_with_LineSpace;
  3253. // font was handled so we can remove the font to skip the following words.
  3254. // because the value only will change if we change the font.
  3255. Font := nil;
  3256. end;
  3257. end;
  3258. end;
  3259. pLine := pLine^.NextItem;
  3260. end;
  3261. end;
  3262. procedure TtsRenderer.PushTempLines;
  3263. begin
  3264. TrimSpaces(fLinesTemp.Lines);
  3265. fLinesTemp.Lines^.LineLength := fLinesTemp.Lines^.LineLength - fLastActiveFont.CharSpacing;
  3266. // add after last item
  3267. if fLinesFirst <> nil then begin
  3268. fLinesLast^.NextLine := fLinesTemp.Lines;
  3269. fLinesLast := fLinesTemp.Lines;
  3270. end;
  3271. // set first item
  3272. if fLinesFirst = nil then begin
  3273. fLinesFirst := fLinesTemp.Lines;
  3274. fLinesLast := fLinesTemp.Lines;
  3275. end;
  3276. // if vertical align is top then draw direktlly
  3277. if tsGetParameteri(TS_VALIGN) = TS_VALIGN_TOP then
  3278. DrawLine(fLinesLast^.LineItemFirst, fLinesLast^.LineLength, fLinesLast^.LineAutoBreak);
  3279. // create new item
  3280. with fLinesTemp do begin
  3281. New(Lines);
  3282. with Lines^ do begin
  3283. NextLine := nil;
  3284. LineItemFirst := nil;
  3285. LineItemLast := nil;
  3286. LineLength := 0;
  3287. LineAutoBreak := False;
  3288. end;
  3289. Empty := True;
  3290. end;
  3291. end;
  3292. procedure TtsRenderer.PushLineItem(pLine: PtsLineItem);
  3293. begin
  3294. with fLinesTemp do begin
  3295. if Lines <> nil then begin
  3296. // add after last item
  3297. if Lines^.LineItemLast <> nil then begin
  3298. pLine^.PrevItem := Lines^.LineItemLast;
  3299. Lines^.LineItemLast^.NextItem := pLine;
  3300. Lines^.LineItemLast := pLine;
  3301. end;
  3302. // set first item
  3303. if Lines^.LineItemFirst = nil then begin
  3304. Lines^.LineItemFirst := pLine;
  3305. Lines^.LineItemLast := pLine;
  3306. end;
  3307. end;
  3308. end;
  3309. end;
  3310. procedure TtsRenderer.SplitIntoLines(pItemList: PtsLineItem);
  3311. var
  3312. pExtractItem: PtsLineItem;
  3313. procedure PushWord(pItem: PtsLineItem);
  3314. begin
  3315. if pItem <> nil then begin
  3316. with fLinesTemp.Lines^ do begin
  3317. // add after last item
  3318. if LineItemLast <> nil then begin
  3319. LineItemLast^.NextItem := pItem;
  3320. pItem^.PrevItem := LineItemLast;
  3321. LineItemLast := pItem;
  3322. end;
  3323. // set first item
  3324. if LineItemFirst = nil then begin
  3325. LineItemFirst := pItem;
  3326. LineItemLast := pItem;
  3327. end;
  3328. end;
  3329. end;
  3330. end;
  3331. begin
  3332. while pItemList <> nil do begin
  3333. // extract word from list
  3334. pExtractItem := pItemList;
  3335. pItemList := pItemList^.NextItem;
  3336. pExtractItem^.NextItem := nil;
  3337. pExtractItem^.PrevItem := nil;
  3338. case pExtractItem^.ItemType of
  3339. TS_BLOCK_WORD, TS_BLOCK_SPACE: begin
  3340. // calculate size
  3341. CalculateWordLength(fLastActiveFont, pExtractItem);
  3342. if fWordWrap {and not fSingleLine} then begin
  3343. // if line + word is larger than draw width
  3344. if fLinesTemp.Lines^.LineLength + pExtractItem^.WordLength > fBlockWidth then begin
  3345. fLinesTemp.Lines^.LineAutoBreak := True;
  3346. // if line is empty
  3347. if fLinesTemp.Lines^.LineLength = 0 then begin
  3348. // ### Split word into multiple lines
  3349. PushWord(pExtractItem);
  3350. pExtractItem := nil;
  3351. end; // else
  3352. PushTempLines;
  3353. end;
  3354. end;
  3355. // add extracted word to intern small list
  3356. if pExtractItem <> nil then begin
  3357. // add word
  3358. PushWord(pExtractItem);
  3359. // add Length
  3360. fLinesTemp.Lines^.LineLength := fLinesTemp.Lines^.LineLength + pExtractItem^.WordLength;
  3361. end;
  3362. end;
  3363. TS_BLOCK_LINEBREAK: begin
  3364. // if not fSingleLine then begin
  3365. PushWord(pExtractItem);
  3366. PushTempLines;
  3367. // end;
  3368. end;
  3369. TS_BLOCK_TAB: begin
  3370. PushWord(pExtractItem);
  3371. end;
  3372. end;
  3373. end;
  3374. end;
  3375. function TtsRenderer.SplitText(pText: PWideChar): PtsLineItem;
  3376. var
  3377. pLastItem: PtsLineItem;
  3378. State: Integer;
  3379. WordLength: Integer;
  3380. pWordBegin: PWideChar;
  3381. procedure ExtractWord;
  3382. var
  3383. pWord: PWideChar;
  3384. pWordItem: PtsLineItem;
  3385. procedure AddItem;
  3386. begin
  3387. // add item to list
  3388. if Result <> nil then begin
  3389. pLastItem^.NextItem := pWordItem;
  3390. pWordItem^.PrevItem := pLastItem;
  3391. pLastItem := pWordItem;
  3392. end;
  3393. if Result = nil then begin
  3394. Result := pWordItem;
  3395. pLastItem := pWordItem;
  3396. end;
  3397. end;
  3398. begin
  3399. if State <> 0 then begin
  3400. // Create listitem
  3401. New(pWordItem);
  3402. pWordItem^.NextItem := nil;
  3403. pWordItem^.PrevItem := nil;
  3404. pWordItem^.ItemType := State;
  3405. // only if space or text
  3406. case State of
  3407. TS_BLOCK_WORD, TS_BLOCK_SPACE: begin
  3408. pWordItem^.Word := tsStrAlloc(WordLength);
  3409. // copy chars
  3410. WordLength := 0;
  3411. pWord := pWordItem^.Word;
  3412. while pWordBegin <> pText do begin
  3413. pWord^ := pWordBegin^;
  3414. Inc(pWord);
  3415. Inc(pWordBegin);
  3416. end;
  3417. AddItem;
  3418. end;
  3419. TS_BLOCK_LINEBREAK: begin
  3420. if pWordBegin <> pText then begin
  3421. // Skip Linebreak
  3422. while pWordBegin <> pText do
  3423. Inc(pWordBegin);
  3424. // if not fSingleLine then begin
  3425. AddItem;
  3426. // end else
  3427. // begin
  3428. // Dispose(pWordItem);
  3429. // pWordItem := nil;
  3430. // end;
  3431. end else
  3432. begin
  3433. Dispose(pWordItem);
  3434. pWordItem := nil;
  3435. end;
  3436. end;
  3437. TS_BLOCK_TAB: begin
  3438. AddItem;
  3439. end;
  3440. end;
  3441. end;
  3442. end;
  3443. begin
  3444. Result := nil;
  3445. pLastItem := nil;
  3446. WordLength := 0;
  3447. State := 0;
  3448. pWordBegin := pText;
  3449. // look for word breaks
  3450. while pText^ <> #0 do begin
  3451. case pText^ of
  3452. // Tabulator
  3453. #$0009: begin
  3454. ExtractWord;
  3455. Inc(pWordBegin);
  3456. State := TS_BLOCK_TAB;
  3457. end;
  3458. // line breaks
  3459. #$000D, #$000A: begin
  3460. if State <> TS_BLOCK_LINEBREAK then
  3461. ExtractWord;
  3462. if pWordBegin <> pText then begin
  3463. ExtractWord;
  3464. Inc(pWordBegin);
  3465. end;
  3466. State := TS_BLOCK_LINEBREAK;
  3467. end;
  3468. // Spaces
  3469. #$0020: begin
  3470. if State <> TS_BLOCK_SPACE then begin
  3471. ExtractWord;
  3472. State := TS_BLOCK_SPACE;
  3473. end;
  3474. end;
  3475. else
  3476. if State <> TS_BLOCK_WORD then begin
  3477. ExtractWord;
  3478. State := TS_BLOCK_WORD;
  3479. end;
  3480. end;
  3481. Inc(pText);
  3482. Inc(WordLength);
  3483. end;
  3484. // copy last word
  3485. if pWordBegin <> pText then
  3486. ExtractWord;
  3487. end;
  3488. function TtsRenderer.TextGetDrawHeight: Integer;
  3489. var
  3490. pLinesItem: PtsLinesItem;
  3491. Metric: TtsTextMetric;
  3492. begin
  3493. Result := 0;
  3494. // all lines
  3495. pLinesItem := fLinesFirst;
  3496. while pLinesItem <> nil do begin
  3497. GetLineMetric(pLinesItem^.LineItemFirst, Metric);
  3498. Result := Result + Metric.LineSkip_with_LineSpace;
  3499. pLinesItem := pLinesItem^.NextLine;
  3500. end;
  3501. // last if we had an templine
  3502. if fLinesTemp.Lines <> nil then begin
  3503. GetLineMetric(fLinesTemp.Lines^.LineItemFirst, Metric);
  3504. Result := Result + Metric.LineSkip_with_LineSpace;
  3505. end;
  3506. end;
  3507. function TtsRenderer.TextGetDrawWidth: Integer;
  3508. var
  3509. pLinesItem: PtsLinesItem;
  3510. Temp: Integer;
  3511. {%H-}Font: TtsFont;
  3512. function IntGetLineWidth(pLine: PtsLineItem): Integer;
  3513. begin
  3514. Result := 0;
  3515. while pLine <> nil do begin
  3516. case pLine^.ItemType of
  3517. TS_BLOCK_FONT: begin
  3518. Font := pLine^.Font;
  3519. end;
  3520. TS_BLOCK_WORD, TS_BLOCK_SPACE: begin
  3521. Result := Result + pLine^.WordLength;
  3522. end;
  3523. end;
  3524. pLine := pLine^.NextItem;
  3525. end;
  3526. end;
  3527. begin
  3528. Result := 0;
  3529. // all lines
  3530. Font := fActiveFont;
  3531. pLinesItem := fLinesFirst;
  3532. while pLinesItem <> nil do begin
  3533. Temp := IntGetLineWidth(pLinesItem^.LineItemFirst);
  3534. if Temp > Result then
  3535. Result := Temp;
  3536. pLinesItem := pLinesItem^.NextLine;
  3537. end;
  3538. // last if we had an templine
  3539. if fLinesTemp.Lines <> nil then begin
  3540. Temp := IntGetLineWidth(fLinesTemp.Lines^.LineItemFirst);
  3541. if Temp > Result then
  3542. Result := Temp;
  3543. end;
  3544. end;
  3545. function TtsRenderer.TextGetWidth(pText: pWideChar): Integer;
  3546. var
  3547. pItemList: PtsLineItem;
  3548. pTempItem: PtsLineItem;
  3549. begin
  3550. Result := 0;
  3551. pItemList := SplitText(pText);
  3552. pTempItem := pItemList;
  3553. while pTempItem <> nil do begin
  3554. CalculateWordLength(fActiveFont, pTempItem);
  3555. Result := Result + pTempItem^.WordLength;
  3556. pTempItem := pTempItem^.NextItem;
  3557. end;
  3558. // Free Items
  3559. FreeLineItems(pItemList);
  3560. end;
  3561. procedure TtsRenderer.TextOut(pText: pWideChar);
  3562. var
  3563. pItemList: PtsLineItem;
  3564. pTempItem: PtsLineItem;
  3565. TempLength: Integer;
  3566. begin
  3567. pItemList := SplitText(pText);
  3568. if isBlock then begin
  3569. SplitIntoLines(pItemList);
  3570. end else
  3571. begin
  3572. DrawSetPosition(0, 0);
  3573. // Calculate Word length
  3574. TempLength := 0;
  3575. pTempItem := pItemList;
  3576. while pTempItem <> nil do begin
  3577. CalculateWordLength(fActiveFont, pTempItem);
  3578. TempLength := TempLength + pTempItem^.WordLength;
  3579. pTempItem := pTempItem^.NextItem;
  3580. end;
  3581. // remove last Char Spacing
  3582. TempLength := TempLength - fActiveFont.CharSpacing;
  3583. // if single line is top then set the Position to the baseline
  3584. if tsGetParameteri(TS_SINGLE_LINE) = TS_SINGLE_LINE_TOP then
  3585. DrawSetPositionRelative(0, fActiveFont.Ascent);
  3586. // draw
  3587. DrawLine(pItemList, TempLength, False);
  3588. // Free Items
  3589. FreeLineItems(pItemList);
  3590. end;
  3591. end;
  3592. procedure TtsRenderer.TrimSpaces(pLinesItem: PtsLinesItem);
  3593. var
  3594. pTempLoopItem, pTempItem: PtsLineItem;
  3595. begin
  3596. if pLinesItem <> nil then begin
  3597. // delete all spaces at beginning
  3598. while pLinesItem^.LineItemFirst <> nil do begin
  3599. if pLinesItem^.LineItemFirst^.ItemType <> TS_BLOCK_SPACE then
  3600. Break;
  3601. // save first
  3602. pTempItem := pLinesItem^.LineItemFirst;
  3603. // remove first item fromlist
  3604. pLinesItem^.LineItemFirst := pLinesItem^.LineItemFirst^.NextItem;
  3605. if pLinesItem^.LineItemFirst = nil then
  3606. pLinesItem^.LineItemLast := nil
  3607. else
  3608. pLinesItem^.LineItemFirst^.PrevItem := nil;
  3609. pLinesItem^.LineLength := pLinesItem^.LineLength - pTempItem^.WordLength;
  3610. // dispose item
  3611. pTempItem^.NextItem := nil;
  3612. FreeLineItems(pTempItem);
  3613. end;
  3614. // delete all spaces at the end
  3615. while pLinesItem^.LineItemLast <> nil do begin
  3616. if pLinesItem^.LineItemLast^.ItemType <> TS_BLOCK_SPACE then
  3617. break;
  3618. // save last item
  3619. pTempItem := pLinesItem^.LineItemLast;
  3620. // remove last item from list
  3621. pLinesItem^.LineItemLast := pLinesItem^.LineItemLast^.PrevItem;
  3622. if pLinesItem^.LineItemLast = nil then
  3623. pLinesItem^.LineItemFirst := nil
  3624. else
  3625. pLinesItem^.LineItemLast^.NextItem := nil;
  3626. pLinesItem^.LineLength := pLinesItem^.LineLength - pTempItem^.WordLength;
  3627. // dispose item
  3628. FreeLineItems(pTempItem);
  3629. end;
  3630. // delete all spaces until some text comes
  3631. pTempLoopItem := pLinesItem^.LineItemFirst;
  3632. while pTempLoopItem <> nil do begin
  3633. // exit if we have an word
  3634. if pTempLoopItem^.ItemType = TS_BLOCK_WORD then
  3635. Break;
  3636. pTempItem := pTempLoopItem;
  3637. pTempLoopItem := pTempLoopItem^.NextItem;
  3638. if pTempItem^.ItemType = TS_BLOCK_SPACE then begin
  3639. pLinesItem^.LineLength := pLinesItem^.LineLength - pTempItem^.WordLength;
  3640. // set new next/prev
  3641. if pTempItem^.NextItem <> nil then
  3642. pTempItem^.NextItem^.PrevItem := pTempItem^.PrevItem;
  3643. if pTempItem^.PrevItem <> nil then
  3644. pTempItem^.PrevItem^.NextItem := pTempItem^.NextItem;
  3645. // remove item
  3646. pTempItem^.PrevItem := nil;
  3647. pTempItem^.NextItem := nil;
  3648. FreeLineItems(pTempItem);
  3649. end;
  3650. end;
  3651. // delete all spaces until some text comes
  3652. pTempLoopItem := pLinesItem^.LineItemLast;
  3653. while pTempLoopItem <> nil do begin
  3654. // exit if we have an word
  3655. if pTempLoopItem^.ItemType = TS_BLOCK_WORD then
  3656. Break;
  3657. pTempItem := pTempLoopItem;
  3658. pTempLoopItem := pTempLoopItem^.PrevItem;
  3659. if pTempItem^.ItemType = TS_BLOCK_SPACE then begin
  3660. pLinesItem^.LineLength := pLinesItem^.LineLength - pTempItem^.WordLength;
  3661. // set new next/prev
  3662. if pTempItem^.PrevItem <> nil then
  3663. pTempItem^.PrevItem^.NextItem := pTempItem^.NextItem;
  3664. if pTempItem^.NextItem <> nil then
  3665. pTempItem^.NextItem^.PrevItem := pTempItem^.PrevItem;
  3666. // remove item
  3667. pTempItem^.PrevItem := nil;
  3668. pTempItem^.NextItem := nil;
  3669. FreeLineItems(pTempItem);
  3670. end;
  3671. end;
  3672. end;
  3673. end;
  3674. procedure TtsRenderer.CharOut(CharCode: WideChar);
  3675. var
  3676. tsChar: TtsChar;
  3677. begin
  3678. tsChar := fActiveFont.GetChar(CharCode);
  3679. if tsChar <> nil then
  3680. DrawChar(fActiveFont, tsChar);
  3681. end;
  3682. { TtsRendererNULL }
  3683. function TtsRendererNULL.AddImage(Char: TtsChar; CharImage: TtsImage): TtsRendererImageReference;
  3684. begin
  3685. Result := TtsRendererNULLImageReference.Create;
  3686. if fSaveImages then
  3687. with TtsRendererNULLImageReference(Result) do begin
  3688. Image := TtsImage.Create;
  3689. Image.AssignFrom(CharImage);
  3690. end;
  3691. end;
  3692. procedure TtsRendererNULL.DrawChar(Font: TtsFont; Char: TtsChar);
  3693. begin
  3694. // nothing
  3695. end;
  3696. procedure TtsRendererNULL.DrawSetColor(Red, Green, Blue, Alpha: Single);
  3697. begin
  3698. // nothing
  3699. end;
  3700. procedure TtsRendererNULL.DrawSetPosition(X, Y: Integer);
  3701. begin
  3702. // nothing
  3703. end;
  3704. procedure TtsRendererNULL.DrawSetPositionRelative(X, Y: Integer);
  3705. begin
  3706. // nothing
  3707. end;
  3708. procedure TtsRendererNULL.RemoveImageReference(ImageReference: TtsRendererImageReference);
  3709. begin
  3710. if (ImageReference is TtsRendererNULLImageReference) then
  3711. with TtsRendererNULLImageReference(ImageReference) do
  3712. if Image <> nil then
  3713. Image.Free;
  3714. end;
  3715. { TtsRendererOpenGL }
  3716. function TtsRendererOpenGL.AddImage(Char: TtsChar; CharImage: TtsImage): TtsRendererImageReference;
  3717. var
  3718. Idx: Integer;
  3719. TextureEntry: PtsRendererOpenGLTextureEntry;
  3720. TextureAdded: Boolean;
  3721. Texture: PtsRendererOpenGLTexture;
  3722. CharHeight, CharWidth: Integer;
  3723. W1, H1, TempBorder: Single;
  3724. begin
  3725. Result := nil;
  3726. if not CharImage.Empty then begin
  3727. Result := TtsRendererOpenGLImageReference.Create;
  3728. with TtsRendererOpenGLImageReference(Result) do begin
  3729. Coordinates.Top := 0;
  3730. Coordinates.Left := 0;
  3731. Coordinates.Right := 0;
  3732. Coordinates.Bottom := 0;
  3733. TextureAdded := False;
  3734. TextureEntry := nil;
  3735. // look if we can add the image to an texture
  3736. for Idx := 0 to fTextures.Count - 1 do begin
  3737. if AddImageToTexture(fTextures[Idx], CharImage, TexID, Coordinates) then begin
  3738. TextureEntry := fTextures[Idx];
  3739. TextureAdded := True;
  3740. Break;
  3741. end;
  3742. end;
  3743. // could not added so create new texture
  3744. if not TextureAdded then begin
  3745. TextureEntry := CreateNewTexture;
  3746. AddImageToTexture(TextureEntry, CharImage, TexID, Coordinates);
  3747. end;
  3748. // generating coords
  3749. if TextureEntry <> nil then begin
  3750. Texture := TextureEntry^.Texture;
  3751. if Texture <> nil then begin
  3752. with Char do begin
  3753. CharHeight := Coordinates.Bottom - Coordinates.Top;
  3754. CharWidth := Coordinates.Right - Coordinates.Left;
  3755. // Set Variables for resizing border
  3756. if HasResizingBorder then begin
  3757. W1 := 1 / Texture^.Width;
  3758. H1 := 1 / Texture^.Height;
  3759. TempBorder := 2;
  3760. end else begin
  3761. W1 := 0;
  3762. H1 := 0;
  3763. TempBorder := 0;
  3764. end;
  3765. // Top Left
  3766. TexCoords[0].X := Coordinates.Left / Texture^.Width + W1;
  3767. TexCoords[0].Y := Coordinates.Top / Texture^.Height + H1;
  3768. // Vertex[0].X := - GlyphRect.Left + Size1;
  3769. // Vertex[0].Y := - GlyphRect.Top - GlyphOriginY + Size1;
  3770. Vertex[0].X := - GlyphRect.Left;
  3771. Vertex[0].Y := - GlyphRect.Top - GlyphOriginY;
  3772. // Bottom Left
  3773. TexCoords[1].X := Coordinates.Left / Texture^.Width + W1;
  3774. TexCoords[1].Y := Coordinates.Bottom / Texture^.Height - H1;
  3775. // Vertex[1].X := - GlyphRect.Left + Size1;
  3776. // Vertex[1].Y := CharHeight - GlyphRect.Top - GlyphOriginY - Size1;
  3777. Vertex[1].X := - GlyphRect.Left;
  3778. Vertex[1].Y := CharHeight - GlyphRect.Top - GlyphOriginY - TempBorder;
  3779. // Bottom Right
  3780. TexCoords[2].X := Coordinates.Right / Texture^.Width - W1;
  3781. TexCoords[2].Y := Coordinates.Bottom / Texture^.Height - H1;
  3782. // Vertex[2].X := CharWidth - GlyphRect.Left - Size1;
  3783. // Vertex[2].Y := CharHeight - GlyphRect.Top - GlyphOriginY - Size1;
  3784. Vertex[2].X := CharWidth - GlyphRect.Left - TempBorder;
  3785. Vertex[2].Y := CharHeight - GlyphRect.Top - GlyphOriginY - TempBorder;
  3786. // Top Right
  3787. TexCoords[3].X := Coordinates.Right / Texture^.Width - W1;
  3788. TexCoords[3].Y := Coordinates.Top / Texture^.Height + H1;
  3789. // Vertex[3].X := CharWidth - GlyphRect.Left - Size1;
  3790. // Vertex[3].Y := - GlyphRect.Top - GlyphOriginY + Size1;
  3791. Vertex[3].X := CharWidth - GlyphRect.Left - TempBorder;
  3792. Vertex[3].Y := - GlyphRect.Top - GlyphOriginY;
  3793. end;
  3794. end;
  3795. end;
  3796. end;
  3797. end;
  3798. end;
  3799. function TtsRendererOpenGL.AddImageToTexture(Texture: PtsRendererOpenGLTextureEntry; Image: TtsImage; var TextureID: Integer; var Coordinates: tsRect): boolean;
  3800. var
  3801. NeedX, NeedY: Word;
  3802. Start: Word;
  3803. Y, Y2: Integer;
  3804. Managed: PtsRendererOpenGLManagedEntry;
  3805. function CheckVertical(StartPos, EndPos: Integer): Boolean;
  3806. var
  3807. TempY: Integer;
  3808. TempManaged: PtsRendererOpenGLManagedEntry;
  3809. Found: Boolean;
  3810. begin
  3811. Result := False;
  3812. for TempY := Y +1 to Y + NeedY -1 do begin
  3813. TempManaged := Texture^.Lines[TempY];
  3814. // Überprüfen ob der entsprechende Bereich noch frei ist.
  3815. Found := False;
  3816. while TempManaged <> nil do begin
  3817. if (TempManaged^.Start <= StartPos) and (TempManaged^.Start + TempManaged^.Count >= EndPos) then
  3818. Found := True;
  3819. TempManaged := TempManaged^.NextEntry;
  3820. end;
  3821. if not Found then
  3822. Exit;
  3823. end;
  3824. Result := True;
  3825. end;
  3826. begin
  3827. Result := False;
  3828. NeedX := Image.Width shr 1;
  3829. if (Image.Width and 1) > 0 then
  3830. Inc(NeedX);
  3831. NeedY := Image.Height shr 1;
  3832. if (Image.Height and 1) > 0 then
  3833. Inc(NeedY);
  3834. // scan for free space
  3835. for Y := Low(Texture^.Lines) to High(Texture^.Lines) - NeedY do begin
  3836. Managed := Texture^.Lines[Y];
  3837. while Managed <> nil do begin
  3838. if Managed^.Count >= NeedX then begin
  3839. if CheckVertical(Managed^.Start, Managed^.Start + NeedX) then begin
  3840. Start := Managed^.Start;
  3841. // allocating space
  3842. for Y2 := Y to Y + NeedY -1 do
  3843. AllocSpace(Texture^.Lines[Y2], Start, NeedX);
  3844. // setting texturecoordinates values
  3845. TextureID := Texture^.ID;
  3846. Coordinates.Left := Start shl 1;
  3847. Coordinates.Top := Y shl 1;
  3848. Coordinates.Right := Coordinates.Left + Image.Width;
  3849. Coordinates.Bottom := Coordinates.Top + Image.Height;
  3850. Texture^.Usage := Texture^.Usage + NeedX * NeedY;
  3851. // copy charimage
  3852. with Texture^.Texture^ do begin
  3853. glBindTexture(GL_TEXTURE_2D, Texture^.Texture^.glTextureID);
  3854. glTexSubImage2D(GL_TEXTURE_2D, 0, Coordinates.Left, Coordinates.Top, Image.Width, Image.Height, GL_RGBA, GL_UNSIGNED_BYTE, Image.Data);
  3855. end;
  3856. Result := True;
  3857. Exit;
  3858. end;
  3859. end;
  3860. Managed := Managed^.NextEntry;
  3861. end;
  3862. end;
  3863. end;
  3864. procedure TtsRendererOpenGL.AfterConstruction;
  3865. begin
  3866. inherited;
  3867. fTextures := TList.Create;
  3868. fTextureSize := 256;
  3869. end;
  3870. procedure TtsRendererOpenGL.AllocSpace(var FirstManaged: PtsRendererOpenGLManagedEntry; Start, Count: Word);
  3871. var
  3872. Managed, TempManaged: PtsRendererOpenGLManagedEntry;
  3873. procedure RemoveManagedItem(pItem: PtsRendererOpenGLManagedEntry);
  3874. var
  3875. pTemp, pTemp2: PtsRendererOpenGLManagedEntry;
  3876. begin
  3877. pTemp := FirstManaged;
  3878. while pTemp <> nil do begin
  3879. pTemp2 := pTemp^.NextEntry;
  3880. if pTemp2 = pItem then begin
  3881. pTemp^.NextEntry := pItem^.NextEntry;
  3882. Break;
  3883. end;
  3884. pTemp := pTemp2;
  3885. end;
  3886. end;
  3887. begin
  3888. // complete remove of the FIRST item (spezial handling for first item removal.)
  3889. if (Start = FirstManaged^.Start) and (Count = FirstManaged^.Count) then begin
  3890. TempManaged := FirstManaged;
  3891. FirstManaged := FirstManaged^.NextEntry;
  3892. Dispose(TempManaged);
  3893. end else
  3894. // look for matching item
  3895. begin
  3896. Managed := FirstManaged;
  3897. while Managed <> nil do begin
  3898. // matched item?
  3899. if (Start >= Managed^.Start) and ((Start + Count) <= (Managed^.Start + Managed^.Count)) then begin
  3900. // cut at start
  3901. if (Start = Managed^.Start) then begin
  3902. // remove the whole item
  3903. if (Count = Managed^.Count) then begin
  3904. RemoveManagedItem(Managed);
  3905. // no need to preserve Managed because we leaving the loop
  3906. Dispose(Managed);
  3907. end else
  3908. // cut at start
  3909. begin
  3910. Managed^.Start := Managed^.Start + Count;
  3911. Managed^.Count := Managed^.Count - Count;
  3912. end;
  3913. end else
  3914. // cut at end
  3915. if (Start + Count) = (Managed^.Start + Managed^.Count) then begin
  3916. Managed^.Count := Managed^.Count - Count;
  3917. end else
  3918. // cut in the middle
  3919. begin
  3920. New(TempManaged);
  3921. TempManaged^.NextEntry := Managed^.NextEntry;
  3922. Managed^.NextEntry := TempManaged;
  3923. TempManaged^.Start := Start + Count;
  3924. TempManaged^.Count := (Managed^.Start + Managed^.Count) - TempManaged^.Start;
  3925. Managed^.Count := Start - Managed^.Start;
  3926. end;
  3927. // we found an item so leave the loop
  3928. Break;
  3929. end;
  3930. Managed := Managed^.NextEntry;
  3931. end;
  3932. end;
  3933. end;
  3934. procedure TtsRendererOpenGL.BeforeDestruction;
  3935. begin
  3936. ClearTextures;
  3937. fTextures.Free;
  3938. inherited;
  3939. end;
  3940. procedure TtsRendererOpenGL.BeginBlock(Left, Top, Width, Height: Integer; Flags: tsBitmask);
  3941. begin
  3942. fPos.X := 0;
  3943. fPos.Y := 0;
  3944. inherited;
  3945. end;
  3946. procedure TtsRendererOpenGL.ClearTextures;
  3947. var
  3948. Idx: Integer;
  3949. begin
  3950. // Disposing items
  3951. for Idx := fTextures.Count - 1 downto 0 do
  3952. DeleteTexture(Idx);
  3953. // Clear list
  3954. fTextures.Clear;
  3955. end;
  3956. function TtsRendererOpenGL.CreateNewTexture: PtsRendererOpenGLTextureEntry;
  3957. var
  3958. Idx: Integer;
  3959. begin
  3960. New (Result);
  3961. with Result^ do begin
  3962. ID := fTextures.Add(Result);
  3963. Usage := 0;
  3964. // create opengl texture
  3965. New(Texture);
  3966. with Texture^ do begin
  3967. Width := TextureSize;
  3968. Height := TextureSize;
  3969. glGenTextures(1, @glTextureID);
  3970. glBindTexture(GL_TEXTURE_2D, glTextureID);
  3971. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  3972. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  3973. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TextureSize, TextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
  3974. end;
  3975. // initiale memory manager value
  3976. SetLength(Lines, Texture^.Height shr 1);
  3977. for Idx := Low(Lines) to High(Lines) do begin
  3978. New(Lines[Idx]);
  3979. Lines[Idx]^.NextEntry := nil;
  3980. Lines[Idx]^.Start := 0;
  3981. Lines[Idx]^.Count := Texture^.Width shr 1;
  3982. end;
  3983. end;
  3984. end;
  3985. procedure TtsRendererOpenGL.DeleteTexture(Idx: Integer);
  3986. var
  3987. pItem: PtsRendererOpenGLTextureEntry;
  3988. LineIdx: Integer;
  3989. pManaged, pTempManaged: PtsRendererOpenGLManagedEntry;
  3990. begin
  3991. pItem := fTextures[Idx];
  3992. fTextures.Delete(Idx);
  3993. if pItem <> nil then begin
  3994. with pItem^ do begin
  3995. // Free opengl texture
  3996. if Texture <> nil then begin
  3997. glDeleteTextures(1, @(Texture^.glTextureID));
  3998. Dispose(Texture);
  3999. end;
  4000. // free lines
  4001. for LineIdx := Low(Lines) to High(Lines) do begin
  4002. pManaged := Lines[LineIdx];
  4003. Lines[LineIdx] := nil;
  4004. while pManaged <> nil do begin
  4005. pTempManaged := pManaged;
  4006. pManaged := pManaged^.NextEntry;
  4007. Dispose(pTempManaged);
  4008. end;
  4009. end;
  4010. SetLength(Lines, 0);
  4011. end;
  4012. Dispose(pItem);
  4013. end;
  4014. end;
  4015. procedure TtsRendererOpenGL.DrawChar(Font: TtsFont; Char: TtsChar);
  4016. var
  4017. Texture: PtsRendererOpenGLTexture;
  4018. TempVertex: tsQuadFloat;
  4019. begin
  4020. if Char.RendererImageReference <> nil then begin
  4021. with Char.RendererImageReference as TtsRendererOpenGLImageReference do begin
  4022. Texture := GetTextureByID(TexID);
  4023. if Texture <> nil then begin
  4024. glBindTexture(GL_TEXTURE_2D, Texture^.glTextureID);
  4025. glEnable(GL_TEXTURE_2D);
  4026. // calculate new quad
  4027. TranslateQuad(TempVertex, Vertex, fPos);
  4028. glBegin(GL_QUADS);
  4029. glTexCoord2fv(@TexCoords[0]);
  4030. glVertex2fv(@TempVertex[0]);
  4031. glTexCoord2fv(@TexCoords[1]);
  4032. glVertex2fv(@TempVertex[1]);
  4033. glTexCoord2fv(@TexCoords[2]);
  4034. glVertex2fv(@TempVertex[2]);
  4035. glTexCoord2fv(@TexCoords[3]);
  4036. glVertex2fv(@TempVertex[3]);
  4037. glEnd;
  4038. // if debug is enabled
  4039. if fContext.gDebugDrawCharRects then begin
  4040. glDisable(GL_TEXTURE_2D);
  4041. // image Rect
  4042. glColor4f(0, 1, 0, 0.1);
  4043. glBegin(GL_QUADS);
  4044. glVertex2fv(@TempVertex[0]);
  4045. glVertex2fv(@TempVertex[1]);
  4046. glVertex2fv(@TempVertex[2]);
  4047. glVertex2fv(@TempVertex[3]);
  4048. glEnd;
  4049. // glyph rect
  4050. glColor4f(1, 0, 0, 0.1);
  4051. glBegin(GL_QUADS);
  4052. glVertex2f(TempVertex[0].X + Char.GlyphRect.Left, TempVertex[0].Y + Char.GlyphRect.Top);
  4053. glVertex2f(TempVertex[0].X + Char.GlyphRect.Left, TempVertex[0].Y + Char.GlyphRect.Bottom);
  4054. glVertex2f(TempVertex[0].X + Char.GlyphRect.Right, TempVertex[0].Y + Char.GlyphRect.Bottom);
  4055. glVertex2f(TempVertex[0].X + Char.GlyphRect.Right, TempVertex[0].Y + Char.GlyphRect.Top);
  4056. glEnd;
  4057. // baseline
  4058. glColor4f(0, 0, 1, 0.25);
  4059. glBegin(GL_LINES);
  4060. glVertex2f(TempVertex[0].X, 0);
  4061. glVertex2f(TempVertex[2].X, 0);
  4062. glEnd;
  4063. glColor4f(1, 1, 1, 1);
  4064. end;
  4065. end;
  4066. end;
  4067. end;
  4068. end;
  4069. procedure TtsRendererOpenGL.DrawSetColor(Red, Green, Blue, Alpha: Single);
  4070. begin
  4071. glColor4f(Red, Green, Blue, Alpha);
  4072. end;
  4073. procedure TtsRendererOpenGL.DrawSetPosition(X, Y: Integer);
  4074. begin
  4075. fPos.X := X;
  4076. fPos.Y := Y;
  4077. end;
  4078. procedure TtsRendererOpenGL.DrawSetPositionRelative(X, Y: Integer);
  4079. begin
  4080. DrawSetPosition(fPos.X + X, fPos.Y + Y);
  4081. end;
  4082. procedure TtsRendererOpenGL.FreeSpace(var FirstManaged: PtsRendererOpenGLManagedEntry; Start, Count: Word);
  4083. var
  4084. Last, Managed, Temp: PtsRendererOpenGLManagedEntry;
  4085. AddItem: Boolean;
  4086. begin
  4087. // if we have no space we can add item directly
  4088. if FirstManaged = nil then begin
  4089. New(Temp);
  4090. Temp^.Start := Start;
  4091. Temp^.Count := Count;
  4092. Temp^.NextEntry := nil;
  4093. FirstManaged := Temp;
  4094. end else
  4095. // Special handling for first Item
  4096. if Start + Count < FirstManaged^.Start then begin
  4097. New(Temp);
  4098. Temp^.Start := Start;
  4099. Temp^.Count := Count;
  4100. Temp^.NextEntry := FirstManaged;
  4101. FirstManaged := Temp;
  4102. end else
  4103. begin
  4104. Managed := FirstManaged;
  4105. Last := nil;
  4106. while Managed <> nil do begin
  4107. // block is in front of another
  4108. if Start + Count = Managed^.Start then begin
  4109. Managed^.Start := Managed^.Start - Count;
  4110. Managed^.Count := Managed^.Count + Count;
  4111. if Last <> nil then begin
  4112. if Last^.Start + Last^.Count = Managed^.Start then begin
  4113. // Remove Item
  4114. Last^.Count := Last^.Count + Managed^.Count;
  4115. Last^.NextEntry := Managed^.NextEntry;
  4116. Dispose(Managed);
  4117. end;
  4118. end;
  4119. Break;
  4120. end else
  4121. // block is behind another
  4122. if Start = Managed^.Start + Managed^.Count then begin
  4123. Managed^.Count := Managed^.Count + Count;
  4124. Temp := Managed^.NextEntry;
  4125. if Temp <> nil then begin
  4126. if Managed^.Start + Managed^.Count = Temp^.Start then begin
  4127. // Remove Item
  4128. Managed^.Count := Managed^.Count + Temp^.Count;
  4129. Managed^.NextEntry := Temp^.NextEntry;
  4130. Dispose(Temp);
  4131. end;
  4132. end;
  4133. Break;
  4134. end else
  4135. // the block dosn't border an other so we must create some other
  4136. begin
  4137. AddItem := False;
  4138. if not (Managed^.NextEntry <> nil) then
  4139. AddItem := True
  4140. else
  4141. if (Managed^.Start + Managed^.Count < Start) and (Managed^.NextEntry^.Start > Start + Count) then
  4142. AddItem := True;
  4143. if AddItem then begin
  4144. New(Temp);
  4145. Temp^.Start := Start;
  4146. Temp^.Count := Count;
  4147. Temp^.NextEntry := Managed^.NextEntry;
  4148. Managed^.NextEntry := Temp;
  4149. Break;
  4150. end;
  4151. end;
  4152. Last := Managed;
  4153. Managed := Managed^.NextEntry;
  4154. end;
  4155. end;
  4156. end;
  4157. function TtsRendererOpenGL.GetTextureByID(ID: Integer): PtsRendererOpenGLTexture;
  4158. var
  4159. Idx: Integer;
  4160. pTexture: PtsRendererOpenGLTextureEntry;
  4161. begin
  4162. Result := nil;
  4163. for Idx := 0 to fTextures.Count - 1 do begin
  4164. pTexture := fTextures[Idx];
  4165. if pTexture <> nil then
  4166. if pTexture^.ID = ID then begin
  4167. Result := pTexture^.Texture;
  4168. Break;
  4169. end;
  4170. end;
  4171. end;
  4172. procedure TtsRendererOpenGL.RemoveImageReference(ImageReference: TtsRendererImageReference);
  4173. var
  4174. OpenGLRef: TtsRendererOpenGLImageReference;
  4175. pItem: PtsRendererOpenGLTextureEntry;
  4176. Idx, TempIdx: Integer;
  4177. TempWidth, TempHeight: Integer;
  4178. NeedX, NeedY: Integer;
  4179. LinesY, TempX, TempY: Integer;
  4180. begin
  4181. OpenGLRef := TtsRendererOpenGLImageReference(ImageReference);
  4182. // freeing texture
  4183. for Idx := 0 to fTextures.Count - 1 do begin
  4184. pItem := fTextures[Idx];
  4185. if pItem <> nil then begin
  4186. if pItem^.ID = OpenGLRef.TexID then begin
  4187. TempWidth := OpenGLRef.Coordinates.Right - OpenGLRef.Coordinates.Left;
  4188. TempHeight := OpenGLRef.Coordinates.Bottom - OpenGLRef.Coordinates.Top;
  4189. with pItem^ do begin
  4190. // calc size
  4191. NeedX := TempWidth shr 1;
  4192. if (TempWidth and 1) > 0 then
  4193. Inc(NeedX);
  4194. NeedY := TempHeight shr 1;
  4195. if (TempHeight and 1) > 0 then
  4196. Inc(NeedY);
  4197. TempY := OpenGLRef.Coordinates.Top shr 1;
  4198. TempX := OpenGLRef.Coordinates.Left shr 1;
  4199. Usage := Usage - NeedX * NeedY;
  4200. Assert(Usage >= 0);
  4201. // Points
  4202. for LinesY := 0 to NeedY - 1 do
  4203. FreeSpace(Lines[TempY + LinesY], TempX, NeedX);
  4204. // freeing opengltexture
  4205. if Usage = 0 then begin
  4206. for TempIdx := 0 to fTextures.Count - 1 do begin
  4207. if PtsRendererOpenGLTextureEntry(fTextures[TempIdx])^.ID = pItem^.ID then begin
  4208. DeleteTexture(TempIdx);
  4209. Break;
  4210. end;
  4211. end;
  4212. end;
  4213. end;
  4214. Break;
  4215. end;
  4216. end;
  4217. end;
  4218. end;
  4219. { TtsContext }
  4220. function TtsContext.AnsiToWide(pText: pAnsiChar): pWideChar;
  4221. function GetDefaultChar: WideChar;
  4222. begin
  4223. Result := #0;
  4224. if tsGetParameteri(TS_EMPTY_CP_ENTRY) = TS_EMPTY_CP_ENTRY_USE_DEFAULT then
  4225. if ActiveFont <> nil then
  4226. Result := ActiveFont.DefaultChar;
  4227. end;
  4228. begin
  4229. Result := nil;
  4230. // UTF-8
  4231. if gCodePage = TS_CODEPAGE_UTF8 then begin
  4232. Result := tsStrAlloc(Length(pText));
  4233. tsAnsiUTF8ToWide(Result, pText, GetDefaultChar);
  4234. end else
  4235. // ISO 8859-1
  4236. if gCodePage = TS_CODEPAGE_8859_1 then begin
  4237. Result := tsStrAlloc(Length(pText));
  4238. tsAnsiISO_8859_1_ToWide(Result, pText);
  4239. end else
  4240. // single or double byte CodePage
  4241. begin
  4242. if (Addr(gCodePageFunc) <> nil) and (gCodePagePtr <> nil) then begin
  4243. Result := tsStrAlloc(Length(pText));
  4244. gCodePageFunc(Result, pText, gCodePagePtr, GetDefaultChar);
  4245. end;
  4246. end;
  4247. end;
  4248. procedure TtsContext.ClearFonts;
  4249. var
  4250. List: TList;
  4251. Idx: Integer;
  4252. pItem: PtsContextFontEntry;
  4253. begin
  4254. List := TList.Create;
  4255. try
  4256. fFonts.GetValues(List);
  4257. fFonts.Clear;
  4258. for Idx := 0 to List.Count - 1 do begin
  4259. pItem := List[Idx];
  4260. pItem^.Font.Free;
  4261. Dispose(pItem);
  4262. end;
  4263. finally
  4264. List.Free;
  4265. end;
  4266. end;
  4267. procedure TtsContext.ClearImages;
  4268. var
  4269. List: TList;
  4270. Idx: Integer;
  4271. pItem: PtsContextImageEntry;
  4272. begin
  4273. List := TList.Create;
  4274. try
  4275. fImages.GetValues(List);
  4276. fImages.Clear;
  4277. for Idx := 0 to List.Count - 1 do begin
  4278. pItem := List[Idx];
  4279. pItem^.Image.Free;
  4280. Dispose(pItem);
  4281. end;
  4282. finally
  4283. List.Free;
  4284. end;
  4285. end;
  4286. constructor TtsContext.Create;
  4287. begin
  4288. inherited;
  4289. Inc(gLastContextID);
  4290. fContextID := gLastContextID;
  4291. // hashes
  4292. fFonts := TtsHash.Create(127);
  4293. fImages := TtsHash.Create(127);
  4294. // defaults
  4295. gEmptyCodePageEntry := TS_EMPTY_CP_ENTRY_USE_DEFAULT;
  4296. gCodePage := TS_CODEPAGE_8859_1;
  4297. gCodePagePtr := nil; //@CP_8859_1;
  4298. gCodePageFunc := nil; //tsAnsiSBCDToWide;
  4299. gGlobalFormat := TS_FORMAT_RGBA8;
  4300. gGlobalAntiAliasing := TS_ANTIALIASING_NORMAL;
  4301. gSingleLine := TS_SINGLE_LINE_BASELINE;
  4302. gAlign := TS_ALIGN_LEFT;
  4303. gVAlign := TS_VALIGN_TOP;
  4304. gClip := TS_CLIP_COMPLETE;
  4305. gImageMode[tsModeRed] := TS_MODE_REPLACE;
  4306. gImageMode[tsModeGreen] := TS_MODE_REPLACE;
  4307. gImageMode[tsModeBlue] := TS_MODE_REPLACE;
  4308. gImageMode[tsModeAlpha] := TS_MODE_MODULATE;
  4309. gImageMode[tsModeLuminance] := TS_MODE_REPLACE;
  4310. gImageLibrary := 0;
  4311. end;
  4312. destructor TtsContext.Destroy;
  4313. begin
  4314. ClearFonts;
  4315. fFonts.Free;
  4316. ClearImages;
  4317. fImages.Free;
  4318. if Renderer <> nil then
  4319. Renderer.Free;
  4320. inherited;
  4321. end;
  4322. function TtsContext.FontAdd(Font: TtsFont): Cardinal;
  4323. var
  4324. Entry: PtsContextFontEntry;
  4325. begin
  4326. New(Entry);
  4327. Inc(fLastFontID);
  4328. Entry^.FontID := fLastFontID;
  4329. Entry^.Font := Font;
  4330. fFonts.Add(fLastFontID, Entry);
  4331. Result := fLastFontID;
  4332. end;
  4333. function TtsContext.FontCount: Cardinal;
  4334. begin
  4335. Result := fFonts.Count;
  4336. end;
  4337. procedure TtsContext.FontDelete(Font: Cardinal);
  4338. var
  4339. Entry: PtsContextFontEntry;
  4340. begin
  4341. if fLastFontID = Font then
  4342. Renderer.FontActivate(0);
  4343. Entry := fFonts.Get(Font);
  4344. if Entry <> nil then begin
  4345. fFonts.Delete(Entry^.FontID);
  4346. Dispose(Entry);
  4347. end;
  4348. end;
  4349. function TtsContext.FontGet(Font: Cardinal): TtsFont;
  4350. var
  4351. Entry: PtsContextFontEntry;
  4352. begin
  4353. Entry := fFonts.Get(Font);
  4354. if Entry <> nil then
  4355. Result := Entry^.Font
  4356. else
  4357. Result := nil;
  4358. end;
  4359. function TtsContext.GetActiveFont: TtsFont;
  4360. begin
  4361. Result := nil;
  4362. if Renderer <> nil then
  4363. Result := Renderer.ActiveFont;
  4364. end;
  4365. function TtsContext.GetIsLocked: boolean;
  4366. begin
  4367. if Renderer <> nil then
  4368. Result := Renderer.isBlock
  4369. else
  4370. Result := False;
  4371. end;
  4372. function TtsContext.ImageAdd(Image: TtsImage): Cardinal;
  4373. var
  4374. Entry: PtsContextImageEntry;
  4375. begin
  4376. New(Entry);
  4377. Inc(fLastImageID);
  4378. Entry^.ImageID := fLastImageID;
  4379. Entry^.Image := Image;
  4380. fImages.Add(fLastImageID, Entry);
  4381. Result := fLastImageID;
  4382. end;
  4383. function TtsContext.ImageCount: Cardinal;
  4384. begin
  4385. Result := fImages.Count;
  4386. end;
  4387. procedure TtsContext.ImageDelete(Image: Cardinal);
  4388. var
  4389. Entry: PtsContextImageEntry;
  4390. begin
  4391. Entry := fImages.Get(Image);
  4392. if Entry <> nil then begin
  4393. fImages.Delete(Entry^.ImageID);
  4394. Dispose(Entry);
  4395. end;
  4396. end;
  4397. function TtsContext.ImageGet(Image: Cardinal): TtsImage;
  4398. var
  4399. Entry: PtsContextImageEntry;
  4400. begin
  4401. Entry := fImages.Get(Image);
  4402. if Entry <> nil then
  4403. Result := Entry^.Image
  4404. else
  4405. Result := nil;
  4406. end;
  4407. end.