From 32b3996cb864b3ae09ac1021df056149ec1cf2e5 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Sun, 19 Mar 2017 09:05:58 +0100 Subject: [PATCH] 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. --- src/main.c | 29 +++++++- src/palette.c | 189 ++++++++++++++++++++++++++++++++------------------ src/palette.h | 3 + 3 files changed, 150 insertions(+), 71 deletions(-) diff --git a/src/main.c b/src/main.c index 51beadfd..ac92bd74 100644 --- a/src/main.c +++ b/src/main.c @@ -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 to use an alternate file with the menu graphics\n"); printf("\t-mode 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 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++; diff --git a/src/palette.c b/src/palette.c index 2bada347..16c9d5f1 100644 --- a/src/palette.c +++ b/src/palette.c @@ -18,9 +18,11 @@ You should have received a copy of the GNU General Public License along with Grafx2; if not, see */ +#include #include #include #include + #include "const.h" #include "struct.h" #include "global.h" @@ -39,11 +41,12 @@ #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; static const int COUNT_Y = 64; - + // Nombre de graduations pour une composante RGB int RGB_scale = 256; // 24bit @@ -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;indexPosition=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 { @@ -1467,7 +1500,7 @@ void Button_Palette(void) non_greys=1; else greys=1; - + for (i=block_start; i<=block_end; i++) { byte is_grey = temp_palette[i].R==temp_palette[i].G && temp_palette[i].R == temp_palette[i].B; @@ -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->PositionPosition)++; 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->PositionPosition)++; 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); @@ -2877,8 +2913,12 @@ 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); diff --git a/src/palette.h b/src/palette.h index 5b68e1d4..9d1b017a 100644 --- a/src/palette.h +++ b/src/palette.h @@ -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.