TinyX/render/miindex.c

360 lines
9.4 KiB
C

/*
*
* Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Keith Packard not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Keith Packard makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#ifndef _MIINDEX_H_
#define _MIINDEX_H_
#include "scrnintstr.h"
#include "gcstruct.h"
#include "pixmapstr.h"
#include "windowstr.h"
#include "mi.h"
#include "picturestr.h"
#include "mipict.h"
#include "colormapst.h"
#define NUM_CUBE_LEVELS 4
#define NUM_GRAY_LEVELS 13
static Bool
miBuildRenderColormap(ColormapPtr pColormap, Pixel * pixels, int *nump)
{
int r, g, b;
unsigned short red, green, blue;
Pixel pixel;
Bool used[MI_MAX_INDEXED];
int needed;
int policy;
int cube, gray;
int i, n;
if (pColormap->mid != pColormap->pScreen->defColormap) {
policy = PictureCmapPolicyAll;
}
else {
int avail = pColormap->pVisual->ColormapEntries;
policy = PictureCmapPolicy;
if (policy == PictureCmapPolicyDefault) {
if (avail >= 256 &&
(pColormap->pVisual->class | DynamicClass) == PseudoColor)
policy = PictureCmapPolicyColor;
else if (avail >= 64)
policy = PictureCmapPolicyGray;
else
policy = PictureCmapPolicyMono;
}
}
/*
* Make sure enough cells are free for the chosen policy
*/
for (;;) {
switch (policy) {
case PictureCmapPolicyAll:
needed = 0;
break;
case PictureCmapPolicyColor:
needed = 71;
break;
case PictureCmapPolicyGray:
needed = 11;
break;
case PictureCmapPolicyMono:
default:
needed = 0;
break;
}
if (needed <= pColormap->freeRed)
break;
policy--;
}
/*
* Compute size of cube and gray ramps
*/
cube = gray = 0;
switch (policy) {
case PictureCmapPolicyAll:
/*
* Allocate as big a cube as possible
*/
if ((pColormap->pVisual->class | DynamicClass) == PseudoColor) {
for (cube = 1;
cube * cube * cube < pColormap->pVisual->ColormapEntries;
cube++);
cube--;
if (cube == 1)
cube = 0;
}
else
cube = 0;
/*
* Figure out how many gray levels to use so that they
* line up neatly with the cube
*/
if (cube) {
needed = pColormap->pVisual->ColormapEntries - (cube * cube * cube);
/* levels to fill in with */
gray = needed / (cube - 1);
/* total levels */
gray = (gray + 1) * (cube - 1) + 1;
}
else
gray = pColormap->pVisual->ColormapEntries;
break;
case PictureCmapPolicyColor:
cube = NUM_CUBE_LEVELS;
/* fall through ... */
case PictureCmapPolicyGray:
gray = NUM_GRAY_LEVELS;
break;
case PictureCmapPolicyMono:
default:
gray = 2;
break;
}
memset(used, '\0', pColormap->pVisual->ColormapEntries * sizeof(Bool));
for (r = 0; r < cube; r++)
for (g = 0; g < cube; g++)
for (b = 0; b < cube; b++) {
pixel = 0;
red = (r * 65535 + (cube - 1) / 2) / (cube - 1);
green = (g * 65535 + (cube - 1) / 2) / (cube - 1);
blue = (b * 65535 + (cube - 1) / 2) / (cube - 1);
if (AllocColor(pColormap, &red, &green,
&blue, &pixel, 0) != Success)
return FALSE;
used[pixel] = TRUE;
}
for (g = 0; g < gray; g++) {
pixel = 0;
red = green = blue = (g * 65535 + (gray - 1) / 2) / (gray - 1);
if (AllocColor(pColormap, &red, &green, &blue, &pixel, 0) != Success)
return FALSE;
used[pixel] = TRUE;
}
n = 0;
for (i = 0; i < pColormap->pVisual->ColormapEntries; i++)
if (used[i])
pixels[n++] = i;
*nump = n;
return TRUE;
}
/* 0 <= red, green, blue < 32 */
static Pixel
FindBestColor(miIndexedPtr pIndexed, Pixel * pixels, int num,
int red, int green, int blue)
{
Pixel best = pixels[0];
int bestDist = 1 << 30;
int dist;
int dr, dg, db;
while (num--) {
Pixel pixel = *pixels++;
CARD32 v = pIndexed->rgba[pixel];
dr = ((v >> 19) & 0x1f);
dg = ((v >> 11) & 0x1f);
db = ((v >> 3) & 0x1f);
dr = dr - red;
dg = dg - green;
db = db - blue;
dist = dr * dr + dg * dg + db * db;
if (dist < bestDist) {
bestDist = dist;
best = pixel;
}
}
return best;
}
/* 0 <= gray < 32768 */
static Pixel
FindBestGray(miIndexedPtr pIndexed, Pixel * pixels, int num, int gray)
{
Pixel best = pixels[0];
int bestDist = 1 << 30;
int dist;
int dr;
int r;
while (num--) {
Pixel pixel = *pixels++;
CARD32 v = pIndexed->rgba[pixel];
r = v & 0xff;
r = r | (r << 8);
dr = gray - (r >> 1);
dist = dr * dr;
if (dist < bestDist) {
bestDist = dist;
best = pixel;
}
}
return best;
}
Bool
miInitIndexed(ScreenPtr pScreen, PictFormatPtr pFormat)
{
ColormapPtr pColormap = pFormat->index.pColormap;
VisualPtr pVisual = pColormap->pVisual;
miIndexedPtr pIndexed;
Pixel pixels[MI_MAX_INDEXED];
xrgb rgb[MI_MAX_INDEXED];
int num = 0;
int i;
Pixel p, r, g, b;
if (pVisual->ColormapEntries > MI_MAX_INDEXED)
return FALSE;
if (pVisual->class & DynamicClass) {
if (!miBuildRenderColormap(pColormap, pixels, &num))
return FALSE;
}
else {
num = pVisual->ColormapEntries;
for (p = 0; p < num; p++)
pixels[p] = p;
}
pIndexed = malloc(sizeof(miIndexedRec));
if (!pIndexed)
return FALSE;
pFormat->index.nvalues = num;
pFormat->index.pValues = malloc(num * sizeof(xIndexValue));
if (!pFormat->index.pValues || !num) {
free(pIndexed);
return FALSE;
}
/*
* Build mapping from pixel value to ARGB
*/
QueryColors(pColormap, num, pixels, rgb);
for (i = 0; i < num; i++) {
p = pixels[i];
pFormat->index.pValues[i].pixel = p;
pFormat->index.pValues[i].red = rgb[i].red;
pFormat->index.pValues[i].green = rgb[i].green;
pFormat->index.pValues[i].blue = rgb[i].blue;
pFormat->index.pValues[i].alpha = 0xffff;
pIndexed->rgba[p] = (0xff000000 |
((rgb[i].red & 0xff00) << 8) |
((rgb[i].green & 0xff00)) |
((rgb[i].blue & 0xff00) >> 8));
}
/*
* Build mapping from RGB to pixel value. This could probably be
* done a bit quicker...
*/
switch (pVisual->class | DynamicClass) {
case GrayScale:
pIndexed->color = FALSE;
for (r = 0; r < 32768; r++)
pIndexed->ent[r] = FindBestGray(pIndexed, pixels, num, r);
break;
case PseudoColor:
pIndexed->color = TRUE;
p = 0;
for (r = 0; r < 32; r++)
for (g = 0; g < 32; g++)
for (b = 0; b < 32; b++) {
pIndexed->ent[p] = FindBestColor(pIndexed, pixels, num,
r, g, b);
p++;
}
break;
}
pFormat->index.devPrivate = pIndexed;
return TRUE;
}
void
miCloseIndexed(ScreenPtr pScreen, PictFormatPtr pFormat)
{
if (pFormat->index.devPrivate) {
free(pFormat->index.devPrivate);
pFormat->index.devPrivate = 0;
}
if (pFormat->index.pValues) {
free(pFormat->index.pValues);
pFormat->index.pValues = 0;
}
}
void
miUpdateIndexed(ScreenPtr pScreen,
PictFormatPtr pFormat, int ndef, xColorItem * pdef)
{
miIndexedPtr pIndexed = pFormat->index.devPrivate;
if (pIndexed) {
while (ndef--) {
pIndexed->rgba[pdef->pixel] = (0xff000000 |
((pdef->red & 0xff00) << 8) |
((pdef->green & 0xff00)) |
((pdef->blue & 0xff00) >> 8));
pdef++;
}
}
}
#endif /* _MIINDEX_H_ */