예제 #1
0
static void textFragmentDrawer(const char* fragment, int x, int y, int alignFlags,
    short textFlags, int initialCount)
{
    DENG2_ASSERT(fragment != 0 && fragment[0]);

    AbstractFont *font = &App_Resources().font(fr.fontNum);
    fr_state_attributes_t* sat = currentAttribs();
    dd_bool noTypein = (textFlags & DTF_NO_TYPEIN) != 0;
    dd_bool noGlitter = (sat->glitterStrength <= 0 || (textFlags & DTF_NO_GLITTER) != 0);
    dd_bool noShadow  = (sat->shadowStrength  <= 0 || (textFlags & DTF_NO_SHADOW)  != 0 ||
                         font->flags().testFlag(AbstractFont::Shadowed));
    dd_bool noCharacter = (textFlags & DTF_NO_CHARACTER) != 0;
    float glitter = (noGlitter? 0 : sat->glitterStrength), glitterMul;
    float shadow  = (noShadow ? 0 : sat->shadowStrength), shadowMul;
    float flashColor[3] = { 0, 0, 0 };
    int w, h, cx, cy, count, yoff;
    unsigned char c;
    const char* ch;

    if(alignFlags & ALIGN_RIGHT)
        x -= textFragmentWidth(fragment);
    else if(!(alignFlags & ALIGN_LEFT))
        x -= textFragmentWidth(fragment)/2;

    if(alignFlags & ALIGN_BOTTOM)
        y -= textFragmentHeight(fragment);
    else if(!(alignFlags & ALIGN_TOP))
        y -= textFragmentHeight(fragment)/2;

    if(!(noTypein && noGlitter))
    {
        flashColor[CR] = (1 + 2 * sat->rgba[CR]) / 3;
        flashColor[CG] = (1 + 2 * sat->rgba[CG]) / 3;
        flashColor[CB] = (1 + 2 * sat->rgba[CB]) / 3;
    }

    if(renderWireframe > 1)
    {
        DENG_ASSERT_IN_MAIN_THREAD();
        DENG_ASSERT_GL_CONTEXT_ACTIVE();

        LIBGUI_GL.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        LIBGUI_GL.glDisable(GL_TEXTURE_2D);
    }
    if(BitmapFont *bmapFont = font->maybeAs<BitmapFont>())
    {
        if(bmapFont->textureGLName())
        {
            GL_BindTextureUnmanaged(bmapFont->textureGLName(), gl::ClampToEdge,
                                    gl::ClampToEdge, filterUI? gl::Linear : gl::Nearest);

            LIBGUI_GL.glMatrixMode(GL_TEXTURE);
            LIBGUI_GL.glPushMatrix();
            LIBGUI_GL.glLoadIdentity();
            LIBGUI_GL.glScalef(1.f / bmapFont->textureDimensions().x,
                     1.f / bmapFont->textureDimensions().y, 1.f);
        }
    }

    for(int pass = (noShadow? 1 : 0); pass < (noCharacter && noGlitter? 1 : 2); ++pass)
    {
        count = initialCount;
        ch = fragment;
        cx = x + (pass == 0? sat->shadowOffsetX : 0);
        cy = y + (pass == 0? sat->shadowOffsetY : 0);

        for(;;)
        {
            c = *ch++;
            yoff = 0;

            glitter    = (noGlitter? 0 : sat->glitterStrength);
            glitterMul = 0;

            shadow    = (noShadow? 0 : sat->shadowStrength);
            shadowMul = (noShadow? 0 : sat->rgba[CA]);

            // Do the type-in effect?
            if(!noTypein && (pass || (!noShadow && !pass)))
            {
                int maxCount = (typeInTime > 0? typeInTime * 2 : 0);

                if(pass)
                {
                    if(!noGlitter)
                    {
                        if(count == maxCount)
                        {
                            glitterMul = 1;
                            flashColor[CR] = sat->rgba[CR];
                            flashColor[CG] = sat->rgba[CG];
                            flashColor[CB] = sat->rgba[CB];
                        }
                        else if(count + 1 == maxCount)
                        {
                            glitterMul = 0.88f;
                            flashColor[CR] = (1 + sat->rgba[CR]) / 2;
                            flashColor[CG] = (1 + sat->rgba[CG]) / 2;
                            flashColor[CB] = (1 + sat->rgba[CB]) / 2;
                        }
                        else if(count + 2 == maxCount)
                        {
                            glitterMul = 0.75f;
                            flashColor[CR] = sat->rgba[CR];
                            flashColor[CG] = sat->rgba[CG];
                            flashColor[CB] = sat->rgba[CB];
                        }
                        else if(count + 3 == maxCount)
                        {
                            glitterMul = 0.5f;
                            flashColor[CR] = sat->rgba[CR];
                            flashColor[CG] = sat->rgba[CG];
                            flashColor[CB] = sat->rgba[CB];
                        }
                        else if(count > maxCount)
                        {
                            break;
                        }
                    }
                    else if(count > maxCount)
                    {
                        break;
                    }
                }
                else
                {
                    if(count == maxCount)
                    {
                        shadowMul = 0;
                    }
                    else if(count + 1 == maxCount)
                    {
                        shadowMul *= .25f;
                    }
                    else if(count + 2 == maxCount)
                    {
                        shadowMul *= .5f;
                    }
                    else if(count + 3 == maxCount)
                    {
                        shadowMul *= .75f;
                    }
                    else if(count > maxCount)
                    {
                        break;
                    }
                }
            }
            count++;

            if(!c || c == '\n')
                break;

            w = FR_CharWidth(c);
            h = FR_CharHeight(c);

            if(' ' != c)
            {
                // A non-white-space character we have a glyph for.
                if(pass)
                {
                    if(!noCharacter)
                    {
                        // The character itself.
                        LIBGUI_GL.glColor4fv(sat->rgba);
                        drawChar(c, cx, cy + yoff, font, ALIGN_TOPLEFT, DTF_NO_EFFECTS);
                    }

                    if(!noGlitter && glitter > 0)
                    {
                        // Do something flashy.
                        Point2Raw origin;
                        Size2Raw size;
                        origin.x = cx;
                        origin.y = cy + yoff;
                        size.width  = w;
                        size.height = h;
                        LIBGUI_GL.glColor4f(flashColor[CR], flashColor[CG], flashColor[CB], glitter * glitterMul);
                        drawFlash(&origin, &size, true);
                    }
                }
                else if(!noShadow)
                {
                    Point2Raw origin;
                    Size2Raw size;
                    origin.x = cx;
                    origin.y = cy + yoff;
                    size.width  = w;
                    size.height = h;
                    LIBGUI_GL.glColor4f(1, 1, 1, shadow * shadowMul);
                    drawFlash(&origin, &size, false);
                }
            }

            cx += w + sat->tracking;
        }
    }

    // Restore previous GL-state.
    if(BitmapFont *bmapFont = font->maybeAs<BitmapFont>())
    {
        if(bmapFont->textureGLName())
        {
            LIBGUI_GL.glMatrixMode(GL_TEXTURE);
            LIBGUI_GL.glPopMatrix();
        }
    }
    if(renderWireframe > 1)
    {
        /// @todo do not assume previous state.
        LIBGUI_GL.glEnable(GL_TEXTURE_2D);
        LIBGUI_GL.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
}
예제 #2
0
std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& font, const GlyphCache& cache, const Float size, const std::string& text, const Alignment alignment) {
    /* Output data, reserve memory as when the text would be ASCII-only. In
       reality the actual vertex count will be smaller, but allocating more at
       once is better than reallocating many times later. */
    std::vector<Vertex> vertices;
    vertices.reserve(text.size()*4);

    /* Total rendered bounds, intial line position, line increment, last+1
       vertex on previous line */
    Range2D rectangle;
    Vector2 linePosition;
    const Vector2 lineAdvance = Vector2::yAxis(font.lineHeight()*size/font.size());
    std::size_t lastLineLastVertex = 0;

    /* Temp buffer so we don't allocate for each new line */
    /**
     * @todo C++1z: use std::string_view to avoid the one allocation and all
     *      the copying altogether
     */
    std::string line;
    line.reserve(text.size());

    /* Render each line separately and align it horizontally */
    std::size_t pos, prevPos = 0;
    do {
        /* Empty line, nothing to do (the rest is done below in while expression) */
        if((pos = text.find('\n', prevPos)) == prevPos) continue;

        /* Copy the line into the temp buffer */
        line.assign(text, prevPos, pos-prevPos);

        /* Layout the line */
        const auto layouter = font.layout(cache, size, line);
        const UnsignedInt vertexCount = layouter->glyphCount()*4;

        /* Verify that we don't reallocate anything. The only problem might
           arise when the layouter decides to compose one character from more
           than one glyph (i.e. accents). Will remove the assert when this
           issue arises. */
        CORRADE_INTERNAL_ASSERT(vertices.size()+vertexCount <= vertices.capacity());

        /* Bounds of rendered line */
        Range2D lineRectangle;

        /* Render all glyphs */
        Vector2 cursorPosition(linePosition);
        for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) {
            Range2D quadPosition, textureCoordinates;
            std::tie(quadPosition, textureCoordinates) = layouter->renderGlyph(i, cursorPosition, lineRectangle);

            /* 0---2
               |   |
               |   |
               |   |
               1---3 */

            vertices.insert(vertices.end(), {
                {quadPosition.topLeft(), textureCoordinates.topLeft()},
                {quadPosition.bottomLeft(), textureCoordinates.bottomLeft()},
                {quadPosition.topRight(), textureCoordinates.topRight()},
                {quadPosition.bottomRight(), textureCoordinates.bottomRight()}
            });
        }

        /** @todo What about top-down text? */

        /* Horizontally align the rendered line */
        Float alignmentOffsetX = 0.0f;
        if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentCenter)
            alignmentOffsetX = -lineRectangle.centerX();
        else if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentRight)
            alignmentOffsetX = -lineRectangle.right();

        /* Integer alignment */
        if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
            alignmentOffsetX = Math::round(alignmentOffsetX);

        /* Align positions and bounds on current line */
        lineRectangle = lineRectangle.translated(Vector2::xAxis(alignmentOffsetX));
        for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it)
            it->position.x() += alignmentOffsetX;

        /* Add final line bounds to total bounds, similarly to AbstractFont::renderGlyph() */
        if(!rectangle.size().isZero()) {
            rectangle.bottomLeft() = Math::min(rectangle.bottomLeft(), lineRectangle.bottomLeft());
            rectangle.topRight() = Math::max(rectangle.topRight(), lineRectangle.topRight());
        } else rectangle = lineRectangle;

    /* Move to next line */
    } while(prevPos = pos+1,
            linePosition -= lineAdvance,
            lastLineLastVertex = vertices.size(),
            pos != std::string::npos);

    /* Vertically align the rendered text */
    Float alignmentOffsetY = 0.0f;
    if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentMiddle)
        alignmentOffsetY = -rectangle.centerY();
    else if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentTop)
        alignmentOffsetY = -rectangle.top();

    /* Integer alignment */
    if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
        alignmentOffsetY = Math::round(alignmentOffsetY);

    /* Align positions and bounds */
    rectangle = rectangle.translated(Vector2::yAxis(alignmentOffsetY));
    for(auto& v: vertices) v.position.y() += alignmentOffsetY;

    return std::make_tuple(std::move(vertices), rectangle);
}
예제 #3
0
std::vector<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter::doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const {
    Utility::Configuration configuration;

    configuration.setValue("version", 1);
    configuration.setValue("image", Utility::Directory::filename(filename) + ".tga");
    configuration.setValue("originalImageSize", cache.textureSize());
    configuration.setValue("padding", cache.padding());
    configuration.setValue("fontSize", font.size());
    configuration.setValue("ascent", font.ascent());
    configuration.setValue("descent", font.descent());
    configuration.setValue("lineHeight", font.lineHeight());

    /* Compress glyph IDs so the glyphs are in consecutive array, glyph 0
       should stay at position 0 */
    std::unordered_map<UnsignedInt, UnsignedInt> glyphIdMap;
    glyphIdMap.reserve(cache.glyphCount());
    glyphIdMap.emplace(0, 0);
    for(const std::pair<UnsignedInt, std::pair<Vector2i, Range2Di>>& glyph: cache)
        glyphIdMap.emplace(glyph.first, glyphIdMap.size());

    /** @todo Save only glyphs contained in @p characters */

    /* Inverse map from new glyph IDs to old ones */
    std::vector<UnsignedInt> inverseGlyphIdMap(glyphIdMap.size());
    for(const std::pair<UnsignedInt, UnsignedInt>& map: glyphIdMap)
        inverseGlyphIdMap[map.second] = map.first;

    /* Character->glyph map, map glyph IDs to new ones */
    for(const char32_t c: characters) {
        Utility::ConfigurationGroup* group = configuration.addGroup("char");
        const UnsignedInt glyphId = font.glyphId(c);
        group->setValue("unicode", c);

        /* Map old glyph ID to new, if not found, map to glyph 0 */
        auto found = glyphIdMap.find(glyphId);
        group->setValue("glyph", found == glyphIdMap.end() ? 0 : glyphIdMap.at(glyphId));
    }

    /* Save glyph properties in order which preserves their IDs, remove padding
       from the values so they aren't added twice when using the font later */
    /** @todo Some better way to handle this padding stuff */
    for(UnsignedInt oldGlyphId: inverseGlyphIdMap) {
        std::pair<Vector2i, Range2Di> glyph = cache[oldGlyphId];
        Utility::ConfigurationGroup* group = configuration.addGroup("glyph");
        group->setValue("advance", font.glyphAdvance(oldGlyphId));
        group->setValue("position", glyph.first+cache.padding());
        group->setValue("rectangle", glyph.second.padded(-cache.padding()));
    }

    std::ostringstream confOut;
    configuration.save(confOut);
    std::string confStr = confOut.str();
    Containers::Array<char> confData{confStr.size()};
    std::copy(confStr.begin(), confStr.end(), confData.begin());

    /* Save cache image */
    Image2D image(PixelFormat::Red, PixelType::UnsignedByte);
    cache.texture().image(0, image);
    auto tgaData = Trade::TgaImageConverter().exportToData(image);

    std::vector<std::pair<std::string, Containers::Array<char>>> out;
    out.emplace_back(filename + ".conf", std::move(confData));
    out.emplace_back(filename + ".tga", std::move(tgaData));
    return out;
}