Using GBM in your own programs

Introduction

GBM has a simple and straightforward C-API consisting of a couple of functions. On OS/2 and Windows there is an import library provided (gbm.lib) that can be linked to the program that needs access to the GBM.DLL API, on Linux the program can be directly linked against libgbm.so.

For backward compatibility on OS/2 a part of the API uses IBM's _Optlink calling convention. These are mainly those functions starting with gbm_ rather than Gbm_ (which use _System). There are some exceptions to this, new API extensions also use gbm_ but with _System calling convention (as new API extensions should do).

On OS/2 it is required to use the functions with _System calling convention in programs when the compiler does not support _Optlink.
On Windows only __stdcall exports of gbm_ functions are available.
On Linux the calling convention of the platform compiler is used.

The following features are provided by the GBM.DLL / libgbm.so API:

For doing bitmap manipulations there is a bunch of helper libraries included in the archives which are used by the provided GBM command line tools. They can also easily be used by other programs as well. They feature:

All of the above algorithms directly work on the standard bitmap data format provided by the GBM.DLL / libgbm.so to the client.


GBM.DLL / libgbm.so API function availability

The following API functions are available since the specified version of GBM.DLL / libgbm.so:

GBM Version Calling Convention
(OS/2)
Calling Convention
(Windows)
Calling Convention
(Linux)
API Function Name
1.00 _System N/A N/A Gbm_version
Gbm_init
Gbm_deinit
Gbm_query_n_filetypes
Gbm_guess_filetype
Gbm_query_filetype
Gbm_read_header
Gbm_read_palette
Gbm_read_data
Gbm_write
Gbm_err
_Optlink __stdcall platform specific gbm_version
gbm_init
gbm_deinit
gbm_query_n_filetypes
gbm_guess_filetype
gbm_query_filetype
gbm_read_header
gbm_read_palette
gbm_read_data
gbm_write
gbm_err
1.07 _Optlink __stdcall platform specific gbm_io_setup
gbm_io_open
gbm_io_create
gbm_io_close
gbm_io_lseek
gbm_io_read
gbm_io_write
1.09 _System N/A N/A Gbm_io_open
Gbm_io_create
Gbm_io_close
Gbm_io_lseek
Gbm_io_read
Gbm_io_write
1.35 _System __stdcall platform specific gbm_restore_io_setup
gbm_read_imgcount

Example program (bitmap converter)

A typical usage of GBM.DLL in a C/C++ program is shown below. The example program below reads a file named "bitmap.png" and writes it in a different format as "bitmap.tif". So it is a simple bitmap format converter. A full blown converter is included in the GBM command line tools (gbmconv.c). The example code below can be compiled with a C-Compiler. To link the executable, the libraries gbm.lib and gbmmem.lib / gbmmem.a have to be included.

/*
 * My bitmap converter.
 */

#include <stdlib.h>
#include <stdio.h>
#include "gbm.h"
#include "gbmmem.h"

int main(int argc, char *argv[])
{
    char * fn_src  = "bitmap.png";
    char * fn_dst  = "bitmap.tif";
    char * opt_src = "ext_bpp"; /* provide full colour depth data */
    char * opt_dst = "lzw";     /* use LZW compression for destination TIF */

    int      fd, ft_src, ft_dst, flag;
    GBM_ERR  rc;
    GBMFT    gbmft;
    GBM      gbm;
    GBMRGB   gbmrgb[0x100];
    gbm_u8  *data   = NULL;
    size_t   stride = 0;

    /* initialize GBM */
    gbm_init();

    /* The bitmap format is usually determined from it's
     * extension. It is also possible to test the format
     * type by checking the bitmap header but this is less
     * performant. In the example here we simply rely on the
     * file extension.
     *
     * Once the file type is determined, the struct GBMFT
     * contains the found bitmap format name in a short
     * and a long description as well as the supported
     * file extensions for this type of bitmap format.
     */

    /* guess the source file type from the source filename */
    if ( gbm_guess_filetype(fn_src, &ft_src) != GBM_ERR_OK )
    {
      fprintf(stderr, "Can't guess bitmap file format for %s\n", fn_src);
      gbm_deinit();
      return 1;
    }

    /* guess the destination file type from the destination filename */
    if ( gbm_guess_filetype(fn_dst, &ft_dst) != GBM_ERR_OK )
    {
      fprintf(stderr, "Can't guess bitmap file format for %s\n", fn_dst);
      gbm_deinit();
      return 1;
    }

    /* Now open the source file for reading */
    if ( (fd = gbm_io_open(fn_src, GBM_O_RDONLY)) == -1 )
    {
      fprintf(stderr, "Can't open %s\n", fn_src);
      gbm_deinit();
      return 1;
    }

    /* Now read the bitmap header of the first bitmap (PNG only has 1).
     * It provides information about the bitmap size and colour depth.
     */
    if ( (rc = gbm_read_header(fn_src, fd, ft_src, &gbm, opt_src)) != GBM_ERR_OK )
    {
      gbm_io_close(fd);
      fprintf(stderr, "Can't read header of %s: %s\n", fn_src, gbm_err(rc));
      gbm_deinit();
      return 1;
    }

    /* Now check whether the destination format supports the colour depth
     * of the source bitmap data.
     */
    gbm_query_filetype(ft_dst, &gbmft);
    switch ( gbm.bpp )
    {
        case 64: flag = GBM_FT_W64; break;
        case 48: flag = GBM_FT_W48; break;
        case 32: flag = GBM_FT_W32; break;
        case 24: flag = GBM_FT_W24; break;
        case  8: flag = GBM_FT_W8;  break;
        case  4: flag = GBM_FT_W4;  break;
        case  1: flag = GBM_FT_W1;  break;
        default: flag = 0;          break;
    }
    if ( (gbmft.flags & flag) == 0 )
    {
      gbm_io_close(fd);
      fprintf(stderr, "Output bitmap format %s does not support writing %d bpp data\n",
                      gbmft.short_name, gbm.bpp);
      gbm_deinit();
      return 1;
    }

    /* Let's read the colour palette of the source format.
     * If the source bitmap is a true colour bitmap, no
     * palette data is available. Palette information is
     * provided for 1, 4 and 8bpp colour depths.
     * If the bitmap has no colour palette (gbm.bpp > 8),
     * no palette information is returned but the call will
     * still succeed. In this case palette information must
     * not be used!
     */

    if ( (rc = gbm_read_palette(fd, ft_src, &gbm, gbmrgb)) != GBM_ERR_OK )
    {
      gbm_io_close(fd);
      fprintf(stderr, "Can't read palette of %s: %s\n", fn_src, gbm_err(rc));
      gbm_deinit();
      return 1;
    }

    /* Now we are ready to read the bitmap data. The returned
     * data has a standard format which is compatible with the
     * Windows DIB and OS/2 bitmap format data.
     *
     * For palette images an index into the colour table is
     * in the data. Each index has the size of the number of
     * bits reported by the bitmap colour depth. For 4bpp,
     * 4 bits refer to an palette entry and thus 2 indices
     * (for 2 pixels) are stored in one byte.
     *
     * For true colour images the colour is directly encoded
     * in the bitmap data. The encoding is scheme BGR (blue, green, red).
     * The size of each part of the triple is a third of the
     * bitmap colour depth (gbm.bpp/3). Colour depths are so far
     * 24bpp and 48bpp.
     *
     * For true colour images with alpha channel a quadruple is
     * used. The encoding scheme is BGRA (blue, green, red, alpha).
     * The size of each part of the quadruple is a fourth of the
     * bitmap colour depth (gbm.bpp/4). Colour depths with alpha
     * channel data are so far 32bpp and 64bpp.
     *
     * Each data row is aligned to the next multiple of 4 bytes.
     * This is important for calculating the correct size
     * for bitmap data buffer.
     */

    stride = ( (((size_t)(gbm.w) * gbm.bpp + 31)/32) * 4 );
    if ( (data = gbmmem_malloc(stride * gbm.h)) == NULL )
    {
      gbm_io_close(fd);
      fprintf(stderr, "Out of memory allocating %d bytes for input bitmap\n", stride * gbm.h);
      gbm_deinit();
      return 1;
    }

    if ( (rc = gbm_read_data(fd, ft_src, &gbm, data)) != GBM_ERR_OK )
    {
      gbmmem_free(data);
      gbm_io_close(fd);
      fprintf(stderr, "Can't read bitmap data of %s: %s\n", fn_src, gbm_err(rc));
      gbm_deinit();
      return 1;
    }

    /* We're done with reading the source bitmap file. */
    gbm_io_close(fd);

    /* Now create the destination bitmap file. */
    if ( (fd = gbm_io_create(fn_dst, GBM_O_WRONLY)) == -1 )
    {
      gbmmem_free(data);
      fprintf(stderr, "Can't create %s\n", fn_dst);
      gbm_deinit();
      return 1;
    }

    /* And finally write the bitmap data, including palette if there is one. */
    if ( (rc = gbm_write(fn_dst, fd, ft_dst, &gbm, gbmrgb, data, opt_dst)) != GBM_ERR_OK )
    {
      gbmmem_free(data);
      gbm_io_close(fd);
      remove(fn_dst);
      fprintf(stderr, "Can't write %s: %s\n", fn_dst, gbm_err(rc));
      gbm_deinit();
      return 1;
    }

    gbmmem_free(data);

    gbm_io_close(fd);

    /* we don't need GBM anymore */
    gbm_deinit();

    return 0;
}

Note:
The gbmmem_ functions provide platform independent support for heap memory allocation. Especially on OS/2 and eComStation they should be preferrably used as they make High Memory (area above 512MB) transparantly available to the application.