如何使用Retina显示在Mac OS上的OpenGL中绘制文本

时间:2012-10-10 13:58:55

标签: macos opengl fonts nsstring retina-display

我正在使用OpenGL在Mac OS中绘图。当我的代码在Retina显示器上运行时,一切正常,除了文本绘图。 在视网膜显示下,文本是它应该的两倍大。之所以会发生这种情况,是因为字体大小是以点为单位,每个点在Retina下都是2个像素,但OpenGL是基于像素的。

以下是标准显示下的正确文字图纸:
Correct text drawing under standard display

以下是Retina显示屏下的错误文字图纸:
Incorrect text drawing under Retina display

这是我如何正常绘制字符串。由于OpenGL没有文本绘图功能,为了绘制文本,我执行以下操作:

  1. 获取字体:

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

  2. 创建并测量字符串:

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

  3. 使用大小分配NSImage:

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

  4. 将字符串绘制到图像中:

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

  5. 获取位:

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

  6. 创建OpenGL纹理:

    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. 绘制纹理:

    glBindTexture ...... 其他OpenGL绘图代码

  8. 我的问题是如何让NSString以像素分辨率绘制而不是以点为单位。

    我尝试了以下内容:

    1. 以点大小的一半绘制:4.5而不是9.这给了我正确的大小,但文本绘制模糊。
    2. 以点大小绘制并将纹理缩小到OpenGL的一半大小,再次这不会给出好看的结果:
      Texture shrink

3 个答案:

答案 0 :(得分:1)

OpenGLs坐标系实际上是基于点而不是基于像素的,但是你决定这些点是什么。上下文通过glOrtho函数定义2D中的坐标系(或者您可以手动构造正交矩阵),其在屏幕上设置最小和最大x,y坐标。例如,可以设置正交投影,使得0位于屏幕左侧,100位于右侧,无论您渲染的帧缓冲区的大小如何。

字体纹理似乎创建得很好。问题是你在几何体上渲染它的大小是它需要的两倍。在OpenGL中,纹理大小不会影响屏幕上呈现的对象的大小。屏幕上的大小由传递给glDrawArraysglBegin等的几何体定义,而不是纹理。

我认为问题在于您使用字体纹理的像素大小来定义用于在屏幕上渲染的四边形大小。这会将您的问题放在“其他OpenGL绘图代码”部分。要解决此问题,您可以将某种比例因子应用于绘图。在视网膜模式下,比例因子为0.5,对于普通屏幕,它将为1.0(UIKit使用类似的想法呈现UIView内容)

四元组计算可能如下所示:

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

另一个选择是将四边形渲染大小与纹理完全分开。如果你有一个函数或类来绘制文本,它可以有一个字体大小参数,它将用作实际的四边形大小,而不是使用纹理大小。

答案 1 :(得分:0)

对于我的视网膜显示,我遇到了我的帧缓冲不适合实际窗口的问题(整个缓冲区渲染到窗口的四分之一)。在这种情况下,使用doubled视口可以解决问题。

# Instead of actual window size width*height,
# double the dimensions for retina display 

glViewport(0, 0, width*2, height*2)

在你的情况下(这部分只是一个假设,因为我无法运行你的代码),改变你传递给gl进行纹理创建的帧大小可以解决问题。

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

答案 2 :(得分:0)

假设你有一个NSBitmapImageRep并从中获取像素栅格数据,那么在从位图创建纹理时,你应该使用bitmap.pixelsWidebitmap.pixelsHigh,而不是frameSize