# Using GBM.DLL in your own programs

## Introduction

GBM.DLL has a simple and straightforward C-API consisting of a couple of functions. There is an import library provided (gbm.lib) that can be linked to the program that needs access to the GBM.DLL API.

For backward compatibility 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).
It is recommended to use the functions with _System calling convention in new programs.

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

- Read/write a bitmap from/to a file.
- Read/write a bitmap by using custom I/O functions that can be registered.
- Reading individual pages of all supported multipage capabable bitmap formats.
- Alpha channel/transparency support for GIF, PNG, TIF, TGA, XPM
- Colour depths 1, 4, 8, 24, 32, 48, 64 bpp on the internal bitmap format. Other external colour depths like 2bpp are usually mapped to the above depths to simplify image processing.

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:

- Fast and high quality (resampling) bitmap resizing
- Rotating in steps of 90°, mirroring
- Gamma correction
- Colour mapping with a bunch of algorithms (like error diffusion or halftoning)

All of the above algorithm directly work on the standard bitmap data format provided by the GBM.DLL to the client.

## GBM.DLL API function availability

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

GBM.DLL Version | Calling convention | API function name
--- | --- | ---
1.00 | _System | 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 | 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 | gbm_io_setup gbm_io_open gbm_io_create gbm_io_close gbm_io_lseek gbm_io_read gbm_io_write
1.09 | _System | Gbm_io_open Gbm_io_create Gbm_io_close Gbm_io_lseek Gbm_io_read Gbm_io_write
1.35 | _System | 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` 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, stride, flag;
        GBM_ERR  rc;
        GBMFT    gbmft;
        GBM      gbm;
        GBMRGB   gbmrgb[0x100];
        byte    *data;

        /* 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 = ( ((gbm.w * gbm.bpp + 31)/32) * 4 );
        if ( (data = gbmmem_malloc((size_t) (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;
    }