genode/demo/src/lib/scout_gfx/sky_texture_painter.cc

173 lines
3.9 KiB
C++

/*
* \brief Functor for drawing a sky texture into a surface
* \author Norman Feske
* \date 2005-10-24
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <scout_gfx/random.h>
#include <scout_gfx/sky_texture_painter.h>
/**
* Calculate fractional part of texture position for a given coordinate
*/
static inline int calc_u(int x, int w, int texture_w)
{
return ((texture_w*x<<8)/w) & 0xff;
}
/**
* Cubic interpolation
*
* \param u relative position between x1 and x2 (0..255)
*/
static inline int filter(int x0, int x1, int x2, int x3, int u)
{
static int cached_u = -1;
static int k0, k1, k2, k3;
/*
* Do not recompute coefficients when called
* with the same subsequencing u values.
*/
if (u != cached_u) {
int v = 255 - u;
int uuu = (u*u*u)>>16;
int vvv = (v*v*v)>>16;
int uu = (u*u)>>8;
int vv = (v*v)>>8;
k0 = vvv/6;
k3 = uuu/6;
k1 = k3*3 - uu + (4<<8)/6;
k2 = k0*3 - vv + (4<<8)/6;
cached_u = u;
}
return (x0*k0 + x1*k1 + x2*k2 + x3*k3)>>8;
}
/**
* Determine texture position by given position in image
*/
static inline int get_idx(int x, int w, int texture_w, int offset)
{
return (offset + texture_w + (texture_w*x)/w) % texture_w;
}
/**
* Generate sky texture based on bicubic interpolation of some noise
*/
static void gen_buf(short tmp[], int noise_w, int noise_h,
short dst[], int dst_w, int dst_h)
{
/* generate noise */
for (int i = 0; i < noise_h; i++) for (int j = 0; j < noise_w; j++)
dst[i*dst_w + j] = Scout::random()%256 - 128;
/* interpolate horizontally */
for (int j = dst_w - 1; j >= 0; j--) {
int x0_idx = get_idx(j, dst_w, noise_w, -1);
int x1_idx = get_idx(j, dst_w, noise_w, 0);
int x2_idx = get_idx(j, dst_w, noise_w, 1);
int x3_idx = get_idx(j, dst_w, noise_w, 2);
int u = calc_u(j, dst_w, noise_w);
for (int i = 0; i < noise_h; i++) {
int x0 = dst[i*dst_w + x0_idx];
int x1 = dst[i*dst_w + x1_idx];
int x2 = dst[i*dst_w + x2_idx];
int x3 = dst[i*dst_w + x3_idx];
tmp[i*dst_w + j] = filter(x0, x1, x2, x3, u);
}
}
/* vertical interpolation */
for (int i = dst_h - 1; i >= 0; i--) {
int y0_idx = get_idx(i, dst_h, noise_h, -1)*dst_w;
int y1_idx = get_idx(i, dst_h, noise_h, 0)*dst_w;
int y2_idx = get_idx(i, dst_h, noise_h, 1)*dst_w;
int y3_idx = get_idx(i, dst_h, noise_h, 2)*dst_w;
int u = calc_u(i, dst_h, noise_h);
for (int j = 0; j < dst_w; j++) {
int y0 = tmp[y0_idx + j];
int y1 = tmp[y1_idx + j];
int y2 = tmp[y2_idx + j];
int y3 = tmp[y3_idx + j];
dst[i*dst_w + j] = filter(y0, y1, y2, y3, u);
}
}
}
/**
* Normalize buffer values to specified maximum
*/
static void normalize_buf(short dst[], int len, int amp)
{
int min = 0x7ffffff, max = 0;
for (int i = 0; i < len; i++) {
if (dst[i] < min) min = dst[i];
if (dst[i] > max) max = dst[i];
}
if (max == min) return;
for (int i = 0; i < len; i++)
dst[i] = (amp*(dst[i] - min))/(max - min);
}
/**
* Add each pair of values of two buffers
*/
static void add_bufs(short src1[], short src2[], short dst[], int len)
{
for (int i = 0; i < len; i++)
dst[i] = src1[i] + src2[i];
}
/**
* We combine (add) multiple low-frequency textures with one high-frequency
* texture to get nice shapes.
*/
void Sky_texture_painter::Sky_texture_base::_brew_texture
(
short tmp[], short tmp2[], short dst[], int w, int h,
int lf_start, int lf_end, int lf_incr, int lf_mul,
int hf_val, int hf_mul
)
{
for (int i = lf_start; i < lf_end; i += lf_incr) {
gen_buf(tmp, i, i, tmp2, w, h);
_multiply_buf(tmp2, w*h, (lf_mul - i)*32);
add_bufs(tmp2, dst, dst, w*h);
}
if (hf_val) {
gen_buf(tmp, hf_val, hf_val, tmp2, w, h);
_multiply_buf(tmp2, w*h, hf_mul*32);
add_bufs(tmp2, dst, dst, w*h);
}
/* normalize texture to use four bits */
normalize_buf(dst, w*h, 15);
}