SDL-1.2.14
[sdl_omap.git] / src / video / SDL_gamma.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library 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 GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /* Gamma correction support */
25
26 #ifdef HAVE_MATH_H
27 #include <math.h>       /* Used for calculating gamma ramps */
28 #else
29 /* Math routines from uClibc: http://www.uclibc.org */
30 #include "math_private.h"
31 #include "e_sqrt.h"
32 #include "e_pow.h"
33 #include "e_log.h"
34 #define pow(x, y)       __ieee754_pow(x, y)
35 #define log(x)          __ieee754_log(x)
36 #endif
37
38 #include "SDL_sysvideo.h"
39
40
41 static void CalculateGammaRamp(float gamma, Uint16 *ramp)
42 {
43         int i;
44
45         /* 0.0 gamma is all black */
46         if ( gamma <= 0.0f ) {
47                 for ( i=0; i<256; ++i ) {
48                         ramp[i] = 0;
49                 }
50                 return;
51         } else
52         /* 1.0 gamma is identity */
53         if ( gamma == 1.0f ) {
54                 for ( i=0; i<256; ++i ) {
55                         ramp[i] = (i << 8) | i;
56                 }
57                 return;
58         } else
59         /* Calculate a real gamma ramp */
60         { int value;
61                 gamma = 1.0f / gamma;
62                 for ( i=0; i<256; ++i ) {
63                         value = (int)(pow((double)i/256.0, gamma)*65535.0+0.5);
64                         if ( value > 65535 ) {
65                                 value = 65535;
66                         }
67                         ramp[i] = (Uint16)value;
68                 }
69         }
70 }
71 static void CalculateGammaFromRamp(float *gamma, Uint16 *ramp)
72 {
73         /* The following is adapted from a post by Garrett Bass on OpenGL
74            Gamedev list, March 4, 2000.
75          */
76         float sum = 0.0f;
77         int i, count = 0;
78
79         *gamma = 1.0;
80         for ( i = 1; i < 256; ++i ) {
81             if ( (ramp[i] != 0) && (ramp[i] != 65535) ) {
82                 double B = (double)i / 256.0;
83                 double A = ramp[i] / 65535.0;
84                 sum += (float) ( log(A) / log(B) );
85                 count++;
86             }
87         }
88         if ( count && sum > 0.0f ) {
89                 *gamma = 1.0f / (sum / count);
90         }
91 }
92
93 int SDL_SetGamma(float red, float green, float blue)
94 {
95         int succeeded;
96         SDL_VideoDevice *video = current_video;
97         SDL_VideoDevice *this  = current_video; 
98
99         succeeded = -1;
100         /* Prefer using SetGammaRamp(), as it's more flexible */
101         {
102                 Uint16 ramp[3][256];
103
104                 CalculateGammaRamp(red, ramp[0]);
105                 CalculateGammaRamp(green, ramp[1]);
106                 CalculateGammaRamp(blue, ramp[2]);
107                 succeeded = SDL_SetGammaRamp(ramp[0], ramp[1], ramp[2]);
108         }
109         if ( (succeeded < 0) && video->SetGamma ) {
110                 SDL_ClearError();
111                 succeeded = video->SetGamma(this, red, green, blue);
112         }
113         return succeeded;
114 }
115
116 /* Calculating the gamma by integrating the gamma ramps isn't exact,
117    so this function isn't officially supported.
118 */
119 int SDL_GetGamma(float *red, float *green, float *blue)
120 {
121         int succeeded;
122         SDL_VideoDevice *video = current_video;
123         SDL_VideoDevice *this  = current_video; 
124
125         succeeded = -1;
126         /* Prefer using GetGammaRamp(), as it's more flexible */
127         {
128                 Uint16 ramp[3][256];
129
130                 succeeded = SDL_GetGammaRamp(ramp[0], ramp[1], ramp[2]);
131                 if ( succeeded >= 0 ) {
132                         CalculateGammaFromRamp(red, ramp[0]);
133                         CalculateGammaFromRamp(green, ramp[1]);
134                         CalculateGammaFromRamp(blue, ramp[2]);
135                 }
136         }
137         if ( (succeeded < 0) && video->GetGamma ) {
138                 SDL_ClearError();
139                 succeeded = video->GetGamma(this, red, green, blue);
140         }
141         return succeeded;
142 }
143
144 int SDL_SetGammaRamp(const Uint16 *red, const Uint16 *green, const Uint16 *blue)
145 {
146         int succeeded;
147         SDL_VideoDevice *video = current_video;
148         SDL_VideoDevice *this  = current_video; 
149         SDL_Surface *screen = SDL_PublicSurface;
150
151         /* Verify the screen parameter */
152         if ( !screen ) {
153                 SDL_SetError("No video mode has been set");
154                 return -1;
155         }
156
157         /* Lazily allocate the gamma tables */
158         if ( ! video->gamma ) {
159                 SDL_GetGammaRamp(0, 0, 0);
160         }
161
162         /* Fill the gamma table with the new values */
163         if ( red ) {
164                 SDL_memcpy(&video->gamma[0*256], red, 256*sizeof(*video->gamma));
165         }
166         if ( green ) {
167                 SDL_memcpy(&video->gamma[1*256], green, 256*sizeof(*video->gamma));
168         }
169         if ( blue ) {
170                 SDL_memcpy(&video->gamma[2*256], blue, 256*sizeof(*video->gamma));
171         }
172
173         /* Gamma correction always possible on split palettes */
174         if ( (screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
175                 SDL_Palette *pal = screen->format->palette;
176
177                 /* If physical palette has been set independently, use it */
178                 if(video->physpal)
179                         pal = video->physpal;
180                       
181                 SDL_SetPalette(screen, SDL_PHYSPAL,
182                                pal->colors, 0, pal->ncolors);
183                 return 0;
184         }
185
186         /* Try to set the gamma ramp in the driver */
187         succeeded = -1;
188         if ( video->SetGammaRamp ) {
189                 succeeded = video->SetGammaRamp(this, video->gamma);
190         } else {
191                 SDL_SetError("Gamma ramp manipulation not supported");
192         }
193         return succeeded;
194 }
195
196 int SDL_GetGammaRamp(Uint16 *red, Uint16 *green, Uint16 *blue)
197 {
198         SDL_VideoDevice *video = current_video;
199         SDL_VideoDevice *this  = current_video; 
200
201         /* Lazily allocate the gamma table */
202         if ( ! video->gamma ) {
203                 video->gamma = SDL_malloc(3*256*sizeof(*video->gamma));
204                 if ( ! video->gamma ) {
205                         SDL_OutOfMemory();
206                         return -1;
207                 }
208                 if ( video->GetGammaRamp ) {
209                         /* Get the real hardware gamma */
210                         video->GetGammaRamp(this, video->gamma);
211                 } else {
212                         /* Assume an identity gamma */
213                         int i;
214                         for ( i=0; i<256; ++i ) {
215                                 video->gamma[0*256+i] = (i << 8) | i;
216                                 video->gamma[1*256+i] = (i << 8) | i;
217                                 video->gamma[2*256+i] = (i << 8) | i;
218                         }
219                 }
220         }
221
222         /* Just copy from our internal table */
223         if ( red ) {
224                 SDL_memcpy(red, &video->gamma[0*256], 256*sizeof(*red));
225         }
226         if ( green ) {
227                 SDL_memcpy(green, &video->gamma[1*256], 256*sizeof(*green));
228         }
229         if ( blue ) {
230                 SDL_memcpy(blue, &video->gamma[2*256], 256*sizeof(*blue));
231         }
232         return 0;
233 }