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

I use OpenGL for drawing on Mac OS. When my code works on the Retina display, everything works fine except for the text drawing. When Retinal is displayed, the text is twice as large as it should be. This is because the font size is in points, and each point has 2 pixels in Retina, but OpenGL is pixel based.

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

The following is the wrong text on the Retina screen:
Incorrect text drawing under Retina display

This is how I draw lines normally. Since OpenGL does not have functions for drawing text, I do the following to draw text:

  • Get the font:

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

  • Create and measure the line:

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

  • Highlight NSImage with size:

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

  • Draw a line in the image:

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

  • Get a bit:

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

    [image unlockFocus];

  • Create OpenGL Texture:

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

  • Draw a texture:

    glBindTexture ....
    different OpenGL drawing code

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

I tried the following:

  • Draw half the size of the point: 4.5 instead of 9. This gives me the correct size, but the text is blurry.
  • Draw a point size and reduce the texture to half the size in OpenGL, again this will not give good results:
    Texture shrink
+4
source share
3 answers

The OpenGLs coordinate system is not really based on pixels, but on who decides what the points are. The context defines the coordinate system in 2D with the glOrtho function (or you can build the ortho matrix manually), which sets the min and max x, y coordinates on the screen. For example, the orthographic projection can be adjusted so that 0 is on the left of the screen, and 100 is on the right, regardless of the size of the framebuffs rendering.

The font texture is created just fine. The problem is that you are rendering it in geometry twice as much as you need. In OpenGL, the size of the texture does not affect the size of the object displayed on your screen. The size on the screen is determined by the geometry passed to glDrawArrays or glBegin , etc., and not the texture.

I think the problem is that you are using the pixel size of the font texture to determine the size of the square used to render on the screen. This will put your problem in a "different OpenGL drawing code." To fix this, you can apply some scale factor to the drawing. In retina mode, the scale factor is 0.5, and for regular screens - 1.0 (UIKit uses a similar idea to display the contents of the UIView)

A square calculation might look something like this:

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

Another option is to completely separate the quad rendering size from the texture. If you have a function or class for drawing text, it may have a font size parameter that it will use as the actual size of the square instead of using the texture size.

+1
source

For my retina screen, I had the problem that my framebuffer does not fit the actual window (the full buffer is displayed in a quarter of the window). In this case, using a double viewport solves the problem.

 # Instead of actual window size width*height, # double the dimensions for retina display glViewport(0, 0, width*2, height*2) 

In your case (this part is just a guess, since I can't run your code), resizing the frames you pass gl to create textures can do the trick.

 GLsizei(frameSize.width*2), GLsizei(frameSize.height*2) 
0
source

Given that you have an NSBitmapImageRep and get pixel bitmap data from it, you should use bitmap.pixelsWide and bitmap.pixelsHigh , not frameSize when creating the texture from the bitmap.

0
source

Source: https://habr.com/ru/post/1438914/


All Articles