Fonts rendering

I modified my JUCE without touching the drawImage methods. What I did is basically adding a buffered Image (called bitmap3) to the FontGlyphAlphaMap class. Then I added an int parameter that specifies the gamma curve to use to Graphics::drawText() and this gamma curve is then given to the GlyphArrangement::draw() and subsequently PositionedGlyph::draw() methods. At the moment, my Graphics::drawText() even chooses itself the gamma curve to use, depending on the brightness of the used text colour.
I used 15 gamma curves. Here’s the main code of FontGlyphAlphaMap:

I know that this code will not work if more FontGlyphAlphaMap instances are used concurrently (because they will try to access bitmap3 concurrently).

[code]#include “…/…/…/…/juce_core/basics/juce_StandardHeader.h”

BEGIN_JUCE_NAMESPACE

#include “juce_GlyphArrangement.h”
#include “…/contexts/juce_LowLevelGraphicsSoftwareRenderer.h”
#include “…/imaging/juce_Image.h”
#include “…/…/…/application/juce_DeletedAtShutdown.h”

#define SHOULD_WRAP(x, wrapwidth) (((x) - 0.0001f) >= (wrapwidth))

//==============================================================================
// GAMMA CURVES implementation

#define GAMMARANGE 0.5

uint8 gammaCurves[NUMGAMMACURVES][256];
bool gammaCurvesHaveBeenGenerated=false;
Image* bitmap3=0;

class Bitmap3Gen
{
public:
Bitmap3Gen()
{
bitmap3=new Image(Image::SingleChannel,30,30,true);
}
~Bitmap3Gen()
{
deleteAndZero(bitmap3);
}
} Bitmap3Gen_;

void generateGammaCurvesIfNeeded()
{
if (gammaCurvesHaveBeenGenerated) return;
gammaCurvesHaveBeenGenerated=true;

for (int i=0; i<NUMGAMMACURVES; i++)
{
	float gamma=float(i)/float(NUMGAMMACURVES-1);
	gamma-=0.5f;
	gamma*=2.0f;
	gamma=powf(float(GAMMARANGE),gamma);

	for (int j=0; j<256; j++)
	{
		float val=float(j)/255.0f;
		val=powf(val,gamma);
		val*=255.0f;
		gammaCurves[i][j]=(uint8)val;
	}
}

}
//==============================================================================

class FontGlyphAlphaMap
{
public:
//==============================================================================
bool draw (const Graphics& g, float x, const float y, const int gammaCurve) const throw()
{
int i,j;

    if (bitmap1 == 0) return false;

	// choose bitmap1 or bitmap2 as source, depending on font size and x position
	x += xOrigin;
    const float xFloor = floorf (x);
    const int intX = (int) xFloor;
	Image *source=((x - xFloor) >= 0.5f && bitmap2 != 0) ? bitmap2 : bitmap1;
	if (bitmap2==0) return false;

	int h=source->getHeight();
	int w=source->getWidth();

	// processing only needed for small fonts and if gammaCurve used
	if (w>=30 || h>=30 || gammaCurve==NOGAMMACORRECTION)
	{
		 g.drawImageAt (source, intX, (int) floorf (y + yOrigin), true);
		 return true;
	}

	// this is a small font (<30 pixels in width or height)
	
	uint8 *gc=gammaCurves[gammaCurve];

	int lineStride, pixelStride;
	uint8 *p=source->lockPixelDataReadWrite(0,0,source->getWidth(),source->getHeight(),lineStride,pixelStride);		
	uint8 *p_=p;
	int lineStride2, pixelStride2;
	uint8 *p2=bitmap3->lockPixelDataReadWrite(0,0,bitmap3->getWidth(),bitmap3->getHeight(),lineStride2,pixelStride2);
	uint8 *p2_=p2;

	for (i=0; i<h; i++)
	{
		for (j=0; j<w; j++) p2[j]=gc[p[j]];
		p+=lineStride;
		p2+=lineStride2;
	}

    g.drawImageAt (bitmap3,
                   intX, (int) floorf (y + yOrigin), true);
	
	// clear bitmap3 again
	p2=p2_;
	for (i=0; i<h; i++)
	{
		for (j=0; j<w; j++) p2[j]=0;
		p2+=lineStride2;
	}

	source->releasePixelDataReadWrite(p_);
	bitmap3->releasePixelDataReadWrite(p2_);
    return true;
}


juce_UseDebuggingNewOperator[/code]

what about the difference in font sizes between juce and other apps?

do you have any idea?

No idea about that,…

While we’re at it, I think that the GlyphCache implementation could be optimized. In fact, when I set the oversampling factor of the EdgeTable to 256, then my application gets reeeeaallly slow, consuming much CPU when some text is displayed. It’s normal that it gets slow the first time the Glyphs have to be generated, that’s normal. But not all the time? I mean, once the Glyphs have been generated and cached, there shouldn’t be much CPU usage anymore.

I am not sure, but I suppose this slowdown is due to the GlyphCache that throws out Glyphs that have to be generated again? I think one could at least hold 1000 glyphs in the cache. If each of those glyphs would be at average in size 40x40, then this would only take 1.6MB, so no memory problems with big Gylph caches.

Anyway the actual JUCE implementation does a simple 1-dimensional table seek of Glyphs following their size,typeface, etc… It could be possible to at least reduce the seeking by creating more Arrays, so it would first seek the Typeface (that would be an array of not many elements), then seek the height in another Array that is related only to this Typeface (again, this would for sure not be many elements), and so forth… This would speed up things if the cache would become bigger.

Let’s use something created to solve this problem aka std::map in that case…

Jules, any word on the font size issue ?

Thanks

Could the font size thing just be because juce isn’t using the scale factor that windows uses for rendering fonts?

Have no idea. What about the GlyphCache optimization?

Guys,

Could you avoid using the same thread for two different topics ?

Well you’re talking about a performance optimisation, but at the expense of memory… It’s not necessarily optimal to do that for every app. I think what needs to happen is that the 256-level rendering needs to be optimised so that it’s not slow, rather than messing with the glyph cache, which I’d say is about right.

I think I have proven with my example of 1000 40x40 glyphs consuming only 1.6MB that this approach wouldn’t be “at the expense of memory”, especially with computer lots of hundreds of MB RAM :slight_smile:

I’m old enough to remember the days when we dreamed of having 1.6MB…

I’m still digging the 16k of RAM in my TRS-80… :slight_smile: and now for something completely different:

FIRST YORKSHIREMAN:
You were lucky. We lived for three months in a paper bag in a septic tank. We used to have to get up at six in the morning, clean the paper bag, eat a crust of stale bread, go to work down t’ mill, fourteen hours a day, week-in week-out, for sixpence a week, and when we got home our Dad would thrash us to sleep wi’ his belt.
SECOND YORKSHIREMAN:
Luxury. We used to have to get out of the lake at six o’clock in the morning, clean the lake, eat a handful of ‘ot gravel, work twenty hour day at mill for tuppence a month, come home, and Dad would thrash us to sleep with a broken bottle, if we were lucky!
THIRD YORKSHIREMAN:
Well, of course, we had it tough. We used to ‘ave to get up out of shoebox at twelve o’clock at night and lick road clean wit’ tongue. We had two bits of cold gravel, worked twenty-four hours a day at mill for sixpence every four years, and when we got home our Dad would slice us in two wit’ bread knife.
FOURTH YORKSHIREMAN:
Right. I had to get up in the morning at ten o’clock at night half an hour before I went to bed, drink a cup of sulphuric acid, work twenty-nine hours a day down mill, and pay mill owner for permission to come to work, and when we got home, our Dad and our mother would kill us and dance about on our graves singing Hallelujah.
FIRST YORKSHIREMAN:
And you try and tell the young people of today that … they won’t believe you.

So, can you sum up a little bit about the current state here?

If I understand correctly, the current Juce rendering either use a Glyphs cache or a graph to draw text.
In case of Glyph caching, Zamrate proposed a solution (doing ???) which is good/not good because ???
In case of graph based text rendering, there is no solution because ???

I’m a bit lost with all those answers here.

Well, zamrate suggested applying a gamma correction to the glyph cache. That’s not ideal because it means you can’t use different gamma for different colours. The “proper” solution is for me to add a gamma control to the graphics context so that this kind of thing is moved away from the low-level font rendering.

Does gamma should apply on every channel of a pixel (like R, G, B) or only to the luminance part of the pixel (Y in YUV) ?
Because in case of the former, it means that it’ll take 3 LUT look up for every rendered pixel, while the latter is independant of the color, hence can be precomputed in the cached images in the glyph cache, or maybe I’m missing something ?

This is the alpha channel that we’re talking about - all the colour info comes later when you try to render the glyph.

Yes, that was the very first thing I suggested, but the code I provided does not apply the alpha to the glyph cache, but afterwards. It works pretty alright for me and basically exceeds to the same as having gamma control on the graphics context.

Sorry for bumping this vintage thread. But of all the threads regarding font-rendering, it was the only one that seems to offer a working solution for my specific use-case:

We also have the problem that most people testing our plugin complain about readbility of the fonts at smaller sizes. 95% of our font usage is light text on a dark background, which is exactly the combination where JUCE’s font aliasing doesn’t work as well as it is supposed to.

I was about to try zamrate’s code from this post but I can’t find any AlphaBitmapRenderer in the current git version of juce, and don’t understand the glyph-rendering/caching code well enough to figure out what it was replaced with.

Does anyone know to apply that idea (a gamma-correction that is specifically optimized for bright texts) on a current juce version? I know this will be a tradeoff and will make dark texts on white background look worse, but in my case it’s probably worth it.

The other option we have is to ditch the custom typeface for the smaller sizes, but it’ll be hard to find a replacement that goes along well with the other parts.

I’d like to see an option for Freetype as a rendering back-end for Juce, since it correctly handles hinting (the patents for which have expired, FYI).

[quote=“steffen”]
The other option we have is to ditch the custom typeface for the smaller sizes, but it’ll be hard to find a replacement that goes along well with the other parts.[/quote]

Are custom typefaces even discernible at the small font sizes you require? If not you can browse through the hundreds of free bitmap fonts here http://www.dafont.com/bitmap.php