/* color.c */ /* Blursk - visualization plugin for XMMS * Copyright (C) 1999 Steve Kirkendall * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* A brief discussion about how this works: * * GDK packs the eight-bit R/G/B values into a 32-bit integer, as 0x00RRGGBB. * Blursk uses the 8 extra bits to store the intensity of the background * color. The (*stylefunc)() function returns these 32-bit values, and the * color[] array stores them in this format. * * The background intensity is similar to an "alpha" channel, thought the * implementation details are different. When blending the background color * with the RGB color, a background intensity of 0 means the RGB value is * used unchanged. A background intensity of 255 adds the background color * to the RGB color WITHOUT DIMINISHING THE RGB COLOR. If you're not careful, * you can cause overflow problems when adding the background color. All of * the color styles implemented here have taken this into consideration; * They only combine high-intensity backgrounds with low-brightness RGB values. * * The actual blending is done in the color_bg() function. It is called for * each frame, to support the "Flash bkgnd" setting. With other background * settings, it usually just returns without doing anything. * * The end result of blending is a new GDK colormap. The color[] array * itself is left unchanged. It *must* be left unchanged, because changing * it would cause problems when you wanted to flash the background for the * next frame. */ #include "config.h" #include #include #include #include #include #include #include #include "blursk.h" typedef struct { double hue, saturation, value; } hsv_t; /* This refers to the current colormap. This is *NOT* the same as the list * of colors; it is an data structure used internally by Gdk. */ GdkRgbCmap *color_map = NULL; /* This is where Blursk stores its version of the colors */ guint32 colors[256]; /* This function pointer refers to a function which can be called to compute * the value of a given cell in color_map. It is only called when setting up * the color_map; it is *not* called for every frame. */ static guint32 (*stylefunc)(guint32); /* These are the R/G/B components of the base color */ static guint32 red, green, blue; /* This is a random color, which is used for the "Colored background" setting */ static guint32 tored, togreen, toblue; static gboolean tonew; static guint32 fromred, fromgreen, fromblue; static guint32 bgred, bggreen, bgblue; static char bgletter; /* first letter of chosen bkgnd, after "Random" */ static int transition_bound; /*---------------------------------------------------------------------------*/ /* Convert a color from RGB format to HSV format */ hsv_t *rgb_to_hsv(guint32 rgb) { static hsv_t hsv; /* HSV value (saved between invocations */ double r, g, b;/* the RGB components, in range 0.0 - 1.0 */ double max, min;/* extremes from r, g, b */ double delta; /* difference between max and min */ /* extract the RGB components from rgb */ r = (double)((rgb >> 16) & 0xff) / 255.0; g = (double)((rgb >> 8) & 0xff) / 255.0; b = (double)(rgb & 0xff) / 255.0; /* find max and min */ if (r > g) { max = (b > r) ? b : r; min = (g > b) ? b : g; } else { max = (b > g) ? b : g; min = (r > b) ? b : r; } /* compute "value" */ hsv.value = max; /* compute "saturation" */ hsv.saturation = (max > 0.0) ? (max - min) / max : 0; /* compute "hue". This is the hard one */ delta = max - min; if (delta <= 0.001) { /* gray - any hue will work */ hsv.hue = 0.0; } else { /* divide hexagonal color wheel into three sectors */ if (max == r) /* color is between yellow and magenta */ hsv.hue = (g - b) / delta; else if (max == g) /* color is between cyan and yellow */ hsv.hue = 2.0 + (b - r) / delta; else /* max == b */ /* color is between magenta and cyan */ hsv.hue = 4.0 + (r - g) / delta; /* convert hue to degrees */ hsv.hue *= 60.0; /* make sure hue is not negative */ if (hsv.hue < 0.0) hsv.hue += 360.0; } /* return the computed color */ return &hsv; } /* convert a color from HSV format to RGB format */ guint32 hsv_to_rgb(hsv_t *hsv) { guint32 r, g, b;/* RGB color components */ double h; /* copy of the "hsv.hue" */ double i, f; /* integer and fractional parts of "h" */ int p, q, t;/* permuted RGB values, in integer form */ int v; /* "hsv.value", in integer form */ if (hsv->saturation < 0.01) { /* simple gray conversion */ r = g = b = (guint32)(hsv->value * 255.0); } else { /* convert hue to range [0,6) */ h = hsv->hue / 60.0; while (h >= 6.0) h -= 6.0; /* break "h" down into integer and fractional parts. */ i = floor(h); f = h - i; /* compute the permuted RGB values */ v = (int)(hsv->value * 255.0); p = (int)((hsv->value * (1.0 - hsv->saturation)) * 255.0); q = (int)((hsv->value * (1.0 - (hsv->saturation * f))) * 255.0); t = (int)((hsv->value * (1.0 - (hsv->saturation * (1.0 - f)))) * 255.0); /* map v, p, q, and t into red, green, and blue values */ switch ((int)i) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; default: r = v, g = p, b = q; break; } } /* return the RGB value as a guint32 */ return ((((guint32)r & 0xff) << 16) | (((guint32)g & 0xff) << 8) | ((guint32)b & 0xff)); } /*---------------------------------------------------------------------------*/ /* The following functions are all possible values for stylefunc. A list of * them is stored at the end of the list. */ static guint32 dimming(guint32 i) { return (((guint32)(i * red / 256) << 16) | ((guint32)(i * green / 256) << 8) | ((guint32)(i * blue / 256)) | ((255 - i) << 24)); } static guint32 brightening(guint32 i) { i = 255 - i; return (((guint32)(i * red / 256) << 16) | ((guint32)(i * green / 256) << 8) | ((guint32)(i * blue / 256)) | ((255 - i) << 24)); } static guint32 milky(guint32 i) { guint32 r, g, b, tmp, k; if (i < 128) { r = i * red / 128; g = i * green / 128; b = i * blue / 128; k = (127 - i) << 25; } else { tmp = 255 - i; r = 255 - (255 - red) * tmp / 128; g = 255 - (255 - green) * tmp / 128; b = 255 - (255 - blue) * tmp / 128; k = 0; } tmp = (r << 16) | (g << 8) | b; if (*config.overall_effect == 'B') /* "Bump effect" */ { #if 0 if (i == 128) tmp -= (tmp >> 2) & 0x3f3f3f; else if (i == 127 || i == 129) tmp -= (tmp >> 3) & 0x1f1f1f; #else if (i == 128) { tmp = (tmp >> 2) & 0x3f3f3f; k = 0xc0000000; } else if (i == 127 || i == 129) { tmp = (tmp >> 1) & 0x7f7f7f; k = 0x80000000; } #endif } return tmp | k; } static guint32 cloud(guint32 i) { guint32 faded; /* r/g/b level of gray version of color */ guint32 r, g, b, k; /* Compute the gray version */ faded = (red * 4 + green * 5 + blue * 3) / 12; /* handle a few specific colors */ if (i == 128 && *config.overall_effect == 'B') /* "Bump effect" */ { /* Use the given color */ r = red; g = green; b = blue; k = 0; } else if ((i == 129 || i == 127) && *config.overall_effect == 'B') /* "Bump effect" */ { /* Use a faded version of the color */ r = (red + faded) / 2; g = (green + faded) / 2; b = (blue + faded) / 2; k = 0; } else if (i > 192) { /* transition between the given color and white */ i -= 192; r = (red * i + 255 * (63 - i)) / 64; g = (green * i + 255 * (63 - i)) / 64; b = (blue * i + 255 * (63 - i)) / 64; k = 0; } else if (i > 128) { /* transition between white and faded */ i -= 128; r = g = b = (255 * i + faded * (63 - i)) / 64; k = 0; } else { /* transition between faded and black */ r = g = b = faded * i / 128; k = (127 - i) << 25; } /* Construct a color value from r/g/b, and return it */ return (r << 16) | (g << 8) | b | k; } static guint32 metal(guint32 i) { guint32 r, g, b, k; if (i < 128) { r = red; g = green; b = blue; } else { r = g = b = 0xe0; i = 255 - i; } if (i > 120) { i = 120 - (i - 120) * 120 / 7; } r = r * i / 120; g = g * i / 120; b = b * i / 120; k = (120 - i) << 25; return ((r << 16) | (g << 8) | b | k); } static guint32 layers(guint32 i) { guint32 k; /* compute the brightness */ if (i < 0x40) { k = 63 - i; i = i << 2; } else if (i < 0x80) { k = 0; i = (i << 2) & 0xff; } else { k = 0; i = (i << 1) & 0xff; } /* set this color */ return (((guint32)(i * red / 256) << 16) | ((guint32)(i * green / 256) << 8) | ((guint32)(i * blue / 256)) | (k << 26)); } static guint32 colorlayers(guint32 i) { guint32 tmp, r, g, b, k; /* shift the hue */ r = red; g = green; b = blue; switch (i & 0xc0) { case 0x00: tmp = r; r = (r + g * 2) / 3; g = (g + b * 2) / 3; b = (b + tmp * 2) / 3; /* fall through, so color gets shifted twice... */ case 0x40: tmp = r; r = (r + g * 2) / 3; g = (g + b * 2) / 3; b = (b + tmp * 2) / 3; break; } /* compute the brightness and k */ if (i < 0x40) { tmp = i << 2; k = 63 - i; } else if (i < 0x80) { tmp = (i << 2) & 0xff; k = 0; } else { tmp = (i << 1) & 0xff; k = 0; } /* set this color */ return (((guint32)(tmp * r / 256) << 16) | ((guint32)(tmp * g / 256) << 8) | ((guint32)(tmp * b / 256)) | k << 26); } static guint32 colorstandoff(guint32 i) { guint32 tmp, r, g, b, k; /* shift the hue */ r = red; g = green; b = blue; switch (i & 0xc0) { case 0x00: tmp = r; r = (r + g * 2) / 3; g = (g + b * 2) / 3; b = (b + tmp * 2) / 3; /* fallthrough, so color gets shifted twice... */ case 0x40: tmp = r; r = (r + g * 2) / 3; g = (g + b * 2) / 3; b = (b + tmp * 2) / 3; break; } /* compute the brightness and k */ if (i < 0x80) { tmp = (i & 0x3f) << 3; } else { tmp = (i & 0x7f) << 2; } if (tmp >= 256) tmp = 511 - tmp; if (i < 0x20) k = (31 - i); else k = 0; /* set this color */ return (((guint32)(tmp * r / 256) << 16) | ((guint32)(tmp * g / 256) << 8) | ((guint32)(tmp * b / 256)) | k << 27); } static guint32 flame(guint32 i) { hsv_t hsv; guint32 k; /* Get the base color */ hsv = *rgb_to_hsv(config.color); /* Change the hue, and maybe brightness, depending on i */ hsv.hue += (255 - i) / 4; if (hsv.hue >= 360.0) hsv.hue -= 360.0; k = 0; if (i < 64) { hsv.value *= (double)i / 64.0; k = 63 - i; } else if (i > 192) hsv.value *= (double)(319 - i) / 128; /* Convert it back to RGB */ return hsv_to_rgb(&hsv) | (k << 26); } static guint32 rainbow(guint32 i) { hsv_t hsv; guint32 k; /* Get the base color */ hsv = *rgb_to_hsv(config.color); /* Change the hue, and maybe brightness, depending on i */ hsv.hue += 2 * (255 - i); if (hsv.hue >= 360.0) hsv.hue -= 360.0; if (i < 64) { hsv.value *= (double)i / 64.0; k = (255 - i) << 26; } else k = 0; /* Convert it back to RGB */ return hsv_to_rgb(&hsv) | k; } static guint32 standoff(guint32 i) { guint k; /* compute the brightness */ k = 0; if (i >= 128) { i = 0; } else { if (i >= 64) i = (128 - i) * 4; else { i *= 4; k = 255 - i; } if (i > 254) i = 254; } /* set this color */ return (((guint32)(i * red / 256) << 16) | ((guint32)(i * green / 256) << 8) | ((guint32)(i * blue / 256)) | (k << 24)); } static guint32 threshold(guint32 i) { /* always return the base color. This is only interesting when it * is modified via contour lines, or by the standard rule that color * 0 is always black. */ return config.color; } static guint32 stripes(guint32 i) { guint32 tmp, k; /* compute the brightness and k */ k = 0; if (i >= 0xd0) { tmp = 254; } else { switch (i & 0x18) { case 0x00: tmp = (i & 0x7) << 5; break; case 0x18: tmp = ((~i) & 0x7) << 5; break; default: tmp = 254; } if (i < 64) { tmp = (tmp * i) >> 6; k = 63 - i; } } /* set this color */ return (((guint32)(tmp * red / 256) << 16) | ((guint32)(tmp * green / 256) << 8) | ((guint32)(tmp * blue / 256)) | (k << 26)); } static guint32 colorstripes(guint32 i) { guint32 r, g, b, k, tmp; static guint32 brightness[] = {0, 64, 128, 192, 254, 254, 254, 254, 254, 254, 254, 254, 254, 192, 128, 64}; /* compute the hue */ tmp = i & 0x30; switch (i & 0xc0) { case 0x40: r = (green * tmp + red * (0x3f - tmp)) >> 6; g = (blue * tmp + green * (0x3f - tmp)) >> 6; b = (red * tmp + blue * (0x3f - tmp)) >> 6; break; case 0x80: r = (blue * tmp + green * (0x3f - tmp)) >> 6; g = (red * tmp + blue * (0x3f - tmp)) >> 6; b = (green * tmp + red * (0x3f - tmp)) >> 6; break; default: r = (red * tmp + blue * (0x3f - tmp)) >> 6; g = (green * tmp + red * (0x3f - tmp)) >> 6; b = (blue * tmp + green * (0x3f - tmp)) >> 6; } /* compute the brightness and k */ k = 0; if (i >= 0xf0) { tmp = 254; } else { tmp = brightness[i & 0xf]; if (i < 64) { tmp = (tmp * i) >> 6; k = 64 - i; } } /* set this color */ return (((guint32)(tmp * r / 256) << 16) | ((guint32)(tmp * g / 256) << 8) | ((guint32)(tmp * b / 256)) | (k << 26)); } static guint32 colorbands(guint32 i) { guint32 r, g, b, k, tmp; /* compute the hue */ tmp = i & 0x20; switch (i & 0xc0) { case 0x40: r = (green * tmp + red * (0x3f - tmp)) >> 6; g = (blue * tmp + green * (0x3f - tmp)) >> 6; b = (red * tmp + blue * (0x3f - tmp)) >> 6; break; case 0x80: r = (blue * tmp + green * (0x3f - tmp)) >> 6; g = (red * tmp + blue * (0x3f - tmp)) >> 6; b = (green * tmp + red * (0x3f - tmp)) >> 6; break; default: r = (red * tmp + blue * (0x3f - tmp)) >> 6; g = (green * tmp + red * (0x3f - tmp)) >> 6; b = (blue * tmp + green * (0x3f - tmp)) >> 6; } /* compute the brightness & k */ if (i >= 0x40) { tmp = 254; k = 0; } else { tmp = i * 4; k = 63 - i; } /* set this color */ return (((guint32)(tmp * r / 256) << 16) | ((guint32)(tmp * g / 256) << 8) | ((guint32)(tmp * b / 256)) | (k << 26)); } static guint32 graying(guint32 i) { guint32 faded, tmp; /* Compute the fully faded color's intensity. Note that we actually * make it slightly dimmer than the base color, because it seems to * look better that way. */ faded = (red * 4 + green * 5 + blue * 3) / 16; /* colormap is divided into two phases: fading and dimming */ if (i < 64) { /* full gray, becoming dimmer */ return ((faded * i * 4) >> 8) * 0x010101 | ((63 - i) << 26); } else { /* full brightness, but fading to gray */ i -= 64; tmp = 192 - i; return (((i * red + tmp * faded) / 192) << 16) | (((i * green + tmp * faded) / 192) << 8) | ((i * blue + tmp * faded) / 192); } } static guint32 noise(guint32 i) { if (rand_0_to(256) < i) return config.color; else return 0xff000000; } /* This is a list of the known color styles */ static struct { char *name; guint32 (*func)(guint32); guint good_for_bump; } colorstyles[] = { {"Dimming", dimming, TRUE}, {"Brightening", brightening, TRUE}, {"Milky", milky, TRUE}, {"Graying", graying, TRUE}, {"Flame", flame, TRUE}, {"Cloud", cloud, TRUE}, {"Metal", metal, TRUE}, {"Layers", layers, FALSE}, {"Color layers", colorlayers, FALSE}, {"Standoff", standoff, FALSE}, {"Color standoff", colorstandoff, FALSE}, {"Threshold", threshold, FALSE}, {"Stripes", stripes, FALSE}, {"Color stripes", colorstripes, FALSE}, {"Color bands", colorbands, FALSE}, {"Rainbow", rainbow, FALSE}, {"Noise", noise, FALSE} }; /* Compute the color of a single cell in the colormap. This uses (*stylefunc)() * and also checks the other relevant options. */ static guint32 cell(guint i) { guint32 c; /* The white_signal option forces color 255 to be white */ if (i == 255 && *config.signal_color == 'W') return 0x00ffffff; /* The last three cells are always the background color */ if (i < 3) return 0xff000000; /* We can also force contour lines to be white. These look * better if we also have a half-white/half-colored value on * either side of it; notice the tricky way we accomplish that. */ if (config.contour_lines) { switch ((i + 8) & 0x1f) { case 0x00: case 0x01: case 0x1f: case 0x1e: /* solid white */ c = 0x00ffffff; break; case 0x02: case 0x1d: /* mixed white & computed color*/ c = (*stylefunc)(i); c = (((c & 0xfefefe) + 0xfefefe) / 2); break; default: /* Just compute the color */ c = (*stylefunc)(i); } } else c = (*stylefunc)(i); /* Return the color */ return c; } static void choosebg(int do_random) { /* "Random", then choose a background */ if (do_random) { if (!strncmp(config.background, "Random", 6)) bgletter = "BWDSCF"[rand_0_to(6)]; else bgletter = *config.background; } /* Choose new background color. Note that we don't handle * "Flash bkgnd" here. */ switch (bgletter) { case 'W': /* White bkgnd */ tored = togreen = toblue = 230; break; case 'D': /* Dark bkgnd */ tored = red / 2; togreen = green / 2; toblue = blue / 2; break; case 'S': /* Shift bkgnd */ tored = blue; togreen = red; toblue = green; break; case 'C': /* Color bkgnd */ if (do_random) { tored = rand_0_to(255); togreen = rand_0_to(255); toblue = rand_0_to(255); } else { tored = fromred; togreen = fromgreen; toblue = fromblue; } break; default: /* Black bkgnd, and also fake Flash bkgnd */ tored = togreen = toblue = 0; } tonew = TRUE; } /* This recalculates a portion of the colors, and then activates the new * colormap. If from==scale then it also chooses a new random color style. * This is called from blur() during its transitions. */ void color_transition( int from, /* highest-numbered cell to change, scaled */ int to, /* lowest-numbered cell to change, scaled */ int scale) /* highest possible value of to & from */ { #if 1 if (to < 0) to = 0; #endif /* Choose a random "Colored background" color at the start of the * transition, if necessary */ if (from == scale) { /* Previous transition must be complete, I guess */ fromred = tored; fromgreen = togreen; fromblue = toblue; choosebg(TRUE); } /* Do the background color transition */ if (to <= 0) { bgred = tored; bggreen = togreen; bgblue = toblue; } else { bgred = (tored * (scale - to) + fromred * to) / scale; bggreen = (togreen * (scale - to) + fromgreen * to) / scale; bgblue = (toblue * (scale - to) + fromblue * to) / scale; } /* if colorstyle isn't "random" then do nothing more */ if (strcmp(config.color_style, "Random")) return; /* if from==scale then choose a new random color style */ if (from == scale) stylefunc = colorstyles[rand_0_to(QTY(colorstyles))].func; /* scale the numbers to match the size of the color table */ from = from * 255 / scale; to = to * 255 / scale; /* recompute ONLY the affected cells */ for (; from > to; from--) { colors[from] = cell(from); #if HAVE_XV xv_palette(from, colors[from]); #endif } /* Adjust the background, and then activate the new colormap. */ tonew = TRUE; color_bg(0, NULL); /* Remember the lower bound of the transition. Other color changes * will be limited to be no lower than this value, so that changing * the hue or contour won't defeat the gradual transition. (The * hue or contour change will be effected for the remaining color * cells as a natural consequence of the transition.) */ transition_bound = to; } /* Generate a colormap, and install it. This function is called once when the * plugin starts, and again whenever the configuration is changed in a way * which affects the colormap. */ void color_genmap(int do_random) { gint32 i; /* If the visual isn't running, then do nothing */ if (!blursk_window) return; /* Decompose the dominant color into R/G/B components */ red = (guint32)(config.color / 0x10000); green = (guint32)((config.color % 0x10000)/0x100); blue = (guint32)(config.color % 0x100); /* Choose a new background, if appropriate */ choosebg(do_random); bgred = fromred = tored; bggreen = fromgreen = togreen; bgblue = fromblue = toblue; tonew = TRUE; /* Find the name in the colorstyles[] table */ if ((do_random || !stylefunc) && !strcmp(config.color_style, "Random")) { /* Choose a "Random" colorstyle */ stylefunc = colorstyles[rand_0_to(QTY(colorstyles))].func; } else if (!stylefunc || strcmp(config.color_style, "Random")) { /* Use the named colorstyle */ for (i = 0; i < QTY(colorstyles) && strcmp(colorstyles[i].name, config.color_style); i++) { } if (i >= QTY(colorstyles)) i = 0; stylefunc = colorstyles[i].func; /* Transitions only affect "Random" colorstyle, not this one */ transition_bound = 0; } /* Generate the basic colormap */ for (i = 255; i >= transition_bound; i--) { colors[i] = cell(i); #if HAVE_XV xv_palette(i, colors[i]); #endif } /* Adjust the background, and then activate the new colormap. */ tonew = TRUE; color_bg(0, NULL); } /* This function is called once for each frame, before the frame's image is * output. It adjusts the colormap's background color in response to the music. */ void color_bg(gint ndata, gint16 *data) { guint32 bgr, bgb, bgg, k, bg; guint32 fgr, fgb, fgg; int i, j; gint16 max, min; gint32 totdelta; guint32 newcolors[256]; static guint32 fallr, fallg, fallb; /* if we aren't doing "Flash bkgnd" and we've reached our final color, * then do nothing */ if (bgletter != 'F' && bgred == tored && bggreen == togreen && bgblue == toblue) { if (!tonew) return; tonew = FALSE; } /* force colors[0] to be the background color */ colors[0] = 0xff000000; /* compute the RGB background color, based on data */ if (bgletter != 'F' || ndata == 0) { /* Use the transition colors */ bgr = bgred; bgg = bggreen; bgb = bgblue; } else /* "Flash bkgnd" */ { if (nspectrums == 0) { /* data is samples */ /* examine the data */ max = min = data[0]; totdelta = 0; for (i = 1; i < ndata; i++) { if (data[i] < min) min = data[i]; else if (data[i] > max) max = data[i]; totdelta += abs(data[i] - data[i - 1]); } /* compute red from the difference between max & min */ bgr = (max - min) >> 8; /* compute blue from delta. */ bgb = totdelta / (ndata * 16); /* green is just the average of those two */ bgg = (bgr + bgb) >> 1; } else { /* data is spectrum. If stereo then we only care * about the second one. (the first is mostly a * duplicate of the second, and the first also * suffers from being backward -- which looks cool * in a graph, but would hurt us here. */ if (nspectrums == 2) ndata /= 2, data += ndata; /* the lower frequencies are used for red, middle * for green, and upper for blue. The spectrum * energy has already been conditioned so we can * use the first 1/3 for red, next 1/3 for green * and last 1/3 for blue. The values have also * been conditioned to fill the gint16 range. */ for (i = 0, bgr = 0, j = ndata / 3; i < j; i++) bgr += data[i]; bgr /= j; for (bgg = 0; i < j * 2; i++) bgg += data[i]; bgg /= j; for (bgb = 0; i < ndata; i++) bgb += data[i]; bgb /= (ndata - 2 * j); /* we need to scale the values to fit in 0..255 range */ bgr = (20000 - bgr) >> 7; bgg = (20000 - bgg) >> 7; bgb = (20000 - bgb) >> 7; } /* during transition from colored to flash, we never want to * be darker than the old color. */ if (bgr < bgred) bgr = bgred; if (bgg < bggreen) bgg = bggreen; if (bgb < bgblue) bgb = bgblue; /* clamp the background color values to be within 0...255. Also * try to avoid dark gray backgrounds by ignoring values < 30 */ if (bgr < 30) bgr = 0; else if (bgr > 255) bgr = 255; if (bgg < 30) bgg = 0; else if (bgg > 255) bgg = 255; if (bgb < 30) bgb = 0; else if (bgb > 255) bgb = 255; /* limit the fall-off speed */ if (bgr < fallr) bgr = fallr; fallr = bgr - ((bgr + 15) >> 4); if (bgg < fallg) bgg = fallg; fallg = bgg - ((bgg + 15) >> 4); if (bgb < fallb) bgb = fallb; fallb = bgb - ((bgb + 15) >> 4); } /* build a new colormap, derived from the black-background one */ for (i = 0; i < 256; i++) { /* extract the bg brightness. If 0, then copy unchanged */ k = (colors[i] >> 24) & 0xff; if (k == 0) { newcolors[i] = colors[i]; continue; } /* else we need to blend -- extract foreground RGB */ bg = (((bgr * k) << 8) & 0x00ff0000) | ( (bgg * k) & 0x0000ff00) | (((bgb * k) >> 8) & 0x000000ff); newcolors[i] = colors[i] + bg; #if HAVE_XV xv_palette(i, newcolors[i]); #endif } /* Activate the new colormap. */ if (color_map) { gdk_rgb_cmap_free(color_map); } color_map = gdk_rgb_cmap_new(newcolors,256); } /* Discard the color_map when blursk shuts down */ void color_cleanup(void) { if (color_map) { gdk_rgb_cmap_free(color_map); color_map = NULL; } } /* Return the name of the i'th color style */ char *color_name(int i) { if (i < QTY(colorstyles)) return colorstyles[i].name; else if (i == QTY(colorstyles)) return "Random"; return NULL; } /* Return the name of the i'th background style */ char *color_background_name(int i) { static char *names[] = { "Black bkgnd", "White bkgnd", "Dark bkgnd", "Shift bkgnd", "Color bkgnd", "Flash bkgnd", "Random bkgnd", NULL}; return names[i]; } /* Return the index of a color style that's good for "bump" mode. If the given * color style is good, return it; else choose a different one and return that. * Note that we return its index, not its name, because that works better in * the config.c file where this function is used. */ int color_good_for_bump(char *given) { int i; /* find the given color style in the list */ for (i = 0; i < QTY(colorstyles); i++) if (!strcmp(given, colorstyles[i].name)) break; /* if valid & good for bumps, return it */ if (i < QTY(colorstyles) && colorstyles[i].good_for_bump) return i; /* else return the name of one that is known to be good */ return 0; } /* Allow colors to respond to beats */ void color_beat(void) { hsv_t hsv; guint32 color; /* if hue_on_beats isn't set, then do nothing */ if (!config.hue_on_beats) return; /* Compute a new base color. Tell the config window about it. */ hsv = *rgb_to_hsv(config.color); hsv.hue += 60.0; if (hsv.hue > 360.0) hsv.hue -= 360.0; color = hsv_to_rgb(&hsv); config_load_color(color); /* That's all! The config window will call color_genmap() to produce * a new colormap using the new color, automatically. */ }