1706f2543Smrg/*
2706f2543Smrg *
3706f2543Smrg * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
4706f2543Smrg *
5706f2543Smrg * Permission to use, copy, modify, distribute, and sell this software and its
6706f2543Smrg * documentation for any purpose is hereby granted without fee, provided that
7706f2543Smrg * the above copyright notice appear in all copies and that both that
8706f2543Smrg * copyright notice and this permission notice appear in supporting
9706f2543Smrg * documentation, and that the name of Keith Packard not be used in
10706f2543Smrg * advertising or publicity pertaining to distribution of the software without
11706f2543Smrg * specific, written prior permission.  Keith Packard makes no
12706f2543Smrg * representations about the suitability of this software for any purpose.  It
13706f2543Smrg * is provided "as is" without express or implied warranty.
14706f2543Smrg *
15706f2543Smrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16706f2543Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17706f2543Smrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18706f2543Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19706f2543Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20706f2543Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21706f2543Smrg * PERFORMANCE OF THIS SOFTWARE.
22706f2543Smrg */
23706f2543Smrg
24706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
25706f2543Smrg#include <dix-config.h>
26706f2543Smrg#endif
27706f2543Smrg
28706f2543Smrg#ifndef _MIINDEX_H_
29706f2543Smrg#define _MIINDEX_H_
30706f2543Smrg
31706f2543Smrg#include "scrnintstr.h"
32706f2543Smrg#include "gcstruct.h"
33706f2543Smrg#include "pixmapstr.h"
34706f2543Smrg#include "windowstr.h"
35706f2543Smrg#include "mi.h"
36706f2543Smrg#include "picturestr.h"
37706f2543Smrg#include "mipict.h"
38706f2543Smrg#include "colormapst.h"
39706f2543Smrg
40706f2543Smrg#define NUM_CUBE_LEVELS	4
41706f2543Smrg#define NUM_GRAY_LEVELS	13
42706f2543Smrg
43706f2543Smrgstatic Bool
44706f2543SmrgmiBuildRenderColormap (ColormapPtr  pColormap, Pixel *pixels, int *nump)
45706f2543Smrg{
46706f2543Smrg    int		r, g, b;
47706f2543Smrg    unsigned short  red, green, blue;
48706f2543Smrg    Pixel	pixel;
49706f2543Smrg    Bool	used[MI_MAX_INDEXED];
50706f2543Smrg    int		needed;
51706f2543Smrg    int		policy;
52706f2543Smrg    int		cube, gray;
53706f2543Smrg    int		i, n;
54706f2543Smrg
55706f2543Smrg    if (pColormap->mid != pColormap->pScreen->defColormap)
56706f2543Smrg    {
57706f2543Smrg	policy = PictureCmapPolicyAll;
58706f2543Smrg    }
59706f2543Smrg    else
60706f2543Smrg    {
61706f2543Smrg	int	avail = pColormap->pVisual->ColormapEntries;
62706f2543Smrg	policy = PictureCmapPolicy;
63706f2543Smrg	if (policy == PictureCmapPolicyDefault)
64706f2543Smrg	{
65706f2543Smrg	    if (avail >= 256 && (pColormap->pVisual->class|DynamicClass) == PseudoColor)
66706f2543Smrg		policy = PictureCmapPolicyColor;
67706f2543Smrg	    else if (avail >= 64)
68706f2543Smrg		policy = PictureCmapPolicyGray;
69706f2543Smrg	    else
70706f2543Smrg		policy = PictureCmapPolicyMono;
71706f2543Smrg	}
72706f2543Smrg    }
73706f2543Smrg    /*
74706f2543Smrg     * Make sure enough cells are free for the chosen policy
75706f2543Smrg     */
76706f2543Smrg    for (;;)
77706f2543Smrg    {
78706f2543Smrg	switch (policy) {
79706f2543Smrg	case PictureCmapPolicyAll:
80706f2543Smrg	    needed = 0;
81706f2543Smrg	    break;
82706f2543Smrg	case PictureCmapPolicyColor:
83706f2543Smrg	    needed = 71;
84706f2543Smrg	    break;
85706f2543Smrg	case PictureCmapPolicyGray:
86706f2543Smrg	    needed = 11;
87706f2543Smrg	    break;
88706f2543Smrg	case PictureCmapPolicyMono:
89706f2543Smrg	default:
90706f2543Smrg	    needed = 0;
91706f2543Smrg	    break;
92706f2543Smrg	}
93706f2543Smrg	if (needed <= pColormap->freeRed)
94706f2543Smrg	    break;
95706f2543Smrg	policy--;
96706f2543Smrg    }
97706f2543Smrg
98706f2543Smrg    /*
99706f2543Smrg     * Compute size of cube and gray ramps
100706f2543Smrg     */
101706f2543Smrg    cube = gray = 0;
102706f2543Smrg    switch (policy) {
103706f2543Smrg    case PictureCmapPolicyAll:
104706f2543Smrg	/*
105706f2543Smrg	 * Allocate as big a cube as possible
106706f2543Smrg	 */
107706f2543Smrg	if ((pColormap->pVisual->class|DynamicClass) == PseudoColor)
108706f2543Smrg	{
109706f2543Smrg	    for (cube = 1; cube * cube * cube < pColormap->pVisual->ColormapEntries; cube++)
110706f2543Smrg		;
111706f2543Smrg	    cube--;
112706f2543Smrg	    if (cube == 1)
113706f2543Smrg		cube = 0;
114706f2543Smrg	}
115706f2543Smrg	else
116706f2543Smrg	    cube = 0;
117706f2543Smrg	/*
118706f2543Smrg	 * Figure out how many gray levels to use so that they
119706f2543Smrg	 * line up neatly with the cube
120706f2543Smrg	 */
121706f2543Smrg	if (cube)
122706f2543Smrg	{
123706f2543Smrg	    needed = pColormap->pVisual->ColormapEntries - (cube*cube*cube);
124706f2543Smrg	    /* levels to fill in with */
125706f2543Smrg	    gray = needed / (cube - 1);
126706f2543Smrg	    /* total levels */
127706f2543Smrg	    gray = (gray + 1) * (cube - 1) + 1;
128706f2543Smrg	}
129706f2543Smrg	else
130706f2543Smrg	    gray = pColormap->pVisual->ColormapEntries;
131706f2543Smrg	break;
132706f2543Smrg
133706f2543Smrg    case PictureCmapPolicyColor:
134706f2543Smrg	cube = NUM_CUBE_LEVELS;
135706f2543Smrg	/* fall through ... */
136706f2543Smrg    case PictureCmapPolicyGray:
137706f2543Smrg	gray = NUM_GRAY_LEVELS;
138706f2543Smrg	break;
139706f2543Smrg    case PictureCmapPolicyMono:
140706f2543Smrg    default:
141706f2543Smrg	gray = 2;
142706f2543Smrg	break;
143706f2543Smrg    }
144706f2543Smrg
145706f2543Smrg    memset (used, '\0', pColormap->pVisual->ColormapEntries * sizeof (Bool));
146706f2543Smrg    for (r = 0; r < cube; r++)
147706f2543Smrg	for (g = 0; g < cube; g++)
148706f2543Smrg	    for (b = 0; b < cube; b++)
149706f2543Smrg	    {
150706f2543Smrg		pixel = 0;
151706f2543Smrg		red = (r * 65535 + (cube-1)/2) / (cube - 1);
152706f2543Smrg		green = (g * 65535 + (cube-1)/2) / (cube - 1);
153706f2543Smrg		blue = (b * 65535 + (cube-1)/2) / (cube - 1);
154706f2543Smrg		if (AllocColor (pColormap, &red, &green,
155706f2543Smrg				&blue, &pixel, 0) != Success)
156706f2543Smrg		    return FALSE;
157706f2543Smrg		used[pixel] = TRUE;
158706f2543Smrg	    }
159706f2543Smrg    for (g = 0; g < gray; g++)
160706f2543Smrg    {
161706f2543Smrg	pixel = 0;
162706f2543Smrg	red = green = blue = (g * 65535 + (gray-1)/2) / (gray - 1);
163706f2543Smrg	if (AllocColor (pColormap, &red, &green, &blue, &pixel, 0) != Success)
164706f2543Smrg	    return FALSE;
165706f2543Smrg	used[pixel] = TRUE;
166706f2543Smrg    }
167706f2543Smrg    n = 0;
168706f2543Smrg    for (i = 0; i < pColormap->pVisual->ColormapEntries; i++)
169706f2543Smrg	if (used[i])
170706f2543Smrg	    pixels[n++] = i;
171706f2543Smrg
172706f2543Smrg    *nump = n;
173706f2543Smrg
174706f2543Smrg    return TRUE;
175706f2543Smrg}
176706f2543Smrg
177706f2543Smrg/* 0 <= red, green, blue < 32 */
178706f2543Smrgstatic Pixel
179706f2543SmrgFindBestColor (miIndexedPtr pIndexed, Pixel *pixels, int num,
180706f2543Smrg	       int red, int green, int blue)
181706f2543Smrg{
182706f2543Smrg    Pixel   best = pixels[0];
183706f2543Smrg    int	    bestDist = 1 << 30;
184706f2543Smrg    int	    dist;
185706f2543Smrg    int	    dr, dg, db;
186706f2543Smrg    while (num--)
187706f2543Smrg    {
188706f2543Smrg	Pixel	pixel = *pixels++;
189706f2543Smrg	CARD32	v = pIndexed->rgba[pixel];
190706f2543Smrg
191706f2543Smrg	dr = ((v >> 19) & 0x1f);
192706f2543Smrg	dg = ((v >> 11) & 0x1f);
193706f2543Smrg	db = ((v >> 3) & 0x1f);
194706f2543Smrg	dr = dr - red;
195706f2543Smrg	dg = dg - green;
196706f2543Smrg	db = db - blue;
197706f2543Smrg	dist = dr * dr + dg * dg + db * db;
198706f2543Smrg	if (dist < bestDist)
199706f2543Smrg	{
200706f2543Smrg	    bestDist = dist;
201706f2543Smrg	    best = pixel;
202706f2543Smrg	}
203706f2543Smrg    }
204706f2543Smrg    return best;
205706f2543Smrg}
206706f2543Smrg
207706f2543Smrg/* 0 <= gray < 32768 */
208706f2543Smrgstatic Pixel
209706f2543SmrgFindBestGray (miIndexedPtr pIndexed, Pixel *pixels, int num, int gray)
210706f2543Smrg{
211706f2543Smrg    Pixel   best = pixels[0];
212706f2543Smrg    int	    bestDist = 1 << 30;
213706f2543Smrg    int	    dist;
214706f2543Smrg    int	    dr;
215706f2543Smrg    int	    r;
216706f2543Smrg
217706f2543Smrg    while (num--)
218706f2543Smrg    {
219706f2543Smrg	Pixel   pixel = *pixels++;
220706f2543Smrg	CARD32	v = pIndexed->rgba[pixel];
221706f2543Smrg
222706f2543Smrg	r = v & 0xff;
223706f2543Smrg	r = r | (r << 8);
224706f2543Smrg	dr = gray - (r >> 1);
225706f2543Smrg	dist = dr * dr;
226706f2543Smrg	if (dist < bestDist)
227706f2543Smrg	{
228706f2543Smrg	    bestDist = dist;
229706f2543Smrg	    best = pixel;
230706f2543Smrg	}
231706f2543Smrg    }
232706f2543Smrg    return best;
233706f2543Smrg}
234706f2543Smrg
235706f2543SmrgBool
236706f2543SmrgmiInitIndexed (ScreenPtr	pScreen,
237706f2543Smrg	       PictFormatPtr	pFormat)
238706f2543Smrg{
239706f2543Smrg    ColormapPtr	    pColormap = pFormat->index.pColormap;
240706f2543Smrg    VisualPtr	    pVisual = pColormap->pVisual;
241706f2543Smrg    miIndexedPtr    pIndexed;
242706f2543Smrg    Pixel	    pixels[MI_MAX_INDEXED];
243706f2543Smrg    xrgb	    rgb[MI_MAX_INDEXED];
244706f2543Smrg    int		    num;
245706f2543Smrg    int		    i;
246706f2543Smrg    Pixel	    p, r, g, b;
247706f2543Smrg
248706f2543Smrg    if (pVisual->ColormapEntries > MI_MAX_INDEXED)
249706f2543Smrg	return FALSE;
250706f2543Smrg
251706f2543Smrg    if (pVisual->class & DynamicClass)
252706f2543Smrg    {
253706f2543Smrg	if (!miBuildRenderColormap (pColormap, pixels, &num))
254706f2543Smrg	    return FALSE;
255706f2543Smrg    }
256706f2543Smrg    else
257706f2543Smrg    {
258706f2543Smrg	num = pVisual->ColormapEntries;
259706f2543Smrg	for (p = 0; p < num; p++)
260706f2543Smrg	    pixels[p] = p;
261706f2543Smrg    }
262706f2543Smrg
263706f2543Smrg    pIndexed = malloc(sizeof (miIndexedRec));
264706f2543Smrg    if (!pIndexed)
265706f2543Smrg	return FALSE;
266706f2543Smrg
267706f2543Smrg    pFormat->index.nvalues = num;
268706f2543Smrg    pFormat->index.pValues = malloc(num * sizeof (xIndexValue));
269706f2543Smrg    if (!pFormat->index.pValues)
270706f2543Smrg    {
271706f2543Smrg	free(pIndexed);
272706f2543Smrg	return FALSE;
273706f2543Smrg    }
274706f2543Smrg
275706f2543Smrg
276706f2543Smrg    /*
277706f2543Smrg     * Build mapping from pixel value to ARGB
278706f2543Smrg     */
279706f2543Smrg    QueryColors (pColormap, num, pixels, rgb, serverClient);
280706f2543Smrg    for (i = 0; i < num; i++)
281706f2543Smrg    {
282706f2543Smrg	p = pixels[i];
283706f2543Smrg	pFormat->index.pValues[i].pixel = p;
284706f2543Smrg	pFormat->index.pValues[i].red   = rgb[i].red;
285706f2543Smrg	pFormat->index.pValues[i].green = rgb[i].green;
286706f2543Smrg	pFormat->index.pValues[i].blue  = rgb[i].blue;
287706f2543Smrg	pFormat->index.pValues[i].alpha = 0xffff;
288706f2543Smrg	pIndexed->rgba[p] = (0xff000000 |
289706f2543Smrg			     ((rgb[i].red   & 0xff00) << 8) |
290706f2543Smrg			     ((rgb[i].green & 0xff00)     ) |
291706f2543Smrg			     ((rgb[i].blue  & 0xff00) >> 8));
292706f2543Smrg    }
293706f2543Smrg
294706f2543Smrg    /*
295706f2543Smrg     * Build mapping from RGB to pixel value.  This could probably be
296706f2543Smrg     * done a bit quicker...
297706f2543Smrg     */
298706f2543Smrg    switch (pVisual->class | DynamicClass) {
299706f2543Smrg    case GrayScale:
300706f2543Smrg	pIndexed->color = FALSE;
301706f2543Smrg	for (r = 0; r < 32768; r++)
302706f2543Smrg	    pIndexed->ent[r] = FindBestGray (pIndexed, pixels, num, r);
303706f2543Smrg	break;
304706f2543Smrg    case PseudoColor:
305706f2543Smrg	pIndexed->color = TRUE;
306706f2543Smrg	p = 0;
307706f2543Smrg	for (r = 0; r < 32; r++)
308706f2543Smrg	    for (g = 0; g < 32; g++)
309706f2543Smrg		for (b = 0; b < 32; b++)
310706f2543Smrg		{
311706f2543Smrg		    pIndexed->ent[p] = FindBestColor (pIndexed, pixels, num,
312706f2543Smrg						      r, g, b);
313706f2543Smrg		    p++;
314706f2543Smrg		}
315706f2543Smrg	break;
316706f2543Smrg    }
317706f2543Smrg    pFormat->index.devPrivate = pIndexed;
318706f2543Smrg    return TRUE;
319706f2543Smrg}
320706f2543Smrg
321706f2543Smrgvoid
322706f2543SmrgmiCloseIndexed (ScreenPtr	pScreen,
323706f2543Smrg		PictFormatPtr	pFormat)
324706f2543Smrg{
325706f2543Smrg    free(pFormat->index.devPrivate);
326706f2543Smrg    pFormat->index.devPrivate = NULL;
327706f2543Smrg    free(pFormat->index.pValues);
328706f2543Smrg    pFormat->index.pValues = NULL;
329706f2543Smrg}
330706f2543Smrg
331706f2543Smrgvoid
332706f2543SmrgmiUpdateIndexed (ScreenPtr	pScreen,
333706f2543Smrg		 PictFormatPtr	pFormat,
334706f2543Smrg		 int		ndef,
335706f2543Smrg		 xColorItem	*pdef)
336706f2543Smrg{
337706f2543Smrg    miIndexedPtr pIndexed = pFormat->index.devPrivate;
338706f2543Smrg
339706f2543Smrg    if (pIndexed)
340706f2543Smrg    {
341706f2543Smrg	while (ndef--)
342706f2543Smrg	{
343706f2543Smrg	    pIndexed->rgba[pdef->pixel] = (0xff000000 |
344706f2543Smrg					   ((pdef->red   & 0xff00) << 8) |
345706f2543Smrg					   ((pdef->green & 0xff00)     ) |
346706f2543Smrg					   ((pdef->blue  & 0xff00) >> 8));
347706f2543Smrg	    pdef++;
348706f2543Smrg	}
349706f2543Smrg    }
350706f2543Smrg}
351706f2543Smrg
352706f2543Smrg#endif /* _MIINDEX_H_ */
353