| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- #pragma once
- #include "BsCorePrerequisites.h"
- #include "BsFontDesc.h"
- #include "BsVector2I.h"
- namespace BansheeEngine
- {
- /**
- * @brief This object takes as input a string, a font and optionally some constraints (like word wrap)
- * and outputs a set of character data you may use for rendering or metrics.
- */
- class TextDataBase
- {
- protected:
- /**
- * @brief Represents a single word as a set of characters, or optionally just a blank space
- * of a certain length.
- *
- * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
- */
- class TextWord
- {
- public:
- /**
- * @brief Initializes the word and signals if it just a space (or multiple spaces), or
- * an actual word with letters.
- */
- void init(bool spacer);
- /**
- * @brief Appends a new character to the word.
- *
- * @param charIdx Sequential index of the character in the original string.
- * @param desc Character description from the font.
- *
- * @returns How many pixels did the added character expand the word by.
- */
- UINT32 addChar(UINT32 charIdx, const CHAR_DESC& desc);
- /**
- * @brief Adds a space to the word. Word must have previously have been declared as
- * a "spacer".
- */
- void addSpace(UINT32 spaceWidth);
- /**
- * @brief Returns the width of the word in pixels.
- */
- UINT32 getWidth() const { return mWidth; }
- /**
- * @brief Returns height of the word in pixels.
- */
- UINT32 getHeight() const { return mHeight; }
- /**
- * @brief Calculates new width of the word if we were to add the provided character,
- * without actually adding it.
- *
- * @param desc Character description from the font.
- *
- * @returns Width of the word in pixels with the character appended to it.
- */
- UINT32 calcWidthWithChar(const CHAR_DESC& desc);
- /**
- * @brief Returns true if word is a spacer. Spacers contain just a space
- * of a certain length with no actual characters.
- */
- bool isSpacer() const { return mSpacer; }
- /**
- * @brief Returns the number of characters in the word.
- */
- UINT32 getNumChars() const { return mLastChar == nullptr ? 0 : (mCharsEnd - mCharsStart + 1); }
- /**
- * @brief Returns the index of the starting character in the word.
- */
- UINT32 getCharsStart() const { return mCharsStart; }
- /**
- * @brief Returns the index of the last character in the word.
- */
- UINT32 getCharsEnd() const { return mCharsEnd; }
- /**
- * @brief Calculates width of the character by which it would expand the width of the word
- * if it was added to it.
- *
- * @param prevDesc Descriptor of the character preceding the one we need the width for. Can be null.
- * @param desc Character description from the font.
- *
- * @returns How many pixels would the added character expand the word by.
- */
- static UINT32 calcCharWidth(const CHAR_DESC* prevDesc, const CHAR_DESC& desc);
- private:
- UINT32 mCharsStart, mCharsEnd;
- UINT32 mWidth;
- UINT32 mHeight;
- const CHAR_DESC* mLastChar;
- bool mSpacer;
- UINT32 mSpaceWidth;
- };
- /**
- * @brief Contains information about a single texture page that contains rendered
- * character data.
- *
- * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
- */
- struct PageInfo
- {
- UINT32 numQuads;
- HTexture texture;
- };
- public:
- /**
- * @brief Represents a single line as a set of words.
- *
- * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
- */
- class BS_CORE_EXPORT TextLine
- {
- public:
- /**
- * @brief Returns width of the line in pixels.
- */
- UINT32 getWidth() const { return mWidth; }
- /**
- * @brief Returns height of the line in pixels.
- */
- UINT32 getHeight() const { return mHeight; }
- /**
- * @brief Returns an offset used to separate two lines.
- */
- UINT32 getYOffset() const { return mTextData->getLineHeight(); }
- /**
- * @brief Calculates new width of the line if we were to add the provided character,
- * without actually adding it.
- *
- * @param desc Character description from the font.
- * @param space True if the character is a space.
- *
- * @returns Width of the line in pixels with the character appended to it.
- */
- UINT32 calcWidthWithChar(const CHAR_DESC& desc, bool space);
- /**
- * @brief Fills the vertex/uv/index buffers for the specified page, with all the character data
- * needed for rendering.
- *
- * @param page The page.
- * @param [out] vertices Pre-allocated array where character vertices will be written.
- * @param [out] uvs Pre-allocated array where character uv coordinates will be written.
- * @param [out] indexes Pre-allocated array where character indices will be written.
- * @param offset Offsets the location at which the method writes to the buffers.
- * Counted as number of quads.
- * @param size Total number of quads that can fit into the specified buffers.
- *
- * @return Number of quads that were written.
- */
- UINT32 fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const;
- /**
- * @brief Checks are we at a word boundary (i.e. next added character will start a new word).
- */
- bool isAtWordBoundary() const;
- /**
- * @brief Returns the total number of characters on this line.
- */
- UINT32 getNumChars() const;
- /**
- * @brief Query if this line was created explicitly due to a newline character.
- * As opposed to a line that was created because a word couldn't fit on the previous line.
- */
- bool hasNewlineChar() const { return mHasNewline; }
- private:
- friend class TextDataBase;
- /**
- * @brief Appends a new character to the line.
- *
- * @param charIdx Sequential index of the character in the original string.
- * @param desc Character description from the font.
- */
- void add(UINT32 charIdx, const CHAR_DESC& charDesc);
- /**
- * @brief Appends a space to the line.
- */
- void addSpace();
- /**
- * @brief Adds a new word to the line.
- *
- * @param wordIdx Sequential index of the word in the original string.
- * Spaces are counted as words as well.
- * @param word Description of the word.
- */
- void addWord(UINT32 wordIdx, const TextWord& word);
- /**
- * @brief Initializes the line. Must be called after construction.
- */
- void init(TextDataBase* textData);
- /**
- * @brief Finalizes the line. Do not add new characters/words after a line has
- * been finalized.
- *
- * @param hasNewlineChar Set to true if line was create due to an explicit newline char.
- * As opposed to a line that was created because a word couldn't fit
- * on the previous line.
- */
- void finalize(bool hasNewlineChar);
- /**
- * @brief Returns true if the line contains no words.
- */
- bool isEmpty() const { return mIsEmpty; }
- /**
- * @brief Removes last word from the line and returns its sequential index.
- */
- UINT32 removeLastWord();
- /**
- * @brief Calculates the line width and height in pixels.
- */
- void calculateBounds();
- private:
- TextDataBase* mTextData;
- UINT32 mWordsStart, mWordsEnd;
- UINT32 mWidth;
- UINT32 mHeight;
- bool mIsEmpty;
- bool mHasNewline;
- };
- public:
- /**
- * @brief Initializes a new text data using the specified string and font. Text will attempt to fit into
- * the provided area. If enabled it will wrap words to new line when they don't fit. Individual words
- * will be broken into multiple pieces if they don't fit on a single line when word break
- * is enabled, otherwise they will be clipped. If the specified area is zero size then the text
- * will not be clipped or word wrapped in any way.
- *
- * After this object is constructed you may call various getter methods to get needed information.
- */
- BS_CORE_EXPORT TextDataBase(const WString& text, const HFont& font, UINT32 fontSize,
- UINT32 width = 0, UINT32 height = 0, bool wordWrap = false, bool wordBreak = true);
- BS_CORE_EXPORT virtual ~TextDataBase() { }
- /**
- * @brief Returns the number of lines that were generated.
- */
- BS_CORE_EXPORT UINT32 getNumLines() const { return mNumLines; }
- /**
- * @brief Returns the number of font pages references by the used characters.
- */
- BS_CORE_EXPORT UINT32 getNumPages() const { return mNumPageInfos; }
- /**
- * @brief Returns the height of a line in pixels.
- */
- BS_CORE_EXPORT UINT32 getLineHeight() const;
- /**
- * @brief Gets information describing a single line at the specified index.
- */
- BS_CORE_EXPORT const TextLine& getLine(UINT32 idx) const { return mLines[idx]; }
- /**
- * @brief Returns font texture for the provided page index.
- */
- BS_CORE_EXPORT const HTexture& getTextureForPage(UINT32 page) const;
- /**
- * @brief Returns the number of quads used by all the characters in the provided page.
- */
- BS_CORE_EXPORT UINT32 getNumQuadsForPage(UINT32 page) const { return mPageInfos[page].numQuads; }
- /**
- * @brief Returns the width of the actual text in pixels.
- */
- BS_CORE_EXPORT UINT32 getWidth() const;
- /**
- * @brief Returns the height of the actual text in pixels.
- */
- BS_CORE_EXPORT UINT32 getHeight() const;
- protected:
- /**
- * @brief Copies internally stored data in temporary buffers to a persistent buffer.
- *
- * @param text Text originally used for creating the internal temporary buffer data.
- * @param buffer Memory location to copy the data to. If null then no data will be copied
- * and the parameter \p size will contain the required buffer size.
- * @param size Size of the provided memory buffer, or if the buffer is null, this will
- * contain the required buffer size after method exists.
- * @param freeTemporary If true the internal temporary data will be freed after copying.
- *
- * @note Must be called after text data has been constructed and is in the temporary buffers.
- */
- BS_CORE_EXPORT void generatePersistentData(const WString& text, UINT8* buffer, UINT32& size, bool freeTemporary = true);
- private:
- friend class TextLine;
- /**
- * @brief Returns Y offset that determines the line on which the characters are placed.
- * In pixels.
- */
- INT32 getBaselineOffset() const;
- /**
- * @brief Returns the width of a single space in pixels.
- */
- UINT32 getSpaceWidth() const;
- /**
- * @brief Gets a description of a single character referenced by its sequential index
- * based on the original string.
- */
- const CHAR_DESC& getChar(UINT32 idx) const { return *mChars[idx]; }
- /**
- * @brief Gets a description of a single word referenced by its sequential index
- * based on the original string.
- */
- const TextWord& getWord(UINT32 idx) const { return mWords[idx]; }
- protected:
- const CHAR_DESC** mChars;
- UINT32 mNumChars;
- TextWord* mWords;
- UINT32 mNumWords;
- TextLine* mLines;
- UINT32 mNumLines;
- PageInfo* mPageInfos;
- UINT32 mNumPageInfos;
- HFont mFont;
- const FontData* mFontData;
- // Static buffers used to reduce runtime memory allocation
- protected:
- /**
- * @brief Stores per-thread memory buffers used to reduce memory allocation.
- */
- // Note: I could replace this with the global frame allocator to avoid the extra logic
- struct BufferData
- {
- BufferData();
- ~BufferData();
- /**
- * @brief Allocates a new word and adds it to the buffer. Returns index of the word
- * in the word buffer.
- *
- * @param spacer Specify true if the word is only to contain spaces. (Spaces are considered
- * a special type of word).
- */
- UINT32 allocWord(bool spacer);
- /**
- * @brief Allocates a new line and adds it to the buffer. Returns index of the line
- * in the line buffer.
- */
- UINT32 allocLine(TextDataBase* textData);
- /**
- * @brief Increments the count of characters for the referenced page, and optionally
- * creates page info if it doesn't already exist.
- */
- void addCharToPage(UINT32 page, const FontData& fontData);
- /**
- * @brief Resets all allocation counters, but doesn't actually release memory.
- */
- void deallocAll();
- TextWord* WordBuffer;
- UINT32 WordBufferSize;
- UINT32 NextFreeWord;
- TextLine* LineBuffer;
- UINT32 LineBufferSize;
- UINT32 NextFreeLine;
- PageInfo* PageBuffer;
- UINT32 PageBufferSize;
- UINT32 NextFreePageInfo;
- };
- static BS_THREADLOCAL BufferData* MemBuffer;
- /**
- * @brief Allocates an initial set of buffers that will be reused while parsing
- * text data.
- */
- static void initAlloc();
- };
- /**
- * @copydoc TextDataBase
- */
- template<class Alloc = GenAlloc>
- class TextData : public TextDataBase
- {
- public:
- /**
- * @copydoc TextDataBase::TextDataBase
- */
- TextData(const WString& text, const HFont& font, UINT32 fontSize,
- UINT32 width = 0, UINT32 height = 0, bool wordWrap = false, bool wordBreak = true)
- :TextDataBase(text, font, fontSize, width, height, wordWrap, wordBreak), mData(nullptr)
- {
- UINT32 totalBufferSize = 0;
- generatePersistentData(text, nullptr, totalBufferSize);
- mData = (UINT8*)bs_alloc<Alloc>(totalBufferSize);
- generatePersistentData(text, (UINT8*)mData, totalBufferSize);
- }
- ~TextData()
- {
- if (mData != nullptr)
- bs_free<Alloc>(mData);
- }
- private:
- UINT8* mData;
- };
- }
|