Anti-aliased bitmapped fonts

Member
Posts: 509
Joined: 2002.05
Post: #1
Right now for GL Golf I'm using the bitmap font code from NeHe tutorial 13 (http://nehe.gamedev.net/data/lessons/les...?lesson=13) . The problem is the text is always aliased, with each pixel being either fully on or fully off. Even after enabling full screen anti-aliasing, the text still shows up aliased (while everything else is smoothed as it should be!). I've also tried using these two pieces of code in the setup, with no luck -

[ [ NSGraphicsContext currentContext ] setShouldAntialias:NO ];
[ [ NSGraphicsContext currentContext ] setImageInterpolation:NSImageInterpolationHigh];

Any ideas on how to get this to anti-alias, either through FSAA or on its own? Do I need to look into something else? I tried to find a good demo using Freetype or something, but I couldn't find anything.

Here is the code that I'm using for the bitmapped fonts -

Code:
- (bool) makeGLDisplayListFirst:(unichar)first count:(int)count base:(GLint)base
{
   GLint curListIndex;
   NSColor *blackColor;
   NSDictionary *attribDict;
   GLint dListNum;
   NSString *currentChar;
   unichar currentUnichar;
   NSSize charSize;
   NSRect charRect;
   NSImage *theImage;
   bool retval;

   // Make sure a list isn't already under construction
   glGetIntegerv( GL_LIST_INDEX, &curListIndex );
   if( curListIndex != 0 )
   {
      [ NSFont doOpenGLLog:@"Display list already under construction" ];
      return FALSE;
   }

   // Save pixel unpacking state
   glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT );

   glPixelStorei( GL_UNPACK_SWAP_BYTES, GL_FALSE );
   glPixelStorei( GL_UNPACK_LSB_FIRST, GL_FALSE );
   glPixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
   glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
   glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
   glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );

   blackColor = [ NSColor blackColor ];
   attribDict = [ NSDictionary dictionaryWithObjectsAndKeys:
                                  self, NSFontAttributeName,
                                  [ NSColor whiteColor ],
                                     NSForegroundColorAttributeName,
                                  blackColor, NSBackgroundColorAttributeName,
                                  nil ];
   charRect.origin.x = charRect.origin.y = 0;
   theImage = [ [ [ NSImage alloc ] initWithSize:NSMakeSize( 0, 0 ) ]
                autorelease ];
   retval = TRUE;
   for( dListNum = base, currentUnichar = first; currentUnichar < first + count;
        dListNum++, currentUnichar++ )
   {
      currentChar = [ NSString stringWithCharacters:&currentUnichar length:1 ];
      charSize = [ currentChar sizeWithAttributes:attribDict ];
      charRect.size = charSize;
      charRect = NSIntegralRect( charRect );
      if( charRect.size.width > 0 && charRect.size.height > 0 )
      {
         [ theImage setSize:charRect.size ];
         [ theImage lockFocus ];
         [ [ NSGraphicsContext currentContext ] setShouldAntialias:NO ];
         [ blackColor set ];
         [ NSBezierPath fillRect:charRect ];
         [ currentChar drawInRect:charRect withAttributes:attribDict ];
         [ theImage unlockFocus ];
         if( ![ self makeDisplayList:dListNum withImage:theImage ] )
         {
            retval = FALSE;
            break;
         }
      }
   }

   glPopClientAttrib();

   return retval;
}


/*
* Create one display list based on the given image.  This assumes the image
* uses 8-bit chunks to represent a sample
*/
- (bool) makeDisplayList:(GLint)listNum withImage:(NSImage *)theImage
{
   NSBitmapImageRep *bitmap;
   int bytesPerRow, pixelsHigh, pixelsWide, samplesPerPixel;
   unsigned char *bitmapBytes;
   int currentBit, byteValue;
   unsigned char *newBuffer, *movingBuffer;
   int rowIndex, colIndex;

   bitmap = [ NSBitmapImageRep imageRepWithData:[ theImage
                          TIFFRepresentationUsingCompression:NSTIFFCompressionNone
                          factor:0 ] ];
   pixelsHigh = [ bitmap pixelsHigh ];
   pixelsWide = [ bitmap pixelsWide ];
   bitmapBytes = [ bitmap bitmapData ];
   bytesPerRow = [ bitmap bytesPerRow ];
   samplesPerPixel = [ bitmap samplesPerPixel ];
   newBuffer = (unsigned char *) calloc( ceil( (float) bytesPerRow / 8.0 ), pixelsHigh );
   if( newBuffer == NULL )
   {
      [ NSFont doOpenGLLog:@"Failed to calloc() memory in "
                           @"makeDisplayList:withImage:" ];
      return FALSE;
   }

   movingBuffer = newBuffer;
   /*
    * Convert the color bitmap into a true bitmap, ie, one bit per pixel.  We
    * read at last row, write to first row as Cocoa and OpenGL have opposite
    * y origins
    */
   for( rowIndex = pixelsHigh - 1; rowIndex >= 0; rowIndex-- )
   {
      currentBit = 0x80;
      byteValue = 0;
      for( colIndex = 0; colIndex < pixelsWide; colIndex++ )
      {
         if( bitmapBytes[ rowIndex * bytesPerRow + colIndex * samplesPerPixel ] )
            byteValue |= currentBit;
         currentBit >>= 1;
         if( currentBit == 0 )
         {
            *movingBuffer++ = byteValue;
            currentBit = 0x80;
            byteValue = 0;
         }
      }
      /*
       * Fill out the last byte; extra is ignored by OpenGL, but each row
       * must start on a new byte
       */
      if( currentBit != 0x80 )
         *movingBuffer++ = byteValue;
   }

   glNewList( listNum, GL_COMPILE );
   glBitmap( pixelsWide, pixelsHigh, 0, 0, pixelsWide, 0, newBuffer );
   glEndList();
   free( newBuffer );

   return TRUE;
}

Code:
- (void) glPrint:(NSString *)fmt, ...
{
   NSString *text;
   va_list ap;                        // Pointer To List Of Arguments
   unichar *uniBuffer;

   if( fmt == nil || [ fmt length ] == 0 )   // If There's No Text
      return;                                // Do Nothing

   va_start( ap, fmt );               // Parses The String For Variables
   text = [ [ [ NSString alloc ] initWithFormat:fmt arguments:ap ] autorelease ];
   va_end( ap );                      // Results Are Stored In Text

   glPushAttrib( GL_LIST_BIT );       // Pushes The Display List Bits
   glListBase( base - 32 );           // Sets The Base Character to 32
   uniBuffer = (unichar *) calloc( [ text length ], sizeof( unichar ) );
   [ text getCharacters:uniBuffer ];
   // Draws The Display List Text
   glCallLists( [ text length ], GL_UNSIGNED_SHORT, uniBuffer );
   free( uniBuffer );
   glPopAttrib();                     // Pops The Display List Bits
}
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
I use http://onesadcookie.com/svn/NSViewTexture for the purpose (requires 10.4) though obviously it works rather differently from your code.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #3
You're welcome to use any output from http://fax.twilightcoders.net/GlyphTool/ in any software you want. It generates bitmap fonts and there's some code to load them which works (or should do) on any system with opengl.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Member
Posts: 509
Joined: 2002.05
Post: #4
Thanks for the info guys.

Unfortunately I can't drop 10.3.9 yet, still to many users (although I finally did give up on 10.2.8 last month!). I did figure out NSViewTexture and it looks pretty cool!

Unknown, I tried building for 10.3.9 and it didn't work, am I correct saying it requires 10.4?
Quote this message in a reply
Member
Posts: 567
Joined: 2004.07
Post: #5
yea, it only works with 10.4. Have you considered using freetype, and bundle it dynamically with your app?

It's not magic, it's Ruby.
Quote this message in a reply
Member
Posts: 509
Joined: 2002.05
Post: #6
Nayr Wrote:yea, it only works with 10.4. Have you considered using freetype, and bundle it dynamically with your app?

I have, but I haven't found any good simple demos showing it yet , I guess thats my next step tomorrow.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #7
http://onesadcookie.com/svn/SimpleText and http://onesadcookie.com/svn/SimpleTextCDemo (not without bugs, but a decent starting point). http://onesadcookie.com/svn/Third-Party builds freetype for all the architectures you have an SDK for.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #8
Jake Wrote:Unknown, I tried building for 10.3.9 and it didn't work, am I correct saying it requires 10.4?

try the binary, and the output should be useable on anything (not just macs)

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #9
Some code from one of my projects that works on 10.3.9. It works a bit differently from yours (draws a whole string at a time), but anti-aliasing works:

Code:
- (id) initWithTextString: (NSString *) string attributes: (NSDictionary *) attributes {
  NSAttributedString * attributedString;
  void * pixels;
  GLenum format;
  
  attributedString = [[NSAttributedString alloc] initWithString: string attributes: attributes];
  
  isText = YES;
  
  width = [attributedString size].width;
  height = [attributedString size].height;
  
  widthPOT = 1;
  while (widthPOT < width) {
    widthPOT <<= 1;
  }
  heightPOT = 1;
  while (heightPOT < height) {
    heightPOT <<= 1;
  }
  
  pixels = getStringImageData(attributedString, widthPOT, heightPOT, &format);
  
  glGenTextures(1, &name);
  glBindTexture(GL_TEXTURE_2D, name);
  glTexImage2D(GL_TEXTURE_2D,
               0,
               GL_ALPHA,
               widthPOT,
               heightPOT,
               0,
               format,
               GL_UNSIGNED_BYTE,
               pixels);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  
  free(pixels);
  [attributedString release];
  
  return self;
}

static void * getStringImageData(NSAttributedString * string, int maxWidth, int maxHeight, GLenum * outFormat) {
  static long systemVersion = 0x0000;
  void * pixels;
  
  if (systemVersion == 0x0000) {
    Gestalt(gestaltSystemVersion, &systemVersion);
  }
  
  if (systemVersion >= 0x1040) {
    NSBitmapImageRep * bitmap;
    NSGraphicsContext * context;
    
    pixels = (unsigned char *) calloc(maxWidth, maxHeight);
    
    bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: (unsigned char **) &pixels
                                       pixelsWide: maxWidth
                                       pixelsHigh: maxHeight
                                       bitsPerSample: 8
                                       samplesPerPixel: 1
                                       hasAlpha: NO
                                       isPlanar: NO
                                       colorSpaceName: NSDeviceWhiteColorSpace
                                       bitmapFormat: 0
                                       bytesPerRow: maxWidth
                                       bitsPerPixel: 8];
    
    context = [NSGraphicsContext graphicsContextWithBitmapImageRep: bitmap];
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext: context];
    [string drawAtPoint: NSMakePoint(0, 0)];
    [NSGraphicsContext restoreGraphicsState];
    *outFormat = GL_ALPHA;
    
    [bitmap release];
  } else {
    NSImage * image;
    NSBitmapImageRep * bitmap;
    
    pixels = (unsigned char *) calloc(maxWidth, maxHeight * 4);
    
    image = [[NSImage alloc] initWithSize: NSMakeSize(maxWidth, maxHeight)];
    [image lockFocus];
    [string drawAtPoint: NSMakePoint(0, 0)];
    
    bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect: NSMakeRect(0.0f, 0.0f, maxWidth, maxHeight)];
    [image unlockFocus];
    
    memcpy(pixels, [bitmap bitmapData], maxWidth * maxHeight * 4);
    *outFormat = GL_RGBA;
    
    [image release];
    [bitmap release];
  }
  
  return pixels;
}
Quote this message in a reply
Member
Posts: 509
Joined: 2002.05
Post: #10
Thanks for the links OSC and the info unknown.

ThemsAllTook - That code was pretty easy to add to my project, do you have a project on your site that uses it that I can download, or a snippet of code to render the text once its created into a texture?
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #11
Full source code for this project isn't publicly available, but here are the relevant snippets:
Code:
- (void) apply {
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  if (isText) {
    glTranslatef(0.0f, (1.0f - (((float) height / (float) heightPOT) * 0.5f)), 0.0f);
    glScalef(((float) width / (float) widthPOT), -((float) height / (float) heightPOT), 1.0f);
    glTranslatef(0.0f, -0.5f, 0.0f);
  }
  glMatrixMode(GL_MODELVIEW);
  
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, name);
}

...

  /* Simple example usage */
  [texture apply];
  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f);
  glVertex2f(left, bottom);
  glTexCoord2f(1.0f, 0.0f);
  glVertex2f(right, bottom);
  glTexCoord2f(1.0f, 1.0f);
  glVertex2f(right, top);
  glTexCoord2f(0.0f, 1.0f);
  glVertex2f(left, top);
  glEnd();
Quote this message in a reply
Member
Posts: 81
Joined: 2007.05
Post: #12
I was using something similar to lesson 13 in my stuff.

Though, I've switched to freetype -- nehe lesson 43 which addresses anti aliasing. The lesson is written clearly.

Works well. Anti aliases and positions the text correctly. Smile
Quote this message in a reply
Member
Posts: 509
Joined: 2002.05
Post: #13
ThemsAllTook Wrote:Full source code for this project isn't publicly available, but here are the relevant snippets:

Thanks, but after tinkering for a while I'm getting white rectangles instead of text - does this look right to set it up?
Code:
NSFont *font = [NSFont fontWithName:@"Lucida Grande" size:14.0];
NSDictionary *attrsDictionary =[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
id tmptxt = [[Text alloc] initWithTextString:@"This is my test string. /n/n Line 2" attributes:attrsDictionary];

Thanks for the link to lesson 43, I didn't realize that one existed!
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  How do I flip upside down fonts in FTGL mr5z 4 1,122 Mar 3, 2014 03:36 PM
Last Post: JustinFic
  Bitmap fonts markhula 6 8,041 Feb 27, 2011 12:55 PM
Last Post: markhula
  Bitmapped Font OptimisticMonkey 8 4,088 Aug 16, 2009 01:27 AM
Last Post: Ingemar
  shared context breaking FTGL texture fonts akb825 12 6,547 Sep 7, 2006 08:56 PM
Last Post: akb825
  Fonts with OpenGL &amp; GLF Jones 15 7,195 Jul 5, 2006 11:32 AM
Last Post: akb825