/**********************************************************************
 * File:			imgbmp.c  (Formerly lz.c)
 * Description:	bmp image reader/writer.
 * Author:		Ray Smith
 * Created:		Tue Jan 06 15:31:25 GMT 1998
 *
 * (C) Copyright 1998, Ray Smith.
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 ** http://www.apache.org/licenses/LICENSE-2.0
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 *
 **********************************************************************/

#include          "mfcpch.h"     //precompiled headers
#ifdef __MSW32__
#include          <io.h>
#else
#include          <unistd.h>
#endif
#include          <stdio.h>
#include          "img.h"
#include          "imgbmp.h"

typedef struct
{                                // bmfh
  char bfType1;                  //'B'
  char bfType2;                  //'M'
} BMPHEADER0;
typedef struct
{                                // bmfh
  uinT32 bfSize;                 //filesize
  uinT16 bfReserved1;            //zero
  uinT16 bfReserved2;            //zero
  uinT32 bfOffBits;              //offset to bitmap
} BMPHEADER;

typedef struct
{                                // bmih
  uinT32 biSize;                 //size of struct
  inT32 biWidth;                 //image width
  inT32 biHeight;                //image height
  uinT16 biPlanes;               //1
  uinT16 biBitCount;             //bpp
  uinT32 biCompression;          //0 for uncompressed
  uinT32 biSizeImage;            //image size
  inT32 biXPelsPerMeter;         //res in pp metre
  inT32 biYPelsPerMeter;
  uinT32 biClrUsed;              //0 or actual size of colour table
  uinT32 biClrImportant;         //usually 0
} BMPHEADER2;

typedef struct
{                                // rgbq
  uinT8 rgbBlue;
  uinT8 rgbGreen;
  uinT8 rgbRed;
  uinT8 rgbReserved;             //0
} WIN32_RGBQUAD;

/**********************************************************************
 * open_bmp_image
 *
 * Read the header of a bmp format image and prepare to read the rest.
 **********************************************************************/

inT8 open_bmp_image(               //read header
                    int fd,        //file to read
                    inT32 *xsize,  //size of image
                    inT32 *ysize,
                    inT8 *bpp,     //bits per pixel
                    inT8 *photo,
                    inT32 *res     //resolution
                   ) {
  uinT32 nread;                  //current bits
  BMPHEADER0 head0;              //first part of header
  BMPHEADER head1;               //first part of header
  BMPHEADER2 head2;              //first part of header

  *photo = 1;
  nread = read (fd, &head0, sizeof (head0));
  if (nread != sizeof (head0))
    return -1;
  nread = read (fd, &head1, sizeof (head1));
  if (nread != sizeof (head1))
    return -1;
  nread = read (fd, &head2, sizeof (head2));
  if (nread != sizeof (head2))
    return -1;

  if (head0.bfType1 != 'B')
    return -1;
  if (head0.bfType2 != 'M')
    return -1;
  lseek (fd, head1.bfOffBits, SEEK_SET);
  *bpp = head2.biBitCount;
  *xsize = head2.biWidth;
  *ysize = head2.biHeight;
  *res = 300;                    //make up resolution
  return -2;                     //success
}


/**********************************************************************
 * read_bmp_image
 *
 * Read a whole lz format image and close the file.
 **********************************************************************/

inT8 read_bmp_image(                //read header
                    int fd,         //file to read
                    uinT8 *pixels,  //pixels of image
                    inT32 xsize,    //size of image
                    inT32 ysize,
                    inT8 bpp,       //bits per pixel
                    inT32           //bytes per line
                   ) {
  uinT32 bpl;                    //bytes per line
  uinT32 wpl;                    //words per line
  uinT32 nread;                  //current bits
  inT32 index;                   //to cols

  bpl = (xsize * bpp + 7) / 8;   //bytes per line
  wpl = (bpl + 3) / 4;
  wpl *= 4;
  for (index = 0; index < ysize; index++) {
    nread = read (fd, pixels + bpl * (ysize - 1 - index), bpl);
    if (nread != bpl)
      return -1;
    if (wpl != bpl)
      lseek (fd, wpl - bpl, SEEK_CUR);
  }
  return 0;
}


/**********************************************************************
 * write_bmp_image
 *
 * Write a whole lz format image and close the file.
 **********************************************************************/

inT8 write_bmp_image(                //write whole image
                     int fd,         //file to write on
                     uinT8 *pixels,  //image pixels
                     inT32 xsize,    //size of image
                     inT32 ysize,
                     inT8 bpp,       //bits per pixel
                     inT8,
                     inT32 res       //resolution
                    ) {
  uinT32 bpl;                    //bytes per line
  uinT32 wpl;                    //words per line
  uinT32 nread;                  //current bits
  inT32 cols;                    //entries in table
  inT32 index;                   //to cols
  BMPHEADER0 head0;              //first part of header
  BMPHEADER head1;               //first part of header
  BMPHEADER2 head2;              //first part of header
  WIN32_RGBQUAD coltab[256];     //colour table

  if (bpp == 24)
    cols = 0;
  else
    cols = 1 << bpp;             //size of colour table
  bpl = (xsize * bpp + 7) / 8;   //bytes per line
  wpl = (bpl + 3) / 4;

  head2.biSize = sizeof (head2); //size of struct
  head2.biWidth = xsize;         //image width
  head2.biHeight = ysize;        //image height
  head2.biPlanes = 1;            //1
  head2.biBitCount = bpp;        //bpp
  head2.biCompression = 0;       //0 for uncompressed
                                 //image size
  head2.biSizeImage = wpl * 4 * ysize;
                                 //res in pp metre
  head2.biXPelsPerMeter = (uinT32) (res * 39.37);
  head2.biYPelsPerMeter = (uinT32) (res * 39.37);
  head2.biClrUsed = cols;        //0 or actual size of colour table
  head2.biClrImportant = 0;      //usually 0

  head0.bfType1 = 'B';
  head0.bfType2 = 'M';
  head1.bfReserved1 = 0;         //zero
  head1.bfReserved2 = 0;         //zero
                                 //offset to bitmap
  head1.bfOffBits = sizeof (head0) + sizeof (head1) + sizeof (head2) + sizeof (WIN32_RGBQUAD) * cols;
                                 //filesize
  head1.bfSize = head1.bfOffBits + head2.biSizeImage;

  for (index = 0; index < cols; index++) {
    coltab[index].rgbBlue = index * 255 / (cols - 1);
    coltab[index].rgbGreen = coltab[index].rgbBlue;
    coltab[index].rgbRed = coltab[index].rgbBlue;
    coltab[index].rgbReserved = 0;
  }

  nread = write (fd, &head0, sizeof (head0));
  if (nread != sizeof (head0))
    return -1;
  nread = write (fd, &head1, sizeof (head1));
  if (nread != sizeof (head1))
    return -1;
  nread = write (fd, &head2, sizeof (head2));
  if (nread != sizeof (head2))
    return -1;
  nread = write (fd, coltab, cols * sizeof (WIN32_RGBQUAD));
  if (nread != cols * sizeof (WIN32_RGBQUAD))
    return -1;
  for (index = 0; index < ysize; index++) {
    nread = write (fd, pixels + bpl * (ysize - 1 - index), wpl * 4);
    if (nread != wpl * 4)
      return -1;
  }
  close(fd);  //done it
  return 0;
}
