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