00a4f631366cb7a1ae1b9c9ce5e2f275f52c2b39
[reactos.git] / subsys / win32k / eng / xlate.c
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          GDI Color Translation Functions
5  * FILE:             subsys/win32k/eng/xlate.c
6  * PROGRAMER:        Jason Filby
7  * REVISION HISTORY:
8  *        8/20/1999: Created
9  */
10
11 // TODO: Cache XLATEOBJs that are created by EngCreateXlate by checking if the given palettes match a cached list
12
13 #include <ddk/ntddk.h>
14 #include <ddk/winddi.h>
15 #include <ddk/ntddvid.h>
16
17 #include <include/object.h>
18 #include "handle.h"
19
20 #define NDEBUG
21 #include <win32k/debug1.h>
22
23 ULONG CCMLastSourceColor = 0, CCMLastColorMatch = 0;
24
25 ULONG RGBtoULONG(BYTE Red, BYTE Green, BYTE Blue)
26 {
27   return ((Red & 0xff) << 16) | ((Green & 0xff) << 8) | (Blue & 0xff);
28 }
29
30 ULONG BGRtoULONG(BYTE Blue, BYTE Green, BYTE Red)
31 {
32   return ((Blue & 0xff) << 16) | ((Green & 0xff) << 8) | (Red & 0xff);
33 }
34
35 static ULONG ShiftAndMask(XLATEGDI *XlateGDI, ULONG Color)
36 {
37   ULONG TranslatedColor;
38
39   TranslatedColor = 0;
40   if (XlateGDI->RedShift < 0)
41   {
42     TranslatedColor = (Color >> -(XlateGDI->RedShift)) & XlateGDI->RedMask;
43   } else
44     TranslatedColor = (Color << XlateGDI->RedShift) & XlateGDI->RedMask;
45   if (XlateGDI->GreenShift < 0)
46   {
47     TranslatedColor |= (Color >> -(XlateGDI->GreenShift)) & XlateGDI->GreenMask;
48   } else
49     TranslatedColor |= (Color << XlateGDI->GreenShift) & XlateGDI->GreenMask;
50   if (XlateGDI->BlueShift < 0)
51   {
52     TranslatedColor |= (Color >> -(XlateGDI->BlueShift)) & XlateGDI->BlueMask;
53   } else
54     TranslatedColor |= (Color << XlateGDI->BlueShift) & XlateGDI->BlueMask;
55
56   return TranslatedColor;
57 }
58
59
60 // FIXME: If the caller knows that the destinations are indexed and not RGB
61 // then we should cache more than one value. Same with the source.
62
63 // Takes indexed palette and a
64 ULONG ClosestColorMatch(XLATEGDI *XlateGDI, ULONG SourceColor, ULONG *DestColors,
65                         ULONG NumColors)
66 {
67   PVIDEO_CLUTDATA cSourceColor;
68   PVIDEO_CLUTDATA cDestColors;
69   LONG idx = 0, i, rt;
70   ULONG SourceRGB;
71   ULONG SourceRed, SourceGreen, SourceBlue;
72   ULONG cxRed, cxGreen, cxBlue, BestMatch = 16777215;
73
74   // Simple cache -- only one value because we don't want to waste time
75   // if the colors aren't very sequential
76
77   if(SourceColor == CCMLastSourceColor)
78   {
79     return CCMLastColorMatch;
80   }
81
82   if (PAL_BITFIELDS == XlateGDI->XlateObj.iSrcType)
83     {
84     /* FIXME: must use bitfields */
85     SourceRGB = ShiftAndMask(XlateGDI, SourceColor);
86     cSourceColor = (PVIDEO_CLUTDATA) &SourceRGB;
87 /*
88     SourceRed = (SourceColor >> 7) & 0xff;
89     SourceGreen = (SourceColor >> 2) & 0xff;
90     SourceBlue = (SourceColor << 3) & 0xff;
91 */
92     }
93   else
94     {
95     cSourceColor = (PVIDEO_CLUTDATA)&SourceColor;
96     } 
97   SourceRed = cSourceColor->Red;
98   SourceGreen = cSourceColor->Green;
99   SourceBlue = cSourceColor->Blue;
100   for (i=0; i<NumColors; i++)
101   {
102     cDestColors = (PVIDEO_CLUTDATA)&DestColors[i];
103
104     cxRed = (SourceRed - cDestColors->Red);
105         cxRed *= cxRed;  //compute cxRed squared
106     cxGreen = (SourceGreen - cDestColors->Green);
107         cxGreen *= cxGreen;
108     cxBlue = (SourceBlue - cDestColors->Blue);
109         cxBlue *= cxBlue;
110
111     rt = /* sqrt */ (cxRed + cxGreen + cxBlue);
112
113     if(rt<=BestMatch)
114     {
115       idx = i;
116       BestMatch = rt;
117     }
118   }
119
120   CCMLastSourceColor = SourceColor;
121   CCMLastColorMatch  = idx;
122
123   return idx;
124 }
125
126 VOID IndexedToIndexedTranslationTable(XLATEGDI *XlateGDI, ULONG *TranslationTable,
127                                       PALGDI *PalDest, PALGDI *PalSource)
128 {
129   ULONG i;
130
131   for(i=0; i<PalSource->NumColors; i++)
132   {
133     TranslationTable[i] = ClosestColorMatch(XlateGDI, PalSource->IndexedColors[i], PalDest->IndexedColors, PalDest->NumColors);
134   }
135 }
136
137 static VOID BitMasksFromPal(USHORT PalType, PPALGDI Palette,
138                             PULONG RedMask, PULONG BlueMask, PULONG GreenMask)
139 {
140   switch(PalType)
141   {
142     case PAL_RGB:
143       *RedMask = RGB(255, 0, 0);
144       *GreenMask = RGB(0, 255, 0);
145       *BlueMask = RGB(0, 0, 255);
146       break;
147     case PAL_BGR:
148       *RedMask = RGB(0, 0, 255);
149       *GreenMask = RGB(0, 255, 0);
150       *BlueMask = RGB(255, 0, 0);
151       break;
152     case PAL_BITFIELDS:
153       *RedMask = Palette->RedMask;
154       *BlueMask = Palette->BlueMask;
155       *GreenMask = Palette->GreenMask;
156       break;
157   }
158 }
159
160 /*
161  * Calculate the number of bits Mask must be shift to the left to get a
162  * 1 in the most significant bit position
163  */
164 static INT CalculateShift(ULONG Mask)
165 {
166    INT Shift = 0;
167    ULONG LeftmostBit = 1 << (8 * sizeof(ULONG) - 1);
168
169    while (0 == (Mask & LeftmostBit) && Shift < 8 * sizeof(ULONG))
170      {
171      Mask = Mask << 1;
172      Shift++;
173      }
174
175    return Shift;
176 }
177
178 XLATEOBJ *IntEngCreateXlate(USHORT DestPalType, USHORT SourcePalType,
179                             HPALETTE PaletteDest, HPALETTE PaletteSource)
180 {
181   // FIXME: Add support for BGR conversions
182
183   HPALETTE NewXlate;
184   XLATEOBJ *XlateObj;
185   XLATEGDI *XlateGDI;
186   PALGDI   *SourcePalGDI, *DestPalGDI;
187   ULONG    IndexedColors;
188   ULONG    SourceRedMask, SourceGreenMask, SourceBlueMask;
189   ULONG    DestRedMask, DestGreenMask, DestBlueMask;
190   UINT     i;
191
192   NewXlate = (HPALETTE)CreateGDIHandle(sizeof( XLATEGDI ), sizeof( XLATEOBJ ));
193   if( !ValidEngHandle( NewXlate ) )
194         return NULL;
195
196   XlateObj = (XLATEOBJ*) AccessUserObject( (ULONG) NewXlate );
197   XlateGDI = (XLATEGDI*) AccessInternalObject( (ULONG) NewXlate );
198   ASSERT( XlateObj );
199   ASSERT( XlateGDI );
200
201   if (NULL != PaletteSource)
202   {
203     SourcePalGDI = (PALGDI*)AccessInternalObject((ULONG)PaletteSource);
204   }
205   if (NULL != PaletteDest)
206   {
207     DestPalGDI = (PALGDI*)AccessInternalObject((ULONG)PaletteDest);
208   }
209
210   XlateObj->iSrcType = SourcePalType;
211   XlateObj->iDstType = DestPalType;
212
213   // Store handles of palettes in internal Xlate GDI object (or NULLs)
214   XlateGDI->DestPal   = PaletteDest;
215   XlateGDI->SourcePal = PaletteSource;
216
217   XlateObj->flXlate = 0;
218
219   XlateGDI->UseShiftAndMask = FALSE;
220
221   /* Compute bit fiddeling constants unless both palettes are indexed, then we don't need them */
222   if (PAL_INDEXED != SourcePalType || PAL_INDEXED != DestPalType)
223   {
224     BitMasksFromPal(PAL_INDEXED == SourcePalType ? PAL_RGB : SourcePalType,
225                     SourcePalGDI, &SourceRedMask, &SourceBlueMask, &SourceGreenMask);
226     BitMasksFromPal(PAL_INDEXED == DestPalType ? PAL_RGB : DestPalType,
227                     DestPalGDI, &DestRedMask, &DestBlueMask, &DestGreenMask);
228     XlateGDI->RedShift = CalculateShift(SourceRedMask) - CalculateShift(DestRedMask);
229     XlateGDI->RedMask = DestRedMask;
230     XlateGDI->GreenShift = CalculateShift(SourceGreenMask) - CalculateShift(DestGreenMask);
231     XlateGDI->GreenMask = DestGreenMask;
232     XlateGDI->BlueShift = CalculateShift(SourceBlueMask) - CalculateShift(DestBlueMask);
233     XlateGDI->BlueMask = DestBlueMask;
234   }
235
236   // If source and destination palettes are the same or if they're RGB/BGR
237   if( (PaletteDest == PaletteSource) ||
238       ((DestPalType == PAL_RGB) && (SourcePalType == PAL_RGB)) ||
239       ((DestPalType == PAL_BGR) && (SourcePalType == PAL_BGR)) )
240   {
241     XlateObj->flXlate |= XO_TRIVIAL;
242     return XlateObj;
243   }
244
245   /* If source and destination are bitfield based (RGB and BGR are just special bitfields) */
246   if ((PAL_RGB == DestPalType || PAL_BGR == DestPalType || PAL_BITFIELDS == DestPalType) &&
247       (PAL_RGB == SourcePalType || PAL_BGR == SourcePalType || PAL_BITFIELDS == SourcePalType))
248   {
249     if (SourceRedMask == DestRedMask &&
250         SourceBlueMask == DestBlueMask &&
251         SourceGreenMask == DestGreenMask)
252       {
253       XlateObj->flXlate |= XO_TRIVIAL;
254       }
255     XlateGDI->UseShiftAndMask = TRUE;
256     return XlateObj;
257   }
258
259   // Prepare the translation table
260   if( (SourcePalType == PAL_INDEXED) || (SourcePalType == PAL_RGB) )
261   {
262     XlateObj->flXlate |= XO_TABLE;
263     if ((SourcePalType == PAL_INDEXED) && (DestPalType == PAL_INDEXED))
264     {
265       if(SourcePalGDI->NumColors > DestPalGDI->NumColors)
266       {
267         IndexedColors = SourcePalGDI->NumColors;
268       } else
269         IndexedColors = DestPalGDI->NumColors;
270     }
271     else if (SourcePalType == PAL_INDEXED) { IndexedColors = SourcePalGDI->NumColors; }
272     else if (DestPalType   == PAL_INDEXED) { IndexedColors = DestPalGDI->NumColors; }
273
274     XlateGDI->translationTable = EngAllocMem(FL_ZERO_MEMORY, sizeof(ULONG)*IndexedColors, 0);
275   }
276
277   // Source palette is indexed
278   if(XlateObj->iSrcType == PAL_INDEXED)
279   {
280     if(XlateObj->iDstType == PAL_INDEXED)
281     {
282       // Converting from indexed to indexed
283       IndexedToIndexedTranslationTable(XlateGDI, XlateGDI->translationTable, DestPalGDI, SourcePalGDI);
284     } else
285       if (PAL_RGB == XlateObj->iDstType || PAL_BITFIELDS == XlateObj->iDstType )
286       {
287         // FIXME: Is this necessary? I think the driver has to call this
288         // function anyways if pulXlate is NULL and Source is PAL_INDEXED
289
290         // Converting from indexed to RGB
291
292         XLATEOBJ_cGetPalette(XlateObj, XO_SRCPALETTE,
293                              SourcePalGDI->NumColors,
294                              XlateGDI->translationTable);
295         if (PAL_BITFIELDS == XlateObj->iDstType)
296         {
297           for (i = 0; i < SourcePalGDI->NumColors; i++)
298           {
299           XlateGDI->translationTable[i] = ShiftAndMask(XlateGDI, XlateGDI->translationTable[i]);
300           }
301         }
302       }
303
304     XlateObj->pulXlate = XlateGDI->translationTable;
305   }
306
307   // Source palette is RGB
308   if(XlateObj->iSrcType == PAL_RGB)
309   {
310     if(XlateObj->iDstType == PAL_INDEXED)
311     {
312       // FIXME: Is this necessary? I think the driver has to call this
313       // function anyways if pulXlate is NULL and Dest is PAL_INDEXED
314
315       // Converting from RGB to indexed
316       XLATEOBJ_cGetPalette(XlateObj, XO_DESTPALETTE, DestPalGDI->NumColors, XlateGDI->translationTable);
317     }
318   }
319
320   // FIXME: Add support for XO_TO_MONO
321   return XlateObj;
322 }
323
324 VOID EngDeleteXlate(XLATEOBJ *XlateObj)
325 {
326   HPALETTE HXlate    = (HPALETTE)AccessHandleFromUserObject(XlateObj);
327   XLATEGDI *XlateGDI = (XLATEGDI*)AccessInternalObject((ULONG)HXlate);
328
329   if(XlateGDI->translationTable!=NULL)
330   {
331     EngFreeMem(XlateGDI->translationTable);
332   }
333
334   FreeGDIHandle((ULONG)HXlate);
335 }
336
337 ULONG * STDCALL
338 XLATEOBJ_piVector(XLATEOBJ *XlateObj)
339 {
340   XLATEGDI *XlateGDI = (XLATEGDI*)AccessInternalObjectFromUserObject(XlateObj);
341
342   if(XlateObj->iSrcType == PAL_INDEXED)
343   {
344     return XlateGDI->translationTable;
345   }
346
347   return NULL;
348 }
349
350 ULONG STDCALL
351 XLATEOBJ_iXlate(XLATEOBJ *XlateObj,
352                 ULONG Color)
353 {
354   PALGDI   *PalGDI;
355   XLATEGDI *XlateGDI = (XLATEGDI*)AccessInternalObjectFromUserObject(XlateObj);
356
357   // Return the original color if there's no color translation object
358   if(!XlateObj) return Color;
359
360   if(XlateObj->flXlate & XO_TRIVIAL)
361   {
362     return Color;
363   } else
364   if(XlateGDI->UseShiftAndMask)
365   {
366     return ShiftAndMask(XlateGDI, Color);
367   } else
368   if(PAL_RGB == XlateObj->iSrcType || PAL_BITFIELDS == XlateObj->iSrcType)
369   {
370     // FIXME: should we cache colors used often?
371     // FIXME: won't work if destination isn't indexed
372
373     // Extract the destination palette
374     PalGDI = (PALGDI*)AccessInternalObject((ULONG)XlateGDI->DestPal);
375
376     // Return closest match for the given color
377     return ClosestColorMatch(XlateGDI, Color, PalGDI->IndexedColors, PalGDI->NumColors);
378   } else
379   if(XlateObj->iSrcType == PAL_INDEXED)
380   {
381     return XlateGDI->translationTable[Color];
382   }
383
384   return 0;
385 }
386
387 ULONG STDCALL
388 XLATEOBJ_cGetPalette(XLATEOBJ *XlateObj,
389                      ULONG PalOutType,
390                      ULONG cPal,
391                      ULONG *OutPal)
392 {
393   ULONG i;
394   HPALETTE HPal;
395   XLATEGDI *XlateGDI;
396   PALGDI *PalGDI;
397
398   XlateGDI = (XLATEGDI*)AccessInternalObjectFromUserObject(XlateObj);
399
400   if(PalOutType == XO_SRCPALETTE)
401   {
402     HPal = XlateGDI->SourcePal;
403   } else
404   if(PalOutType == XO_DESTPALETTE)
405   {
406     HPal = XlateGDI->DestPal;
407   }
408
409   PalGDI = (PALGDI*)AccessInternalObject((ULONG)HPal);
410   RtlCopyMemory(OutPal, PalGDI->IndexedColors, sizeof(ULONG)*cPal);
411
412   return i;
413 }