This site is 100% GIF free! Powered by libmng!
libmng - The MNG reference library & related info


Example 2 - Read and examine a MNG

The following example demonstrates a possible use of the chunk-access functions. The code is available as an example ("mngtree") in the BCB samples directory (only in the zip downloads) as well as in the Unix samples directory. This little command-line utility takes a single parameter which should be the location of a Network Graphic (PNG/MNG/JNG). It uses libmng to read the graphic and iterate through it's chunks to display a tree-like structure of them.

The following code shows initial definitions.

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include "../../libmng.h"

typedef struct user_struct {
          FILE *hFile;                 /* file handle */
          int  iIndent;                /* for nice indented formatting */
        } userdata;
typedef userdata * userdatap;

After that it's time to define the callback functions. We only need 6. Memory-management, file open/read/close & a callback for iterating the chunk-list. Since the file is opened in the main function the fileopen/fileclose callbacks are nops. In other situations it will usually be better to have the file opened and closed inside the callbacks, but for this sample it doesn't make much difference.

The chunk-iteration callback function could be extended to "get" the properties of each chunk and print those behind the name of the chunk. This is basically how a graphic-script converter would work. Check the nice BCB contribution by Andy Protano named mngdump. You'll find it in contrib/bcb/mngdump.

/* ************************************************************************** */

mng_ptr myalloc (mng_size_t iSize)
{
  return (mng_ptr)calloc (1, iSize);   /* duh! */
}

/* ************************************************************************** */

void myfree (mng_ptr pPtr, mng_size_t iSize)
{
  free (pPtr);                         /* duh! */
  return;
}

/* ************************************************************************** */

mng_bool myopenstream (mng_handle hMNG)
{
  return MNG_TRUE;                     /* already opened in main function */
}

/* ************************************************************************** */

mng_bool myclosestream (mng_handle hMNG)
{
  return MNG_TRUE;                     /* gets closed in main function */
}

/* ************************************************************************** */

mng_bool myreaddata (mng_handle hMNG,
                     mng_ptr    pBuf,
                     mng_uint32 iSize,
                     mng_uint32 *iRead)
{                                      /* get to my file handle */
  userdatap pMydata = (userdatap)mng_get_userdata (hMNG);
                                       /* read it */
  *iRead = fread (pBuf, 1, iSize, pMydata->hFile);
                                       /* iRead will indicate EOF */
  return MNG_TRUE;
}

/* ************************************************************************** */

mng_bool myiterchunk (mng_handle  hMNG,
                      mng_handle  hChunk,
                      mng_chunkid iChunktype,
                      mng_uint32  iChunkseq)
{                                      /* get to my file handle */
  userdatap pMydata = (userdatap)mng_get_userdata (hMNG);
  char aCh[4];
  char zIndent[80];
  int iX;
                                       /* decode the chunkname */
  aCh[0] = (char)((iChunktype >> 24) & 0xFF);
  aCh[1] = (char)((iChunktype >> 16) & 0xFF);
  aCh[2] = (char)((iChunktype >>  8) & 0xFF);
  aCh[3] = (char)((iChunktype      ) & 0xFF);
                                       /* indent less ? */
  if ( (iChunktype == MNG_UINT_MEND) || (iChunktype == MNG_UINT_IEND) ||
       (iChunktype == MNG_UINT_ENDL) )
    pMydata->iIndent -= 2;
                                       /* this looks ugly; but I haven't
                                          figured out how to do it prettier */
  for (iX = 0; iX < pMydata->iIndent; iX++)
    zIndent[iX] = ' ';
  zIndent[pMydata->iIndent] = '\0';
                                       /* print a nicely indented line */
  printf ("%s%c%c%c%c\n", zIndent, aCh[0], aCh[1], aCh[2], aCh[3]);
                                       /* indent more ? */
  if ( (iChunktype == MNG_UINT_MHDR) || (iChunktype == MNG_UINT_IHDR) ||
       (iChunktype == MNG_UINT_JHDR) || (iChunktype == MNG_UINT_DHDR) ||
       (iChunktype == MNG_UINT_BASI) || (iChunktype == MNG_UINT_LOOP)    )
    pMydata->iIndent += 2;

  return MNG_TRUE;                     /* keep'm coming... */
}

/* ************************************************************************** */

Now that the callbacks are known it's time to look at the initialization code. It's not much to brag about. Check and open the file. Call mng_initialize to fire up libmng, and register the callbacks.

int dumptree (char * zFilename)
{
  userdatap pMydata;
  mng_handle hMNG;
  mng_retcode iRC;
                                       /* get a data buffer */
  pMydata = (userdatap)calloc (1, sizeof (userdata));

  if (pMydata == NULL)                 /* oke ? */
  {
    fprintf (stderr, "Cannot allocate a data buffer.\n");
    return 1;
  }
                                       /* can we open the file ? */
  if ((pMydata->hFile = fopen (zFilename, "rb")) == NULL)
  {                                    /* error out if we can't */
    fprintf (stderr, "Cannot open input file %s.\n", zFilename);
    return 1;
  }
                                       /* let's initialize the library */
  hMNG = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL);

  if (!hMNG)                           /* did that work out ? */
  {
    fprintf (stderr, "Cannot initialize libmng.\n");
    iRC = 1;
  }
  else
  {                                    /* setup callbacks */
    if ( ((iRC = mng_setcb_openstream  (hMNG, myopenstream )) != 0) ||
         ((iRC = mng_setcb_closestream (hMNG, myclosestream)) != 0) ||
         ((iRC = mng_setcb_readdata    (hMNG, myreaddata   )) != 0)    )
      fprintf (stderr, "Cannot set callbacks for libmng.\n");
    else

The actual reading of the graphic and subsequent iteration of the chunks is really a piece of cake.

    {                                  /* read the file into memory */
      if ((iRC = mng_read (hMNG)) != 0)
        fprintf (stderr, "Cannot read the file.\n");
      else
      {
        pMydata->iIndent = 2;          /* start the indenting at a nice level */

        printf ("Starting dump of %s.\n\n", zFilename);
                                       /* run through the chunk list */
        if ((iRC = mng_iterate_chunks (hMNG, 0, myiterchunk)) != 0)
          fprintf (stderr, "Cannot iterate the chunks.\n");

        printf ("\nDone.\n");
      }
    }

And cleaning up isn't much harder.

    mng_cleanup (&hMNG);               /* cleanup the library */
  }

  fclose (pMydata->hFile);             /* cleanup */
  free (pMydata);

  return iRC;
}

Remains the main-function of the program. After making sure there's a parameter specified it just calls the routine that handles it all.

int main(int argc, char *argv[])
{
        if (argc > 1)                  /* need that first parameter ! */
          return dumptree (argv[1]);
        else
          printf ("\nUsage: mngtree <file.mng>\n\n");  

        return 0;
}

I really can't make it much simpler than that. This should probably have been the first example, but it's too late now to change it around...
Note that the code could be made a lot more user-friendly by using mng_getlasterror to display more details in case libmng reports an error.