/* * * 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 #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; 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) { free(pIndexed); return FALSE; } /* * Build mapping from pixel value to ARGB */ QueryColors (pColormap, num, pixels, rgb, serverClient); 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) { free(pFormat->index.devPrivate); pFormat->index.devPrivate = NULL; free(pFormat->index.pValues); pFormat->index.pValues = NULL; } 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_ */