Image Readers

OBJECTS

  typedef struct ImageReader  ImageReader;

  typedef int (*ImageMessageFunc) (ImageReader *reader, char *message);
  typedef int (*ImageProgressFunc)(ImageReader *reader);

  struct ImageReader {
    char *              filename;           /* user-given fields */
    FILE *              file;               /* file to read from */
    Palette *           src_pal;            /* dither to this palette */
    int                 max_cmap_size;      /* only use this many colours */
    int                 required_depth;     /* 8 or 32 */

    ImageMessageFunc    message_func;       /* report warnings */
    ImageProgressFunc   error_func;         /* if error, tidy up */
    ImageProgressFunc   startup_func;       /* called before start */
    ImageProgressFunc   after_dither_func;  /* called after dither */
    ImageProgressFunc   progress_func;      /* called after each line */
    ImageProgressFunc   rendering_func;     /* called after each line */
    ImageProgressFunc   success_func;       /* called after success */
    void *              user_data;          /* user-defined data */

    int                 state;          /* DITHERING, RENDERING etc */
    int                 stage;          /* 1 <= stage <= max_stages */
    int                 max_stages;     /* total number of stages */
    int                 row;            /* 0 <= row < height, random */
    int                 rows_done;      /* 0,1,...,height in order */
    int                 row_height;     /* of current stage */
    int                 width;          /* in pixels */
    int                 height;         /* in pixels */
    byte **             data8;          /* implies palette */
    Colour **           data32;         /* implies no palette */
    Palette *           pal;            /* if data8 used */
  };

FUNCTIONS

  ImageReader * new_image_reader(void);
  void    del_image_reader(ImageReader *reader);

  Image * read_image(char *filename, int required_depth);
  Image * read_image_file(FILE *file, int required_depth);
  Image * read_image_progressively(ImageReader *reader);
  int     find_image_format(FILE *file);

CONSTANTS

  enum ImageReaderState {
    STOPPED         = 0,    /* at start or end of processing */
    STARTING,               /* creating data structures */
    DITHERING,              /* now dithering to a palette */
    RENDERING,              /* processing lines of pixels */
    IMAGE_ERROR     = -1    /* an error has happened */
  };

  enum ImageFormat {
    PNG_FORMAT      = 1,    /* Portable Network Graphics */
    JPEG_FORMAT     = 2,    /* Joint Photographic Experts Group */
    GIF_FORMAT      = 3,    /* Graphic Interchange Format */
    UNKNOWN_FORMAT  = -1
  };

NOTES

An ImageReader is an abstract object used to read an Image from a file. It can be used to control how an image is read and to receive notifications as lines of the image are completed.

An empty ImageReader structure is first obtained using new_image_reader, and then fields are assigned to control the reading process. The fields filename, file, src_pal, max_cmap_size, and required_depth should be assigned values. If dithering to a user-specified palette is not required, the src_pal and max_cmap_size can be left as their initial empty values.

Call-back functions can be assigned which will notify the program when the image reading process has completed certain stages:

If an error occurs part-way through processing, some of the call-backs may not occur at all. For instance, the success function only happens if the image has been completely processed. Any of the call-backs can be set to NULL (which they are initialised to in any case), which prevents that function from being called.

A programmer-specified data pointer can be set in the user_data field, for use during the call-back functions. The pointer is never touched by the image reader code.

The remaining fields of the ImageReader structure are modified automatically during image processing.

The state field begins (and ends) at STOPPED and is set to different values as image reading progresses: STARTING means that data structures are being allocated, DITHERING means the reader is dithering the image to the required palette, RENDERING means lines of pixels are being read from the image. The IMAGE_ERROR state only happens if there is an error in the image, or if the connection to the image's file is somehow broken.

The stage field reports the current stage in processing, from 1 to max_stages inclusive. Different image formats will have a different maximum number of stages. Interlaced images, for example, may have between 3 and 7 stages, depending on the interlacing technique. Dithering may also be counted as a stage. In general, it is not possible to determine in advance how many stages there are for a given image, but it will either be 1, or a small integer usually less than 10.

The row gives the current pixel line which is being decoded. This will be a number greater than or equal to zero, and less than the pixel height of the image.

The row_height field reports the pixel height of this line of pixels when drawn. Usually this will be equal to one pixel, but it could be larger in the case of interlacing, where lines are read in a non-linear order. For example, a GIF image might store lines in the file in the order 0,4,2,1. In that case, it might be desirable to draw the line taller than 1 pixel, so that the image appears as a series of filled rectangles. Ignoring row_height and using a height of 1 for each line in an interlaced image will instead produce a Venetian blind effect.

The rows_done field increases from 0 to the image height, inclusive. It is a cumulative total of the number of lines read so far.

The lines of image pixels are stored into either the data8 or data32 fields, depending on whether the required_depth field was set to 8 or 32.

The pal field will point to the image's palette if the required_depth was 8, NULL otherwise. This will either be a copy of the src_pal supplied by the programmer, or it will be the palette given in the image file, or a constructed palette if the image file contains 24-bit or 32-bit data. The max_cmap_size field given by the programmer can be zero, which means the palette can have the maximum possible size (256 elements), or it can be set to a positive integer between 1 and 256 inclusive, to specify the maximum number of colours that may be present in the final image.