From http://www.jwz.org/xscreensaver/xscreensaver-5.27.tar.gz
[xscreensaver] / utils / alpha.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 2006
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Beauty is only skin deep, unless you've got an alpha channel. */
14
15
16 #include "utils.h"
17 #include "alpha.h"
18 #include "visual.h"
19 #include "hsv.h"
20 #include "yarandom.h"
21 #include "resources.h"
22
23 #include <X11/Xutil.h>
24
25 #ifndef countof
26 # define countof(x) (sizeof(*(x))/sizeof((x)))
27 #endif
28
29
30 /* I don't believe this fucking language doesn't have builtin exponentiation.
31    I further can't believe that the fucking ^ character means fucking XOR!! */
32 static int 
33 i_exp (int i, int j)
34 {
35   int k = 1;
36   while (j--) k *= i;
37   return k;
38 }
39
40
41 static void
42 merge_colors (int argc, XColor **argv, XColor *into_color, int mask,
43               Bool additive_p)
44 {
45   int j;
46   *into_color = *argv [0];
47   into_color->pixel |= mask;
48
49   for (j = 1; j < argc; j++)
50     {
51 # define SHORT_INC(x,y) (x = ((((x)+(y)) > 0xFFFF) ? 0xFFFF : ((x)+(y))))
52 # define SHORT_DEC(x,y) (x = ((((x)-(y)) < 0)      ? 0      : ((x)-(y))))
53       if (additive_p)
54         {
55           SHORT_INC (into_color->red,   argv[j]->red);
56           SHORT_INC (into_color->green, argv[j]->green);
57           SHORT_INC (into_color->blue,  argv[j]->blue);
58         }
59       else
60         {
61           SHORT_DEC (into_color->red,   argv[j]->red);
62           SHORT_DEC (into_color->green, argv[j]->green);
63           SHORT_DEC (into_color->blue,  argv[j]->blue);
64         }
65 # undef SHORT_INC
66 # undef SHORT_DEC
67     }
68 }
69
70 static void
71 permute_colors (XColor *pcolors, XColor *colors,
72                 int count,
73                 unsigned long *plane_masks,
74                 Bool additive_p)
75 {
76   int out = 0;
77   int max = i_exp (2, count);
78   if (count > 31) abort ();
79   for (out = 1; out < max; out++)
80     {
81       XColor *argv [32];
82       int this_mask = 0;
83       int argc = 0;
84       int bit;
85       for (bit = 0; bit < 32; bit++)
86         if (out & (1<<bit))
87           {
88             argv [argc++] = &pcolors [bit];
89             this_mask |= plane_masks [bit];
90           }
91       merge_colors (argc, argv, &colors [out-1], this_mask, additive_p);
92     }
93 }
94
95
96 static int
97 allocate_color_planes (Display *dpy, Colormap cmap,
98                        int nplanes, unsigned long *plane_masks,
99                        unsigned long *base_pixel_ret)
100 {
101   while (nplanes > 1 &&
102          !XAllocColorCells (dpy, cmap, False, plane_masks, nplanes,
103                             base_pixel_ret, 1))
104     nplanes--;
105
106   return nplanes;
107 }
108                        
109
110 static void
111 initialize_transparency_colormap (Display *dpy, Colormap cmap,
112                                   int nplanes,
113                                   unsigned long base_pixel,
114                                   unsigned long *plane_masks,
115                                   XColor *colors,
116                                   Bool additive_p)
117 {
118   int i;
119   int total_colors = i_exp (2, nplanes);
120   XColor *all_colors = (XColor *) calloc (total_colors, sizeof (XColor));
121
122   for (i = 0; i < nplanes; i++)
123     colors[i].pixel = base_pixel | plane_masks [i];
124   permute_colors (colors, all_colors, nplanes, plane_masks, additive_p);
125
126   /* clone the default background of the window into our "base" pixel */
127   all_colors [total_colors - 1].pixel =
128     get_pixel_resource (dpy, cmap, "background", "Background");
129   XQueryColor (dpy, cmap, &all_colors [total_colors - 1]);
130   all_colors [total_colors - 1].pixel = base_pixel;
131
132   for (i = 0; i < total_colors; i++)
133     all_colors[i].flags = DoRed|DoGreen|DoBlue;
134   XStoreColors (dpy, cmap, all_colors, total_colors);
135   XFree ((XPointer) all_colors);
136 }
137
138
139 Bool
140 allocate_alpha_colors (Screen *screen, Visual *visual, Colormap cmap,
141                        int *nplanesP, Bool additive_p,
142                        unsigned long **plane_masks,
143                        unsigned long *base_pixelP)
144 {
145   Display *dpy = DisplayOfScreen (screen);
146   XColor *colors;
147   int nplanes = *nplanesP;
148   int i;
149
150   if (!has_writable_cells (screen, visual))
151     cmap = 0;
152
153   if (!cmap)            /* A TrueColor visual, or similar. */
154     {
155       int depth = visual_depth (screen, visual);
156       unsigned long masks;
157       XVisualInfo vi_in, *vi_out;
158
159       /* Find out which bits the R, G, and B components actually occupy
160          on this visual. */
161       vi_in.screen = screen_number (screen);
162       vi_in.visualid = XVisualIDFromVisual (visual);
163       vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
164                                &vi_in, &i);
165       if (! vi_out) abort ();
166       masks = vi_out[0].red_mask | vi_out[0].green_mask | vi_out[0].blue_mask;
167       XFree ((char *) vi_out);
168
169       if (nplanes > depth)
170         nplanes = depth;
171       *nplanesP = nplanes;
172       *base_pixelP = 0;
173       *plane_masks = (unsigned long *) calloc(sizeof(unsigned long), nplanes);
174
175       /* Pick the planar values randomly, but constrain them to fall within
176          the bit positions of the R, G, and B fields. */
177       for (i = 0; i < nplanes; i++)
178         (*plane_masks)[i] = random() & masks;
179
180     }
181   else                  /* A PseudoColor visual, or similar. */
182     {
183       if (nplanes > 31) nplanes = 31;
184       *plane_masks = (unsigned long *) malloc(sizeof(unsigned long) * nplanes);
185
186       nplanes = allocate_color_planes (dpy, cmap, nplanes, *plane_masks,
187                                    base_pixelP);
188       *nplanesP = nplanes;
189
190       if (nplanes <= 1)
191         {
192           free(*plane_masks);
193           *plane_masks = 0;
194           return False;
195         }
196
197       colors = (XColor *) calloc (nplanes, sizeof (XColor));
198       for (i = 0; i < nplanes; i++)
199         {
200           /* pick the base colors. If we are in subtractive mode, pick higher
201              intensities. */
202           hsv_to_rgb (random () % 360,
203                       frand (1.0),
204                       frand (0.5) + (additive_p ? 0.2 : 0.5),
205                       &colors[i].red,
206                       &colors[i].green,
207                       &colors[i].blue);
208         }
209       initialize_transparency_colormap (dpy, cmap, nplanes,
210                                         *base_pixelP, *plane_masks, colors,
211                                         additive_p);
212       XFree ((XPointer) colors);
213     }
214   return True;
215 }