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 #include <string.h>
21 #ifndef _MSC_VER
22 #include <strings.h>
23 #endif
24 #include <stdlib.h>
25 #include <math.h>
26 #include "struct.h"
27 #include "global.h"
28 #include "errors.h"
29 #include "buttons.h"
30 #include "engine.h"
31 #include "misc.h"
32 #include "osdep.h"
33 #include "keyboard.h"
34 #include "screen.h"
35 #include "windows.h"
36 #include "palette.h"
37 #include "input.h"
38 #include "graph.h"
39 #include "pages.h"
40 
41 ///Count used palette indexes in the whole picture
42 ///Return the total number of different colors
43 ///Fill in "usage" with the count for each color
Count_used_colors(dword * usage)44 word Count_used_colors(dword* usage)
45 {
46   int nb_pixels = 0;
47   byte* current_pixel;
48   byte color;
49   word nb_colors = 0;
50   int i;
51   int layer;
52 
53   for (i = 0; i < 256; i++) usage[i]=0;
54 
55   // Compute total number of pixels in the picture
56   nb_pixels = Main.image_height * Main.image_width;
57 
58   // For each layer
59   for (layer = 0; layer < Main.backups->Pages->Nb_layers; layer++)
60   {
61     current_pixel = Main.backups->Pages->Image[layer].Pixels;
62     // For each pixel in picture
63     for (i = 0; i < nb_pixels; i++)
64     {
65       color=*current_pixel; // get color in picture for this pixel
66 
67       usage[color]++; // add it to the counter
68 
69       // go to next pixel
70       current_pixel++;
71     }
72   }
73 
74   // count the total number of unique used colors
75   for (i = 0; i < 256; i++)
76   {
77     if (usage[i]!=0)
78       nb_colors++;
79   }
80 
81   return nb_colors;
82 }
83 
84 /// Same as ::Count_used_colors, but use a block screen memory instead of
85 /// picture data. Used to count colors in the loading screen.
Count_used_colors_screen_area(dword * usage,word start_x,word start_y,word width,word height)86 word Count_used_colors_screen_area(dword* usage, word start_x, word start_y,
87   word width, word height)
88 {
89   byte color;
90   word x, y;
91   word nb_colors = 0;
92   int i;
93 
94   // Init usage table
95   for (i = 0; i < 256; i++) usage[i]=0;
96 
97   // For each pixel in screen area
98   for (y = 0; y < height; y++)
99   {
100     for (x = 0; x < width; x++)
101     {
102       // Get color in screen memory
103       //color=*(Screen_pixels+((start_x + x)+(start_y + y) * Screen_width
104       //  * Pixel_height) * Pixel_width);
105       color = Get_Screen_pixel(start_x + x, start_y + y);
106       usage[color]++; //Un point de plus pour cette couleur
107     }
108   }
109   //On va maintenant compter dans la table les couleurs utilisées:
110   for (i = 0; i < 256; i++)
111   {
112     if (usage[i]!=0)
113       nb_colors++;
114   }
115   return nb_colors;
116 }
117 
118 
119 /// Same as ::Count_used_colors, but for a given rectangle in the picture only.
120 /// Used bu the C64 block constraint checker.
Count_used_colors_area(dword * usage,word start_x,word start_y,word width,word height)121 word Count_used_colors_area(dword* usage, word start_x, word start_y,
122   word width, word height)
123 {
124   byte color;
125   word x, y;
126   word nb_colors = 0;
127   int i;
128 
129   // Init usage table
130   for (i = 0; i < 256; i++) usage[i]=0;
131 
132   // On parcourt l'écran courant pour compter les utilisations des couleurs
133   for (y = 0; y < height; y++)
134   {
135     for (x = 0; x < width; x++)
136     {
137       // Get color from picture
138       color=*(Main_screen+((start_x + x)+(start_y + y)*Main.image_width));
139       usage[color]++; //Un point de plus pour cette couleur
140     }
141   }
142 
143   //On va maintenant compter dans la table les couleurs utilisées:
144   for (i = 0; i < 256; i++)
145   {
146     if (usage[i]!=0)
147       nb_colors++;
148   }
149   return nb_colors;
150 }
151 
152 
153 // Backup of the currently displayed palette.
154 // It is not always Main_palette ! (for example during a preview)
155 // external code must not modify this array but use Set_palette() / Set_color()
156 // Get_current_palette() offers a READ-ONLY access.
157 static T_Palette Current_palette;
158 
Get_current_palette(void)159 const T_Components * Get_current_palette(void)
160 {
161   return Current_palette;
162 }
163 
Set_palette(T_Palette palette)164 void Set_palette(T_Palette palette)
165 {
166   int i;
167 
168   memcpy(Current_palette, palette, sizeof(T_Palette));
169   for(i=0;i<256;i++)
170   {
171     palette[i].R = Round_palette_component(palette[i].R);
172     palette[i].G = Round_palette_component(palette[i].G);
173     palette[i].B = Round_palette_component(palette[i].B);
174   }
175   GFX2_SetPalette(palette, 0, 256);
176 }
177 
Set_color(byte color,byte red,byte green,byte blue)178 void Set_color(byte color, byte red, byte green, byte blue)
179 {
180   Current_palette[color].R = red;
181   Current_palette[color].G = green;
182   Current_palette[color].B = blue;
183   GFX2_SetPalette(Current_palette + color, color, 1);
184 }
185 
Wait_end_of_click(void)186 void Wait_end_of_click(void)
187 {
188   // On désactive tous les raccourcis clavier
189 
190   while(Mouse_K)
191     Get_input(20);
192 }
193 
Clear_current_image_with_stencil(byte color,byte * stencil)194 void Clear_current_image_with_stencil(byte color, byte * stencil)
195   //Effacer l'image courante avec une certaine couleur en mode Stencil
196 {
197   int nb_pixels=0; //ECX
198   //al=color
199   //edi=Screen_pixels
200   byte* pixel=Main.backups->Pages->Image[Main.current_layer].Pixels;
201   int i;
202 
203   nb_pixels=Main.image_height*Main.image_width;
204 
205   for(i=0;i<nb_pixels;i++)
206   {
207     if (stencil[*pixel]==0)
208       *pixel=color;
209     pixel++;
210   }
211 }
212 
Clear_current_image(byte color)213 void Clear_current_image(byte color)
214   // Effacer l'image courante avec une certaine couleur
215 {
216   memset(
217     Main.backups->Pages->Image[Main.current_layer].Pixels,
218     color ,
219     Main.image_width * Main.image_height
220     );
221 }
222 
Init_chrono(dword delay)223 void Init_chrono(dword delay)
224   // Démarrer le chrono
225 {
226   Timer_delay = delay;
227   Timer_start = GFX2_GetTicks()/55;
228   return;
229 }
230 
Pixel_in_brush(word x,word y,byte color)231 void Pixel_in_brush (word x, word y, byte color)
232 {
233   *(Brush + y * Brush_width + x)=color;
234 }
235 
Read_pixel_from_brush(word x,word y)236 byte Read_pixel_from_brush (word x, word y)
237 {
238   return *(Brush + y * Brush_width + x);
239 }
240 
Copy_part_of_image_to_another(byte * source,word source_x,word source_y,word width,word height,word source_width,byte * dest,word dest_x,word dest_y,word destination_width)241 void Copy_part_of_image_to_another(byte * source,word source_x,word source_y,word width,word height,word source_width,byte * dest,word dest_x,word dest_y,word destination_width)
242 {
243   // ESI = adresse de la source en (S_Pox_X,source_y)
244   byte* esi = source + source_y * source_width + source_x;
245 
246   // EDI = adresse de la destination (dest_x,dest_y)
247   byte* edi = dest + dest_y * destination_width + dest_x;
248 
249   int line;
250 
251   // Pour chaque ligne
252   for (line=0;line < height; line++)
253   {
254     memcpy(edi,esi,width);
255 
256     // Passe à la ligne suivante
257     esi+=source_width;
258     edi+=destination_width;
259   }
260 }
261 
Read_pixel_from_spare_screen(word x,word y)262 byte Read_pixel_from_spare_screen(word x,word y)
263 {
264 //  return *(Spare_screen+y*Spare.image_width+x);
265 
266   // Clipping is required as this can be called with coordinates from main image
267   // (can be a bigger or smaller image)
268   if (x>=Spare.image_width || y>=Spare.image_height)
269     return Spare.backups->Pages->Transparent_color;
270   if (Spare.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
271   {
272     return *(Spare.backups->Pages->Image[Spare.current_layer].Pixels + y*Spare.image_width + x);
273   }
274   else
275   {
276     return *(Spare.visible_image.Image + y*Spare.image_width + x);
277   }
278 }
279 
Rotate_90_deg_lowlevel(byte * source,byte * dest,short width,short height)280 void Rotate_90_deg_lowlevel(byte * source, byte * dest, short width, short height)
281 {
282   word x,y;
283 
284   for(y=0;y<height;y++)
285   {
286     for(x=0;x<width;x++)
287     {
288       *(dest+height*(width-1-x)+y)=*source;
289       source++;
290     }
291   }
292 }
293 
Rotate_270_deg_lowlevel(byte * source,byte * dest,short width,short height)294 void Rotate_270_deg_lowlevel(byte * source, byte * dest, short width, short height)
295 {
296   word x,y;
297 
298   for(y=0;y<height;y++)
299   {
300     for(x=0;x<width;x++)
301     {
302       *(dest+(height-1-y)+x*height)=*source;
303       source++;
304     }
305   }
306 }
307 
308 // Replace une couleur par une autre dans un buffer
309 
Remap_general_lowlevel(byte * conversion_table,byte * in_buffer,byte * out_buffer,short width,short height,short buffer_width)310 void Remap_general_lowlevel(byte * conversion_table,byte * in_buffer, byte *out_buffer,short width,short height,short buffer_width)
311 {
312   int dx,cx;
313 
314   // Pour chaque ligne
315   for(dx=height;dx>0;dx--)
316   {
317     // Pour chaque pixel
318     for(cx=width;cx>0;cx--)
319     {
320       *out_buffer = conversion_table[*in_buffer];
321       in_buffer++;
322       out_buffer++;
323     }
324     in_buffer += buffer_width-width;
325     out_buffer += buffer_width-width;
326   }
327 }
328 
Copy_image_to_brush(short start_x,short start_y,short Brush_width,short Brush_height,word image_width)329 void Copy_image_to_brush(short start_x,short start_y,short Brush_width,short Brush_height,word image_width)
330 {
331   byte* src=start_y*image_width+start_x+Main.backups->Pages->Image[Main.current_layer].Pixels; //Adr départ image (ESI)
332   byte* dest=Brush_original_pixels; //Adr dest brosse (EDI)
333   int dx;
334 
335   for (dx=Brush_height;dx!=0;dx--)
336     //Pour chaque ligne
337   {
338 
339     // On fait une copie de la ligne
340     memcpy(dest,src,Brush_width);
341 
342     // On passe à la ligne suivante
343     src+=image_width;
344     dest+=Brush_width;
345   }
346 
347 }
348 
Read_pixel_from_feedback_screen(word x,word y)349 byte Read_pixel_from_feedback_screen (word x,word y)
350 {
351   return *(FX_feedback_screen+y*Main.image_width+x);
352 }
353 
Round_div(dword numerator,dword divisor)354 dword Round_div(dword numerator,dword divisor)
355 {
356   return numerator/divisor;
357 }
358 
Effect_sieve(word x,word y)359 byte Effect_sieve(word x,word y)
360 {
361   return Sieve[x % Sieve_width][y % Sieve_height];
362 }
363 
Replace_colors_within_limits(byte * replace_table)364 void Replace_colors_within_limits(byte * replace_table)
365 {
366   int y;
367   int x;
368   byte* pixel;
369 
370   // Pour chaque ligne :
371   for(y = Limit_top;y <= Limit_bottom; y++)
372   {
373     // Pour chaque pixel sur la ligne :
374     for (x = Limit_left;x <= Limit_right;x ++)
375     {
376       pixel = Main.backups->Pages->Image[Main.current_layer].Pixels+y*Main.image_width+x;
377       *pixel = replace_table[*pixel];
378     }
379   }
380 }
381 
Read_pixel_from_backup_screen(word x,word y)382 byte Read_pixel_from_backup_screen (word x,word y)
383 {
384   return *(Screen_backup + x + Main.image_width * y);
385 }
386 
Effect_interpolated_colorize(word x,word y,byte color)387 byte Effect_interpolated_colorize  (word x,word y,byte color)
388 {
389   // factor_a = 256*(100-Colorize_opacity)/100
390   // factor_b = 256*(    Colorize_opacity)/100
391   //
392   // (Couleur_dessous*factor_a+color*facteur_B)/256
393   //
394 
395   // On place dans ESI 3*Couleur_dessous ( = position de cette couleur dans la
396   // palette des teintes) et dans EDI, 3*color.
397   byte color_under = Read_pixel_from_feedback_screen(x,y);
398   byte blue_under=Main.palette[color_under].B;
399   byte blue=Main.palette[color].B;
400   byte green_under=Main.palette[color_under].G;
401   byte green=Main.palette[color].G;
402   byte red_under=Main.palette[color_under].R;
403   byte red=Main.palette[color].R;
404 
405   // On récupère les 3 composantes RVB
406 
407   // blue
408   blue = (Factors_inv_table[blue]
409       + Factors_table[blue_under]) / 256;
410   green = (Factors_inv_table[green]
411       + Factors_table[green_under]) / 256;
412   red = (Factors_inv_table[red]
413       + Factors_table[red_under]) / 256;
414   return Best_color(red,green,blue);
415 
416 }
417 
Effect_additive_colorize(word x,word y,byte color)418 byte Effect_additive_colorize    (word x,word y,byte color)
419 {
420   byte color_under = Read_pixel_from_feedback_screen(x,y);
421   byte blue_under=Main.palette[color_under].B;
422   byte green_under=Main.palette[color_under].G;
423   byte red_under=Main.palette[color_under].R;
424   byte blue=Main.palette[color].B;
425   byte green=Main.palette[color].G;
426   byte red=Main.palette[color].R;
427 
428   return Best_color(
429     red>red_under?red:red_under,
430     green>green_under?green:green_under,
431     blue>blue_under?blue:blue_under);
432 }
433 
Effect_substractive_colorize(word x,word y,byte color)434 byte Effect_substractive_colorize(word x,word y,byte color)
435 {
436   byte color_under = Read_pixel_from_feedback_screen(x,y);
437   byte blue_under=Main.palette[color_under].B;
438   byte green_under=Main.palette[color_under].G;
439   byte red_under=Main.palette[color_under].R;
440   byte blue=Main.palette[color].B;
441   byte green=Main.palette[color].G;
442   byte red=Main.palette[color].R;
443 
444   return Best_color(
445     red<red_under?red:red_under,
446     green<green_under?green:green_under,
447     blue<blue_under?blue:blue_under);
448 }
449 
Effect_alpha_colorize(word x,word y,byte color)450 byte Effect_alpha_colorize    (word x,word y,byte color)
451 {
452   byte color_under = Read_pixel_from_feedback_screen(x,y);
453   byte blue_under=Main.palette[color_under].B;
454   byte green_under=Main.palette[color_under].G;
455   byte red_under=Main.palette[color_under].R;
456   int factor=(Main.palette[color].R*76 +
457     Main.palette[color].G*151 +
458     Main.palette[color].B*28)/255;
459 
460   return Best_color(
461     (Main.palette[Fore_color].R*factor + red_under*(255-factor))/255,
462     (Main.palette[Fore_color].G*factor + green_under*(255-factor))/255,
463     (Main.palette[Fore_color].B*factor + blue_under*(255-factor))/255);
464 }
465 
Check_timer(void)466 void Check_timer(void)
467 {
468   if((GFX2_GetTicks()/55)-Timer_delay>Timer_start) Timer_state=1;
469 }
470 
Flip_Y_lowlevel(byte * src,short width,short height)471 void Flip_Y_lowlevel(byte *src, short width, short height)
472 {
473   // ESI pointe sur la partie haute de la brosse
474   // EDI sur la partie basse
475   byte* ESI = src ;
476   byte* EDI = src + (height - 1) *width;
477   byte tmp;
478   word cx;
479 
480   while(ESI < EDI)
481   {
482     // Il faut inverser les lignes pointées par ESI et
483     // EDI ("Brush_width" octets en tout)
484 
485     for(cx = width;cx>0;cx--)
486     {
487       tmp = *ESI;
488       *ESI = *EDI;
489       *EDI = tmp;
490       ESI++;
491       EDI++;
492     }
493 
494     // On change de ligne :
495     // ESI pointe déjà sur le début de la ligne suivante
496     // EDI pointe sur la fin de la ligne en cours, il
497     // doit pointer sur le début de la précédente...
498     EDI -= 2 * width; // On recule de 2 lignes
499   }
500 }
501 
Flip_X_lowlevel(byte * src,short width,short height)502 void Flip_X_lowlevel(byte *src, short width, short height)
503 {
504   // ESI pointe sur la partie gauche et EDI sur la partie
505   // droite
506   byte* ESI = src;
507   byte* EDI = src + width - 1;
508 
509   byte* line_start;
510   byte* line_end;
511   byte tmp;
512   word cx;
513 
514   while(ESI<EDI)
515   {
516     line_start = ESI;
517     line_end = EDI;
518 
519     // On échange par colonnes
520     for(cx=height;cx>0;cx--)
521     {
522       tmp=*ESI;
523       *ESI=*EDI;
524       *EDI=tmp;
525       EDI+=width;
526       ESI+=width;
527     }
528 
529     // On change de colonne
530     // ESI > colonne suivante
531     // EDI > colonne précédente
532     ESI = line_start + 1;
533     EDI = line_end - 1;
534   }
535 }
536 
537 // Rotate a pixel buffer 180º on itself.
Rotate_180_deg_lowlevel(byte * src,short width,short height)538 void Rotate_180_deg_lowlevel(byte *src, short width, short height)
539 {
540   // ESI pointe sur la partie supérieure de la brosse
541   // EDI pointe sur la partie basse
542   byte* ESI = src;
543   byte* EDI = src + height*width - 1;
544   // EDI pointe sur le dernier pixel de la derniere ligne
545   byte tmp;
546   word cx;
547 
548   // In case of odd height, the algorithm in this function would
549   // miss the middle line, so we do it this way:
550   if (height & 1)
551   {
552     Flip_X_lowlevel(src, width, height);
553     Flip_Y_lowlevel(src, width, height);
554     return;
555   }
556 
557 
558   while(ESI < EDI)
559   {
560     // On échange les deux lignes pointées par EDI et
561     // ESI (Brush_width octets)
562     // En même temps, on échange les pixels, donc EDI
563     // pointe sur la FIN de sa ligne
564 
565     for(cx=width;cx>0;cx--)
566     {
567       tmp = *ESI;
568       *ESI = *EDI;
569       *EDI = tmp;
570 
571       EDI--; // Attention ici on recule !
572       ESI++;
573     }
574   }
575 }
576 
Rescale(byte * src_buffer,short src_width,short src_height,byte * dst_buffer,short dst_width,short dst_height,short x_flipped,short y_flipped)577 void Rescale(byte *src_buffer, short src_width, short src_height, byte *dst_buffer, short dst_width, short dst_height, short x_flipped, short y_flipped)
578 {
579   int    offset,line,column;
580 
581   int    x_pos_in_brush;   // Position courante dans l'ancienne brosse
582   int    y_pos_in_brush;
583   int    initial_x_pos;       // Position X de début de parcours de ligne
584   int    initial_y_pos;       // Position Y de début de parcours de ligne
585 
586   int	delta_x, delta_y;
587 
588   offset=0;
589 
590   // Calcul de la valeur initiale de y_pos:
591   if (y_flipped) {
592     initial_y_pos=(src_height)-1; // Inversion en Y de la brosse
593 	delta_y = -1 * src_height;
594   } else {
595     initial_y_pos=0;                // Pas d'inversion en Y de la brosse
596 	delta_y = src_height;
597   }
598 
599   // Calcul de la valeur initiale de x_pos pour chaque ligne:
600   if (x_flipped) {
601     initial_x_pos = (src_width)-1; // Inversion en X de la brosse
602 	delta_x = -1 * src_width;
603   } else {
604     initial_x_pos = 0;                // Pas d'inversion en X de la brosse
605 	delta_x = src_width;
606   }
607 
608   // Pour chaque ligne
609   for (line=0;line<dst_height;line++)
610   {
611     // On passe à la ligne de brosse suivante:
612     y_pos_in_brush = initial_y_pos + line * delta_y / dst_height;
613 
614     // Pour chaque colonne:
615     for (column=0;column<dst_width;column++)
616     {
617       // On passe à la colonne de brosse suivante:
618       x_pos_in_brush = initial_x_pos + column * delta_x / dst_width;
619       // On copie le pixel:
620       dst_buffer[offset]=*(src_buffer + x_pos_in_brush + y_pos_in_brush * src_width);
621       // On passe au pixel suivant de la nouvelle brosse:
622       offset++;
623     }
624   }
625 }
626 
627 
Scroll_picture(byte * main_src,byte * main_dest,short x_offset,short y_offset)628 void Scroll_picture(byte * main_src, byte * main_dest, short x_offset,short y_offset)
629 {
630   byte* src = main_src; //source de la copie
631   byte* dest = main_dest + y_offset * Main.image_width + x_offset;
632   const word length = Main.image_width - x_offset; // Nombre de pixels à copier à droite
633   word y;
634   for(y = Main.image_height - y_offset;y>0;y--)
635   {
636     // Pour chaque ligne
637     memcpy(dest,src,length);
638     memcpy(dest - x_offset,src+length,x_offset);
639 
640     // On passe à la ligne suivante
641     dest += Main.image_width;
642     src += Main.image_width;
643   }
644 
645   // On vient de faire le traitement pour otutes les lignes au-dessous de y_offset
646   // Maintenant on traite celles au dessus
647   dest = x_offset + main_dest;
648   for(y = y_offset;y>0;y--)
649   {
650     memcpy(dest,src,length);
651     memcpy(dest - x_offset,src+length,x_offset);
652 
653     dest += Main.image_width;
654     src += Main.image_width;
655   }
656 
657   Update_rect(0,0,0,0);
658 }
659 
Zoom_a_line(byte * original_line,byte * zoomed_line,word factor,word width)660 void Zoom_a_line(byte* original_line, byte* zoomed_line,
661     word factor, word width
662     )
663 {
664   byte color;
665   word x;
666 
667   // Pour chaque pixel
668   for(x=0;x<width;x++){
669     color = *original_line;
670 
671     memset(zoomed_line,color,factor);
672     zoomed_line+=factor;
673 
674     original_line++;
675   }
676 }
677 
678 /*############################################################################*/
679 
680 // Arrondir un nombre réel à la valeur entière la plus proche
681 // TODO : this should probably be replaced with round() from C99...
Round(float value)682 short Round(float value)
683 {
684   short temp=value;
685 
686   if (value>=0)
687   { if ((value-temp)>= 0.5) temp++; }
688   else
689   { if ((value-temp)<=-0.5) temp--; }
690 
691   return temp;
692 }
693 
694 
695 // Arrondir le résultat d'une division à la valeur entière supérieure
Round_div_max(short numerator,short divisor)696 short Round_div_max(short numerator,short divisor)
697 {
698   if (!(numerator % divisor))
699     return (numerator/divisor);
700   else
701     return (numerator/divisor)+1;
702 }
703 
704 
705 // Retourne le minimum entre deux nombres
Min(int a,int b)706 int Min(int a,int b)
707 {
708   return (a<b)?a:b;
709 }
710 
711 
712 // Retourne le maximum entre deux nombres
Max(int a,int b)713 int Max(int a,int b)
714 {
715   return (a>b)?a:b;
716 }
717 
718 /* Round number n to d decimal points */
Fround(double n,unsigned d)719 double Fround(double n, unsigned d)
720 {
721   double exp;
722   exp = pow(10.0, d);
723   return floor(n * exp + 0.5) / exp;
724 }
725 
726 
727 // Fonction retournant le libellé d'une mode (ex: " 320x200")
Mode_label(int mode)728 const char * Mode_label(int mode)
729 {
730   static char str[24];
731   if (! Video_mode[mode].Fullscreen)
732     return "window";
733   sprintf(str, "%dx%d", Video_mode[mode].Width, Video_mode[mode].Height);
734 
735   return str;
736 }
737 
738 
739 // Trouve un mode video à partir d'une chaine: soit "window",
740 // soit de la forme "320x200"
741 // Renvoie -1 si la chaine n'est pas convertible
Convert_videomode_arg(const char * argument)742 int Convert_videomode_arg(const char *argument)
743 {
744   // Je suis paresseux alors je vais juste tester les libellés
745   int mode_index;
746   for (mode_index=0; mode_index<Nb_video_modes; mode_index++)
747     // Attention les vieilles fonctions de lecture .ini mettent tout en MAJUSCULE.
748     if (!strcasecmp(Mode_label(mode_index), argument) && (Video_mode[mode_index].State &128) ==0)
749       return mode_index;
750 
751   return -1;
752 }
753