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 41static 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} 71static 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 93int 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*/ 119int 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 144int 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 196int 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