1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /*  Grafx2 - The Ultimate 256-color bitmap paint program
4 
5 	Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6 
7     Grafx2 is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License
9     as published by the Free Software Foundation; version 2
10     of the License.
11 
12     Grafx2 is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 
20 ********************************************************************************
21 
22     Graphical interface management functions (windows, menu, cursor)
23 */
24 
25 #include <math.h>
26 #include <stdarg.h> // va_args ...
27 #include <stdlib.h> // atoi()
28 #include <string.h> // strncpy() strlen()
29 
30 #ifdef _MSC_VER
31 #include <stdio.h>
32 #define strdup _strdup
33 #if _MSC_VER < 1900
34 #define snprintf _snprintf
35 #endif
36 #endif
37 
38 #if defined(__MINT__)
39 #define fabsf(x)  __builtin_fabsf(x)
40 #endif
41 
42 #include "windows.h"
43 
44 #include "engine.h"
45 #include "errors.h"
46 #include "global.h"
47 #include "graph.h"
48 #include "input.h"
49 #include "misc.h"
50 #include "op_c.h"
51 #include "readline.h"
52 #include "screen.h"
53 #include "palette.h"
54 #include "unicode.h"
55 #include "keycodes.h"
56 #include "keyboard.h"
57 
58 #ifndef MIN
59 #define MIN(a,b) ((a)<(b)?(a):(b))
60 #endif
61 
62 #if defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__)
63 // We don't want to underline the keyboard shortcuts as there is no keyboard
64 #define NO_KEYBOARD
65 #endif
66 
67 T_Toolbar_button Buttons_Pool[NB_BUTTONS];
68 T_Menu_Bar Menu_bars[MENUBAR_COUNT] =
69   {{MENU_WIDTH,  9, 1, 45, {NULL,NULL,NULL},  20, BUTTON_HIDE }, // Status
70   {MENU_WIDTH, 14, 1, 35, {NULL,NULL,NULL}, 236, BUTTON_ANIM_PLAY }, // Animation
71   {MENU_WIDTH, 10, 1, 35, {NULL,NULL,NULL}, 144, BUTTON_LAYER_SELECT }, // Layers
72   {MENU_WIDTH, 35, 1,  0, {NULL,NULL,NULL}, 254, BUTTON_CHOOSE_COL }} // Main
73   ;
74 
75 
76 /// Width of one layer button, in pixels before scaling
77 word Layer_button_width = 1;
78 
79 // L'encapsulation tente une percée...ou un dernier combat.
80 
81 // Nombre de cellules réel dans la palette du menu
82 word Menu_cells_X;
Palette_cells_X()83 word Palette_cells_X()
84 {
85   return Menu_cells_X;
86 }
87 word Menu_cells_Y;
Palette_cells_Y()88 word Palette_cells_Y()
89 {
90   return Menu_cells_Y;
91 }
92 
93 // Affichage d'un pixel dans le menu (si visible)
Pixel_in_menu(word bar,word x,word y,byte color)94 void Pixel_in_menu(word bar, word x, word y, byte color)
95 {
96   if (Menu_is_visible && Menu_bars[bar].Visible)
97     Block(x*Menu_factor_X,(y+Menu_bars[bar].Top)*Menu_factor_Y+Menu_Y,Menu_factor_X,Menu_factor_Y,color);
98 }
99 
100 // Affichage d'un pixel dans le menu et met a jour la bitmap de skin
Pixel_in_menu_and_skin(word bar,word x,word y,byte color)101 void Pixel_in_menu_and_skin(word bar, word x, word y, byte color)
102 {
103   Pixel_in_menu(bar, x, y, color);
104   Menu_bars[bar].Skin[2][y*Menu_bars[bar].Skin_width + x] = color;
105 }
106 
107 // Affichage d'un pixel dans la fenêtre (la fenêtre doit être visible)
Pixel_in_window(word x,word y,byte color)108 void Pixel_in_window(word x,word y,byte color)
109 {
110     Block((x*Menu_factor_X)+Window_pos_X,(y*Menu_factor_Y)+Window_pos_Y,Menu_factor_X,Menu_factor_Y,color);
111 }
112 
113 // Affichage d'un rectangle dans la fenêtre (la fenêtre doit être visible)
Window_rectangle(word x_pos,word y_pos,word width,word height,byte color)114 void Window_rectangle(word x_pos,word y_pos,word width,word height,byte color)
115 {
116   Block((x_pos*Menu_factor_X)+Window_pos_X,(y_pos*Menu_factor_Y)+Window_pos_Y,width*Menu_factor_X,height*Menu_factor_Y,color);
117 }
118 
119 
120 // -- Affichages de différents cadres dans une fenêtre -----------------------
121 
122   // -- Frame général avec couleurs paramètrables --
123 
Window_display_frame_generic(word x_pos,word y_pos,word width,word height,byte color_tl,byte color_br,byte color_s,byte color_tlc,byte color_brc)124 void Window_display_frame_generic(word x_pos,word y_pos,word width,word height,
125                                     byte color_tl,byte color_br,byte color_s,byte color_tlc,byte color_brc)
126 // Paramètres de couleurs:
127 // color_tl =Bords Haut et Gauche
128 // color_br =Bords Bas et Droite
129 // color_s  =Coins Haut-Droite et Bas-Gauche
130 // color_tlc=Coin Haut-Gauche
131 // color_brc=Coin Bas-Droite
132 {
133   // Bord haut (sans les extrémités)
134   Window_rectangle(x_pos+1,y_pos,width-2,1,color_tl);
135 
136   // Bord bas (sans les extrémités)
137   Window_rectangle(x_pos+1,y_pos+height-1,width-2,1,color_br);
138 
139   // Bord gauche (sans les extrémités)
140   Window_rectangle(x_pos, y_pos+1,1,height-2,color_tl);
141 
142   // Bord droite (sans les extrémités)
143   Window_rectangle(x_pos+width-1,y_pos+1,1,height-2,color_br);
144 
145   // Coin haut gauche
146   Pixel_in_window(x_pos,y_pos,color_tlc);
147   // Coin haut droite
148   Pixel_in_window(x_pos+width-1,y_pos,color_s);
149   // Coin bas droite
150   Pixel_in_window(x_pos+width-1,y_pos+height-1,color_brc);
151   // Coin bas gauche
152   Pixel_in_window(x_pos,y_pos+height-1,color_s);
153 }
154 
155   // -- Frame dont tout le contour est d'une seule couleur --
156 
Window_display_frame_mono(word x_pos,word y_pos,word width,word height,byte color)157 void Window_display_frame_mono(word x_pos,word y_pos,word width,word height,byte color)
158 {
159   Window_display_frame_generic(x_pos,y_pos,width,height,color,color,color,color,color);
160 }
161 
162   // -- Frame creux: foncé en haut-gauche et clair en bas-droite --
163 
Window_display_frame_in(word x_pos,word y_pos,word width,word height)164 void Window_display_frame_in(word x_pos,word y_pos,word width,word height)
165 {
166   Window_display_frame_generic(x_pos,y_pos,width,height,MC_Dark,MC_White,MC_Light,MC_Dark,MC_White);
167 }
168 
169   // -- Frame bombé: clair en haut-gauche et foncé en bas-droite --
170 
Window_display_frame_out(word x_pos,word y_pos,word width,word height)171 void Window_display_frame_out(word x_pos,word y_pos,word width,word height)
172 {
173   Window_display_frame_generic(x_pos,y_pos,width,height,MC_White,MC_Dark,MC_Light,MC_White,MC_Dark);
174 }
175 
176   // -- Frame de séparation: un cadre bombé dans un cadre creux (3D!!!) --
177 
Window_display_frame(word x_pos,word y_pos,word width,word height)178 void Window_display_frame(word x_pos,word y_pos,word width,word height)
179 {
180   Window_display_frame_in(x_pos,y_pos,width,height);
181   Window_display_frame_out(x_pos+1,y_pos+1,width-2,height-2);
182 }
183 
184 
185 //-- Affichages relatifs à la palette dans le menu ---------------------------
186 
187   // -- Affichage des couleurs courante (fore/back) de pinceau dans le menu --
188 
Display_foreback(void)189 void Display_foreback(void)
190 {
191   if (Menu_is_visible && Menu_bars[MENUBAR_TOOLS].Visible)
192   {
193     Block((MENU_WIDTH-17)*Menu_factor_X,Menu_Y+Menu_factor_Y,Menu_factor_X<<4,Menu_factor_Y*7,Back_color);
194     Block((MENU_WIDTH-13)*Menu_factor_X,Menu_Y+(Menu_factor_Y<<1),Menu_factor_X<<3,Menu_factor_Y*5,Fore_color);
195 
196     Update_rect((MENU_WIDTH-17)*Menu_factor_X,Menu_Y+Menu_factor_Y,Menu_factor_X<<4,Menu_factor_Y*7);
197   }
198 }
199 
200 /*! Get the top left corner for the palette cell of a color
201     @param index Index of the color, starting at 0 for the top left one. Limited to Menu_cells_X/Menu_cells_Y.
202 */
Palette_cell_X(byte index)203 word Palette_cell_X(byte index)
204 {
205   if (Config.Palette_vertical)
206   {
207     return (MENU_WIDTH+1+((index-First_color_in_palette)%Menu_cells_X)*Menu_palette_cell_width)*Menu_factor_X;
208   }
209   else
210   {
211     return (MENU_WIDTH+1+((index-First_color_in_palette)/Menu_cells_Y)*Menu_palette_cell_width)*Menu_factor_X;
212   }
213 }
214 
215 /*! Get the top left corner for the palette cell of a color
216     @param index Index of the color, starting at 0 for the top left one. Limited to Menu_cells_X/Menu_cells_Y.
217 */
Palette_cell_Y(byte index)218 word Palette_cell_Y(byte index)
219 {
220   if (Config.Palette_vertical)
221   {
222     return Menu_Y+((1+(((index-First_color_in_palette)/Menu_cells_X)*(Menu_bars[MENUBAR_TOOLS].Height/Menu_cells_Y)))*Menu_factor_Y);
223   }
224   else
225   {
226     return Menu_Y+((1+(((index-First_color_in_palette)%Menu_cells_Y)*(Menu_bars[MENUBAR_TOOLS].Height/Menu_cells_Y)))*Menu_factor_Y);
227   }
228 }
229 
Set_fore_color(byte color)230 void Set_fore_color(byte color)
231 {
232   byte old_fore_color = Fore_color;
233 
234   Fore_color=color;
235   Reposition_palette();
236   Display_foreback();
237   Frame_menu_color(old_fore_color);
238   Frame_menu_color(Fore_color);
239 }
240 
Set_back_color(byte color)241 void Set_back_color(byte color)
242 {
243   byte old_back_color = Back_color;
244 
245   Back_color=color;
246   Display_foreback();
247   Frame_menu_color(old_back_color);
248   Frame_menu_color(Back_color);
249 }
250 
251 ///
252 /// Redraw the cell in the menu palette for ::Fore_color.
253 /// This function checks bounds, it won't draw anything if Fore_color is not visible.
254 /// @param id: Color number to frame
Frame_menu_color(byte id)255 void Frame_menu_color(byte id)
256 {
257   word start_x,start_y,end_x,end_y;
258   word index;
259   word cell_height=Menu_bars[MENUBAR_TOOLS].Height/Menu_cells_Y;
260   byte color;
261 
262   if (! Menu_bars[MENUBAR_TOOLS].Visible)
263     return;
264 
265   if (id==Fore_color)
266     color = MC_White;
267   else if (id==Back_color)
268     color = MC_Dark;
269   else
270     color = MC_Black;
271 
272   if ((id>=First_color_in_palette) && (id<First_color_in_palette+Menu_cells_X*Menu_cells_Y) && (Menu_is_visible))
273   {
274     if (Config.Separate_colors)
275     {
276       start_x=Palette_cell_X(id)-1;
277       start_y=Palette_cell_Y(id)-1;
278 
279       // TODO: if color is black, we are unselecting a color. If another color next to it is selected, we
280       // will erase one edge of its selection square.
281       // We should check for that here.
282       // But we have to find which color is above and below (not so easy) and for the horizontal, check we
283       // are not at the edge of the palette. This makes a lot of cases to handle.
284       // Top
285       Block(start_x,start_y,(Menu_palette_cell_width)*Menu_factor_X+1,1,color);
286       // Bottom
287       Block(start_x,start_y+cell_height*Menu_factor_Y,(Menu_palette_cell_width)*Menu_factor_X+1,1,color);
288 
289       // Left
290       Block(start_x,start_y+1,1,(cell_height)* Menu_factor_Y,color);
291       //Right
292       Block(start_x+(Menu_palette_cell_width*Menu_factor_X),start_y+1,1,(cell_height)* Menu_factor_Y,color);
293 
294       Update_rect(start_x,start_y,(Menu_palette_cell_width+1)*Menu_factor_X,(cell_height+1)* Menu_factor_Y);
295     }
296     else
297     {
298       // Not separated colors
299       start_x=Palette_cell_X(id);
300       start_y=Palette_cell_Y(id);
301 
302       if (color==MC_Black)
303       {
304         int transparent = -1;
305         int cw = (Menu_palette_cell_width)*Menu_factor_X;
306         int ch = (cell_height)*Menu_factor_Y;
307 
308         if (Main.backups->Pages->Image_mode == 0 && Main.backups->Pages->Nb_layers > 1)
309             transparent = Main.backups->Pages->Transparent_color;
310 
311         // Color is not selected, no dotted lines
312         Block(start_x,start_y,Menu_palette_cell_width*Menu_factor_X,
313             cell_height*Menu_factor_Y,id);
314         if (id == transparent) {
315             Block(start_x, start_y,
316               cw / 2, ch / 2, MC_Light);
317             Block(start_x + cw / 2, start_y + ch / 2,
318               (cw+1) / 2, (ch+1) / 2, MC_Dark);
319         }
320       }
321       else
322       {
323         end_x=Menu_palette_cell_width-1;
324         end_y=cell_height-1;
325 
326         // Draw dotted lines
327 
328         // Top line
329         for (index=0; index<=end_x; index++)
330           Block(start_x+index*Menu_factor_X,start_y,
331                 Menu_factor_X,Menu_factor_Y,
332                 ((index)&1)?color:MC_Black);
333         // Left line
334         for (index=1; index<end_y; index++)
335           Block(start_x,start_y+index*Menu_factor_Y,
336                 Menu_factor_X,Menu_factor_Y,
337                 ((index)&1)?color:MC_Black);
338         // Right line
339         for (index=1; index<end_y; index++)
340           Block(start_x+end_x*Menu_factor_X,start_y+index*Menu_factor_Y,
341                 Menu_factor_X,Menu_factor_Y,
342                 ((index+end_x)&1)?color:MC_Black);
343         // Bottom line
344         for (index=0; index<=end_x; index++)
345           Block(start_x+index*Menu_factor_X,start_y+end_y*Menu_factor_Y,
346                 Menu_factor_X,Menu_factor_Y,
347                 ((index+end_y)&1)?color:MC_Black);
348       }
349       Update_rect(start_x,start_y,Menu_palette_cell_width*Menu_factor_X,cell_height*Menu_factor_Y);
350     }
351   }
352 }
353 
354   // -- Afficher la palette dans le menu --
355 
Display_menu_palette(void)356 void Display_menu_palette(void)
357 {
358   int color;
359   byte cell_height=Menu_bars[MENUBAR_TOOLS].Height/Menu_cells_Y;
360   // width: Menu_palette_cell_width
361 
362   if (Menu_is_visible && Menu_bars[MENUBAR_TOOLS].Visible)
363   {
364 	int transparent = -1;
365 	int cw,ch;
366 
367 	// Fill the whole palette area with black
368     Block(
369       Menu_bars[MENUBAR_TOOLS].Width*Menu_factor_X,
370       Menu_Y,
371       Screen_width-(Menu_bars[MENUBAR_TOOLS].Width*Menu_factor_X),
372       (Menu_bars[MENUBAR_TOOLS].Height)*Menu_factor_Y,
373       MC_Black);
374 
375 	if (Main.backups->Pages->Image_mode == 0
376 		&& Main.backups->Pages->Nb_layers > 1)
377 		transparent = Main.backups->Pages->Transparent_color;
378 
379 	// Compute the size of the color cells (they are smaller by 1px when using
380 	// 'separate colors"
381     if (Config.Separate_colors) {
382 		cw = Menu_palette_cell_width * Menu_factor_X - 1;
383         ch = cell_height * Menu_factor_Y - 1;
384 	} else {
385 		cw = (Menu_palette_cell_width)*Menu_factor_X;
386         ch = (cell_height)*Menu_factor_Y;
387 	}
388 
389 	for (color=First_color_in_palette;color<256&&(color-First_color_in_palette)<Menu_cells_X*Menu_cells_Y;color++) {
390 		// Draw the color block
391         Block(Palette_cell_X(color), Palette_cell_Y(color), cw, ch, color);
392 
393 		// Make the transparent color more visible by adding a MC_Dark/MC_Light pattern to it.
394 		if (color == transparent) {
395         	Block(Palette_cell_X(color),
396               Palette_cell_Y(color),
397               cw / 2, ch / 2, MC_Light);
398         	Block(Palette_cell_X(color) + cw / 2,
399               Palette_cell_Y(color) + ch / 2,
400               (cw+1) / 2, (ch+1) / 2, MC_Dark);
401 		}
402 	  }
403 
404     Frame_menu_color(Back_color);
405     Frame_menu_color(Fore_color);
406     Update_rect(MENU_WIDTH*Menu_factor_X,Menu_Y,Screen_width-(MENU_WIDTH*Menu_factor_X),(Menu_height-11)*Menu_factor_Y);
407   }
408 }
409 
410   // -- Recalculer l'origine de la palette dans le menu pour rendre la
411   //    Fore_color visible --
412 
Reposition_palette(void)413 void Reposition_palette(void)
414 {
415   byte old_color=First_color_in_palette;
416   short cells;
417   if (Config.Palette_vertical)
418     cells=Menu_cells_X;
419   else
420     cells=Menu_cells_Y;
421 
422 
423   if (Fore_color<First_color_in_palette)
424   {
425     while (Fore_color<First_color_in_palette)
426       First_color_in_palette-=cells;
427   }
428   else
429   {
430     while (Fore_color>=First_color_in_palette+Menu_cells_X*Menu_cells_Y)
431       First_color_in_palette+=cells;
432   }
433   if (old_color!=First_color_in_palette)
434     Display_menu_palette();
435 }
436 
Change_palette_cells()437 void Change_palette_cells()
438 {
439   // On initialise avec la configuration de l'utilisateur
440   Menu_cells_X=Config.Palette_cells_X;
441   Menu_cells_Y=Config.Palette_cells_Y;
442   // Mais on sait jamais
443   if (Menu_cells_X<1)
444     Menu_cells_X=1;
445   if (Menu_cells_Y<1)
446     Menu_cells_Y=1;
447 
448   while (1)
449   {
450     Menu_palette_cell_width = ((Screen_width/Menu_factor_X)-(MENU_WIDTH+2)) / Menu_cells_X;
451 
452     // Si ça tient, c'est bon. Sinon, on retente avec une colonne de moins
453     if (Menu_palette_cell_width>2)
454       break;
455     Menu_cells_X--;
456   }
457 
458   // Cale First_color_in_palette sur un multiple du nombre de cellules (arrondi inférieur)
459   if (Config.Palette_vertical)
460     First_color_in_palette=First_color_in_palette/Menu_cells_X*Menu_cells_X;
461   else
462     First_color_in_palette=First_color_in_palette/Menu_cells_Y*Menu_cells_Y;
463 
464   // Si le nombre de cellules a beaucoup augmenté et qu'on était près de
465   // la fin, il faut reculer First_color_in_palette pour montrer plein
466   // de couleurs.
467   if ((int)First_color_in_palette+(Menu_cells_Y)*Menu_cells_X*2>=256)
468   {
469     if (Config.Palette_vertical)
470       First_color_in_palette=255/Menu_cells_X*Menu_cells_X-(Menu_cells_Y-1)*Menu_cells_X;
471     else
472       First_color_in_palette=255/Menu_cells_Y*Menu_cells_Y-(Menu_cells_X-1)*Menu_cells_Y;
473   }
474 
475   // Mise à jour de la taille du bouton dans le menu. C'est pour pas que
476   // la bordure noire soit active.
477   Buttons_Pool[BUTTON_CHOOSE_COL].Width=(Menu_palette_cell_width*Menu_cells_X)-1;
478   Buttons_Pool[BUTTON_CHOOSE_COL].Height=(MENU_HEIGHT-9)/Menu_cells_Y*Menu_cells_Y-1;
479 }
480 
481 // Retrouve la couleur sur laquelle pointe le curseur souris.
482 // Cette fonction suppose qu'on a déja vérifié que le curseur est dans
483 // la zone rectangulaire du BUTTON_CHOOSE_COL
484 // La fonction renvoie -1 si on est "trop à gauche" (pas possible)
485 // ou après la couleur 255 (Ce qui peut arriver si la palette est affichée
486 // avec un nombre de lignes qui n'est pas une puissance de deux.)
Pick_color_in_palette()487 int Pick_color_in_palette()
488 {
489   int color;
490   int line;
491   int column;
492 
493   line=(((Mouse_Y-Menu_Y)/Menu_factor_Y)-1)/((Menu_bars[MENUBAR_TOOLS].Height)/Menu_cells_Y);
494   column=(((Mouse_X/Menu_factor_X)-(MENU_WIDTH+1))/Menu_palette_cell_width);
495   if (Config.Palette_vertical)
496   {
497     color=First_color_in_palette+line*Menu_cells_X+column;
498   }
499   else
500   {
501     color=First_color_in_palette+line+column*Menu_cells_Y;
502   }
503   if (color<0 || color>255)
504     return -1;
505   return color;
506 }
507 
508 /// Draws a solid textured area, to the right of a toolbar.
Draw_bar_remainder(word current_menu,word x_off)509 void Draw_bar_remainder(word current_menu, word x_off)
510 {
511   word y_pos;
512   word x_pos;
513 
514   for (y_pos=0;y_pos<Menu_bars[current_menu].Height;y_pos++)
515     for (x_pos=x_off;x_pos<Screen_width/Menu_factor_X;x_pos++)
516       Pixel_in_menu(current_menu, x_pos, y_pos, Menu_bars[current_menu].Skin[0][y_pos * Menu_bars[current_menu].Skin_width + Menu_bars[current_menu].Skin_width - 2 + (x_pos&1)]);
517 }
518 
519 
520 /// Display / update the layer menubar
Display_layerbar(void)521 void Display_layerbar(void)
522 {
523 
524   if (Menu_bars[MENUBAR_LAYERS].Visible)
525   {
526     word x_off=0;
527     word button_width = LAYER_SPRITE_WIDTH;
528     word button_number = Main.backups->Pages->Nb_layers;
529     word horiz_space;
530     word current_button;
531     word repeats=1;
532 
533     // Available space in pixels
534     horiz_space = Screen_width / Menu_factor_X - Menu_bars[MENUBAR_LAYERS].Skin_width;
535 
536     // Don't display all buttons if not enough room
537     if (horiz_space/button_width < button_number)
538       button_number = horiz_space/button_width;
539     // Only 16 icons at the moment
540     if (button_number > 16) // can be different from MAX_NB_LAYERS
541       button_number = 16;
542 
543     // Enlarge the buttons themselves if there's enough room
544     while (button_number*(button_width+2) < horiz_space && repeats < 20)
545     {
546       repeats+=1;
547       button_width+=2;
548     }
549 
550     x_off=Menu_bars[MENUBAR_LAYERS].Skin_width;
551     for (current_button=0; current_button<button_number; current_button++)
552     {
553       word x_pos=0;
554       word y_pos;
555       word sprite_index;
556 
557       if (Main.current_layer == current_button)
558         sprite_index=1;
559       else if (Main.layers_visible & (1 << current_button))
560         sprite_index=0;
561       else
562         sprite_index=2;
563 
564 
565       for (y_pos=0;y_pos<LAYER_SPRITE_HEIGHT;y_pos++)
566       {
567         word source_x=0;
568 
569         for (source_x=0;source_x<LAYER_SPRITE_WIDTH;source_x++)
570         {
571           short i = 1;
572 
573           // This stretches a button, by duplicating the 2nd from right column
574           // and 3rd column from left.
575           if (source_x == 1 || (source_x == LAYER_SPRITE_WIDTH-3))
576             i=repeats;
577 
578           for (;i>0; i--)
579           {
580             Pixel_in_menu(MENUBAR_LAYERS, x_pos + x_off, y_pos, Gfx->Layer_sprite[sprite_index][current_button][y_pos][source_x]);
581             x_pos++;
582           }
583         }
584         // Next line
585         x_pos=0;
586       }
587       // Next button
588       x_off+=button_width;
589     }
590     // Texture any remaining space to the right.
591     // This overwrites any junk like deleted buttons.
592     Draw_bar_remainder(MENUBAR_LAYERS, x_off);
593 
594     // Update the active area of the layers pseudo-button
595     Buttons_Pool[BUTTON_LAYER_SELECT].Width = button_number * button_width;
596 
597     // Required to determine which layer button is clicked
598     Layer_button_width = button_width;
599 
600     // A screen refresh required by some callers
601     Update_rect(
602       Menu_bars[MENUBAR_LAYERS].Skin_width,
603       Menu_Y+Menu_bars[MENUBAR_LAYERS].Top*Menu_factor_Y,
604       horiz_space*Menu_factor_X,
605       Menu_bars[MENUBAR_LAYERS].Height*Menu_factor_Y);
606   }
607   if (Menu_bars[MENUBAR_ANIMATION].Visible)
608   {
609     char str[24];
610     // Rest of horizontal line
611     Draw_bar_remainder(MENUBAR_ANIMATION, Menu_bars[MENUBAR_ANIMATION].Skin_width);
612     // Frame# background rectangle
613     // Block((Menu_bars[MENUBAR_ANIMATION].Skin_width)*Menu_factor_X,(0+Menu_bars[MENUBAR_ANIMATION].Top)*Menu_factor_Y+Menu_Y,8*8*Menu_factor_X,8*Menu_factor_Y,MC_Light);
614     // Frame #/#
615     snprintf(str, sizeof(str), "%3d/%3d", Main.current_layer+1, Main.backups->Pages->Nb_layers);
616     Print_general((59)*Menu_factor_X,(Menu_bars[MENUBAR_ANIMATION].Top+3)*Menu_factor_Y+Menu_Y,str,MC_Black,MC_Light);
617     Update_rect(
618       (59)*Menu_factor_X,
619       (Menu_bars[MENUBAR_ANIMATION].Top+3)*Menu_factor_Y+Menu_Y,
620       7*8*Menu_factor_X,
621       8*Menu_factor_Y);
622   }
623 }
624 
625 
626 /// Display the whole menu
Display_menu(void)627 void Display_menu(void)
628 {
629   word x_pos;
630   word y_pos;
631   int8_t current_menu;
632   char str[4];
633 
634 
635   if (Menu_is_visible)
636   {
637     // display menu sprite
638     for (current_menu = MENUBAR_COUNT - 1; current_menu >= 0; current_menu --)
639     {
640       if(Menu_bars[current_menu].Visible)
641       {
642         // Skinned area
643         for (y_pos=0;y_pos<Menu_bars[current_menu].Height;y_pos++)
644           for (x_pos=0;x_pos<Menu_bars[current_menu].Skin_width;x_pos++)
645             Pixel_in_menu(current_menu, x_pos, y_pos, Menu_bars[current_menu].Skin[2][y_pos * Menu_bars[current_menu].Skin_width + x_pos]);
646 
647         if (current_menu == MENUBAR_LAYERS || current_menu == MENUBAR_ANIMATION)
648         {
649           // The layerbar has its own display, for the whole length.
650           Display_layerbar();
651         }
652         else
653         {
654           // If some area is remaining to the right, texture it with a copy of
655           // the last two columns
656           Draw_bar_remainder(current_menu, Menu_bars[current_menu].Skin_width);
657         }
658 
659         // Next bar
660       }
661     }
662 
663     // Display palette
664     Display_menu_palette();
665 
666     // Display selected colors
667     Display_foreback();
668 
669 
670     if (!Windows_open)
671     {
672       if ((Mouse_Y<Menu_Y) && // Mouse in the picture area
673           ( (!Main.magnifier_mode) || (Mouse_X<Main.separator_position) || (Mouse_X>=Main.X_zoom) ))
674       {
675         // Prepare display of XY coordinates even if in some cases they will be
676         // erased with some other text
677         if ( (Current_operation!=OPERATION_COLORPICK)
678           && (Current_operation!=OPERATION_REPLACE) )
679           Print_in_menu("X:       Y:             ",0);
680         else
681         {
682           // The colorpicker display the color id between the parentheses
683           Print_in_menu("X:       Y:       (    )",0);
684           Num2str(Colorpicker_color,str,3);
685           Print_in_menu(str,20);
686           Print_general(170*Menu_factor_X,Menu_status_Y," ",0,Colorpicker_color);
687         }
688         Print_coordinates();
689       }
690       Print_filename();
691     }
692     // Now update the area: menu height and whole screen width (including palette)
693     Update_rect(0,Menu_Y,Screen_width,Menu_height*Menu_factor_Y);
694   }
695 }
696 
697 // -- Affichage de texte -----------------------------------------------------
698 
699 
Get_font_character_pixel(unsigned int c)700 static const byte * Get_font_character_pixel(unsigned int c)
701 {
702   // convert some known Unicode chars we have in the Grafx2 base font
703   switch (c)
704   {
705     case 0x20ac:
706       c = 0x80; // ;Euro Sign
707       break;
708     case 0x201a:
709       c = 0x82; // Single Low-9 Quotation Mark
710       break;
711     case 0x0192:
712       c = 0x83; // Latin Small Letter F With Hook
713       break;
714     case 0x201e:
715       c = 0x84; // Double Low-9 Quotation Mark
716       break;
717     case 0x2026:
718       c = 0x85; // Horizontal Ellipsis
719       break;
720     case 0x2020:
721       c = 0x86; // Dagger
722       break;
723     case 0x2021:
724       c = 0x87; // Double Dagger
725       break;
726     case 0x02c6:
727       c = 0x88; // Modifier Letter Circumflex Accent
728       break;
729     case 0x2030:
730       c = 0x89; // Per Mille Sign
731       break;
732     case 0x0160:
733       c = 0x8a; // Latin Capital Letter S With Caron
734       break;
735     case 0x2039:
736       c = 0x8b; // Single Left-Pointing Angle Quotation Mark
737       break;
738     case 0x0152:
739       c = 0x8c; // Latin Capital Ligature Oe
740       break;
741     case 0x017d:
742       c = 0x8e; // Latin Capital Letter Z With Caron
743       break;
744     case 0x2018:
745       c = 0x91; // Left Single Quotation Mark
746       break;
747     case 0x2019:
748       c = 0x92; // Right Single Quotation Mark
749       break;
750     case 0x201c:
751       c = 0x93; // Left Double Quotation Mark
752       break;
753     case 0x201d:
754       c = 0x94; // Right Double Quotation Mark
755       break;
756     case 0x2022:
757       c = 0x95; // Bullet
758       break;
759     case 0x2013:
760       c = 0x96; // En Dash
761       break;
762     case 0x2014:
763       c = 0x97; // Em Dash
764       break;
765     case 0x02dc:
766       c = 0x98; // Small Tilde
767       break;
768     case 0x2122:
769       c = 0x99; // Trade Mark Sign
770       break;
771     case 0x0161:
772       c = 0x9a; // Latin Small Letter S With Caron
773       break;
774     case 0x203a:
775       c = 0x9b; // Single Right-Pointing Angle Quotation Mark
776       break;
777     case 0x0153:
778       c = 0x9c; // Latin Small Ligature Oe
779       break;
780     case 0x017e:
781       c = 0x9e; // Latin Small Letter Z With Caron
782       break;
783     case 0x0178:
784       c = 0x9f; // Latin Capital Letter Y With Diaeresis
785       break;
786     case 0x2190:
787       c = 0x1b; // left arrow
788       break;
789     case 0x2191:
790       c = 0x18; // up arrow
791       break;
792     case 0x2192:
793       c = 0x1a; // right arrow
794       break;
795     case 0x2193:
796       c = 0x19; // down arrow
797       break;
798     case 0x2194:
799       c = 0x1d; // right/left arrow
800       break;
801     case 0x2195:
802       c = 0x12; // up/down arrow
803       break;
804     case 0x21A8:
805       c = 0x17; // up/down arrow with base
806       break;
807     case 0x221F:
808       c = 0x1c; // right angle
809       break;
810     case 0x2302:
811       c = 0x7f; // House
812       break;
813     case 0x25ac:
814       c = 0x16; // Black rectangle
815       break;
816     case 0x25b2:
817       c = 0x1e; // up triangle
818       break;
819     case 0x25ba:
820       c = 0x10; // right rectangle
821       break;
822     case 0x25bc:
823       c = 0x1f; // down rectangle
824       break;
825     case 0x25c4:
826       c = 0x11; // left rectange
827       break;
828     case 0x25cb:
829       c = 0x09; // circle
830       break;
831     case 0x25d8:
832       c = 0x08; // inverse bullet
833       break;
834     case 0x263a:
835       c = 0x01; // smile
836       break;
837     case 0x263b:
838       c = 0x02; // smile !
839       break;
840     case 0x263c:
841       c = 0x0f; // Sun
842       break;
843     case 0x2640:
844       c = 0x0c; // female
845       break;
846     case 0x2642:
847       c = 0x0b; // male
848       break;
849     case 0x2660:
850       c = 0x06; // spade
851       break;
852     case 0x2663:
853       c = 0x05; // club
854       break;
855     case 0x2665:
856       c = 0x03; // heart
857       break;
858     case 0x2666:
859       c = 0x04; // diamond
860       break;
861     case 0x266a:
862       c = 0x0d; // eighth note
863       break;
864     case 0x266b:
865       c = 0x0e; // beamed eighth notes
866       break;
867   }
868   if (c < 256)
869     return Menu_font+(c<<6);
870   else
871   {
872     T_Unicode_Font * ufont;
873     const byte * font_pixel = Menu_font + (1<<6); // dummy character
874     for (ufont = Unicode_fonts; ufont != NULL; ufont = ufont->Next)
875       if (ufont->FirstChar <= c && c <= ufont->LastChar)
876       {
877         font_pixel = ufont->FontData + ((c - ufont->FirstChar) << 6);
878         break;
879       }
880     return font_pixel;
881   }
882 }
883 
884   // -- Afficher une chaîne n'importe où à l'écran --
885 
Print_general(short x,short y,const char * str,byte text_color,byte background_color)886 void Print_general(short x,short y,const char * str,byte text_color,byte background_color)
887 {
888   word  index;
889   int x_pos;
890   int y_pos;
891   byte *font_pixel;
892   short real_x;
893   short real_y;
894   byte repeat_menu_x_factor;
895   byte repeat_menu_y_factor;
896 
897   real_y=y;
898   for (y_pos=0;y_pos<8<<3;y_pos+=1<<3)
899   {
900     real_x=0; // Position dans le buffer
901     for (index=0;str[index]!='\0';index++)
902     {
903       // Pointeur sur le premier pixel du caractère
904       font_pixel=Menu_font+((unsigned char)str[index]<<6);
905       for (x_pos=0;x_pos<8;x_pos+=1)
906         for (repeat_menu_x_factor=0;repeat_menu_x_factor<Menu_factor_X*Pixel_width;repeat_menu_x_factor++)
907           Horizontal_line_buffer[real_x++]=*(font_pixel+x_pos+y_pos)?text_color:background_color;
908     }
909     for (repeat_menu_y_factor=0;repeat_menu_y_factor<Menu_factor_Y;repeat_menu_y_factor++)
910       Display_line_fast(x,real_y++,index*Menu_factor_X*8,Horizontal_line_buffer);
911   }
912 }
913 
Print_general_unicode(short x,short y,const word * str,byte text_color,byte background_color)914 void Print_general_unicode(short x,short y,const word * str,byte text_color,byte background_color)
915 {
916   word  index;
917   int x_pos;
918   int y_pos;
919   const byte *font_pixel;
920   short real_x;
921   short real_y;
922   byte repeat_menu_x_factor;
923   byte repeat_menu_y_factor;
924 
925   real_y=y;
926   for (y_pos=0;y_pos<8<<3;y_pos+=1<<3)
927   {
928     real_x=0; // Position dans le buffer
929     for (index=0;str[index]!=0;index++)
930     {
931       font_pixel = Get_font_character_pixel(str[index]);
932       for (x_pos=0;x_pos<8;x_pos+=1)
933         for (repeat_menu_x_factor=0;repeat_menu_x_factor<Menu_factor_X*Pixel_width;repeat_menu_x_factor++)
934           Horizontal_line_buffer[real_x++]=*(font_pixel+x_pos+y_pos)?text_color:background_color;
935     }
936     for (repeat_menu_y_factor=0;repeat_menu_y_factor<Menu_factor_Y;repeat_menu_y_factor++)
937       Display_line_fast(x,real_y++,index*Menu_factor_X*8,Horizontal_line_buffer);
938   }
939 }
940 
941 /// Draws a char in a window
Print_char_in_window(short x_pos,short y_pos,unsigned int c,byte text_color,byte background_color)942 void Print_char_in_window(short x_pos, short y_pos, unsigned int c,byte text_color,byte background_color)
943 {
944   short x,y;
945   const byte *pixel;
946   // First pixel of the character
947   pixel = Get_font_character_pixel(c);
948 
949   for (y=0;y<8;y++)
950     for (x=0;x<8;x++)
951       Pixel_in_window(x_pos+x, y_pos+y,
952             (*(pixel++)?text_color:background_color));
953 }
954 
955 ///Draws a char in a window, checking for bounds
Print_in_window_limited(short x,short y,const char * str,byte size,byte text_color,byte background_color)956 void Print_in_window_limited(short x,short y,const char * str,byte size,byte text_color,byte background_color)
957 {
958   if (strlen(str) > size)
959   {
960     char * display_string = strdup(str);
961     display_string[size-1] = ELLIPSIS_CHARACTER;
962     display_string[size] = '\0';
963     Print_in_window(x, y, display_string, text_color, background_color);
964     free(display_string);
965   }
966   else
967     Print_in_window(x, y, str, text_color, background_color);
968 }
969 
970 ///Draws a char in a window, checking for bounds
Print_in_window_limited_unicode(short x,short y,const word * str,byte size,byte text_color,byte background_color)971 void Print_in_window_limited_unicode(short x, short y, const word * str, byte size, byte text_color, byte background_color)
972 {
973   if (Unicode_strlen(str) > size)
974   {
975     word * display_string = Unicode_strdup(str);
976     display_string[size-1] = (byte)ELLIPSIS_CHARACTER;
977     display_string[size] = 0;
978     Print_in_window_unicode(x, y, display_string, text_color, background_color);
979     free(display_string);
980   }
981   else
982     Print_in_window_unicode(x, y, str, text_color, background_color);
983 }
984 
985 /// Draws a string in a window with underscore
986 /// undersc_letter is 0 for no underscore, 1-indexed array index otherwise
Print_in_window_underscore(short x,short y,const char * str,byte text_color,byte background_color,byte undersc_letter)987 void Print_in_window_underscore(short x,short y,const char * str,byte text_color,byte background_color, byte undersc_letter)
988 {
989   short x_pos = x;
990   const unsigned char * p = (const unsigned char *)str;
991 
992   while (*p !='\0')
993   {
994     Print_char_in_window(x,y,*p++,text_color,background_color);
995     x+=8;
996   }
997 #if !defined(NO_KEYBOARD)
998   if (undersc_letter)
999     Window_rectangle(x_pos+((undersc_letter-1)<<3),y+8,8,1,text_color);
1000 #else
1001   (void)undersc_letter;
1002 #endif
1003   Update_window_area(x_pos,y,x-x_pos,8);
1004 }
1005 
1006 /// Draws a string in a window
Print_in_window(short x,short y,const char * str,byte text_color,byte background_color)1007 void Print_in_window(short x,short y,const char * str,byte text_color,byte background_color)
1008 {
1009   Print_in_window_underscore(x,y,str,text_color,background_color,0);
1010 }
1011 
1012 /// Draws a string in a window
Print_in_window_unicode(short x,short y,const word * str,byte text_color,byte background_color)1013 void Print_in_window_unicode(short x,short y,const word * str,byte text_color,byte background_color)
1014 {
1015   short x_pos = x;
1016   const word * p = str;
1017 
1018   while (*p != 0)
1019   {
1020     Print_char_in_window(x,y,*p++,text_color,background_color);
1021     x+=8;
1022   }
1023   Update_window_area(x_pos,y,x-x_pos,8);
1024 }
1025 
1026 
1027 // Draws a string in the menu's status bar
Print_in_menu(const char * str,short position)1028 void Print_in_menu(const char * str, short position)
1029 {
1030   Print_general((18+(position<<3))*Menu_factor_X,Menu_status_Y,str,MC_Black,MC_Light);
1031   Update_status_line(position, strlen(str));
1032 }
1033 
1034 /// Draws the mouse coordinates on the menu
1035 /// Only update the digits and doesn't refresh the "X: Y:" labels. This function needs to be fast as it is called each time the mouse moves.
Print_coordinates(void)1036 void Print_coordinates(void)
1037 {
1038   char temp[5];
1039 
1040   if (Menu_is_visible && !Cursor_in_menu)
1041   {
1042     if ( (Current_operation==OPERATION_COLORPICK)
1043       || (Current_operation==OPERATION_RMB_COLORPICK)
1044       || (Current_operation==OPERATION_REPLACE) )
1045     {
1046       if ( (Paintbrush_X>=0) && (Paintbrush_Y>=0)
1047         && (Paintbrush_X<Main.image_width)
1048         && (Paintbrush_Y<Main.image_height) )
1049         Colorpicker_color=Read_pixel_from_current_screen(Paintbrush_X,Paintbrush_Y);
1050       else
1051         Colorpicker_color=0;
1052       Colorpicker_X=Paintbrush_X;
1053       Colorpicker_Y=Paintbrush_Y;
1054 
1055       Num2str(Colorpicker_color,temp,3);
1056       Print_in_menu(temp,20);
1057       Print_general(170*Menu_factor_X,Menu_status_Y," ",0,Colorpicker_color);
1058     }
1059     else if (Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5
1060             || Main.backups->Pages->Image_mode == IMAGE_MODE_RASTER)
1061     {
1062       int ink;
1063       temp[1] = '\0';
1064       if ( (Paintbrush_X>=0) && (Paintbrush_Y>=0)
1065         && (Paintbrush_X<Main.image_width)
1066         && (Paintbrush_Y<Main.image_height) )
1067       {
1068         for (ink = 0; ink < 4; ink++)
1069         {
1070           byte color = Main.backups->Pages->Image[ink].Pixels[Paintbrush_X+Paintbrush_Y*Main.image_width];
1071           temp[0] = '0' + ink;
1072           Print_general((170+ink*8)*Menu_factor_X, Menu_status_Y,temp,MC_Dark,color);
1073         }
1074         Update_status_line(19, 4);
1075       }
1076     }
1077 
1078     Num2str(Paintbrush_X,temp,4);
1079     Print_in_menu(temp,2);
1080     Num2str(Paintbrush_Y,temp,4);
1081     Print_in_menu(temp,11);
1082   }
1083 }
1084 
1085   // -- Afficher le nom du fichier dans le menu --
1086 
Print_filename(void)1087 void Print_filename(void)
1088 {
1089   word display_string[256];
1090   word max_size;
1091   word string_size;
1092 
1093   // Determine maximum size, in characters
1094   max_size = 12 + (Screen_width / Menu_factor_X - 320) / 8;
1095 
1096   // Erase whole area
1097   Block(Screen_width-max_size*8*Menu_factor_X,
1098     Menu_status_Y,Menu_factor_X*max_size*8,Menu_factor_Y<<3,MC_Light);
1099 
1100   // Partial copy of the name
1101   if (Main.backups->Pages->Filename_unicode != NULL)
1102     Unicode_strlcpy(display_string, Main.backups->Pages->Filename_unicode, 256);
1103   else
1104   {
1105 #ifdef ENABLE_FILENAMES_ICONV
1106     char * input = Main.backups->Pages->Filename;
1107     size_t inbytesleft = strlen(input);
1108     char * output = (char *)display_string;
1109     size_t outbytesleft = sizeof(display_string)-2;
1110     if(cd_utf16 != (iconv_t)-1 && (ssize_t)iconv(cd_utf16, &input, &inbytesleft, &output, &outbytesleft) >= 0)
1111       output[1] = output[0] = '\0';
1112     else
1113 #endif /* ENABLE_FILENAMES_ICONV */
1114       Unicode_char_strlcpy(display_string, Main.backups->Pages->Filename, 256);
1115   }
1116 
1117   string_size = Unicode_strlen(display_string);
1118 
1119   if (string_size > max_size)
1120   {
1121     // check if the begining of the Spare file name is the same
1122     if (Spare.backups->Pages->Filename_unicode != NULL
1123         && 0 == memcmp(display_string, Spare.backups->Pages->Filename_unicode, (max_size - 1) * sizeof(word)))
1124     {
1125       // display : "...end_of_filename.ext"
1126       display_string[0] = (byte)ELLIPSIS_CHARACTER;
1127       memmove(display_string + 1,
1128               display_string + string_size - max_size + 1,
1129               (max_size - 1) * sizeof(word));
1130       string_size = max_size;
1131     }
1132     else
1133     {
1134       // display : "begin_of_filename..."
1135       string_size = max_size;
1136       display_string[string_size-1] = (byte)ELLIPSIS_CHARACTER;
1137     }
1138     display_string[string_size] = 0;
1139   }
1140   // Print
1141   if (string_size > 0)
1142     Print_general_unicode(Screen_width-(string_size<<3)*Menu_factor_X,Menu_status_Y,display_string,MC_Black,MC_Light);
1143 }
1144 
1145 // Fonction d'affichage d'une chaine numérique avec une fonte très fine
1146 // Spécialisée pour les compteurs RGB
Print_counter(short x,short y,const char * str,byte text_color,byte background_color)1147 void Print_counter(short x,short y,const char * str,byte text_color,byte background_color)
1148 {
1149   // Macros pour écrire des litteraux binaires.
1150   // Ex: Ob(11110000) == 0xF0
1151   #define Ob(x)  ((unsigned)Ob_(0 ## x ## uL))
1152   #define Ob_(x) ((x & 1) | (x >> 2 & 2) | (x >> 4 & 4) | (x >> 6 & 8) |                \
1153           (x >> 8 & 16) | (x >> 10 & 32) | (x >> 12 & 64) | (x >> 14 & 128))
1154 
1155   byte thin_font[14][8] = {
1156    { // 0
1157     Ob(00011100),
1158     Ob(00110110),
1159     Ob(00110110),
1160     Ob(00110110),
1161     Ob(00110110),
1162     Ob(00110110),
1163     Ob(00110110),
1164     Ob(00011100)
1165    },
1166    { // 1
1167     Ob(00001100),
1168     Ob(00011100),
1169     Ob(00111100),
1170     Ob(00001100),
1171     Ob(00001100),
1172     Ob(00001100),
1173     Ob(00001100),
1174     Ob(00001100)
1175    },
1176    { // 2
1177     Ob(00011100),
1178     Ob(00110110),
1179     Ob(00000110),
1180     Ob(00000110),
1181     Ob(00000110),
1182     Ob(00001100),
1183     Ob(00011000),
1184     Ob(00111110)
1185    },
1186    { // 3
1187     Ob(00011100),
1188     Ob(00110110),
1189     Ob(00000110),
1190     Ob(00001100),
1191     Ob(00000110),
1192     Ob(00000110),
1193     Ob(00110110),
1194     Ob(00011100)
1195    },
1196    { // 4
1197     Ob(00001100),
1198     Ob(00001100),
1199     Ob(00011000),
1200     Ob(00011000),
1201     Ob(00110000),
1202     Ob(00110100),
1203     Ob(00111110),
1204     Ob(00000100)
1205    },
1206    { // 5
1207     Ob(00111110),
1208     Ob(00110000),
1209     Ob(00110000),
1210     Ob(00111100),
1211     Ob(00000110),
1212     Ob(00000110),
1213     Ob(00110110),
1214     Ob(00011100)
1215    },
1216    { // 6
1217     Ob(00011100),
1218     Ob(00110110),
1219     Ob(00110000),
1220     Ob(00111100),
1221     Ob(00110110),
1222     Ob(00110110),
1223     Ob(00110110),
1224     Ob(00011100)
1225    },
1226    { // 7
1227     Ob(00111110),
1228     Ob(00000110),
1229     Ob(00000110),
1230     Ob(00001100),
1231     Ob(00011000),
1232     Ob(00011000),
1233     Ob(00011000),
1234     Ob(00011000)
1235    },
1236    { // 8
1237     Ob(00011100),
1238     Ob(00110110),
1239     Ob(00110110),
1240     Ob(00011100),
1241     Ob(00110110),
1242     Ob(00110110),
1243     Ob(00110110),
1244     Ob(00011100)
1245    },
1246    { // 9
1247     Ob(00011100),
1248     Ob(00110110),
1249     Ob(00110110),
1250     Ob(00011110),
1251     Ob(00000110),
1252     Ob(00000110),
1253     Ob(00110110),
1254     Ob(00011100)
1255    },
1256    { // (espace)
1257     Ob(00000000),
1258     Ob(00000000),
1259     Ob(00000000),
1260     Ob(00000000),
1261     Ob(00000000),
1262     Ob(00000000),
1263     Ob(00000000),
1264     Ob(00000000)
1265    },
1266    { // +
1267     Ob(00000000),
1268     Ob(00001000),
1269     Ob(00001000),
1270     Ob(00111110),
1271     Ob(00001000),
1272     Ob(00001000),
1273     Ob(00000000),
1274     Ob(00000000)
1275    },
1276    { // -
1277     Ob(00000000),
1278     Ob(00000000),
1279     Ob(00000000),
1280     Ob(00111110),
1281     Ob(00000000),
1282     Ob(00000000),
1283     Ob(00000000),
1284     Ob(00000000)
1285    },
1286    { // +-
1287     Ob(00001000),
1288     Ob(00001000),
1289     Ob(00111110),
1290     Ob(00001000),
1291     Ob(00001000),
1292     Ob(00000000),
1293     Ob(00111110),
1294     Ob(00000000)
1295    } };
1296 
1297   word  index;
1298   short x_pos;
1299   short y_pos;
1300   for (index=0;str[index]!='\0';index++)
1301   {
1302     int char_number;
1303     switch(str[index])
1304     {
1305       case '0':
1306       case '1':
1307       case '2':
1308       case '3':
1309       case '4':
1310       case '5':
1311       case '6':
1312       case '7':
1313       case '8':
1314       case '9':
1315         char_number=str[index]-'0';
1316         break;
1317       case ' ':
1318       default:
1319         char_number=10;
1320         break;
1321       case '+':
1322         char_number=11;
1323         break;
1324       case '-':
1325         char_number=12;
1326         break;
1327       case '\xb1':
1328         char_number=13;
1329         break;
1330     }
1331     for (y_pos=0;y_pos<8;y_pos++)
1332     {
1333       for (x_pos=0;x_pos<6;x_pos++)
1334       {
1335         byte color = (thin_font[char_number][y_pos] & (1 << (6-x_pos))) ? text_color:background_color;
1336         Pixel_in_window(x+(index*6+x_pos),y+y_pos,color);
1337       }
1338     }
1339   }
1340   Update_window_area(x,y,strlen(str)*6,8);
1341 }
1342 
1343 
1344 
1345 ///
1346 /// Window asking for confirmation before an action is performed.
1347 /// This function is able to display multi-line messages and
1348 /// center the lines, but the carriage returns have to be explicit.
1349 /// The function will clip the message in case of problem.
1350 /// @return 1 if user pressed OK, 0 if CANCEL
Confirmation_box(const char * message)1351 byte Confirmation_box(const char * message)
1352 {
1353   short clicked_button;
1354   word  window_width = 120;
1355   word  nb_lines = 1;
1356   const char *c = message;
1357   short current_length=0;
1358   short current_line;
1359 
1360   // Count lines, and measure max line length
1361   for (c=message; *c != '\0'; c++)
1362   {
1363     if (*c == '\n')
1364     {
1365       current_length=0;
1366       nb_lines++;
1367     }
1368     else
1369     {
1370       current_length++;
1371       window_width=Max(window_width, (current_length<<3)+20);
1372     }
1373   }
1374   // Safety
1375   if (window_width>310)
1376     window_width=310;
1377 
1378   Open_window(window_width,52+(nb_lines<<3),"Confirmation");
1379 
1380   c=message;
1381   for (current_line=0; current_line < nb_lines; current_line++)
1382   {
1383     char * next_eol;
1384     char display_string[36+1];
1385 
1386     next_eol = strchr(c, '\n');
1387     if (next_eol==NULL) // last line
1388       current_length = strlen(c);
1389     else
1390       current_length = next_eol-c;
1391 
1392     // Safeguard
1393     if (current_length>36)
1394       current_length=36;
1395     // Copy part of string in null-terminated buffer
1396     strncpy(display_string, c, current_length);
1397     display_string[current_length]='\0';
1398 
1399     Print_in_window((window_width>>1)-(current_length<<2), 20+(current_line<<3), display_string, MC_Black, MC_Light);
1400 
1401     c += current_length;
1402     if (*c == '\n')
1403       c++;
1404   }
1405 
1406   Window_set_normal_button((window_width/3)-20     ,29+(nb_lines<<3),40,14,"Yes",1,1,KEY_y); // 1
1407   Window_set_normal_button(((window_width<<1)/3)-20,29+(nb_lines<<3),40,14,"No" ,1,1,KEY_n); // 2
1408 
1409   Update_window_area(0, 0, Window_width, Window_height);
1410 
1411   Display_cursor();
1412 
1413   do
1414   {
1415     clicked_button=Window_clicked_button();
1416     if (Key==KEY_RETURN) clicked_button=1;
1417     if (Key==KEY_ESC) clicked_button=2;
1418   }
1419   while (clicked_button<=0);
1420   Key=0;
1421 
1422   Close_window();
1423   Display_cursor();
1424 
1425   return (clicked_button==1)? 1 : 0;
1426 }
1427 
1428 
1429 /// Window that allows you to enter a single value
Requester_window(const char * message,int initial_value)1430 int Requester_window(const char* message, int initial_value)
1431 {
1432   short clicked_button = 0;
1433   word window_width;
1434   char str[10];
1435 
1436   window_width=(strlen(message)<<3)+20;
1437 
1438   if (window_width<120)
1439     window_width = 120;
1440 
1441   Open_window(window_width, 60, "Request");
1442 
1443   Print_in_window((window_width>>1)-(strlen(message)<<2), 20, message,
1444     MC_Black, MC_Light);
1445   sprintf(str, "%d", initial_value);
1446   Window_set_input_button(10, 37, 4); // 1
1447   Print_in_window(11, 39, str, MC_Black, MC_Light);
1448   Window_set_normal_button(60 ,37,40,14,"OK",1,1,KEY_y); // 2
1449   Window_set_normal_button(130,37,60,14,"Cancel" ,1,1,KEY_n); // 3
1450 
1451   Update_window_area(0, 0, window_width, 60);
1452   Display_cursor();
1453 
1454   do
1455   {
1456     clicked_button = Window_clicked_button();
1457     if (clicked_button == 1)
1458       Readline(11, 39, str, 4, INPUT_TYPE_INTEGER);
1459     if (Key == KEY_ESCAPE) clicked_button = 2;
1460   }
1461   while (clicked_button <= 0);
1462 
1463   Key = 0;
1464 
1465   Close_window();
1466   Display_cursor();
1467 
1468   return clicked_button==2?-1:atoi(str);
1469 }
1470 
1471 /// Ask the user to choose between multiple choices
Dialog_multiple_choice(const char * title,const T_MultipleChoice * choices,int default_choice)1472 int Dialog_multiple_choice(const char * title, const T_MultipleChoice * choices, int default_choice)
1473 {
1474   int i;
1475   int choice;
1476   word window_width = 130, window_height = 70;
1477   unsigned int max_len_label = 0;
1478   unsigned int max_len_hint = 0;
1479   short clicked_button;
1480   T_Dropdown_button* dropdown;
1481   char hint[38];
1482   const char * default_hint = NULL;
1483   const char * default_label = "please choose";
1484 
1485   choice = default_choice;
1486   for (i = 0; choices[i].label != NULL; i++)
1487   {
1488     if (strlen(choices[i].label) > max_len_label)
1489       max_len_label = strlen(choices[i].label);
1490     if (choices[i].hint != NULL && strlen(choices[i].hint) > max_len_hint)
1491       max_len_hint = strlen(choices[i].hint);
1492     if (default_choice == choices[i].value)
1493     {
1494       default_label = choices[i].label;
1495       default_hint = choices[i].hint;
1496     }
1497   }
1498   GFX2_Log(GFX2_DEBUG, "max_len_label=%u max_len_hint=%u\n", max_len_label, max_len_hint);
1499   if (max_len_hint > 37)  // maximum window width is 320 pixels, so that's 37 characters
1500     max_len_hint = 37;
1501 
1502   if (max_len_label*8 + 6 + 20 > window_width)
1503     window_width = max_len_label*8 + 6 + 20;
1504   if (max_len_hint > 0)
1505   {
1506     window_height += 10;
1507     if(max_len_hint*8 + 20 > window_width)
1508       window_width = max_len_hint * 8 + 20;
1509   }
1510   if (window_width > 320)
1511     window_width = 320;
1512 
1513   Open_window(window_width, window_height, title);
1514   Window_set_normal_button(10, window_height-23, 40,14, "OK", 1,1,KEY_o); // 1
1515   Window_set_normal_button(window_width-70, window_height-23, 60,14, "Cancel", 1,1,KEY_c); // 2
1516   dropdown = Window_set_dropdown_button(10, 21, window_width-20, 14, window_width-20,
1517                                         default_label, 1, 0, 1, RIGHT_SIDE|LEFT_SIDE, 0); // 3
1518   for (i = 0; choices[i].label != NULL; i++)
1519     Window_dropdown_add_item(dropdown, choices[i].value, choices[i].label);
1520 
1521   if (default_hint != NULL)
1522     Print_in_window(10, 21+18, default_hint, MC_Dark, MC_Light);
1523 
1524   Update_window_area(0, 0, window_width, window_height);
1525   Display_cursor();
1526 
1527   do
1528   {
1529     clicked_button = Window_clicked_button();
1530     if (clicked_button == 3)
1531     {
1532       choice = Window_attribute2;
1533       for (i = 0; choices[i].label != NULL; i++)
1534         if (choice == choices[i].value)
1535         {
1536           memset(hint, ' ', sizeof(hint));
1537           hint[max_len_hint] = '\0';
1538           if (choices[i].hint != NULL)
1539             memcpy(hint, choices[i].hint, MIN(37, strlen(choices[i].hint)));
1540           Hide_cursor();
1541           Print_in_window(10, 21+18, hint, MC_Dark, MC_Light);
1542           Display_cursor();
1543           break;
1544         }
1545     }
1546     else if (Key==KEY_ESCAPE)
1547       clicked_button = 2; // Cancel
1548     else if (Key==KEY_RETURN)
1549       clicked_button = 1; // OK
1550   }
1551   while (clicked_button <= 0 || clicked_button > 2);
1552 
1553   Close_window();
1554   Display_cursor();
1555   return (clicked_button == 2) ? -1 : choice;
1556 }
1557 
1558 
1559 /// Window that shows a warning message and wait for a click on the OK button
Warning_message(const char * message)1560 void Warning_message(const char * message)
1561 {
1562   short clicked_button;
1563   word  window_width;
1564 
1565   window_width=(strlen(message)<<3)+20;
1566   if (window_width<120)
1567     window_width=120;
1568 
1569   Open_window(window_width,60,"Warning!");
1570 
1571   Print_in_window((window_width>>1)-(strlen(message)<<2),20,message,MC_Black,MC_Light);
1572   Window_set_normal_button((window_width>>1)-20     ,37,40,14,"OK",1,1,KEY_RETURN); // 1
1573   Update_window_area(0,0,window_width,60);
1574   Display_cursor();
1575 
1576   do
1577     clicked_button=Window_clicked_button();
1578   while ((clicked_button<=0) && (Key!=KEY_ESC) && (Key!=KEY_o));
1579   Key=0;
1580 
1581   Close_window();
1582   Display_cursor();
1583 }
1584 
1585 /// Window that shows a warning message and waits for a click on the OK button
1586 ///
1587 /// This has the added advantage of supporting the printf interface.
Warning_with_format(const char * template,...)1588 void Warning_with_format(const char *template, ...) {
1589   va_list arg_ptr;
1590   static char message[400]; // This is enough for 10 lines of text in 320x200
1591 
1592   va_start(arg_ptr, template);
1593   vsnprintf(message, sizeof(message), template, arg_ptr);
1594   Verbose_message("Warning", message);
1595   va_end(arg_ptr);
1596 }
1597 
1598 /// Window that shows a big message (up to 35x13), and waits for a click on OK.
1599 /// - On call: Cursor must be displayed
1600 /// - On exit: Cursor is displayed
Verbose_message(const char * caption,const char * message)1601 void Verbose_message(const char *caption, const char * message )
1602 {
1603   short clicked_button;
1604   int line;
1605   int last_space;
1606   int nb_char;
1607   char buffer[36];
1608   byte original_cursor_shape = Cursor_shape;
1609 
1610   GFX2_Log(GFX2_INFO, "* USER MSG * %s : %s\n", caption, message);
1611 
1612   Open_window(300,160,caption);
1613 
1614   // Word-wrap the message
1615   for (line=0; line < 13 && *message!='\0'; line++)
1616   {
1617     last_space = -1;
1618     for (nb_char=0; nb_char<35 && message[nb_char]!='\0'; nb_char++)
1619     {
1620       buffer[nb_char]=message[nb_char];
1621       if (message[nb_char] == ' ')
1622       {
1623         last_space = nb_char;
1624       }
1625       else if (message[nb_char] == '\n')
1626       {
1627         last_space = nb_char;
1628         break;
1629       }
1630     }
1631     // Close line buffer
1632     if (message[nb_char]=='\0' || last_space == -1)
1633       last_space = nb_char;
1634     buffer[last_space]='\0';
1635 
1636     // Print
1637     Print_in_window(10,20+line*8,buffer,MC_Black,MC_Light);
1638 
1639     // Next line
1640     message=message+last_space;
1641     // Strip at most one carriage return and any leading spaces
1642     if (*message == '\n')
1643       message++;
1644     while (*message == ' ')
1645       message++;
1646   }
1647 
1648   Window_set_normal_button(300/2-20,160-23,40,14,"OK",1,1,KEY_RETURN); // 1
1649   Update_window_area(0,0,Window_width,Window_height);
1650   Cursor_shape=CURSOR_SHAPE_ARROW;
1651   Display_cursor();
1652 
1653   do
1654     clicked_button=Window_clicked_button();
1655   while ((clicked_button<=0) && (Key!=KEY_ESC) && (Key!=KEY_o));
1656   Key=0;
1657 
1658   Close_window();
1659   Cursor_shape=original_cursor_shape;
1660   Display_cursor();
1661 }
1662 
1663   // -- Redessiner le sprite d'un bouton dans le menu --
1664 
Display_sprite_in_menu(int btn_number,char sprite_number)1665 void Display_sprite_in_menu(int btn_number,char sprite_number)
1666 {
1667   Buttons_Pool[btn_number].Icon=sprite_number;
1668 
1669   if (Buttons_Pool[btn_number].Shape == BUTTON_SHAPE_TRIANGLE_TOP_LEFT)
1670     Buttons_Pool[btn_number+1].Icon=sprite_number;
1671 
1672   else if (Buttons_Pool[btn_number].Shape == BUTTON_SHAPE_TRIANGLE_BOTTOM_RIGHT)
1673     Buttons_Pool[btn_number-1].Icon=sprite_number;
1674 }
1675 
1676   // -- Redessiner la forme du pinceau dans le menu --
1677 
Display_paintbrush_in_menu(void)1678 void Display_paintbrush_in_menu(void)
1679 {
1680   switch(Paintbrush_shape)
1681   {
1682     case PAINTBRUSH_SHAPE_COLOR_BRUSH:
1683       Display_sprite_in_menu(BUTTON_PAINTBRUSHES, MENU_SPRITE_COLOR_BRUSH);
1684       break;
1685     case PAINTBRUSH_SHAPE_MONO_BRUSH:
1686       Display_sprite_in_menu(BUTTON_PAINTBRUSHES, MENU_SPRITE_MONO_BRUSH);
1687       break;
1688     default:
1689       Display_sprite_in_menu(BUTTON_PAINTBRUSHES, -1);
1690       break;
1691   }
1692   Draw_menu_button(BUTTON_PAINTBRUSHES,BUTTON_RELEASED);
1693 }
1694 
1695   // -- Dessiner un pinceau prédéfini dans la fenêtre --
1696 
Display_paintbrush_in_window(word x,word y,int number)1697 void Display_paintbrush_in_window(word x,word y,int number)
1698   // Pinceau = 0..NB_PAINTBRUSH_SPRITES-1 : Pinceau prédéfini
1699 {
1700   word x_pos;
1701   word y_pos;
1702   word window_x_pos;
1703   word window_y_pos;
1704   int x_size;
1705   int y_size;
1706   word origin_x;
1707   word origin_y;
1708   word width;
1709   word height;
1710 
1711   x_size=Menu_factor_X/Pixel_height;
1712   if (x_size<1)
1713     x_size=1;
1714   y_size=Menu_factor_Y/Pixel_width;
1715   if (y_size<1)
1716     y_size=1;
1717 
1718   width=Min(Paintbrush[number].Width,PAINTBRUSH_WIDTH);
1719   height=Min(Paintbrush[number].Height,PAINTBRUSH_WIDTH);
1720 
1721   origin_x = (x + 8)*Menu_factor_X - (width/2)*x_size+Window_pos_X;
1722   origin_y = (y + 8)*Menu_factor_Y - (height/2)*y_size+Window_pos_Y;
1723 
1724   for (window_y_pos=0,y_pos=0; y_pos<height; window_y_pos++,y_pos++)
1725     for (window_x_pos=0,x_pos=0; x_pos<width; window_x_pos++,x_pos++)
1726       if (Paintbrush[number].Sprite[y_pos][x_pos])
1727         Block(origin_x+window_x_pos*x_size,origin_y+window_y_pos*y_size,x_size,y_size,MC_Black);
1728   // On n'utilise pas Pixel_in_window() car on ne dessine pas
1729   // forcément avec la même taille de pixel.
1730 
1731   Update_rect( ToWinX(origin_x), ToWinY(origin_y),
1732         ToWinL(Paintbrush[number].Width),
1733         ToWinH(Paintbrush[number].Height)
1734   );
1735 }
1736 
1737   // -- Dessiner des zigouigouis --
1738 
Draw_thingumajig(word x,word y,byte color,short direction)1739 void Draw_thingumajig(word x,word y, byte color, short direction)
1740 {
1741   word i;
1742 
1743   for (i=0; i<11; i++) Pixel_in_window(x,y+i,color);
1744   x+=direction;
1745   for (i=1; i<10; i++) Pixel_in_window(x,y+i,color);
1746   x+=direction+direction;
1747   for (i=3; i<8; i++) Pixel_in_window(x,y+i,color);
1748   x+=direction+direction;
1749   Pixel_in_window(x,y+5,color);
1750 }
1751 
1752   // -- Dessiner un bloc de couleurs dégradé verticalement
1753 
Display_grad_block_in_window(word x_pos,word y_pos,word width,word height,word block_start,word block_end)1754 void Display_grad_block_in_window(word x_pos,word y_pos,word width,word height,word block_start,word block_end)
1755 {
1756   word total_lines  =Menu_factor_Y*height;
1757   word nb_colors   =(block_start<=block_end)?block_end-block_start+1:block_start-block_end+1;
1758   word Selected_line_mode=(block_start<=block_end)?0:total_lines-1;
1759 
1760   word start_x       =Window_pos_X+(Menu_factor_X*x_pos);
1761   word line_width =Menu_factor_X*width;
1762 
1763   word start_y       =Window_pos_Y+(Menu_factor_Y*y_pos);
1764   word end_y         =start_y+total_lines;
1765   word index;
1766 
1767   if (block_start>block_end)
1768   {
1769     index=block_start;
1770     block_start=block_end;
1771     block_end=index;
1772   }
1773 
1774   for (index=start_y;index<end_y;index++,Selected_line_mode++)
1775     Block(start_x,index,line_width,1,block_start+(nb_colors*Selected_line_mode)/total_lines);
1776 
1777   Update_rect(ToWinX(x_pos),ToWinY(y_pos),line_width,total_lines);
1778 }
1779 
1780 
1781 
1782   // -- Dessiner un petit sprite représentant le type d'un drive --
1783 
Window_display_icon_sprite(word x_pos,word y_pos,byte type)1784 void Window_display_icon_sprite(word x_pos,word y_pos,byte type)
1785 {
1786   word i,j;
1787 
1788   for (j=0; j<ICON_SPRITE_HEIGHT; j++)
1789     for (i=0; i<ICON_SPRITE_WIDTH; i++)
1790       Pixel_in_window(x_pos+i,y_pos+j,Gfx->Icon_sprite[type][j][i]);
1791   Update_rect(ToWinX(x_pos),ToWinY(y_pos),ToWinL(ICON_SPRITE_WIDTH),ToWinH(ICON_SPRITE_HEIGHT));
1792 }
1793 
1794 
1795 
Display_menu_palette_avoiding_window(byte * table)1796 void Display_menu_palette_avoiding_window(byte * table)
1797 {
1798   // On part du principe qu'il n'y a que le bas d'une fenêtre qui puisse
1799   // empiéter sur la palette... Et c'est déjà pas mal!
1800   word color,real_color;
1801   word start_x,start_y;
1802   word end_x,end_y;
1803   word width;
1804   word height;
1805   word corner_x=Window_pos_X+Window_width*Menu_factor_X; // |_ Coin bas-droit
1806   word corner_y=Window_pos_Y+Window_height*Menu_factor_Y; // |  de la fenêtre +1
1807 
1808   if (Config.Separate_colors)
1809   {
1810     width=(Menu_palette_cell_width-1)*Menu_factor_X;
1811     height=Menu_factor_Y*((Menu_height-11)/Menu_cells_Y-1);
1812   }
1813   else
1814   {
1815     width=Menu_palette_cell_width*Menu_factor_X;
1816     height=Menu_factor_Y*((Menu_height-11)/Menu_cells_Y);
1817   }
1818 
1819   for (color=0,real_color=First_color_in_palette;color<Menu_cells_X*Menu_cells_Y;color++,real_color++)
1820   {
1821     if (table[real_color]!=real_color)
1822     {
1823       start_x=Palette_cell_X(real_color);
1824       start_y=Palette_cell_Y(real_color);
1825       end_x=start_x+width;
1826       end_y=start_y+height;
1827 
1828       //   On affiche le bloc en entier si on peut, sinon on le découpe autour
1829       // de la fenêtre.
1830       if ( (start_y>=corner_y) || (end_x<=Window_pos_X) || (start_x>=corner_x) )
1831         Block(start_x,start_y,width,height,real_color);
1832       else
1833       {
1834 
1835         if (start_x>=Window_pos_X)
1836         {
1837           if ( (end_x>corner_x) || (end_y>corner_y) )
1838           {
1839             if ( (end_x>corner_x) && (end_y>corner_y) )
1840             {
1841               Block(corner_x,start_y,end_x-corner_x,corner_y-start_y,real_color);
1842               Block(start_x,corner_y,width,end_y-corner_y,real_color);
1843             }
1844             else
1845             {
1846               if (end_y>corner_y)
1847                 Block(start_x,corner_y,width,end_y-corner_y,real_color);
1848               else
1849                 Block(corner_x,start_y,end_x-corner_x,height,real_color);
1850             }
1851           }
1852         }
1853         else
1854         {
1855           if (end_x<corner_x)
1856           {
1857             if (end_y>corner_y)
1858             {
1859               Block(start_x,start_y,Window_pos_X-start_x,corner_y-start_y,real_color);
1860               Block(start_x,corner_y,width,end_y-corner_y,real_color);
1861             }
1862             else
1863               Block(start_x,start_y,Window_pos_X-start_x,height,real_color);
1864           }
1865           else
1866           {
1867             if (end_y>corner_y)
1868             {
1869               Block(start_x,start_y,Window_pos_X-start_x,corner_y-start_y,real_color);
1870               Block(corner_x,start_y,end_x-corner_x,corner_y-start_y,real_color);
1871               Block(start_x,corner_y,width,end_y-corner_y,real_color);
1872             }
1873             else
1874             {
1875               Block(start_x,start_y,Window_pos_X-start_x,height,real_color);
1876               Block(corner_x,start_y,end_x-corner_x,height,real_color);
1877             }
1878           }
1879         }
1880       }
1881       {
1882         // Affichage du bloc directement dans le "buffer de fond" de la fenetre.
1883         // Cela permet au bloc de couleur d'apparaitre si on déplace la fenetre.
1884         short x_pos;
1885         short y_pos;
1886         short relative_x; // besoin d'une variable signée
1887         short relative_y; // besoin d'une variable signée
1888         // Attention aux unités
1889         relative_x = ((short)start_x - (short)Window_pos_X);
1890         relative_y = ((short)start_y - (short)Window_pos_Y);
1891 
1892         for (y_pos=relative_y;y_pos<(relative_y+height)&&y_pos<Window_height*Menu_factor_Y;y_pos++)
1893           for (x_pos=relative_x;x_pos<(relative_x+width)&&x_pos<Window_width*Menu_factor_X;x_pos++)
1894             if (x_pos>=0&&y_pos>=0)
1895               Pixel_background(x_pos,y_pos,real_color);
1896       }
1897     }
1898   }
1899   Update_rect(MENU_WIDTH*Menu_factor_X,Menu_Y_before_window,Screen_width-(MENU_WIDTH*Menu_factor_X),(Menu_height-11)*Menu_factor_Y);
1900 }
1901 
1902 // -------- Calcul des bornes de la partie d'image visible à l'écran ---------
Compute_limits(void)1903 void Compute_limits(void)
1904 /*
1905   Avant l'appel à cette fonction, les données de la loupe doivent être à jour.
1906 */
1907 {
1908   if (Main.magnifier_mode)
1909   {
1910     // -- Calcul des limites de la partie non zoomée de l'image --
1911     Limit_top  =Main.offset_Y;
1912     Limit_left=Main.offset_X;
1913     Limit_visible_bottom   =Limit_top+Menu_Y-1;
1914     Limit_visible_right=Limit_left+Main.separator_position-1;
1915 
1916     if (Limit_visible_bottom>=Main.image_height)
1917       Limit_bottom=Main.image_height-1;
1918     else
1919       Limit_bottom=Limit_visible_bottom;
1920 
1921     if (Limit_visible_right>=Main.image_width)
1922       Limit_right=Main.image_width-1;
1923     else
1924       Limit_right=Limit_visible_right;
1925 
1926     // -- Calcul des limites de la partie zoomée de l'image --
1927     Limit_top_zoom  =Main.magnifier_offset_Y;
1928     Limit_left_zoom=Main.magnifier_offset_X;
1929     Limit_visible_bottom_zoom   =Limit_top_zoom+Main.magnifier_height-1;
1930     Limit_visible_right_zoom=Limit_left_zoom+Main.magnifier_width-1;
1931 
1932     if (Limit_visible_bottom_zoom>=Main.image_height)
1933       Limit_bottom_zoom=Main.image_height-1;
1934     else
1935       Limit_bottom_zoom=Limit_visible_bottom_zoom;
1936 
1937     if (Limit_visible_right_zoom>=Main.image_width)
1938       Limit_right_zoom=Main.image_width-1;
1939     else
1940       Limit_right_zoom=Limit_visible_right_zoom;
1941   }
1942   else
1943   {
1944     // -- Calcul des limites de la partie visible de l'image --
1945     Limit_top  =Main.offset_Y;
1946     Limit_left=Main.offset_X;
1947     Limit_visible_bottom   =Limit_top+(Menu_is_visible?Menu_Y:Screen_height)-1; // A REVOIR POUR SIMPLIFICATION
1948     Limit_visible_right=Limit_left+Screen_width-1;
1949 
1950     if (Limit_visible_bottom>=Main.image_height)
1951       Limit_bottom=Main.image_height-1;
1952     else
1953       Limit_bottom=Limit_visible_bottom;
1954 
1955     if (Limit_visible_right>=Main.image_width)
1956       Limit_right=Main.image_width-1;
1957     else
1958       Limit_right=Limit_visible_right;
1959   }
1960 }
1961 
1962 
1963 // -- Calculer les coordonnées du pinceau en fonction du snap et de la loupe -
Compute_paintbrush_coordinates(void)1964 void Compute_paintbrush_coordinates(void)
1965 {
1966   if ((Main.magnifier_mode) && (Mouse_X>=Main.X_zoom))
1967   {
1968     Paintbrush_X=((Mouse_X-Main.X_zoom)/Main.magnifier_factor)+Main.magnifier_offset_X;
1969     Paintbrush_Y=(Mouse_Y/Main.magnifier_factor)+Main.magnifier_offset_Y;
1970   }
1971   else
1972   {
1973     Paintbrush_X=Mouse_X+Main.offset_X;
1974     Paintbrush_Y=Mouse_Y+Main.offset_Y;
1975   }
1976 
1977   if (Snap_mode)
1978   {
1979     Paintbrush_X=(((Paintbrush_X+(Snap_width>>1)-Snap_offset_X)/Snap_width)*Snap_width)+Snap_offset_X;
1980     Paintbrush_Y=(((Paintbrush_Y+(Snap_height>>1)-Snap_offset_Y)/Snap_height)*Snap_height)+Snap_offset_Y;
1981   }
1982 
1983   // Handling the snap axis mode, when shift is pressed.
1984   switch (Current_operation)
1985   {
1986     // Operations that don't implement it
1987     case OPERATION_LINE:
1988     case OPERATION_ROTATE_BRUSH:
1989       Snap_axis=0;
1990       break;
1991     // Operations that implement it
1992     default:
1993       if (Snap_axis==0 && (Get_Key_modifiers() & GFX2_MOD_SHIFT))
1994       {
1995         // Start "Snap axis" mode
1996         Snap_axis=1;
1997         Snap_axis_origin_X=Paintbrush_X;
1998         Snap_axis_origin_Y=Paintbrush_Y;
1999       }
2000   }
2001 
2002   if (Snap_axis==1)
2003   {
2004     // Cursor moved
2005     if (Paintbrush_X != Snap_axis_origin_X || Paintbrush_Y != Snap_axis_origin_Y)
2006     {
2007       if ((Paintbrush_X-Snap_axis_origin_X)*(Paintbrush_X-Snap_axis_origin_X) >
2008           (Paintbrush_Y-Snap_axis_origin_Y)*(Paintbrush_Y-Snap_axis_origin_Y))
2009       // Displacement was bigger on X axis: lock Y
2010         Snap_axis=2;
2011       else
2012         Snap_axis=3;
2013     }
2014   }
2015   if (Snap_axis==2)
2016   {
2017     Paintbrush_Y = Snap_axis_origin_Y;
2018   }
2019   else if (Snap_axis==3)
2020   {
2021     Paintbrush_X = Snap_axis_origin_X;
2022   }
2023 }
2024 
2025 
2026 
2027 // -- Affichage de la limite de l'image -------------------------------------
Display_image_limits(void)2028 void Display_image_limits(void)
2029 {
2030   short start;
2031   short pos;
2032   short end;
2033   byte right_is_visible;
2034   byte bottom_is_visible;
2035   short old_zoom_limit;
2036 
2037   right_is_visible=Main.image_width<((Main.magnifier_mode)?Main.separator_position:Screen_width);
2038   bottom_is_visible   =Main.image_height<Menu_Y;
2039 
2040 
2041   // On vérifie que la limite à droite est visible:
2042   if (right_is_visible)
2043   {
2044     start=Limit_top;
2045     end=(Limit_bottom<Main.image_height)?
2046         Limit_bottom:Main.image_height;
2047 
2048     if (bottom_is_visible)
2049       end++;
2050 
2051     // Juste le temps d'afficher les limites, on étend les limites de la loupe
2052     // aux limites visibles, car sinon Pixel_preview ne voudra pas afficher.
2053     old_zoom_limit=Limit_right_zoom;
2054     Limit_right_zoom=Limit_visible_right_zoom;
2055 
2056     for (pos=start;pos<=end;pos++)
2057       Pixel_preview(Main.image_width,pos,((pos+Main.image_height)&1)?MC_White:MC_Black);
2058 
2059     Update_rect(Main.image_width,start,1,end-start + 1);
2060     // On restaure la bonne valeur des limites
2061     Limit_right_zoom=old_zoom_limit;
2062   }
2063 
2064   // On vérifie que la limite en bas est visible:
2065   if (bottom_is_visible)
2066   {
2067     start=Limit_left;
2068     end=(Limit_right<Main.image_width)?
2069         Limit_right:Main.image_width;
2070 
2071     // On étend également les limites en bas (comme pour la limite droit)
2072     old_zoom_limit=Limit_bottom_zoom;
2073     Limit_bottom_zoom=Limit_visible_bottom_zoom;
2074 
2075     for (pos=start;pos<=end;pos++)
2076       Pixel_preview(pos,Main.image_height,((pos+Main.image_height)&1)?MC_White:MC_Black);
2077 
2078     Update_rect(start,Main.image_height,end-start + 1,1);
2079 
2080     // On restaure la bonne valeur des limites
2081     Limit_bottom_zoom=old_zoom_limit;
2082   }
2083 }
2084 
2085 
2086 
2087 // -- Recadrer la partie non-zoomée de l'image par rapport à la partie zoomée
2088 //    lorsqu'on scrolle en mode Loupe --
Position_screen_according_to_zoom(void)2089 void Position_screen_according_to_zoom(void)
2090 {
2091   // Centrage en X
2092   if (Main.image_width>Main.separator_position)
2093   {
2094     Main.offset_X=Main.magnifier_offset_X+(Main.magnifier_width>>1)
2095                          -(Main.separator_position>>1);
2096     if (Main.offset_X<0)
2097       Main.offset_X=0;
2098     else if (Main.image_width<Main.offset_X+Main.separator_position)
2099       Main.offset_X=Main.image_width-Main.separator_position;
2100   }
2101   else
2102     Main.offset_X=0;
2103 
2104   // Centrage en Y
2105   if (Main.image_height>Menu_Y)
2106   {
2107     Main.offset_Y=Main.magnifier_offset_Y+(Main.magnifier_height>>1)
2108                          -(Menu_Y>>1);
2109     if (Main.offset_Y<0)
2110       Main.offset_Y=0;
2111     else if (Main.image_height<Main.offset_Y+Menu_Y)
2112       Main.offset_Y=Main.image_height-Menu_Y;
2113   }
2114   else
2115     Main.offset_Y=0;
2116 }
2117 
2118 // -- Recenter the non-zoomed part of image around a precise pixel
Position_screen_according_to_position(int target_x,int target_y)2119 void Position_screen_according_to_position(int target_x, int target_y)
2120 {
2121   // Centrage en X
2122   if (Main.image_width>Main.separator_position)
2123   {
2124     Main.offset_X=target_x-Mouse_X;
2125     // Do not allow the zoomed part to show something that the
2126     // non-zoomed part doesn't see. All clipping is computed according
2127     // to the non-zoomed part.
2128     if (Main.magnifier_offset_X<Main.offset_X)
2129       Main.offset_X=Main.magnifier_offset_X;
2130     else if (Main.magnifier_offset_X+Main.magnifier_width > Main.offset_X+Main.separator_position)
2131       Main.offset_X = Main.magnifier_offset_X+Main.magnifier_width-Main.separator_position;
2132     if (Main.offset_X<0)
2133       Main.offset_X=0;
2134     else if (Main.image_width<Main.offset_X+Main.separator_position)
2135       Main.offset_X=Main.image_width-Main.separator_position;
2136 
2137 
2138   }
2139   else
2140     Main.offset_X=0;
2141 
2142   // Centrage en Y
2143   if (Main.image_height>Menu_Y)
2144   {
2145     Main.offset_Y=target_y-Mouse_Y;
2146     // Do not allow the zoomed part to show something that the
2147     // non-zoomed part doesn't see. All clipping is computed according
2148     // to the non-zoomed part.
2149     if (Main.magnifier_offset_Y<Main.offset_Y)
2150       Main.offset_Y=Main.magnifier_offset_Y;
2151     else if (Main.magnifier_offset_Y+Main.magnifier_height > Main.offset_Y)
2152       Main.offset_Y = Main.magnifier_offset_Y+Main.magnifier_height;
2153     if (Main.offset_Y<0)
2154       Main.offset_Y=0;
2155     else if (Main.image_height<Main.offset_Y+Menu_Y)
2156       Main.offset_Y=Main.image_height-Menu_Y;
2157   }
2158   else
2159     Main.offset_Y=0;
2160 }
2161 
2162 
2163 // - Calcul des données du split en fonction de la proportion de chaque zone -
Compute_separator_data(void)2164 void Compute_separator_data(void)
2165 {
2166   //short temp;
2167   short theoric_X=Round(Main.separator_proportion*Screen_width);
2168 
2169   Main.X_zoom=Screen_width-(((Screen_width+(Main.magnifier_factor>>1)-theoric_X)/Main.magnifier_factor)*Main.magnifier_factor);
2170   Main.separator_position=Main.X_zoom-(Menu_factor_X*SEPARATOR_WIDTH);
2171 
2172   // Correction en cas de débordement sur la gauche
2173   while (Main.separator_position*(Main.magnifier_factor+1)<Screen_width-(Menu_factor_X*SEPARATOR_WIDTH))
2174   {
2175     Main.separator_position+=Main.magnifier_factor;
2176     Main.X_zoom+=Main.magnifier_factor;
2177   }
2178   // Correction en cas de débordement sur la droite
2179   theoric_X=Screen_width-((NB_ZOOMED_PIXELS_MIN-1)*Main.magnifier_factor);
2180   while (Main.X_zoom>=theoric_X)
2181   {
2182     Main.separator_position-=Main.magnifier_factor;
2183     Main.X_zoom-=Main.magnifier_factor;
2184   }
2185 }
2186 
2187 
2188 
2189 // -------------------- Calcul des information de la loupe -------------------
Compute_magnifier_data(void)2190 void Compute_magnifier_data(void)
2191 /*
2192   Après modification des données de la loupe, il faut recalculer les limites.
2193 */
2194 {
2195   Compute_separator_data();
2196 
2197   Main.magnifier_width=(Screen_width-Main.X_zoom)/Main.magnifier_factor;
2198 
2199   Main.magnifier_height=Menu_Y/Main.magnifier_factor;
2200   if (Menu_Y%Main.magnifier_factor)
2201     Main.magnifier_height++;
2202 
2203   Clip_magnifier_offsets(&Main.magnifier_offset_X, &Main.magnifier_offset_Y);
2204 }
2205 
Clip_magnifier_offsets(short * x_offset,short * y_offset)2206 void Clip_magnifier_offsets(short *x_offset, short *y_offset)
2207 {
2208   if (Main.magnifier_mode)
2209   {
2210     if (*x_offset)
2211     {
2212       if (Main.image_width<*x_offset+Main.magnifier_width)
2213         *x_offset=Main.image_width-Main.magnifier_width;
2214       if (*x_offset<0)
2215         *x_offset=0;
2216     }
2217     if (*y_offset)
2218     {
2219       if (Main.image_height<*y_offset+Main.magnifier_height)
2220         *y_offset=Main.image_height-Main.magnifier_height+(Main.magnifier_height*Main.magnifier_factor-Menu_Y>=Main.magnifier_factor/2);
2221       if (*y_offset<0)
2222         *y_offset=0;
2223     }
2224   }
2225 }
2226 
2227 /// Changes magnifier factor and updates everything needed
Change_magnifier_factor(byte factor_index,byte point_at_mouse)2228 void Change_magnifier_factor(byte factor_index, byte point_at_mouse)
2229 {
2230   int target_x,target_y; // These coordinates are in image space
2231   byte magnified_view_leads=1;
2232 
2233   // Values that need to be computed before switching to the new zoom factor
2234   if (!point_at_mouse || Cursor_in_menu || !Main.magnifier_mode)
2235   {
2236     // Locate the pixel in center of the magnified area
2237     target_x = Main.magnifier_offset_X + (Main.magnifier_width >> 1);
2238     target_y = Main.magnifier_offset_Y + (Main.magnifier_height >> 1);
2239     point_at_mouse=0;
2240   }
2241   else if (Mouse_X>=Main.X_zoom)
2242   {
2243     // Locate the pixel under the cursor, in magnified area
2244     target_x=((Mouse_X-Main.X_zoom)/Main.magnifier_factor)+Main.magnifier_offset_X;
2245     target_y=(Mouse_Y/Main.magnifier_factor)+Main.magnifier_offset_Y;
2246     point_at_mouse=1;
2247   }
2248   else
2249   {
2250     // Locate the pixel under the cursor, in normal area
2251     target_x=Mouse_X+Main.offset_X;
2252     target_y=Mouse_Y+Main.offset_Y;
2253     magnified_view_leads=0;
2254     point_at_mouse=0;
2255   }
2256 
2257   Main.magnifier_factor=ZOOM_FACTOR[factor_index];
2258   Compute_magnifier_data();
2259 
2260   if (Main.magnifier_mode)
2261   {
2262     // Recompute the magnifier offset (center its view)
2263     if (point_at_mouse)
2264     {
2265       // Target pixel must be located under the mouse position.
2266       Main.magnifier_offset_X = target_x-((Mouse_X-Main.X_zoom)/Main.magnifier_factor);
2267       Main.magnifier_offset_Y = target_y-((Mouse_Y)/Main.magnifier_factor);
2268     }
2269     else
2270     {
2271       // Target pixel must be positioned at new center
2272       Main.magnifier_offset_X = target_x-(Main.magnifier_width>>1);
2273       Main.magnifier_offset_Y = target_y-(Main.magnifier_height>>1);
2274     }
2275     // Fix cases where the image would overflow on edges
2276     Clip_magnifier_offsets(&Main.magnifier_offset_X, &Main.magnifier_offset_Y);
2277 
2278     if (magnified_view_leads)
2279       Position_screen_according_to_zoom();
2280     else
2281       Position_screen_according_to_position(target_x, target_y);
2282 
2283     Pixel_preview=Pixel_preview_magnifier;
2284 
2285   }
2286   else
2287     Pixel_preview=Pixel_preview_normal;
2288 
2289   Compute_limits();
2290   Compute_paintbrush_coordinates();
2291 }
2292 
Copy_view_to_spare(void)2293 void Copy_view_to_spare(void)
2294 {
2295 
2296   // Don't do anything if the pictures have different dimensions
2297   if (Main.image_width!=Spare.image_width || Main.image_height!=Spare.image_height)
2298     return;
2299 
2300   // Copie des décalages de la fenêtre principale (non zoomée) de l'image
2301   Spare.offset_X=Main.offset_X;
2302   Spare.offset_Y=Main.offset_Y;
2303 
2304   // Copie du booléen "Mode loupe" de l'image
2305   Spare.magnifier_mode=Main.magnifier_mode;
2306 
2307   // Copie du facteur de zoom du brouillon
2308   Spare.magnifier_factor=Main.magnifier_factor;
2309 
2310   // Copie des dimensions de la fenêtre de zoom
2311   Spare.magnifier_width=Main.magnifier_width;
2312   Spare.magnifier_height=Main.magnifier_height;
2313 
2314   // Copie des décalages de la fenêtre de zoom
2315   Spare.magnifier_offset_X=Main.magnifier_offset_X;
2316   Spare.magnifier_offset_Y=Main.magnifier_offset_Y;
2317 
2318   // Copie des données du split du zoom
2319   Spare.separator_position=Main.separator_position;
2320   Spare.X_zoom=Main.X_zoom;
2321   Spare.separator_proportion=Main.separator_proportion;
2322 }
2323 
2324   // -- Afficher la barre de séparation entre les parties zoomées ou non en
2325   //    mode Loupe --
2326 
Display_separator(void)2327 void Display_separator(void)
2328 {
2329   // Partie grise du milieu
2330   Block(Main.separator_position+(Menu_factor_X<<1),Menu_factor_Y,
2331         (SEPARATOR_WIDTH-4)*Menu_factor_X,
2332         Menu_Y-(Menu_factor_Y<<1),MC_Light);
2333 
2334   // Barre noire de gauche
2335   Block(Main.separator_position,0,Menu_factor_X,Menu_Y,MC_Black);
2336 
2337   // Barre noire de droite
2338   Block(Main.X_zoom-Menu_factor_X,0,Menu_factor_X,Menu_Y,MC_Black);
2339 
2340   // Bord haut (blanc)
2341   Block(Main.separator_position+Menu_factor_X,0,
2342         (SEPARATOR_WIDTH-3)*Menu_factor_X,Menu_factor_Y,MC_White);
2343 
2344   // Bord gauche (blanc)
2345   Block(Main.separator_position+Menu_factor_X,Menu_factor_Y,
2346         Menu_factor_X,(Menu_Y-(Menu_factor_Y<<1)),MC_White);
2347 
2348   // Bord droite (gris foncé)
2349   Block(Main.X_zoom-(Menu_factor_X<<1),Menu_factor_Y,
2350         Menu_factor_X,(Menu_Y-(Menu_factor_Y<<1)),MC_Dark);
2351 
2352   // Bord bas (gris foncé)
2353   Block(Main.separator_position+(Menu_factor_X<<1),Menu_Y-Menu_factor_Y,
2354         (SEPARATOR_WIDTH-3)*Menu_factor_X,Menu_factor_Y,MC_Dark);
2355 
2356   // Coin bas gauche
2357   Block(Main.separator_position+Menu_factor_X,Menu_Y-Menu_factor_Y,
2358         Menu_factor_X,Menu_factor_Y,MC_Light);
2359   // Coin haut droite
2360   Block(Main.X_zoom-(Menu_factor_X<<1),0,
2361         Menu_factor_X,Menu_factor_Y,MC_Light);
2362 
2363   Update_rect(Main.separator_position,0,SEPARATOR_WIDTH*Menu_factor_X,Menu_Y); // On réaffiche toute la partie à gauche du split, ce qui permet d'effacer son ancienne position
2364 }
2365 
2366 
2367 
2368 // -- Fonctions de manipulation du curseur -----------------------------------
2369 
2370 
2371   // -- Afficher une barre horizontale XOR zoomée
2372 
Horizontal_XOR_line_zoom(short x_pos,short y_pos,short width)2373 void Horizontal_XOR_line_zoom(short x_pos, short y_pos, short width)
2374 {
2375   short real_x_pos=Main.X_zoom+(x_pos-Main.magnifier_offset_X)*Main.magnifier_factor;
2376   short real_y_pos=(y_pos-Main.magnifier_offset_Y)*Main.magnifier_factor;
2377   short real_width=width*Main.magnifier_factor;
2378   short end_y_pos=(real_y_pos+Main.magnifier_factor<Menu_Y)?real_y_pos+Main.magnifier_factor:Menu_Y;
2379   short index;
2380 
2381   for (index=real_y_pos; index<end_y_pos; index++)
2382     Horizontal_XOR_line(real_x_pos,index,real_width);
2383 
2384   Update_rect(real_x_pos,real_y_pos,real_width,end_y_pos-real_y_pos);
2385 }
2386 
2387 
2388   // -- Afficher une barre verticale XOR zoomée
2389 
Vertical_XOR_line_zoom(short x_pos,short y_pos,short height)2390 void Vertical_XOR_line_zoom(short x_pos, short y_pos, short height)
2391 {
2392   short real_x_pos=Main.X_zoom+(x_pos-Main.magnifier_offset_X)*Main.magnifier_factor;
2393   short real_y_pos=(y_pos-Main.magnifier_offset_Y)*Main.magnifier_factor;
2394   short end_y_pos=(real_y_pos+height*Main.magnifier_factor<Menu_Y)?real_y_pos+(height*Main.magnifier_factor):Menu_Y;
2395   short index;
2396 
2397   for (index=real_y_pos; index<end_y_pos; index++)
2398     Horizontal_XOR_line(real_x_pos,index,Main.magnifier_factor);
2399 
2400   Update_rect(real_x_pos,real_y_pos,Main.magnifier_factor,end_y_pos-real_y_pos);
2401 }
2402 
2403 
2404   // -- Afficher le curseur --
2405 
Display_cursor(void)2406 void Display_cursor(void)
2407 {
2408   byte  shape;
2409   short start_x;
2410   short start_y;
2411   short end_x;
2412   short end_y;
2413   short x_pos;
2414   short y_pos;
2415   short counter_x = 0;
2416   short counter_y;
2417   int   temp;
2418   byte  color;
2419   float cos_a,sin_a;
2420   short x1,y1,x2,y2,x3,y3,x4,y4;
2421 
2422   // Si le curseur est dans le menu ou sur la barre de split, on affiche toujours une flèche.
2423   if ( ( (Mouse_Y<Menu_Y)
2424       && ( (!Main.magnifier_mode) || (Mouse_X<Main.separator_position) || (Mouse_X>=Main.X_zoom) ) )
2425     || (Windows_open) || (Cursor_shape==CURSOR_SHAPE_HOURGLASS) )
2426     shape=Cursor_shape;
2427   else
2428     shape=CURSOR_SHAPE_ARROW;
2429 
2430   switch(shape)
2431   {
2432     case CURSOR_SHAPE_TARGET :
2433       if (!Paintbrush_hidden)
2434         Display_paintbrush(Paintbrush_X,Paintbrush_Y,Fore_color);
2435       if (!Cursor_hidden)
2436       {
2437         if (Config.Cursor==1)
2438         {
2439           start_y=(Mouse_Y<6)?6-Mouse_Y:0;
2440           if (start_y<4)
2441             Vertical_XOR_line  (Mouse_X,Mouse_Y+start_y-6,4-start_y);
2442 
2443           start_x=(Mouse_X<6)?(short)6-Mouse_X:0;
2444           if (start_x<4)
2445             Horizontal_XOR_line(Mouse_X+start_x-6,Mouse_Y,4-start_x);
2446 
2447           end_x=(Mouse_X+7>Screen_width)?Mouse_X+7-Screen_width:0;
2448           if (end_x<4)
2449             Horizontal_XOR_line(Mouse_X+3,Mouse_Y,4-end_x);
2450 
2451           end_y=(Mouse_Y+7>Screen_height)?Mouse_Y+7-Screen_height:0;
2452           if (end_y<4)
2453             Vertical_XOR_line  (Mouse_X,Mouse_Y+3,4-end_y);
2454 
2455           Update_rect(Mouse_X+start_x-6,Mouse_Y+start_y-6,13-end_x,13-end_y);
2456         }
2457         else
2458         {
2459           temp=(Config.Cursor)?CURSOR_SHAPE_THIN_TARGET:CURSOR_SHAPE_TARGET;
2460           start_x=Mouse_X-Gfx->Cursor_offset_X[temp];
2461           start_y=Mouse_Y-Gfx->Cursor_offset_Y[temp];
2462 
2463           for (y_pos=start_y,counter_y=0; counter_y<15 && y_pos < Screen_height;
2464             y_pos++,counter_y++)
2465           {
2466             if( y_pos < 0 ) continue;
2467               for (x_pos=start_x,counter_x=0;
2468                 counter_x<15 && x_pos < Screen_width; x_pos++,counter_x++)
2469               {
2470                 if( x_pos < 0 ) continue;
2471                   color=Gfx->Cursor_sprite[temp][counter_y][counter_x];
2472                   Cursor_background[counter_y][counter_x]=Read_pixel(x_pos,y_pos);
2473                   if (color!=MC_Trans)
2474                     Pixel(x_pos,y_pos,color);
2475             }
2476           }
2477 
2478           Update_rect(Max(start_x,0),Max(start_y,0),counter_x,counter_y);
2479         }
2480       }
2481       break;
2482 
2483     case CURSOR_SHAPE_COLORPICKER:
2484       if (!Paintbrush_hidden)
2485         Display_paintbrush(Paintbrush_X,Paintbrush_Y,Fore_color);
2486       if (Config.Cursor==1)
2487       {
2488         // Barres formant la croix principale
2489 
2490         start_y=(Mouse_Y<5)?5-Mouse_Y:0;
2491         if (start_y<3)
2492           Vertical_XOR_line  (Mouse_X,Mouse_Y+start_y-5,3-start_y);
2493 
2494         start_x=(Mouse_X<5)?(short)5-Mouse_X:0;
2495         if (start_x<3)
2496           Horizontal_XOR_line(Mouse_X+start_x-5,Mouse_Y,3-start_x);
2497 
2498         end_x=(Mouse_X+6>Screen_width)?Mouse_X+6-Screen_width:0;
2499         if (end_x<3)
2500           Horizontal_XOR_line(Mouse_X+3,Mouse_Y,3-end_x);
2501 
2502         end_y=(Mouse_Y+6>Menu_Y/*Screen_height*/)?Mouse_Y+6-Menu_Y/*Screen_height*/:0;
2503         if (end_y<3)
2504           Vertical_XOR_line  (Mouse_X,Mouse_Y+3,3-end_y);
2505 
2506         // Petites barres aux extrémités
2507 
2508         start_x=(!Mouse_X);
2509         start_y=(!Mouse_Y);
2510         end_x=(Mouse_X>=Screen_width-1);
2511         end_y=(Mouse_Y>=Menu_Y-1);
2512 
2513         if (Mouse_Y>5)
2514           Horizontal_XOR_line(start_x+Mouse_X-1,Mouse_Y-6,3-(start_x+end_x));
2515 
2516         if (Mouse_X>5)
2517           Vertical_XOR_line  (Mouse_X-6,start_y+Mouse_Y-1,3-(start_y+end_y));
2518 
2519         if (Mouse_X<Screen_width-6)
2520           Vertical_XOR_line  (Mouse_X+6,start_y+Mouse_Y-1,3-(start_y+end_y));
2521 
2522         if (Mouse_Y<Menu_Y-6)
2523           Horizontal_XOR_line(start_x+Mouse_X-1,Mouse_Y+6,3-(start_x+end_x));
2524       }
2525       else
2526       {
2527         temp=(Config.Cursor)?CURSOR_SHAPE_THIN_COLORPICKER:CURSOR_SHAPE_COLORPICKER;
2528         start_x=Mouse_X-Gfx->Cursor_offset_X[temp];
2529         start_y=Mouse_Y-Gfx->Cursor_offset_Y[temp];
2530 
2531         for (y_pos=start_y,counter_y=0;counter_y<15;y_pos++,counter_y++)
2532         {
2533           if(y_pos<0) continue;
2534           if(y_pos>=Screen_height) break;
2535           for (x_pos=start_x,counter_x=0;counter_x<15;x_pos++,counter_x++)
2536           {
2537             if(x_pos<0) continue;
2538             if(x_pos>=Screen_width) break;
2539             color=Gfx->Cursor_sprite[temp][counter_y][counter_x];
2540             // On sauvegarde dans Cursor_background pour restaurer plus tard
2541             Cursor_background[counter_y][counter_x]=Read_pixel(x_pos,y_pos);
2542             if (color!=MC_Trans)
2543               Pixel(x_pos,y_pos,color);
2544           }
2545         }
2546         Update_rect(Max(start_x,0),Max(start_y,0),counter_x,counter_y);
2547       }
2548       break;
2549 
2550     case CURSOR_SHAPE_MULTIDIRECTIONAL :
2551     case CURSOR_SHAPE_HORIZONTAL :
2552     case CURSOR_SHAPE_BUCKET :
2553       if (Cursor_hidden)
2554         break;
2555 #if defined(__GNUC__) && (__GNUC__ >= 7)
2556           __attribute__ ((fallthrough));
2557 #endif
2558 
2559     case CURSOR_SHAPE_ARROW :
2560     case CURSOR_SHAPE_HOURGLASS :
2561       start_x=Mouse_X-Gfx->Cursor_offset_X[shape];
2562       start_y=Mouse_Y-Gfx->Cursor_offset_Y[shape];
2563       for (y_pos=start_y,counter_y=0;counter_y<CURSOR_SPRITE_HEIGHT;y_pos++,counter_y++)
2564       {
2565         if(y_pos<0) continue;
2566         if(y_pos>=Screen_height) break;
2567         for (x_pos=start_x,counter_x=0;counter_x<CURSOR_SPRITE_WIDTH;x_pos++,counter_x++)
2568         {
2569           if(x_pos<0) continue;
2570           if(x_pos>=Screen_width) break;
2571           color=Gfx->Cursor_sprite[shape][counter_y][counter_x];
2572           // On sauvegarde dans Cursor_background pour restaurer plus tard
2573           Cursor_background[counter_y][counter_x]=Read_pixel(x_pos,y_pos);
2574           if (color!=MC_Trans)
2575             Pixel(x_pos,y_pos,color);
2576         }
2577       }
2578       Update_rect(Max(start_x,0),Max(start_y,0),counter_x,counter_y);
2579       break;
2580 
2581     case CURSOR_SHAPE_XOR_TARGET :
2582       x_pos=Paintbrush_X-Main.offset_X;
2583       y_pos=Paintbrush_Y-Main.offset_Y;
2584 
2585       counter_x=(Main.magnifier_mode)?Main.separator_position:Screen_width; // width de la barre XOR
2586       if ((y_pos<Menu_Y) && (Paintbrush_Y>=Limit_top))
2587       {
2588         Horizontal_XOR_line(0,Paintbrush_Y-Main.offset_Y,counter_x);
2589         Update_rect(0,Paintbrush_Y-Main.offset_Y,counter_x,1);
2590       }
2591 
2592       if ((x_pos<counter_x) && (Paintbrush_X>=Limit_left))
2593       {
2594         Vertical_XOR_line(Paintbrush_X-Main.offset_X,0,Menu_Y);
2595         Update_rect(Paintbrush_X-Main.offset_X,0,1,Menu_Y);
2596       }
2597 
2598       if (Main.magnifier_mode)
2599       {
2600         // UPDATERECT
2601         if ((Paintbrush_Y>=Limit_top_zoom) && (Paintbrush_Y<=Limit_visible_bottom_zoom))
2602           Horizontal_XOR_line_zoom(Limit_left_zoom,Paintbrush_Y,Main.magnifier_width);
2603         if ((Paintbrush_X>=Limit_left_zoom) && (Paintbrush_X<=Limit_visible_right_zoom))
2604           Vertical_XOR_line_zoom(Paintbrush_X,Limit_top_zoom,Main.magnifier_height);
2605       }
2606       break;
2607     case CURSOR_SHAPE_XOR_RECTANGLE :
2608       // !!! Cette forme ne peut pas être utilisée en mode Loupe !!!
2609 
2610       // Petite croix au centre
2611       start_x=(Mouse_X-3);
2612       start_y=(Mouse_Y-3);
2613       end_x  =(Mouse_X+4);
2614       end_y  =(Mouse_Y+4);
2615       if (start_x<0)
2616         start_x=0;
2617       if (start_y<0)
2618         start_y=0;
2619       if (end_x>Screen_width)
2620         end_x=Screen_width;
2621       if (end_y>Menu_Y)
2622         end_y=Menu_Y;
2623 
2624       Horizontal_XOR_line(start_x,Mouse_Y,end_x-start_x);
2625       Vertical_XOR_line  (Mouse_X,start_y,end_y-start_y);
2626 
2627       // Grand rectangle autour
2628       start_x=Mouse_X-(Main.magnifier_width>>1);
2629       start_y=Mouse_Y-(Main.magnifier_height>>1);
2630       if (start_x+Main.magnifier_width>=Limit_right-Main.offset_X)
2631         start_x=Limit_right-Main.magnifier_width-Main.offset_X+1;
2632       if (start_y+Main.magnifier_height>=Limit_bottom-Main.offset_Y)
2633         start_y=Limit_bottom-Main.magnifier_height-Main.offset_Y+1;
2634       if (start_x<0)
2635         start_x=0;
2636       if (start_y<0)
2637         start_y=0;
2638       end_x=start_x+Main.magnifier_width-1;
2639       end_y=start_y+Main.magnifier_height-1;
2640 
2641       Horizontal_XOR_line(start_x,start_y,Main.magnifier_width);
2642       Vertical_XOR_line(start_x,start_y+1,Main.magnifier_height-2);
2643       Vertical_XOR_line(  end_x,start_y+1,Main.magnifier_height-2);
2644       Horizontal_XOR_line(start_x,  end_y,Main.magnifier_width);
2645 
2646       Update_rect(start_x,start_y,end_x+1-start_x,end_y+1-start_y);
2647 
2648       break;
2649     default: //case CURSOR_SHAPE_XOR_ROTATION :
2650       start_x=1-(Brush_width>>1);
2651       start_y=1-(Brush_height>>1);
2652       end_x=start_x+Brush_width-1;
2653       end_y=start_y+Brush_height-1;
2654 
2655       if (Brush_rotation_center_is_defined)
2656       {
2657         if ( (Brush_rotation_center_X==Paintbrush_X)
2658           && (Brush_rotation_center_Y==Paintbrush_Y) )
2659         {
2660           cos_a=1.0;
2661           sin_a=0.0;
2662         }
2663         else
2664         {
2665           x_pos=Paintbrush_X-Brush_rotation_center_X;
2666           y_pos=Paintbrush_Y-Brush_rotation_center_Y;
2667           cos_a=(float)x_pos/sqrt((x_pos*x_pos)+(y_pos*y_pos));
2668           sin_a=sin(acos(cos_a));
2669           if (y_pos>0) sin_a=-sin_a;
2670         }
2671 
2672         Transform_point(start_x,start_y, cos_a,sin_a, &x1,&y1);
2673         Transform_point(end_x  ,start_y, cos_a,sin_a, &x2,&y2);
2674         Transform_point(start_x,end_y  , cos_a,sin_a, &x3,&y3);
2675         Transform_point(end_x  ,end_y  , cos_a,sin_a, &x4,&y4);
2676 
2677         x1+=Brush_rotation_center_X;
2678         y1+=Brush_rotation_center_Y;
2679         x2+=Brush_rotation_center_X;
2680         y2+=Brush_rotation_center_Y;
2681         x3+=Brush_rotation_center_X;
2682         y3+=Brush_rotation_center_Y;
2683         x4+=Brush_rotation_center_X;
2684         y4+=Brush_rotation_center_Y;
2685         Pixel_figure_preview_xor(Brush_rotation_center_X,Brush_rotation_center_Y,0);
2686         Draw_line_preview_xor(Brush_rotation_center_X,Brush_rotation_center_Y,Paintbrush_X,Paintbrush_Y,0);
2687       }
2688       else
2689       {
2690         x1=x3=1-Brush_width;
2691         y1=y2=start_y;
2692         x2=x4=Paintbrush_X;
2693         y3=y4=end_y;
2694 
2695         x1+=Paintbrush_X;
2696         y1+=Paintbrush_Y;
2697         y2+=Paintbrush_Y;
2698         x3+=Paintbrush_X;
2699         y3+=Paintbrush_Y;
2700         y4+=Paintbrush_Y;
2701         Pixel_figure_preview_xor(Paintbrush_X-end_x,Paintbrush_Y,0);
2702         Draw_line_preview_xor(Paintbrush_X-end_x,Paintbrush_Y,Paintbrush_X,Paintbrush_Y,0);
2703       }
2704 
2705       Draw_line_preview_xor(x1,y1,x2,y2,0);
2706       Draw_line_preview_xor(x2,y2,x4,y4,0);
2707       Draw_line_preview_xor(x4,y4,x3,y3,0);
2708       Draw_line_preview_xor(x3,y3,x1,y1,0);
2709   }
2710 }
2711 
2712   // -- Effacer le curseur --
2713 
Hide_cursor(void)2714 void Hide_cursor(void)
2715 {
2716   byte  shape;
2717   int start_x; // int car sont parfois négatifs ! (quand on dessine sur un bord)
2718   int start_y;
2719   short end_x;
2720   short end_y;
2721   int x_pos = 0;
2722   int y_pos;
2723   short counter_x = 0;
2724   short counter_y;
2725   int   temp;
2726   float cos_a,sin_a;
2727   short x1,y1,x2,y2,x3,y3,x4,y4;
2728 
2729   if ( ( (Mouse_Y<Menu_Y)
2730       && ( (!Main.magnifier_mode) || (Mouse_X<Main.separator_position)
2731                          || (Mouse_X>=Main.X_zoom) ) )
2732     || (Windows_open) || (Cursor_shape==CURSOR_SHAPE_HOURGLASS) )
2733     shape=Cursor_shape;
2734   else
2735     shape=CURSOR_SHAPE_ARROW;
2736 
2737   switch(shape)
2738   {
2739     case CURSOR_SHAPE_TARGET :
2740       if (!Cursor_hidden)
2741       {
2742         if (Config.Cursor==1)
2743         {
2744           start_y=(Mouse_Y<6)?6-Mouse_Y:0;
2745           if (start_y<4)
2746             Vertical_XOR_line  (Mouse_X,Mouse_Y+start_y-6,4-start_y);
2747 
2748           start_x=(Mouse_X<6)?(short)6-Mouse_X:0;
2749           if (start_x<4)
2750             Horizontal_XOR_line(Mouse_X+start_x-6,Mouse_Y,4-start_x);
2751 
2752           end_x=(Mouse_X+7>Screen_width)?Mouse_X+7-Screen_width:0;
2753           if (end_x<4)
2754             Horizontal_XOR_line(Mouse_X+3,Mouse_Y,4-end_x);
2755 
2756           end_y=(Mouse_Y+7>Screen_height)?Mouse_Y+7-Screen_height:0;
2757           if (end_y<4)
2758             Vertical_XOR_line  (Mouse_X,Mouse_Y+3,4-end_y);
2759 
2760           Update_rect(Mouse_X+start_x-6,Mouse_Y+start_y-6,13-end_x,13-end_y);
2761         }
2762         else
2763         {
2764           temp=(Config.Cursor)?CURSOR_SHAPE_THIN_TARGET:CURSOR_SHAPE_TARGET;
2765           start_x=Mouse_X-Gfx->Cursor_offset_X[temp];
2766           start_y=Mouse_Y-Gfx->Cursor_offset_Y[temp];
2767 
2768           for (y_pos=start_y,counter_y=0;counter_y<15;y_pos++,counter_y++)
2769           {
2770             if(y_pos < 0) continue;
2771             if(y_pos>=Screen_height) break;
2772             for (x_pos=start_x,counter_x=0;counter_x<15;x_pos++,counter_x++)
2773             {
2774               if(x_pos < 0) continue;
2775               else if (x_pos>=Screen_width) break;
2776               Pixel(x_pos,y_pos,Cursor_background[counter_y][counter_x]);
2777             }
2778           }
2779 
2780           Update_rect(Max(start_x,0),Max(start_y,0),x_pos-start_x,y_pos-start_y);
2781         }
2782       }
2783       if (!Paintbrush_hidden)
2784       {
2785         Hide_paintbrush(Paintbrush_X,Paintbrush_Y);
2786       }
2787       break;
2788 
2789     case CURSOR_SHAPE_COLORPICKER:
2790         if (Config.Cursor==1)
2791         {
2792           // Barres formant la croix principale
2793 
2794           start_y=(Mouse_Y<5)?5-Mouse_Y:0;
2795           if (start_y<3)
2796             Vertical_XOR_line  (Mouse_X,Mouse_Y+start_y-5,3-start_y);
2797 
2798           start_x=(Mouse_X<5)?(short)5-Mouse_X:0;
2799           if (start_x<3)
2800             Horizontal_XOR_line(Mouse_X+start_x-5,Mouse_Y,3-start_x);
2801 
2802           end_x=(Mouse_X+6>Screen_width)?Mouse_X+6-Screen_width:0;
2803           if (end_x<3)
2804             Horizontal_XOR_line(Mouse_X+3,Mouse_Y,3-end_x);
2805 
2806           end_y=(Mouse_Y+6>Screen_height)?Mouse_Y+6-Screen_height:0;
2807           if (end_y<3)
2808             Vertical_XOR_line  (Mouse_X,Mouse_Y+3,3-end_y);
2809 
2810           start_x=(!Mouse_X);
2811           start_y=(!Mouse_Y);
2812           end_x=(Mouse_X>=Screen_width-1);
2813           end_y=(Mouse_Y>=Menu_Y-1);
2814 
2815           if (Mouse_Y>5)
2816             Horizontal_XOR_line(start_x+Mouse_X-1,Mouse_Y-6,3-(start_x+end_x));
2817 
2818           if (Mouse_X>5)
2819             Vertical_XOR_line  (Mouse_X-6,start_y+Mouse_Y-1,3-(start_y+end_y));
2820 
2821           if (Mouse_X<Screen_width-6)
2822             Vertical_XOR_line  (Mouse_X+6,start_y+Mouse_Y-1,3-(start_y+end_y));
2823 
2824           if (Mouse_Y<Menu_Y-6)
2825             Horizontal_XOR_line(start_x+Mouse_X-1,Mouse_Y+6,3-(start_x+end_x));
2826 
2827           Update_rect(start_x,start_y,end_x-start_x,end_y-start_y);
2828         }
2829         else
2830         {
2831           temp=(Config.Cursor)?CURSOR_SHAPE_THIN_COLORPICKER:CURSOR_SHAPE_COLORPICKER;
2832           start_x=Mouse_X-Gfx->Cursor_offset_X[temp];
2833           start_y=Mouse_Y-Gfx->Cursor_offset_Y[temp];
2834 
2835           for (y_pos=start_y,counter_y=0;counter_y<15;y_pos++,counter_y++)
2836           {
2837             if(y_pos<0) continue;
2838             if(y_pos>=Screen_height) break;
2839             for (x_pos=start_x,counter_x=0;counter_x<15;x_pos++,counter_x++)
2840             {
2841               if(x_pos<0) continue;
2842               if(x_pos>=Screen_width) break;
2843                   Pixel(x_pos,y_pos,Cursor_background[counter_y][counter_x]);
2844             }
2845           }
2846           Update_rect(Max(start_x,0),Max(start_y,0),counter_x,counter_y);
2847         }
2848       if (!Paintbrush_hidden)
2849         Hide_paintbrush(Paintbrush_X,Paintbrush_Y);
2850       break;
2851 
2852     case CURSOR_SHAPE_MULTIDIRECTIONAL :
2853     case CURSOR_SHAPE_HORIZONTAL :
2854     case CURSOR_SHAPE_BUCKET :
2855       if (Cursor_hidden)
2856         break;
2857 #if defined(__GNUC__) && (__GNUC__ >= 7)
2858           __attribute__ ((fallthrough));
2859 #endif
2860 
2861     case CURSOR_SHAPE_ARROW :
2862     case CURSOR_SHAPE_HOURGLASS :
2863       start_x=Mouse_X-Gfx->Cursor_offset_X[shape];
2864       start_y=Mouse_Y-Gfx->Cursor_offset_Y[shape];
2865 
2866       for (y_pos=start_y,counter_y=0;counter_y<CURSOR_SPRITE_HEIGHT;y_pos++,counter_y++)
2867       {
2868         if(y_pos<0) continue;
2869         if(y_pos>=Screen_height) break;
2870         for (x_pos=start_x,counter_x=0;counter_x<CURSOR_SPRITE_WIDTH;x_pos++,counter_x++)
2871         {
2872           if(x_pos<0) continue;
2873           if(x_pos>=Screen_width) break;
2874             Pixel(x_pos,y_pos,Cursor_background[counter_y][counter_x]);
2875         }
2876       }
2877       Update_rect(Max(start_x,0),Max(start_y,0),counter_x,counter_y);
2878       break;
2879 
2880     case CURSOR_SHAPE_XOR_TARGET :
2881       x_pos=Paintbrush_X-Main.offset_X;
2882       y_pos=Paintbrush_Y-Main.offset_Y;
2883 
2884       counter_x=(Main.magnifier_mode)?Main.separator_position:Screen_width; // width de la barre XOR
2885       if ((y_pos<Menu_Y) && (Paintbrush_Y>=Limit_top))
2886       {
2887         Horizontal_XOR_line(0,Paintbrush_Y-Main.offset_Y,counter_x);
2888         Update_rect(0,Paintbrush_Y-Main.offset_Y,counter_x,1);
2889       }
2890 
2891       if ((x_pos<counter_x) && (Paintbrush_X>=Limit_left))
2892       {
2893         Vertical_XOR_line(Paintbrush_X-Main.offset_X,0,Menu_Y);
2894         Update_rect(Paintbrush_X-Main.offset_X,0,1,Menu_Y);
2895       }
2896 
2897       if (Main.magnifier_mode)
2898       {
2899         // UPDATERECT
2900         if ((Paintbrush_Y>=Limit_top_zoom) && (Paintbrush_Y<=Limit_visible_bottom_zoom))
2901           Horizontal_XOR_line_zoom(Limit_left_zoom,Paintbrush_Y,Main.magnifier_width);
2902         if ((Paintbrush_X>=Limit_left_zoom) && (Paintbrush_X<=Limit_visible_right_zoom))
2903           Vertical_XOR_line_zoom(Paintbrush_X,Limit_top_zoom,Main.magnifier_height);
2904       }
2905 
2906 
2907       break;
2908     case CURSOR_SHAPE_XOR_RECTANGLE :
2909       // !!! Cette forme ne peut pas être utilisée en mode Loupe !!!
2910 
2911       // Petite croix au centre
2912       start_x=(Mouse_X-3);
2913       start_y=(Mouse_Y-3);
2914       end_x  =(Mouse_X+4);
2915       end_y  =(Mouse_Y+4);
2916       if (start_x<0)
2917         start_x=0;
2918       if (start_y<0)
2919         start_y=0;
2920       if (end_x>Screen_width)
2921         end_x=Screen_width;
2922       if (end_y>Menu_Y)
2923         end_y=Menu_Y;
2924 
2925       Horizontal_XOR_line(start_x,Mouse_Y,end_x-start_x);
2926       Vertical_XOR_line  (Mouse_X,start_y,end_y-start_y);
2927 
2928       // Grand rectangle autour
2929 
2930       start_x=Mouse_X-(Main.magnifier_width>>1);
2931       start_y=Mouse_Y-(Main.magnifier_height>>1);
2932       if (start_x+Main.magnifier_width>=Limit_right-Main.offset_X)
2933         start_x=Limit_right-Main.magnifier_width-Main.offset_X+1;
2934       if (start_y+Main.magnifier_height>=Limit_bottom-Main.offset_Y)
2935         start_y=Limit_bottom-Main.magnifier_height-Main.offset_Y+1;
2936       if (start_x<0)
2937         start_x=0;
2938       if (start_y<0)
2939         start_y=0;
2940       end_x=start_x+Main.magnifier_width-1;
2941       end_y=start_y+Main.magnifier_height-1;
2942 
2943       Horizontal_XOR_line(start_x,start_y,Main.magnifier_width);
2944       Vertical_XOR_line(start_x,start_y+1,Main.magnifier_height-2);
2945       Vertical_XOR_line(  end_x,start_y+1,Main.magnifier_height-2);
2946       Horizontal_XOR_line(start_x,  end_y,Main.magnifier_width);
2947 
2948       Update_rect(start_x,start_y,end_x+1-start_x,end_y+1-start_y);
2949 
2950       break;
2951     default: //case CURSOR_SHAPE_XOR_ROTATION :
2952       start_x=1-(Brush_width>>1);
2953       start_y=1-(Brush_height>>1);
2954       end_x=start_x+Brush_width-1;
2955       end_y=start_y+Brush_height-1;
2956 
2957       if (Brush_rotation_center_is_defined)
2958       {
2959         if ( (Brush_rotation_center_X==Paintbrush_X)
2960           && (Brush_rotation_center_Y==Paintbrush_Y) )
2961         {
2962           cos_a=1.0;
2963           sin_a=0.0;
2964         }
2965         else
2966         {
2967           x_pos=Paintbrush_X-Brush_rotation_center_X;
2968           y_pos=Paintbrush_Y-Brush_rotation_center_Y;
2969           cos_a=(float)x_pos/sqrt((x_pos*x_pos)+(y_pos*y_pos));
2970           sin_a=sin(acos(cos_a));
2971           if (y_pos>0) sin_a=-sin_a;
2972         }
2973 
2974         Transform_point(start_x,start_y, cos_a,sin_a, &x1,&y1);
2975         Transform_point(end_x  ,start_y, cos_a,sin_a, &x2,&y2);
2976         Transform_point(start_x,end_y  , cos_a,sin_a, &x3,&y3);
2977         Transform_point(end_x  ,end_y  , cos_a,sin_a, &x4,&y4);
2978 
2979         x1+=Brush_rotation_center_X;
2980         y1+=Brush_rotation_center_Y;
2981         x2+=Brush_rotation_center_X;
2982         y2+=Brush_rotation_center_Y;
2983         x3+=Brush_rotation_center_X;
2984         y3+=Brush_rotation_center_Y;
2985         x4+=Brush_rotation_center_X;
2986         y4+=Brush_rotation_center_Y;
2987         Pixel_figure_preview_xor(Brush_rotation_center_X,Brush_rotation_center_Y,0);
2988         Draw_line_preview_xor(Brush_rotation_center_X,Brush_rotation_center_Y,Paintbrush_X,Paintbrush_Y,0);
2989       }
2990       else
2991       {
2992         x1=x3=1-Brush_width;
2993         y1=y2=start_y;
2994         x2=x4=Paintbrush_X;
2995         y3=y4=end_y;
2996 
2997         x1+=Paintbrush_X;
2998         y1+=Paintbrush_Y;
2999         y2+=Paintbrush_Y;
3000         x3+=Paintbrush_X;
3001         y3+=Paintbrush_Y;
3002         y4+=Paintbrush_Y;
3003         Pixel_figure_preview_xor(Paintbrush_X-end_x,Paintbrush_Y,0);
3004         Draw_line_preview_xor(Paintbrush_X-end_x,Paintbrush_Y,Paintbrush_X,Paintbrush_Y,0);
3005       }
3006 
3007       Draw_line_preview_xor(x1,y1,x2,y2,0);
3008       Draw_line_preview_xor(x2,y2,x4,y4,0);
3009       Draw_line_preview_xor(x4,y4,x3,y3,0);
3010       Draw_line_preview_xor(x3,y3,x1,y1,0);
3011   }
3012 }
3013 
3014 
3015 
3016 // -- Fonction diverses d'affichage ------------------------------------------
3017 
3018   // -- Reafficher toute l'image (en prenant en compte le facteur de zoom) --
3019 
Display_all_screen(void)3020 void Display_all_screen(void)
3021 {
3022   word width;
3023   word height;
3024 
3025   // ---/\/\/\  Partie non zoomée: /\/\/\---
3026   if (Main.magnifier_mode)
3027   {
3028     if (Main.image_width<Main.separator_position)
3029       width=Main.image_width;
3030     else
3031       width=Main.separator_position;
3032   }
3033   else
3034   {
3035     if (Main.image_width<Screen_width)
3036       width=Main.image_width;
3037     else
3038       width=Screen_width;
3039   }
3040   if (Main.image_height<Menu_Y)
3041     height=Main.image_height;
3042   else
3043     height=Menu_Y;
3044   Display_screen(width,height,Main.image_width);
3045 
3046   // Effacement de la partie non-image dans la partie non zoomée:
3047   if (Main.magnifier_mode)
3048   {
3049     if (Main.image_width<Main.separator_position && Main.image_width < Screen_width)
3050       Block(Main.image_width,0,(Main.separator_position-Main.image_width),Menu_Y,Main.backups->Pages->Transparent_color);
3051   }
3052   else
3053   {
3054     if (Main.image_width<Screen_width)
3055       Block(Main.image_width,0,(Screen_width-Main.image_width),Menu_Y,Main.backups->Pages->Transparent_color);
3056   }
3057   if (Main.image_height<Menu_Y)
3058     Block(0,Main.image_height,width,(Menu_Y-height),Main.backups->Pages->Transparent_color);
3059 
3060   // ---/\/\/\  Partie zoomée: /\/\/\---
3061   if (Main.magnifier_mode)
3062   {
3063     // Affichage de la barre de split
3064     Display_separator();
3065 
3066     // Calcul de la largeur visible
3067     if (Main.image_width<Main.magnifier_width)
3068       width=Main.image_width;
3069     else
3070       width=Main.magnifier_width;
3071 
3072     // Calcul du nombre de lignes visibles de l'image zoomée
3073     if (Main.image_height<Main.magnifier_height)
3074       height=Main.image_height*Main.magnifier_factor;
3075     else if (Main.image_height<Main.magnifier_offset_Y+Main.magnifier_height)
3076       // Omit "last line" if it's outside picture limits
3077       height=Menu_Y/Main.magnifier_factor*Main.magnifier_factor;
3078     else
3079       height=Menu_Y;
3080 
3081     Display_zoomed_screen(width,height,Main.image_width,Horizontal_line_buffer);
3082 
3083     // Effacement de la partie non-image dans la partie zoomée:
3084     if (Main.image_width<Main.magnifier_width)
3085       Block(Main.X_zoom+(Main.image_width*Main.magnifier_factor),0,
3086             (Main.magnifier_width-Main.image_width)*Main.magnifier_factor,
3087             Menu_Y,Main.backups->Pages->Transparent_color);
3088     if (height<Menu_Y)
3089       Block(Main.X_zoom,height,width*Main.magnifier_factor,(Menu_Y-height),Main.backups->Pages->Transparent_color);
3090   }
3091 
3092   // ---/\/\/\ Affichage des limites /\/\/\---
3093   if (Config.Display_image_limits)
3094     Display_image_limits();
3095   Update_rect(0,0,Screen_width,Menu_Y); // TODO On peut faire plus fin, en évitant de mettre à jour la partie à droite du split quand on est en mode loupe. Mais c'est pas vraiment intéressant ?
3096 }
3097 
3098 
3099 
Best_color(byte r,byte g,byte b)3100 byte Best_color(byte r,byte g,byte b)
3101 {
3102   int col;
3103   int   delta_r,delta_g,delta_b;
3104   int   dist;
3105   int   best_dist=0x7FFFFFFF;
3106   int   rmean;
3107   byte  best_color=0;
3108 
3109   for (col=0; col<256; col++)
3110   {
3111     if (!Exclude_color[col])
3112     {
3113       delta_r=(int)Main.palette[col].R-r;
3114       delta_g=(int)Main.palette[col].G-g;
3115       delta_b=(int)Main.palette[col].B-b;
3116 
3117       rmean = ( Main.palette[col].R + r ) / 2;
3118 
3119       if (!(dist= ( ( (512+rmean) *delta_r*delta_r) >>8) + 4*delta_g*delta_g + (((767-rmean)*delta_b*delta_b)>>8)))
3120       //if (!(dist=(delta_r*delta_r*30)+(delta_g*delta_g*59)+(delta_b*delta_b*11)))
3121         return col;
3122 
3123       if (dist<best_dist)
3124       {
3125         best_dist=dist;
3126         best_color=col;
3127       }
3128     }
3129   }
3130 
3131   return best_color;
3132 }
3133 
Best_color_nonexcluded(byte red,byte green,byte blue)3134 byte Best_color_nonexcluded(byte red,byte green,byte blue)
3135 {
3136   int   col;
3137   int   delta_r,delta_g,delta_b;
3138   int   dist;
3139   int   best_dist=0x7FFFFFFF;
3140   int   rmean;
3141   byte  best_color=0;
3142 
3143   for (col=0; col<256; col++)
3144   {
3145     delta_r=(int)Main.palette[col].R-red;
3146     delta_g=(int)Main.palette[col].G-green;
3147     delta_b=(int)Main.palette[col].B-blue;
3148 
3149     if(delta_r == 0 && delta_g == 0 && delta_b == 0) return col;
3150 
3151     rmean = ( Main.palette[col].R + red ) / 2;
3152 
3153     dist= ( ( (512+rmean) *delta_r*delta_r) >>8) + 4*delta_g*delta_g + (((767-rmean)*delta_b*delta_b)>>8);
3154     //dist=(delta_r*delta_r*30)+(delta_g*delta_g*59)+(delta_b*delta_b*11)
3155 
3156     if (dist<best_dist)
3157     {
3158       best_dist=dist;
3159       best_color=col;
3160     }
3161   }
3162   return best_color;
3163 }
3164 
Best_color_range(byte r,byte g,byte b,byte max)3165 byte Best_color_range(byte r, byte g, byte b, byte max)
3166 {
3167 
3168   int col;
3169   float best_diff=255.0f*1.56905f;
3170   byte  best_color=0;
3171   float target_bri;
3172   float bri;
3173   float diff_b, diff_c, diff;
3174 
3175   // Similar to Perceptual_lightness();
3176   target_bri = sqrt(0.26*r*0.26*r + 0.55*g*0.55*g + 0.19*b*0.19*b);
3177 
3178   for (col=0; col<max; col++)
3179   {
3180     if (Exclude_color[col])
3181       continue;
3182 
3183     diff_c = sqrt(
3184       (0.26*(Main.palette[col].R-r))*
3185       (0.26*(Main.palette[col].R-r))+
3186       (0.55*(Main.palette[col].G-g))*
3187       (0.55*(Main.palette[col].G-g))+
3188       (0.19*(Main.palette[col].B-b))*
3189       (0.19*(Main.palette[col].B-b)));
3190     // Exact match
3191     if (diff_c==0)
3192       return col;
3193 
3194     bri = sqrt(0.26*Main.palette[col].R*0.26*Main.palette[col].R + 0.55*Main.palette[col].G*0.55*Main.palette[col].G + 0.19*Main.palette[col].B*0.19*Main.palette[col].B);
3195     diff_b = fabsf(target_bri-bri);
3196 
3197     diff=0.25*(diff_b-diff_c)+diff_c;
3198     if (diff<best_diff)
3199     {
3200       best_diff=diff;
3201       best_color=col;
3202     }
3203   }
3204 
3205   return best_color;
3206 }
3207 
Best_color_perceptual(byte r,byte g,byte b)3208 byte Best_color_perceptual(byte r,byte g,byte b)
3209 {
3210 
3211   int col;
3212   float best_diff=255.0f*1.56905f;
3213   byte  best_color=0;
3214   float target_bri;
3215   float bri;
3216   float diff_b, diff_c, diff;
3217 
3218   // Similar to Perceptual_lightness();
3219   target_bri = sqrt(0.26*r*0.26*r + 0.55*g*0.55*g + 0.19*b*0.19*b);
3220 
3221   for (col=0; col<256; col++)
3222   {
3223     if (Exclude_color[col])
3224       continue;
3225 
3226     diff_c = sqrt(
3227       (0.26*(Main.palette[col].R-r))*
3228       (0.26*(Main.palette[col].R-r))+
3229       (0.55*(Main.palette[col].G-g))*
3230       (0.55*(Main.palette[col].G-g))+
3231       (0.19*(Main.palette[col].B-b))*
3232       (0.19*(Main.palette[col].B-b)));
3233     // Exact match
3234     if (diff_c==0)
3235       return col;
3236 
3237     bri = sqrt(0.26*Main.palette[col].R*0.26*Main.palette[col].R + 0.55*Main.palette[col].G*0.55*Main.palette[col].G + 0.19*Main.palette[col].B*0.19*Main.palette[col].B);
3238     diff_b = fabsf(target_bri-bri);
3239 
3240     diff=0.25*(diff_b-diff_c)+diff_c;
3241     if (diff<best_diff)
3242     {
3243       best_diff=diff;
3244       best_color=col;
3245     }
3246   }
3247 
3248   return best_color;
3249 }
3250 
Best_color_perceptual_except(byte r,byte g,byte b,byte except)3251 byte Best_color_perceptual_except(byte r,byte g,byte b, byte except)
3252 {
3253 
3254   int col;
3255   float best_diff=255.0f*1.56905f;
3256   byte  best_color=0;
3257   float target_bri;
3258   float bri;
3259   float diff_b, diff_c, diff;
3260 
3261   // Similar to Perceptual_lightness();
3262   target_bri = sqrt(0.26*r*0.26*r + 0.55*g*0.55*g + 0.19*b*0.19*b);
3263 
3264   for (col=0; col<256; col++)
3265   {
3266     if (col==except || Exclude_color[col])
3267       continue;
3268 
3269     diff_c = sqrt(
3270       (0.26*(Main.palette[col].R-r))*
3271       (0.26*(Main.palette[col].R-r))+
3272       (0.55*(Main.palette[col].G-g))*
3273       (0.55*(Main.palette[col].G-g))+
3274       (0.19*(Main.palette[col].B-b))*
3275       (0.19*(Main.palette[col].B-b)));
3276     // Exact match
3277     if (diff_c==0)
3278       return col;
3279 
3280     bri = sqrt(0.26*Main.palette[col].R*0.26*Main.palette[col].R + 0.55*Main.palette[col].G*0.55*Main.palette[col].G + 0.19*Main.palette[col].B*0.19*Main.palette[col].B);
3281     diff_b = fabsf(target_bri-bri);
3282 
3283     diff=0.25*(diff_b-diff_c)+diff_c;
3284     if (diff<best_diff)
3285     {
3286       best_diff=diff;
3287       best_color=col;
3288     }
3289   }
3290 
3291   return best_color;
3292 }
3293 
3294 static byte Old_black;
3295 static byte Old_dark;
3296 static byte Old_light;
3297 static byte Old_white;
3298 static byte Old_trans;
3299 
Remap_pixel(byte * pixel)3300 void Remap_pixel(byte * pixel)
3301 {
3302   if (*pixel==Old_light)         // On commence par tester le Gris clair
3303     *pixel=MC_Light;             // qui est pas mal utilisé.
3304   else
3305   {
3306     if (*pixel==Old_black)        // Puis le Noir...
3307       *pixel=MC_Black;
3308     else
3309     {
3310       if (*pixel==Old_dark)     // etc...
3311         *pixel=MC_Dark;
3312       else
3313       {
3314         if (*pixel==Old_white)
3315           *pixel=MC_White;
3316         else
3317         {
3318           if (*pixel==Old_trans)
3319             *pixel=MC_Trans;
3320         }
3321       }
3322     }
3323   }
3324 }
3325 
3326 
3327 
Remap_screen_after_menu_colors_change(void)3328 void Remap_screen_after_menu_colors_change(void)
3329 {
3330   short index;
3331   byte  conversion_table[256];
3332   //short temp/*,temp2*/;
3333   int window_index, pos_y;
3334 
3335   if ( (MC_Light!=Old_light) || (MC_Dark!=Old_dark) || (MC_White!=Old_white) || (MC_Black !=Old_black )
3336     || (MC_Trans!=Old_trans) )
3337   {
3338     // Création de la table de conversion
3339     for (index=0; index<256; index++)
3340       conversion_table[index]=index;
3341 
3342     conversion_table[Old_black ]=MC_Black;
3343     conversion_table[Old_dark]=MC_Dark;
3344     conversion_table[Old_light]=MC_Light;
3345     conversion_table[Old_white]=MC_White;
3346 
3347     // Remappage de l'écran
3348 
3349     // remap only screen pixels covered by a window or the menu
3350     for (pos_y = 0; pos_y < Screen_height; pos_y++)
3351     {
3352       int min_x = 0xffff;
3353       int max_x = 0;
3354       if (Menu_is_visible_before_window && pos_y >= Menu_Y_before_window)
3355       {
3356         min_x = 0;
3357         max_x = Screen_width;
3358       }
3359       else for (window_index = 0; window_index < Windows_open; window_index++)
3360       {
3361         if (pos_y < Window_stack[window_index].Pos_Y
3362          || pos_y >= (Window_stack[window_index].Pos_Y + Window_stack[window_index].Height*Menu_factor_Y) )
3363           continue; // this window doesn't occupy this screen row
3364         if (min_x > Window_stack[window_index].Pos_X)
3365           min_x = Window_stack[window_index].Pos_X;
3366         if (max_x < (Window_stack[window_index].Pos_X + Window_stack[window_index].Width*Menu_factor_X))
3367           max_x = Window_stack[window_index].Pos_X + Window_stack[window_index].Width*Menu_factor_X;
3368       }
3369       if (max_x > min_x)
3370         Remap_screen(min_x, pos_y, max_x - min_x, 1, conversion_table);
3371     }
3372 
3373     // Remap windows and menu in the backgrounds buffers
3374     Remap_UI_in_window_backgrounds(conversion_table);
3375     if (Menu_is_visible_before_window)
3376     {
3377       /*
3378          Il faudrait peut-être remapper les pointillés délimitant l'image.
3379          Mais ça va être chiant parce qu'ils peuvent être affichés en mode Loupe.
3380          Mais de toutes façons, c'est franchement facultatif...
3381       */
3382       // On passe la table juste pour ne rafficher que les couleurs modifiées
3383       Display_menu_palette_avoiding_window(conversion_table);
3384     }
3385     Clear_border(MC_Black);
3386   }
3387 
3388 }
3389 
3390 
Diff(const T_Components * palette,int i,int j)3391 static int Diff(const T_Components * palette, int i, int j) {
3392   int dr = palette[i].R - palette[j].R;
3393   int dg = palette[i].G - palette[j].G;
3394   int db = palette[i].B - palette[j].B;
3395 
3396   return dr*dr + dg*dg + db*db;
3397 }
3398 
compute_xor_table(const T_Components * palette)3399 static void compute_xor_table(const T_Components * palette)
3400 {
3401 	int i;
3402 	byte found;
3403 
3404 	// Initialize the table with some "random" values
3405 	for(i = 0; i < 256; i++)
3406 	{
3407 		xor_lut[i] = i ^ 1;
3408 	}
3409 
3410 	do {
3411 		// Find the smallest difference in the table
3412 		int idx;
3413 
3414 		// Try to pair these two colors better
3415 		found = 0;
3416 		for(idx = 0; idx < 256; idx++)
3417 		{
3418 			int improvement = 0;
3419 			int betterpair = idx;
3420 			for(i = 0; i < 256; i++)
3421 			{
3422 				// diffs before the swap
3423 				int before = Diff(palette, idx, xor_lut[idx]) + Diff(palette, i, xor_lut[i]);
3424 
3425 				// diffs after the swap
3426 				int after = Diff(palette, idx, xor_lut[i])  + Diff(palette, i, xor_lut[idx]);
3427 
3428 				if (after - before > improvement)
3429 				{
3430 					improvement = after - before;
3431 					betterpair = i;
3432 				}
3433 			}
3434 
3435 			if (improvement > 0)
3436 			{
3437 				// Swapping these colors get us something "more different". Do it !
3438 				byte idx2 = xor_lut[betterpair];
3439 				byte i2 = xor_lut[idx];
3440 
3441 				xor_lut[betterpair] = i2;
3442 				xor_lut[i2] = betterpair;
3443 				xor_lut[idx] = idx2;
3444 				xor_lut[idx2] = idx;
3445 
3446 				found = 1;
3447 			}
3448 		}
3449 	} while(found);
3450 }
3451 
Same_color(const T_Components * palette,byte c1,byte c2)3452 static int Same_color(const T_Components * palette, byte c1, byte c2)
3453 {
3454 	if (palette[c1].R==palette[c2].R &&
3455 			palette[c1].G==palette[c2].G &&
3456 			palette[c1].B==palette[c2].B)
3457 		return 1;
3458 	return 0;
3459 }
3460 
3461 static void Remap_menu_sprites(const T_Components * palette);
3462 
Compute_optimal_menu_colors(const T_Components * palette)3463 void Compute_optimal_menu_colors(const T_Components * palette)
3464 {
3465 	int i;
3466 	byte l[256];
3467 	byte s[256];
3468 	byte h;
3469 	int max_l = -1, min_l = 256;
3470 	int low_l, hi_l;
3471 	int delta_low = 999999;
3472 	int delta_high = 999999;
3473 	const int tolerence=16;
3474 	const T_Components cpc_colors[4] = {
3475 		{  0,  0,  0},
3476 		{  0,  0,128}, // Dark blue
3477 		{128,128,128}, // Grey
3478 		{255,255,255}
3479 	};
3480 
3481 	Old_black =MC_Black;
3482 	Old_dark = MC_Dark;
3483 	Old_light = MC_Light;
3484 	Old_white = MC_White;
3485 	Old_trans = MC_Trans;
3486 
3487 	// First method:
3488 	// If all close matches for the ideal colors exist, pick them.
3489 	for (i=255; i>=0; i--)
3490 	{
3491 
3492 		if (Round_palette_component(palette[i].R)/tolerence==Gfx->Default_palette[Gfx->Color[3]].R/tolerence
3493 				&& Round_palette_component(palette[i].G)/tolerence==Gfx->Default_palette[Gfx->Color[3]].G/tolerence
3494 				&& Round_palette_component(palette[i].B)/tolerence==Gfx->Default_palette[Gfx->Color[3]].B/tolerence)
3495 		{
3496 			MC_White=i;
3497 			for (i=255; i>=0; i--)
3498 			{
3499 				if (Round_palette_component(palette[i].R)/tolerence==Gfx->Default_palette[Gfx->Color[2]].R/tolerence
3500 						&& Round_palette_component(palette[i].G)/tolerence==Gfx->Default_palette[Gfx->Color[2]].G/tolerence
3501 						&& Round_palette_component(palette[i].B)/tolerence==Gfx->Default_palette[Gfx->Color[2]].B/tolerence)
3502 				{
3503 					MC_Light=i;
3504 					for (i=255; i>=0; i--)
3505 					{
3506 						if (Round_palette_component(palette[i].R)/tolerence==Gfx->Default_palette[Gfx->Color[1]].R/tolerence
3507 								&& Round_palette_component(palette[i].G)/tolerence==Gfx->Default_palette[Gfx->Color[1]].G/tolerence
3508 								&& Round_palette_component(palette[i].B)/tolerence==Gfx->Default_palette[Gfx->Color[1]].B/tolerence)
3509 						{
3510 							MC_Dark=i;
3511 							for (i=255; i>=0; i--)
3512 							{
3513 								if (Round_palette_component(palette[i].R)/tolerence==Gfx->Default_palette[Gfx->Color[0]].R/tolerence
3514 										&& Round_palette_component(palette[i].G)/tolerence==Gfx->Default_palette[Gfx->Color[0]].G/tolerence
3515 										&& Round_palette_component(palette[i].B)/tolerence==Gfx->Default_palette[Gfx->Color[0]].B/tolerence)
3516 								{
3517 									MC_Black=i;
3518 									// On cherche une couleur de transparence différente des 4 autres.
3519 									for (MC_Trans=0; ((MC_Trans==MC_Black) || (MC_Trans==MC_Dark) ||
3520 												(MC_Trans==MC_Light) || (MC_Trans==MC_White)); MC_Trans++);
3521 									// Easy case
3522 									MC_OnBlack=MC_Dark;
3523 									MC_Window=MC_Light;
3524 									MC_Lighter=MC_White;
3525 									MC_Darker=MC_Dark;
3526 									Remap_menu_sprites(palette);
3527 									return;
3528 								}
3529 							}
3530             }
3531           }
3532         }
3533       }
3534     }
3535   }
3536   // Second method: For CPC 27-color modes only
3537   // Try to find colors that just work
3538   if (Get_palette_RGB_scale()==3)
3539   for (i=255; i>=0; i--)
3540   {
3541 
3542     if (Round_palette_component(palette[i].R)/tolerence==cpc_colors[3].R/tolerence
3543      && Round_palette_component(palette[i].G)/tolerence==cpc_colors[3].G/tolerence
3544      && Round_palette_component(palette[i].B)/tolerence==cpc_colors[3].B/tolerence)
3545     {
3546       MC_White=i;
3547       for (i=255; i>=0; i--)
3548       {
3549         if (Round_palette_component(palette[i].R)/tolerence==cpc_colors[2].R/tolerence
3550          && Round_palette_component(palette[i].G)/tolerence==cpc_colors[2].G/tolerence
3551          && Round_palette_component(palette[i].B)/tolerence==cpc_colors[2].B/tolerence)
3552         {
3553           MC_Light=i;
3554           for (i=255; i>=0; i--)
3555           {
3556             if (Round_palette_component(palette[i].R)/tolerence==cpc_colors[1].R/tolerence
3557              && Round_palette_component(palette[i].G)/tolerence==cpc_colors[1].G/tolerence
3558              && Round_palette_component(palette[i].B)/tolerence==cpc_colors[1].B/tolerence)
3559             {
3560               MC_Dark=i;
3561               for (i=255; i>=0; i--)
3562               {
3563                 if (Round_palette_component(palette[i].R)/tolerence==cpc_colors[0].R/tolerence
3564                  && Round_palette_component(palette[i].G)/tolerence==cpc_colors[0].G/tolerence
3565                  && Round_palette_component(palette[i].B)/tolerence==cpc_colors[0].B/tolerence)
3566                 {
3567                   MC_Black=i;
3568                   // On cherche une couleur de transparence différente des 4 autres.
3569                   for (MC_Trans=0; ((MC_Trans==MC_Black) || (MC_Trans==MC_Dark) ||
3570                                    (MC_Trans==MC_Light) || (MC_Trans==MC_White)); MC_Trans++);
3571                   // Easy case
3572                   MC_OnBlack=MC_Dark;
3573                   MC_Window=MC_Light;
3574                   MC_Lighter=MC_White;
3575                   MC_Darker=MC_Dark;
3576                   Remap_menu_sprites(palette);
3577                   return;
3578                 }
3579               }
3580             }
3581           }
3582         }
3583       }
3584     }
3585   }
3586 
3587   // Third method:
3588 
3589   // Compute luminance for whole palette
3590   // Take the darkest as black, the brightest white
3591   for(i = 0; i < 256; i++)
3592   {
3593     RGB_to_HSL(palette[i].R, palette[i].G, palette[i].B, &h, &s[i], &l[i]);
3594     // Another formula for lightness, in 0-255 range
3595     //l[i]=Perceptual_lightness(&palette[i])/4062/255;
3596     if (l[i] > max_l)
3597     {
3598       max_l = l[i];
3599       MC_White = i;
3600     }
3601   }
3602   for(i = 0; i < 256; i++)
3603   {
3604     if (l[i] < min_l && i!=MC_White)
3605     {
3606       min_l = l[i];
3607       MC_Black = i;
3608     }
3609   }
3610   // Alter the S values according to the L range - this is for the future
3611   // comparisons, so that highly variable saturation doesn't weigh
3612   // too heavily when the the lightness is in a narrow range.
3613   for(i = 0; i < 256; i++)
3614   {
3615     s[i]=s[i]*(max_l-min_l)/255;
3616   }
3617   for(i = 0; i < 256; i++)
3618   {
3619     // Adjust (reduce) perceived saturation at both ends of L spectrum
3620     if (l[i]>192)
3621       s[i]=s[i]*(255-l[i])/64;
3622     else if (l[i]<64)
3623       s[i]=s[i]*l[i]/64;
3624   }
3625 
3626 
3627   // Find color nearest to min+2(max-min)/3
3628   // but at the same time we try to minimize the saturation so that the menu
3629   // still looks grey
3630   hi_l = min_l + 2*(max_l - min_l)/3;
3631 
3632   for (i = 0; i < 256; i++)
3633   {
3634     if ( abs(l[i] - hi_l) + s[i]/2 < delta_high && i!=MC_White && i!=MC_Black)
3635     {
3636       delta_high = abs(l[i] - hi_l) + s[i]/2;
3637       MC_Light = i;
3638     }
3639   }
3640 
3641   // Target "Dark color" is 2/3 between Light and Black
3642   low_l = ((int)l[MC_Light]*2+l[MC_Black])/3;
3643   for (i = 0; i < 256; i++)
3644   {
3645     if ( abs((int)l[i] - low_l) + s[i]/6 < delta_low && i!=MC_White && i!=MC_Black && i!=MC_Light)
3646     {
3647       delta_low = abs((int)l[i] - low_l)+ s[i]/6;
3648       MC_Dark = i;
3649     }
3650   }
3651 
3652 
3653   //if (l[MC_Light]<l[MC_Dark])
3654   //{
3655   //  // Not sure if that can happen, but just in case:
3656   //  SWAP_BYTES(MC_Light, MC_Dark)
3657   //}
3658 
3659   // Si deux des couleurs choisies ont le même index, c'est destructif car
3660   // on fait ensuite un remap de l'image. Donc on évite ce problème (un
3661   // peu brutalement)
3662   // On commence par déplacer les gris, comme ça on a plus de chances de garder au moins
3663   // le blanc et le noir
3664   //while (MC_Dark == MC_Light || MC_Dark == MC_White || MC_Black == MC_Dark || Same_color(palette, MC_Dark, MC_White)) MC_Dark--;
3665   //while (MC_White == MC_Light || MC_Dark == MC_Light || MC_Black == MC_Light || Same_color(palette, MC_Light, MC_Black)) MC_Light--;
3666   //while (MC_White == MC_Light || MC_Dark == MC_White || MC_Black == MC_White) MC_White--;
3667 
3668   // On cherche une couleur de transparence différente des 4 autres.
3669   for (MC_Trans=0; ((MC_Trans==MC_Black) || (MC_Trans==MC_Dark) ||
3670                    (MC_Trans==MC_Light) || (MC_Trans==MC_White)); MC_Trans++);
3671 
3672   if (Same_color(palette, MC_Black, MC_Dark))
3673     MC_OnBlack=MC_Light;
3674   else
3675     MC_OnBlack=MC_Dark;
3676 
3677   if (Same_color(palette, MC_White, MC_Light))
3678   {
3679     MC_Window=MC_Dark;
3680     MC_Darker=MC_Black;
3681   }
3682   else
3683   {
3684     MC_Window=MC_Light;
3685     MC_Darker=MC_Dark;
3686   }
3687   MC_Lighter=MC_White;
3688 
3689   Remap_menu_sprites(palette);
3690 }
3691 
3692 /// Remap all menu data when the palette changes or a new skin is loaded
Remap_menu_sprites(const T_Components * palette)3693 static void Remap_menu_sprites(const T_Components * palette)
3694 {
3695   int i, j, k, l;
3696 
3697   compute_xor_table(palette);
3698   if ( (MC_Light!=Old_light)
3699     || (MC_Dark!=Old_dark)
3700     || (MC_White!=Old_white)
3701     || (MC_Black !=Old_black )
3702     || (MC_Trans!=Old_trans) )
3703   {
3704     // Mouse cursor sprites
3705     for (k=0; k<NB_CURSOR_SPRITES; k++)
3706       for (j=0; j<CURSOR_SPRITE_HEIGHT; j++)
3707         for (i=0; i<CURSOR_SPRITE_WIDTH; i++)
3708           Remap_pixel(&Gfx->Cursor_sprite[k][j][i]);
3709     // Main menu bar
3710     for (k=0; k<3; k++)
3711       for (j=0; j<Menu_bars[MENUBAR_TOOLS].Height; j++)
3712         for (i=0; i<Menu_bars[MENUBAR_TOOLS].Skin_width; i++)
3713           Remap_pixel(&Gfx->Menu_block[k][j][i]);
3714     // Menu sprites
3715     for (l=0; l<2; l++)
3716       for (k=0; k<NB_MENU_SPRITES; k++)
3717         for (j=0; j<MENU_SPRITE_HEIGHT; j++)
3718           for (i=0; i<MENU_SPRITE_WIDTH; i++)
3719             Remap_pixel(&Gfx->Menu_sprite[l][k][j][i]);
3720     // Effects sprites
3721     for (k=0; k<NB_EFFECTS_SPRITES; k++)
3722       for (j=0; j<EFFECT_SPRITE_HEIGHT; j++)
3723         for (i=0; i<EFFECT_SPRITE_WIDTH; i++)
3724           Remap_pixel(&Gfx->Effect_sprite[k][j][i]);
3725     // Layers buttons
3726     for (l=0; l<3; l++)
3727       for (k=0; k<16; k++)
3728         for (j=0; j<LAYER_SPRITE_HEIGHT; j++)
3729           for (i=0; i<LAYER_SPRITE_WIDTH; i++)
3730             Remap_pixel(&Gfx->Layer_sprite[l][k][j][i]);
3731 
3732     // Status bar
3733     for (k=0; k<3; k++)
3734       for (j=0; j<Menu_bars[MENUBAR_STATUS].Height; j++)
3735         for (i=0; i<Menu_bars[MENUBAR_STATUS].Skin_width; i++)
3736           Remap_pixel(&Gfx->Statusbar_block[k][j][i]);
3737     // Layer bar
3738     for (k=0; k<3; k++)
3739       for (j=0; j<10; j++)
3740         for (i=0; i<144; i++)
3741           Remap_pixel(&Gfx->Layerbar_block[k][j][i]);
3742     // Anim bar
3743     for (k=0; k<3; k++)
3744       for (j=0; j<14; j++)
3745         for (i=0; i<236; i++)
3746           Remap_pixel(&Gfx->Animbar_block[k][j][i]);
3747     // Help fonts
3748     for (k=0; k<256; k++)
3749       for (j=0; j<8; j++)
3750         for (i=0; i<6; i++)
3751           Remap_pixel(&Gfx->Help_font_norm[k][i][j]);
3752     for (k=0; k<256; k++)
3753       for (j=0; j<8; j++)
3754         for (i=0; i<6; i++)
3755           Remap_pixel(&Gfx->Bold_font[k][i][j]);
3756     for (k=0; k<64; k++)
3757       for (j=0; j<8; j++)
3758         for (i=0; i<6; i++)
3759           Remap_pixel(&Gfx->Help_font_t1[k][i][j]);
3760     for (k=0; k<64; k++)
3761       for (j=0; j<8; j++)
3762         for (i=0; i<6; i++)
3763           Remap_pixel(&Gfx->Help_font_t2[k][i][j]);
3764     for (k=0; k<64; k++)
3765       for (j=0; j<8; j++)
3766         for (i=0; i<6; i++)
3767           Remap_pixel(&Gfx->Help_font_t3[k][i][j]);
3768     for (k=0; k<64; k++)
3769       for (j=0; j<8; j++)
3770         for (i=0; i<6; i++)
3771           Remap_pixel(&Gfx->Help_font_t4[k][i][j]);
3772 
3773     // Drives and other misc. 8x8 icons
3774     for (k=0; k<NB_ICON_SPRITES; k++)
3775       for (j=0; j<ICON_SPRITE_HEIGHT; j++)
3776         for (i=0; i<ICON_SPRITE_WIDTH; i++)
3777           Remap_pixel(&Gfx->Icon_sprite[k][j][i]);
3778 
3779     // Skin preview
3780     for (j = 0; j < 173; j++)
3781       for (i = 0; i < 16; i++)
3782         Remap_pixel(&Gfx->Preview[i][j]);
3783   }
3784   Clear_border(MC_Black);
3785 }
3786