/*
 * ANSI C code from the article
 * "Fast Embossing Effects on Raster Image Data"
 * by John Schlag, jfs@kerner.com
 * in "Graphics Gems IV", Academic Press, 1994
 *
 *
 * Emboss - shade 24-bit pixels using a single distant light source.
 * Normals are obtained by differentiating a monochrome 'bump' image.
 * The unary case ('texture' == NULL) uses the shading result as output.
 * The binary case multiples the optional 'texture' image by the shade.
 * Images are in row major order with interleaved color components (rgbrgb...).
 * E.g., component c of pixel x,y of 'dst' is dst[3*(y*xSize + x) + c].
 *
 * To compile a test program on an SGI:
 *	cc -DMAIN emboss.c -lgl_s -lm -o emboss
 * The code for the Emboss routine itself is portable to most machines.
 */

#include <math.h>
#include <sys/types.h>

void
Emboss(
    double azimuth, double elevation,	/* light source direction */
    u_short width45,			/* filter width */
    u_char *bump,			/* monochrome bump image */
    u_char *texture, u_char *dst,	/* texture & output images */
    u_short xSize, u_short ySize	/* image size */
)
{
    long Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL;
    register u_char *s1, *s2, *s3, shade, background;
    register u_short x, y;

    #define pixelScale 255.9

    /*
     * compute the light vector from the input parameters.
     * normalize the length to pixelScale for fast shading calculation.
     */
    Lx = cos(azimuth) * cos(elevation) * pixelScale;
    Ly = sin(azimuth) * cos(elevation) * pixelScale;
    Lz = sin(elevation) * pixelScale;

    /*
     * constant z component of image surface normal - this depends on the
     * image slope we wish to associate with an angle of 45 degrees, which
     * depends on the width of the filter used to produce the source image.
     */
    Nz = (6 * 255) / width45;
    Nz2 = Nz * Nz;
    NzLz = Nz * Lz;

    /* optimization for vertical normals: L.[0 0 1] */
    background = Lz;

    /* mung pixels, avoiding edge pixels */
    dst += xSize*3;
    if (texture) texture += xSize*3;
    for (y = 1; y < ySize-1; y++, bump += xSize, dst += 3)
    {
	s1 = bump + 1;
	s2 = s1 + xSize;
	s3 = s2 + xSize;
	dst += 3;
	if (texture) texture += 3;
	for (x = 1; x < xSize-1; x++, s1++, s2++, s3++)
	{
	    /*
	     * compute the normal from the bump map. the type of the expression
	     * before the cast is compiler dependent. in some cases the sum is
	     * unsigned, in others it is signed. ergo, cast to signed.
	     */
	    Nx = (int)(s1[-1] + s2[-1] + s3[-1] - s1[1] - s2[1] - s3[1]);
	    Ny = (int)(s3[-1] + s3[0] + s3[1] - s1[-1] - s1[0] - s1[1]);

	    /* shade with distant light source */
	    if ( Nx == 0 && Ny == 0 )
		shade = background;
	    else if ( (NdotL = Nx*Lx + Ny*Ly + NzLz) < 0 )
		shade = 0;
	    else
		shade = NdotL / sqrt(Nx*Nx + Ny*Ny + Nz2);

	    /* do something with the shading result */
	    if ( texture ) {
		*dst++ = (*texture++ * shade) >> 8;
		*dst++ = (*texture++ * shade) >> 8;
		*dst++ = (*texture++ * shade) >> 8;
	    }
	    else {
		*dst++ = shade;
		*dst++ = shade;
		*dst++ = shade;
	    }
	}
	if (texture) texture += 3;
    }
}

#ifdef MAIN

#define TEXTURE 1

main()
{
    #define xSize 200
    #define ySize 200
    u_char bump[ySize][xSize];
    u_char texture[ySize][xSize][3], *txt = 0;
    u_char dst[ySize][xSize][3];
    int i, j;

    /* make a simple input image */
    memset(bump, 0, sizeof(bump));
    for(i = xSize/4; i < 3*xSize/4; i++)
	for(j = ySize/4; j < 3*ySize/4; j++)
	    bump[j][i] = 128;

    #if TEXTURE
    /* make a texture */
    for(i = 0; i < xSize; i++)
	for(j = 0; j < ySize; j++) {
	    texture[j][i][0] = random() >> (31 - 8);
	    texture[j][i][1] = random() >> (31 - 8);
	    texture[j][i][2] = random() >> (31 - 8);
	}
    txt = (u_char *)texture;
    #endif

    /* emboss it */
    memset(dst, 0, sizeof(dst));
    #define dToR(d) ((d)*(M_PI/180))
    Emboss(dToR(30), dToR(30), 3, (u_char *)bump, txt, (u_char *)dst,
	xSize, ySize);

    /* display it (sgi component order is ABGR) */
    prefsize(xSize, ySize);
    winopen("emboss");
    RGBmode();
    gconfig();
    for(i = 0; i < ySize; i++) {
	u_long line[xSize];
	u_char *lp = (u_char *)line;
	for(j = 0; j < xSize; j++) {
	    *lp++ = 255;
	    *lp++ = dst[i][j][2];
	    *lp++ = dst[i][j][1];
	    *lp++ = dst[i][j][0];
	}
	lrectwrite(0, ySize-1 - i, xSize-1, ySize-1 - i, line);
    }
    sleep(30);
}

#endif
