105b261ecSmrg/*
205b261ecSmrg *
305b261ecSmrg * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
405b261ecSmrg *
505b261ecSmrg * Permission to use, copy, modify, distribute, and sell this software and its
605b261ecSmrg * documentation for any purpose is hereby granted without fee, provided that
705b261ecSmrg * the above copyright notice appear in all copies and that both that
805b261ecSmrg * copyright notice and this permission notice appear in supporting
905b261ecSmrg * documentation, and that the name of Keith Packard not be used in
1005b261ecSmrg * advertising or publicity pertaining to distribution of the software without
1105b261ecSmrg * specific, written prior permission.  Keith Packard makes no
1205b261ecSmrg * representations about the suitability of this software for any purpose.  It
1305b261ecSmrg * is provided "as is" without express or implied warranty.
1405b261ecSmrg *
1505b261ecSmrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1605b261ecSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1705b261ecSmrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1805b261ecSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1905b261ecSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
2005b261ecSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2105b261ecSmrg * PERFORMANCE OF THIS SOFTWARE.
2205b261ecSmrg */
2305b261ecSmrg
2405b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
2505b261ecSmrg#include <dix-config.h>
2605b261ecSmrg#endif
2705b261ecSmrg
2805b261ecSmrg#ifndef _MIINDEX_H_
2905b261ecSmrg#define _MIINDEX_H_
3005b261ecSmrg
3105b261ecSmrg#include "scrnintstr.h"
3205b261ecSmrg#include "gcstruct.h"
3305b261ecSmrg#include "pixmapstr.h"
3405b261ecSmrg#include "windowstr.h"
3505b261ecSmrg#include "mi.h"
3605b261ecSmrg#include "picturestr.h"
3705b261ecSmrg#include "mipict.h"
3805b261ecSmrg#include "colormapst.h"
3905b261ecSmrg
4005b261ecSmrg#define NUM_CUBE_LEVELS	4
4105b261ecSmrg#define NUM_GRAY_LEVELS	13
4205b261ecSmrg
4305b261ecSmrgstatic Bool
4435c4bbdfSmrgmiBuildRenderColormap(ColormapPtr pColormap, Pixel * pixels, int *nump)
4505b261ecSmrg{
4635c4bbdfSmrg    int r, g, b;
4735c4bbdfSmrg    unsigned short red, green, blue;
4835c4bbdfSmrg    Pixel pixel;
4935c4bbdfSmrg    Bool used[MI_MAX_INDEXED];
5035c4bbdfSmrg    int needed;
5135c4bbdfSmrg    int policy;
5235c4bbdfSmrg    int cube, gray;
5335c4bbdfSmrg    int i, n;
5435c4bbdfSmrg
5535c4bbdfSmrg    if (pColormap->mid != pColormap->pScreen->defColormap) {
5635c4bbdfSmrg        policy = PictureCmapPolicyAll;
5705b261ecSmrg    }
5835c4bbdfSmrg    else {
5935c4bbdfSmrg        int avail = pColormap->pVisual->ColormapEntries;
6035c4bbdfSmrg
6135c4bbdfSmrg        policy = PictureCmapPolicy;
6235c4bbdfSmrg        if (policy == PictureCmapPolicyDefault) {
6335c4bbdfSmrg            if (avail >= 256 &&
6435c4bbdfSmrg                (pColormap->pVisual->class | DynamicClass) == PseudoColor)
6535c4bbdfSmrg                policy = PictureCmapPolicyColor;
6635c4bbdfSmrg            else if (avail >= 64)
6735c4bbdfSmrg                policy = PictureCmapPolicyGray;
6835c4bbdfSmrg            else
6935c4bbdfSmrg                policy = PictureCmapPolicyMono;
7035c4bbdfSmrg        }
7105b261ecSmrg    }
7205b261ecSmrg    /*
7305b261ecSmrg     * Make sure enough cells are free for the chosen policy
7405b261ecSmrg     */
7535c4bbdfSmrg    for (;;) {
7635c4bbdfSmrg        switch (policy) {
7735c4bbdfSmrg        case PictureCmapPolicyAll:
7835c4bbdfSmrg            needed = 0;
7935c4bbdfSmrg            break;
8035c4bbdfSmrg        case PictureCmapPolicyColor:
8135c4bbdfSmrg            needed = 71;
8235c4bbdfSmrg            break;
8335c4bbdfSmrg        case PictureCmapPolicyGray:
8435c4bbdfSmrg            needed = 11;
8535c4bbdfSmrg            break;
8635c4bbdfSmrg        case PictureCmapPolicyMono:
8735c4bbdfSmrg        default:
8835c4bbdfSmrg            needed = 0;
8935c4bbdfSmrg            break;
9035c4bbdfSmrg        }
9135c4bbdfSmrg        if (needed <= pColormap->freeRed)
9235c4bbdfSmrg            break;
9335c4bbdfSmrg        policy--;
9435c4bbdfSmrg    }
9535c4bbdfSmrg
9605b261ecSmrg    /*
9705b261ecSmrg     * Compute size of cube and gray ramps
9805b261ecSmrg     */
9905b261ecSmrg    cube = gray = 0;
10005b261ecSmrg    switch (policy) {
10105b261ecSmrg    case PictureCmapPolicyAll:
10235c4bbdfSmrg        /*
10335c4bbdfSmrg         * Allocate as big a cube as possible
10435c4bbdfSmrg         */
10535c4bbdfSmrg        if ((pColormap->pVisual->class | DynamicClass) == PseudoColor) {
10635c4bbdfSmrg            for (cube = 1;
10735c4bbdfSmrg                 cube * cube * cube < pColormap->pVisual->ColormapEntries;
10835c4bbdfSmrg                 cube++);
10935c4bbdfSmrg            cube--;
11035c4bbdfSmrg            if (cube == 1)
11135c4bbdfSmrg                cube = 0;
11235c4bbdfSmrg        }
11335c4bbdfSmrg        else
11435c4bbdfSmrg            cube = 0;
11535c4bbdfSmrg        /*
11635c4bbdfSmrg         * Figure out how many gray levels to use so that they
11735c4bbdfSmrg         * line up neatly with the cube
11835c4bbdfSmrg         */
11935c4bbdfSmrg        if (cube) {
12035c4bbdfSmrg            needed = pColormap->pVisual->ColormapEntries - (cube * cube * cube);
12135c4bbdfSmrg            /* levels to fill in with */
12235c4bbdfSmrg            gray = needed / (cube - 1);
12335c4bbdfSmrg            /* total levels */
12435c4bbdfSmrg            gray = (gray + 1) * (cube - 1) + 1;
12535c4bbdfSmrg        }
12635c4bbdfSmrg        else
12735c4bbdfSmrg            gray = pColormap->pVisual->ColormapEntries;
12835c4bbdfSmrg        break;
12935c4bbdfSmrg
13005b261ecSmrg    case PictureCmapPolicyColor:
13135c4bbdfSmrg        cube = NUM_CUBE_LEVELS;
13235c4bbdfSmrg        /* fall through ... */
13305b261ecSmrg    case PictureCmapPolicyGray:
13435c4bbdfSmrg        gray = NUM_GRAY_LEVELS;
13535c4bbdfSmrg        break;
13605b261ecSmrg    case PictureCmapPolicyMono:
13705b261ecSmrg    default:
13835c4bbdfSmrg        gray = 2;
13935c4bbdfSmrg        break;
14005b261ecSmrg    }
14135c4bbdfSmrg
14235c4bbdfSmrg    memset(used, '\0', pColormap->pVisual->ColormapEntries * sizeof(Bool));
14305b261ecSmrg    for (r = 0; r < cube; r++)
14435c4bbdfSmrg        for (g = 0; g < cube; g++)
14535c4bbdfSmrg            for (b = 0; b < cube; b++) {
14635c4bbdfSmrg                pixel = 0;
14735c4bbdfSmrg                red = (r * 65535 + (cube - 1) / 2) / (cube - 1);
14835c4bbdfSmrg                green = (g * 65535 + (cube - 1) / 2) / (cube - 1);
14935c4bbdfSmrg                blue = (b * 65535 + (cube - 1) / 2) / (cube - 1);
15035c4bbdfSmrg                if (AllocColor(pColormap, &red, &green,
15135c4bbdfSmrg                               &blue, &pixel, 0) != Success)
15235c4bbdfSmrg                    return FALSE;
15335c4bbdfSmrg                used[pixel] = TRUE;
15435c4bbdfSmrg            }
15535c4bbdfSmrg    for (g = 0; g < gray; g++) {
15635c4bbdfSmrg        pixel = 0;
15735c4bbdfSmrg        red = green = blue = (g * 65535 + (gray - 1) / 2) / (gray - 1);
15835c4bbdfSmrg        if (AllocColor(pColormap, &red, &green, &blue, &pixel, 0) != Success)
15935c4bbdfSmrg            return FALSE;
16035c4bbdfSmrg        used[pixel] = TRUE;
16105b261ecSmrg    }
16205b261ecSmrg    n = 0;
16305b261ecSmrg    for (i = 0; i < pColormap->pVisual->ColormapEntries; i++)
16435c4bbdfSmrg        if (used[i])
16535c4bbdfSmrg            pixels[n++] = i;
16605b261ecSmrg
16705b261ecSmrg    *nump = n;
16835c4bbdfSmrg
16905b261ecSmrg    return TRUE;
17005b261ecSmrg}
17105b261ecSmrg
17205b261ecSmrg/* 0 <= red, green, blue < 32 */
17305b261ecSmrgstatic Pixel
17435c4bbdfSmrgFindBestColor(miIndexedPtr pIndexed, Pixel * pixels, int num,
17535c4bbdfSmrg              int red, int green, int blue)
17605b261ecSmrg{
17735c4bbdfSmrg    Pixel best = pixels[0];
17835c4bbdfSmrg    int bestDist = 1 << 30;
17935c4bbdfSmrg    int dist;
18035c4bbdfSmrg    int dr, dg, db;
18105b261ecSmrg
18235c4bbdfSmrg    while (num--) {
18335c4bbdfSmrg        Pixel pixel = *pixels++;
18435c4bbdfSmrg        CARD32 v = pIndexed->rgba[pixel];
18535c4bbdfSmrg
18635c4bbdfSmrg        dr = ((v >> 19) & 0x1f);
18735c4bbdfSmrg        dg = ((v >> 11) & 0x1f);
18835c4bbdfSmrg        db = ((v >> 3) & 0x1f);
18935c4bbdfSmrg        dr = dr - red;
19035c4bbdfSmrg        dg = dg - green;
19135c4bbdfSmrg        db = db - blue;
19235c4bbdfSmrg        dist = dr * dr + dg * dg + db * db;
19335c4bbdfSmrg        if (dist < bestDist) {
19435c4bbdfSmrg            bestDist = dist;
19535c4bbdfSmrg            best = pixel;
19635c4bbdfSmrg        }
19705b261ecSmrg    }
19805b261ecSmrg    return best;
19905b261ecSmrg}
20005b261ecSmrg
20105b261ecSmrg/* 0 <= gray < 32768 */
20205b261ecSmrgstatic Pixel
20335c4bbdfSmrgFindBestGray(miIndexedPtr pIndexed, Pixel * pixels, int num, int gray)
20405b261ecSmrg{
20535c4bbdfSmrg    Pixel best = pixels[0];
20635c4bbdfSmrg    int bestDist = 1 << 30;
20735c4bbdfSmrg    int dist;
20835c4bbdfSmrg    int dr;
20935c4bbdfSmrg    int r;
21035c4bbdfSmrg
21135c4bbdfSmrg    while (num--) {
21235c4bbdfSmrg        Pixel pixel = *pixels++;
21335c4bbdfSmrg        CARD32 v = pIndexed->rgba[pixel];
21405b261ecSmrg
21535c4bbdfSmrg        r = v & 0xff;
21635c4bbdfSmrg        r = r | (r << 8);
21735c4bbdfSmrg        dr = gray - (r >> 1);
21835c4bbdfSmrg        dist = dr * dr;
21935c4bbdfSmrg        if (dist < bestDist) {
22035c4bbdfSmrg            bestDist = dist;
22135c4bbdfSmrg            best = pixel;
22235c4bbdfSmrg        }
22305b261ecSmrg    }
22405b261ecSmrg    return best;
22505b261ecSmrg}
22605b261ecSmrg
22705b261ecSmrgBool
22835c4bbdfSmrgmiInitIndexed(ScreenPtr pScreen, PictFormatPtr pFormat)
22905b261ecSmrg{
23035c4bbdfSmrg    ColormapPtr pColormap = pFormat->index.pColormap;
23135c4bbdfSmrg    VisualPtr pVisual = pColormap->pVisual;
23235c4bbdfSmrg    miIndexedPtr pIndexed;
23335c4bbdfSmrg    Pixel pixels[MI_MAX_INDEXED];
23435c4bbdfSmrg    xrgb rgb[MI_MAX_INDEXED];
23535c4bbdfSmrg    int num;
23635c4bbdfSmrg    int i;
23735c4bbdfSmrg    Pixel p, r, g, b;
23805b261ecSmrg
23905b261ecSmrg    if (pVisual->ColormapEntries > MI_MAX_INDEXED)
24035c4bbdfSmrg        return FALSE;
24135c4bbdfSmrg
24235c4bbdfSmrg    if (pVisual->class & DynamicClass) {
24335c4bbdfSmrg        if (!miBuildRenderColormap(pColormap, pixels, &num))
24435c4bbdfSmrg            return FALSE;
24505b261ecSmrg    }
24635c4bbdfSmrg    else {
24735c4bbdfSmrg        num = pVisual->ColormapEntries;
24835c4bbdfSmrg        for (p = 0; p < num; p++)
24935c4bbdfSmrg            pixels[p] = p;
25005b261ecSmrg    }
25135c4bbdfSmrg
25235c4bbdfSmrg    pIndexed = malloc(sizeof(miIndexedRec));
25305b261ecSmrg    if (!pIndexed)
25435c4bbdfSmrg        return FALSE;
25535c4bbdfSmrg
25605b261ecSmrg    pFormat->index.nvalues = num;
25735c4bbdfSmrg    pFormat->index.pValues = xallocarray(num, sizeof(xIndexValue));
25835c4bbdfSmrg    if (!pFormat->index.pValues) {
25935c4bbdfSmrg        free(pIndexed);
26035c4bbdfSmrg        return FALSE;
26105b261ecSmrg    }
26235c4bbdfSmrg
26305b261ecSmrg    /*
26405b261ecSmrg     * Build mapping from pixel value to ARGB
26505b261ecSmrg     */
26635c4bbdfSmrg    QueryColors(pColormap, num, pixels, rgb, serverClient);
26735c4bbdfSmrg    for (i = 0; i < num; i++) {
26835c4bbdfSmrg        p = pixels[i];
26935c4bbdfSmrg        pFormat->index.pValues[i].pixel = p;
27035c4bbdfSmrg        pFormat->index.pValues[i].red = rgb[i].red;
27135c4bbdfSmrg        pFormat->index.pValues[i].green = rgb[i].green;
27235c4bbdfSmrg        pFormat->index.pValues[i].blue = rgb[i].blue;
27335c4bbdfSmrg        pFormat->index.pValues[i].alpha = 0xffff;
27435c4bbdfSmrg        pIndexed->rgba[p] = (0xff000000 |
27535c4bbdfSmrg                             ((rgb[i].red & 0xff00) << 8) |
27635c4bbdfSmrg                             ((rgb[i].green & 0xff00)) |
27735c4bbdfSmrg                             ((rgb[i].blue & 0xff00) >> 8));
27805b261ecSmrg    }
27905b261ecSmrg
28005b261ecSmrg    /*
28105b261ecSmrg     * Build mapping from RGB to pixel value.  This could probably be
28205b261ecSmrg     * done a bit quicker...
28305b261ecSmrg     */
28405b261ecSmrg    switch (pVisual->class | DynamicClass) {
28505b261ecSmrg    case GrayScale:
28635c4bbdfSmrg        pIndexed->color = FALSE;
28735c4bbdfSmrg        for (r = 0; r < 32768; r++)
28835c4bbdfSmrg            pIndexed->ent[r] = FindBestGray(pIndexed, pixels, num, r);
28935c4bbdfSmrg        break;
29005b261ecSmrg    case PseudoColor:
29135c4bbdfSmrg        pIndexed->color = TRUE;
29235c4bbdfSmrg        p = 0;
29335c4bbdfSmrg        for (r = 0; r < 32; r++)
29435c4bbdfSmrg            for (g = 0; g < 32; g++)
29535c4bbdfSmrg                for (b = 0; b < 32; b++) {
29635c4bbdfSmrg                    pIndexed->ent[p] = FindBestColor(pIndexed, pixels, num,
29735c4bbdfSmrg                                                     r, g, b);
29835c4bbdfSmrg                    p++;
29935c4bbdfSmrg                }
30035c4bbdfSmrg        break;
30105b261ecSmrg    }
30205b261ecSmrg    pFormat->index.devPrivate = pIndexed;
30305b261ecSmrg    return TRUE;
30405b261ecSmrg}
30505b261ecSmrg
30605b261ecSmrgvoid
30735c4bbdfSmrgmiCloseIndexed(ScreenPtr pScreen, PictFormatPtr pFormat)
30805b261ecSmrg{
3099ace9065Smrg    free(pFormat->index.devPrivate);
3109ace9065Smrg    pFormat->index.devPrivate = NULL;
3119ace9065Smrg    free(pFormat->index.pValues);
3129ace9065Smrg    pFormat->index.pValues = NULL;
31305b261ecSmrg}
31405b261ecSmrg
31505b261ecSmrgvoid
31635c4bbdfSmrgmiUpdateIndexed(ScreenPtr pScreen,
31735c4bbdfSmrg                PictFormatPtr pFormat, int ndef, xColorItem * pdef)
31805b261ecSmrg{
31905b261ecSmrg    miIndexedPtr pIndexed = pFormat->index.devPrivate;
32005b261ecSmrg
32135c4bbdfSmrg    if (pIndexed) {
32235c4bbdfSmrg        while (ndef--) {
32335c4bbdfSmrg            pIndexed->rgba[pdef->pixel] = (0xff000000 |
32435c4bbdfSmrg                                           ((pdef->red & 0xff00) << 8) |
32535c4bbdfSmrg                                           ((pdef->green & 0xff00)) |
32635c4bbdfSmrg                                           ((pdef->blue & 0xff00) >> 8));
32735c4bbdfSmrg            pdef++;
32835c4bbdfSmrg        }
32905b261ecSmrg    }
33005b261ecSmrg}
33105b261ecSmrg
33235c4bbdfSmrg#endif                          /* _MIINDEX_H_ */
333