Skip to main content

Load_32-bit_BMP_with_Alpha — wiki

I was stuck using a platform that did not support extended image formats, but I absolutely had to have per-pixel-alpha. To overcome, I wrote this nifty little module.

"""
bmp32.py

Load a 32-bit, RGBA Windows Bitmap file for use with PyGame.
2007 Michael Thomas Greer

Released to the Public Domain.

Overview

  PyGame does not always come with the ability to load all image types. This
  is inconvenient because sometimes you just really, really want to play with
  per-pixel alpha images, but PyGame's BMP support doesn't understand a
  bitmap's alpha channel, and if you are stuck using a platform without
  extended image support, who do you call?

  Windows BMP files have always been capable of storing alpha data, but it was
  only with the release of Windows XP that Microsoft actually supported such
  use. As a result, most BMP readers/editors ignore or clobber alpha data in
  your bitmap. PyGame is no different.

  This module addresses that problem in pure python code.

Example of How to Use

  Here's a very simple example. You'll need two files to run it. The first,
  named "background.bmp", is any normal BMP file to use for the background
  image. The other, "foreground.bmp32", is a 32-bit BMP with alpha channel
  which follows the cursor as you move it around the PyGame display window.

    import pygame
    import bmp32

    pygame.init()
    bg     = pygame.image.load( 'background.bmp' )
    screen = pygame.display.set_mode( bg.get_size() )
    bg     = bg.convert()
    fg     = bmp32.load( 'foreground.bmp32' ).convert_alpha()

    screen.blit( bg, (0, 0) )
    pygame.display.update()
    pygame.mouse.set_visible( False )

    done = False
    while not done:
      for event in pygame.event.get():
        if event.type in [pygame.QUIT, pygame.KEYDOWN]:
          done = True
          break
        elif event.type == pygame.MOUSEMOTION:
          screen.blit( bg, (0, 0) )
          screen.blit( fg, event.pos )
          pygame.display.update()

Building a 32-bit BMP with alpha channel

  Photoshop is one of the few programs that can load and save a BMP with alpha
  data. If you don't have access to Photoshop (as I don't), here's a little
  cross-platform C++ source to compile a bitmap with alpha.

  It requires the EasyBMP library at "http://easybmp.sourceforge.net/".
  EasyBMP is specifically cross-platform, small, and self-contained. Just
  unzip everything to a new directory. Create a new CPP file in that same
  directory and paste the following into it.

    // bmp32.cpp
    //
    // Takes two BMP files and combines them into a 32-bit, RGBA Bitmap.
    // 2007 Michael Thomas Greer

    #include <iostream>
    #include <fstream>
    #include "EasyBMP.h"

    using namespace std;

    int usage() {
      cerr &lt;&lt; "usage:"                                              &lt;&lt; endl
           &lt;&lt; "  bmp32 rgb.bmp alpha.bmp result.bmp32"              &lt;&lt; endl
           &lt;&lt;                                                          endl
           &lt;&lt; "where:"                                              &lt;&lt; endl
           &lt;&lt; "  rgb.bmp    is the name of the image with colors."  &lt;&lt; endl
           &lt;&lt; "  alpha.bmp  is a grayscale image representing the"  &lt;&lt; endl
           &lt;&lt; "             alpha channel. Black is fully"          &lt;&lt; endl
           &lt;&lt; "             transparent and white is fully opaque." &lt;&lt; endl
           &lt;&lt; "  result.bmp is the name of the file you wish to"    &lt;&lt; endl
           &lt;&lt; "             create (overwrites existing files)."    &lt;&lt; endl;
      return EXIT_FAILURE;
      }

    int failure( const char *message ) {
      cout &lt;&lt; message &lt;&lt; endl;
      return EXIT_FAILURE;
      }

    int main( int argc, char **argv ) {

      BMP rgb_file, alpha_file;

      if (argc &lt; 4) return usage();

      if (!rgb_file  .ReadFromFile( argv[ 1 ] ))
        return failure( "Could not read RGB color bitmap" );

      if (!alpha_file.ReadFromFile( argv[ 2 ] ))
        return failure( "Could not read alpha data bitmap" );

      if ((rgb_file.TellHeight() != alpha_file.TellHeight()) ||
          (rgb_file.TellWidth()  != alpha_file.TellWidth()))
        return failure( "The source bitmaps are not the same size!" );

      rgb_file.SetBitDepth( 32 );

      for (int y = 0; y &lt; rgb_file.TellHeight(); y++)
      for (int x = 0; x &lt; rgb_file.TellWidth();  x++)
        rgb_file( x, y )->Alpha = alpha_file( x, y )->Red;

      if (!rgb_file.WriteToFile( argv[ 3 ] ))
        return failure( "Failed to create or overwrite output file." );

      return EXIT_SUCCESS;
      }

    // end bmp32.cpp

  Compile it with your favorite C++ compiler. I use the GCC:

    g++ -o bmp32 bmp32.cpp EasyBMP.cpp

"""

import pygame

#-----------------------------------------------------------------------------
def load( filename ):
  """
  Load a 32-bit BMP image file containing per-pixel-alpha.

  You will probably want to call convert_alpha() on the result.

  Returns the new surface, or None on error.

  """

  #...........................................................................
  def get_word( bytes ):
    result = 0
    is_neg = bool( ord( bytes[ -1 ] ) >> 7 )
    for shift, value in enumerate( bytes ):
      if is_neg:
        result -= (255 - ord( value )) &lt;&lt; (shift *8)
      else:
        result += ord( value ) &lt;&lt; (shift *8)
    if is_neg:
      return result -1
    else:
      return result

  # Initialize ...............................................................
  try:
    f = open( filename, 'rb' )
    data = f.read()
    f.close()

    # Assert: this is a Windows BMP
    if data[:2] != 'BM': raise Exception()

    pixel_data_offset  = get_word( data[ 10:14 ] )
    bitmap_info_header = data[ 14:(14+40) ]

    # Assert: this is a valid Windows BMP
    if get_word( bitmap_info_header[ :4 ] ) != 40: raise Exception()

    # Assert: image type is BI_RGB (not compressed) 
    if get_word( bitmap_info_header[ 16:20 ] ) != 0: raise Exception()

    width    = get_word( bitmap_info_header[  4:8  ] )
    height   = get_word( bitmap_info_header[  8:12 ] )
    bitcount = get_word( bitmap_info_header[ 14:16 ] )

    # Assert: bitmap has bitdepth of 32-bits per pixel 
    if bitcount != 32: raise Exception()

    is_inverted = (height &lt; 0)
    if is_inverted: height = -height

    # Load and store the image data ..........................................

    result = pygame.Surface( (width, height), pygame.SRCALPHA, 32 )
    result.lock()
    try:
      for y in xrange( height ):
        if not is_inverted: y = height -y

        for x in xrange( width ):
          result.set_at(
            (x, y),
            (ord( data[ pixel_data_offset +2 ] ),  # Red
             ord( data[ pixel_data_offset +1 ] ),  # Green
             ord( data[ pixel_data_offset +0 ] ),  # Blue
             ord( data[ pixel_data_offset +3 ] ))  # Alpha
            )
          pixel_data_offset += 4
    finally:
      result.unlock()

  except:
    return None

  return result

# end bmp32.py
</fstream></iostream>

It looks like the wiki code is broken when dealing with C++ include directives. That last line with iostream and fstream doesn't belong there.

Alas, I also can't figure out how to get rid of & lt; in the code block. If you copy this file, you'll have to do a search and replace to change them back to the less-than and left-shift operators.

--Duoas