Endianness issues using the framebuffer

Roger Leigh <[email protected]>
Newsgroups gmane.linux.fbdev.user
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi folks,

I'm looking into writing a program which will use the framebuffer to
display rendered fonts and some graphics.  However, I've not yet found
much in the way of programming documentation.  Is there anything you
would recommend?

(I've looked at SDL, directfb and GGI sources, but they are very
complex.)

Using the comments in <linux/fb.h> and some simple examples I found,
I've written the sample code below.  However, none of the examples I
saw made any use of the fb_bitfield information.  I ran into problems
when I tried my (i386 tested) code on a PPC.  After fixing the code,
it still doesn't work.  I have the bizarre situation that if I run it
under X11 (using XF86_FBDev), I see the glyphs rendered fine, but if I
run it on the console the colours are mucked up, and I can't see any
change in the bitfield data.

Are there any examples of painting simple RGB pixel data onto any
arbitrary framebuffer device which also works on big and little endian
systems.  I'm sure this is fairly simple once you know how!

Additionally, is there any documentation about how colourmaps and the
other framebuffer features work?


Many thanks,
Roger


This program simply renders each ASCII character in turn and paints
it.  You might need to change the fb device and font path.  It's
currently hard-coded for RBG LCDs, but is simple to change for a CRT.

Compile with
c99 -o ft-lcd ft-lcd.c `freetype-config --cflags --libs`

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

#include <ft2build.h>
#include FT_FREETYPE_H

void fail (const char *reason)
{
  fprintf (stderr, "%s\n", reason);
  exit (EXIT_FAILURE);
}

int
main()
{
  int fbfd = 0;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  long int screensize = 0;
  char *fbp = 0;
  int x = 0, y = 0;
  long int location = 0;

  // Open the file for reading and writing
  fbfd = open("/dev/fb/0", O_RDWR);
  if (fbfd < 0)
    fail("Error: cannot open framebuffer device.");

  // Get fixed screen information
  if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
    fail("Error reading fixed information.");

  // Get variable screen information
  if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
    fail("Error reading variable information.");

  printf("%ux%u (%ux%u), %ubpp\n", vinfo.xres, vinfo.yres,
	 vinfo.xres_virtual, vinfo.yres_virtual, vinfo.bits_per_pixel );
  printf("offsets: x=%u, y=%u\n", vinfo.xoffset, vinfo.yoffset);
  printf("red: o=%u, l=%u, mr=%u\n", vinfo.red.offset, vinfo.red.length, vinfo.red.msb_right);
  printf("green: o=%u, l=%u, mr=%u\n", vinfo.green.offset, vinfo.green.length, vinfo.green.msb_right);
  printf("blue: o=%u, l=%u, mr=%u\n", vinfo.blue.offset, vinfo.blue.length, vinfo.blue.msb_right);
  printf("transp: o=%u, l=%u, mr=%u\n", vinfo.transp.offset, vinfo.transp.length, vinfo.transp.msb_right);

  unsigned int red_max = 1 << vinfo.red.length;
  unsigned int green_max = 1 << vinfo.green.length;
  unsigned int blue_max = 1 << vinfo.blue.length;
  unsigned int transp_max = 1 << vinfo.transp.length;

  unsigned int red_mask = 0;
  for (unsigned int i=0; i<vinfo.red.length; ++i)
    red_mask |= 1 << (vinfo.red.offset + i);
  unsigned int green_mask = 0;
  for (unsigned int i=0; i<vinfo.green.length; ++i)
    green_mask |= 1 << (vinfo.green.offset + i);
  unsigned int blue_mask = 0;
  for (unsigned int i=0; i<vinfo.blue.length; ++i)
    blue_mask |= 1 << (vinfo.blue.offset + i);
  unsigned int transp_mask = 0;
  for (unsigned int i=0; i<vinfo.transp.length; ++i)
    transp_mask |= 1 << (vinfo.transp.offset + i);


  // Figure out the size of the screen in bytes
  screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

  // Map the device to memory
  fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
		     fbfd, 0);
  if ((int)fbp == -1)
    fail("Error: failed to map framebuffer device to memory.\n");

  // Now setup FreeType
  FT_Library library;
  FT_Face face;

  int error = FT_Init_FreeType( &library );
  if ( error )
    {
      fail ("Failed to initialise FreeType");
    }

  error = FT_New_Face( library, "/usr/share/fonts/truetype/ttf-bitstream-vera/VeraMono.ttf", 0, &face );
  if ( error == FT_Err_Unknown_File_Format )
    {
      fail ("Unsupported font format");
    }
  else if ( error )
    {
      fail ("Error reading font file");
    }

  error = FT_Set_Char_Size ( face,
			     0,
			     12 * 64,
			     100,
			     100 );
  if (error)
    {
      fail ("Error setting char size");
    }

  // Display each ASCII printable char
  for (FT_ULong n = 33; n < 127; ++n)
    {
      FT_UInt glyph_index = FT_Get_Char_Index (face, n);

      FT_Load_Glyph (face,
		     glyph_index,
		     FT_LOAD_DEFAULT|FT_LOAD_TARGET_LCD);

      error = FT_Render_Glyph (face->glyph,
			       FT_RENDER_MODE_LCD);

      if (error)
	{
	  fail ("Error rendering glyph");
	}

      if (n == '_')
	continue;

      FT_Bitmap *bitmap = &face->glyph->bitmap;

      //      printf ("Glyph '%c': %d * %d\n", (char) n, bitmap->width, bitmap->rows);

      //printf ("Bitmap top: %d\nBitmap left: %d\n", face->glyph->bitmap_top,
      //      face->glyph->bitmap_left);

      for (y=0; y<200; ++y)
	for (x=0; x<200; ++x)
	  {
	    location = ((x + 0)+vinfo.xoffset) *
	      (vinfo.bits_per_pixel/8) +
	      ((y + 0)+vinfo.yoffset) * finfo.line_length;

	    if ( vinfo.bits_per_pixel == 32 ) {
	      unsigned int *pixel = (int *) (fbp + location);
	      *pixel = 0;
	    }
	  }

      for (y=0; y<bitmap->rows; ++y)
	for (x=0; x< (bitmap->width / 3); ++x)
	  {
	    location = ((x + face->glyph->bitmap_left)+vinfo.xoffset) *
	      (vinfo.bits_per_pixel/8) +
	      ((y + face->glyph->bitmap_top)+vinfo.yoffset) * finfo.line_length;

	    unsigned int r = *(bitmap->buffer + (y * bitmap->pitch) + (x*3 + 0));
	    //	    r *= (red_max /8);
	    unsigned int g = *(bitmap->buffer + (y * bitmap->pitch) + (x*3 + 1));
	    //	    g *= (red_max /8);
	    unsigned int b = *(bitmap->buffer + (y * bitmap->pitch) + (x*3 + 2));
	    //	    b *= (red_max /8);

	    // printf("r=%u, g=%u, b=%u\n", r, g, b);

	    if ( vinfo.bits_per_pixel == 32 ) {
	      unsigned int *pixel = (int *) (fbp + location);
	      *pixel = 0;

	      // This works when I run under X11_FBDev (i.e. the
	      // colours are correct), but if I run on the console, I
	      // get a mess and mostly blue colours...

	      *pixel |= ((r << vinfo.red.offset)   & red_mask);
	      *pixel |= ((g << vinfo.green.offset) & green_mask);
	      *pixel |= ((r << vinfo.blue.offset)  & blue_mask);

	    }
	  }

      sleep (1);

    }

  return 0;
}


- -- 
Roger Leigh
                Printing on GNU/Linux?  http://gimp-print.sourceforge.net/
                Debian GNU/Linux        http://www.debian.org/
                GPG Public Key: 0x25BFB848.  Please sign and encrypt your mail.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Processed by Mailcrypt 3.5.8 <http://mailcrypt.sourceforge.net/>

iD8DBQFCwGWTVcFcaSW/uEgRAkt1AJ9n98Nx0sFOCSQz6FKJ4mftcnijLACgiW6A
Ew9wJRanhXh419I9x6PRgzU=
=2YCz
-----END PGP SIGNATURE-----


-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.