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