1 /*
2  * Copyright (c) 1998-2001 by The XFree86 Project, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Except as contained in this notice, the name of the copyright holder(s)
23  * and author(s) shall not be used in advertising or otherwise to promote
24  * the sale, use or other dealings in this Software without prior written
25  * authorization from the copyright holder(s) and author(s).
26  */
27 
28 #ifdef HAVE_XORG_CONFIG_H
29 #include <xorg-config.h>
30 #endif
31 
32 #if defined(_XOPEN_SOURCE) || defined(__sun) && defined(__SVR4)
33 #include <math.h>
34 #else
35 #define _XOPEN_SOURCE           /* to get prototype for pow on some systems */
36 #include <math.h>
37 #undef _XOPEN_SOURCE
38 #endif
39 
40 #include <X11/X.h>
41 #include "misc.h"
42 #include <X11/Xproto.h>
43 #include "colormapst.h"
44 #include "scrnintstr.h"
45 
46 #include "resource.h"
47 
48 #include "xf86.h"
49 #include "xf86_OSproc.h"
50 #include "xf86str.h"
51 #include "micmap.h"
52 #include "xf86RandR12.h"
53 #include "xf86Crtc.h"
54 
55 #ifdef XFreeXDGA
56 #include <X11/extensions/xf86dgaproto.h>
57 #include "dgaproc.h"
58 #endif
59 
60 #include "xf86cmap.h"
61 
62 #define SCREEN_PROLOGUE(pScreen, field) ((pScreen)->field = \
63     ((CMapScreenPtr)dixLookupPrivate(&(pScreen)->devPrivates, CMapScreenKey))->field)
64 #define SCREEN_EPILOGUE(pScreen, field, wrapper)\
65     ((pScreen)->field = wrapper)
66 
67 #define LOAD_PALETTE(pmap) \
68     ((pmap == GetInstalledmiColormap(pmap->pScreen)) && \
69      ((pScreenPriv->flags & CMAP_LOAD_EVEN_IF_OFFSCREEN) || \
70       xf86ScreenToScrn(pmap->pScreen)->vtSema || pScreenPriv->isDGAmode))
71 
72 typedef struct _CMapLink {
73     ColormapPtr cmap;
74     struct _CMapLink *next;
75 } CMapLink, *CMapLinkPtr;
76 
77 typedef struct {
78     CloseScreenProcPtr CloseScreen;
79     CreateColormapProcPtr CreateColormap;
80     DestroyColormapProcPtr DestroyColormap;
81     InstallColormapProcPtr InstallColormap;
82     StoreColorsProcPtr StoreColors;
83     Bool (*EnterVT) (ScrnInfoPtr);
84     Bool (*SwitchMode) (ScrnInfoPtr, DisplayModePtr);
85     int (*SetDGAMode) (ScrnInfoPtr, int, DGADevicePtr);
86     xf86ChangeGammaProc *ChangeGamma;
87     int maxColors;
88     int sigRGBbits;
89     int gammaElements;
90     LOCO *gamma;
91     int *PreAllocIndices;
92     CMapLinkPtr maps;
93     unsigned int flags;
94     Bool isDGAmode;
95 } CMapScreenRec, *CMapScreenPtr;
96 
97 typedef struct {
98     int numColors;
99     LOCO *colors;
100     Bool recalculate;
101     int overscan;
102 } CMapColormapRec, *CMapColormapPtr;
103 
104 static DevPrivateKeyRec CMapScreenKeyRec;
105 
106 #define CMapScreenKeyRegistered dixPrivateKeyRegistered(&CMapScreenKeyRec)
107 #define CMapScreenKey (&CMapScreenKeyRec)
108 static DevPrivateKeyRec CMapColormapKeyRec;
109 
110 #define CMapColormapKey (&CMapColormapKeyRec)
111 
112 static void CMapInstallColormap(ColormapPtr);
113 static void CMapStoreColors(ColormapPtr, int, xColorItem *);
114 static Bool CMapCloseScreen(ScreenPtr);
115 static Bool CMapCreateColormap(ColormapPtr);
116 static void CMapDestroyColormap(ColormapPtr);
117 
118 static Bool CMapEnterVT(ScrnInfoPtr);
119 static Bool CMapSwitchMode(ScrnInfoPtr, DisplayModePtr);
120 
121 #ifdef XFreeXDGA
122 static int CMapSetDGAMode(ScrnInfoPtr, int, DGADevicePtr);
123 #endif
124 static int CMapChangeGamma(ScrnInfoPtr, Gamma);
125 
126 static void ComputeGamma(ScrnInfoPtr, CMapScreenPtr);
127 static Bool CMapAllocateColormapPrivate(ColormapPtr);
128 static void CMapRefreshColors(ColormapPtr, int, int *);
129 static void CMapSetOverscan(ColormapPtr, int, int *);
130 static void CMapReinstallMap(ColormapPtr);
131 static void CMapUnwrapScreen(ScreenPtr pScreen);
132 
133 Bool
xf86ColormapAllocatePrivates(ScrnInfoPtr pScrn)134 xf86ColormapAllocatePrivates(ScrnInfoPtr pScrn)
135 {
136     if (!dixRegisterPrivateKey(&CMapScreenKeyRec, PRIVATE_SCREEN, 0))
137         return FALSE;
138 
139     if (!dixRegisterPrivateKey(&CMapColormapKeyRec, PRIVATE_COLORMAP, 0))
140         return FALSE;
141     return TRUE;
142 }
143 
144 Bool
xf86HandleColormaps(ScreenPtr pScreen,int maxColors,int sigRGBbits,xf86LoadPaletteProc * loadPalette,xf86SetOverscanProc * setOverscan,unsigned int flags)145 xf86HandleColormaps(ScreenPtr pScreen,
146                     int maxColors,
147                     int sigRGBbits,
148                     xf86LoadPaletteProc * loadPalette,
149                     xf86SetOverscanProc * setOverscan, unsigned int flags)
150 {
151     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
152     ColormapPtr pDefMap = NULL;
153     CMapScreenPtr pScreenPriv;
154     LOCO *gamma;
155     int *indices;
156     int elements;
157 
158     if (!maxColors || !sigRGBbits ||
159         (!loadPalette && !xf86_crtc_supports_gamma(pScrn)))
160         return FALSE;
161 
162     elements = 1 << sigRGBbits;
163 
164     if (!(gamma = xallocarray(elements, sizeof(LOCO))))
165         return FALSE;
166 
167     if (!(indices = xallocarray(maxColors, sizeof(int)))) {
168         free(gamma);
169         return FALSE;
170     }
171 
172     if (!(pScreenPriv = malloc(sizeof(CMapScreenRec)))) {
173         free(gamma);
174         free(indices);
175         return FALSE;
176     }
177 
178     dixSetPrivate(&pScreen->devPrivates, &CMapScreenKeyRec, pScreenPriv);
179 
180     pScreenPriv->CloseScreen = pScreen->CloseScreen;
181     pScreenPriv->CreateColormap = pScreen->CreateColormap;
182     pScreenPriv->DestroyColormap = pScreen->DestroyColormap;
183     pScreenPriv->InstallColormap = pScreen->InstallColormap;
184     pScreenPriv->StoreColors = pScreen->StoreColors;
185     pScreen->CloseScreen = CMapCloseScreen;
186     pScreen->CreateColormap = CMapCreateColormap;
187     pScreen->DestroyColormap = CMapDestroyColormap;
188     pScreen->InstallColormap = CMapInstallColormap;
189     pScreen->StoreColors = CMapStoreColors;
190 
191     pScrn->LoadPalette = loadPalette;
192     pScrn->SetOverscan = setOverscan;
193     pScreenPriv->maxColors = maxColors;
194     pScreenPriv->sigRGBbits = sigRGBbits;
195     pScreenPriv->gammaElements = elements;
196     pScreenPriv->gamma = gamma;
197     pScreenPriv->PreAllocIndices = indices;
198     pScreenPriv->maps = NULL;
199     pScreenPriv->flags = flags;
200     pScreenPriv->isDGAmode = FALSE;
201 
202     pScreenPriv->EnterVT = pScrn->EnterVT;
203     pScreenPriv->SwitchMode = pScrn->SwitchMode;
204     pScreenPriv->SetDGAMode = pScrn->SetDGAMode;
205     pScreenPriv->ChangeGamma = pScrn->ChangeGamma;
206 
207     if (!(flags & CMAP_LOAD_EVEN_IF_OFFSCREEN)) {
208         pScrn->EnterVT = CMapEnterVT;
209         if ((flags & CMAP_RELOAD_ON_MODE_SWITCH) && pScrn->SwitchMode)
210             pScrn->SwitchMode = CMapSwitchMode;
211     }
212 #ifdef XFreeXDGA
213     pScrn->SetDGAMode = CMapSetDGAMode;
214 #endif
215     pScrn->ChangeGamma = CMapChangeGamma;
216 
217     ComputeGamma(pScrn, pScreenPriv);
218 
219     /* get the default map */
220     dixLookupResourceByType((void **) &pDefMap, pScreen->defColormap,
221                             RT_COLORMAP, serverClient, DixInstallAccess);
222 
223     if (!CMapAllocateColormapPrivate(pDefMap)) {
224         CMapUnwrapScreen(pScreen);
225         return FALSE;
226     }
227 
228     if (xf86_crtc_supports_gamma(pScrn)) {
229         pScrn->LoadPalette = xf86RandR12LoadPalette;
230 
231         if (!xf86RandR12InitGamma(pScrn, elements)) {
232             CMapUnwrapScreen(pScreen);
233             return FALSE;
234         }
235     }
236 
237     /* Force the initial map to be loaded */
238     SetInstalledmiColormap(pScreen, NULL);
239     CMapInstallColormap(pDefMap);
240     return TRUE;
241 }
242 
243 /**** Screen functions ****/
244 
245 static Bool
CMapCloseScreen(ScreenPtr pScreen)246 CMapCloseScreen(ScreenPtr pScreen)
247 {
248     CMapUnwrapScreen(pScreen);
249 
250     return (*pScreen->CloseScreen) (pScreen);
251 }
252 
253 static Bool
CMapColormapUseMax(VisualPtr pVisual,CMapScreenPtr pScreenPriv)254 CMapColormapUseMax(VisualPtr pVisual, CMapScreenPtr pScreenPriv)
255 {
256     if (pVisual->nplanes > 16)
257         return TRUE;
258     return ((1 << pVisual->nplanes) > pScreenPriv->maxColors);
259 }
260 
261 static Bool
CMapAllocateColormapPrivate(ColormapPtr pmap)262 CMapAllocateColormapPrivate(ColormapPtr pmap)
263 {
264     CMapScreenPtr pScreenPriv =
265         (CMapScreenPtr) dixLookupPrivate(&pmap->pScreen->devPrivates,
266                                          CMapScreenKey);
267     CMapColormapPtr pColPriv;
268     CMapLinkPtr pLink;
269     int numColors;
270     LOCO *colors;
271 
272     if (CMapColormapUseMax(pmap->pVisual, pScreenPriv))
273         numColors = pmap->pVisual->ColormapEntries;
274     else
275         numColors = 1 << pmap->pVisual->nplanes;
276 
277     if (!(colors = xallocarray(numColors, sizeof(LOCO))))
278         return FALSE;
279 
280     if (!(pColPriv = malloc(sizeof(CMapColormapRec)))) {
281         free(colors);
282         return FALSE;
283     }
284 
285     dixSetPrivate(&pmap->devPrivates, CMapColormapKey, pColPriv);
286 
287     pColPriv->numColors = numColors;
288     pColPriv->colors = colors;
289     pColPriv->recalculate = TRUE;
290     pColPriv->overscan = -1;
291 
292     /* add map to list */
293     pLink = malloc(sizeof(CMapLink));
294     if (pLink) {
295         pLink->cmap = pmap;
296         pLink->next = pScreenPriv->maps;
297         pScreenPriv->maps = pLink;
298     }
299 
300     return TRUE;
301 }
302 
303 static Bool
CMapCreateColormap(ColormapPtr pmap)304 CMapCreateColormap(ColormapPtr pmap)
305 {
306     ScreenPtr pScreen = pmap->pScreen;
307     CMapScreenPtr pScreenPriv =
308         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
309     Bool ret = FALSE;
310 
311     pScreen->CreateColormap = pScreenPriv->CreateColormap;
312     if ((*pScreen->CreateColormap) (pmap)) {
313         if (CMapAllocateColormapPrivate(pmap))
314             ret = TRUE;
315     }
316     pScreen->CreateColormap = CMapCreateColormap;
317 
318     return ret;
319 }
320 
321 static void
CMapDestroyColormap(ColormapPtr cmap)322 CMapDestroyColormap(ColormapPtr cmap)
323 {
324     ScreenPtr pScreen = cmap->pScreen;
325     CMapScreenPtr pScreenPriv =
326         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
327     CMapColormapPtr pColPriv =
328         (CMapColormapPtr) dixLookupPrivate(&cmap->devPrivates, CMapColormapKey);
329     CMapLinkPtr prevLink = NULL, pLink = pScreenPriv->maps;
330 
331     if (pColPriv) {
332         free(pColPriv->colors);
333         free(pColPriv);
334     }
335 
336     /* remove map from list */
337     while (pLink) {
338         if (pLink->cmap == cmap) {
339             if (prevLink)
340                 prevLink->next = pLink->next;
341             else
342                 pScreenPriv->maps = pLink->next;
343             free(pLink);
344             break;
345         }
346         prevLink = pLink;
347         pLink = pLink->next;
348     }
349 
350     if (pScreenPriv->DestroyColormap) {
351         pScreen->DestroyColormap = pScreenPriv->DestroyColormap;
352         (*pScreen->DestroyColormap) (cmap);
353         pScreen->DestroyColormap = CMapDestroyColormap;
354     }
355 }
356 
357 static void
CMapStoreColors(ColormapPtr pmap,int ndef,xColorItem * pdefs)358 CMapStoreColors(ColormapPtr pmap, int ndef, xColorItem * pdefs)
359 {
360     ScreenPtr pScreen = pmap->pScreen;
361     VisualPtr pVisual = pmap->pVisual;
362     CMapScreenPtr pScreenPriv =
363         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
364     int *indices = pScreenPriv->PreAllocIndices;
365     int num = ndef;
366 
367     /* At the moment this isn't necessary since there's nobody below us */
368     pScreen->StoreColors = pScreenPriv->StoreColors;
369     (*pScreen->StoreColors) (pmap, ndef, pdefs);
370     pScreen->StoreColors = CMapStoreColors;
371 
372     /* should never get here for these */
373     if ((pVisual->class == TrueColor) ||
374         (pVisual->class == StaticColor) || (pVisual->class == StaticGray))
375         return;
376 
377     if (pVisual->class == DirectColor) {
378         CMapColormapPtr pColPriv =
379             (CMapColormapPtr) dixLookupPrivate(&pmap->devPrivates,
380                                                CMapColormapKey);
381         int i;
382 
383         if (CMapColormapUseMax(pVisual, pScreenPriv)) {
384             int index;
385 
386             num = 0;
387             while (ndef--) {
388                 if (pdefs[ndef].flags & DoRed) {
389                     index = (pdefs[ndef].pixel & pVisual->redMask) >>
390                         pVisual->offsetRed;
391                     i = num;
392                     while (i--)
393                         if (indices[i] == index)
394                             break;
395                     if (i == -1)
396                         indices[num++] = index;
397                 }
398                 if (pdefs[ndef].flags & DoGreen) {
399                     index = (pdefs[ndef].pixel & pVisual->greenMask) >>
400                         pVisual->offsetGreen;
401                     i = num;
402                     while (i--)
403                         if (indices[i] == index)
404                             break;
405                     if (i == -1)
406                         indices[num++] = index;
407                 }
408                 if (pdefs[ndef].flags & DoBlue) {
409                     index = (pdefs[ndef].pixel & pVisual->blueMask) >>
410                         pVisual->offsetBlue;
411                     i = num;
412                     while (i--)
413                         if (indices[i] == index)
414                             break;
415                     if (i == -1)
416                         indices[num++] = index;
417                 }
418             }
419 
420         }
421         else {
422             /* not really as overkill as it seems */
423             num = pColPriv->numColors;
424             for (i = 0; i < pColPriv->numColors; i++)
425                 indices[i] = i;
426         }
427     }
428     else {
429         while (ndef--)
430             indices[ndef] = pdefs[ndef].pixel;
431     }
432 
433     CMapRefreshColors(pmap, num, indices);
434 }
435 
436 static void
CMapInstallColormap(ColormapPtr pmap)437 CMapInstallColormap(ColormapPtr pmap)
438 {
439     ScreenPtr pScreen = pmap->pScreen;
440     CMapScreenPtr pScreenPriv =
441         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
442 
443     if (pmap == GetInstalledmiColormap(pmap->pScreen))
444         return;
445 
446     pScreen->InstallColormap = pScreenPriv->InstallColormap;
447     (*pScreen->InstallColormap) (pmap);
448     pScreen->InstallColormap = CMapInstallColormap;
449 
450     /* Important. We let the lower layers, namely DGA,
451        overwrite the choice of Colormap to install */
452     if (GetInstalledmiColormap(pmap->pScreen))
453         pmap = GetInstalledmiColormap(pmap->pScreen);
454 
455     if (!(pScreenPriv->flags & CMAP_PALETTED_TRUECOLOR) &&
456         (pmap->pVisual->class == TrueColor) &&
457         CMapColormapUseMax(pmap->pVisual, pScreenPriv))
458         return;
459 
460     if (LOAD_PALETTE(pmap))
461         CMapReinstallMap(pmap);
462 }
463 
464 /**** ScrnInfoRec functions ****/
465 
466 static Bool
CMapEnterVT(ScrnInfoPtr pScrn)467 CMapEnterVT(ScrnInfoPtr pScrn)
468 {
469     ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
470     Bool ret;
471     CMapScreenPtr pScreenPriv =
472         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
473 
474     pScrn->EnterVT = pScreenPriv->EnterVT;
475     ret = (*pScreenPriv->EnterVT) (pScrn);
476     pScreenPriv->EnterVT = pScrn->EnterVT;
477     pScrn->EnterVT = CMapEnterVT;
478     if (ret) {
479         if (GetInstalledmiColormap(pScreen))
480             CMapReinstallMap(GetInstalledmiColormap(pScreen));
481         return TRUE;
482     }
483     return FALSE;
484 }
485 
486 static Bool
CMapSwitchMode(ScrnInfoPtr pScrn,DisplayModePtr mode)487 CMapSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
488 {
489     ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
490     CMapScreenPtr pScreenPriv =
491         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
492 
493     if ((*pScreenPriv->SwitchMode) (pScrn, mode)) {
494         if (GetInstalledmiColormap(pScreen))
495             CMapReinstallMap(GetInstalledmiColormap(pScreen));
496         return TRUE;
497     }
498     return FALSE;
499 }
500 
501 #ifdef XFreeXDGA
502 static int
CMapSetDGAMode(ScrnInfoPtr pScrn,int num,DGADevicePtr dev)503 CMapSetDGAMode(ScrnInfoPtr pScrn, int num, DGADevicePtr dev)
504 {
505     ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
506     CMapScreenPtr pScreenPriv =
507         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
508     int ret;
509 
510     ret = (*pScreenPriv->SetDGAMode) (pScrn, num, dev);
511 
512     pScreenPriv->isDGAmode = DGAActive(pScrn->scrnIndex);
513 
514     if (!pScreenPriv->isDGAmode && GetInstalledmiColormap(pScreen)
515         && xf86ScreenToScrn(pScreen)->vtSema)
516         CMapReinstallMap(GetInstalledmiColormap(pScreen));
517 
518     return ret;
519 }
520 #endif
521 
522 /**** Utilities ****/
523 
524 static void
CMapReinstallMap(ColormapPtr pmap)525 CMapReinstallMap(ColormapPtr pmap)
526 {
527     CMapScreenPtr pScreenPriv =
528         (CMapScreenPtr) dixLookupPrivate(&pmap->pScreen->devPrivates,
529                                          CMapScreenKey);
530     CMapColormapPtr cmapPriv =
531         (CMapColormapPtr) dixLookupPrivate(&pmap->devPrivates, CMapColormapKey);
532     ScrnInfoPtr pScrn = xf86ScreenToScrn(pmap->pScreen);
533     int i = cmapPriv->numColors;
534     int *indices = pScreenPriv->PreAllocIndices;
535 
536     while (i--)
537         indices[i] = i;
538 
539     if (cmapPriv->recalculate)
540         CMapRefreshColors(pmap, cmapPriv->numColors, indices);
541     else {
542         (*pScrn->LoadPalette) (pScrn, cmapPriv->numColors,
543                                indices, cmapPriv->colors, pmap->pVisual);
544         if (pScrn->SetOverscan) {
545 #ifdef DEBUGOVERSCAN
546             ErrorF("SetOverscan() called from CMapReinstallMap\n");
547 #endif
548             pScrn->SetOverscan(pScrn, cmapPriv->overscan);
549         }
550     }
551 
552     cmapPriv->recalculate = FALSE;
553 }
554 
555 static void
CMapRefreshColors(ColormapPtr pmap,int defs,int * indices)556 CMapRefreshColors(ColormapPtr pmap, int defs, int *indices)
557 {
558     CMapScreenPtr pScreenPriv =
559         (CMapScreenPtr) dixLookupPrivate(&pmap->pScreen->devPrivates,
560                                          CMapScreenKey);
561     CMapColormapPtr pColPriv =
562         (CMapColormapPtr) dixLookupPrivate(&pmap->devPrivates, CMapColormapKey);
563     VisualPtr pVisual = pmap->pVisual;
564     ScrnInfoPtr pScrn = xf86ScreenToScrn(pmap->pScreen);
565     int numColors, i;
566     LOCO *gamma, *colors;
567     EntryPtr entry;
568     int reds, greens, blues, maxValue, index, shift;
569 
570     numColors = pColPriv->numColors;
571     shift = 16 - pScreenPriv->sigRGBbits;
572     maxValue = (1 << pScreenPriv->sigRGBbits) - 1;
573     gamma = pScreenPriv->gamma;
574     colors = pColPriv->colors;
575 
576     reds = pVisual->redMask >> pVisual->offsetRed;
577     greens = pVisual->greenMask >> pVisual->offsetGreen;
578     blues = pVisual->blueMask >> pVisual->offsetBlue;
579 
580     switch (pVisual->class) {
581     case StaticGray:
582         for (i = 0; i < numColors; i++) {
583             index = (i + 1) * maxValue / numColors;
584             colors[i].red = gamma[index].red;
585             colors[i].green = gamma[index].green;
586             colors[i].blue = gamma[index].blue;
587         }
588         break;
589     case TrueColor:
590         if (CMapColormapUseMax(pVisual, pScreenPriv)) {
591             for (i = 0; i <= reds; i++)
592                 colors[i].red = gamma[i * maxValue / reds].red;
593             for (i = 0; i <= greens; i++)
594                 colors[i].green = gamma[i * maxValue / greens].green;
595             for (i = 0; i <= blues; i++)
596                 colors[i].blue = gamma[i * maxValue / blues].blue;
597             break;
598         }
599         for (i = 0; i < numColors; i++) {
600             colors[i].red = gamma[((i >> pVisual->offsetRed) & reds) *
601                                   maxValue / reds].red;
602             colors[i].green = gamma[((i >> pVisual->offsetGreen) & greens) *
603                                     maxValue / greens].green;
604             colors[i].blue = gamma[((i >> pVisual->offsetBlue) & blues) *
605                                    maxValue / blues].blue;
606         }
607         break;
608     case StaticColor:
609     case PseudoColor:
610     case GrayScale:
611         for (i = 0; i < defs; i++) {
612             index = indices[i];
613             entry = (EntryPtr) &pmap->red[index];
614 
615             if (entry->fShared) {
616                 colors[index].red =
617                     gamma[entry->co.shco.red->color >> shift].red;
618                 colors[index].green =
619                     gamma[entry->co.shco.green->color >> shift].green;
620                 colors[index].blue =
621                     gamma[entry->co.shco.blue->color >> shift].blue;
622             }
623             else {
624                 colors[index].red = gamma[entry->co.local.red >> shift].red;
625                 colors[index].green =
626                     gamma[entry->co.local.green >> shift].green;
627                 colors[index].blue = gamma[entry->co.local.blue >> shift].blue;
628             }
629         }
630         break;
631     case DirectColor:
632         if (CMapColormapUseMax(pVisual, pScreenPriv)) {
633             for (i = 0; i < defs; i++) {
634                 index = indices[i];
635                 if (index <= reds)
636                     colors[index].red =
637                         gamma[pmap->red[index].co.local.red >> shift].red;
638                 if (index <= greens)
639                     colors[index].green =
640                         gamma[pmap->green[index].co.local.green >> shift].green;
641                 if (index <= blues)
642                     colors[index].blue =
643                         gamma[pmap->blue[index].co.local.blue >> shift].blue;
644 
645             }
646             break;
647         }
648         for (i = 0; i < defs; i++) {
649             index = indices[i];
650 
651             colors[index].red = gamma[pmap->red[(index >> pVisual->
652                                                  offsetRed) & reds].co.local.
653                                       red >> shift].red;
654             colors[index].green =
655                 gamma[pmap->green[(index >> pVisual->offsetGreen) & greens].co.
656                       local.green >> shift].green;
657             colors[index].blue =
658                 gamma[pmap->blue[(index >> pVisual->offsetBlue) & blues].co.
659                       local.blue >> shift].blue;
660         }
661         break;
662     }
663 
664     if (LOAD_PALETTE(pmap))
665         (*pScrn->LoadPalette) (pScrn, defs, indices, colors, pmap->pVisual);
666 
667     if (pScrn->SetOverscan)
668         CMapSetOverscan(pmap, defs, indices);
669 
670 }
671 
672 static Bool
CMapCompareColors(LOCO * color1,LOCO * color2)673 CMapCompareColors(LOCO * color1, LOCO * color2)
674 {
675     /* return TRUE if the color1 is "closer" to black than color2 */
676 #ifdef DEBUGOVERSCAN
677     ErrorF("#%02x%02x%02x vs #%02x%02x%02x (%d vs %d)\n",
678            color1->red, color1->green, color1->blue,
679            color2->red, color2->green, color2->blue,
680            color1->red + color1->green + color1->blue,
681            color2->red + color2->green + color2->blue);
682 #endif
683     return (color1->red + color1->green + color1->blue <
684             color2->red + color2->green + color2->blue);
685 }
686 
687 static void
CMapSetOverscan(ColormapPtr pmap,int defs,int * indices)688 CMapSetOverscan(ColormapPtr pmap, int defs, int *indices)
689 {
690     CMapScreenPtr pScreenPriv =
691         (CMapScreenPtr) dixLookupPrivate(&pmap->pScreen->devPrivates,
692                                          CMapScreenKey);
693     CMapColormapPtr pColPriv =
694         (CMapColormapPtr) dixLookupPrivate(&pmap->devPrivates, CMapColormapKey);
695     ScrnInfoPtr pScrn = xf86ScreenToScrn(pmap->pScreen);
696     VisualPtr pVisual = pmap->pVisual;
697     int i;
698     LOCO *colors;
699     int index;
700     Bool newOverscan = FALSE;
701     int overscan, tmpOverscan;
702 
703     colors = pColPriv->colors;
704     overscan = pColPriv->overscan;
705 
706     /*
707      * Search for a new overscan index in the following cases:
708      *
709      *   - The index hasn't yet been initialised.  In this case search
710      *     for an index that is black or a close match to black.
711      *
712      *   - The colour of the old index is changed.  In this case search
713      *     all indices for a black or close match to black.
714      *
715      *   - The colour of the old index wasn't black.  In this case only
716      *     search the indices that were changed for a better match to black.
717      */
718 
719     switch (pVisual->class) {
720     case StaticGray:
721     case TrueColor:
722         /* Should only come here once.  Initialise the overscan index to 0 */
723         overscan = 0;
724         newOverscan = TRUE;
725         break;
726     case StaticColor:
727         /*
728          * Only come here once, but search for the overscan in the same way
729          * as for the other cases.
730          */
731     case DirectColor:
732     case PseudoColor:
733     case GrayScale:
734         if (overscan < 0 || overscan > pScreenPriv->maxColors - 1) {
735             /* Uninitialised */
736             newOverscan = TRUE;
737         }
738         else {
739             /* Check if the overscan was changed */
740             for (i = 0; i < defs; i++) {
741                 index = indices[i];
742                 if (index == overscan) {
743                     newOverscan = TRUE;
744                     break;
745                 }
746             }
747         }
748         if (newOverscan) {
749             /* The overscan is either uninitialised or it has been changed */
750 
751             if (overscan < 0 || overscan > pScreenPriv->maxColors - 1)
752                 tmpOverscan = pScreenPriv->maxColors - 1;
753             else
754                 tmpOverscan = overscan;
755 
756             /* search all entries for a close match to black */
757             for (i = pScreenPriv->maxColors - 1; i >= 0; i--) {
758                 if (colors[i].red == 0 && colors[i].green == 0 &&
759                     colors[i].blue == 0) {
760                     overscan = i;
761 #ifdef DEBUGOVERSCAN
762                     ErrorF("Black found at index 0x%02x\n", i);
763 #endif
764                     break;
765                 }
766                 else {
767 #ifdef DEBUGOVERSCAN
768                     ErrorF("0x%02x: ", i);
769 #endif
770                     if (CMapCompareColors(&colors[i], &colors[tmpOverscan])) {
771                         tmpOverscan = i;
772 #ifdef DEBUGOVERSCAN
773                         ErrorF("possible \"Black\" at index 0x%02x\n", i);
774 #endif
775                     }
776                 }
777             }
778             if (i < 0)
779                 overscan = tmpOverscan;
780         }
781         else {
782             /* Check of the old overscan wasn't black */
783             if (colors[overscan].red != 0 || colors[overscan].green != 0 ||
784                 colors[overscan].blue != 0) {
785                 int oldOverscan = tmpOverscan = overscan;
786 
787                 /* See of there is now a better match */
788                 for (i = 0; i < defs; i++) {
789                     index = indices[i];
790                     if (colors[index].red == 0 && colors[index].green == 0 &&
791                         colors[index].blue == 0) {
792                         overscan = index;
793 #ifdef DEBUGOVERSCAN
794                         ErrorF("Black found at index 0x%02x\n", index);
795 #endif
796                         break;
797                     }
798                     else {
799 #ifdef DEBUGOVERSCAN
800                         ErrorF("0x%02x: ", index);
801 #endif
802                         if (CMapCompareColors(&colors[index],
803                                               &colors[tmpOverscan])) {
804                             tmpOverscan = index;
805 #ifdef DEBUGOVERSCAN
806                             ErrorF("possible \"Black\" at index 0x%02x\n",
807                                    index);
808 #endif
809                         }
810                     }
811                 }
812                 if (i == defs)
813                     overscan = tmpOverscan;
814                 if (overscan != oldOverscan)
815                     newOverscan = TRUE;
816             }
817         }
818         break;
819     }
820     if (newOverscan) {
821         pColPriv->overscan = overscan;
822         if (LOAD_PALETTE(pmap)) {
823 #ifdef DEBUGOVERSCAN
824             ErrorF("SetOverscan() called from CmapSetOverscan\n");
825 #endif
826             pScrn->SetOverscan(pScrn, overscan);
827         }
828     }
829 }
830 
831 static void
CMapUnwrapScreen(ScreenPtr pScreen)832 CMapUnwrapScreen(ScreenPtr pScreen)
833 {
834     CMapScreenPtr pScreenPriv =
835         (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates, CMapScreenKey);
836     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
837 
838     pScreen->CloseScreen = pScreenPriv->CloseScreen;
839     pScreen->CreateColormap = pScreenPriv->CreateColormap;
840     pScreen->DestroyColormap = pScreenPriv->DestroyColormap;
841     pScreen->InstallColormap = pScreenPriv->InstallColormap;
842     pScreen->StoreColors = pScreenPriv->StoreColors;
843 
844     pScrn->EnterVT = pScreenPriv->EnterVT;
845     pScrn->SwitchMode = pScreenPriv->SwitchMode;
846     pScrn->SetDGAMode = pScreenPriv->SetDGAMode;
847     pScrn->ChangeGamma = pScreenPriv->ChangeGamma;
848 
849     free(pScreenPriv->gamma);
850     free(pScreenPriv->PreAllocIndices);
851     free(pScreenPriv);
852 }
853 
854 static void
ComputeGamma(ScrnInfoPtr pScrn,CMapScreenPtr priv)855 ComputeGamma(ScrnInfoPtr pScrn, CMapScreenPtr priv)
856 {
857     int elements = priv->gammaElements - 1;
858     double RedGamma, GreenGamma, BlueGamma;
859     int i;
860 
861 #ifndef DONT_CHECK_GAMMA
862     /* This check is to catch drivers that are not initialising pScrn->gamma */
863     if (pScrn->gamma.red < GAMMA_MIN || pScrn->gamma.red > GAMMA_MAX ||
864         pScrn->gamma.green < GAMMA_MIN || pScrn->gamma.green > GAMMA_MAX ||
865         pScrn->gamma.blue < GAMMA_MIN || pScrn->gamma.blue > GAMMA_MAX) {
866 
867         xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, 0,
868                        "The %s driver didn't call xf86SetGamma() to initialise\n"
869                        "\tthe gamma values.\n", pScrn->driverName);
870         xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, 0,
871                        "PLEASE FIX THE `%s' DRIVER!\n",
872                        pScrn->driverName);
873         pScrn->gamma.red = 1.0;
874         pScrn->gamma.green = 1.0;
875         pScrn->gamma.blue = 1.0;
876     }
877 #endif
878 
879     RedGamma = 1.0 / (double) pScrn->gamma.red;
880     GreenGamma = 1.0 / (double) pScrn->gamma.green;
881     BlueGamma = 1.0 / (double) pScrn->gamma.blue;
882 
883     for (i = 0; i <= elements; i++) {
884         if (RedGamma == 1.0)
885             priv->gamma[i].red = i;
886         else
887             priv->gamma[i].red = (CARD16) (pow((double) i / (double) elements,
888                                                RedGamma) * (double) elements +
889                                            0.5);
890 
891         if (GreenGamma == 1.0)
892             priv->gamma[i].green = i;
893         else
894             priv->gamma[i].green = (CARD16) (pow((double) i / (double) elements,
895                                                  GreenGamma) *
896                                              (double) elements + 0.5);
897 
898         if (BlueGamma == 1.0)
899             priv->gamma[i].blue = i;
900         else
901             priv->gamma[i].blue = (CARD16) (pow((double) i / (double) elements,
902                                                 BlueGamma) * (double) elements +
903                                             0.5);
904     }
905 }
906 
907 int
CMapChangeGamma(ScrnInfoPtr pScrn,Gamma gamma)908 CMapChangeGamma(ScrnInfoPtr pScrn, Gamma gamma)
909 {
910     int ret = Success;
911     ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
912     CMapColormapPtr pColPriv;
913     CMapScreenPtr pScreenPriv;
914     CMapLinkPtr pLink;
915 
916     /* Is this sufficient checking ? */
917     if (!CMapScreenKeyRegistered)
918         return BadImplementation;
919 
920     pScreenPriv = (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
921                                                    CMapScreenKey);
922     if (!pScreenPriv)
923         return BadImplementation;
924 
925     if (gamma.red < GAMMA_MIN || gamma.red > GAMMA_MAX ||
926         gamma.green < GAMMA_MIN || gamma.green > GAMMA_MAX ||
927         gamma.blue < GAMMA_MIN || gamma.blue > GAMMA_MAX)
928         return BadValue;
929 
930     pScrn->gamma.red = gamma.red;
931     pScrn->gamma.green = gamma.green;
932     pScrn->gamma.blue = gamma.blue;
933 
934     ComputeGamma(pScrn, pScreenPriv);
935 
936     /* mark all colormaps on this screen */
937     pLink = pScreenPriv->maps;
938     while (pLink) {
939         pColPriv = (CMapColormapPtr) dixLookupPrivate(&pLink->cmap->devPrivates,
940                                                       CMapColormapKey);
941         pColPriv->recalculate = TRUE;
942         pLink = pLink->next;
943     }
944 
945     if (GetInstalledmiColormap(pScreen) &&
946         ((pScreenPriv->flags & CMAP_LOAD_EVEN_IF_OFFSCREEN) ||
947          pScrn->vtSema || pScreenPriv->isDGAmode)) {
948         ColormapPtr pMap = GetInstalledmiColormap(pScreen);
949 
950         if (!(pScreenPriv->flags & CMAP_PALETTED_TRUECOLOR) &&
951             (pMap->pVisual->class == TrueColor) &&
952             CMapColormapUseMax(pMap->pVisual, pScreenPriv)) {
953 
954             /* if the current map doesn't have a palette look
955                for another map to change the gamma on. */
956 
957             pLink = pScreenPriv->maps;
958             while (pLink) {
959                 if (pLink->cmap->pVisual->class == PseudoColor)
960                     break;
961                 pLink = pLink->next;
962             }
963 
964             if (pLink) {
965                 /* need to trick CMapRefreshColors() into thinking
966                    this is the currently installed map */
967                 SetInstalledmiColormap(pScreen, pLink->cmap);
968                 CMapReinstallMap(pLink->cmap);
969                 SetInstalledmiColormap(pScreen, pMap);
970             }
971         }
972         else
973             CMapReinstallMap(pMap);
974     }
975 
976     pScrn->ChangeGamma = pScreenPriv->ChangeGamma;
977     if (pScrn->ChangeGamma)
978         ret = pScrn->ChangeGamma(pScrn, gamma);
979     pScrn->ChangeGamma = CMapChangeGamma;
980 
981     return ret;
982 }
983 
984 static void
ComputeGammaRamp(CMapScreenPtr priv,unsigned short * red,unsigned short * green,unsigned short * blue)985 ComputeGammaRamp(CMapScreenPtr priv,
986                  unsigned short *red,
987                  unsigned short *green, unsigned short *blue)
988 {
989     int elements = priv->gammaElements;
990     LOCO *entry = priv->gamma;
991     int shift = 16 - priv->sigRGBbits;
992 
993     while (elements--) {
994         entry->red = *(red++) >> shift;
995         entry->green = *(green++) >> shift;
996         entry->blue = *(blue++) >> shift;
997         entry++;
998     }
999 }
1000 
1001 int
xf86ChangeGammaRamp(ScreenPtr pScreen,int size,unsigned short * red,unsigned short * green,unsigned short * blue)1002 xf86ChangeGammaRamp(ScreenPtr pScreen,
1003                     int size,
1004                     unsigned short *red,
1005                     unsigned short *green, unsigned short *blue)
1006 {
1007     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1008     CMapColormapPtr pColPriv;
1009     CMapScreenPtr pScreenPriv;
1010     CMapLinkPtr pLink;
1011 
1012     if (!CMapScreenKeyRegistered)
1013         return BadImplementation;
1014 
1015     pScreenPriv = (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
1016                                                    CMapScreenKey);
1017     if (!pScreenPriv)
1018         return BadImplementation;
1019 
1020     if (pScreenPriv->gammaElements != size)
1021         return BadValue;
1022 
1023     ComputeGammaRamp(pScreenPriv, red, green, blue);
1024 
1025     /* mark all colormaps on this screen */
1026     pLink = pScreenPriv->maps;
1027     while (pLink) {
1028         pColPriv = (CMapColormapPtr) dixLookupPrivate(&pLink->cmap->devPrivates,
1029                                                       CMapColormapKey);
1030         pColPriv->recalculate = TRUE;
1031         pLink = pLink->next;
1032     }
1033 
1034     if (GetInstalledmiColormap(pScreen) &&
1035         ((pScreenPriv->flags & CMAP_LOAD_EVEN_IF_OFFSCREEN) ||
1036          pScrn->vtSema || pScreenPriv->isDGAmode)) {
1037         ColormapPtr pMap = GetInstalledmiColormap(pScreen);
1038 
1039         if (!(pScreenPriv->flags & CMAP_PALETTED_TRUECOLOR) &&
1040             (pMap->pVisual->class == TrueColor) &&
1041             CMapColormapUseMax(pMap->pVisual, pScreenPriv)) {
1042 
1043             /* if the current map doesn't have a palette look
1044                for another map to change the gamma on. */
1045 
1046             pLink = pScreenPriv->maps;
1047             while (pLink) {
1048                 if (pLink->cmap->pVisual->class == PseudoColor)
1049                     break;
1050                 pLink = pLink->next;
1051             }
1052 
1053             if (pLink) {
1054                 /* need to trick CMapRefreshColors() into thinking
1055                    this is the currently installed map */
1056                 SetInstalledmiColormap(pScreen, pLink->cmap);
1057                 CMapReinstallMap(pLink->cmap);
1058                 SetInstalledmiColormap(pScreen, pMap);
1059             }
1060         }
1061         else
1062             CMapReinstallMap(pMap);
1063     }
1064 
1065     return Success;
1066 }
1067 
1068 int
xf86GetGammaRampSize(ScreenPtr pScreen)1069 xf86GetGammaRampSize(ScreenPtr pScreen)
1070 {
1071     CMapScreenPtr pScreenPriv;
1072 
1073     if (!CMapScreenKeyRegistered)
1074         return 0;
1075 
1076     pScreenPriv = (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
1077                                                    CMapScreenKey);
1078     if (!pScreenPriv)
1079         return 0;
1080 
1081     return pScreenPriv->gammaElements;
1082 }
1083 
1084 int
xf86GetGammaRamp(ScreenPtr pScreen,int size,unsigned short * red,unsigned short * green,unsigned short * blue)1085 xf86GetGammaRamp(ScreenPtr pScreen,
1086                  int size,
1087                  unsigned short *red,
1088                  unsigned short *green, unsigned short *blue)
1089 {
1090     CMapScreenPtr pScreenPriv;
1091     LOCO *entry;
1092     int shift, sigbits;
1093 
1094     if (!CMapScreenKeyRegistered)
1095         return BadImplementation;
1096 
1097     pScreenPriv = (CMapScreenPtr) dixLookupPrivate(&pScreen->devPrivates,
1098                                                    CMapScreenKey);
1099     if (!pScreenPriv)
1100         return BadImplementation;
1101 
1102     if (size > pScreenPriv->gammaElements)
1103         return BadValue;
1104 
1105     entry = pScreenPriv->gamma;
1106     sigbits = pScreenPriv->sigRGBbits;
1107 
1108     while (size--) {
1109         *red = entry->red << (16 - sigbits);
1110         *green = entry->green << (16 - sigbits);
1111         *blue = entry->blue << (16 - sigbits);
1112         shift = sigbits;
1113         while (shift < 16) {
1114             *red |= *red >> shift;
1115             *green |= *green >> shift;
1116             *blue |= *blue >> shift;
1117             shift += sigbits;
1118         }
1119         red++;
1120         green++;
1121         blue++;
1122         entry++;
1123     }
1124 
1125     return Success;
1126 }
1127 
1128 int
xf86ChangeGamma(ScreenPtr pScreen,Gamma gamma)1129 xf86ChangeGamma(ScreenPtr pScreen, Gamma gamma)
1130 {
1131     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1132 
1133     if (pScrn->ChangeGamma)
1134         return (*pScrn->ChangeGamma) (pScrn, gamma);
1135 
1136     return BadImplementation;
1137 }
1138