/******************************************************************************/
/*                                                                            */
/*  DIBIMAGE - All routines for class DIBimage for DIB manipulation           */
/*                                                                            */
/******************************************************************************/

#define STRICT
#include <windows.h>
#include <commctrl.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include "const.h"
#include "classes.h"
#include "funcdefs.h"
#include "lin.rh"
#include "extern.h"

/*
--------------------------------------------------------------------------------

   Constructors and destructor

--------------------------------------------------------------------------------
*/

DIBimage::DIBimage ()  // Never used
{
    nr = nc = nb = -1 ;
}

DIBimage::DIBimage (
   int nrows ,           // Number of rows, top to bottom
   int ncols ,           // Number of columns, each nbytes bytes (R, G, B)
   int nbytes ,          // Bytes per pixel, 1 for B&W, 3 for RGB color
   unsigned char *image  // Image is here, nrows * ncols * nbytes long
   )
{
   int i ;

   nr = nrows ;
   nc = ncols ;
   nb = nbytes ;

/*
   rgb_size is the number of color table entries.
   True color does not have a color table.
*/

   if (nb == 1)            // Black-and-white
      rgb_size = 256 ;
   else if (nb == 3)       // True color
      rgb_size = 0 ;
   else {
      nr = nc = nb = -1 ;
      return ;
      }

/*
   Allocate memory for the BITMAPINFO object.
   It has two members.  The first is the BITMAPINFOHEADER.
   The second is the RGBQUAD color table, which is not used for true color.
*/

   bit_map_info = (BITMAPINFO *) malloc ( sizeof(BITMAPINFOHEADER) +
                                          rgb_size * sizeof(RGBQUAD) ) ;
   if (bit_map_info == NULL) {
      nr = nc = nb = -1 ;
      return ;
      }

   bit_map_info_header = (BITMAPINFOHEADER *) bit_map_info ;

/*
   Each scan line must start on a LONG boundary.
   So we allocate enough memory for any padding necessary at the end of each.
*/

   row_bytes = (nc * nb + sizeof(LONG) - 1) / sizeof(LONG) * sizeof(LONG) ;

   img = (unsigned char *) malloc ( nr * row_bytes ) ;
   if (img == NULL) {
      nr = nc = nb = -1 ;
      free ( bit_map_info ) ;
      bit_map_info = NULL ;
      return ;
      }

   memset ( img , 0 , nr * row_bytes ) ;

   for (i=0 ; i<nr ; i++)
      memcpy ( img + i * row_bytes , image + i * nc * nb , nc * nb ) ;

/*
   Fill in the BITMAPINFOHEADER members.
*/

   memset ( bit_map_info_header , 0 , sizeof(BITMAPINFOHEADER) ) ;

   bit_map_info_header->biSize = sizeof(BITMAPINFOHEADER) ;
   bit_map_info_header->biWidth = nc ;
   bit_map_info_header->biHeight = -nr ;  // Negative makes it top-to-bottom
   bit_map_info_header->biPlanes = 1 ;
   bit_map_info_header->biBitCount = (char) (8 * nb) ;
   bit_map_info_header->biCompression = BI_RGB ;
   bit_map_info_header->biSizeImage = 0 ;  // OK for BI_RGB
   bit_map_info_header->biXPelsPerMeter = 0 ;
   bit_map_info_header->biYPelsPerMeter = 0 ;
   bit_map_info_header->biClrUsed = 0 ;
   bit_map_info_header->biClrImportant = 0 ;  // Uses full RGB table

/*
   Fill in the RGBQUAD color table if it is used
*/

   if (nb == 1) {
      rgb_quad = (RGBQUAD *) ((char *) bit_map_info + sizeof(BITMAPINFOHEADER));
      for (i=0 ; i<256 ; i++) {
         rgb_quad[i].rgbBlue = rgb_quad[i].rgbGreen = rgb_quad[i].rgbRed = (char) i ;
         rgb_quad[i].rgbReserved = 0 ;
         }
      }
}

DIBimage::~DIBimage ()
{
   if (bit_map_info != NULL)
      free ( bit_map_info ) ;

   if (img != NULL)
      free ( img ) ;
}

/*
--------------------------------------------------------------------------------

   draw - Draw the DIB.  Called in response to WM_PAINT as well as directly
          from CB_FRAME.CPP in response to a user PRINT command

--------------------------------------------------------------------------------
*/

int DIBimage::draw ( HWND hwnd , int printing )
{
   int printer_rows, printer_cols, rows_printed, cols_printed ;
   HDC hdc ;
   PAINTSTRUCT ps ;
   RECT rect ;

   if (printing) {
      hdc = print_dlg.hDC ;
      printer_rows = GetDeviceCaps ( hdc , VERTRES ) ;
      printer_cols = GetDeviceCaps ( hdc , HORZRES ) ;
      cols_printed = (int) (printer_cols * 0.01 * print_params.fraction + 0.5) ;
      if (cols_printed < 1)             // Should never happen, but be safe
         cols_printed = 1 ;
      if (cols_printed > printer_cols)  // Ditto
         cols_printed = printer_cols ;
      rows_printed = (int) (cols_printed * 0.01 * print_params.vert_scaling *
                            nr / nc + 0.5 ) ;
      if (rows_printed < 1)
         rows_printed = 1 ;
      if (rows_printed > printer_rows)
         rows_printed = printer_rows ;
      rect.left = (printer_cols - cols_printed) / 2 ;  // Center it
      rect.right = cols_printed ;
      rect.top = (printer_rows - rows_printed) / 2 ;
      rect.bottom = rows_printed ;
      }

   else {
      InvalidateRect ( hwnd , NULL , TRUE ) ; // Erase when BeginPaint called
      hdc = BeginPaint ( hwnd , &ps ) ;
      GetClientRect ( hwnd , &rect ) ;
      }

   SetStretchBltMode ( hdc , COLORONCOLOR ) ;
   SetMapMode ( hdc , MM_TEXT ) ;
   StretchDIBits ( hdc , rect.left , rect.top , rect.right , rect.bottom ,
                   0 , 0 , nc , nr , img , bit_map_info ,
                   DIB_RGB_COLORS , SRCCOPY ) ;

   if (! printing)
      EndPaint ( hwnd , &ps ) ;

   return 0 ;
}

/*
--------------------------------------------------------------------------------

   Miscellaneous functions

--------------------------------------------------------------------------------
*/

int DIBimage::valid ()
{
   return (nr > 0) ;
}
