Back to index

plt-scheme  4.2.1
wxAllocColor.c
Go to the documentation of this file.
00001 
00002 #include <stdlib.h>
00003 #include <X11/Xcms.h>
00004 
00005 typedef struct {
00006   unsigned short red_in, green_in, blue_in;
00007   unsigned short red_out, green_out, blue_out;
00008   unsigned long pixel;
00009   int weight;
00010 } ColorCache;
00011 
00012 #define COLOR_CACHE_SIZE 1000
00013 
00014 static ColorCache cache[COLOR_CACHE_SIZE];
00015 static int cache_end;
00016 
00017 static long alloc_size;
00018 static long alloc_count;
00019 static unsigned long *alloced;
00020 
00021 extern Screen *wxAPP_SCREEN;
00022 extern Display *wxAPP_DISPLAY;
00023 extern Visual *wxAPP_VISUAL;
00024 
00025 #define OK 1
00026 
00027 /* Fast TrueColor code is from Guillaume Chazarain <gfc@altern.org> */
00028 
00029 /* How many bits are set in mask ? */
00030 static int mask_length(unsigned long mask)
00031 {
00032     int length = 0;
00033 
00034     while (mask) {
00035         length += mask & 1;
00036         mask >>= 1;
00037     }
00038 
00039     return length;
00040 }
00041 
00042 /* Where does the mask start, in bits from the LSB? */
00043 static int mask_start(unsigned long mask)
00044 {
00045     int pos = 0;
00046 
00047     while (!(mask & 0x1)) {
00048       pos++;
00049       mask >>= 1;
00050     }
00051 
00052     return pos;
00053 }
00054 
00055 /* Take the nb most significant bits in value. */
00056 static inline unsigned short n_bits(unsigned short value, int nb)
00057 {
00058     /* length should be 16. */
00059     const int length = sizeof(unsigned short) * 8;
00060     unsigned short mask;
00061 
00062     /*        16 - nb bits
00063      *        vvvvvvvvvvv
00064      * mask = 0000000000011111
00065      *                   ^^^^^
00066      *                  nb bits
00067      */
00068     mask = (1 << nb) - 1;
00069 
00070     /* mask = 1111100000000000 */
00071     mask <<= length - nb;
00072 
00073     /* value = xxxxx00000000000 */
00074     value &= mask;
00075 
00076     /* value = 00000000000xxxxx */
00077     value >>= length - nb;
00078 
00079     return value;
00080 }
00081 
00082 static Visual *tc;
00083 
00084 static int tc_known;
00085 
00086 static unsigned int r_length, g_length, b_length;
00087 static unsigned int r_start, g_start, b_start;
00088 
00089 int wx_simple_r_start, wx_simple_g_start, wx_simple_b_start;
00090 
00091 int wx_alloc_color_is_fast;
00092 
00093 Colormap fast_colormap = 0; /* for init, we assume that no valid colormap is 0! */
00094 
00095 Status wxAllocColor(Display *d, Colormap cm, XColor *c)
00096 {
00097   int i;
00098   int min_weight;
00099   int min_weight_pos;
00100   unsigned short ri, gi, bi;
00101   int p, w, o;
00102   unsigned long pixel;
00103   Status status;
00104 
00105   /* Check fast path first: */
00106   if (cm == fast_colormap) {
00107     c->red = n_bits(c->red, r_length);
00108     c->green = n_bits(c->green, g_length);
00109     c->blue = n_bits(c->blue, b_length);
00110 
00111     c->pixel = ((c->red << r_start)
00112               | (c->green << g_start)
00113               | (c->blue << b_start));
00114 
00115     return OK;
00116   }
00117 
00118   /* If we have a weird colormap, essentially give up (no
00119      deallocation). */
00120   if (cm != wx_default_colormap)
00121     return XAllocColor(d, cm, c);
00122 
00123   /* Is the default colormap TrueColor? */
00124 
00125   if (!tc_known) {
00126     tc = wxAPP_VISUAL;
00127     if (tc->class != TrueColor)
00128       tc = NULL;
00129     else {
00130       r_length = mask_length(tc->red_mask);
00131       g_length = mask_length(tc->green_mask);
00132       b_length = mask_length(tc->blue_mask);
00133 
00134       r_start = mask_start(tc->red_mask);
00135       g_start = mask_start(tc->green_mask);
00136       b_start = mask_start(tc->blue_mask);
00137 
00138       if ((r_length == 8) && (g_length == 8) && (b_length == 8)) {
00139        wx_simple_r_start = r_start;
00140        wx_simple_g_start = g_start;
00141        wx_simple_b_start = b_start;
00142        wx_alloc_color_is_fast = 2;
00143       } else
00144        wx_alloc_color_is_fast = 1;
00145 
00146       fast_colormap = wx_default_colormap;
00147     }
00148     tc_known = 1;
00149 
00150     /* Try again: */
00151     return wxAllocColor(d, cm, c);
00152   }
00153   
00154   /* Not TrueColor. Do things the difficult way... */
00155 
00156   /* Check for black: */
00157   if (!c->red && !c->green && !c->blue) {
00158     c->pixel = BlackPixelOfScreen(wxAPP_SCREEN);
00159     return OK;
00160   }
00161   
00162   /* Check for white: */
00163   if ((c->red >= 0xFF00) && (c->green >= 0xFF00) && (c->blue >= 0xFF00)) {
00164     c->pixel = WhitePixelOfScreen(wxAPP_SCREEN);
00165     c->red = 0xFFFF;
00166     c->green = 0xFFFF;
00167     c->blue = 0xFFFF;
00168     return OK;
00169   }
00170 
00171   ri = c->red;
00172   gi = c->green;
00173   bi = c->blue;
00174 
00175   /* Check in cache: */ 
00176   min_weight_pos = 0;
00177   min_weight = cache[0].weight;
00178   for (i = 0; i < cache_end; i++) {
00179     if (cache[i].red_in == ri
00180        && cache[i].green_in == gi
00181        && cache[i].blue_in == bi) {
00182       c->red = cache[i].red_out;
00183       c->green = cache[i].green_out;
00184       c->blue = cache[i].blue_out;
00185       c->pixel = cache[i].pixel;
00186 
00187       if (cache[i].weight < 10000)
00188        cache[i].weight++;
00189 
00190       return OK;
00191     } else if (cache[i].weight < min_weight) {
00192       min_weight = cache[i].weight;
00193       min_weight_pos = i;
00194     }
00195   }
00196 
00197   if (cache_end == COLOR_CACHE_SIZE) {
00198     /* Degrade weights: */
00199     if (cache[COLOR_CACHE_SIZE - 1].pixel) {
00200       for (i = 0; i < cache_end; i++) 
00201        if (cache[i].weight)
00202          --cache[i].weight;
00203     }
00204   } else
00205     min_weight_pos = cache_end++;
00206 
00207   status = XAllocColor(d, cm, c);
00208 
00209   if (status == OK) {
00210     /* Add to cache: */
00211     cache[min_weight_pos].red_in = ri;
00212     cache[min_weight_pos].green_in = gi;
00213     cache[min_weight_pos].blue_in = bi;
00214     cache[min_weight_pos].red_out = c->red;
00215     cache[min_weight_pos].green_out = c->green;
00216     cache[min_weight_pos].blue_out = c->blue;
00217     cache[min_weight_pos].pixel = c->pixel;
00218     cache[min_weight_pos].weight = 10;
00219 
00220     /* Record allocation */
00221 
00222     /* Binary search for pixel: */
00223     pixel = c->pixel;
00224     if (alloc_count) {
00225       o = 0;
00226       p = alloc_count >> 1;
00227       w = alloc_count;
00228       
00229       while (1) {
00230        unsigned long v;
00231        
00232        v = alloced[p];
00233        
00234        if (v == pixel) {
00235          /* Balance redundant Alloc with Free: */
00236          XFreeColors(d, cm, &pixel, 1, 0);
00237          return OK;
00238        }
00239        if (w == 1) {
00240          if (v < pixel)
00241            p++;
00242          break;
00243        }
00244        if (v < pixel) {
00245          w = o + w - p;
00246          o = p;
00247        } else {
00248          w = p - o;
00249        }
00250        p = o + (w >> 1);
00251       }
00252     } else
00253       p = 0;
00254 
00255     /* Not alloced before. */
00256     /* First make sure array is large enough: */
00257     if (alloc_count == alloc_size) {
00258       unsigned long *old = alloced;
00259 
00260       if (!alloc_size)
00261        alloc_size = 256;
00262       else
00263        alloc_size = alloc_size * 2;
00264       
00265       alloced = (unsigned long *)malloc(sizeof(unsigned long) * alloc_size);
00266       for (i = 0; i < alloc_count; i++)
00267        alloced[i] = old[i];
00268       free(old);
00269     }
00270     
00271     for (i = alloc_count; i-- > p; )
00272       alloced[i + 1] = alloced[i];
00273     alloced[p] = pixel;
00274     alloc_count++;
00275     
00276     return OK;
00277   } else
00278     return status;
00279 }
00280 
00281 int wxQueryColor(Display *display, Colormap colormap, XColor *def_in_out)
00282 {
00283   if (tc && (colormap == wx_default_colormap)) {
00284     unsigned long pixel = def_in_out->pixel, red, green, blue;
00285 
00286     red = (pixel >> r_start) & ((1 << r_length) - 1);
00287     green = (pixel >> g_start) & ((1 << g_length) - 1);
00288     blue = (pixel >> b_start) & ((1 << b_length) - 1);
00289 
00290     def_in_out->red = (red << (16 - r_length));
00291     def_in_out->green = (green << (16 - g_length));
00292     def_in_out->blue = (blue << (16 - b_length));
00293     
00294     return 1; /* is this the right return value? */
00295   }
00296 
00297   return XQueryColor(display, colormap, def_in_out);
00298 }