Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw text in OpenGL on Mac OS with Retina display

I'm using OpenGL to draw in Mac OS. When my code runs on Retina display everything works fine except text drawing. Under Retinal display the text is twice as big as it should be. It happens because the font size is in points and each point is 2 pixels under Retina, but OpenGL is pixel based.

Here is the correct text drawing under standard display:
Correct text drawing under standard display

Here is the incorrect text drawing under Retina display:
Incorrect text drawing under Retina display

Here is how I normaly draw strings. Since OpenGL does not have text drawing functions, in order to draw text I do the following:

  1. Get the font:

    NSFontManager fontManager = [NSFontManager sharedFontManager];
    NSString
    font_name = [NSString stringWithCString: "Helvetica" encoding: NSMacOSRomanStringEncoding];
    font = [fontManager fontWithFamily: font_name traits:fontStyle weight:5 size:9];
    attribs = [[NSMutableDictionary dictionaryWithCapacity: 3] retain];
    [attribs setObject:font forKey:NSFontAttributeName];

  2. Create and measure the string:

    NSString* aString = [NSString stringWithCString: "blah blah" encoding: NSMacOSRomanStringEncoding];
    NSSize frameSize = [aString sizeWithAttributes: m_attribs];

  3. Allocate NSImage with the size:

    NSImage* image = [[NSImage alloc] initWithSize:frameSize];
    [image lockFocus];

  4. Draw the string into the image:

    [aString drawAtPoint:NSMakePoint (0, 0) withAttributes:m_attribs];

  5. Get the bits:

    NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, frameSize.width, frameSize.height)];
    [image unlockFocus];

  6. Create OpenGL texture:

    GLuint texture = 0;
    glGenTextures(1, &texture);
    glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, GLsizei(frameSize.width), GLsizei(frameSize.height), 0, GL_RGBA, [bitmap bitmapData]);

  7. Draw the texture:

    glBindTexture ….
    other OpenGL drawing code

My question is how to get NSString to draw in pixel resolution not in points.

I tried the following:

  1. Draw at half the point size: 4.5 instead of 9. This gives me the correct size but the text is drawn blurry.
  2. Draw at point size and shrink the texture to half the size in OpenGL, again this does not give good looking results:
    Texture shrink
like image 252
Periodic Maintenance Avatar asked Nov 13 '22 20:11

Periodic Maintenance


1 Answers

OpenGLs coordinate system is in fact point based not pixel based, but you are the one who decides what those points are. A context defines the coordinate system in 2D by the glOrtho function (or you could construct an ortho matrix by hand) which sets up the min and max x,y coordinates on the screen. For example an orthographic projection could be setup so that 0 was on the left of the screen and 100 was on the right regardless of the size of the framebuffers you are rendering into.

The font texture appears to be created just fine. The problem is that you are rendering it on geometry twice as large as it needs to be. In OpenGL, texture size does not effect the size of the object rendered on your screen. The size on screen is defined by the geometry passed to glDrawArrays or glBegin etc not the texture.

I think the problem is that you are using the pixel size of the font texture to define the quad size used to render on screen. This would put your problem in the "other OpenGL Drawing code" section. To fix that you could apply some sort of scale factor to the drawing. In retina mode the scale factor to 0.5 and for normal screens it would be 1.0 (UIKit uses a similar idea to render UIView content)

The quad calculation could look something like this:

quad.width = texture.width * scaleFactor 
quad.height = texture.height * scaleFactor

Another option would be to separate the quad rendering size completely from the texture. If you had a function or a class for drawing text it could have a font size parameter which it would use as the actual quad size instead of using the texture size.

like image 95
Justin Meiners Avatar answered Nov 15 '22 11:11

Justin Meiners