Exchanging the font & software render

I would just like to weigh in…

…openGL and other renderering engines is great, but I hope that we always keep the built-in software rendering engine (the awesome one that Jules wrote) in tip-top shape, so we have something that achieves these goals:

  1. Is available in source code form and can be modified
  2. Produces a consistent result regardless of platform
  3. Is immune to proprietary changes in third party libraries (glares at Apple)

Don’t worry, the software renderer isn’t going away! On some older systems it’s probably going to be faster than openGL anyway - will be interesting to see how the benchmarks perform.

Oh, I’m not worried that its going away, only that improvements to it will be abandoned in favor of latest the new shiny toy.

Kind of like a faithful girlfriend who gets dumped for a younger model.

@Vinn: But at least you’ll be able to optimize it yourself now when it becomes exchangeable! For instance, for me only a couple of functions need to be faster or better. I don’t care about the rest.

FYI I’ve added a LookAndFeel::createGraphicsContext method now, which will let you replace the renderer with your own class.

Thanks!

I’ve got a problem: like I said, I wanted to exchange the font rendering. But the render context only has a function drawGlyph() that gets the number of the glyph (In the Glyph Cache I suppose?). How can I transfer this number into the character it represents?
Also, how do I retrieve the font?

In short: the glyph caching is done by my own font rendering system, which implements vertical hinting btw. I just want font name + font size + bold/italic/etc… + character, that’s all. I do not need JUCE’s glyph caching and it should be turned off since there’s no need for it anymore after I added my changes.

The character has already been converted to a glyph index by the layout engine - you certainly don’t want to convert it back again! You can pass that number to a Typeface object to get the glyph’s shape.

If you’re doing a custom renderer, then presumably it has stored the current font somewhere in there.

Ok, let me ask the question differently:

After the call to Graphics::drawText(), JUCE should not be involved anymore, I want my rendering system to do the rest. How do I achieve that? The whole system of glyph shapes etc… is already implemented using freetype in my code. I don’t need JUCE to do this.

Well then you’d also need to write your own Typeface class, because that’s what does the glyph layout.

I just noticed that there’s also a setFont() function in the LowLevelGraphicsContext class. Well that’s already very good, because then I know what font to use.
Still, the only problem that remains, is that drawGlyph() has a glyph number as parameter. I can’t do anything with that information. I need a character number, best would be an uint32 for UTF32 encoding. Is there any way to achieve this?

I think you might have got completely the wrong idea.

If you’re trying to do custom layout and glyph rendering, why not just do what TheVinn did a year or so ago, and write a custom typeface class? He didn’t need to touch the graphics context at all, and I already added some hooks to do what he needed.

In fact, from the sound of it, you might actually just be re-creating exactly what TheVinn already did!

Ok I think I understand how it works now that I found the setFont() function. Thanks! No I’m not recreating what he is, I’m making a LCD optimized font renderer.

In that case, surely you just need to modify the edgetable rendering to be LCD-optimised, and not worry about fonts or anything?

Sadly not, because the whole point is the vertical hinting of the font.

You do realize that in my code, there is an #ifdef that, while turned off by default, not only creates the outlines but also provides Juce with the bitmap? You could just override that routine and turn the macro on.

look into

#define TYPEFACE_BITMAP_RENDERING 1

in the .cpp of my post:

http://rawmaterialsoftware.com/viewtopic.php?f=6&t=6393

LCD-optimised glyphs would also need to be represented not as just one alpha bitmap, but 3 alpha bitmaps, one for each channel (RGB).

LCD-optimised glyphs would also need to be represented not as just one alpha bitmap, but 3 alpha bitmaps, one for each channel (RGB).

No, not at all! A few cunning tweaks in the code that renders an edgetable should be all that’s required. The edgetable already contains a high horizontal pixel resolution, so is an ideal source for sub-pixel rendering. There’d be no need to change anything else in the font code to make it work, and it’d also mean that all paths would get rendered at sub-pixel resolution for free too.

OK, interesting, and what about the vertical hinting? Basically, what I implemented (and want to add to JUCE), is some font rendering that works after the scheme described in http://www.antigrain.com/research/font_rasterization/#toc0012

and looks like this:

IMHO the best font rendering I saw. Hinting is only applied vertically (and only on small fonts which look bad without vertical hinting), the rest is merely done via LCD optimized rendering, for which the code is actually very short:

[code]//=====================================================lcd_distribution_lut
class lcd_distribution_lut
{
public:
lcd_distribution_lut(double prim, double second, double tert)
{
double norm = 1.0 / (prim + second2 + tert2);
prim *= norm;
second *= norm;
tert *= norm;
for(unsigned i = 0; i < 256; i++)
{
m_primary[i] = (unsigned char)floor(prim * i);
m_secondary[i] = (unsigned char)floor(second * i);
m_tertiary[i] = (unsigned char)floor(tert * i);
}
}

    unsigned primary(unsigned v)   const { return m_primary[v];   }
    unsigned secondary(unsigned v) const { return m_secondary[v]; }
    unsigned tertiary(unsigned v)  const { return m_tertiary[v];  }

private:
    unsigned char m_primary[256];
    unsigned char m_secondary[256];
    unsigned char m_tertiary[256];
};





//========================================================pixfmt_rgb24_lcd
class pixfmt_rgb24_lcd
{
public:
    typedef rgba8 color_type;
    typedef rendering_buffer::row_data row_data;
    typedef color_type::value_type value_type;
    typedef color_type::calc_type calc_type;

    //--------------------------------------------------------------------
    pixfmt_rgb24_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut)
        : m_rbuf(&rb),
          m_lut(&lut)
    {
    }

    //--------------------------------------------------------------------
    unsigned width()  const { return m_rbuf->width() * 3;  }
    unsigned height() const { return m_rbuf->height(); }


    //--------------------------------------------------------------------
    void blend_hline(int x, int y,
                     unsigned len, 
                     const color_type& c,
                     int8u cover)
    {
        int8u* p = m_rbuf->row_ptr(y) + x + x + x;
        int alpha = int(cover) * c.a;
        do
        {
            p[0] = (int8u)((((c.r - p[0]) * alpha) + (p[0] << 16)) >> 16);
            p[1] = (int8u)((((c.g - p[1]) * alpha) + (p[1] << 16)) >> 16);
            p[2] = (int8u)((((c.b - p[2]) * alpha) + (p[2] << 16)) >> 16);
            p += 3;
        }
        while(--len);
    }


    //--------------------------------------------------------------------
    void blend_solid_hspan(int x, int y,
                           unsigned len, 
                           const color_type& c,
                           const int8u* covers)
    {
        int8u c3[2048*3];
        memset(c3, 0, len+4);

        int i;
        for(i = 0; i < int(len); i++)
        {
            c3[i+0] += m_lut->tertiary(covers[i]);
            c3[i+1] += m_lut->secondary(covers[i]);
            c3[i+2] += m_lut->primary(covers[i]);
            c3[i+3] += m_lut->secondary(covers[i]);
            c3[i+4] += m_lut->tertiary(covers[i]);
        }

        x -= 2;
        len += 4;

        if(x < 0)
        {
            len -= x;
            x = 0;
        }

        covers = c3;
        i = x % 3;

        int8u rgb[3] = { c.r, c.g, c.b };
        int8u* p = m_rbuf->row_ptr(y) + x;

        do 
        {
            int alpha = int(*covers++) * c.a;
            if(alpha)
            {
                if(alpha == 255*255)
                {
                    *p = (int8u)rgb[i];
                }
                else
                {
                    *p = (int8u)((((rgb[i] - *p) * alpha) + (*p << 16)) >> 16);
                }
            }
            ++p;
            ++i;
            if(i >= 3) i = 0;
        }
        while(--len);
    }


private:
    rendering_buffer* m_rbuf;
    const lcd_distribution_lut* m_lut;
};[/code]