/* vim:expandtab:ts=2 sw=2: */ /* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2007-2017 Adrien Destugues Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) Grafx2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. Grafx2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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" #include "misc.h" #include "engine.h" #include "readline.h" #include "buttons.h" #include "pages.h" #include "help.h" #include "sdlscreen.h" #include "errors.h" #include "op_c.h" #include "windows.h" #include "input.h" #include "palette.h" #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 //int RGB_scale = 64; // VGA //int RGB_scale = 16; // Amiga //int RGB_scale = 4; // MSX2 //int RGB_scale = 3; // Amstrad CPC // Nombre de graduations pour une composante dans le mode actuel int Color_count=256; // Les composantes vont de 0 à (Color_count-1) int Color_max=255; // Le demi-pas est une quantité que l'on ajoute à une composante // avant de faire un arrondi par division. int Color_halfstep=0; void Set_palette_RGB_scale(int scale) { if (scale>= 2 && scale <= 256) RGB_scale = scale; } 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 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) { // 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-(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". static int Decode_component(int comp) { float res = pow((float)comp/Color_max,1/Gamma)*255; return (int)res; } /// Turns a RGB component from 0-255 to 0-(RGB_scale-1) and apply Gamma correction. static int Encode_component(int comp) { float res = pow(comp/255.0,Gamma)*Color_max; return ceil(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; } // Définir les unités pour les graduations R G B ou H S V void Component_unit(int count) { Color_count = count; Color_max = count-1; Color_halfstep = 256/count/2; } void Set_HSL(T_Palette start_palette, T_Palette end_palette, byte color, short diff_h, short diff_s, short diff_l) { byte h, s, l; RGB_to_HSL(start_palette[color].R,start_palette[color].G,start_palette[color].B,&h,&s,&l); // La teinte (Hue) est cyclique h=(diff_h+256+h); // Pour les autres (Saturation, Lightness), au lieu d'additionner, // on va faire un ratio, cela utilise mieux la plage de valeurs 0-255 if (diff_s<0) s=(255+diff_s)*s/255; else if (diff_s>0) s=255-(255-diff_s)*(255-s)/255; if (diff_l<0) l=(255+diff_l)*l/255; else if (diff_l>0) l=255-(255-diff_l)*(255-l)/255; HSL_to_RGB(h,s,l,&end_palette[color].R,&end_palette[color].G,&end_palette[color].B); } void Set_red(byte color, short new_color, T_Palette palette) { if (new_color< 0) new_color= 0; if (new_color>255) new_color=255; // Arrondi new_color=Round_palette_component(new_color); palette[color].R=new_color; } void Set_green(byte color, short new_color, T_Palette palette) { if (new_color< 0) new_color= 0; if (new_color>255) new_color=255; // Arrondi new_color=Round_palette_component(new_color); palette[color].G=new_color; } void Set_blue(byte color, short new_color, T_Palette palette) { if (new_color< 0) new_color= 0; if (new_color>255) new_color=255; // Arrondi new_color=Round_palette_component(new_color); palette[color].B=new_color; } void Format_component(byte value, char *str) // Formate une chaine de 4 caractères+\0 : "nnn " { Num2str(value,str,3); str[3]=' '; str[4]='\0'; } void Spread_colors(short start,short end,T_Palette palette) // Modifie la palette pour obtenir un dégradé de couleur entre les deux bornes // passées en paramètre { short start_red; short start_green; short start_blue; short end_red; short end_green; short end_blue; short index; // On vérifie qu'il y ait assez de couleurs entre le début et la fin pour // pouvoir faire un dégradé: if ( (start!=end) && (start+1!=end) ) { start_red = Encode_component(palette[start].R); start_green = Encode_component(palette[start].G); start_blue = Encode_component(palette[start].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=Window_pos_Y) && (y_pos=Window_pos_X) && (x_posPages->Image_mode != IMAGE_MODE_ANIMATION && Main.backups->Pages->Image_mode != IMAGE_MODE_MODE5) { Remap_general_lowlevel(conversion_table,Main.visible_image.Image,Main.visible_image.Image, Main.image_width,Main.image_height,Main.image_width); } // Remap all layers for (layer=0; layerPages->Nb_layers; layer++) Remap_general_lowlevel(conversion_table,Main.backups->Pages->Image[layer].Pixels,Main.backups->Pages->Image[layer].Pixels,Main.image_width,Main.image_height,Main.image_width); // Remap transparent color Main.backups->Pages->Transparent_color = conversion_table[Main.backups->Pages->Transparent_color]; // On calcule les limites à l'écran de l'image if (Main.image_height>=Menu_Y_before_window) end_y=Menu_Y_before_window; else end_y=Main.image_height; if (!Main.magnifier_mode) { if (Main.image_width>=Screen_width) end_x=Screen_width; else end_x=Main.image_width; } else { if (Main.image_width>=Main.separator_position) end_x=Main.separator_position; else end_x=Main.image_width; if ((Main.X_zoom+(Main.image_width*Main.magnifier_factor))>=Screen_width) end_x_mag=Screen_width; else end_x_mag=(Main.X_zoom+(Main.image_width*Main.magnifier_factor)); if (Main.image_height*Main.magnifier_factor>=Menu_Y_before_window) end_y_mag=Menu_Y_before_window; else end_y_mag=Main.image_height*Main.magnifier_factor; } // On doit maintenant faire la traduction à l'écran Remap_zone_highlevel(0,0,end_x,end_y,conversion_table); if (Main.magnifier_mode) { Remap_zone_highlevel(Main.separator_position,0,end_x_mag,end_y_mag,conversion_table); // Il peut encore rester le bas de la barre de split à remapper si la // partie zoomée ne descend pas jusqu'en bas... Remap_zone_highlevel(Main.separator_position,end_y_mag, (Main.separator_position+(SEPARATOR_WIDTH*Menu_factor_X)), Menu_Y_before_window,conversion_table); } // Remappe tous les fonds de fenetre (qui doivent contenir un bout d'écran) Remap_window_backgrounds(conversion_table, 0, Menu_Y_before_window); } void Swap(int with_remap,short block_1_start,short block_2_start,short block_size,T_Palette palette, dword * color_usage) { short pos_1; short pos_2; short end_1; short end_2; byte conversion_table[256]; T_Components temp_palette[256]; dword temp_usage[256]; // On fait une copie de la palette memcpy(temp_palette, palette, sizeof(T_Palette)); // On fait une copie de la table d'used des couleurs memcpy(temp_usage, color_usage, sizeof(dword) * 256); // On commence à initialiser la table de conversion à un état où elle ne // fera aucune conversion. for (pos_1=0;pos_1<=255;pos_1++) conversion_table[pos_1]=pos_1; // On calcul les dernières couleurs de chaque bloc. end_1=block_1_start+block_size-1; end_2=block_2_start+block_size-1; if ((block_2_start>=block_1_start) && (block_2_start<=end_1)) { // Le bloc destination commence dans le bloc source. for (pos_1=block_1_start,pos_2=end_1+1;pos_1<=end_2;pos_1++) { // Il faut transformer la couleur pos_1 en pos_2: conversion_table[pos_2]=pos_1; color_usage[pos_1]=temp_usage[pos_2]; palette[pos_1].R=temp_palette[pos_2].R; palette[pos_1].G=temp_palette[pos_2].G; palette[pos_1].B=temp_palette[pos_2].B; // On gère la mise à jour de pos_2 if (pos_2==end_2) pos_2=block_1_start; else pos_2++; } } else if ((block_2_start=block_1_start)) { // Le bloc destination déborde dans le bloc source. for (pos_1=block_2_start,pos_2=block_1_start;pos_1<=end_1;pos_1++) { // Il faut transformer la couleur pos_1 en pos_2: conversion_table[pos_2]=pos_1; color_usage[pos_1]=temp_usage[pos_2]; palette[pos_1].R=temp_palette[pos_2].R; palette[pos_1].G=temp_palette[pos_2].G; palette[pos_1].B=temp_palette[pos_2].B; // On gère la mise à jour de pos_2 if (pos_2==end_1) pos_2=block_2_start; else pos_2++; } } else { // Le bloc source et le bloc destination sont distincts. for (pos_1=block_1_start,pos_2=block_2_start;pos_1<=end_1;pos_1++,pos_2++) { // Il va falloir permutter la couleur pos_1 avec la couleur pos_2 conversion_table[pos_1]=pos_2; conversion_table[pos_2]=pos_1; // On intervertit le nombre d'used des couleurs pour garder une // cohérence lors d'un éventuel "Zap unused". SWAP_DWORDS(color_usage[pos_1], color_usage[pos_2]) // On fait un changement de teinte: SWAP_BYTES(palette[pos_1].R, palette[pos_2].R) SWAP_BYTES(palette[pos_1].G, palette[pos_2].G) SWAP_BYTES(palette[pos_1].B, palette[pos_2].B) } } if (with_remap) { Remap_image_highlevel(conversion_table); } else { // Restore color usage. Shouldn't have reordered it in the first place. memcpy(color_usage, temp_usage, sizeof(dword) * 256); } } void Set_nice_menu_colors(dword * color_usage,int not_picture) { short index,index2; byte color; byte replace_table[256]; T_Components rgb[4]; short new_colors[4]={255,254,253,252}; // On initialise la table de remplacement for (index=0; index<256; index++) replace_table[index]=index; // On recherche les 4 couleurs les moins utilisées dans l'image pour pouvoir // les remplacer par les nouvelles couleurs. for (index2=0; index2<4; index2++) for (index=255; index>=0; index--) { if ((index!=new_colors[0]) && (index!=new_colors[1]) && (index!=new_colors[2]) && (index!=new_colors[3]) && (color_usage[index]new_colors[index+1]) { index2 =new_colors[index]; new_colors[index] =new_colors[index+1]; new_colors[index+1]=index2; color=1; } } } while (color); // On sauvegarde dans rgb les teintes qu'on va remplacer et on met les // couleurs du menu par défaut for (index=0; index<4; index++) { const T_Components * target_rgb; target_rgb=Favorite_GUI_color(index); color=new_colors[index]; rgb[index].R=Main.palette[color].R; rgb[index].G=Main.palette[color].G; rgb[index].B=Main.palette[color].B; Main.palette[color].R=Round_palette_component(target_rgb->R); Main.palette[color].G=Round_palette_component(target_rgb->G); Main.palette[color].B=Round_palette_component(target_rgb->B); } // Maintenant qu'on a placé notre nouvelle palette, on va chercher quelles // sont les couleurs qui peuvent remplacer les anciennes Hide_cursor(); for (index=0; index<4; index++) replace_table[new_colors[index]]=Best_color_nonexcluded (rgb[index].R,rgb[index].G,rgb[index].B); if (not_picture) { // Remap caused by preview. Only remap screen Remap_zone_highlevel(0,0,Screen_width,Screen_height,replace_table); } else { // On fait un changement des couleurs visibles à l'écran et dans l'image Remap_image_highlevel(replace_table); } Display_cursor(); } void Reduce_palette(short * used_colors,int nb_colors_asked,T_Palette palette,dword * color_usage) { char str[5]; // buffer d'affichage du compteur byte conversion_table[256]; // Table de conversion int color_1; // |_ Variables de balayages int color_2; // | de la palette int best_color_1=0; int best_color_2=0; long best_difference; dword used; dword best_used; // On commence par initialiser la table de conversion dans un état où // aucune conversion ne sera effectuée. for (color_1=0; color_1<=255; color_1++) conversion_table[color_1]=color_1; // Si on ne connait pas encore le nombre de couleurs utilisées, on le // calcule! (!!! La fonction appelée Efface puis Affiche le curseur !!!) if ((*used_colors)<0) Update_color_count(used_colors,color_usage); Hide_cursor(); // On tasse la palette vers le début parce qu'elle doit ressembler à // du Gruyère (et comme Papouille il aime pas le fromage...) // Pour cela, on va scruter la couleur color_1 et se servir de l'indice // color_2 comme position de destination. for (color_1=color_2=0;color_1<=255;color_1++) { if (color_usage[color_1]) { // On commence par s'occuper des teintes de la palette palette[color_2].R=palette[color_1].R; palette[color_2].G=palette[color_1].G; palette[color_2].B=palette[color_1].B; // Ensuite, on met à jour le tableau d'occupation des couleurs. color_usage[color_2]=color_usage[color_1]; // On va maintenant s'occuper de la table de conversion: conversion_table[color_1]=color_2; // Maintenant, la place désignée par color_2 est occupée, alors on // doit passer à un indice de destination suivant. color_2++; } } // On met toutes les couleurs inutilisées en noir for (;color_2<256;color_2++) { palette[color_2].R=0; palette[color_2].G=0; palette[color_2].B=0; color_usage[color_2]=0; } // Maintenant qu'on a une palette clean, on va boucler en réduisant // le nombre de couleurs jusqu'à ce qu'on atteigne le nombre désiré. // (The stop condition is further down) while (1) { // Il s'agit de trouver les 2 couleurs qui se ressemblent le plus // parmis celles qui sont utilisées (bien sûr) et de les remplacer par // une seule couleur qui est la moyenne pondérée de ces 2 couleurs // en fonction de leur utilisation dans l'image. best_difference =26*255*26*255+55*255*255+19*255*19*255; best_used=0x7FFFFFFF; for (color_1=0;color_1<(*used_colors);color_1++) for (color_2=color_1+1;color_2<(*used_colors);color_2++) if (color_1!=color_2) { long dr, dg, db; long difference; // could also be called distance (or distance square) dr = (long)palette[color_1].R - (long)palette[color_2].R; dg = (long)palette[color_1].G - (long)palette[color_2].G; db = (long)palette[color_1].B - (long)palette[color_2].B; difference =26*26*dr*dr + 55*55*dg*dg + 19*19*db*db; if (difference<=best_difference) { used=color_usage[color_1]+color_usage[color_2]; if ((differencebest_color_2) { // La color_1 va scroller en arrière. // Donc on transfère son utilisation dans l'utilisation de la // couleur qui la précède. color_usage[color_1-1]=color_usage[color_1]; // Et on transfère ses teintes dans les teintes de la couleur qui // la précède. palette[color_1-1].R=palette[color_1].R; palette[color_1-1].G=palette[color_1].G; palette[color_1-1].B=palette[color_1].B; } // Une fois la palette et la table d'utilisation gérées, on peut // s'occuper de notre table de conversion. if (conversion_table[color_1]>best_color_2) // La color_1 avait l'intention de se faire remplacer par une // couleur que l'on va (ou que l'on a déjà) bouger en arrière. conversion_table[color_1]--; } // On vient d'éjecter une couleur, donc on peut mettre à jour le nombre // de couleurs utilisées. (*used_colors)--; // A la fin, on doit passer (dans la palette) les teintes du dernier // élément de notre ensemble en noir. palette[*used_colors].R=0; palette[*used_colors].G=0; palette[*used_colors].B=0; // Au passage, on va s'assurer que l'on a pas oublié de la mettre à une // utilisation nulle. color_usage[*used_colors]=0; // Après avoir éjecté une couleur, on le fait savoir à l'utilisateur par // l'intermédiaire du compteur de nombre utilisées. Num2str(*used_colors,str,3); Print_in_window(COUNT_X,COUNT_Y,str,MC_Black,MC_Light); } // Maintenant, tous ces calculs doivent êtres pris en compte dans la // palette, l'image et à l'écran. Remap_image_highlevel(conversion_table); // Et voila pour l'image et l'écran Display_cursor(); } // Position of the numeric values of the R G B sliders static const int NUMERIC_R_X = 216; static const int NUMERIC_G_X = 243; static const int NUMERIC_B_X = 270; static const int NUMERIC_Y = 183; // Position of the whole button static const int NUMERIC_BOX_X = 215; static const int NUMERIC_BOX_Y = 181; static const int NUMERIC_BOX_W = 81; static const int NUMERIC_BOX_H = 12; void Set_palette_slider(T_Scroller_button * slider, word nb_elements, word position, char * value, short x_pos) { slider->Nb_elements=nb_elements; slider->Position=position; Compute_slider_cursor_length(slider); Window_draw_slider(slider); Print_counter(x_pos,NUMERIC_Y,value,MC_Black,MC_Light); } void Display_sliders(T_Scroller_button * red_slider, T_Scroller_button * green_slider, T_Scroller_button * blue_slider, byte block_is_selected, T_Components * palette) { char str[5]; if (block_is_selected) { Set_palette_slider(red_slider,Color_max*2+1,Color_max,"± 0",NUMERIC_R_X); Set_palette_slider(green_slider,Color_max*2+1,Color_max,"± 0",NUMERIC_G_X); Set_palette_slider(blue_slider,Color_max*2+1,Color_max,"± 0",NUMERIC_B_X); } else { byte j1, j2, j3; j1= palette[Fore_color].R; j2= palette[Fore_color].G; j3= palette[Fore_color].B; if (!Palette_view_is_RGB) { RGB_to_HSL(j1,j2,j3,&j1,&j2,&j3); } 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); } } void Draw_all_palette_sliders(T_Scroller_button * red_slider, T_Scroller_button * green_slider, T_Scroller_button * blue_slider, T_Palette palette,byte start,byte end) { char str[5]; Hide_cursor(); // Réaffichage des jauges: if (start!=end) { // Dans le cas d'un bloc, tout à 0. red_slider->Position =Color_max; Window_draw_slider(red_slider); Print_counter(NUMERIC_R_X,NUMERIC_Y,"± 0",MC_Black,MC_Light); green_slider->Position =Color_max; Window_draw_slider(green_slider); Print_counter(NUMERIC_G_X,NUMERIC_Y,"± 0",MC_Black,MC_Light); blue_slider->Position =Color_max; Window_draw_slider(blue_slider); Print_counter(NUMERIC_B_X,NUMERIC_Y,"± 0",MC_Black,MC_Light); } else { // Dans le cas d'une seule couleur, composantes. byte j1, j2, j3; j1= palette[start].R; j2= palette[start].G; j3= palette[start].B; if (!Palette_view_is_RGB) { RGB_to_HSL(j1,j2,j3,&j1,&j2,&j3); } 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(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(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); } Display_cursor(); } int Window_Histogram(unsigned char block_start, unsigned char block_end, dword* color_usage) { int i, j; unsigned int max_count = 0; int old_height=0; int hovered_color=-1; int new_hovered_color; int bar_width; T_Special_button *histo; int clicked_button; /* Draws an histogram of the selected range in a separate window */ if (block_start == block_end) { // only one color selected: auto-detect the range for (block_start=0; block_start!=255; block_start++) if (color_usage[block_start]) break; for (block_end=255; block_end!=0; block_end--) if (color_usage[block_end]) break; } // Normalize the histogram towards the most used color // Step 1 : find the most used color in the range for(i=block_start; i<= block_end; i++) { if(color_usage[i] > max_count) max_count = color_usage[i]; } if (max_count == 0) { Warning_message("All these colors are unused!"); Hide_cursor(); return -1; } Open_window(263, 150, "Histogram"); Window_set_normal_button(120, 130, 42, 14, "Close",-1,1,SDLK_RETURN); Print_in_window(6, 17, "Color:", MC_Dark, MC_Light); Print_in_window(110+12*8, 17, "Pixels", MC_Dark, MC_Light); // Step 2 : draw bars bar_width=256/(block_end-block_start+1); j = 0; for(i=block_start; i<= block_end; i++) { int height = 100*color_usage[i]/max_count; // Don't draw anything if the color is unused if (color_usage[i]!=0) { // Draw at least one pixel if the color is used if (height==0) height=1; Window_rectangle( 3+j*bar_width, 127-height, bar_width, height, i); //if (i == MC_Light) { Window_rectangle( 3+j*bar_width, 126-height, bar_width, 1,MC_Black); //} } // vertical outline if (height>old_height) Window_rectangle( 2+j*bar_width, 126-height, 1, height-old_height+1,MC_Black); else if (old_height>height) Window_rectangle( 3+j*bar_width, 126-old_height, 1, old_height-height+1,MC_Black); old_height=height; j++; } // Last vertical outline if (old_height!=0) Window_rectangle( 3+j*(256/(block_end-block_start+1)), 126-old_height, 1, old_height+1,MC_Black); histo = Window_set_special_button(3, 27, j*bar_width, 100); // 2 Update_window_area(0,0,263,150); Display_cursor(); do { // Find hovered area if (Window_click_in_rectangle(histo->Pos_X,histo->Pos_Y,histo->Pos_X+histo->Width-1,histo->Pos_Y+histo->Height-1)) { short x_pos; x_pos=((short)Mouse_X-Window_pos_X)/Menu_factor_X; new_hovered_color=block_start+(x_pos-histo->Pos_X)/bar_width; } else new_hovered_color=-1; // When changing hovered color, update the info area if (new_hovered_color!=hovered_color) { char str[12]; hovered_color=new_hovered_color; Hide_cursor(); if (hovered_color==-1) { Window_rectangle(6+6*8,17,3*8,7,MC_Light); Update_window_area(6+6*8,17,3*8,7); Window_rectangle(86,17,2*8,8,MC_Light); Update_window_area(86,17,2*8,8); Window_rectangle(110,17,11*8,7,MC_Light); Update_window_area(110,17,11*8,7); } else { Num2str(hovered_color,str ,3); Print_in_window(6+6*8,17,str,MC_Black,MC_Light); Window_rectangle(86,17,2*8,8,hovered_color); Update_window_area(86,17,2*8,8); Num2str(color_usage[hovered_color],str ,11); Print_in_window(110,17,str,MC_Black,MC_Light); } Display_cursor(); } clicked_button=Window_clicked_button(); if (Key == KEY_ESC) clicked_button=1; } while( clicked_button < 1); Close_window(); if (clicked_button==2) { // This is a counter-hack. Close_window() sets Mouse_K to zero // on exit, I don't know why (It will become 1 again if you move // the mouse slightly) // Here I force it back to 1, so that the Wait_end_of_click() // will really wait for a release of mouse button. Mouse_K=1; return hovered_color; } return -1; } void Print_RGB_or_HSL(byte mode) { Print_in_window(224,16,mode?"H":"R",MC_Dark,MC_Light); Print_in_window(251,16,mode?"S":"G",MC_Dark,MC_Light); Print_in_window(278,16,mode?"L":"B",MC_Dark,MC_Light); } void Tag_used_colors(byte color, dword color_usage[]) { word index; for (index=0;index<=255;index++) { short x_pos=Window_palette_button_list->Pos_X+6+((index>>4)*10); short y_pos=Window_palette_button_list->Pos_Y+3+((index&15)* 5); byte col; col=(color&&color_usage[index])?MC_White:MC_Light; Window_rectangle(x_pos+5,y_pos+0,1,5,col); } Update_window_area(Window_palette_button_list->Pos_X+3,Window_palette_button_list->Pos_Y+3,12*16,5*16); } void Button_Palette(int btn) { static const int BUTTON_PLUS_X = 182; static const int BUTTON_PLUS_Y = 163; static const int BUTTON_MINUS_X = 198; static const int BUTTON_MINUS_Y = 163; // Coordinates of the block that displays Backcolor static const int BGCOLOR_DISPLAY_X = 176; static const int BGCOLOR_DISPLAY_Y = 65; static const int BGCOLOR_DISPLAY_W = 40; static const int BGCOLOR_DISPLAY_H = 96; // Coordinates of the block that displays Forecolor static const int FGCOLOR_DISPLAY_X = 180; static const int FGCOLOR_DISPLAY_Y = 69; static const int FGCOLOR_DISPLAY_W = 32; static const int FGCOLOR_DISPLAY_H = 88; // Coordinates of the Color# static const int COLOR_X = 111; static const int COLOR_Y = 79; static short reduce_colors_number = 256; short temp_color; // Variable pouvant reservir pour différents calculs intermédiaires dword temp; byte color,click; // Variables pouvant reservir pour différents calculs intermédiaires short clicked_button; word old_mouse_x; word old_mouse_y; byte old_mouse_k; byte block_start; byte block_end; byte first_color; byte last_color; char str[10]; word i; T_Scroller_button * red_slider; T_Scroller_button * green_slider; T_Scroller_button * blue_slider; T_Dropdown_button * reduce_dropdown; T_Dropdown_button * sort_dropdown; byte image_is_backed_up = 0; byte need_to_remap = 0; dword color_usage[256]; short used_colors = -1; // -1 <=> Inconnu byte conversion_table[256]; //T_Components * backup_palette; //T_Components * temp_palette; //T_Components * working_palette; static byte show_used_colors=0; static const int C1_X = 5; static const int C1_W = 45; static const int C2_X = 51; static const int C2_W = 53; static const int C3_X = 105; static const int C3_W = 53; static const int C4_X = 159; static const int C4_W = 53; static const int L1 = 16; static const int L2 = 31; static const int L3 = 46; static const int L4 = 61; (void)btn; backup_palette =(T_Components *)malloc(sizeof(T_Palette)); temp_palette=(T_Components *)malloc(sizeof(T_Palette)); working_palette=(T_Components *)malloc(sizeof(T_Palette)); Component_unit(RGB_scale); Open_window(299, 196,"Palette"); memcpy(working_palette, Main.palette, sizeof(T_Palette)); Palette_edit_step(); Window_set_palette_button(5, 89); // 1 // Frame around the palette sliders //Window_display_frame (172, 63, 122, 129); // Graduation des jauges de couleur Window_rectangle(220,39+32,17,1,MC_Dark); Window_rectangle(247,39+32,17,1,MC_Dark); Window_rectangle(274,39+32,17,1,MC_Dark); Window_rectangle(220,39+64,17,1,MC_Dark); Window_rectangle(247,39+64,17,1,MC_Dark); Window_rectangle(274,39+64,17,1,MC_Dark); Window_rectangle(220,39+96,17,1,MC_Dark); 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-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); Component_unit(RGB_scale); } else { Print_RGB_or_HSL(1); Component_unit(256); } first_color=last_color=block_start=block_end=Fore_color; Tag_color_range(block_start,block_end); // Affichage dans le block de visu de la couleur en cours Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H,Back_color); Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); // Affichage des valeurs de la couleur courante (pour 1 couleur) Display_sliders(red_slider,green_slider,blue_slider,(block_start!=block_end),working_palette); Print_in_window(7, COLOR_Y, "Color number:", MC_Dark, MC_Light); Num2str(Fore_color, str, 3); Print_in_window(COLOR_X, COLOR_Y, str, MC_Black, MC_Light); Window_set_normal_button(C4_X,L3,C4_W,14,"Merge" ,0,1,SDLK_m); // 5 Window_set_normal_button(C2_X,L3,C2_W,14,"Gray" ,1,1,SDLK_g); // 6 Window_set_normal_button(C1_X,L1,C1_W,14,"Swap" ,0,1,KEY_NONE); // 7 Window_set_normal_button(C2_X,L1,C2_W,14,"X-Swap" ,1,1,SDLK_x); // 8 Window_set_normal_button(C3_X,L1,C3_W,14,"Copy" ,1,1,SDLK_c); // 9 Window_set_normal_button(C3_X,L3,C3_W,14,"Spread" ,4,1,SDLK_e); // 10 reduce_dropdown = Window_set_dropdown_button(89, L4, 83, 14, 84, "Reduce", 0, 0, 1, RIGHT_SIDE|LEFT_SIDE, 0); // 11 Window_dropdown_add_item(reduce_dropdown, 256, "to uniques"); Window_dropdown_add_item(reduce_dropdown, 128, "to 128"); Window_dropdown_add_item(reduce_dropdown, 64, "to 64"); Window_dropdown_add_item(reduce_dropdown, 32, "to 32"); Window_dropdown_add_item(reduce_dropdown, 16, "to 16"); Window_dropdown_add_item(reduce_dropdown, 8, "to 8"); Window_dropdown_add_item(reduce_dropdown, 4, "to 4"); Window_dropdown_add_item(reduce_dropdown, 2, "to 2"); Window_dropdown_add_item(reduce_dropdown, 0, "Other"); Window_set_normal_button( 5,178,35,14,"Undo" ,1,1,SDLK_u); // 12 Window_set_normal_button(122,178,51,14,"Cancel",0,1,KEY_ESC); // 13 Window_set_normal_button(177,178,35,14,"OK" ,0,1,SDLK_RETURN); // 14 Window_set_normal_button(C4_X,L2,C4_W,14,"Used",0,1,SDLK_d); // 15 Window_set_normal_button(C1_X,L4,83,14,"Zap unused",0,1,SDLK_DELETE);//16 Window_set_repeatable_button(BUTTON_PLUS_X, BUTTON_PLUS_Y,12,11,"+",0,1,SDLK_KP_PLUS); // 17 Window_set_repeatable_button(BUTTON_MINUS_X,BUTTON_MINUS_Y,12,11,"-",0,1,SDLK_KP_MINUS); // 18 Window_set_normal_button(C1_X,L3,C1_W,14,"Neg" ,1,1,SDLK_n); // 19 Window_set_normal_button(C1_X,L2,C1_W,14,"Flip" ,1,1,SDLK_i); // 20 Window_set_normal_button(C2_X,L2,C2_W,14,"X-Flip" ,5,1,SDLK_v); // 21 // Button without outline (RGB/HSL switch) Window_set_normal_button(NUMERIC_BOX_X,14,81,11,"" ,0,1,SDLK_h); // 22 Window_display_frame_mono(NUMERIC_BOX_X-1,14-1,81+2,11+2,MC_Light); sort_dropdown = Window_set_dropdown_button(C3_X, L2, C3_W, 14, 80, " Sort", 0, 1, 1, RIGHT_SIDE|LEFT_SIDE, 0); // 23 Window_dropdown_add_item(sort_dropdown, 0, "Hue/Light"); Window_dropdown_add_item(sort_dropdown, 1, "Lightness"); Window_dropdown_add_item(sort_dropdown, 2, "Histogram"); Window_set_normal_button(NUMERIC_BOX_X,NUMERIC_BOX_Y,NUMERIC_BOX_W,NUMERIC_BOX_H,"" ,0,1,KEY_NONE); // 24 // Button without outline Window_display_frame_mono(NUMERIC_BOX_X-1,NUMERIC_BOX_Y-1,NUMERIC_BOX_W+2,NUMERIC_BOX_H+2,MC_Light); Window_set_normal_button(C4_X,L1,C4_W,14,"Histo",0,1,KEY_NONE);// 25 Window_set_normal_button( 44,178,35,14,"Load" ,1,1,SDLK_l); // 26 Window_set_normal_button( 83,178,35,14,"Save" ,1,1,SDLK_s); // 27 // Dessin des petits effets spéciaux pour les boutons [+] et [-] Draw_thingumajig(BUTTON_PLUS_X-5, BUTTON_PLUS_Y,MC_White,-1); Draw_thingumajig(BUTTON_MINUS_X+16,BUTTON_MINUS_Y,MC_Dark,+1); Display_cursor(); Update_color_count(&used_colors,color_usage); if (show_used_colors) Tag_used_colors(1, color_usage); Update_window_area(0,0,299,196); do { old_mouse_x=Mouse_X; old_mouse_y=Mouse_Y; old_mouse_k=Mouse_K; clicked_button=Window_clicked_button(); switch (clicked_button) { case 0 : // Nulle part break; case -1 : // Hors de la fenêtre case 1 : // palette if ( (Mouse_X!=old_mouse_x) || (Mouse_Y!=old_mouse_y) || (Mouse_K!=old_mouse_k) ) { Hide_cursor(); temp_color=(clicked_button==1) ? Window_attribute2 : Read_pixel(Mouse_X,Mouse_Y); // Right click outside the window if (Mouse_K==RIGHT_SIDE && clicked_button==-1) { if (Back_color!=temp_color) { Back_color=temp_color; // 4 blocks de back_color entourant la fore_color Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y-BGCOLOR_DISPLAY_Y,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y+FGCOLOR_DISPLAY_H,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_Y+BGCOLOR_DISPLAY_H-FGCOLOR_DISPLAY_Y-FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_X-BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(FGCOLOR_DISPLAY_X+FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_X+BGCOLOR_DISPLAY_W-FGCOLOR_DISPLAY_X-FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Back_color); Update_window_area(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H); } Display_cursor(); } // Right-click inside the palette else if (Mouse_K==RIGHT_SIDE) { // Contextual menu T_Dropdown_button dropdown; T_Dropdown_choice *item; dropdown.Pos_X =0; dropdown.Pos_Y =0; dropdown.Height =0; dropdown.Dropdown_width=48; dropdown.First_item =NULL; dropdown.Bottom_up =1; Window_dropdown_add_item(&dropdown, 1, "Copy"); Window_dropdown_add_item(&dropdown, 2, "Paste"); item=Dropdown_activate(&dropdown,Mouse_X,Mouse_Y); if (item && item->Number == 1) { // Copy Set_clipboard_colors(block_end+1-block_start,working_palette + block_start); Display_cursor(); } else if (item && item->Number == 2) { // Paste int nb_colors; // Backup Palette_edit_step(); nb_colors = Get_clipboard_colors(working_palette, block_start); if (nb_colors>0) { memcpy(temp_palette,working_palette,sizeof(T_Palette)); Set_palette(working_palette); need_to_remap=1; Display_cursor(); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); } else { Display_cursor(); } } else if (Back_color!=temp_color) { // Just select back color Back_color=temp_color; // 4 blocks de back_color entourant la fore_color Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y-BGCOLOR_DISPLAY_Y,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y+FGCOLOR_DISPLAY_H,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_Y+BGCOLOR_DISPLAY_H-FGCOLOR_DISPLAY_Y-FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_X-BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(FGCOLOR_DISPLAY_X+FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_X+BGCOLOR_DISPLAY_W-FGCOLOR_DISPLAY_X-FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Back_color); Update_window_area(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H); Display_cursor(); } else { Display_cursor(); } Window_dropdown_clear_items(&dropdown); } else { if (!old_mouse_k) { // On vient de clicker sur une couleur (et une seule) if ( (Fore_color!=temp_color) || (block_start!=block_end) ) { // La couleur en question est nouvelle ou elle annule un // ancien bloc. Il faut donc sélectionner cette couleur comme // unique couleur choisie. Fore_color=first_color=last_color=block_start=block_end=temp_color; Tag_color_range(block_start,block_end); // Affichage du n° de la couleur sélectionnée Window_rectangle(COLOR_X,COLOR_Y,56,7,MC_Light); Num2str(Fore_color,str,3); Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); Update_window_area(COLOR_X,COLOR_Y,56,7); // Affichage des jauges Window_rectangle(NUMERIC_R_X,NUMERIC_Y,72,7,MC_Light); Display_sliders(red_slider,green_slider,blue_slider,0,working_palette); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); Update_window_area(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H); Palette_edit_select_range(); } } else { // On maintient le click, on va donc tester si le curseur bouge if (temp_color!=last_color) { // On commence par ordonner la 1ère et dernière couleur du bloc if (first_colortemp_color) { block_start=temp_color; block_end=first_color; // Affichage du n° de la couleur sélectionnée Num2str(block_start,str ,3); Num2str(block_end ,str+4,3); str[3]=26; // Flèche vers la droite Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // Affichage des jauges Display_sliders(red_slider,green_slider,blue_slider,1,NULL); // Affichage dans le block de visu du bloc (dégradé) en cours Display_grad_block_in_window(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,block_start,block_end); } else { block_start=block_end=first_color; Window_rectangle(NUMERIC_R_X,NUMERIC_Y,72,7,MC_Light); // Affichage du n° de la couleur sélectionnée Window_rectangle(COLOR_X+24,COLOR_Y,32,7,MC_Light); Update_window_area(COLOR_X+24,COLOR_Y,32,7); Num2str(Fore_color,str,3); Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // Affichage des jauges Display_sliders(red_slider,green_slider,blue_slider,0,working_palette); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); Update_window_area(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H); } // On tagge le bloc (ou la couleur) Tag_color_range(block_start,block_end); } last_color=temp_color; } Display_cursor(); } } break; case 2 : // Jauge rouge Hide_cursor(); Palette_edit_alter_channel(); if (block_start==block_end) { if(Palette_view_is_RGB) { Set_red(Fore_color,Decode_component(Color_max-red_slider->Position),working_palette); Format_component(Color_max-red_slider->Position,str); } else { HSL_to_RGB( 255-red_slider->Position, 255-green_slider->Position, 255-blue_slider->Position, &working_palette[Fore_color].R, &working_palette[Fore_color].G, &working_palette[Fore_color].B); Format_component((int)255-red_slider->Position,str); } Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light); } else { if(Palette_view_is_RGB) { for (i=block_start; i<=block_end; i++) Set_red(i,Add_encoded(temp_palette[i].R, Color_max-red_slider->Position),working_palette); } else { byte greys=0; byte non_greys=0; // Check if the range contains both greys and non-greys for (i=block_start; i<=block_end; i++) if (temp_palette[i].R==temp_palette[i].G && temp_palette[i].R == temp_palette[i].B) 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; Set_HSL( temp_palette, working_palette, i, is_grey && greys && non_greys ? 0 : Color_max-red_slider->Position, is_grey && greys && non_greys ? 0 : Color_max-green_slider->Position, Color_max-blue_slider->Position ); } } if (red_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-red_slider->Position),str,4); str[0]='-'; } else if (red_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light); } need_to_remap=1; Display_cursor(); Set_palette(working_palette); break; case 3 : // Jauge verte Hide_cursor(); Palette_edit_alter_channel(); if (block_start==block_end) { if(Palette_view_is_RGB) { Set_green (Fore_color,Decode_component(Color_max-green_slider->Position),working_palette); Format_component(Color_max-green_slider->Position,str); } else { HSL_to_RGB( 255-red_slider->Position, 255-green_slider->Position, 255-blue_slider->Position, &working_palette[Fore_color].R, &working_palette[Fore_color].G, &working_palette[Fore_color].B); Format_component((int)255-green_slider->Position,str); } Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light); } else { if(Palette_view_is_RGB) { for (i=block_start; i<=block_end; i++) Set_green (i,Add_encoded(temp_palette[i].G, Color_max-green_slider->Position),working_palette); } else { byte greys=0; byte non_greys=0; // Check if the range contains both greys and non-greys for (i=block_start; i<=block_end; i++) if (temp_palette[i].R==temp_palette[i].G && temp_palette[i].R == temp_palette[i].B) 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; Set_HSL( temp_palette, working_palette, i, is_grey && greys && non_greys ? 0 : Color_max-red_slider->Position, is_grey && greys && non_greys ? 0 : Color_max-green_slider->Position, Color_max-blue_slider->Position ); } } if (green_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-green_slider->Position),str,4); str[0]='-'; } else if (green_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light); } need_to_remap=1; Display_cursor(); Set_palette(working_palette); break; case 4 : // Jauge bleue Hide_cursor(); Palette_edit_alter_channel(); if (block_start==block_end) { if(Palette_view_is_RGB) { Set_blue (Fore_color,Decode_component(Color_max-blue_slider->Position),working_palette); Format_component(Color_max-blue_slider->Position,str); } else { HSL_to_RGB( 255-red_slider->Position, 255-green_slider->Position, 255-blue_slider->Position, &working_palette[Fore_color].R, &working_palette[Fore_color].G, &working_palette[Fore_color].B); Format_component((int)255-blue_slider->Position,str); } Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light); } else { if(Palette_view_is_RGB) { for (i=block_start; i<=block_end; i++) Set_blue(i,Add_encoded(temp_palette[i].B, Color_max-blue_slider->Position),working_palette); } else { byte greys=0; byte non_greys=0; // Check if the range contains both greys and non-greys for (i=block_start; i<=block_end; i++) if (temp_palette[i].R==temp_palette[i].G && temp_palette[i].R == temp_palette[i].B) 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; Set_HSL( temp_palette, working_palette, i, is_grey && greys && non_greys ? 0 : Color_max-red_slider->Position, is_grey && greys && non_greys ? 0 : Color_max-green_slider->Position, Color_max-blue_slider->Position ); } } if (blue_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-blue_slider->Position),str,4); str[0]='-'; } else if (blue_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light); } need_to_remap=1; Display_cursor(); Set_palette(working_palette); break; case 5 : // Merge if (block_start!=block_end) { dword sum_r=0, sum_g=0, sum_b=0, used=0; Palette_edit_step(); // Compute weighted average for (i=block_start; i<=block_end; i++) { used+=color_usage[i]; sum_r+=working_palette[i].R * color_usage[i]; sum_g+=working_palette[i].G * color_usage[i]; sum_b+=working_palette[i].B * color_usage[i]; } // Do normal average if no pixels used if (used==0) { sum_r=sum_g=sum_b=used=0; for (i=block_start; i<=block_end; i++) { used+=1; sum_r+=working_palette[i].R; sum_g+=working_palette[i].G; sum_b+=working_palette[i].B; } } for (i=block_start; i<=block_end; i++) { Set_red (i,sum_r/used,working_palette); Set_green(i,sum_g/used,working_palette); Set_blue (i,sum_b/used,working_palette); } } else { temp_color=Wait_click_in_palette(Window_palette_button_list); if (temp_color>=0) { dword sum_r=0, sum_g=0, sum_b=0, used; Palette_edit_step(); // Compute weighted average used=color_usage[temp_color]+color_usage[Fore_color]; if (used) { sum_r=(working_palette[temp_color].R * color_usage[temp_color] + working_palette[Fore_color].R * color_usage[Fore_color]) / used; sum_g=(working_palette[temp_color].G * color_usage[temp_color] + working_palette[Fore_color].G * color_usage[Fore_color]) / used; sum_b=(working_palette[temp_color].B * color_usage[temp_color] + working_palette[Fore_color].B * color_usage[Fore_color]) / used; } else // Normal average { sum_r=(working_palette[temp_color].R+working_palette[Fore_color].R)/2; sum_g=(working_palette[temp_color].G+working_palette[Fore_color].G)/2; sum_b=(working_palette[temp_color].B+working_palette[Fore_color].B)/2; } Set_red (temp_color,sum_r,working_palette); Set_green(temp_color,sum_g,working_palette); Set_blue (temp_color,sum_b,working_palette); Set_red (Fore_color,sum_r,working_palette); Set_green(Fore_color,sum_g,working_palette); Set_blue (Fore_color,sum_b,working_palette); Wait_end_of_click(); } } Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); // On prépare la "modifiabilité" des nouvelles couleurs Set_palette(working_palette); memcpy(temp_palette,working_palette,sizeof(T_Palette)); need_to_remap=1; break; case 6 : // Grey scale // Backup Palette_edit_step(); // Grey Scale for (i=block_start;i<=block_end;i++) { temp_color=(dword)( ((dword)working_palette[i].R*30) + ((dword)working_palette[i].G*59) + ((dword)working_palette[i].B*11) )/100; Set_red(i,temp_color,working_palette); Set_green (i,temp_color,working_palette); Set_blue (i,temp_color,working_palette); } Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); // On prépare la "modifiabilité" des nouvelles couleurs Set_palette(working_palette); memcpy(temp_palette,working_palette,sizeof(T_Palette)); need_to_remap=1; break; case 7 : // Swap case 8 : // X-Swap temp_color=Wait_click_in_palette(Window_palette_button_list); if ((temp_color>=0) && (temp_color!=block_start)) { Hide_cursor(); Palette_edit_step(); // On calcule le nombre de couleurs a swapper sans risquer de sortir // de la palette (La var. first_color est utilisée pour économiser 1 var; c'est tout) first_color=(temp_color+block_end-block_start<=255)?block_end+1-block_start:256-temp_color; if (clicked_button==8) // On ne fait de backup de l'image que si on // est en mode X-SWAP. if (!image_is_backed_up) { Backup_layers(LAYER_ALL); image_is_backed_up=1; } Swap(clicked_button==8,block_start,temp_color,first_color,working_palette,color_usage); memcpy(temp_palette,working_palette,sizeof(T_Palette)); // On déplace le bloc vers les modifs: last_color=block_end=temp_color+first_color-1; Fore_color=first_color=block_start=temp_color; // On raffiche le n° des bornes du bloc: if (block_start!=block_end) { // Cas d'un bloc multi-couleur Num2str(block_start,str ,3); Num2str(block_end ,str+4,3); str[3]=26; // Flèche vers la droite // Affichage dans le block de visu du bloc (dégradé) en cours Display_grad_block_in_window(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,block_start,block_end); } else { // Cas d'une seule couleur Num2str(Fore_color,str,3); Window_rectangle(COLOR_X,COLOR_Y,56,7,MC_Light); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); } Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // On tag le bloc (ou la couleur) Tag_color_range(block_start,block_end); if (show_used_colors) Tag_used_colors(1, color_usage); need_to_remap=1; Set_palette(working_palette); Display_cursor(); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); // En cas de X-Swap, tout l'ecran a pu changer de couleur. if (clicked_button==8) { Palette_edit_step(); // Disable Undo Update_rect(0, 0, Screen_width, Menu_Y_before_window); End_of_modification(); } Wait_end_of_click(); } break; case 9 : // Copy (to other slot) temp_color=Wait_click_in_palette(Window_palette_button_list); if ((temp_color>=0) && (temp_color!=block_start)) { Hide_cursor(); Palette_edit_step(); memcpy(working_palette+temp_color,backup_palette+block_start, ((temp_color+block_end-block_start<=255)?block_end+1-block_start:256-temp_color)*3); memcpy(temp_palette,working_palette,sizeof(T_Palette)); Set_palette(working_palette); // On déplace le bloc vers les modifs: last_color=block_end=((temp_color+block_end-block_start<=255)?(temp_color+block_end-block_start):255); Fore_color=first_color=block_start=temp_color; // On raffiche le n° des bornes du bloc: if (block_start!=block_end) { // Cas d'un bloc multi-couleur Num2str(block_start,str ,3); Num2str(block_end ,str+4,3); str[3]=26; // Flèche vers la droite // Affichage dans le block de visu du bloc (dégradé) en cours Display_grad_block_in_window(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,block_start,block_end); } else { // Cas d'une seule couleur Num2str(Fore_color,str,3); Window_rectangle(COLOR_X,COLOR_Y,56,7,MC_Light); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); } Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // On tag le bloc (ou la couleur) Tag_color_range(block_start,block_end); need_to_remap=1; Display_cursor(); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); Wait_end_of_click(); } break; case 10 : // Spread if (block_start!=block_end) { Palette_edit_step(); Spread_colors(block_start,block_end,working_palette); } else { temp_color=Wait_click_in_palette(Window_palette_button_list); if (temp_color>=0) { Palette_edit_step(); if (temp_color 256) break; // Cancel reduce_colors_number = choice; } else // Each other dropdown item has a number of colors as id. reduce_colors_number = Window_attribute2; if (reduce_colors_number >= 2) { if (!image_is_backed_up) { Backup_layers(LAYER_ALL); image_is_backed_up = 1; } Reduce_palette(&used_colors, reduce_colors_number, working_palette, color_usage); if ((Config.Safety_colors) && (used_colors<4)) { memcpy(temp_palette, Main.palette, sizeof(T_Palette)); memcpy(Main.palette, working_palette, sizeof(T_Palette)); Set_nice_menu_colors(color_usage, 0); memcpy(working_palette, Main.palette, sizeof(T_Palette)); memcpy(Main.palette, temp_palette, sizeof(T_Palette)); } Set_palette(working_palette); // On définit la nouvelle palette Draw_all_palette_sliders(red_slider, green_slider, blue_slider, working_palette, block_start, block_end); memcpy(temp_palette, working_palette, sizeof(T_Palette)); Palette_edit_step(); // Disable Undo End_of_modification(); need_to_remap = 1; } break; case 12: // Undo Palette_edit_undo_redo(); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); Set_palette(working_palette); need_to_remap=1; break; case 15 : // Used : show usage tags show_used_colors = !show_used_colors; Tag_used_colors(show_used_colors, color_usage); break; case 16 : // Zap unused Palette_edit_step(); if (used_colors==-1) Update_color_count(&used_colors,color_usage); for (i=0; i<256; i++) { if (!color_usage[i]) { temp_color=block_start+(i % (block_end+1-block_start)); working_palette[i].R=backup_palette[temp_color].R; working_palette[i].G=backup_palette[temp_color].G; working_palette[i].B=backup_palette[temp_color].B; } } if ((Config.Safety_colors) && (used_colors<4) && (block_end==block_start)) { memcpy(temp_palette,Main.palette,sizeof(T_Palette)); memcpy(Main.palette,working_palette,sizeof(T_Palette)); Set_nice_menu_colors(color_usage,0); memcpy(working_palette,Main.palette,sizeof(T_Palette)); memcpy(Main.palette,temp_palette,sizeof(T_Palette)); } Set_palette(working_palette); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); need_to_remap=1; break; case 17 : // [+] if (!Palette_view_is_RGB) break; Hide_cursor(); Palette_edit_alter_channel(); if (block_start==block_end) { if (red_slider->Position) { (red_slider->Position)--; Window_draw_slider(red_slider); 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,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,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); } } else { if (red_slider->Position) { (red_slider->Position)--; Window_draw_slider(red_slider); } if (green_slider->Position) { (green_slider->Position)--; Window_draw_slider(green_slider); } if (blue_slider->Position) { (blue_slider->Position)--; Window_draw_slider(blue_slider); } for (i=block_start; i<=block_end; i++) { 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 -- if (red_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-red_slider->Position),str,4); str[0]='-'; } else if (red_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light); // -- green -- if (green_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-green_slider->Position),str,4); str[0]='-'; } else if (green_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light); // -- blue -- if (blue_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-blue_slider->Position),str,4); str[0]='-'; } else if (blue_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light); } need_to_remap=1; Display_cursor(); Set_palette(working_palette); break; case 18 : // [-] if (!Palette_view_is_RGB) break; Hide_cursor(); Palette_edit_alter_channel(); if (block_start==block_end) { if (red_slider->PositionPosition)++; Window_draw_slider(red_slider); 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,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,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); } } else { if (red_slider->Position<(Color_max*2)) { (red_slider->Position)++; Window_draw_slider(red_slider); } if (green_slider->Position<(Color_max*2)) { (green_slider->Position)++; Window_draw_slider(green_slider); } if (blue_slider->Position<(Color_max*2)) { (blue_slider->Position)++; Window_draw_slider(blue_slider); } for (i=block_start; i<=block_end; i++) { 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 -- if (red_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-red_slider->Position),str,4); str[0]='-'; } else if (red_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_R_X,NUMERIC_Y,str,MC_Black,MC_Light); // -- green -- if (green_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-green_slider->Position),str,4); str[0]='-'; } else if (green_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_G_X,NUMERIC_Y,str,MC_Black,MC_Light); // -- blue -- if (blue_slider->Position>Color_max) { // Jauge dans les négatifs: Num2str(-(Color_max-blue_slider->Position),str,4); str[0]='-'; } else if (blue_slider->PositionPosition ,str,4); str[0]='+'; } else { // Jauge nulle: strcpy(str,"± 0"); } Print_counter(NUMERIC_B_X,NUMERIC_Y,str,MC_Black,MC_Light); } need_to_remap=1; Display_cursor(); Set_palette(working_palette); break; case 19 : // Negative // Backup Palette_edit_step(); // Negative for (i=block_start;i<=block_end;i++) { Set_red(i,255-working_palette[i].R,working_palette); Set_green (i,255-working_palette[i].G,working_palette); Set_blue (i,255-working_palette[i].B,working_palette); } Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); Set_palette(working_palette); // On prépare la "modifiabilité" des nouvelles couleurs memcpy(temp_palette,working_palette,sizeof(T_Palette)); need_to_remap=1; break; case 20 : // Inversion case 21 : // X-Inversion // Backup Palette_edit_step(); // Not undoable if X-Invert // On initialise la table de conversion for (i=0; i<=255; i++) conversion_table[i]=i; // Inversion for (i=block_start; i < block_start + (block_end-block_start+1)/2;i++) { temp_color=block_end-(i-block_start); Set_red (i,backup_palette[temp_color].R,working_palette); Set_green (i,backup_palette[temp_color].G,working_palette); Set_blue (i,backup_palette[temp_color].B,working_palette); Set_red (temp_color,backup_palette[i].R,working_palette); Set_green (temp_color,backup_palette[i].G,working_palette); Set_blue (temp_color,backup_palette[i].B,working_palette); if (clicked_button==21) { conversion_table[i]=temp_color; conversion_table[temp_color]=i; temp=color_usage[i]; color_usage[i]=color_usage[temp_color]; color_usage[temp_color]=temp; } } Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); // Si on est en X-Invert, on remap l'image (=> on fait aussi 1 backup) if (clicked_button==21) { if (!image_is_backed_up) { Backup_layers(LAYER_ALL); image_is_backed_up=1; } Hide_cursor(); Remap_image_highlevel(conversion_table); Display_cursor(); End_of_modification(); Palette_edit_step(); // Disable Undo } // On prépare la "modifiabilité" des nouvelles couleurs Set_palette(working_palette); memcpy(temp_palette,working_palette,sizeof(T_Palette)); need_to_remap=1; break; case 22 : // HSL <> RGB // Acte les changements en cours sur une ou plusieurs couleurs Palette_edit_select_range(); Hide_cursor(); Palette_view_is_RGB = !Palette_view_is_RGB; if(! Palette_view_is_RGB) { // On passe en HSL Print_RGB_or_HSL(1); Component_unit(256); // Display the + and - button as disabled Window_draw_normal_bouton(BUTTON_PLUS_X, BUTTON_PLUS_Y,12,11,"+",0,0); Window_draw_normal_bouton(BUTTON_MINUS_X,BUTTON_MINUS_Y,12,11,"-",0,0); } else { // On passe en RGB Print_RGB_or_HSL(0); Component_unit(RGB_scale); // Display the + and - button as enabled Window_draw_normal_bouton(BUTTON_PLUS_X, BUTTON_PLUS_Y,12,11,"+",0,1); Window_draw_normal_bouton(BUTTON_MINUS_X,BUTTON_MINUS_Y,12,11,"-",0,1); } Display_sliders(red_slider,green_slider,blue_slider,(block_start!=block_end),working_palette); Display_cursor(); Update_window_area(BUTTON_PLUS_X-1,BUTTON_PLUS_Y-1,14,14); Update_window_area(BUTTON_MINUS_X-1,BUTTON_MINUS_Y-1,14,14); break; case 23 : // Sort palette { byte h = 0, l = 0, s=0; byte oh=0,ol=0,os=0; // Valeur pour la couleur précédente int swap=1; byte remap_table[256]; byte inverted_table[256]; byte begin, end; long lightness; long old_lightness; if(block_start==block_end) { begin = 0; end = 255; } else { begin = block_start; end = block_end; } // Init remap table for (i=0;i<256;i++) remap_table[i]=i; // Make a backup because remapping is an undoable modification if (!image_is_backed_up) { Backup_layers(LAYER_ALL); image_is_backed_up=1; } if(Window_attribute2==0) // Sort by Hue (H) and Lightness (L) while(swap==1) { swap=0; h=0;l=255;s=0; for(temp_color=begin;temp_color<=end;temp_color++) { oh=h; ol=l; os=s; RGB_to_HSL(working_palette[temp_color].R, working_palette[temp_color].G, working_palette[temp_color].B,&h,&s,&l); if( ((s==0) && (os>0)) // A grey is before a saturated color || ((s>0 && os > 0) && (hol))) // 2 saturated colors: sort by H, then by L || ((os==0 && s==0) && l>ol)) // Two greys: sort by L only { // Swap color with the previous one SWAP_BYTES(working_palette[temp_color].R, working_palette[temp_color-1].R) SWAP_BYTES(working_palette[temp_color].G, working_palette[temp_color-1].G) SWAP_BYTES(working_palette[temp_color].B, working_palette[temp_color-1].B) SWAP_DWORDS(color_usage[temp_color], color_usage[temp_color-1]) SWAP_BYTES(remap_table[temp_color], remap_table[temp_color-1]) swap=1; } } } else if(Window_attribute2==1) // Sort only on perceived lightness while(swap==1) { swap=0; lightness=Perceptual_lightness(working_palette+begin); for(temp_color=begin+1;temp_color<=end;temp_color++) { old_lightness=lightness; lightness=Perceptual_lightness(working_palette+temp_color); if(lightness>old_lightness) { // Swap color with the previous one SWAP_BYTES(working_palette[temp_color].R, working_palette[temp_color-1].R) SWAP_BYTES(working_palette[temp_color].G, working_palette[temp_color-1].G) SWAP_BYTES(working_palette[temp_color].B, working_palette[temp_color-1].B) SWAP_DWORDS(color_usage[temp_color], color_usage[temp_color-1]) SWAP_BYTES(remap_table[temp_color], remap_table[temp_color-1]) swap=1; } } } else // Sort by color usage in histogram while(swap==1) { swap=0; lightness=color_usage[begin]; for(temp_color=begin+1;temp_color<=end;temp_color++) { old_lightness=lightness; lightness=color_usage[temp_color]; if(lightness>old_lightness) { // Swap color with the previous one SWAP_BYTES(working_palette[temp_color].R, working_palette[temp_color-1].R) SWAP_BYTES(working_palette[temp_color].G, working_palette[temp_color-1].G) SWAP_BYTES(working_palette[temp_color].B, working_palette[temp_color-1].B) SWAP_DWORDS(color_usage[temp_color], color_usage[temp_color-1]) SWAP_BYTES(remap_table[temp_color], remap_table[temp_color-1]) swap=1; } } } for (i=0;i<256;i++) inverted_table[remap_table[i]]=i; Remap_image_highlevel(inverted_table); // Maintenant, tous ces calculs doivent êtres pris en compte dans la // palette, l'image et à l'écran. Set_palette(working_palette); Palette_edit_step(); // Disable Undo End_of_modification(); need_to_remap=1; } break; case 24: // R G B value: Hex entry { char str[7]; unsigned int new_color; Hide_cursor(); Print_in_window(NUMERIC_BOX_X+2,NUMERIC_BOX_Y+2,"Hex",MC_Black,MC_Light); // Clear out remaining area Window_rectangle(NUMERIC_BOX_X+1+3*8,NUMERIC_BOX_Y+1,NUMERIC_BOX_W-3-3*8, NUMERIC_BOX_H-3,MC_Light); Update_window_area(NUMERIC_BOX_X+1+3*8,NUMERIC_BOX_Y+1,NUMERIC_BOX_W-3-3*8, NUMERIC_BOX_H-3); str[0]='\0'; Display_cursor(); if (Readline(NUMERIC_BOX_X+NUMERIC_BOX_W-2-6*8, NUMERIC_BOX_Y+2, str, 6, INPUT_TYPE_HEXA)) { int length = strlen(str); short new_red, new_blue, new_green; if (length==3 || length==6) { sscanf(str, "%x", &new_color); if (length==3) { new_color = ((new_color&0xF00)*0x1100) | ((new_color&0x0F0)*0x110) | ((new_color&0x00F)*0x11); } new_red=(new_color&0xFF0000) >> 16; new_green=(new_color&0x00FF00) >> 8; new_blue=(new_color&0x0000FF); // Backup Palette_edit_step(); // Assign color for (i=block_start;i<=block_end;i++) { Set_red(i,new_red,working_palette); Set_green (i,new_green,working_palette); Set_blue (i,new_blue,working_palette); } // On prépare la "modifiabilité" des nouvelles couleurs Set_palette(working_palette); memcpy(temp_palette,working_palette,sizeof(T_Palette)); need_to_remap=1; } } // Clear out numeric area Window_rectangle(NUMERIC_BOX_X+1,NUMERIC_BOX_Y+1,NUMERIC_BOX_W-2, NUMERIC_BOX_H-2,MC_Light); Update_window_area(NUMERIC_BOX_X+1,NUMERIC_BOX_Y+1,NUMERIC_BOX_W-2, NUMERIC_BOX_H-2); Display_cursor(); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); } break; case 25: // Number of colors used: Open histogram { int selected_col; selected_col=Window_Histogram(block_start, block_end, color_usage); if (selected_col!=-1) { // Tag selected color Fore_color=first_color=last_color=block_start=block_end=selected_col; Tag_color_range(block_start,block_end); // Affichage du n° de la couleur sélectionnée Window_rectangle(COLOR_X,COLOR_Y,56,7,MC_Light); Num2str(Fore_color,str,3); Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); Update_window_area(COLOR_X,COLOR_Y,56,7); // Affichage des jauges Window_rectangle(NUMERIC_R_X,NUMERIC_Y,72,7,MC_Light); Display_sliders(red_slider,green_slider,blue_slider,0,working_palette); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); Update_window_area(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H); Palette_edit_select_range(); } Display_cursor(); Input_sticky_control=0; Wait_end_of_click(); break; } case 26: // Load palette memcpy(backup_palette, Main.palette, sizeof(T_Palette)); memcpy(Main.palette, working_palette, sizeof(T_Palette)); Load_picture(CONTEXT_PALETTE); memcpy(working_palette, Main.palette, sizeof(T_Palette)); Set_palette(working_palette); memcpy(temp_palette,working_palette,sizeof(T_Palette)); memcpy(Main.palette, backup_palette, sizeof(T_Palette)); need_to_remap=1; break; case 27: // Save palette Save_picture(CONTEXT_PALETTE); break; } if (!Mouse_K) { if (Key) { if (Is_shortcut(Key,SPECIAL_PREVIOUS_FORECOLOR)) // Décaler Forecolor vers la gauche { if (block_start==block_end) { Fore_color--; first_color--; last_color--; block_start--; block_end--; Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); Hide_cursor(); Tag_color_range(block_start,block_end); // Affichage du n° de la couleur sélectionnée Num2str(Fore_color,str,3); Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); Update_window_area(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H); Display_cursor(); } Key=0; } else if (Is_shortcut(Key,SPECIAL_NEXT_FORECOLOR)) // Décaler Forecolor vers la droite { if (block_start==block_end) { Fore_color++; first_color++; last_color++; block_start++; block_end++; Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); Hide_cursor(); Tag_color_range(block_start,block_end); // Affichage du n° de la couleur sélectionnée Num2str(Fore_color,str,3); Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); Update_window_area(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H); Display_cursor(); } Key=0; } else if (Is_shortcut(Key,SPECIAL_PREVIOUS_BACKCOLOR)) { Back_color--; Hide_cursor(); Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y-BGCOLOR_DISPLAY_Y,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y+FGCOLOR_DISPLAY_H,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_Y+BGCOLOR_DISPLAY_H-FGCOLOR_DISPLAY_Y-FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_X-BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(FGCOLOR_DISPLAY_X+FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_X+BGCOLOR_DISPLAY_W-FGCOLOR_DISPLAY_X-FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Back_color); Update_window_area(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H); Display_cursor(); Key=0; } else if (Is_shortcut(Key,SPECIAL_NEXT_BACKCOLOR)) { Back_color++; Hide_cursor(); Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y-BGCOLOR_DISPLAY_Y,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y+FGCOLOR_DISPLAY_H,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_Y+BGCOLOR_DISPLAY_H-FGCOLOR_DISPLAY_Y-FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_X-BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(FGCOLOR_DISPLAY_X+FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_X+BGCOLOR_DISPLAY_W-FGCOLOR_DISPLAY_X-FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Back_color); Update_window_area(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H); Display_cursor(); Key=0; } else if (Key == SDLK_BACKSPACE) // Remise des couleurs du menu à l'état normal en essayant // de ne pas trop modifier l'image. { if (!image_is_backed_up) { Backup_layers(LAYER_ALL); image_is_backed_up=1; } if (used_colors==-1) Update_color_count(&used_colors, color_usage); Palette_edit_step(); memcpy(temp_palette,Main.palette,sizeof(T_Palette)); memcpy(Main.palette,working_palette,sizeof(T_Palette)); Set_nice_menu_colors(color_usage,0); memcpy(working_palette,Main.palette,sizeof(T_Palette)); memcpy(Main.palette,temp_palette,sizeof(T_Palette)); Set_palette(working_palette); memcpy(temp_palette,working_palette,sizeof(T_Palette)); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); Update_color_count(&used_colors,color_usage); // End_of_modification(); // Not really needed, the change was in palette entries need_to_remap=1; Key=0; } else if (Is_shortcut(Key,0x100+BUTTON_COLORPICKER)) { // Récupération d'une couleur derrière le menu Get_color_behind_window(&color,&click); if (click) { Hide_cursor(); if (click==RIGHT_SIDE) { if (Back_color!=color) { Back_color=color; // 4 blocks de back_color entourant la fore_color Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y-BGCOLOR_DISPLAY_Y,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y+FGCOLOR_DISPLAY_H,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_Y+BGCOLOR_DISPLAY_H-FGCOLOR_DISPLAY_Y-FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_X-BGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_H,Back_color); Window_rectangle(FGCOLOR_DISPLAY_X+FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_X+BGCOLOR_DISPLAY_W-FGCOLOR_DISPLAY_X-FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Back_color); Update_window_area(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H); } } else { Fore_color=first_color=last_color=block_start=block_end=color; Tag_color_range(block_start,block_end); // Affichage du n° de la couleur sélectionnée Window_rectangle(COLOR_X+24,COLOR_Y,32,7,MC_Light); Update_window_area(COLOR_X+24,COLOR_Y,32,7); Num2str(Fore_color,str,3); Print_in_window(COLOR_X,COLOR_Y,str,MC_Black,MC_Light); // Affichage des jauges Display_sliders(red_slider,green_slider,blue_slider,0,working_palette); // Affichage dans le block de visu de la couleur en cours Window_rectangle(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,Fore_color); Update_window_area(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H); Palette_edit_select_range(); } Display_cursor(); Wait_end_of_click(); } Key=0; } else if (Is_shortcut(Key,0x100+BUTTON_HELP)) { Key=0; Window_help(BUTTON_PALETTE, NULL); } else if (Is_shortcut(Key,0x100+BUTTON_PALETTE)) { // Close (confirm) clicked_button=14; } else if (Key == SHORTCUT_COPY) { Set_clipboard_colors(block_end+1-block_start,working_palette + block_start); } else if (Key == SHORTCUT_PASTE) { int nb_colors; Hide_cursor(); // Backup Palette_edit_step(); nb_colors = Get_clipboard_colors(working_palette, block_start); if (nb_colors>0) { memcpy(temp_palette,working_palette,sizeof(T_Palette)); Set_palette(working_palette); need_to_remap=1; Display_cursor(); Draw_all_palette_sliders(red_slider,green_slider,blue_slider,working_palette,block_start,block_end); } } } if (need_to_remap) { Hide_cursor(); Compute_optimal_menu_colors(working_palette); // On remappe brutalement Remap_screen_after_menu_colors_change(); // Puis on remet les trucs qui ne devaient pas changer Window_draw_palette_bouton(5,89); if (show_used_colors) Tag_used_colors(1, color_usage); Window_rectangle(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H,Back_color); Update_window_area(BGCOLOR_DISPLAY_X,BGCOLOR_DISPLAY_Y,BGCOLOR_DISPLAY_W,BGCOLOR_DISPLAY_H); Display_grad_block_in_window(FGCOLOR_DISPLAY_X,FGCOLOR_DISPLAY_Y,FGCOLOR_DISPLAY_W,FGCOLOR_DISPLAY_H,block_start,block_end); Update_window_area(8,82,16*10,5*16); Display_cursor(); need_to_remap=0; } } } while ((clicked_button!=13) && (clicked_button!=14)); if (clicked_button==14) // Sortie par OK { if ( (!image_is_backed_up) && memcmp(Main.palette,working_palette,sizeof(T_Palette)) ) Backup_layers(LAYER_NONE); memcpy(Main.palette,working_palette,sizeof(T_Palette)); End_of_modification(); // Not really needed, the change was in palette entries } Compute_optimal_menu_colors(Main.palette); // La variable employée ici n'a pas vraiment de rapport avec son nom... need_to_remap=(Window_pos_Y+(Window_height*Menu_factor_Y)Position = (rgb_scale_slider->Position < 128) ? rgb_scale_slider->Position * 2 : 256; Num2str(rgb_scale_slider->Position,str,3); Print_in_window(RGBScale_X,RGBScale_Y,str,MC_Black,MC_Light); Window_draw_slider(rgb_scale_slider); break; case 10: // /2 RGB scale rgb_scale_slider->Position = (rgb_scale_slider->Position <= 2) ? 1 : (rgb_scale_slider->Position)/2; Num2str(rgb_scale_slider->Position,str,3); Print_in_window(RGBScale_X,RGBScale_Y,str,MC_Black,MC_Light); Window_draw_slider(rgb_scale_slider); break; case 11: // Gamma slider Num2str(Window_attribute2,str,2); Hide_cursor(); Print_in_window(178,99,str,MC_Black,MC_Light); Display_cursor(); break; } } while (clicked_button!=1 && clicked_button!=2 && clicked_button!=3 && clicked_button!=4); // We need to get the sliders positions before closing the window, because they will be freed. palette_cols=256-columns_slider->Position; palette_lines=16-lines_slider->Position; rgb_scale=rgb_scale_slider->Position; gamma=gamma_slider->Position/10.f; Close_window(); Unselect_button(BUTTON_PALETTE); Display_cursor(); if (clicked_button==4) // Cancel return; if (palette_vertical != Config.Palette_vertical) { Config.Palette_vertical=palette_vertical; palette_needs_redraw=1; } if (palette_cols!=Config.Palette_cells_X || palette_lines!=Config.Palette_cells_Y) { Config.Palette_cells_X = palette_cols; Config.Palette_cells_Y = palette_lines; palette_needs_redraw=1; } 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); } if (clicked_button==1) { Menu_tag_colors("Tag colors to exclude",Exclude_color,&dummy,1, NULL, SPECIAL_EXCLUDE_COLORS_MENU); } else if (clicked_button==2) { // Open the menu with Shade settings. Same as the shortcut, except // that this will not activate shade mode on exit. Shade_settings_menu(); } if (palette_needs_redraw) { Change_palette_cells(); Display_menu(); Display_sprite_in_menu(BUTTON_PAL_LEFT,Config.Palette_vertical?MENU_SPRITE_VERTICAL_PALETTE_SCROLL:-1); Draw_menu_button(BUTTON_PAL_LEFT,BUTTON_RELEASED); Draw_menu_button(BUTTON_PAL_RIGHT,BUTTON_RELEASED); } } // ========= Clipboard management ============== int Palette_clipboard_count=0; T_Palette Palette_clipboard; /// Put some colors in the clipboard. /// @param nb_colors Number of colors to push /// @param colors First color of the input array void Set_clipboard_colors(int nb_colors, T_Components *colors) { Palette_clipboard_count=nb_colors; if (nb_colors) { memcpy(Palette_clipboard, colors, nb_colors*sizeof(T_Components)); } } /// Get some RGB colors from clipboard. /// @param palette Target palette /// @param start_color Index of first color to replace /// @return Number of colors retrieved (0-256) int Get_clipboard_colors(T_Palette palette, byte start_color) { int nb_colors = Palette_clipboard_count; if (nb_colors==0) return 0; if (start_color+nb_colors > 256) { nb_colors=256-start_color; } memcpy(palette+start_color, Palette_clipboard, nb_colors*sizeof(T_Components)); return nb_colors; } /// Get the favorite color to use for GUI's black,dark,light or white. const T_Components * Favorite_GUI_color(byte color_index) { static const T_Components cpc_colors[4] = { { 0, 0, 0}, { 0, 0,128}, // Dark blue {128,128,128}, // Grey {255,255,255} }; if (RGB_scale==3) { // Check if ALL GUI colors are compatible with /rgb 3 int i; for (i=0; i<4; i++) { T_Components col; col=Gfx->Default_palette[Gfx->Color[i]]; if ((col.R!=255 && col.R!=128 && col.R!=0) ||(col.G!=255 && col.G!=128 && col.G!=0) ||(col.B!=255 && col.B!=128 && col.B!=0)) // Specialized colors for CPC palette return &cpc_colors[color_index]; } // Skin has suitable colors return &(Gfx->Default_palette[Gfx->Color[color_index]]); } else // Should be Config.Fav_menu_colors[index] if using user colors return &(Gfx->Default_palette[Gfx->Color[color_index]]); }