all repos — openbox @ 7fe3301e7ea905a8a76d54c22751f3d8a346e28b

openbox fork - make it a bit more like ryudo

otk/pseudorendercontrol.cc (raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-

#include "config.h"

#include "pseudorendercontrol.hh"
#include "display.hh"
#include "screeninfo.hh"
#include "surface.hh"
#include "rendertexture.hh"

extern "C" {
#include "../src/gettext.h"
#define _(str) gettext(str)
}

#include <cstdlib>

namespace otk {

PseudoRenderControl::PseudoRenderControl(int screen)
  : RenderControl(screen)
{
  printf("Initializing PseudoColor RenderControl\n");
  const ScreenInfo *info = display->screenInfo(_screen);
  int depth = info->depth();

  // determine the number of colors and the bits-per-color
  _bpc = 2; // XXX THIS SHOULD BE A USER OPTION
  assert(_bpc >= 1);
  _ncolors = 1 << (_bpc * 3);

  if (_ncolors > 1 << depth) {
    fprintf(stderr,
            _("PseudoRenderControl: Invalid colormap size. Resizing.\n"));
    _bpc = 1 << (depth/3) >> 3;
    _ncolors = 1 << (_bpc * 3);
  }

  // build a color cube
  _colors = new XColor[_ncolors];
int tr, tg, tb;
  int cpc = 1 << _bpc; // colors per channel
  for (int n = 0,
         r = 0; r < cpc; r++)
    for (int g = 0; g < cpc; g++)
      for (int b = 0; b < cpc; b++, n++) {
        tr = (int)(((float)(r)/(float)(cpc-1)) * 0xFF);
        tg = (int)(((float)(g)/(float)(cpc-1)) * 0xFF);
        tb = (int)(((float)(b)/(float)(cpc-1)) * 0xFF);
        _colors[n].red = tr | tr << 8;
        _colors[n].green = tg | tg << 8;
        _colors[n].blue = tb | tb << 8;
        _colors[n].flags = DoRed|DoGreen|DoBlue; // used to track allocation
      }

  // allocate the colors
  for (int i = 0; i < _ncolors; i++)
    if (!XAllocColor(**display, info->colormap(), &_colors[i]))
      _colors[i].flags = 0; // mark it as unallocated

  // try allocate any colors that failed allocation above

  // get the allocated values from the X server (only the first 256 XXX why!?)
  XColor icolors[256];
  int incolors = (((1 << depth) > 256) ? 256 : (1 << depth));
  for (int i = 0; i < incolors; i++)
    icolors[i].pixel = i;
  XQueryColors(**display, info->colormap(), icolors, incolors);

  // try match unallocated ones
  for (int i = 0; i < _ncolors; i++) {
    if (!_colors[i].flags) { // if it wasn't allocated...
      unsigned long closest = 0xffffffff, close = 0;
      for (int ii = 0; ii < incolors; ii++) {
        // find deviations
        int r = (_colors[i].red - icolors[ii].red) & 0xff;
        int g = (_colors[i].green - icolors[ii].green) & 0xff;
        int b = (_colors[i].blue - icolors[ii].blue) & 0xff;
        // find a weighted absolute deviation
        unsigned long dev = (r * r) + (g * g) + (b * b);

        if (dev < closest) {
          closest = dev;
          close = ii;
        }
      }

      _colors[i].red = icolors[close].red;
      _colors[i].green = icolors[close].green;
      _colors[i].blue = icolors[close].blue;
      _colors[i].pixel = icolors[close].pixel;

      // try alloc this closest color, it had better succeed!
      if (XAllocColor(**display, info->colormap(), &_colors[i]))
        _colors[i].flags = DoRed|DoGreen|DoBlue; // mark as alloced
      else
        assert(false); // wtf has gone wrong, its already alloced for chissake!
    }
  }
}

PseudoRenderControl::~PseudoRenderControl()
{
  printf("Destroying PseudoColor RenderControl\n");

  unsigned long *pixels = new unsigned long [_ncolors], *p = pixels;
  for (int i = 0; i < _ncolors; ++i, ++p)
    *p = _colors[i].pixel;
  XFreeColors(**display, display->screenInfo(_screen)->colormap(), pixels,
              _ncolors, 0);
  delete [] _colors;
}

inline const XColor *PseudoRenderControl::pickColor(int r, int g, int b) const
{
  r = (r & 0xff) >> (8-_bpc);
  g = (g & 0xff) >> (8-_bpc);
  b = (b & 0xff) >> (8-_bpc);
  return &_colors[(r << (2*_bpc)) + (g << (1*_bpc)) + b];
}

void PseudoRenderControl::reduceDepth(Surface &sf, XImage *im) const
{
  pixel32 *data = sf.pixelData();
  pixel32 *ret = (pixel32*)malloc(im->width * im->height * 4);
  char *p = (char *)ret;
  int x, y;
  for (y = 0; y < im->height; y++) {
    for (x = 0; x < im->width; x++) {
      p[x] = pickColor(data[x] >> default_red_shift,
                       data[x] >> default_green_shift,
                       data[x] >> default_blue_shift)->pixel;
    }
    data += im->width;
    p += im->bytes_per_line;
  }
  im->data = (char*)ret;
}

void PseudoRenderControl::allocateColor(XColor *color) const
{
  const XColor *c = pickColor(color->red, color->blue, color->green);

  color->red = c->red;
  color->green = c->green;
  color->blue = c->blue;
  color->pixel = c->pixel;

  if (XAllocColor(**display, display->screenInfo(_screen)->colormap(), color))
    color->flags = DoRed|DoGreen|DoBlue; // mark as alloced
  else
    assert(false); // wtf has gone wrong, its already alloced for chissake!

  return;
}

}