iDevGames Forums
Anti-aliased bitmapped fonts - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: Anti-aliased bitmapped fonts (/thread-2931.html)



Anti-aliased bitmapped fonts - Jake - Nov 18, 2007 12:36 PM

Right now for GL Golf I'm using the bitmap font code from NeHe tutorial 13 (http://nehe.gamedev.net/data/lessons/lesson.asp?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
}



Anti-aliased bitmapped fonts - OneSadCookie - Nov 18, 2007 12:44 PM

I use http://onesadcookie.com/svn/NSViewTexture for the purpose (requires 10.4) though obviously it works rather differently from your code.


Anti-aliased bitmapped fonts - unknown - Nov 18, 2007 12:48 PM

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.


Anti-aliased bitmapped fonts - Jake - Nov 18, 2007 01:56 PM

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?


Anti-aliased bitmapped fonts - Duane - Nov 18, 2007 02:10 PM

yea, it only works with 10.4. Have you considered using freetype, and bundle it dynamically with your app?


Anti-aliased bitmapped fonts - Jake - Nov 18, 2007 08:58 PM

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.


Anti-aliased bitmapped fonts - OneSadCookie - Nov 19, 2007 01:31 AM

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.


Anti-aliased bitmapped fonts - unknown - Nov 19, 2007 01:59 AM

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)


Anti-aliased bitmapped fonts - ThemsAllTook - Nov 19, 2007 07:26 AM

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;
}



Anti-aliased bitmapped fonts - Jake - Nov 19, 2007 11:43 AM

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?


Anti-aliased bitmapped fonts - ThemsAllTook - Nov 19, 2007 12:22 PM

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();



Anti-aliased bitmapped fonts - macnib - Nov 19, 2007 03:34 PM

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


Anti-aliased bitmapped fonts - Jake - Nov 19, 2007 05:02 PM

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!