diff options
Diffstat (limited to 'src/ftgl/FTLayout/FTSimpleLayout.cpp')
-rw-r--r-- | src/ftgl/FTLayout/FTSimpleLayout.cpp | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/src/ftgl/FTLayout/FTSimpleLayout.cpp b/src/ftgl/FTLayout/FTSimpleLayout.cpp new file mode 100644 index 0000000..b5a7beb --- /dev/null +++ b/src/ftgl/FTLayout/FTSimpleLayout.cpp @@ -0,0 +1,460 @@ +/* + * FTGL - OpenGL font library + * + * Copyright (c) 2001-2004 Henry Maddocks <ftgl@opengl.geek.nz> + * Copyright (c) 2008 Sam Hocevar <sam@zoy.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <ctype.h> +#include <wctype.h> + +#include "FTInternals.h" +#include "FTUnicode.h" + +#include "FTGlyphContainer.h" +#include "FTSimpleLayoutImpl.h" + + +// +// FTSimpleLayout +// + + +FTSimpleLayout::FTSimpleLayout() : + FTLayout(new FTSimpleLayoutImpl()) +{} + + +FTSimpleLayout::~FTSimpleLayout() +{} + + +FTBBox FTSimpleLayout::BBox(const char *string, const int len, FTPoint pos) +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->BBox(string, len, pos); +} + + +FTBBox FTSimpleLayout::BBox(const wchar_t *string, const int len, FTPoint pos) +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->BBox(string, len, pos); +} + + +void FTSimpleLayout::Render(const char *string, const int len, FTPoint pos, + int renderMode) +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->Render(string, len, pos, + renderMode); +} + + +void FTSimpleLayout::Render(const wchar_t* string, const int len, FTPoint pos, + int renderMode) +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->Render(string, len, pos, + renderMode); +} + + +void FTSimpleLayout::SetFont(FTFont *fontInit) +{ + dynamic_cast<FTSimpleLayoutImpl*>(impl)->currentFont = fontInit; +} + + +FTFont *FTSimpleLayout::GetFont() +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->currentFont; +} + + +void FTSimpleLayout::SetLineLength(const float LineLength) +{ + dynamic_cast<FTSimpleLayoutImpl*>(impl)->lineLength = LineLength; +} + + +float FTSimpleLayout::GetLineLength() const +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->lineLength; +} + + +void FTSimpleLayout::SetAlignment(const FTGL::TextAlignment Alignment) +{ + dynamic_cast<FTSimpleLayoutImpl*>(impl)->alignment = Alignment; +} + + +FTGL::TextAlignment FTSimpleLayout::GetAlignment() const +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->alignment; +} + + +void FTSimpleLayout::SetLineSpacing(const float LineSpacing) +{ + dynamic_cast<FTSimpleLayoutImpl*>(impl)->lineSpacing = LineSpacing; +} + + +float FTSimpleLayout::GetLineSpacing() const +{ + return dynamic_cast<FTSimpleLayoutImpl*>(impl)->lineSpacing; +} + + +// +// FTSimpleLayoutImpl +// + + +FTSimpleLayoutImpl::FTSimpleLayoutImpl() +{ + currentFont = NULL; + lineLength = 100.0f; + alignment = FTGL::ALIGN_LEFT; + lineSpacing = 1.0f; +} + + +template <typename T> +inline FTBBox FTSimpleLayoutImpl::BBoxI(const T* string, const int len, + FTPoint position) +{ + FTBBox tmp; + + WrapText(string, len, position, 0, &tmp); + + return tmp; +} + + +FTBBox FTSimpleLayoutImpl::BBox(const char *string, const int len, + FTPoint position) +{ + return BBoxI(string, len, position); +} + + +FTBBox FTSimpleLayoutImpl::BBox(const wchar_t *string, const int len, + FTPoint position) +{ + return BBoxI(string, len, position); +} + + +template <typename T> +inline void FTSimpleLayoutImpl::RenderI(const T *string, const int len, + FTPoint position, int renderMode) +{ + pen = FTPoint(0.0f, 0.0f); + WrapText(string, len, position, renderMode, NULL); +} + + +void FTSimpleLayoutImpl::Render(const char *string, const int len, + FTPoint position, int renderMode) +{ + RenderI(string, len, position, renderMode); +} + + +void FTSimpleLayoutImpl::Render(const wchar_t* string, const int len, + FTPoint position, int renderMode) +{ + RenderI(string, len, position, renderMode); +} + + +template <typename T> +inline void FTSimpleLayoutImpl::WrapTextI(const T *buf, const int len, + FTPoint position, int renderMode, + FTBBox *bounds) +{ + FTUnicodeStringItr<T> breakItr(buf); // points to the last break character + FTUnicodeStringItr<T> lineStart(buf); // points to the line start + float nextStart = 0.0; // total width of the current line + float breakWidth = 0.0; // width of the line up to the last word break + float currentWidth = 0.0; // width of all characters on the current line + float prevWidth; // width of all characters but the current glyph + float wordLength = 0.0; // length of the block since the last break char + int charCount = 0; // number of characters so far on the line + int breakCharCount = 0; // number of characters before the breakItr + float glyphWidth, advance; + FTBBox glyphBounds; + + // Reset the pen position + pen.Y(0); + + // If we have bounds mark them invalid + if(bounds) + { + bounds->Invalidate(); + } + + // Scan the input for all characters that need output + FTUnicodeStringItr<T> prevItr(buf); + for (FTUnicodeStringItr<T> itr(buf); *itr; prevItr = itr++, charCount++) + { + // Find the width of the current glyph + glyphBounds = currentFont->BBox(itr.getBufferFromHere(), 1); + glyphWidth = glyphBounds.Upper().Xf() - glyphBounds.Lower().Xf(); + + advance = currentFont->Advance(itr.getBufferFromHere(), 1); + prevWidth = currentWidth; + // Compute the width of all glyphs up to the end of buf[i] + currentWidth = nextStart + glyphWidth; + // Compute the position of the next glyph + nextStart += advance; + + // See if the current character is a space, a break or a regular character + if((currentWidth > lineLength) || (*itr == '\n')) + { + // A non whitespace character has exceeded the line length. Or a + // newline character has forced a line break. Output the last + // line and start a new line after the break character. + // If we have not yet found a break, break on the last character + if(breakItr == lineStart || (*itr == '\n')) + { + // Break on the previous character + breakItr = prevItr; + breakCharCount = charCount - 1; + breakWidth = prevWidth; + // None of the previous words will be carried to the next line + wordLength = 0; + // If the current character is a newline discard its advance + if(*itr == '\n') advance = 0; + } + + float remainingWidth = lineLength - breakWidth; + + // Render the current substring + FTUnicodeStringItr<T> breakChar = breakItr; + // move past the break character and don't count it on the next line either + ++breakChar; --charCount; + // If the break character is a newline do not render it + if(*breakChar == '\n') + { + ++breakChar; --charCount; + } + + OutputWrapped(lineStart.getBufferFromHere(), breakCharCount, + //breakItr.getBufferFromHere() - lineStart.getBufferFromHere(), + position, renderMode, remainingWidth, bounds); + + // Store the start of the next line + lineStart = breakChar; + // TODO: Is Height() the right value here? + pen -= FTPoint(0, currentFont->LineHeight() * lineSpacing); + // The current width is the width since the last break + nextStart = wordLength + advance; + wordLength += advance; + currentWidth = wordLength + advance; + // Reset the safe break for the next line + breakItr = lineStart; + charCount -= breakCharCount; + } + else if(iswspace(*itr)) + { + // This is the last word break position + wordLength = 0; + breakItr = itr; + breakCharCount = charCount; + + // Check to see if this is the first whitespace character in a run + if(buf == itr.getBufferFromHere() || !iswspace(*prevItr)) + { + // Record the width of the start of the block + breakWidth = currentWidth; + } + } + else + { + wordLength += advance; + } + } + + float remainingWidth = lineLength - currentWidth; + // Render any remaining text on the last line + // Disable justification for the last row + if(alignment == FTGL::ALIGN_JUSTIFY) + { + alignment = FTGL::ALIGN_LEFT; + OutputWrapped(lineStart.getBufferFromHere(), -1, position, renderMode, + remainingWidth, bounds); + alignment = FTGL::ALIGN_JUSTIFY; + } + else + { + OutputWrapped(lineStart.getBufferFromHere(), -1, position, renderMode, + remainingWidth, bounds); + } +} + + +void FTSimpleLayoutImpl::WrapText(const char *buf, const int len, + FTPoint position, int renderMode, + FTBBox *bounds) +{ + WrapTextI(buf, len, position, renderMode, bounds); +} + + +void FTSimpleLayoutImpl::WrapText(const wchar_t* buf, const int len, + FTPoint position, int renderMode, + FTBBox *bounds) +{ + WrapTextI(buf, len, position, renderMode, bounds); +} + + +template <typename T> +inline void FTSimpleLayoutImpl::OutputWrappedI(const T *buf, const int len, + FTPoint position, int renderMode, + const float remaining, + FTBBox *bounds) +{ + float distributeWidth = 0.0; + // Align the text according as specified by Alignment + switch (alignment) + { + case FTGL::ALIGN_LEFT: + pen.X(0); + break; + case FTGL::ALIGN_CENTER: + pen.X(remaining / 2); + break; + case FTGL::ALIGN_RIGHT: + pen.X(remaining); + break; + case FTGL::ALIGN_JUSTIFY: + pen.X(0); + distributeWidth = remaining; + break; + } + + // If we have bounds expand them by the line's bounds, otherwise render + // the line. + if(bounds) + { + FTBBox temp = currentFont->BBox(buf, len); + + // Add the extra space to the upper x dimension + temp = FTBBox(temp.Lower() + pen, + temp.Upper() + pen + FTPoint(distributeWidth, 0)); + + // See if this is the first area to be added to the bounds + if(bounds->IsValid()) + { + *bounds |= temp; + } + else + { + *bounds = temp; + } + } + else + { + RenderSpace(buf, len, position, renderMode, distributeWidth); + } +} + + +void FTSimpleLayoutImpl::OutputWrapped(const char *buf, const int len, + FTPoint position, int renderMode, + const float remaining, FTBBox *bounds) +{ + OutputWrappedI(buf, len, position, renderMode, remaining, bounds); +} + + +void FTSimpleLayoutImpl::OutputWrapped(const wchar_t *buf, const int len, + FTPoint position, int renderMode, + const float remaining, FTBBox *bounds) +{ + OutputWrappedI(buf, len, position, renderMode, remaining, bounds); +} + + +template <typename T> +inline void FTSimpleLayoutImpl::RenderSpaceI(const T *string, const int len, + FTPoint position, int renderMode, + const float extraSpace) +{ + float space = 0.0; + + // If there is space to distribute, count the number of spaces + if(extraSpace > 0.0) + { + int numSpaces = 0; + + // Count the number of space blocks in the input + FTUnicodeStringItr<T> prevItr(string), itr(string); + for(int i = 0; ((len < 0) && *itr) || ((len >= 0) && (i <= len)); + ++i, prevItr = itr++) + { + // If this is the end of a space block, increment the counter + if((i > 0) && !iswspace(*itr) && iswspace(*prevItr)) + { + numSpaces++; + } + } + + space = extraSpace/numSpaces; + } + + // Output all characters of the string + FTUnicodeStringItr<T> prevItr(string), itr(string); + for(int i = 0; ((len < 0) && *itr) || ((len >= 0) && (i <= len)); + ++i, prevItr = itr++) + { + // If this is the end of a space block, distribute the extra space + // inside it + if((i > 0) && !iswspace(*itr) && iswspace(*prevItr)) + { + pen += FTPoint(space, 0); + } + + pen = currentFont->Render(itr.getBufferFromHere(), 1, pen, FTPoint(), renderMode); + } +} + + +void FTSimpleLayoutImpl::RenderSpace(const char *string, const int len, + FTPoint position, int renderMode, + const float extraSpace) +{ + RenderSpaceI(string, len, position, renderMode, extraSpace); +} + + +void FTSimpleLayoutImpl::RenderSpace(const wchar_t *string, const int len, + FTPoint position, int renderMode, + const float extraSpace) +{ + RenderSpaceI(string, len, position, renderMode, extraSpace); +} + |