Logo Search packages:      
Sourcecode: zgv version File versions

readbmp.c

/* zgv 5.5 - GIF, JPEG, PNM and BMP viewer, for VGA PCs running Linux.
 * Copyright (C) 1993-2001 Russell Marks. See README for license details.
 *
 * readbmp.c - BMP loader.
 *
 * BMP support by Carsten Engelmann (based on readpnm.c).
 */

/* portability fixes RJM 2001 May 25 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>
#include "zgv.h"
#include "readbmp.h"
#include "readpnm.h"    /* for dithering routines */
#include "rcfile.h"
#include "rc_config.h"

/* prototypes */
static int flip (byte * image, unsigned int w, unsigned int h);


/* for aborted_file_bmp_cleanup() */
static unsigned char *work_bmap, *work_pal;
static FILE *work_in;


#define BMPGET2(x)      ((x)[0]|((x)[1]<<8))
#define BMPGET4(x)      ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24))

void read_bch(BITMAPCOREHEADER *bchp,unsigned char *ptr)
{
bchp->bcSize=BMPGET4(ptr);    ptr+=4;
bchp->bcWidth=BMPGET2(ptr);   ptr+=2;
bchp->bcHeight=BMPGET2(ptr);  ptr+=2;
bchp->bcPlanes=BMPGET2(ptr);  ptr+=2;
bchp->bcBitCount=BMPGET2(ptr);
}

void read_bih(BITMAPINFOHEADER *bihp,unsigned char *ptr)
{
bihp->biSize=BMPGET4(ptr);          ptr+=4;
bihp->biWidth=BMPGET4(ptr);         ptr+=4;
bihp->biHeight=BMPGET4(ptr);        ptr+=4;
bihp->biPlanes=BMPGET2(ptr);        ptr+=2;
bihp->biBitCount=BMPGET2(ptr);            ptr+=2;
bihp->biCompression=BMPGET4(ptr);   ptr+=4;
bihp->biSizeImage=BMPGET4(ptr);           ptr+=4;
bihp->biXPelsPerMeter=BMPGET4(ptr); ptr+=4;
bihp->biYPelsPerMeter=BMPGET4(ptr); ptr+=4;
bihp->biClrUsed=BMPGET4(ptr);       ptr+=4;
bihp->biClrImportant=BMPGET4(ptr);
}



int read_bmp_file (char *filename, hffunc howfarfunc, unsigned char **bmap,
                   unsigned char **pal, int *output_type, PICINFO *pp)
{
  FILE *in;
  int i, j;
  int iswindows = 0;
  int dummy, count, done, bytepp, bytes_in_image;
  unsigned char *buf;
  unsigned char read[2];
  unsigned char *p;
  unsigned char *ptr, *dptr, *hptr;
  unsigned int w;
  unsigned int h;
  unsigned int palsize;
  BITMAPINFOHEADER bih;
  BITMAPCOREHEADER bch;

  *bmap = NULL;
  *pal = NULL;

  if ((work_in = in = fopen (filename, "rb")) == NULL)
    return (_PICERR_NOFILE);

  if ((buf = malloc (54)) == NULL)
    CLOSE_AND_RET(_PICERR_NOMEM);

  fread (buf, 1, 26, in);

  if (buf[0] != 'B')
    CLOSE_AND_RET(_PICERR_BADMAGIC);

  if (buf[1] != 'M')
    CLOSE_AND_RET(_PICERR_BADMAGIC);

  read_bch(&bch,buf+14);

  iswindows=0;
  if (bch.bcSize == 40)       /* truly MS_Windows 3.x ?*/
    {
      fread (buf + 26, 1, 28, in);/* then we need the rest */
      read_bih(&bih,buf+14);
      iswindows = 1;
    }

  if ((p = calloc (768,1)) == NULL)
    CLOSE_AND_RET(_PICERR_NOMEM);

  /* only actually used if using dither 8-bit display of 24-bit image,
   * but defined always
   */
  make_332_palette(p);

  palsize=(buf[10]|(buf[11]<<8)|(buf[12]<<16)|(buf[13]<<24));
  if (iswindows)        /*  MS_Windows 3.x */
    {
      pp->width = w = bih.biWidth;
      pp->height = h = bih.biHeight;
      pp->bpp = bih.biBitCount;

      /* Here the "offbits" -  we need 'em for the
       * palette size - e.g. XV uses different sizes
       */
      palsize=(palsize-54)/4;
    }
  else                         /*  OS/2 V1.1       */
    {
      pp->width = w = bch.bcWidth;
      pp->height = h = bch.bcHeight;
      pp->bpp = bch.bcBitCount;
      palsize=(palsize-26)/3;
      
      /* in case these are used later -rjm */
      bih.biWidth=bch.bcWidth;
      bih.biHeight=bch.bcHeight;
    }
  
  if (w == 0 || h == 0 || palsize > 256)
    CLOSE_AND_RET(_PICERR_CORRUPT);

  if ((pp->bpp >> 3) < 3)
    *output_type = 1;

  /* w is the size of a horizontal line in bytes
   * bih.biWidth is the number of valid pixels in a line
   * the latter one is passed to vgadisp.c as pp->width
   */
  switch (pp->bpp)
    {
    case 1:
      if (w % 32)
        w = (w / 32) * 32 + 32;
      break;
    case 4:
      if (w % 8)
      w = (w / 8) * 8 + 8;
      break;
    case 8:
      if (w % 4)
      w = (w / 4) * 4 + 4;
      break;
    case 24:
      if ((w * 3) % 4)
      w = (w * 3 / 4) * 4 + 4;
      else
      w = w * 3;
      break;
    default:
      CLOSE_AND_RET(_PICERR_CORRUPT);
    }

  if (w == 0 || h == 0)
    CLOSE_AND_RET(_PICERR_CORRUPT);

  if ((*pal = calloc (768,1)) == NULL)
    CLOSE_AND_RET(_PICERR_NOMEM);

  bytepp=1;
  if ((pp->bpp == 24) && (*output_type == 3))
    bytepp = 3;
  if (WH_BAD(w,h) ||
      (work_bmap = *bmap = calloc (w * (h + 2) * bytepp,1)) == NULL)
    CLOSE_AND_RET(_PICERR_NOMEM);

  bytes_in_image=w*h*bytepp;

  /* this should be used whenever adding a file-input-dependent amount
   * to the current position pointer. It's > rather than >= so that we
   * can point *just* past the end having read in data, which is ok.
   */
#define FAIL_FOR_BAD_OFFSET(cur,add) \
  do {if((cur)+(add)>bytes_in_image) CLOSE_AND_RET(_PICERR_CORRUPT);} while(0)
  
  /* if you look at the stuff below, `p' is the palette
   * which would be right to free by the time we're actually
   * reading the image, so...
   */
  work_pal=p;

  free(buf);

  switch (pp->bpp)
    {
    case 1:
      /* 1bit non compressed */
      ptr = *pal;
      fread (ptr , 1, 3, in);
      if (iswindows)
        fread (&dummy, 1, 1, in);
      fread (ptr + 3, 1, 3, in);
      if (iswindows)
        fread (&dummy, 1, 1, in);
      for (i = 0; i < 2; i++)
        {
          p[i] = ptr[3 * i + 2];
          p[i + 256] = ptr[3 * i + 1];
          p[i + 512] = ptr[3 * i];
        }
      free (*pal);
      *pal = p;
      ptr = *bmap;
      for (j = h - 1; j >= 0; j--)
        for (i = 0, count=0 ; i < (w >> 3); i++)
          {
            hptr = ptr + j * pp->width;
            dummy = fgetc (in);
            if (count < pp->width)
              {
                hptr[count] = (dummy & 128)?1:0;count++;
                hptr[count] = (dummy & 64)?1:0;count++;
                hptr[count] = (dummy & 32)?1:0;count++;
                hptr[count] = (dummy & 16)?1:0;count++;
                hptr[count] = (dummy & 8)?1:0;count++;
                hptr[count] = (dummy & 4)?1:0;count++;
                hptr[count] = (dummy & 2)?1:0;count++;
                hptr[count] = dummy & 1;count++;
              }
            if (howfarfunc)
              howfarfunc (h-j, h);
          }
      pp->numcols=2;
      break;
    case 4:
      /* 4bit non compressed */
      ptr = *pal;
      for (i = 0; i < palsize; i++)
      {
        fread (ptr + 3 * i, 1, 3, in);
        if (iswindows)
          fread (&dummy, 1, 1, in);
      }
      for (i = 0; i < palsize; i++)
      {
        p[i] = ptr[3 * i + 2];
        p[i + 256] = ptr[3 * i + 1];
        p[i + 512] = ptr[3 * i];
      }
      free (*pal);
      *pal = p;
      ptr = *bmap;
      if ((!iswindows) || (bih.biCompression == 0))
      {
        for (j = h - 1; j >= 0; j--)
          for (i = 0, count = 0; i < (w / 2); i++)
            {
            dummy = fgetc (in);
            if (count < pp->width)
              {
                ptr[count + j * pp->width] = dummy >> 4;
                count++;
              }
            if (count < pp->width)
              {
                ptr[count + j * pp->width] = dummy & 15;
                count++;
              }
            }
      }
      else
      {
        /* 4bit RLE compressed */
        done = 0;
        count = 0;
        while (done == 0)
          {
            fread (read, 1, 2, in);
            if (*read)
            {
                  FAIL_FOR_BAD_OFFSET(ptr-*bmap,*read);
              i = 0;
              do
                {
                  *ptr = read[1] >> 4;
                  ptr++;
                  i++;
                  if (i < (read[0]))
                  {
                    *ptr = read[1] & 15;
                    ptr++;
                    i++;
                  }
                }
              while (i < (*read));
            }
            else if (read[1] == 0)
            {
              if (howfarfunc)
                howfarfunc (count, h);
              count++;
            }
            else if (read[1] == 1)
            done = 1;
            else if (read[1] == 2)
            {
                int ofs=fgetc (in);
                int ytmp=fgetc (in);
                
                ofs+=bih.biWidth * ytmp;
                FAIL_FOR_BAD_OFFSET(ptr-*bmap,ofs);
            ptr += ofs;
                count += ytmp; if(howfarfunc) howfarfunc(count,h);
            }
            else
            {
                  FAIL_FOR_BAD_OFFSET(ptr-*bmap,read[1]);
              dptr = hptr = malloc (((read[1]+3)>>2) << 1);
              fread (dptr, 1, ((read[1]+3)>>2) << 1, in);
              i = 0;
              do
                {
                  *ptr = (*dptr) >> 4;
                  i++;
                  ptr++;
                  if (i < read[1])
                  {
                    *ptr = (*dptr) & 15;
                    i++;
                    ptr++;
                  }
                  dptr++;
                }
              while (i < read[1]);
              free (hptr);
            }
          }
        if (_PICERR_NOMEM == flip (*bmap, bih.biWidth, bih.biHeight))
          CLOSE_AND_RET(_PICERR_NOMEM);
      }
      /*pp->width = w;*/            /* believed bogus -rjm 2001 Apr 10 */
      pp->numcols= 16;
      break;

    case 8:
      /* 8bit non compressed */
      ptr = *pal;
      for (i = 0; i < palsize; i++)
      {
        fread (ptr + 3 * i, 1, 3, in);
        if (iswindows)
          dummy = fgetc (in);
      }
      for (i = 0; i < palsize; i++)
      {
        p[i] = ptr[3 * i + 2];
        p[i + 256] = ptr[3 * i + 1];
        p[i + 512] = ptr[3 * i];
      }
      free (*pal);
      *pal = p;
      ptr = *bmap;
      if ((!iswindows) || (bih.biCompression == 0))
      for (i = h - 1; i >= 0; i--)
        {
          fread (ptr + pp->width * i, 1, pp->width, in);
          if (howfarfunc)
            howfarfunc (h - i, h);
            if(w>pp->width)
              for(j=pp->width;j<w;j++)
                fgetc(in);
        }
      else
      /* 8bit RLE compressed */
      {
        done = 0;
        count = 0;
        while (done == 0)
          {
            fread (read, 1, 2, in);
            if (read[0])
                {
                FAIL_FOR_BAD_OFFSET(ptr-*bmap,read[0]);
            for (i = 0; i < (int) read[0]; i++)
              {
                *ptr = read[1];
                ptr++;
              }
                }
            else if (read[1] == 0)
            {
              if (howfarfunc)
                howfarfunc (count, h);
              count++;
            }
            else if (read[1] == 1)
            done = 1;
            else if (read[1] == 2)
                {
                int ofs=fgetc (in);
                int ytmp=fgetc (in);
                
                ofs+=bih.biWidth * ytmp;
                FAIL_FOR_BAD_OFFSET(ptr-*bmap,ofs);
            ptr += ofs;
                count += ytmp; if(howfarfunc) howfarfunc(count,h);
                }
            else
            {
                FAIL_FOR_BAD_OFFSET(ptr-*bmap,read[1]);
            fread (ptr, 1, read[1], in);
                if (read[1] % 2)
                  fgetc (in);
                ptr += read[1];
            }
          }
        if (_PICERR_NOMEM == flip (*bmap, bih.biWidth, bih.biHeight))
          CLOSE_AND_RET(_PICERR_NOMEM);
      }
      pp->numcols= 256;
      break;

    case 24:
      /* true color */
      /* skip any bogus `palette' */
      for (i = 0; i < palsize; i++)
      {
        fgetc(in); fgetc(in); fgetc(in);
        if(iswindows) fgetc(in);
      }
      /* don't need temp palette at all, just transfer the 8-bit-dither one */
      free (*pal);
      *pal = p;
      ptr = *bmap;
      if ((*output_type) == 1 || cfg.jpeg24bit==0)
      {
          *output_type=1;
        /* Truecolor BMPs and 8-bit display */
        if (ditherinit (pp->width) == 0)
          CLOSE_AND_RET(_PICERR_NOMEM);
        for (i = 1; i <= h; i++)
          {
            hptr = ptr + pp->width * i;
            fread (hptr, 1, w, in);
            ditherline (hptr, i, pp->width);
            if (howfarfunc)
            howfarfunc (i, h);
          }
        ditherfinish ();
        if (_PICERR_NOMEM == flip (*bmap, pp->width, pp->height))
          CLOSE_AND_RET(_PICERR_NOMEM);
        pp->bpp = 8;
          pp->numcols= 256;
      }
      else
      for (i = h - 1; i >= 0; i--)
        {
          fread (ptr + pp->width * 3 * i, 1, w, in);
          if (howfarfunc)
            howfarfunc (h - i, h);
        }
      break;
    }

  fclose (in);
  return (_PIC_OK);
}

static int flip (unsigned char * image, unsigned int w, unsigned int h)
{
  unsigned int i;
  unsigned char *hptr;

  if ((hptr = malloc (w)) == 0)
    return (_PICERR_NOMEM);
  for (i = 0; i < (h / 2); i++)
    {
      memcpy (hptr, image + i * w, w);
      memcpy (image + i * w, image + (h - i - 1) * w, w);
      memcpy (image + (h - i - 1) * w, hptr, w);
    }
  free (hptr);
  return (_PIC_OK);
}

void aborted_file_bmp_cleanup ()
{
  free (work_bmap);
  free (work_pal);
  fclose (work_in);
}

Generated by  Doxygen 1.6.0   Back to index