• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2012 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 
CalculateGammaRamp(float gamma,Uint16 * ramp)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 }
CalculateGammaFromRamp(float * gamma,Uint16 * ramp)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 
SDL_SetGamma(float red,float green,float blue)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 */
SDL_GetGamma(float * red,float * green,float * blue)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 
SDL_SetGammaRamp(const Uint16 * red,const Uint16 * green,const Uint16 * blue)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 
SDL_GetGammaRamp(Uint16 * red,Uint16 * green,Uint16 * blue)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 }
234