/***********************************************************************/
/*                                                                     */
/* Project      : GRAV                                                 */
/* Current Rel. : 3.1                                                  */
/* Creator      : Michael Knigge                                       */
/* Creation Date: 02/06/95                                             */
/* Description  : change Bits-per-Pixel of bitmap                      */
/*                                                                     */
/* Functions    : grav_1bpp_to_8bpp()                                  */
/*                grav_4bpp_to_8bpp()                                  */
/*                grav_24bpp_to_8bpp()                                 */
/*                grav_24bpp_to_15bpp()                                */
/*                grav_24_bpp_to_16bpp()                               */
/*                grav_transform()                                     */
/*                                                                     */
/***********************************************************************/

#include <stdio.h>
#include <malloc.h>
#include <ncurses.h>
#include <vga.h>
#include "defines.h"
#include "standard.h"
#include "gbm.h"
#include "trans.h"
#include "error.h"

#define MAKE_15_BIT(RED,GREEN,BLUE) (((RED>>3)<<10)|((GREEN>>3)<<5)|(BLUE>>3))
#define MAKE_16_BIT(RED,GREEN,BLUE) (((RED>>3)<<11)|((GREEN>>2)<<5)|(BLUE>>3))

char *grav_transform(int *error_id, char *bitmap, GBM *gbm, GBMRGB gbmrgb[], int mode_index, GMODE gmode[])
{
   char *new_bitmap;
   int  rc;

   /*
    * in the first part of this function we will check if the read
    * picture can be displayed using the current video-mode. if not,
    * we will return GRAV_ERROR_COLOR. in the second part we check if
    * we have to transform the read picture so it can be displayed.
    *
    * so, let's start....
    *
    * if we got a 24bit picture and the current video-mode allows less
    * than 256 colors, we can't display this picture....
    */
    
   if (gbm->bpp == 24 && gmode[mode_index].colors < 256)
   {
      *error_id = GRAV_ERROR_COLORS;
      return(NULL);
   }

   /*
    * the same story if we got a 8bit picture and we are using only
    * a 16 color mode...
    */
    
   if (gbm->bpp == 8 && gmode[mode_index].colors < 256)
   {
      *error_id = GRAV_ERROR_COLORS;
      return(NULL);
   }

   /*
    * so, from here we check if we have to transform the graphic...
    *
    * let's start....
    */

  if (gbm->bpp == 8)
  {
     *error_id = GRAV_NO_ERROR;
     return(NULL);
  }    

  /*
   * if we have a b/w bitmap, let's transform to a 8bit bitmap
   * SEE THE NOTE UNDER THIS if() !!!
   */
          
  if (gbm->bpp == 1)
     new_bitmap = grav_1bpp_to_8bpp(&rc, bitmap, gbm);

  /*
   * if we have a 16 color bitmap we have to transform the 
   * bitmap to a 8bit bitmap.  NOTE: If we use a 16 color
   * video-mode, SVGALIB uses a 8-bit-per-pixel string for
   * vga_drawscansegment so we have to transform the 4bit bitmap
   * IN ALL CASES TO 8 BIT!
   */
          
  if (gbm->bpp == 4)
     new_bitmap = grav_4bpp_to_8bpp(&rc, bitmap, gbm);

  /*
   * if the current video-mode can't display hi- oder truecolor
   * we have to generate a 8bit bitmap...
   */
             
  if (gbm->bpp == 24)
  {
     switch(gmode[mode_index].colors)
     {
     case 256:    new_bitmap = grav_24bpp_to_8bpp(&rc, bitmap, gbm, gbmrgb);
                  if (rc != GRAV_NO_ERROR)
                  {
                     *error_id = rc;
                     return(NULL);
                  }
                  break;
                
     case 32768:  new_bitmap = grav_24bpp_to_15bpp(&rc, bitmap, gbm);
                  if (rc != GRAV_NO_ERROR)
                  {
                     *error_id = rc;
                     return(NULL);
                  }
                  break;
                  
     case 65536:  new_bitmap = grav_24bpp_to_16bpp(&rc, bitmap, gbm);
                  if (rc != GRAV_NO_ERROR)
                  {
                     *error_id = rc;
                     return(NULL);
                  }
                  break;
                  
     default:     *error_id = GRAV_NO_ERROR;
                  return(NULL);
      }
   }
   
   *error_id = GRAV_NO_ERROR;
   return(new_bitmap);
}

char *grav_1bpp_to_8bpp(int *error_id, char *bitmap, GBM *gbm)
{
   int  lines;
   int  cols;
   int  pixel;
   int  pos;
   int  new_size;
   int  old_width;
   int  new_width;
   
   unsigned char *old_work_bitmap;
   unsigned char *new_work_bitmap;
   unsigned char *new_bitmap;


   /*
    * first calculate the size of the new bitmap...
    */

   new_size = gbm->h * (((gbm->w * 8 + 31) / 32) * 4);

   /*
    * allocate memory for the bitmap...
    */
    
   if ((new_bitmap = malloc((int) new_size)) == NULL)
   {
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }  
   
   /*
    * initialize the bitmap
    */
    
   memset(new_bitmap, 0, new_size);

   /*
    * set some variables....
    */
    
   old_width       = grav_get_bitmap_width(gbm);
   gbm->bpp        = 8;
   new_width       = grav_get_bitmap_width(gbm);
   old_work_bitmap = bitmap;
   new_work_bitmap = new_bitmap;

   
   for (lines = 0; lines < gbm->h; lines++)
   {
      for (cols = 0; cols < (gbm->w+7)/8; cols++)
      {
         pos = 0;
         for(pixel = 128; pixel > 0; pixel = pixel / 2)
         {
            if ((old_work_bitmap[cols] & pixel) != 0)
               new_work_bitmap[pos] = 0x01;
            else
               new_work_bitmap[pos] = 0x00;
            pos++;
         }
         new_work_bitmap = new_work_bitmap + 8;
      }
      old_work_bitmap = old_work_bitmap + old_width;
      new_work_bitmap = new_bitmap + ((lines + 1) * new_width);
   }

   *error_id = GRAV_NO_ERROR;
   
   return(new_bitmap);
}


char *grav_4bpp_to_8bpp(int *error_id, char *bitmap, GBM *gbm)
{
   int  lines;
   int  cols;
   int  new_size;
   int  old_width;
   int  new_width;
   
   unsigned char *old_work_bitmap;
   unsigned char *new_work_bitmap;
   unsigned char *new_bitmap;


   /*
    * first calculate the size of the new bitmap...
    */

   new_size = gbm->h * (((gbm->w * 8 + 31) / 32) * 4);

   /*
    * allocate memory for the bitmap...
    */
    
   if ((new_bitmap = malloc((int) new_size)) == NULL)
   {
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }  
   
   /*
    * initialize the bitmap
    */
    
   memset(new_bitmap, 0, new_size);

   /*
    * set some variables....
    */
    
   old_width       = grav_get_bitmap_width(gbm);
   gbm->bpp        = 8;
   new_width       = grav_get_bitmap_width(gbm);
   old_work_bitmap = bitmap;
   new_work_bitmap = new_bitmap;

   
   for (lines = 0; lines < gbm->h; lines++)
   {
      for (cols = 0; cols < (gbm->w+1)/2; cols++)
      {
         new_work_bitmap[1] = old_work_bitmap[0] << 4;
         new_work_bitmap[1] = new_work_bitmap[1] >> 4;
         new_work_bitmap[0] = old_work_bitmap[0] >> 4;
         
         old_work_bitmap++;
         new_work_bitmap = new_work_bitmap + 2;
      }
      old_work_bitmap = bitmap + ((lines + 1) * old_width);
      new_work_bitmap = new_bitmap + ((lines + 1) * new_width);
   }

   *error_id = GRAV_NO_ERROR;
   
   return(new_bitmap);
}

char *grav_24bpp_to_8bpp(int *error_id, char *bitmap, GBM *gbm, GBMRGB gbmrgb[])
{
   int  new_size;
   int  old_width;
   int  new_width;
   
   unsigned char *new_bitmap;

   /*
    * first calculate the size of the new bitmap...
    */

   new_size = gbm->h * (((gbm->w * 8 + 31) / 32) * 4);

   /*
    * allocate memory for the bitmap...
    */
    
   if ((new_bitmap = malloc((int) new_size)) == NULL)
   {
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }  
   
   /*
    * initialize the bitmap
    */
    
   memset(new_bitmap, 0, new_size);
   
   /*
    * and do the transform....
    */
    
   gbm->bpp   = 8;
   *error_id  = GRAV_NO_ERROR;

   gbm_hist(gbm, bitmap, gbmrgb, new_bitmap, 256, 255, 255, 255);
   
   return(new_bitmap);
}

char *grav_24bpp_to_16bpp(int *error_id, char *bitmap, GBM *gbm)
{
   int  new_size;
   int  old_width;
   int  new_width;
   int  lines;
   int  cols;
   GBM  temp_gbm;
   
   unsigned char  *new_bitmap;
   unsigned char  *old_work_bitmap;
   unsigned char  *new_work_bitmap;   
   unsigned short *temp_field;

   /*
    * save the current GBM-Structure....
    */

   temp_gbm.w    = gbm->w;
   temp_gbm.h    = gbm->h;
   temp_gbm.bpp  = gbm->bpp;

   /*
    * first calculate the size of the new bitmap...
    */

   new_size = temp_gbm.h * (((temp_gbm.w * 24 + 31) / 32) * 4);

   /*
    * allocate memory for the bitmap...
    */
    
   if ((new_bitmap = malloc((int) new_size)) == NULL)
   {
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }  
   
   /*
    * initialize the bitmap
    */
    
   memset(new_bitmap, 0, new_size);
   
   /*
    * and do the transform....
    */

   old_width       = grav_get_bitmap_width(gbm);
   new_width       = grav_get_bitmap_width(&temp_gbm);
   old_work_bitmap = bitmap;
   new_work_bitmap = new_bitmap;
   
   for (lines = 0; lines < gbm->h; lines++)
   {
      temp_field = (unsigned short *) new_work_bitmap;
      for (cols = 0; cols < gbm->w * 3; cols = cols + 3)
      {
         *temp_field = MAKE_16_BIT(old_work_bitmap[cols + 2],
                                   old_work_bitmap[cols + 1],
                                   old_work_bitmap[cols]);
         temp_field++;
      }
      old_work_bitmap = old_work_bitmap + old_width;
      new_work_bitmap = new_bitmap + ((lines + 1) * new_width);
   }

    
   *error_id  = GRAV_NO_ERROR;
   return(new_bitmap);
}

char *grav_24bpp_to_15bpp(int *error_id, char *bitmap, GBM *gbm)
{
   int  new_size;
   int  old_width;
   int  new_width;
   int  lines;
   int  cols;
   GBM  temp_gbm;
   
   unsigned char  *new_bitmap;
   unsigned char  *old_work_bitmap;
   unsigned char  *new_work_bitmap;   
   unsigned short *temp_field;

   /*
    * save the current GBM-Structure....
    */

   temp_gbm.w    = gbm->w;
   temp_gbm.h    = gbm->h;
   temp_gbm.bpp  = gbm->bpp;

   /*
    * first calculate the size of the new bitmap...
    */

   new_size = temp_gbm.h * (((temp_gbm.w * 24 + 31) / 32) * 4);

   /*
    * allocate memory for the bitmap...
    */
    
   if ((new_bitmap = malloc((int) new_size)) == NULL)
   {
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }  
   
   /*
    * initialize the bitmap
    */
    
   memset(new_bitmap, 0, new_size);
   
   /*
    * and do the transform....
    */

   old_width       = grav_get_bitmap_width(gbm);
   new_width       = grav_get_bitmap_width(&temp_gbm);
   old_work_bitmap = bitmap;
   new_work_bitmap = new_bitmap;
   
   for (lines = 0; lines < gbm->h; lines++)
   {
      temp_field = (unsigned short *) new_work_bitmap;
      for (cols = 0; cols < gbm->w * 3; cols = cols + 3)
      {
         *temp_field = MAKE_15_BIT(old_work_bitmap[cols + 2],
                                   old_work_bitmap[cols + 1],
                                   old_work_bitmap[cols]);
         temp_field++;
      }
      old_work_bitmap = old_work_bitmap + old_width;
      new_work_bitmap = new_bitmap + ((lines + 1) * new_width);
   }

    
   *error_id  = GRAV_NO_ERROR;
   return(new_bitmap);
}


