/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #define _GNU_SOURCE /* For M_PI */ #include #include "compositor-private.h" #include "shadow.h" #include "tidy/tidy-texture-frame.h" #define SHADOW_RADIUS 8 #define SHADOW_OPACITY 0.9 #define SHADOW_OFFSET_X (SHADOW_RADIUS) #define SHADOW_OFFSET_Y (SHADOW_RADIUS) #define MAX_TILE_SZ 8 /* Must be <= shaddow radius */ #define TILE_WIDTH (3*MAX_TILE_SZ) #define TILE_HEIGHT (3*MAX_TILE_SZ) static unsigned char* shadow_gaussian_make_tile (void); ClutterActor * meta_create_shadow_frame (MetaCompositor *compositor) { ClutterActor *frame; if (!compositor->shadow_src) { guchar *data; data = shadow_gaussian_make_tile (); compositor->shadow_src = clutter_texture_new (); clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (compositor->shadow_src), data, TRUE, TILE_WIDTH, TILE_HEIGHT, TILE_WIDTH*4, 4, 0, NULL); g_free (data); } frame = tidy_texture_frame_new (CLUTTER_TEXTURE (compositor->shadow_src), MAX_TILE_SZ, MAX_TILE_SZ, MAX_TILE_SZ, MAX_TILE_SZ); clutter_actor_set_position (frame, SHADOW_OFFSET_X , SHADOW_OFFSET_Y); return frame; } typedef struct GaussianMap { int size; double * data; } GaussianMap; static double gaussian (double r, double x, double y) { return ((1 / (sqrt (2 * M_PI * r))) * exp ((- (x * x + y * y)) / (2 * r * r))); } static GaussianMap * make_gaussian_map (double r) { GaussianMap *c; int size = ((int) ceil ((r * 3)) + 1) & ~1; int center = size / 2; int x, y; double t = 0.0; double g; c = g_malloc (sizeof (GaussianMap) + size * size * sizeof (double)); c->size = size; c->data = (double *) (c + 1); for (y = 0; y < size; y++) for (x = 0; x < size; x++) { g = gaussian (r, (double) (x - center), (double) (y - center)); t += g; c->data[y * size + x] = g; } for (y = 0; y < size; y++) for (x = 0; x < size; x++) c->data[y*size + x] /= t; return c; } static unsigned char sum_gaussian (GaussianMap * map, double opacity, int x, int y, int width, int height) { int fx, fy; double * g_data; double * g_line = map->data; int g_size = map->size; int center = g_size / 2; int fx_start, fx_end; int fy_start, fy_end; double v; unsigned int r; /* * Compute set of filter values which are "in range", * that's the set with: * 0 <= x + (fx-center) && x + (fx-center) < width && * 0 <= y + (fy-center) && y + (fy-center) < height * * 0 <= x + (fx - center) x + fx - center < width * center - x <= fx fx < width + center - x */ fx_start = center - x; if (fx_start < 0) fx_start = 0; fx_end = width + center - x; if (fx_end > g_size) fx_end = g_size; fy_start = center - y; if (fy_start < 0) fy_start = 0; fy_end = height + center - y; if (fy_end > g_size) fy_end = g_size; g_line = g_line + fy_start * g_size + fx_start; v = 0; for (fy = fy_start; fy < fy_end; fy++) { g_data = g_line; g_line += g_size; for (fx = fx_start; fx < fx_end; fx++) v += *g_data++; } if (v > 1) v = 1; v *= (opacity * 255.0); r = (unsigned int) v; return (unsigned char) r; } static unsigned char * shadow_gaussian_make_tile () { unsigned char * data; int size; int center; int x, y; unsigned char d; int pwidth, pheight; double opacity = SHADOW_OPACITY; static GaussianMap * gaussian_map = NULL; struct _mypixel { unsigned char r; unsigned char g; unsigned char b; unsigned char a; } * _d; if (!gaussian_map) gaussian_map = make_gaussian_map (SHADOW_RADIUS); size = gaussian_map->size; center = size / 2; /* Top & bottom */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; data = g_malloc0 (4 * TILE_WIDTH * TILE_HEIGHT); _d = (struct _mypixel*) data; /* N */ for (y = 0; y < pheight; y++) { d = sum_gaussian (gaussian_map, opacity, center, y - center, TILE_WIDTH, TILE_HEIGHT); for (x = 0; x < pwidth; x++) { _d[y*3*pwidth + x + pwidth].r = 0; _d[y*3*pwidth + x + pwidth].g = 0; _d[y*3*pwidth + x + pwidth].b = 0; _d[y*3*pwidth + x + pwidth].a = d; } } /* S */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; for (y = 0; y < pheight; y++) { d = sum_gaussian (gaussian_map, opacity, center, y - center, TILE_WIDTH, TILE_HEIGHT); for (x = 0; x < pwidth; x++) { _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].r = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].g = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].b = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].a = d; } } /* w */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; for (x = 0; x < pwidth; x++) { d = sum_gaussian (gaussian_map, opacity, x - center, center, TILE_WIDTH, TILE_HEIGHT); for (y = 0; y < pheight; y++) { _d[y*3*pwidth + 3*pwidth*pheight + x].r = 0; _d[y*3*pwidth + 3*pwidth*pheight + x].g = 0; _d[y*3*pwidth + 3*pwidth*pheight + x].b = 0; _d[y*3*pwidth + 3*pwidth*pheight + x].a = d; } } /* E */ for (x = 0; x < pwidth; x++) { d = sum_gaussian (gaussian_map, opacity, x - center, center, TILE_WIDTH, TILE_HEIGHT); for (y = 0; y < pheight; y++) { _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].r = 0; _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].g = 0; _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].b = 0; _d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].a = d; } } /* NW */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gaussian_map, opacity, x-center, y-center, TILE_WIDTH, TILE_HEIGHT); _d[y*3*pwidth + x].r = 0; _d[y*3*pwidth + x].g = 0; _d[y*3*pwidth + x].b = 0; _d[y*3*pwidth + x].a = d; } /* SW */ for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gaussian_map, opacity, x-center, y-center, TILE_WIDTH, TILE_HEIGHT); _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].r = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].g = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].b = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].a = d; } /* SE */ for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gaussian_map, opacity, x-center, y-center, TILE_WIDTH, TILE_HEIGHT); _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + 2*pwidth].r = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + 2*pwidth].g = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + 2*pwidth].b = 0; _d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) + 2*pwidth].a = d; } /* NE */ for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gaussian_map, opacity, x-center, y-center, TILE_WIDTH, TILE_HEIGHT); _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].r = 0; _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].g = 0; _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].b = 0; _d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].a = d; } /* center */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; d = sum_gaussian (gaussian_map, opacity, center, center, TILE_WIDTH, TILE_HEIGHT); for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].r = 0; _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].g = 0; _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].b = 0; _d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].a = 0; } return data; }