Gamma/LUT correction

Source code

//=======================================================================
// Corrections: gamma, luminosity, contrast,... with LUT.
// QUAGLIOZZI ERIC - © 2009   All rights reserved
//=======================================================================

/*
   Usable for low res (72 dpi) and double res (144 dpi) 
   bitmap  16 BIT COLORS ONLY.
   ---------------------------------------------------------
   | Do not use with other resolutions, may system crash ! |
   ---------------------------------------------------------
*/



//--------------------------- Macros and constants ----------------------

#if defined(_MSC_VER)     // If compiling with VC++...

    #define private        static __inline
    #define longint        __int64
    
#elif defined(__GNUC__)  // If compiling with GCC...

    #define private        static inline
    #define longint        long long int
    
#else

    #error "Unknown Compiler!"
    
#endif

//endianness conversions (16 bit and 32 bit)
#define EndianSwap16(n) (((((unsigned int) n) << 8) & 0xFF00) | \
                         ((((unsigned int) n) >> 8) & 0x00FF))
                         
#define EndianSwap32(n) (((((unsigned long) n) << 24) & 0xFF000000) | \
                         ((((unsigned long) n) <<  8) & 0x00FF0000) | \
                         ((((unsigned long) n) >>  8) & 0x0000FF00) | \
                         ((((unsigned long) n) >> 24) & 0x000000FF))

#define FixedPoint 12    // 12 bit fixed point operations: 20:12
#define ONEHALF 0x00800  // 0.5 , 12 bit fixed point

#define MAXGRAY    63
#define TABLESIZE  32

// Luminance = 0.3R + 0.59G + 0.11B
#define Rc  0x04CD    // 0.3*4096 <=> 0.3 in 12 bit fixed point
#define Gc  0x0971    // 0.59*4096 <=> 0.59 in 12 bit fixed point
#define Bc  0x01C2    // 0.11*4096 <=> 0.11 in 12 bit fixed point

// Action kind
#define ACT_LUTCORRECTION 0 // ask for LUT correction
#define ACT_TESTGRAYSCALE 1 // ask for grayscale test
#define ACT_DOGRAYSCALE   2 // perform grayscale conversion
#define ACT_DOCHANNELSSEPARATION   3 // perform channel separation

// Options
#define OPT_GRAYSCALEMODE 0x0001 // activate grayscale mode

//--------------------------- Types -----------------------------------


typedef unsigned long Call68KFuncType(const void * emulStateP,
                                      unsigned long trapOrFunction,
                                      const void * argsOnStackP,
                                      unsigned long argsSizeAndwantA0);

typedef unsigned long NativeFuncType(const void * emulStateP,
                                     void * userData68KP,
                                     Call68KFuncType * call68KFuncP);

typedef struct {
    char hb_object_header[14];
    short swTransparent;
    unsigned char    * pData;
    short w;
    short wl;
    short h;
    short bitdepth;
    unsigned char swTranspColorH;
    unsigned char swTranspColorL;
    unsigned char swTranspIndex;
} ImageData;


typedef struct {
    long DATA[TABLESIZE];
} LUT;


typedef struct {
    char hb_object_header[14];
    short         debug;              // for padding and debug...
    ImageData     * pSourceImage;     // source bitmap
    ImageData     * pDestImage;       // destination bitmap for channel separation
    LUT           * pLUT;             // loouk up table
    short         Action;             // Action (grascale conversion, LUT correction)
    short         SResult;            // result of asked test
    unsigned short Params;            // parameters for action
} CORRECTION;    


typedef struct _ParamsType
  {
    CORRECTION       * pCorr;
  } ParamsType;


//-------- Grayscale image detection, result in 68k format --------------
  
private short isGraycale(ImageData *pImage)
{
  short  i, j, ir, result;  
  long   R, G, B, RGB;  
  unsigned char * pSrc;  
  pSrc=pImage->pData;
  result=1; j=0; i=0; ir=0;
  while((j<pImage->h)&&(result))
  { // Get R & G & B of source in 16 bits colors
    RGB = (pSrc[ir]<<8)|pSrc[ir+1];
    R=(RGB >> 11) & 0x001F;
    G=(RGB >> 6) & 0x001F;
    B= RGB & 0x001F;
    result=((R==G)&&(R==B));
    if(result)
    { ir+=2; i++;
      if(i>=pImage->w)
      { pSrc += pImage->wl;
        j++; i=0; ir=0;
      }
    }
  }
  if(result) result=EndianSwap16(1); else result=0;
  return result;
}

//----------------------- Grayscale image conversion --------------------
  
private void doGraycale(ImageData *pImage)
{
  short  i, j, ir, result;  
  long   R, G, B, RGB, Gray;  
  unsigned char * pSrc;  
  pSrc=pImage->pData;
  for (j=0;j<pImage->h;j++)  
  {
     ir=0;
     for (i=0; i<pImage->w; i++)
     {
       // Get R & G & B of source in 16 bits colors
       RGB = (pSrc[ir]<<8)|pSrc[ir+1];
       R=(RGB >> 11) & 0x001F;
       G=(RGB >> 5) & 0x003F;
       B= RGB & 0x001F;
       // Gray = 0.3R + 0.59G + 0.11B
       // normalized to 6 bits (rgb 565)
       Gray  = (R<<1) * Rc;
       Gray += G * Gc;
       Gray += (B<<1) * Bc;
       Gray = (Gray+ONEHALF)>>FixedPoint;
       if (Gray>MAXGRAY) Gray=MAXGRAY;
       Gray >>= 1;
       //new gray color
       pSrc[ir]  = (Gray<<3)|(Gray>>2);
       pSrc[ir+1]= (Gray<<6)|(Gray);
       ir +=2;
     }
     pSrc+=pImage->wl;
  }
  return;
}


//----------------------- Channel separation ---------------------------
// return a grayscale image from the selected channel
  
private void doSeparateChannel(CORRECTION  * pCorr)
{
  short  i, j, ir, result;
  short  w, h, wl;  
  long   R, G, B, RGB, Gray;  
  unsigned char * pSrc;  
  unsigned char * pDest;  
  pSrc = pCorr->pSourceImage->pData;
  pDest = pCorr->pDestImage->pData;
  w = pCorr->pSourceImage->w;
  h = pCorr->pSourceImage->h;
  wl = pCorr->pSourceImage->wl;
  for (j=0;j<h;j++)  
  {
     ir=0;
     for (i=0; i<w; i++)
     {
       // Get R & G & B of source in 16 bits colors
       RGB = (pSrc[ir]<<8)|pSrc[ir+1];
       R=(RGB >> 11) & 0x001F;
       G=(RGB >> 5) & 0x003F;
       B= RGB & 0x001F;
       switch(pCorr->Params)
       {
         case 0: //red channel
          Gray = R;
          break;
         case 1: //green channel
          Gray = G>>1;
          break;
         case 2: //blue channel
          Gray = B;
          break;
       }
       //new gray color
       pDest[ir]  = (Gray<<3)|(Gray>>2);
       pDest[ir+1]= (Gray<<6)|(Gray);
       ir +=2;
     }
     pSrc += wl;
     pDest += wl;
  }
  return;
}


//----------------------- LUT based correction --------------------------

private void doLUTcorrection(CORRECTION  * pCorr)
{
  unsigned char * Psource;
  LUT           * pLUT;
  short  w, h, wl;
  short  i, j, ir;
  long   Rs,Gs,Bs;
  long   Gray, RGB;
  //local copy
  pLUT = pCorr->pLUT;
  w = pCorr->pSourceImage->w;
  h = pCorr->pSourceImage->h;
  wl = pCorr->pSourceImage->wl;
  Psource = pCorr->pSourceImage->pData;
  //correction
  for (j=0;j<h;j++)  
  {
     ir=0;
     for (i=0; i<w; i++)
     {
       // Get R & G & B of source in 16 bits colors
       RGB = (Psource[ir]<<8)|Psource[ir+1];
       Rs=(RGB >> 11) & 0x001F;
       Gs=(RGB >> 5) & 0x003F;
       Bs= RGB & 0x001F;
       //get value from LUT
       Rs = pLUT->DATA[Rs];
       Gs = (pLUT->DATA[Gs>>1])<<1;
       Bs = pLUT->DATA[Bs];
       //new color
       Psource[ir]  = (Rs<<3)|(Gs>>3);
       Psource[ir+1]= (Gs<<5)|(Bs);
       ir +=2;
     }
     Psource+=wl;
  }
  return;
}


//----------------------- Correction Function ---------------------------


unsigned long FuncCorrectionLUT(const void * emulStateP,
                     void * userData68KP, Call68KFuncType * call68KFuncP)
{
  ParamsType    * p;                 //pointer: params structure
  CORRECTION    * pCorr;             //pointer: class structure

  // >>>>  Do local copy of params
  p=(ParamsType *) userData68KP;
  pCorr = p->pCorr;
  switch(pCorr->Action)
  {
    // LUT based correction
    case ACT_LUTCORRECTION:
      doLUTcorrection(pCorr);
      break;
    // grascale test  
    case ACT_TESTGRAYSCALE:
      pCorr->SResult = isGraycale(pCorr->pSourceImage);
      break;
    // grayscale conversion
    case ACT_DOGRAYSCALE:
      doGraycale(pCorr->pSourceImage);
      break;
    //channels separation
    case ACT_DOCHANNELSSEPARATION:
      doSeparateChannel(pCorr);
      break;
  }
  //return home.
  return 0;
}


//=======================================================================