/* Routines for manipulating wet pixels. Copyright 1999 Raph Levien Released under GPL. A wet pixel is a sequence of eight bytes, arranged as follows: Red value when composited over black Green value when composited over black Blue value when composited over black Volume of water Red value when composited over white Green value when composited over white Blue value when composited over white Height of paper surface */ #include #include #include #include "wetpix.h" u32 *wet_render_tab = NULL; static void wet_init_render_tab(void) { int i; double d; int a, b; wet_render_tab = g_new(u32, 4096); for (i = 0; i < 4096; i++) { d = i * (1.0 / 512.0); if (i == 0) a = 0; else a = floor(0xff00 / i + 0.5); b = floor(0x8000 * exp(-d) + 0.5); #if 0 g_print("%d: %x %x\n", i, a, b); #endif wet_render_tab[i] = (a << 16) | b; } } void wet_composite(byte * rgb, int rgb_rowstride, WetPix * wet, int wet_rowstride, int width, int height) { int x, y; byte *rgb_line = rgb; WetPix *wet_line = wet; if (wet_render_tab == NULL) wet_init_render_tab(); for (y = 0; y < height; y++) { byte *rgb_ptr = rgb_line; WetPix *wet_ptr = wet_line; for (x = 0; x < width; x++) { int r, g, b; int d, w; int ab; int wa; r = rgb_ptr[0]; w = wet_ptr[0].rw >> 4; d = wet_ptr[0].rd >> 4; /* d = d >= 4096 ? 4095 : d; */ ab = wet_render_tab[d]; wa = (w * (ab >> 16) + 0x80) >> 8; r = wa + (((r - wa) * (ab & 0xffff) + 0x4000) >> 15); rgb_ptr[0] = r; #if 0 if (x == 128 && y == 128) { g_print("w %d d %d r %d\n", w, d, r); } #endif g = rgb_ptr[1]; w = wet_ptr[0].gw >> 4; d = wet_ptr[0].gd >> 4; d = d >= 4096 ? 4095 : d; ab = wet_render_tab[d]; wa = (w * (ab >> 16) + 0x80) >> 8; g = wa + (((g - wa) * (ab & 0xffff) + 0x4000) >> 15); rgb_ptr[1] = g; b = rgb_ptr[2]; w = wet_ptr[0].bw >> 4; d = wet_ptr[0].bd >> 4; d = d >= 4096 ? 4095 : d; ab = wet_render_tab[d]; wa = (w * (ab >> 16) + 0x80) >> 8; b = wa + (((b - wa) * (ab & 0xffff) + 0x4000) >> 15); rgb_ptr[2] = b; rgb_ptr += 3; wet_ptr++; } rgb_line += rgb_rowstride; wet_line += wet_rowstride; } } void wet_render_wetness(byte * rgb, int rgb_rowstride, WetLayer * layer, int x0, int y0, int width, int height) { static int wet_phase = 0; int x, y; byte *rgb_line = rgb; WetPix *wet_line = layer->buf + (y0 * layer->rowstride) + x0; int highlight; for (y = 0; y < height; y++) { byte *rgb_ptr = rgb_line; WetPix *wet_ptr = wet_line; for (x = 0; x < width; x++) { if (((x + y) & 3) == wet_phase) { highlight = 255 - (wet_ptr[0].w >> 1); if (highlight < 255) { rgb_ptr[0] = 255 - (((255 - rgb_ptr[0]) * highlight) >> 8); rgb_ptr[1] = 255 - (((255 - rgb_ptr[1]) * highlight) >> 8); rgb_ptr[2] = 255 - (((255 - rgb_ptr[2]) * highlight) >> 8); } } rgb_ptr += 3; wet_ptr++; } rgb_line += rgb_rowstride; wet_line += layer->rowstride; } wet_phase += 1; wet_phase &= 3; } void wet_composite_layer(byte * rgb, int rgb_rowstride, WetLayer * layer, int x0, int y0, int width, int height) { /* todo: sanitycheck bounds */ wet_composite(rgb, rgb_rowstride, layer->buf + (y0 * layer->rowstride) + x0, layer->rowstride, width, height); } void wet_pack_render(byte * rgb, int rgb_rowstride, WetPack * pack, int x0, int y0, int width, int height) { int y; byte *rgb_line = rgb; int i; /* clear rgb buffer to white */ for (y = 0; y < height; y++) { memset(rgb_line, 255, width * 3); rgb_line += rgb_rowstride; } /* black stripe */ /* rgb_line = rgb; for (y = y0; y < 8 && y < y0 + height; y++) { memset (rgb_line, 0, width * 3); rgb_line += rgb_rowstride; } */ for (i = 0; i < pack->n_layers; i++) wet_composite_layer(rgb, rgb_rowstride, pack->layers[i], x0, y0, width, height); wet_render_wetness(rgb, rgb_rowstride, pack->layers[pack->n_layers - 1], x0, y0, width, height); } WetLayer *wet_layer_new(int width, int height) { WetLayer *layer; layer = g_new(WetLayer, 1); layer->buf = g_new(WetPix, width * height); layer->width = width; layer->height = height; layer->rowstride = width; return layer; } void wet_layer_clear(WetLayer * layer) { int x, y; WetPix *wet_line = layer->buf; int width = layer->width; for (y = 0; y < layer->height; y++) { for (x = 0; x < width; x++) { /* transparent, dry, smooth */ wet_line[x].rd = 0; wet_line[x].rw = 0; wet_line[x].gd = 0; wet_line[x].gw = 0; wet_line[x].bd = 0; wet_line[x].bw = 0; wet_line[x].w = 0; wet_line[x].h = 128; } wet_line += layer->rowstride; } } WetPack *wet_pack_new(int width, int height) { WetPack *pack; pack = g_new(WetPack, 1); pack->n_layers = 2; pack->layers = g_new(WetLayer *, pack->n_layers); pack->layers[0] = wet_layer_new(width, height); wet_layer_clear(pack->layers[0]); pack->layers[1] = wet_layer_new(width, height); wet_layer_clear(pack->layers[1]); return pack; } void wet_pix_to_double(WetPixDbl * dst, WetPix * src) { dst->rd = (1.0 / 8192.0) * src->rd; dst->rw = (1.0 / 8192.0) * src->rw; dst->gd = (1.0 / 8192.0) * src->gd; dst->gw = (1.0 / 8192.0) * src->gw; dst->bd = (1.0 / 8192.0) * src->bd; dst->bw = (1.0 / 8192.0) * src->bw; dst->w = (1.0 / 8192.0) * src->w; dst->h = (1.0 / 8192.0) * src->h; } void wet_pix_from_double(WetPix * dst, WetPixDbl * src) { int v; v = floor(8192.0 * src->rd + 0.5); if (v < 0) v = 0; if (v > 65535) v = 65535; dst->rd = v; g_print("src->rd = %f, dst->rd = %d\n", src->rd, dst->rd); v = floor(8192.0 * src->rw + 0.5); if (v < 0) v = 0; if (v > 65535) v = 65535; dst->rw = v; v = floor(8192.0 * src->gd + 0.5); if (v < 0) v = 0; if (v > 65535) v = 65535; dst->gd = v; v = floor(8192.0 * src->gw + 0.5); if (v < 0) v = 0; if (v > 65535) v = 65535; dst->gw = v; v = floor(8192.0 * src->bd + 0.5); if (v < 0) v = 0; if (v > 65535) v = 65535; dst->bd = v; v = floor(8192.0 * src->bw + 0.5); if (v < 0) v = 0; if (v > 65535) v = 65535; dst->bw = v; v = floor(8192.0 * src->w + 0.5); if (v < 0) v = 0; if (v > 511) v = 511; dst->w = v; #if 0 g_print("src->w = %f, dst->w = %d\n", src->w, dst->w); #endif v = floor(8192.0 * src->h + 0.5); if (v < 0) v = 0; if (v > 511) v = 511; dst->h = v; }