Add Gamma correction in palette editor

Particularly useful when working for platforms with non-linear palettes.
For example the Thomson TO8 has a Gamma correction of 2.8, so the
darkest shade of color is already very bright. It is important to take
this into account when drawing pictures to avoid bad surprises when
showing them on the real machine.

The setting is available from the command line, or from the extended
palette window, which gets a little relayout using horizontal sliders
now that these are available.
This commit is contained in:
Adrien Destugues 2017-03-19 09:05:58 +01:00
parent 2d4ad537ad
commit 32b3996cb8
3 changed files with 150 additions and 71 deletions

View File

@ -107,7 +107,8 @@ void Display_syntax(void)
printf("\t-tall2 to emulate a video mode with double tall pixels (2x4)\n");
printf("\t-triple to emulate a video mode with triple pixels (3x3)\n");
printf("\t-quadruple to emulate a video mode with quadruple pixels (4x4)\n");
printf("\t-rgb n to reduce RGB precision from 256 to n levels\n");
printf("\t-rgb n to reduce RGB precision (2 to 256, 256=max precision)\n");
printf("\t-gamma n to adjust Gamma correction (1 to 30, 10=no correction)\n");
printf("\t-skin <filename> to use an alternate file with the menu graphics\n");
printf("\t-mode <videomode> to set a video mode\n");
printf("Arguments can be prefixed either by / - or --\n");
@ -208,6 +209,7 @@ enum CMD_PARAMS
CMDPARAM_PIXELRATIO_TALL3,
CMDPARAM_PIXELRATIO_WIDE2,
CMDPARAM_RGB,
CMDPARAM_GAMMA,
CMDPARAM_SKIN
};
@ -229,6 +231,7 @@ struct {
{"tall3", CMDPARAM_PIXELRATIO_TALL3},
{"wide2", CMDPARAM_PIXELRATIO_WIDE2},
{"rgb", CMDPARAM_RGB},
{"gamma", CMDPARAM_GAMMA},
{"skin", CMDPARAM_SKIN}
};
@ -337,7 +340,7 @@ int Analyze_command_line(int argc, char * argv[], char *main_filename, char *mai
Pixel_ratio = PIXEL_WIDE2;
break;
case CMDPARAM_RGB:
// echelle des composants RGB
/* RGB scale */
index++;
if (index<argc)
{
@ -358,6 +361,28 @@ int Analyze_command_line(int argc, char * argv[], char *main_filename, char *mai
exit(0);
}
break;
case CMDPARAM_GAMMA:
/* Gamma correction */
index++;
if (index<argc)
{
int scale;
scale = atoi(argv[index]);
if (scale < 1 || scale > 30)
{
Error(ERROR_COMMAND_LINE);
Display_syntax();
exit(0);
}
Set_palette_Gamma(scale);
}
else
{
Error(ERROR_COMMAND_LINE);
Display_syntax();
exit(0);
}
break;
case CMDPARAM_SKIN:
// GUI skin file
index++;

View File

@ -18,9 +18,11 @@
You should have received a copy of the GNU General Public License
along with Grafx2; if not, see <http://www.gnu.org/licenses/>
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "const.h"
#include "struct.h"
#include "global.h"
@ -39,6 +41,7 @@
#include "shade.h"
byte Palette_view_is_RGB = 1; // Indique si on est en HSL ou en RGB
float Gamma = 1.0;
// Coordinates of the color count (on histogram button)
static const int COUNT_X = 138;
@ -72,32 +75,62 @@ int Get_palette_RGB_scale(void)
return RGB_scale;
}
void Set_palette_Gamma(int gamma)
{
Gamma = gamma / 10.0;
}
///
/// Round a 0-255 RGB component according to the RGB_scale.
/// Round a 0-255 RGB component according to the RGB_scale and gamm correction.
// The result is a color as similar as possible, but that can be encoded in the
// picture colorspace.
/// The result is also in the 0-255 range.
byte Round_palette_component(byte comp)
{
return (comp*RGB_scale/256+(RGB_scale-(256-1)*RGB_scale/256)/2) * (255/(RGB_scale-1));
// Apply Gamma correction
float fc = pow(comp/255.0,Gamma)*255;
// Convert to encodable value
comp = ceil(fc);
comp = (comp*RGB_scale/256+(RGB_scale-(256-1)*RGB_scale/256)/2) * (255/(RGB_scale-1));
// Apply reverse Gamma correction
fc = pow(comp/255.0,1/Gamma)*255;
return fc;
}
///
/// Turns a RGB component from 0-255 scale to 0-(RGB_scale-1).
/// Turns a RGB component from 0-(RGB_scale-1) scale to 0-255 and apply reverse
// gamma correction.
/// The passed value should come from Round_palette_component(),
/// otherwise the rounding will be "down".
int Reduce_component(int comp)
static int Decode_component(int comp)
{
return (comp)*255/Color_max;
float res = pow((float)comp/Color_max,1/Gamma)*255;
return (int)res;
}
/// Compute the component at a given distance from the current one.
/// Used by the add/remove and lighten/darken operations.
static int Add_encoded(int comp, int offset)
{
int new = Decode_component(Encode_component(comp)+offset);
if (new < 0)
new = 0;
if (new > 255)
new = 255;
return new;
}
///
/// Turns a RGB component from 0-(RGB_scale-1) to 0-255.
int Expand_component(int comp)
/// Turns a RGB component from 0-255 to 0-(RGB_scale-1) and apply Gamma correction.
static int Encode_component(int comp)
{
if (Color_max==255)
return comp;
return (comp+1)*Color_max/255;
// The +1 cancel any rounding down, the above test prevents
// the only case where it would be too much.
float res = pow(comp/255.0,Gamma)*Color_max;
return ceil(res);
}
// Définir les unités pour les graduations R G B ou H S V
@ -189,19 +222,19 @@ void Spread_colors(short start,short end,T_Palette palette)
// pouvoir faire un dégradé:
if ( (start!=end) && (start+1!=end) )
{
start_red=palette[start].R;
start_green =palette[start].G;
start_blue =palette[start].B;
start_red = Encode_component(palette[start].R);
start_green = Encode_component(palette[start].G);
start_blue = Encode_component(palette[start].B);
end_red =palette[end ].R;
end_green =palette[end ].G;
end_blue =palette[end ].B;
end_red = Encode_component(palette[end ].R);
end_green = Encode_component(palette[end ].G);
end_blue = Encode_component(palette[end ].B);
for (index=start+1;index<end;index++)
{
Set_red(index, ((end_red-start_red) * (index-start))/(end-start) + start_red,palette);
Set_green (index, ((end_green -start_green ) * (index-start))/(end-start) + start_green ,palette);
Set_blue (index, ((end_blue -start_blue ) * (index-start))/(end-start) + start_blue ,palette);
Set_red (index, Decode_component(((end_red -start_red ) * (index-start))/(end-start) + start_red), palette);
Set_green(index, Decode_component(((end_green-start_green) * (index-start))/(end-start) + start_green), palette);
Set_blue (index, Decode_component(((end_blue -start_blue ) * (index-start))/(end-start) + start_blue), palette);
}
Set_palette(palette);
}
@ -797,12 +830,12 @@ void Display_sliders(T_Scroller_button * red_slider,
RGB_to_HSL(j1,j2,j3,&j1,&j2,&j3);
}
Format_component(j1*Color_count/256,str);
Set_palette_slider(red_slider,Color_count,Color_max-Expand_component(j1),str,NUMERIC_R_X);
Format_component(j2*Color_count/256,str);
Set_palette_slider(green_slider,Color_count,Color_max-Expand_component(j2),str,NUMERIC_G_X);
Format_component(j3*Color_count/256,str);
Set_palette_slider(blue_slider,Color_count,Color_max-Expand_component(j3),str,NUMERIC_B_X);
Format_component(Encode_component(j1), str);
Set_palette_slider(red_slider,Color_count,Color_max-Encode_component(j1),str,NUMERIC_R_X);
Format_component(Encode_component(j2),str);
Set_palette_slider(green_slider,Color_count,Color_max-Encode_component(j2),str,NUMERIC_G_X);
Format_component(Encode_component(j3),str);
Set_palette_slider(blue_slider,Color_count,Color_max-Encode_component(j3),str,NUMERIC_B_X);
}
}
@ -841,18 +874,18 @@ void Draw_all_palette_sliders(T_Scroller_button * red_slider,
{
RGB_to_HSL(j1,j2,j3,&j1,&j2,&j3);
}
Format_component(j1*Color_count/256,str);
red_slider->Position=Color_max-Expand_component(j1);
Format_component(Encode_component(j1),str);
red_slider->Position=Color_max-Encode_component(j1);
Window_draw_slider(red_slider);
Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light);
Format_component(j2*Color_count/256,str);
green_slider->Position=Color_max-Expand_component(j2);
Format_component(Encode_component(j2),str);
green_slider->Position=Color_max-Encode_component(j2);
Window_draw_slider(green_slider);
Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light);
Format_component(j3*Color_count/256,str);
blue_slider->Position=Color_max-Expand_component(j3);
Format_component(Encode_component(j3),str);
blue_slider->Position=Color_max-Encode_component(j3);
Window_draw_slider(blue_slider);
Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
@ -1137,9 +1170,9 @@ void Button_Palette(void)
Window_rectangle(247,39+96,17,1,MC_Dark);
Window_rectangle(274,39+96,17,1,MC_Dark);
// Jauges de couleur
red_slider = Window_set_scroller_button(223, 27, 128+24,Color_count,1,Color_max-Reduce_component(working_palette[Fore_color].R));// 2
green_slider = Window_set_scroller_button(250, 27, 128+24,Color_count,1,Color_max-Reduce_component(working_palette[Fore_color].G));// 3
blue_slider = Window_set_scroller_button(277, 27, 128+24,Color_count,1,Color_max-Reduce_component(working_palette[Fore_color].B));// 4
red_slider = Window_set_scroller_button(223, 27, 128+24,Color_count,1,Color_max-Decode_component(working_palette[Fore_color].R));// 2
green_slider = Window_set_scroller_button(250, 27, 128+24,Color_count,1,Color_max-Decode_component(working_palette[Fore_color].G));// 3
blue_slider = Window_set_scroller_button(277, 27, 128+24,Color_count,1,Color_max-Decode_component(working_palette[Fore_color].B));// 4
if(Palette_view_is_RGB==1) {
Print_RGB_or_HSL(0);
@ -1434,8 +1467,8 @@ void Button_Palette(void)
{
if(Palette_view_is_RGB)
{
Set_red(Fore_color,Reduce_component(Color_max-red_slider->Position),working_palette);
Format_component((working_palette[Fore_color].R)*Color_count/256,str);
Set_red(Fore_color,Decode_component(Color_max-red_slider->Position),working_palette);
Format_component(Color_max-red_slider->Position,str);
}
else
{
@ -1455,7 +1488,7 @@ void Button_Palette(void)
if(Palette_view_is_RGB)
{
for (i=block_start; i<=block_end; i++)
Set_red(i,temp_palette[i].R+Reduce_component(Color_max-red_slider->Position),working_palette);
Set_red(i,Add_encoded(temp_palette[i].R, Color_max-red_slider->Position),working_palette);
}
else
{
@ -1515,8 +1548,8 @@ void Button_Palette(void)
{
if(Palette_view_is_RGB)
{
Set_green (Fore_color,Reduce_component(Color_max-green_slider->Position),working_palette);
Format_component(working_palette[Fore_color].G*Color_count/256,str);
Set_green (Fore_color,Decode_component(Color_max-green_slider->Position),working_palette);
Format_component(Color_max-green_slider->Position,str);
}
else
{
@ -1536,7 +1569,7 @@ void Button_Palette(void)
if(Palette_view_is_RGB)
{
for (i=block_start; i<=block_end; i++)
Set_green (i,temp_palette[i].G+Reduce_component(Color_max-green_slider->Position),working_palette);
Set_green (i,Add_encoded(temp_palette[i].G, Color_max-green_slider->Position),working_palette);
}
else
{
@ -1596,8 +1629,8 @@ void Button_Palette(void)
{
if(Palette_view_is_RGB)
{
Set_blue (Fore_color,Reduce_component(Color_max-blue_slider->Position),working_palette);
Format_component(working_palette[Fore_color].B*Color_count/256,str);
Set_blue (Fore_color,Decode_component(Color_max-blue_slider->Position),working_palette);
Format_component(Color_max-blue_slider->Position,str);
}
else
{
@ -1617,7 +1650,7 @@ void Button_Palette(void)
if(Palette_view_is_RGB)
{
for (i=block_start; i<=block_end; i++)
Set_blue(i,temp_palette[i].B+Reduce_component(Color_max-blue_slider->Position),working_palette);
Set_blue(i,Add_encoded(temp_palette[i].B, Color_max-blue_slider->Position),working_palette);
}
else
{
@ -2013,24 +2046,24 @@ void Button_Palette(void)
{
(red_slider->Position)--;
Window_draw_slider(red_slider);
Set_red(Fore_color,Reduce_component(Color_max-red_slider->Position),working_palette);
Format_component(working_palette[Fore_color].R*Color_count/256,str);
Set_red(Fore_color,Decode_component(Color_max-red_slider->Position),working_palette);
Format_component(Color_max-red_slider->Position,str);
Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
if (green_slider->Position)
{
(green_slider->Position)--;
Window_draw_slider(green_slider);
Set_green (Fore_color,Reduce_component(Color_max-green_slider->Position),working_palette);
Format_component(working_palette[Fore_color].G*Color_count/256,str);
Set_green (Fore_color,Decode_component(Color_max-green_slider->Position),working_palette);
Format_component(Color_max-green_slider->Position,str);
Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
if (blue_slider->Position)
{
(blue_slider->Position)--;
Window_draw_slider(blue_slider);
Set_blue (Fore_color,Reduce_component(Color_max-blue_slider->Position),working_palette);
Format_component(working_palette[Fore_color].B*Color_count/256,str);
Set_blue (Fore_color,Decode_component(Color_max-blue_slider->Position),working_palette);
Format_component(Color_max-blue_slider->Position,str);
Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
}
@ -2054,9 +2087,9 @@ void Button_Palette(void)
for (i=block_start; i<=block_end; i++)
{
Set_red(i,temp_palette[i].R+Reduce_component(Color_max-red_slider->Position),working_palette);
Set_green (i,temp_palette[i].G+Reduce_component(Color_max-green_slider->Position),working_palette);
Set_blue (i,temp_palette[i].B+Reduce_component(Color_max-blue_slider->Position),working_palette);
Set_red(i,Add_encoded(temp_palette[i].R, Color_max-red_slider->Position),working_palette);
Set_green (i,Add_encoded(temp_palette[i].G, Color_max-green_slider->Position),working_palette);
Set_blue (i,Add_encoded(temp_palette[i].B, Color_max-blue_slider->Position),working_palette);
}
// -- red --
@ -2139,24 +2172,24 @@ void Button_Palette(void)
{
(red_slider->Position)++;
Window_draw_slider(red_slider);
Set_red(Fore_color,Reduce_component(Color_max-red_slider->Position),working_palette);
Format_component(working_palette[Fore_color].R*Color_count/256,str);
Set_red(Fore_color,Decode_component(Color_max-red_slider->Position),working_palette);
Format_component(Color_max-red_slider->Position,str);
Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
if (green_slider->Position<Color_max)
{
(green_slider->Position)++;
Window_draw_slider(green_slider);
Set_green (Fore_color,Reduce_component(Color_max-green_slider->Position),working_palette);
Format_component(working_palette[Fore_color].G*Color_count/256,str);
Set_green (Fore_color,Decode_component(Color_max-green_slider->Position),working_palette);
Format_component(Color_max-green_slider->Position,str);
Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
if (blue_slider->Position<Color_max)
{
(blue_slider->Position)++;
Window_draw_slider(blue_slider);
Set_blue (Fore_color,Reduce_component(Color_max-blue_slider->Position),working_palette);
Format_component(working_palette[Fore_color].B*Color_count/256,str);
Set_blue (Fore_color,Decode_component(Color_max-blue_slider->Position),working_palette);
Format_component(Color_max-blue_slider->Position,str);
Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light);
}
}
@ -2180,9 +2213,9 @@ void Button_Palette(void)
for (i=block_start; i<=block_end; i++)
{
Set_red(i,temp_palette[i].R+Reduce_component(Color_max-red_slider->Position),working_palette);
Set_green (i,temp_palette[i].G+Reduce_component(Color_max-green_slider->Position),working_palette);
Set_blue (i,temp_palette[i].B+Reduce_component(Color_max-blue_slider->Position),working_palette);
Set_red(i,temp_palette[i].R+Decode_component(Color_max-red_slider->Position),working_palette);
Set_green (i,temp_palette[i].G+Decode_component(Color_max-green_slider->Position),working_palette);
Set_blue (i,temp_palette[i].B+Decode_component(Color_max-blue_slider->Position),working_palette);
}
// -- red --
@ -2840,13 +2873,15 @@ void Button_Secondary_palette(void)
T_Scroller_button * columns_slider;
T_Scroller_button * lines_slider;
T_Scroller_button * rgb_scale_slider;
T_Scroller_button * gamma_slider;
char str[4];
byte palette_vertical = Config.Palette_vertical;
byte palette_cols, palette_lines;
word rgb_scale;
float gamma;
byte palette_needs_redraw=0;
Open_window(200,146,"Palettes");
Open_window(230,146,"Palettes");
Window_set_normal_button(10,20,180,14,"Colors for best match",12,1,SDLK_b); // 1
Window_set_normal_button(10,37,180,14,"User's color series" ,14,1,SDLK_s); // 2
@ -2858,6 +2893,7 @@ void Button_Secondary_palette(void)
Print_in_window(84,77,"Lines",MC_Dark,MC_Light);
Print_in_window(157,58,"RGB",MC_Dark,MC_Light);
Print_in_window(152,68,"Scale",MC_Dark,MC_Light);
Print_in_window(188,58,"Gamma",MC_Dark,MC_Light);
columns_slider = Window_set_scroller_button(19,72,29,255,1,256-Config.Palette_cells_X);// 5
Num2str(Config.Palette_cells_X,str,3);
@ -2878,7 +2914,11 @@ void Button_Secondary_palette(void)
Window_set_normal_button(152,88,18,14,"x2" , 1,1,SDLK_x); // 9
Window_set_normal_button(172,88,18,14,"÷2" , 0,1,SDLK_w); // 10
Update_window_area(0,0,200,146);
gamma_slider = Window_set_scroller_button(200,68,40,30, 1,Gamma*10);// 11
Num2str(Gamma*10,str,3);
Print_in_window(190,112,str,MC_Black,MC_Light);
Update_window_area(0,0,230,146);
Display_cursor();
@ -2934,6 +2974,15 @@ void Button_Secondary_palette(void)
Num2str(256-rgb_scale_slider->Position,str,3);
Print_in_window(157,78,str,MC_Black,MC_Light);
Window_draw_slider(rgb_scale_slider);
break;
case 11:
// Gamma slider
Num2str(Window_attribute2,str,3);
Hide_cursor();
Print_in_window(190,112,str,MC_Black,MC_Light);
Display_cursor();
break;
}
}
while (clicked_button!=1 && clicked_button!=2 && clicked_button!=3 && clicked_button!=4);
@ -2942,6 +2991,7 @@ void Button_Secondary_palette(void)
palette_cols=256-columns_slider->Position;
palette_lines=16-lines_slider->Position;
rgb_scale=256-rgb_scale_slider->Position;
gamma=gamma_slider->Position/10.f;
Close_window();
Unselect_button(BUTTON_PALETTE);
@ -2962,8 +3012,9 @@ void Button_Secondary_palette(void)
Config.Palette_cells_Y = palette_lines;
palette_needs_redraw=1;
}
if (rgb_scale!=RGB_scale)
if (rgb_scale!=RGB_scale || gamma != Gamma)
{
Gamma = gamma;
Set_palette_RGB_scale(rgb_scale);
Set_palette(Main_palette);
Compute_optimal_menu_colors(Main_palette);

View File

@ -33,6 +33,9 @@ void Set_palette_RGB_scale(int);
int Get_palette_RGB_scale(void);
/// Configure Gamma correction
void Set_palette_Gamma(int);
///
/// Scale a component (R, G or B) according to the current RGB graduations.
/// Returns the resulting value, in the [0-255] range.