BsTextData.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. #pragma once
  2. #include "BsCorePrerequisites.h"
  3. #include "BsFontDesc.h"
  4. #include "BsVector2I.h"
  5. namespace BansheeEngine
  6. {
  7. /**
  8. * @brief This object takes as input a string, a font and optionally some constraints (like word wrap)
  9. * and outputs a set of character data you may use for rendering or metrics.
  10. */
  11. class TextDataBase
  12. {
  13. protected:
  14. /**
  15. * @brief Represents a single word as a set of characters, or optionally just a blank space
  16. * of a certain length.
  17. *
  18. * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
  19. */
  20. class TextWord
  21. {
  22. public:
  23. /**
  24. * @brief Initializes the word and signals if it just a space (or multiple spaces), or
  25. * an actual word with letters.
  26. */
  27. void init(bool spacer);
  28. /**
  29. * @brief Appends a new character to the word.
  30. *
  31. * @param charIdx Sequential index of the character in the original string.
  32. * @param desc Character description from the font.
  33. *
  34. * @returns How many pixels did the added character expand the word by.
  35. */
  36. UINT32 addChar(UINT32 charIdx, const CHAR_DESC& desc);
  37. /**
  38. * @brief Adds a space to the word. Word must have previously have been declared as
  39. * a "spacer".
  40. */
  41. void addSpace(UINT32 spaceWidth);
  42. /**
  43. * @brief Returns the width of the word in pixels.
  44. */
  45. UINT32 getWidth() const { return mWidth; }
  46. /**
  47. * @brief Returns height of the word in pixels.
  48. */
  49. UINT32 getHeight() const { return mHeight; }
  50. /**
  51. * @brief Calculates new width of the word if we were to add the provided character,
  52. * without actually adding it.
  53. *
  54. * @param desc Character description from the font.
  55. *
  56. * @returns Width of the word in pixels with the character appended to it.
  57. */
  58. UINT32 calcWidthWithChar(const CHAR_DESC& desc);
  59. /**
  60. * @brief Returns true if word is a spacer. Spacers contain just a space
  61. * of a certain length with no actual characters.
  62. */
  63. bool isSpacer() const { return mSpacer; }
  64. /**
  65. * @brief Returns the number of characters in the word.
  66. */
  67. UINT32 getNumChars() const { return mLastChar == nullptr ? 0 : (mCharsEnd - mCharsStart + 1); }
  68. /**
  69. * @brief Returns the index of the starting character in the word.
  70. */
  71. UINT32 getCharsStart() const { return mCharsStart; }
  72. /**
  73. * @brief Returns the index of the last character in the word.
  74. */
  75. UINT32 getCharsEnd() const { return mCharsEnd; }
  76. /**
  77. * @brief Calculates width of the character by which it would expand the width of the word
  78. * if it was added to it.
  79. *
  80. * @param prevDesc Descriptor of the character preceding the one we need the width for. Can be null.
  81. * @param desc Character description from the font.
  82. *
  83. * @returns How many pixels would the added character expand the word by.
  84. */
  85. static UINT32 calcCharWidth(const CHAR_DESC* prevDesc, const CHAR_DESC& desc);
  86. private:
  87. UINT32 mCharsStart, mCharsEnd;
  88. UINT32 mWidth;
  89. UINT32 mHeight;
  90. const CHAR_DESC* mLastChar;
  91. bool mSpacer;
  92. UINT32 mSpaceWidth;
  93. };
  94. /**
  95. * @brief Contains information about a single texture page that contains rendered
  96. * character data.
  97. *
  98. * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
  99. */
  100. struct PageInfo
  101. {
  102. UINT32 numQuads;
  103. HTexture texture;
  104. };
  105. public:
  106. /**
  107. * @brief Represents a single line as a set of words.
  108. *
  109. * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
  110. */
  111. class BS_CORE_EXPORT TextLine
  112. {
  113. public:
  114. /**
  115. * @brief Returns width of the line in pixels.
  116. */
  117. UINT32 getWidth() const { return mWidth; }
  118. /**
  119. * @brief Returns height of the line in pixels.
  120. */
  121. UINT32 getHeight() const { return mHeight; }
  122. /**
  123. * @brief Returns an offset used to separate two lines.
  124. */
  125. UINT32 getYOffset() const { return mTextData->getLineHeight(); }
  126. /**
  127. * @brief Calculates new width of the line if we were to add the provided character,
  128. * without actually adding it.
  129. *
  130. * @param desc Character description from the font.
  131. *
  132. * @returns Width of the line in pixels with the character appended to it.
  133. */
  134. UINT32 calcWidthWithChar(const CHAR_DESC& desc);
  135. /**
  136. * @brief Fills the vertex/uv/index buffers for the specified page, with all the character data
  137. * needed for rendering.
  138. *
  139. * @param page The page.
  140. * @param [out] vertices Pre-allocated array where character vertices will be written.
  141. * @param [out] uvs Pre-allocated array where character uv coordinates will be written.
  142. * @param [out] indexes Pre-allocated array where character indices will be written.
  143. * @param offset Offsets the location at which the method writes to the buffers.
  144. * Counted as number of quads.
  145. * @param size Total number of quads that can fit into the specified buffers.
  146. *
  147. * @return Number of quads that were written.
  148. */
  149. UINT32 fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const;
  150. /**
  151. * @brief Checks are we at a word boundary (i.e. next added character will start a new word).
  152. */
  153. bool isAtWordBoundary() const;
  154. /**
  155. * @brief Returns the total number of characters on this line.
  156. */
  157. UINT32 getNumChars() const;
  158. /**
  159. * @brief Query if this line was created explicitly due to a newline character.
  160. * As opposed to a line that was created because a word couldn't fit on the previous line.
  161. */
  162. bool hasNewlineChar() const { return mHasNewline; }
  163. private:
  164. friend class TextDataBase;
  165. /**
  166. * @brief Appends a new character to the line.
  167. *
  168. * @param charIdx Sequential index of the character in the original string.
  169. * @param desc Character description from the font.
  170. */
  171. void add(UINT32 charIdx, const CHAR_DESC& charDesc);
  172. /**
  173. * @brief Appends a space to the line.
  174. */
  175. void addSpace(UINT32 spaceWidth);
  176. /**
  177. * @brief Adds a new word to the line.
  178. *
  179. * @param wordIdx Sequential index of the word in the original string.
  180. * Spaces are counted as words as well.
  181. * @param word Description of the word.
  182. */
  183. void addWord(UINT32 wordIdx, const TextWord& word);
  184. /**
  185. * @brief Initializes the line. Must be called after construction.
  186. */
  187. void init(TextDataBase* textData);
  188. /**
  189. * @brief Finalizes the line. Do not add new characters/words after a line has
  190. * been finalized.
  191. *
  192. * @param hasNewlineChar Set to true if line was create due to an explicit newline char.
  193. * As opposed to a line that was created because a word couldn't fit
  194. * on the previous line.
  195. */
  196. void finalize(bool hasNewlineChar);
  197. /**
  198. * @brief Returns true if the line contains no words.
  199. */
  200. bool isEmpty() const { return mIsEmpty; }
  201. /**
  202. * @brief Removes last word from the line and returns its sequential index.
  203. */
  204. UINT32 removeLastWord();
  205. /**
  206. * @brief Calculates the line width and height in pixels.
  207. */
  208. void calculateBounds();
  209. private:
  210. TextDataBase* mTextData;
  211. UINT32 mWordsStart, mWordsEnd;
  212. UINT32 mWidth;
  213. UINT32 mHeight;
  214. bool mIsEmpty;
  215. bool mHasNewline;
  216. };
  217. public:
  218. /**
  219. * @brief Initializes a new text data using the specified string and font. Text will attempt to fit into
  220. * the provided area. If enabled it will wrap words to new line when they don't fit. Individual words
  221. * will be broken into multiple pieces if they don't fit on a single line when word break
  222. * is enabled, otherwise they will be clipped. If the specified area is zero size then the text
  223. * will not be clipped or word wrapped in any way.
  224. *
  225. * After this object is constructed you may call various getter methods to get needed information.
  226. */
  227. BS_CORE_EXPORT TextDataBase(const WString& text, const HFont& font, UINT32 fontSize,
  228. UINT32 width = 0, UINT32 height = 0, bool wordWrap = false, bool wordBreak = true);
  229. BS_CORE_EXPORT virtual ~TextDataBase() { }
  230. /**
  231. * @brief Returns the number of lines that were generated.
  232. */
  233. BS_CORE_EXPORT UINT32 getNumLines() const { return mNumLines; }
  234. /**
  235. * @brief Returns the number of font pages references by the used characters.
  236. */
  237. BS_CORE_EXPORT UINT32 getNumPages() const { return mNumPageInfos; }
  238. /**
  239. * @brief Returns the height of a line in pixels.
  240. */
  241. BS_CORE_EXPORT UINT32 getLineHeight() const;
  242. /**
  243. * @brief Gets information describing a single line at the specified index.
  244. */
  245. BS_CORE_EXPORT const TextLine& getLine(UINT32 idx) const { return mLines[idx]; }
  246. /**
  247. * @brief Returns font texture for the provided page index.
  248. */
  249. BS_CORE_EXPORT const HTexture& getTextureForPage(UINT32 page) const;
  250. /**
  251. * @brief Returns the number of quads used by all the characters in the provided page.
  252. */
  253. BS_CORE_EXPORT UINT32 getNumQuadsForPage(UINT32 page) const { return mPageInfos[page].numQuads; }
  254. /**
  255. * @brief Returns the width of the actual text in pixels.
  256. */
  257. BS_CORE_EXPORT UINT32 getWidth() const;
  258. /**
  259. * @brief Returns the height of the actual text in pixels.
  260. */
  261. BS_CORE_EXPORT UINT32 getHeight() const;
  262. protected:
  263. /**
  264. * @brief Copies internally stored data in temporary buffers to a persistent buffer.
  265. *
  266. * @param text Text originally used for creating the internal temporary buffer data.
  267. * @param buffer Memory location to copy the data to. If null then no data will be copied
  268. * and the parameter \p size will contain the required buffer size.
  269. * @param size Size of the provided memory buffer, or if the buffer is null, this will
  270. * contain the required buffer size after method exists.
  271. * @param freeTemporary If true the internal temporary data will be freed after copying.
  272. *
  273. * @note Must be called after text data has been constructed and is in the temporary buffers.
  274. */
  275. BS_CORE_EXPORT void generatePersistentData(const WString& text, UINT8* buffer, UINT32& size, bool freeTemporary = true);
  276. private:
  277. friend class TextLine;
  278. /**
  279. * @brief Returns Y offset that determines the line on which the characters are placed.
  280. * In pixels.
  281. */
  282. INT32 getBaselineOffset() const;
  283. /**
  284. * @brief Returns the width of a single space in pixels.
  285. */
  286. UINT32 getSpaceWidth() const;
  287. /**
  288. * @brief Gets a description of a single character referenced by its sequential index
  289. * based on the original string.
  290. */
  291. const CHAR_DESC& getChar(UINT32 idx) const { return *mChars[idx]; }
  292. /**
  293. * @brief Gets a description of a single word referenced by its sequential index
  294. * based on the original string.
  295. */
  296. const TextWord& getWord(UINT32 idx) const { return mWords[idx]; }
  297. protected:
  298. const CHAR_DESC** mChars;
  299. UINT32 mNumChars;
  300. TextWord* mWords;
  301. UINT32 mNumWords;
  302. TextLine* mLines;
  303. UINT32 mNumLines;
  304. PageInfo* mPageInfos;
  305. UINT32 mNumPageInfos;
  306. HFont mFont;
  307. SPtr<const FontBitmap> mFontData;
  308. // Static buffers used to reduce runtime memory allocation
  309. protected:
  310. /**
  311. * @brief Stores per-thread memory buffers used to reduce memory allocation.
  312. */
  313. // Note: I could replace this with the global frame allocator to avoid the extra logic
  314. struct BufferData
  315. {
  316. BufferData();
  317. ~BufferData();
  318. /**
  319. * @brief Allocates a new word and adds it to the buffer. Returns index of the word
  320. * in the word buffer.
  321. *
  322. * @param spacer Specify true if the word is only to contain spaces. (Spaces are considered
  323. * a special type of word).
  324. */
  325. UINT32 allocWord(bool spacer);
  326. /**
  327. * @brief Allocates a new line and adds it to the buffer. Returns index of the line
  328. * in the line buffer.
  329. */
  330. UINT32 allocLine(TextDataBase* textData);
  331. /**
  332. * @brief Increments the count of characters for the referenced page, and optionally
  333. * creates page info if it doesn't already exist.
  334. */
  335. void addCharToPage(UINT32 page, const FontBitmap& fontData);
  336. /**
  337. * @brief Resets all allocation counters, but doesn't actually release memory.
  338. */
  339. void deallocAll();
  340. TextWord* WordBuffer;
  341. UINT32 WordBufferSize;
  342. UINT32 NextFreeWord;
  343. TextLine* LineBuffer;
  344. UINT32 LineBufferSize;
  345. UINT32 NextFreeLine;
  346. PageInfo* PageBuffer;
  347. UINT32 PageBufferSize;
  348. UINT32 NextFreePageInfo;
  349. };
  350. static BS_THREADLOCAL BufferData* MemBuffer;
  351. /**
  352. * @brief Allocates an initial set of buffers that will be reused while parsing
  353. * text data.
  354. */
  355. static void initAlloc();
  356. };
  357. /**
  358. * @copydoc TextDataBase
  359. */
  360. template<class Alloc = GenAlloc>
  361. class TextData : public TextDataBase
  362. {
  363. public:
  364. /**
  365. * @copydoc TextDataBase::TextDataBase
  366. */
  367. TextData(const WString& text, const HFont& font, UINT32 fontSize,
  368. UINT32 width = 0, UINT32 height = 0, bool wordWrap = false, bool wordBreak = true)
  369. :TextDataBase(text, font, fontSize, width, height, wordWrap, wordBreak), mData(nullptr)
  370. {
  371. UINT32 totalBufferSize = 0;
  372. generatePersistentData(text, nullptr, totalBufferSize);
  373. mData = (UINT8*)bs_alloc<Alloc>(totalBufferSize);
  374. generatePersistentData(text, (UINT8*)mData, totalBufferSize);
  375. }
  376. ~TextData()
  377. {
  378. if (mData != nullptr)
  379. bs_free<Alloc>(mData);
  380. }
  381. private:
  382. UINT8* mData;
  383. };
  384. }