1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2008 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it either under the terms of the GNU Lesser General Public
7  * License version 2.1 as published by the Free Software Foundation
8  * (the "LGPL") or, at your option, under the terms of the Mozilla
9  * Public License Version 1.1 (the "MPL"). If you do not alter this
10  * notice, a recipient may use your version of this file under either
11  * the MPL or the LGPL.
12  *
13  * You should have received a copy of the LGPL along with this library
14  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16  * You should have received a copy of the MPL along with this library
17  * in the file COPYING-MPL-1.1
18  *
19  * The contents of this file are subject to the Mozilla Public License
20  * Version 1.1 (the "License"); you may not use this file except in
21  * compliance with the License. You may obtain a copy of the License at
22  * http://www.mozilla.org/MPL/
23  *
24  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26  * the specific language governing rights and limitations.
27  *
28  * The Original Code is the cairo graphics library.
29  *
30  * The Initial Developer of the Original Code is Red Hat, Inc.
31  *
32  * Contributor(s):
33  *	Carl D. Worth <cworth@cworth.org>
34  */
35 
36 #include "cairoint.h"
37 
38 #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
39 
40 #include "cairo-xlib-private.h"
41 
42 #include "cairo-error-private.h"
43 #include "cairo-list-inline.h"
44 
45 /* A perceptual distance metric between two colors. No sqrt needed
46  * since the square of the distance is still a valid metric. */
47 
48 /* XXX: This is currently using linear distance in RGB space which is
49  * decidedly not perceptually linear. If someone cared a lot about the
50  * quality, they might choose something else here. Then again, they
51  * might also choose not to use a PseudoColor visual... */
52 static inline int
_color_distance(unsigned short r1,unsigned short g1,unsigned short b1,unsigned short r2,unsigned short g2,unsigned short b2)53 _color_distance (unsigned short r1, unsigned short g1, unsigned short b1,
54 		 unsigned short r2, unsigned short g2, unsigned short b2)
55 {
56     r1 >>= 8; g1 >>= 8; b1 >>= 8;
57     r2 >>= 8; g2 >>= 8; b2 >>= 8;
58 
59     return ((r2 - r1) * (r2 - r1) +
60 	    (g2 - g1) * (g2 - g1) +
61 	    (b2 - b1) * (b2 - b1));
62 }
63 
64 cairo_status_t
_cairo_xlib_visual_info_create(Display * dpy,int screen,VisualID visualid,cairo_xlib_visual_info_t ** out)65 _cairo_xlib_visual_info_create (Display *dpy,
66 	                        int screen,
67 				VisualID visualid,
68 				cairo_xlib_visual_info_t **out)
69 {
70     cairo_xlib_visual_info_t *info;
71     Colormap colormap = DefaultColormap (dpy, screen);
72     XColor color;
73     int gray, red, green, blue;
74     int i, j, distance, min_distance = 0;
75     XColor colors[256];
76     unsigned short cube_index_to_short[CUBE_SIZE];
77     unsigned short ramp_index_to_short[RAMP_SIZE];
78     unsigned char  gray_to_pseudocolor[RAMP_SIZE];
79 
80     for (i = 0; i < CUBE_SIZE; i++)
81 	cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1);
82     for (i = 0; i < RAMP_SIZE; i++)
83 	ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1);
84 
85     info = _cairo_malloc (sizeof (cairo_xlib_visual_info_t));
86     if (unlikely (info == NULL))
87 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
88 
89     cairo_list_init (&info->link);
90     info->visualid = visualid;
91 
92     /* Allocate a gray ramp and a color cube.
93      * Give up as soon as failures start. */
94 
95     for (gray = 0; gray < RAMP_SIZE; gray++) {
96 	color.red = color.green = color.blue = ramp_index_to_short[gray];
97 	if (! XAllocColor (dpy, colormap, &color))
98 	    goto DONE_ALLOCATE;
99     }
100 
101     /* XXX: Could do this in a more clever order to have the best
102      * possible results from early failure. Could also choose a cube
103      * uniformly distributed in a better space than RGB. */
104     for (red = 0; red < CUBE_SIZE; red++) {
105 	for (green = 0; green < CUBE_SIZE; green++) {
106 	    for (blue = 0; blue < CUBE_SIZE; blue++) {
107 		color.red = cube_index_to_short[red];
108 		color.green = cube_index_to_short[green];
109 		color.blue = cube_index_to_short[blue];
110 		color.pixel = 0;
111 		color.flags = 0;
112 		color.pad = 0;
113 		if (! XAllocColor (dpy, colormap, &color))
114 		    goto DONE_ALLOCATE;
115 	    }
116 	}
117     }
118   DONE_ALLOCATE:
119 
120     for (i = 0; i < ARRAY_LENGTH (colors); i++)
121 	colors[i].pixel = i;
122     XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors));
123 
124     /* Search for nearest colors within allocated colormap. */
125     for (gray = 0; gray < RAMP_SIZE; gray++) {
126 	for (i = 0; i < 256; i++) {
127 	    distance = _color_distance (ramp_index_to_short[gray],
128 					ramp_index_to_short[gray],
129 					ramp_index_to_short[gray],
130 					colors[i].red,
131 					colors[i].green,
132 					colors[i].blue);
133 	    if (i == 0 || distance < min_distance) {
134 		gray_to_pseudocolor[gray] = colors[i].pixel;
135 		min_distance = distance;
136 		if (!min_distance)
137 		    break;
138 	    }
139 	}
140     }
141     for (red = 0; red < CUBE_SIZE; red++) {
142 	for (green = 0; green < CUBE_SIZE; green++) {
143 	    for (blue = 0; blue < CUBE_SIZE; blue++) {
144 		for (i = 0; i < 256; i++) {
145 		    distance = _color_distance (cube_index_to_short[red],
146 						cube_index_to_short[green],
147 						cube_index_to_short[blue],
148 						colors[i].red,
149 						colors[i].green,
150 						colors[i].blue);
151 		    if (i == 0 || distance < min_distance) {
152 			info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel;
153 			min_distance = distance;
154 			if (!min_distance)
155 			    break;
156 		    }
157 		}
158 	    }
159 	}
160     }
161 
162     for (i = 0, j = 0; i < 256; i++) {
163 	if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i)))
164 	    j++;
165 	info->field8_to_cube[i] = j;
166 
167 	info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1);
168     }
169     for (i = 0, j = 0; i < 256; i++) {
170 	if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i)))
171 	    j++;
172 	info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j];
173     }
174 
175     for (i = 0; i < 256; i++) {
176 	info->colors[i].a = 0xff;
177 	info->colors[i].r = colors[i].red   >> 8;
178 	info->colors[i].g = colors[i].green >> 8;
179 	info->colors[i].b = colors[i].blue  >> 8;
180     }
181 
182     *out = info;
183     return CAIRO_STATUS_SUCCESS;
184 }
185 
186 void
_cairo_xlib_visual_info_destroy(cairo_xlib_visual_info_t * info)187 _cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info)
188 {
189     /* No need for XFreeColors() whilst using DefaultColormap */
190     _cairo_list_del (&info->link);
191     free (info);
192 }
193 
194 #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
195