Spotlight effect

Source code

//========================================================
//
//  spotlight effect 
//  QUAGLIOZZI ERIC (c) 2007
//
//========================================================

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

 Lighting effect
 ---------------

L'éclairage est simulé en considérant une lumière pontuelle
située à une distance finie à la verticale du bitmap.
L'angle alors formé par les rayons de la source de lumière
et la normale au bitmap fait que la quantité de lumière 
réfléchie en direction de l'oeil de l'observateur est plus
ou moins grande. On suppose (ce qui est souvent le cas) que
l'observateur est situé face à l'image et donc dans l'alignement
de la normale à cette surface.


               source lumière
                            O
                           /|\        
                 _        / | \       _
                |\       /  |  \      /|
                  \     /   |   \    /
                   \   /    |    \  /
                    \ /    \|/    \/         image
               -------------------------------------
 
L'intensité du flux reçu se calcule en général à l'aide d'un
produit scalaire entre le vecteur lumière incidant et le 
vecteur normale à la surface. Cette variation s'exprime à
l'aide du cosinus de l'angle formé par les deux vecteurs.
Cela signifie que pour etre rigoureux il faut, pour chaque
pixel, calculer les coordonnées du vecteur lumière incident,
définir un vecteur normal à la surface, calculer le produit
scalaire normé et appliquer ce coefficient aux couleurs du
pixel.

Pour alléger un peu les calculs on procède autrement. Cet
autre solution est en fait une conséquence géométrique
simplifiée.

On considère que la lumière est omnidirectionnelle, Sa
projection dans le plan image est donc l'intersection d'un
cone de lumière avec celui-ci et aligné sur sa normale.

Le résultat de cette projection est donc un cercle.

Plus le rayon du cercle est grand et plus on s'écarte de 
la normale de la source, donc plus la lumière est faible.

Ceci nous donne un moyen plus rapide d'arriver à un résultat
satisfaisant.

Pour retrouver une variation en simili-cosinus on utlise un
polynome de degré 2.

Algorithme général:
------------------

R^2=Rayon d'action réel de la lumière

Pour tout pixel

  1/ K=(x-x0)^2+(Y-Y0)^2

  2/ SI k < R^2 alors    ( 0 =< c =< 1 ) 
                k
       c= (1 - ---)+Seuil
               R^2 
       SI c >1 ALORS c=1
     SINON
       c=Seuil

  3/ Alpha Blending avec couleur d'éclairge

     R= Rpixel(1-c) + Rspot*c
     G= Gpixel(1-c) + Gspot*c
     B= Bpixel(1-c) + Bspot*c
Fin Pour


Dans la pratique il faut optimiser le code en terme de vitesse.

Poposition
----------

On constate que la division par R^2 est réalisée pour chaque pixel.
Une idée serait de transformer cette division par une puissance
de deux afin d'utiliser des décalages à droite.
         k      k*u                       2^N
Ainsi  ----- =  --- ce qui conduit à u =  ---
        R^2     2^N                       R^2

Il faut bien choisir N car K est la somme de deux carrés et
l'on travaille en virgule fixe 20:12 à la base. Donc le plus
grand nombre entier non signé est codé sur 20-1=19 bits.

Avec cette valeur, un calcul sommaire conduit à un bitmap
de 512 pixels max de coté.

Il est donc préférable d'avoir un coefficient u<1,
ainsi u*k<kmax.

Avec cette solution il faut
 1/ Déterminer N tel que 2^N<=R^2 ou encore M=N/2 tel que
    2^M<=R (vrai pour tout nombre >=1) et alpha=2^M/R,
    puis alpha=alpha^2.

 2/ utiliser l'algorithme précédent avec le coeff u.

Les calculs de R^2 et 2^N sont calculés en langage HB++ et
passés en parametres.

*/


#include "MathLibFixed.c"

//------------------- 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);


//RGB Palette Structure
typedef struct {
    unsigned char RGB4[1024];
} PalmOSPalette;    

//Inverse Palette Structure
typedef struct {
    unsigned char Index[216];
} PalmOSInverse;    

//HB++ Class Structure
typedef struct {
  char hb_object_header[14];         //HB++ class header
  short padding;
  short                 WS;          // Width-height source and destination
  short                 HS;
  short                 Density;
  short                 BitDepth;
  long                  LightColors; // infos on light color
  short                 LightMin;    // infos on light
  short                 LightMax;
  long                  Rpix;        // Ray in pixels (PALM)
  long                  N2;          // power of 2 for Alpha computation
  short                 X0;          // X0 and Y0 signed 16 bit, light centre
  short                 Y0;
  unsigned char    * pSrc;           // bitmap source pointer
  unsigned char    * pDest;          // bitmap destination pointer
  unsigned char    *  pPal;          // array of indexed colors
  unsigned char    *  pIndex;        // Fast RGB colors
} HBSpotLightStruct;


// global ARM call structure
typedef struct _ParamsType
  {
    HBSpotLightStruct    * pHBSL;   //pointer to HB spot light class Structure
  }
    ParamsType;



//----------- Spotlight Function Extended ----------------


unsigned long SpotLightFuncEx(const void * emulStateP,
                              void * userData68KP, 
                              Call68KFuncType * call68KFuncP)
{
  ParamsType    * p;                   //pointer to params structure
  HBSpotLightStruct * st;              //pointer to HB  Structure
  long              Rpix, N2, x0y0;
  long            Rsqr;
  unsigned char * src;
  unsigned char * dest;
  unsigned char * Pal;
  unsigned char * Index;

  unsigned char * P1;                  //Vars for computations
  unsigned char * P2;                  // pixels pointers
  short          w, h, wlimit;         // real W and H of bitmaps
  short       i, j, idx, ix;           // index
  long          x0, y0;                // light center
  short          density, bitdepth;    // bitmaps density
  long          R,G,B;                 // color of pixel source
  long          RL,GL,BL;              // Light color
  short          LightMin,DeltaLight;  // minimum and maximum of light (diffuse)
  long          Alpha, K, C;           // Coeff.
  short          cl;                   // Coeff.
  long        Rd,Gd,Bd;                // Coeff.
  short       RGB;


  // Do local copy of params

  p=(ParamsType *) userData68KP;
  st=p->pHBSL;
  
  density=st->Density;
  bitdepth=st->BitDepth;
  w=st->WS*density;                    // bitdepth = 1 for 8 bit, = 2 for 16 bit
  wlimit=w*bitdepth;
  if (wlimit & 0x0001) wlimit += 1;
  h=st->HS*density;
  
  Rpix=st->Rpix; 
  N2=st->N2; 
  x0=st->X0 * density; 
  y0=st->Y0 * density; 
  src=st->pSrc; 
  dest=st->pDest; 
  Pal=st->pPal;
  Index=st->pIndex;
  Rsqr = Rpix* density;                // compute R^2 and take care of density
  Rsqr*=Rsqr;
  N2+=density;
  Alpha= 1 << N2;                      // compute Alpha and take care of density
  Alpha=FPUS_DIV(Alpha,Rsqr);
  BL=st->LightColors & 0x000000FF;     // extract light color
  GL=(st->LightColors >>8)  & 0x000000FF;
  RL=(st->LightColors >>16) & 0x000000FF;
  LightMin = st->LightMin;             // extract light infos
  DeltaLight = st->LightMax - LightMin;
  
  // perform Lighting
  P1=src;
  P2=dest;

  if (bitdepth==1)
  {
     for (j=0;j<h;j++)  
     {
       for (i=0; i<w; i++)
        {
          //color of current pixel
          idx = P1[i]<<2;
          R=Pal[idx+1];
          G=Pal[idx+2];
          B=Pal[idx+3];
        
          //compute light
          K=(i-x0)*(i-x0)+(j-y0)*(j-y0);
          if (K<Rsqr)
          {
            C = FPS_MUL(((Rsqr-K)<<FixedPoint),Alpha);
            C = C >> N2;
            C = ((C * DeltaLight) >> FixedPoint)+LightMin;
          }
          else
          {
            C = LightMin;
          }
          if (C > 0xFF) C = 0xFF;
          cl = C & 0x000000FF;

          // Alpha blending with light color
          Rd= R*(255-cl) + RL*cl;
          Gd= G*(255-cl) + GL*cl;
          Bd= B*(255-cl) + BL*cl;
          R=Rd>>8; if (R>0x00FF) R=0xFF;
          G=Gd>>8; if (G>0x00FF) G=0xFF;
          B=Bd>>8; if (B>0x00FF) B=0xFF;

          //retrieve nearest color   
          R=((R+26)*5)>>8;           // (x*5)>>8 is about x/51
          G=((G+26)*5)>>8;
          B=((B+26)*5)>>8;
          P2[i]= Index[R*36+G*6+B];  // copy new color
        } 
       P1+=wlimit;
       P2+=wlimit;
     }
  } // End of 8 bit case
  else
  {
     // normalize ligth color components, for RGB mode
     RL=RL>>3; // (5 bits)
     GL=GL>>2; // (6 bits)
     BL=BL>>3; // (5 bits)
     for (j=0;j<h;j++)  
      {
        for (i=0; i<w; i++)
        {
          //color of current pixel
          ix=i<<1;
          RGB = (P1[ix]<<8) | P1[ix+1];
          R=(RGB >> 11) & 0x001F;
          G=(RGB >> 5) & 0x003F;
          B= RGB & 0x001F;

          //compute light
          K=(i-x0)*(i-x0)+(j-y0)*(j-y0);
          if (K<Rsqr)
          {
            C = FPS_MUL(((Rsqr-K)<<FixedPoint),Alpha);
            C = C >> N2;
            C = ((C * DeltaLight) >> FixedPoint)+LightMin;
          }
          else
          {
            C = LightMin;
          }
          if (C > 0xFF) C = 0xFF;
          cl = C & 0x000000FF;

          // Alpha blending with light color
          Rd= R*(255-cl) + RL*cl;
          Gd= G*(255-cl) + GL*cl;
          Bd= B*(255-cl) + BL*cl;

          //final color
          R=Rd>>8;
          G=Gd>>8;
          B=Bd>>8;
          if (R > 0x001F) R=0x001F;
          if (G > 0x003F) G=0x003F;
          if (B > 0x001F) B=0x001F;
         
          //copy new color
          P2[ix]  = (R<<3)|(G>>3);
          P2[ix+1]= (G<<5)|(B);
        }
         P1+=wlimit;
         P2+=wlimit;
      }
  } // end of 16 bits case
  return 0;
}

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