Edit

kc3-lang/ftgl/src/FTLayout/FTSimpleLayout.cpp

Branch :

  • Show log

    Commit

  • Author : dtremenak
    Date : 2008-05-06 06:38:37
    Hash : eb45c699
    Message : VC build fixes from bzflag revs 17848-17852. * size_t consistency * avoid coercing from int to bool * make casts from double to float explicit rather than implicit, mostly by way of a few new getter functions in FTPoint, or avoid if possible.

  • src/FTLayout/FTSimpleLayout.cpp
  • /*
     * FTGL - OpenGL font library
     *
     * Copyright (c) 2001-2004 Henry Maddocks <ftgl@opengl.geek.nz>
     *
     * 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 "FTInternals.h"
    
    #include "FTGlyphContainer.h"
    #include "FTSimpleLayoutImpl.h"
    
    
    //
    //  FTSimpleLayout
    //
    
    
    FTSimpleLayout::FTSimpleLayout() :
        FTLayout(new FTSimpleLayoutImpl())
    {}
    
    
    FTSimpleLayout::~FTSimpleLayout()
    {}
    
    
    void FTSimpleLayout::BBox(const char *string, float& llx, float& lly,
                                  float& llz, float& urx, float& ury, float& urz)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->BBox(string, llx, lly, llz,
                                                      urx, ury, urz);
    }
    
    
    void FTSimpleLayout::BBox(const wchar_t *string, float& llx, float& lly,
                                  float& llz, float& urx, float& ury, float& urz)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->BBox(string, llx, lly, llz,
                                                      urx, ury, urz);
    }
    
    
    void FTSimpleLayout::Render(const char *string)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->Render(string);
    }
    
    
    void FTSimpleLayout::Render(const wchar_t* string)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->Render(string);
    }
    
    
    void FTSimpleLayout::Render(const char *string, int renderMode)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->Render(string, renderMode);
    }
    
    
    void FTSimpleLayout::Render(const wchar_t* string, int renderMode)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->Render(string, 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;
    }
    
    
    void FTSimpleLayout::RenderSpace(const char *string, const float ExtraSpace)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->RenderSpace(string, ExtraSpace);
    }
    
    
    void FTSimpleLayout::RenderSpace(const wchar_t *string, const float ExtraSpace)
    {
        dynamic_cast<FTSimpleLayoutImpl*>(impl)->RenderSpace(string, ExtraSpace);
    }
    
    
    //
    //  FTSimpleLayoutImpl
    //
    
    
    FTSimpleLayoutImpl::FTSimpleLayoutImpl()
    {
        currentFont = NULL;
        lineLength = 100.0f;
        alignment = FTGL::ALIGN_LEFT;
        lineSpacing = 1.0f;
    }
    
    
    template <typename T>
    inline void FTSimpleLayoutImpl::BBoxI(const T* string,
                                          float& llx, float& lly, float& llz,
                                          float& urx, float& ury, float& urz)
    {
        FTBBox bounds;
    
        WrapText(string, 0, &bounds);
        llx = bounds.Lower().Xf(); lly = bounds.Lower().Yf(); llz = bounds.Lower().Zf();
        urx = bounds.Upper().Xf(); ury = bounds.Upper().Yf(); urz = bounds.Upper().Zf();
    }
    
    
    void FTSimpleLayoutImpl::BBox(const char *string, float& llx, float& lly,
                                  float& llz, float& urx, float& ury, float& urz)
    {
        BBoxI(string, llx, lly, llz, urx, ury, urz);
    }
    
    
    void FTSimpleLayoutImpl::BBox(const wchar_t *string, float& llx, float& lly,
                                  float& llz, float& urx, float& ury, float& urz)
    {
        BBoxI(string, llx, lly, llz, urx, ury, urz);
    }
    
    
    template <typename T>
    inline void FTSimpleLayoutImpl::RenderI(const T *string, int renderMode)
    {
        pen = FTPoint(0.0f, 0.0f);
        WrapText(string, renderMode, NULL);
    }
    
    
    void FTSimpleLayoutImpl::Render(const char *string)
    {
        RenderI(string, FTGL::RENDER_FRONT | FTGL::RENDER_BACK | FTGL::RENDER_SIDE);
    }
    
    
    void FTSimpleLayoutImpl::Render(const wchar_t* string)
    {
        RenderI(string, FTGL::RENDER_FRONT | FTGL::RENDER_BACK | FTGL::RENDER_SIDE);
    }
    
    
    void FTSimpleLayoutImpl::Render(const char *string, int renderMode)
    {
        RenderI(string, renderMode);
    }
    
    
    void FTSimpleLayoutImpl::Render(const wchar_t* string, int renderMode)
    {
        RenderI(string, renderMode);
    }
    
    
    void FTSimpleLayoutImpl::RenderSpace(const char *string,
                                         const float ExtraSpace)
    {
        pen.X(0);
        pen.Y(0);
        RenderSpace(string, 0, -1, 0, ExtraSpace);
    }
    
    
    void FTSimpleLayoutImpl::RenderSpace(const wchar_t *string,
                                         const float ExtraSpace)
    {
        pen.X(0);
        pen.Y(0);
        RenderSpace(string, 0, -1, 0, ExtraSpace);
    }
    
    
    template <typename T>
    inline void FTSimpleLayoutImpl::WrapTextI(const T *buf, int renderMode,
                                              FTBBox *bounds)
    {
        int breakIdx = 0;          // index of the last break character
        int lineStart = 0;         // character index of 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
        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
        for(int i = 0; buf[i]; i++)
        {
            // Find the width of the current glyph
            CheckGlyph(currentFont, buf[i]);
            glyphBounds = GetGlyphs(currentFont)->BBox(buf[i]);
            glyphWidth = glyphBounds.Upper().Xf() - glyphBounds.Lower().Xf();
    
            advance = GetGlyphs(currentFont)->Advance(buf[i], buf[i + 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 buf[i] is a space, a break or a regular character
            if((currentWidth > lineLength) || (buf[i] == '\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(!breakIdx || (buf[i] == '\n'))
                {
                    // Break on the previous character
                    breakIdx = i - 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(buf[i] == '\n') advance = 0;
                }
    
                float remainingWidth = lineLength - breakWidth;
    
                // Render the current substring
                // If the break character is a newline do not render it
                if(buf[breakIdx + 1] == '\n')
                {
                    breakIdx++;
                    OutputWrapped(buf, lineStart, breakIdx - 1,
                                  remainingWidth, bounds, renderMode);
                }
                else
                {
                    OutputWrapped(buf, lineStart, breakIdx, remainingWidth, bounds, renderMode);
                }
    
                // Store the start of the next line
                lineStart = breakIdx + 1;
                // TODO: Is Height() the right value here?
                pen -= FTPoint(0, GetCharSize(currentFont).Height() * 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
                breakIdx = 0;
            }
            else if(isspace(buf[i]))
            {
                // This is the last word break position
                wordLength = 0;
                breakIdx = i;
    
                // Check to see if this is the first whitespace character in a run
                if(!i || !isspace(buf[i - 1]))
                {
                    // 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(buf, lineStart, -1, remainingWidth, bounds, renderMode);
            alignment = FTGL::ALIGN_JUSTIFY;
        }
        else
        {
            OutputWrapped(buf, lineStart, -1, remainingWidth, bounds, renderMode);
        }
    }
    
    
    void FTSimpleLayoutImpl::WrapText(const char *buf, int renderMode,
                                      FTBBox *bounds)
    {
        WrapTextI(buf, renderMode, bounds);
    }
    
    
    void FTSimpleLayoutImpl::WrapText(const wchar_t* buf, int renderMode,
                                      FTBBox *bounds)
    {
        WrapTextI(buf, renderMode, bounds);
    }
    
    
    template <typename T>
    inline void FTSimpleLayoutImpl::OutputWrappedI(const T *buf, const int start,
                                                   const int end,
                                                   const float remaining,
                                                   FTBBox *bounds, int renderMode)
    {
        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)
        {
            float llx, lly, llz, urx, ury, urz;
            currentFont->BBox(buf, start, end, llx, lly, llz, urx, ury, urz);
    
            // Add the extra space to the upper x dimension
            urx += distributeWidth;
            // TODO: It's a little silly to convert from a FTBBox to floats and
            // back again, but I don't want to implement yet another method for
            // finding the bounding box as a BBox.
            FTBBox temp(llx, lly, llz, urx, ury, urz);
            temp.Move(FTPoint(pen.X(), pen.Y(), 0.0f));
    
            // See if this is the first area to be added to the bounds
            if(!bounds->IsValid())
            {
                *bounds = temp;
            }
            else
            {
                *bounds += temp;
            }
        }
        else
        {
            RenderSpace(buf, start, end, renderMode, distributeWidth);
        }
    }
    
    
    void FTSimpleLayoutImpl::OutputWrapped(const char *buf, const int start,
                                           const int end, const float remaining,
                                           FTBBox *bounds, int renderMode)
    {
        OutputWrappedI(buf, start, end, remaining, bounds, renderMode);
    }
    
    
    void FTSimpleLayoutImpl::OutputWrapped(const wchar_t *buf, const int start,
                                           const int end, const float remaining,
                                           FTBBox *bounds, int renderMode)
    {
        OutputWrappedI(buf, start, end, remaining, bounds, renderMode);
    }
    
    
    template <typename T>
    inline void FTSimpleLayoutImpl::RenderSpaceI(const T *string, const int start,
                                                 const int end, 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
            for(int i = start; ((end < 0) && string[i])
                                  || ((end >= 0) && (i <= end)); i++)
            {
                // If this is the end of a space block, increment the counter
                if((i > start) && !isspace(string[i]) && isspace(string[i - 1]))
                {
                    numSpaces++;
                }
            }
    
            space = ExtraSpace/numSpaces;
        }
    
        // Output all characters of the string
        for(int i = start; ((end < 0) && string[i])
                              || ((end >= 0) && (i <= end)); i++)
        {
            // If this is the end of a space block, distribute the extra space
            // inside it
            if((i > start) && !isspace(string[i]) && isspace(string[i - 1]))
            {
                pen += FTPoint(space, 0);
            }
    
            DoRender(currentFont, string[i], string[i + 1], renderMode);
        }
    }
    
    
    void FTSimpleLayoutImpl::RenderSpace(const char *string, const int start,
                                         const int end, int renderMode,
                                         const float ExtraSpace)
    {
        RenderSpaceI(string, start, end, renderMode, ExtraSpace);
    }
    
    
    void FTSimpleLayoutImpl::RenderSpace(const wchar_t *string, const int start,
                                         const int end, int renderMode,
                                         const float ExtraSpace)
    {
        RenderSpaceI(string, start, end, renderMode, ExtraSpace);
    }