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     Drawing functions and effects.
23 
24 */
25 
26 #include <string.h>
27 #include <stdlib.h>
28 #include <math.h>
29 
30 #include "global.h"
31 #include "struct.h"
32 #include "engine.h"
33 #include "buttons.h"
34 #include "pages.h"
35 #include "errors.h"
36 #include "screen.h"
37 #include "graph.h"
38 #include "misc.h"
39 #include "osdep.h"
40 #include "pxsimple.h"
41 #include "pxtall.h"
42 #include "pxwide.h"
43 #include "pxdouble.h"
44 #include "pxtriple.h"
45 #include "pxwide2.h"
46 #include "pxtall2.h"
47 #include "pxtall3.h"
48 #include "pxquad.h"
49 #include "windows.h"
50 #include "input.h"
51 #include "brush.h"
52 #include "tiles.h"
53 #if defined(USE_SDL) || defined(USE_SDL2)
54 #include "sdlscreen.h"
55 #endif
56 
57 #ifndef M_PI
58     #define M_PI 3.141592653589793238462643
59 #endif
60 
61 #ifndef MAX
62 #define MAX(a,b) (((a)>(b)) ? (a) : (b))
63 #endif
64 
65 // Generic pixel-drawing function.
66 static Func_pixel Pixel_figure;
67 
Set_Pixel_figure(Func_pixel func)68 void Set_Pixel_figure(Func_pixel func)
69 {
70   Pixel_figure = func;
71 }
72 
73 
74 typedef struct
75 {
76   long  vertical_radius_squared;
77   long  horizontal_radius_squared;
78   qword limit;
79 } T_Ellipse_limits;
80 
81 // Calcule les valeurs suivantes en fonction des deux paramètres:
82 //
83 // Ellipse_vertical_radius_squared
84 // Ellipse_horizontal_radius_squared
85 // Ellipse_Limit_High
86 // Ellipse_Limit_Low
Ellipse_compute_limites(short horizontal_radius,short vertical_radius,T_Ellipse_limits * Ellipse)87 static void Ellipse_compute_limites(short horizontal_radius,short vertical_radius, T_Ellipse_limits * Ellipse)
88 {
89   Ellipse->horizontal_radius_squared = (long)horizontal_radius * horizontal_radius;
90   Ellipse->vertical_radius_squared =   (long)vertical_radius * vertical_radius;
91   Ellipse->limit = (qword)Ellipse->horizontal_radius_squared * Ellipse->vertical_radius_squared;
92 }
93 
94 //   Indique si le pixel se trouvant à Ellipse_cursor_X pixels
95 // (Ellipse_cursor_X>0 = à droite, Ellipse_cursor_X<0 = à gauche) et à
96 // Ellipse_cursor_Y pixels (Ellipse_cursor_Y>0 = en bas,
97 // Ellipse_cursor_Y<0 = en haut) du centre se trouve dans l'ellipse en
98 // cours.
Pixel_in_ellipse(long x,long y,const T_Ellipse_limits * Ellipse)99 static byte Pixel_in_ellipse(long x, long y, const T_Ellipse_limits * Ellipse)
100 {
101   qword ediesi = (qword)x * x * Ellipse->vertical_radius_squared +
102     (qword)y * y * Ellipse->horizontal_radius_squared;
103   if((ediesi) <= Ellipse->limit) return 255;
104 
105   return 0;
106 }
107 
108 //   Indique si le pixel se trouvant à Circle_cursor_X pixels
109 // (Circle_cursor_X>0 = à droite, Circle_cursor_X<0 = à gauche) et à
110 // Circle_cursor_Y pixels (Circle_cursor_Y>0 = en bas,
111 // Circle_cursor_Y<0 = en haut) du centre se trouve dans le cercle en
112 // cours.
Pixel_in_circle(long x,long y,long limit)113 static byte Pixel_in_circle(long x, long y, long limit)
114 {
115   if((x * x + y * y) <= limit)
116     return 255;
117   return 0;
118 }
119 
120 /** Update the picture on screen, for the area passed in parameters.
121  *
122  * Takes into account the X/Y scrolling and zoom, and performs all safety checks so no updates will
123  * go outside the display area.
124  */
Update_part_of_screen(short x,short y,short width,short height)125 void Update_part_of_screen(short x, short y, short width, short height)
126 {
127   short effective_w, effective_h;
128   short effective_X;
129   short effective_Y;
130   short diff;
131 
132   // First make sure the zone is in forward direction (positive width/height)
133   if (width < 0)
134   {
135     x += width;
136     width = - width;
137   }
138 
139   if (height < 0)
140   {
141     y += height;
142     height = - height;
143   }
144 
145   // Round up to a multiple of 8 pixels, because some special modes (ZX, Thomson, ...) can change
146   // more pixels than expected (attribute clash)
147   width = ((width + (x & 7)) | 7) + 1;
148   height = ((height + (y & 7)) | 7) + 1;
149   x &= 0xFFF8;
150   y &= 0xFFF8;
151 
152   // Update "normal" view
153   diff = x-Main.offset_X;
154   if (diff<0)
155   {
156     effective_w = width + diff;
157     effective_X = 0;
158   }
159   else
160   {
161     effective_w = width;
162     effective_X = diff;
163   }
164   diff = y-Main.offset_Y;
165   if (diff<0)
166   {
167     effective_h = height + diff;
168     effective_Y = 0;
169   }
170   else
171   {
172     effective_h = height;
173     effective_Y = diff;
174   }
175 
176   // Clamp to actually visible area. All tools are normally constrained to this, but there are some
177   // exceptions:
178   // - Brush preview requests updates outside the visible screen,
179   // - ZX/Thomson constraints can lead to pixel changes outside the visible area.
180   if(Main.magnifier_mode && effective_X + effective_w > Main.separator_position)
181     effective_w = Main.separator_position - effective_X;
182   else if(effective_X + effective_w > Screen_width)
183     effective_w = Screen_width - effective_X;
184 
185   if(effective_Y + effective_h > Menu_Y)
186     effective_h = Menu_Y - effective_Y;
187 
188   /* (for debug purposes, highlight the rectangle that is updated)
189   SDL_Rect r;
190   r.x=effective_X;
191   r.y=effective_Y;
192   r.h=effective_h;
193   r.w=effective_w;
194   SDL_FillRect(Screen_SDL,&r,3);
195   */
196   Update_rect(effective_X,effective_Y,effective_w,effective_h);
197 
198   // Now update the "zoomed" part of the display
199   if(Main.magnifier_mode)
200   {
201     // Convert picture to zoomed-screen coordinates
202     effective_X = (x-Main.magnifier_offset_X)*Main.magnifier_factor;
203     effective_Y = (y-Main.magnifier_offset_Y)*Main.magnifier_factor;
204     effective_w = width * Main.magnifier_factor;
205     effective_h = height * Main.magnifier_factor;
206 
207     // Apply horizontal clipping
208     if (effective_X < 0)
209     {
210       effective_w+=effective_X;
211       if (effective_w<0)
212         return;
213 
214       effective_X = Main.separator_position + SEPARATOR_WIDTH*Menu_factor_X;
215     }
216     else
217       effective_X += Main.separator_position + SEPARATOR_WIDTH*Menu_factor_X;
218     diff = effective_X+effective_w-Min(Screen_width, Main.X_zoom+(Main.image_width-Main.magnifier_offset_X)*Main.magnifier_factor);
219     if (diff>0)
220     {
221       effective_w -=diff;
222       if (effective_w<0)
223         return;
224     }
225 
226 
227     // Vertical clipping
228     if (effective_Y < 0)
229     {
230       effective_h+=effective_Y;
231       if (effective_h<0)
232         return;
233       effective_Y = 0;
234     }
235     diff = effective_Y+effective_h-Min(Menu_Y, (Main.image_height-Main.magnifier_offset_Y)*Main.magnifier_factor);
236     if (diff>0)
237     {
238       effective_h -=diff;
239       if (effective_h<0)
240         return;
241     }
242 
243 
244  // Again, for debugging purposes, display the touched rectangle
245     /*SDL_Rect r;
246     r.x=effective_X;
247     r.y=effective_Y;
248     r.h=effective_h;
249     r.w=effective_w;
250     SDL_FillRect(Screen_SDL,&r,3);*/
251 
252     // When the grid is displayed in Tilemap mode, this tests if
253     // one edge of the grid has been touched :
254     // In this case, the whole magnified area requires a refreshed grid.
255     // This could be optimized further, but at the moment this seemed
256     // fast enough.
257     if (Show_grid && Main.tilemap_mode && (
258         x/Snap_width <(x+width )/Snap_width ||
259         y/Snap_height<(y+height)/Snap_height))
260     {
261       short w,h;
262 
263       w=Min(Screen_width-Main.X_zoom, (Main.image_width-Main.magnifier_offset_X)*Main.magnifier_factor);
264       h=Min(Menu_Y, (Main.image_height-Main.magnifier_offset_Y)*Main.magnifier_factor);
265 
266       Redraw_grid(Main.X_zoom,0,w,h);
267       Update_rect(Main.X_zoom,0,w,h);
268     }
269     else
270     {
271       Redraw_grid(effective_X,effective_Y,effective_w,effective_h);
272       Update_rect(effective_X,effective_Y,effective_w,effective_h);
273     }
274   }
275 }
276 
277 
278 
Transform_point(short x,short y,float cos_a,float sin_a,short * rx,short * ry)279 void Transform_point(short x, short y, float cos_a, float sin_a,
280                        short * rx, short * ry)
281 {
282   *rx=Round(((float)x*cos_a)+((float)y*sin_a));
283   *ry=Round(((float)y*cos_a)-((float)x*sin_a));
284 }
285 
286 
287 //--------------------- Initialisation d'un mode vidéo -----------------------
288 
Init_mode_video(int width,int height,int fullscreen,int pix_ratio)289 int Init_mode_video(int width, int height, int fullscreen, int pix_ratio)
290 {
291   int index;
292   int factor;
293   int pix_width;
294   int pix_height;
295   byte screen_changed;
296   byte pixels_changed;
297   int absolute_mouse_x=Mouse_X*Pixel_width;
298   int absolute_mouse_y=Mouse_Y*Pixel_height;
299   static int Wrong_resize;
300 
301 try_again:
302 
303   GFX2_Log(GFX2_DEBUG, "Init_mode_video(%d, %d, %d, %d)\n", width, height, fullscreen, pix_ratio);
304   switch (pix_ratio)
305   {
306       default:
307       case PIXEL_SIMPLE:
308           pix_width=1;
309           pix_height=1;
310       break;
311       case PIXEL_TALL:
312           pix_width=1;
313           pix_height=2;
314       break;
315       case PIXEL_WIDE:
316           pix_width=2;
317           pix_height=1;
318       break;
319       case PIXEL_DOUBLE:
320           pix_width=2;
321           pix_height=2;
322       break;
323       case PIXEL_TRIPLE:
324           pix_width=3;
325           pix_height=3;
326       break;
327       case PIXEL_WIDE2:
328           pix_width=4;
329           pix_height=2;
330       break;
331       case PIXEL_TALL2:
332           pix_width=2;
333           pix_height=4;
334       break;
335       case PIXEL_TALL3:
336           pix_width=3;
337           pix_height=4;
338       break;
339       case PIXEL_QUAD:
340           pix_width=4;
341           pix_height=4;
342       break;
343   }
344 
345   screen_changed = (Screen_width*Pixel_width!=width ||
346                     Screen_height*Pixel_height!=height ||
347                     Video_mode[Current_resolution].Fullscreen != fullscreen);
348 
349   // Valeurs raisonnables: minimum 320x200
350   if (!fullscreen)
351   {
352     if (Wrong_resize>20 && (width < 320*pix_width || height < 200*pix_height))
353     {
354       if(pix_ratio != PIXEL_SIMPLE) {
355         pix_ratio = PIXEL_SIMPLE;
356         Verbose_message("Error!", "Your WM is forcing GrafX2 to resize to something "
357           "smaller than the minimal resolution.\n"
358           "GrafX2 switched to a smaller\npixel scaler to avoid problems                     ");
359         goto try_again;
360       }
361     }
362 
363     if (width > 320*pix_width && height > 200*pix_height)
364       Wrong_resize = 0;
365 
366     if (width < 320*pix_width)
367     {
368         width = 320*pix_width;
369         screen_changed=1;
370         Wrong_resize++;
371     }
372     if (height < 200*pix_height)
373     {
374         height = 200*pix_height;
375         screen_changed=1;
376         Wrong_resize++;
377     }
378     Video_mode[0].Width = width;
379     Video_mode[0].Height = height;
380 
381   }
382   else
383   {
384     if (width < 320*pix_width || height < 200*pix_height)
385       return 1;
386   }
387   // La largeur doit être un multiple de 4
388   #ifdef __amigaos4__
389       // On AmigaOS the systems adds some more constraints on that ...
390       width = (width + 15) & 0xFFFFFFF0;
391   #else
392       //width = (width + 3 ) & 0xFFFFFFFC;
393   #endif
394 
395   pixels_changed = (Pixel_ratio!=pix_ratio);
396 
397   if (!screen_changed && !pixels_changed)
398   {
399     Resize_width=0;
400     Resize_height=0;
401     return 0;
402   }
403 
404   if (screen_changed)
405   {
406     GFX2_Set_mode(&width, &height, fullscreen);
407   }
408 
409   if (screen_changed || pixels_changed)
410   {
411     Pixel_ratio=pix_ratio;
412     Pixel_width=pix_width;
413     Pixel_height=pix_height;
414     switch (Pixel_ratio)
415     {
416         default:
417         case PIXEL_SIMPLE:
418 #define Display_line_on_screen_fast_simple Display_line_on_screen_simple
419 #define SETPIXEL(x) \
420             Pixel = Pixel_##x ; \
421             Read_pixel= Read_pixel_##x ; \
422             Display_screen = Display_part_of_screen_##x ; \
423             Block = Block_##x ; \
424             Pixel_preview_normal = Pixel_preview_normal_##x ; \
425             Pixel_preview_magnifier = Pixel_preview_magnifier_##x ; \
426             Horizontal_XOR_line = Horizontal_XOR_line_##x ; \
427             Vertical_XOR_line = Vertical_XOR_line_##x ; \
428             Display_brush_color = Display_brush_color_##x ; \
429             Display_brush_mono = Display_brush_mono_##x ; \
430             Clear_brush = Clear_brush_##x ; \
431             Remap_screen = Remap_screen_##x ; \
432             Display_line = Display_line_on_screen_##x ; \
433             Display_line_fast = Display_line_on_screen_fast_##x ; \
434             Read_line = Read_line_screen_##x ; \
435             Display_zoomed_screen = Display_part_of_screen_scaled_##x ; \
436             Display_brush_color_zoom = Display_brush_color_zoom_##x ; \
437             Display_brush_mono_zoom = Display_brush_mono_zoom_##x ; \
438             Clear_brush_scaled = Clear_brush_scaled_##x ; \
439             Display_brush = Display_brush_##x ;
440 			SETPIXEL(simple)
441         break;
442         case PIXEL_TALL:
443 #define Display_line_on_screen_fast_tall Display_line_on_screen_tall
444 			SETPIXEL(tall)
445         break;
446         case PIXEL_WIDE:
447 			SETPIXEL(wide)
448         break;
449         case PIXEL_DOUBLE:
450 			SETPIXEL(double)
451         break;
452         case PIXEL_TRIPLE:
453 			SETPIXEL(triple)
454         break;
455         case PIXEL_WIDE2:
456 			SETPIXEL(wide2)
457         break;
458         case PIXEL_TALL2:
459 			SETPIXEL(tall2)
460         break;
461         case PIXEL_TALL3:
462 			SETPIXEL(tall3)
463         break;
464         case PIXEL_QUAD:
465 			SETPIXEL(quad)
466         break;
467     }
468   }
469   Screen_width = width/Pixel_width;
470   Screen_height = height/Pixel_height;
471 
472   Clear_border(MC_Black); // Requires up-to-date Screen_* and Pixel_*
473 
474   // Set menu size (software zoom)
475   if (Screen_width/320 > Screen_height/200)
476     factor=Screen_height/200;
477   else
478     factor=Screen_width/320;
479 
480   switch (Config.Ratio)
481   {
482     case 1: // Always the biggest possible
483       Menu_factor_X=factor;
484       Menu_factor_Y=factor;
485       break;
486     case 2: // Only keep the aspect ratio
487       Menu_factor_X=factor-1;
488       if (Menu_factor_X<1) Menu_factor_X=1;
489       Menu_factor_Y=factor-1;
490       if (Menu_factor_Y<1) Menu_factor_Y=1;
491       break;
492     case 0: // Always smallest possible
493       Menu_factor_X=1;
494       Menu_factor_Y=1;
495       break;
496     default: // Stay below some reasonable size
497       if (factor>Max(Pixel_width,Pixel_height))
498         factor/=Max(Pixel_width,Pixel_height);
499       Menu_factor_X=Min(factor,abs(Config.Ratio));
500       Menu_factor_Y=Min(factor,abs(Config.Ratio));
501   }
502   if (Pixel_height>Pixel_width && Screen_width>=Menu_factor_X*2*320)
503     Menu_factor_X*=2;
504   else if (Pixel_width>Pixel_height && Screen_height>=Menu_factor_Y*2*200)
505     Menu_factor_Y*=2;
506 
507   free(Horizontal_line_buffer);
508   Horizontal_line_buffer=(byte *)malloc(Pixel_width *
509     ((Screen_width>Main.image_width)?Screen_width:Main.image_width));
510 
511   Set_palette(Main.palette);
512 
513   Current_resolution=0;
514   if (fullscreen)
515   {
516     for (index=1; index<Nb_video_modes; index++)
517     {
518       if (Video_mode[index].Width/Pixel_width==Screen_width &&
519           Video_mode[index].Height/Pixel_height==Screen_height)
520       {
521         Current_resolution=index;
522         break;
523       }
524     }
525   }
526 
527   Change_palette_cells();
528 
529   Menu_Y = Screen_height;
530   if (Menu_is_visible)
531     Menu_Y -= Menu_height * Menu_factor_Y;
532   Menu_status_Y = Screen_height-(Menu_factor_Y<<3);
533 
534   Adjust_mouse_sensitivity(fullscreen);
535 
536   Mouse_X=absolute_mouse_x/Pixel_width;
537   if (Mouse_X>=Screen_width)
538     Mouse_X=Screen_width-1;
539   Mouse_Y=absolute_mouse_y/Pixel_height;
540   if (Mouse_Y>=Screen_height)
541     Mouse_Y=Screen_height-1;
542   if (fullscreen)
543     Set_mouse_position();
544 
545   Spare.offset_X=0; // |  Il faut penser à éviter les incohérences
546   Spare.offset_Y=0; // |- de décalage du brouillon par rapport à
547   Spare.magnifier_mode=0; // |  la résolution.
548 
549   if (Main.magnifier_mode)
550   {
551     Pixel_preview=Pixel_preview_magnifier;
552   }
553   else
554   {
555     Pixel_preview=Pixel_preview_normal;
556     // Recaler la vue (meme clipping que dans Scroll_screen())
557     if (Main.offset_X+Screen_width>Main.image_width)
558       Main.offset_X=Main.image_width-Screen_width;
559     if (Main.offset_X<0)
560       Main.offset_X=0;
561     if (Main.offset_Y+Menu_Y>Main.image_height)
562       Main.offset_Y=Main.image_height-Menu_Y;
563     if (Main.offset_Y<0)
564       Main.offset_Y=0;
565   }
566 
567   Compute_magnifier_data();
568   if (Main.magnifier_mode)
569     Position_screen_according_to_zoom();
570   Compute_limits();
571   Compute_paintbrush_coordinates();
572 
573   Resize_width=0;
574   Resize_height=0;
575   return 0;
576 }
577 
578 
579 
580   // -- Redimentionner l'image (nettoie l'écran virtuel) --
581 
Resize_image(word chosen_width,word chosen_height)582 void Resize_image(word chosen_width,word chosen_height)
583 {
584   word old_width=Main.image_width;
585   word old_height=Main.image_height;
586   int i;
587 
588   // +-+-+
589   // |C| |  A+B+C = Ancienne image
590   // +-+A|
591   // |B| |    C   = Nouvelle image
592   // +-+-+
593 
594   Upload_infos_page(&Main);
595   if (Backup_with_new_dimensions(chosen_width,chosen_height))
596   {
597     // La nouvelle page a pu être allouée, elle est pour l'instant pleine de
598     // 0s. Elle fait Main.image_width de large.
599 
600     Main.image_is_modified=1;
601 
602     // On copie donc maintenant la partie C dans la nouvelle image.
603     for (i=0; i<Main.backups->Pages->Nb_layers; i++)
604     {
605       Copy_part_of_image_to_another(
606         Main.backups->Pages->Next->Image[i].Pixels,0,0,Min(old_width,Main.image_width),
607         Min(old_height,Main.image_height),old_width,
608         Main.backups->Pages->Image[i].Pixels,0,0,Main.image_width);
609     }
610     Redraw_layered_image();
611   }
612   else
613   {
614     // Afficher un message d'erreur
615     Display_cursor();
616     Message_out_of_memory();
617     Hide_cursor();
618   }
619 }
620 
621 
622 
Remap_spare(void)623 void Remap_spare(void)
624 {
625   short x_pos; // Variable de balayage de la brosse
626   short y_pos; // Variable de balayage de la brosse
627   byte  used[256]; // Tableau de booléens "La couleur est utilisée"
628   int   color;
629   int   layer;
630 
631   // On commence par initialiser le tableau de booléens à faux
632   for (color=0;color<=255;color++)
633     used[color]=0;
634 
635   // On calcule la table d'utilisation des couleurs
636   for (layer=0; layer<Spare.backups->Pages->Nb_layers; layer++)
637     for (y_pos=0;y_pos<Spare.image_height;y_pos++)
638       for (x_pos=0;x_pos<Spare.image_width;x_pos++)
639         used[*(Spare.backups->Pages->Image[layer].Pixels+(y_pos*Spare.image_width+x_pos))]=1;
640 
641   //   On va maintenant se servir de la table "used" comme table de
642   // conversion: pour chaque indice, la table donne une couleur de
643   // remplacement.
644   // Note : Seules les couleurs utilisées on besoin d'êtres recalculées: les
645   //       autres ne seront jamais consultées dans la nouvelle table de
646   //       conversion puisque elles n'existent pas dans l'image, donc elles
647   //       ne seront pas utilisées par Remap_general_lowlevel.
648   for (color=0;color<=255;color++)
649     if (used[color])
650       used[color]=Best_color_perceptual(Spare.palette[color].R,Spare.palette[color].G,Spare.palette[color].B);
651 
652   //   Maintenant qu'on a une super table de conversion qui n'a que le nom
653   // qui craint un peu, on peut faire l'échange dans la brosse de toutes les
654   // teintes.
655   for (layer=0; layer<Spare.backups->Pages->Nb_layers; layer++)
656     Remap_general_lowlevel(used,Spare.backups->Pages->Image[layer].Pixels,Spare.backups->Pages->Image[layer].Pixels,Spare.image_width,Spare.image_height,Spare.image_width);
657 
658   // Change transparent color index
659   Spare.backups->Pages->Transparent_color=used[Spare.backups->Pages->Transparent_color];
660 }
661 
662 
663 
Get_colors_from_brush(void)664 void Get_colors_from_brush(void)
665 {
666   short x_pos; // Variable de balayage de la brosse
667   short y_pos; // Variable de balayage de la brosse
668   byte  brush_used[256]; // Tableau de booléens "La couleur est utilisée"
669   dword usage[256];
670   int   color;
671   int   image_color;
672 
673   //if (Confirmation_box("Modify current palette ?"))
674 
675   // Backup with unchanged layers, only palette is modified
676   Backup_layers(LAYER_NONE);
677 
678   // Init array of new colors
679   for (color=0;color<=255;color++)
680     brush_used[color]=0;
681 
682   // Tag used colors
683   for (y_pos=0;y_pos<Brush_height;y_pos++)
684     for (x_pos=0;x_pos<Brush_width;x_pos++)
685       brush_used[*(Brush_original_pixels + y_pos * Brush_width + x_pos)]=1;
686 
687   // Check used colors in picture (to know which palette entries are free)
688   Count_used_colors(usage);
689 
690   // First pass : omit colors that are already in palette
691   for (color=0; color<256; color++)
692   {
693     // For each color used in brush (to add in palette)
694     if (brush_used[color])
695     {
696       // Try locate it in current palette
697       for (image_color=0; image_color<256; image_color++)
698       {
699         if (Brush_original_palette[color].R==Main.palette[image_color].R
700          && Brush_original_palette[color].G==Main.palette[image_color].G
701          && Brush_original_palette[color].B==Main.palette[image_color].B)
702         {
703           // Color already in main palette:
704 
705           // Tag as used, so that no new color will overwrite it
706           usage[image_color]=1;
707 
708           // Tag as non-new, to avoid it in pass 2
709           brush_used[color]=0;
710 
711           break;
712         }
713       }
714     }
715   }
716 
717   // Second pass : For each color to add, find an empty slot in
718   // main palette to add it
719   image_color=0;
720   for (color=0; color<256 && image_color<256; color++)
721   {
722     // For each color used in brush
723     if (brush_used[color])
724     {
725       for (; image_color<256; image_color++)
726       {
727         if (!usage[image_color])
728         {
729           // Copy from color to image_color
730           Main.palette[image_color].R=Brush_original_palette[color].R;
731           Main.palette[image_color].G=Brush_original_palette[color].G;
732           Main.palette[image_color].B=Brush_original_palette[color].B;
733 
734           image_color++;
735           break;
736         }
737       }
738     }
739   }
740   Remap_brush();
741 
742   Set_palette(Main.palette);
743   Compute_optimal_menu_colors(Main.palette);
744   Hide_cursor();
745   Display_all_screen();
746   Display_menu();
747   Display_cursor();
748   End_of_modification();
749 
750   Main.image_is_modified=1;
751 }
752 
753 
754 
755 //////////////////////////////////////////////////////////////////////////////
756 ////////////////////////////// GESTION DU FILLER /////////////////////////////
757 //////////////////////////////////////////////////////////////////////////////
758 
759 
Fill(short * top_reached,short * bottom_reached,short * left_reached,short * right_reached)760 void Fill(short * top_reached  , short * bottom_reached,
761           short * left_reached, short * right_reached)
762 //
763 //   Cette fonction fait un remplissage classique d'une zone délimitée de
764 // l'image. Les limites employées sont Limit_top, Limit_bottom, Limit_left
765 // et Limit_right. Le point de départ du remplissage est Paintbrush_X,Paintbrush_Y
766 // et s'effectue en théorie sur la couleur 1 et emploie la couleur 2 pour le
767 // remplissage. Ces restrictions sont dûes à l'utilisation qu'on en fait dans
768 // la fonction principale "Fill_general", qui se charge de faire une gestion de
769 // tous les effets.
770 //   Cette fonction ne doit pas être directement appelée.
771 //
772 {
773   short x_pos;   // Abscisse de balayage du segment, utilisée lors de l'"affichage"
774   short line;   // Ordonnée de la ligne en cours de traitement
775   short start_x; // Abscisse de départ du segment traité
776   short end_x;   // Abscisse de fin du segment traité
777   int   changes_made;    // Booléen "On a fait une modif dans le dernier passage"
778   int   can_propagate; // Booléen "On peut propager la couleur dans le segment"
779   short current_limit_bottom;  // Intervalle vertical restreint
780   short current_limit_top;
781   int   line_is_modified;       // Booléen "On a fait une modif dans la ligne"
782 
783   changes_made=1;
784   current_limit_top=Paintbrush_Y;
785   current_limit_bottom =Min(Paintbrush_Y+1,Limit_bottom);
786   *left_reached=Paintbrush_X;
787   *right_reached=Paintbrush_X+1;
788   Pixel_in_current_layer(Paintbrush_X,Paintbrush_Y,2);
789 
790   while (changes_made)
791   {
792     changes_made=0;
793 
794     for (line=current_limit_top;line<=current_limit_bottom;line++)
795     {
796       line_is_modified=0;
797       // On va traiter le cas de la ligne n° line.
798 
799       // On commence le traitement à la gauche de l'écran
800       start_x=Limit_left;
801 
802       // Pour chaque segment de couleur 1 que peut contenir la ligne
803       while (start_x<=Limit_right)
804       {
805         // On cherche son début
806         while((start_x<=Limit_right) &&
807                 (Read_pixel_from_current_layer(start_x,line)!=1))
808              start_x++;
809 
810         if (start_x<=Limit_right)
811         {
812           // Un segment de couleur 1 existe et commence à la position start_x.
813           // On va donc en chercher la fin.
814           for (end_x=start_x+1;(end_x<=Limit_right) &&
815                (Read_pixel_from_current_layer(end_x,line)==1);end_x++);
816 
817           //   On sait qu'il existe un segment de couleur 1 qui commence en
818           // start_x et qui se termine en end_x-1.
819 
820           //   On va maintenant regarder si une couleur sur la périphérie
821           // permet de colorier ce segment avec la couleur 2.
822 
823           can_propagate=(
824             // Test de la présence d'un point à gauche du segment
825             ((start_x>Limit_left) &&
826              (Read_pixel_from_current_layer(start_x-1,line)==2)) ||
827             // Test de la présence d'un point à droite du segment
828             ((end_x-1<Limit_right) &&
829              (Read_pixel_from_current_layer(end_x    ,line)==2))
830                                );
831 
832           // Test de la présence d'un point en haut du segment
833           if (!can_propagate && (line>Limit_top))
834             for (x_pos=start_x;x_pos<end_x;x_pos++)
835               if (Read_pixel_from_current_layer(x_pos,line-1)==2)
836               {
837                 can_propagate=1;
838                 break;
839               }
840 
841           if (can_propagate)
842           {
843             if (start_x<*left_reached)
844               *left_reached=start_x;
845             if (end_x>*right_reached)
846               *right_reached=end_x;
847             // On remplit le segment de start_x à end_x-1.
848             for (x_pos=start_x;x_pos<end_x;x_pos++)
849               Pixel_in_current_layer(x_pos,line,2);
850             // On vient d'effectuer des modifications.
851             changes_made=1;
852             line_is_modified=1;
853           }
854 
855           start_x=end_x+1;
856         }
857       }
858 
859       // Si on est en bas, et qu'on peut se propager vers le bas...
860       if ( (line==current_limit_bottom) &&
861            (line_is_modified) &&
862            (current_limit_bottom<Limit_bottom) )
863         current_limit_bottom++; // On descend cette limite vers le bas
864     }
865 
866     // Pour le prochain balayage vers le haut, on va se permettre d'aller
867     // voir une ligne plus haut.
868     // Si on ne le fait pas, et que la première ligne (current_limit_top)
869     // n'était pas modifiée, alors cette limite ne serait pas remontée, donc
870     // le filler ne progresserait pas vers le haut.
871     if (current_limit_top>Limit_top)
872       current_limit_top--;
873 
874     for (line=current_limit_bottom;line>=current_limit_top;line--)
875     {
876       line_is_modified=0;
877       // On va traiter le cas de la ligne n° line.
878 
879       // On commence le traitement à la gauche de l'écran
880       start_x=Limit_left;
881 
882       // Pour chaque segment de couleur 1 que peut contenir la ligne
883       while (start_x<=Limit_right)
884       {
885         // On cherche son début
886         for (;(start_x<=Limit_right) &&
887              (Read_pixel_from_current_layer(start_x,line)!=1);start_x++);
888 
889         if (start_x<=Limit_right)
890         {
891           // Un segment de couleur 1 existe et commence à la position start_x.
892           // On va donc en chercher la fin.
893           for (end_x=start_x+1;(end_x<=Limit_right) &&
894                (Read_pixel_from_current_layer(end_x,line)==1);end_x++);
895 
896           //   On sait qu'il existe un segment de couleur 1 qui commence en
897           // start_x et qui se termine en end_x-1.
898 
899           //   On va maintenant regarder si une couleur sur la périphérie
900           // permet de colorier ce segment avec la couleur 2.
901 
902           can_propagate=(
903             // Test de la présence d'un point à gauche du segment
904             ((start_x>Limit_left) &&
905              (Read_pixel_from_current_layer(start_x-1,line)==2)) ||
906             // Test de la présence d'un point à droite du segment
907             ((end_x-1<Limit_right) &&
908              (Read_pixel_from_current_layer(end_x    ,line)==2))
909                                );
910 
911           // Test de la présence d'un point en bas du segment
912           if (!can_propagate && (line<Limit_bottom))
913             for (x_pos=start_x;x_pos<end_x;x_pos++)
914               if (Read_pixel_from_current_layer(x_pos,line+1)==2)
915               {
916                 can_propagate=1;
917                 break;
918               }
919 
920           if (can_propagate)
921           {
922             if (start_x<*left_reached)
923               *left_reached=start_x;
924             if (end_x>*right_reached)
925               *right_reached=end_x;
926             // On remplit le segment de start_x à end_x-1.
927             for (x_pos=start_x;x_pos<end_x;x_pos++)
928               Pixel_in_current_layer(x_pos,line,2);
929             // On vient d'effectuer des modifications.
930             changes_made=1;
931             line_is_modified=1;
932           }
933 
934           start_x=end_x+1;
935         }
936       }
937 
938       // Si on est en haut, et qu'on peut se propager vers le haut...
939       if ( (line==current_limit_top) &&
940            (line_is_modified) &&
941            (current_limit_top>Limit_top) )
942         current_limit_top--; // On monte cette limite vers le haut
943     }
944   }
945 
946   *top_reached=current_limit_top;
947   *bottom_reached =current_limit_bottom;
948   (*right_reached)--;
949 } // end de la routine de remplissage "Fill"
950 
Read_pixel_from_backup_layer(word x,word y)951 byte Read_pixel_from_backup_layer(word x,word y)
952 {
953   return *((y)*Main.image_width+(x)+Main.backups->Pages->Next->Image[Main.current_layer].Pixels);
954 }
955 
Fill_general(byte fill_color)956 void Fill_general(byte fill_color)
957 //
958 //  Cette fonction fait un remplissage qui gère tous les effets. Elle fait
959 // appel à "Fill()".
960 //
961 {
962   byte   cursor_shape_before_fill;
963   short  x_pos,y_pos;
964   short  top_reached  ,bottom_reached;
965   short  left_reached,right_reached;
966   byte   replace_table[256];
967   int old_limit_right=Limit_right;
968   int old_limit_left=Limit_left;
969   int old_limit_top=Limit_top;
970   int old_limit_bottom=Limit_bottom;
971 
972 
973   // Avant toute chose, on vérifie que l'on n'est pas en train de remplir
974   // en dehors de l'image:
975 
976   if ( (Paintbrush_X>=Limit_left) &&
977        (Paintbrush_X<=Limit_right) &&
978        (Paintbrush_Y>=Limit_top)   &&
979        (Paintbrush_Y<=Limit_bottom) )
980   {
981     // If tilemap mode is ON, ignore action if it's outside grid limits
982     if (Main.tilemap_mode)
983     {
984       if (Paintbrush_X<Snap_offset_X)
985         return;
986       if (Paintbrush_X >= (Main.image_width-Snap_offset_X)/Snap_width*Snap_width+Snap_offset_X)
987         return;
988       if (Paintbrush_Y<Snap_offset_Y)
989         return;
990       if (Paintbrush_Y >= (Main.image_height-Snap_offset_Y)/Snap_height*Snap_height+Snap_offset_Y)
991         return;
992     }
993 
994     // On suppose que le curseur est déjà caché.
995     // Hide_cursor();
996 
997     //   On va faire patienter l'utilisateur en lui affichant un joli petit
998     // sablier:
999     cursor_shape_before_fill=Cursor_shape;
1000     Cursor_shape=CURSOR_SHAPE_HOURGLASS;
1001     Display_cursor();
1002 
1003     // On commence par effectuer un backup de l'image.
1004     Backup();
1005 
1006     // On fait attention au Feedback qui DOIT se faire avec le backup.
1007     Update_FX_feedback(0);
1008 
1009     // If tilemap mode is ON, adapt limits to current tile only
1010     if (Main.tilemap_mode)
1011     {
1012       Limit_right = Min(Limit_right, (Paintbrush_X-Snap_offset_X)/Snap_width*Snap_width+Snap_width-1+Snap_offset_X);
1013       Limit_left = Max(Limit_left, (Paintbrush_X-Snap_offset_X)/Snap_width*Snap_width+Snap_offset_X);
1014       Limit_bottom = Min(Limit_bottom, (Paintbrush_Y-Snap_offset_Y)/Snap_height*Snap_height+Snap_height-1+Snap_offset_Y);
1015       Limit_top = Max(Limit_top, (Paintbrush_Y-Snap_offset_Y)/Snap_height*Snap_height+Snap_offset_Y);
1016     }
1017 
1018     // On va maintenant "épurer" la zone visible de l'image:
1019     memset(replace_table,0,256);
1020     replace_table[Read_pixel_from_backup_layer(Paintbrush_X,Paintbrush_Y)]=1;
1021     Replace_colors_within_limits(replace_table);
1022 
1023     // On fait maintenant un remplissage classique de la couleur 1 avec la 2
1024     Fill(&top_reached  ,&bottom_reached,
1025          &left_reached,&right_reached);
1026 
1027     //  On s'apprête à faire des opérations qui nécessitent un affichage. Il
1028     // faut donc retirer de l'écran le curseur:
1029     Hide_cursor();
1030     Cursor_shape=cursor_shape_before_fill;
1031 
1032     //  Il va maintenant falloir qu'on "turn" ce gros caca "into" un truc qui
1033     // ressemble un peu plus à ce à quoi l'utilisateur peut s'attendre.
1034     if (top_reached>Limit_top)
1035       Copy_part_of_image_to_another(Main.backups->Pages->Next->Image[Main.current_layer].Pixels, // source
1036                                                Limit_left,Limit_top,       // Pos X et Y dans source
1037                                                (Limit_right-Limit_left)+1, // width copie
1038                                                top_reached-Limit_top,// height copie
1039                                                Main.image_width,         // width de la source
1040                                                Main.backups->Pages->Image[Main.current_layer].Pixels, // Destination
1041                                                Limit_left,Limit_top,       // Pos X et Y destination
1042                                                Main.image_width);        // width destination
1043     if (bottom_reached<Limit_bottom)
1044       Copy_part_of_image_to_another(Main.backups->Pages->Next->Image[Main.current_layer].Pixels,
1045                                                Limit_left,bottom_reached+1,
1046                                                (Limit_right-Limit_left)+1,
1047                                                Limit_bottom-bottom_reached,
1048                                                Main.image_width,Main.backups->Pages->Image[Main.current_layer].Pixels,
1049                                                Limit_left,bottom_reached+1,Main.image_width);
1050     if (left_reached>Limit_left)
1051       Copy_part_of_image_to_another(Main.backups->Pages->Next->Image[Main.current_layer].Pixels,
1052                                                Limit_left,top_reached,
1053                                                left_reached-Limit_left,
1054                                                (bottom_reached-top_reached)+1,
1055                                                Main.image_width,Main.backups->Pages->Image[Main.current_layer].Pixels,
1056                                                Limit_left,top_reached,Main.image_width);
1057     if (right_reached<Limit_right)
1058       Copy_part_of_image_to_another(Main.backups->Pages->Next->Image[Main.current_layer].Pixels,
1059                                                right_reached+1,top_reached,
1060                                                Limit_right-right_reached,
1061                                                (bottom_reached-top_reached)+1,
1062                                                Main.image_width,Main.backups->Pages->Image[Main.current_layer].Pixels,
1063                                                right_reached+1,top_reached,Main.image_width);
1064 
1065     // Restore image limits : this is needed by the tilemap effect,
1066     // otherwise it will not display other modified tiles.
1067     Limit_right=old_limit_right;
1068     Limit_left=old_limit_left;
1069     Limit_top=old_limit_top;
1070     Limit_bottom=old_limit_bottom;
1071 
1072     for (y_pos=top_reached;y_pos<=bottom_reached;y_pos++)
1073     {
1074       for (x_pos=left_reached;x_pos<=right_reached;x_pos++)
1075       {
1076         byte filled = Read_pixel_from_current_layer(x_pos,y_pos);
1077 
1078         // First, restore the color.
1079         Pixel_in_current_screen(x_pos,y_pos,Read_pixel_from_backup_layer(x_pos,y_pos));
1080 
1081         if (filled==2)
1082         {
1083           // Update the color according to the fill color and all effects
1084           Display_pixel(x_pos,y_pos,fill_color);
1085         }
1086       }
1087     }
1088 
1089     // Restore original feedback value
1090     Update_FX_feedback(Config.FX_Feedback);
1091 
1092     //   A la fin, on n'a pas besoin de réafficher le curseur puisque c'est
1093     // l'appelant qui s'en charge, et on n'a pas besoin de rafficher l'image
1094     // puisque les seuls points qui ont changé dans l'image ont été raffichés
1095     // par l'utilisation de "Display_pixel()", et que les autres... eh bein
1096     // on n'y a jamais touché à l'écran les autres: ils sont donc corrects.
1097     if(Main.magnifier_mode)
1098     {
1099       short w,h;
1100 
1101       w=Min(Screen_width-Main.X_zoom, (Main.image_width-Main.magnifier_offset_X)*Main.magnifier_factor);
1102       h=Min(Menu_Y, (Main.image_height-Main.magnifier_offset_Y)*Main.magnifier_factor);
1103 
1104       Redraw_grid(Main.X_zoom,0,w,h);
1105     }
1106 
1107     Update_rect(0,0,0,0);
1108     End_of_modification();
1109   }
1110 }
1111 
1112 
1113 
1114 //////////////////////////////////////////////////////////////////////////////
1115 ////////////////// TRACéS DE FIGURES GéOMéTRIQUES STANDARDS //////////////////
1116 ////////////////////////// avec gestion de previews //////////////////////////
1117 //////////////////////////////////////////////////////////////////////////////
1118 
1119   // Data used by ::Init_permanent_draw() and ::Pixel_figure_permanent()
1120   static dword Permanent_draw_next_refresh=0;
1121   static int Permanent_draw_count=0;
1122 
Init_permanent_draw(void)1123   void Init_permanent_draw(void)
1124   {
1125     Permanent_draw_count = 0;
1126     Permanent_draw_next_refresh = GFX2_GetTicks() + 100;
1127   }
1128 
1129   // Affichage d'un point de façon définitive (utilisation du pinceau)
Pixel_figure_permanent(word x_pos,word y_pos,byte color)1130   void Pixel_figure_permanent(word x_pos,word y_pos,byte color)
1131   {
1132     Draw_paintbrush(x_pos,y_pos,color);
1133     Permanent_draw_count ++;
1134 
1135     // Check every 8 pixels
1136     if (! (Permanent_draw_count&7))
1137     {
1138       dword now = GFX2_GetTicks();
1139 #if defined(USE_SDL) || defined(USE_SDL2)
1140       SDL_PumpEvents();
1141 #endif
1142       if (now>= Permanent_draw_next_refresh)
1143       {
1144         Permanent_draw_next_refresh = now+100;
1145         Flush_update();
1146       }
1147     }
1148   }
1149 
1150   // Affichage d'un point de façon définitive
Pixel_clipped(word x_pos,word y_pos,byte color)1151   void Pixel_clipped(word x_pos,word y_pos,byte color)
1152   {
1153     if ( (x_pos>=Limit_left) &&
1154          (x_pos<=Limit_right) &&
1155          (y_pos>=Limit_top)   &&
1156          (y_pos<=Limit_bottom) )
1157     Display_pixel(x_pos,y_pos,color);
1158   }
1159 
1160   // Affichage d'un point pour une preview
Pixel_figure_preview(word x_pos,word y_pos,byte color)1161   void Pixel_figure_preview(word x_pos,word y_pos,byte color)
1162   {
1163     if ( (x_pos>=Limit_left) &&
1164          (x_pos<=Limit_right) &&
1165          (y_pos>=Limit_top)   &&
1166          (y_pos<=Limit_bottom) )
1167       Pixel_preview(x_pos,y_pos,color);
1168   }
1169   // Affichage d'un point pour une preview, avec sa propre couleur
Pixel_figure_preview_auto(word x_pos,word y_pos)1170   void Pixel_figure_preview_auto(word x_pos,word y_pos)
1171   {
1172     if ( (x_pos>=Limit_left) &&
1173          (x_pos<=Limit_right) &&
1174          (y_pos>=Limit_top)   &&
1175          (y_pos<=Limit_bottom) )
1176       Pixel_preview(x_pos,y_pos,Read_pixel_from_current_screen(x_pos,y_pos));
1177   }
1178 
1179   // Affichage d'un point pour une preview en xor
Pixel_figure_preview_xor(word x_pos,word y_pos,byte color)1180   void Pixel_figure_preview_xor(word x_pos,word y_pos,byte color)
1181   {
1182     (void)color; // unused
1183 
1184     if ( (x_pos>=Limit_left) &&
1185          (x_pos<=Limit_right) &&
1186          (y_pos>=Limit_top)   &&
1187          (y_pos<=Limit_bottom) )
1188       Pixel_preview(x_pos,y_pos,xor_lut[Read_pixel(x_pos-Main.offset_X,
1189                                            y_pos-Main.offset_Y)]);
1190   }
1191 
1192   // Affichage d'un point pour une preview en xor additif
1193   // (Il lit la couleur depuis la page backup)
Pixel_figure_preview_xorback(word x_pos,word y_pos,byte color)1194   void Pixel_figure_preview_xorback(word x_pos,word y_pos,byte color)
1195   {
1196     (void)color; // unused
1197 
1198     if ( (x_pos>=Limit_left) &&
1199          (x_pos<=Limit_right) &&
1200          (y_pos>=Limit_top)   &&
1201          (y_pos<=Limit_bottom) )
1202       Pixel_preview(x_pos,y_pos,xor_lut[Main_screen[x_pos+y_pos*Main.image_width]]);
1203   }
1204 
1205 
1206   // Effacement d'un point de preview
Pixel_figure_clear_preview(word x_pos,word y_pos,byte color)1207   void Pixel_figure_clear_preview(word x_pos,word y_pos,byte color)
1208   {
1209     (void)color; // unused
1210 
1211     if ( (x_pos>=Limit_left) &&
1212          (x_pos<=Limit_right) &&
1213          (y_pos>=Limit_top)   &&
1214          (y_pos<=Limit_bottom) )
1215       Pixel_preview(x_pos,y_pos,Read_pixel_from_current_screen(x_pos,y_pos));
1216   }
1217 
1218   // Affichage d'un point dans la brosse
Pixel_figure_in_brush(word x_pos,word y_pos,byte color)1219   void Pixel_figure_in_brush(word x_pos,word y_pos,byte color)
1220   {
1221     x_pos-=Brush_offset_X;
1222     y_pos-=Brush_offset_Y;
1223     if ( (x_pos<Brush_width) && // Les pos sont des word donc jamais < 0 ...
1224          (y_pos<Brush_height) )
1225       Pixel_in_brush(x_pos,y_pos,color);
1226   }
1227 
1228 
1229   // -- Tracer général d'un cercle vide -------------------------------------
1230 
Draw_empty_circle_general(short center_x,short center_y,long sqradius,byte color)1231 void Draw_empty_circle_general(short center_x,short center_y, long sqradius,byte color)
1232 {
1233   short start_x;
1234   short start_y;
1235   short x_pos;
1236   short y_pos;
1237   long x, y;
1238   short radius;
1239 
1240   radius = sqrt(sqradius);
1241 
1242   // Ensuite, on va parcourire le quart haut gauche du cercle
1243   start_x=center_x-radius;
1244   start_y=center_y-radius;
1245 
1246   // Affichage des extremitées du cercle sur chaque quart du cercle:
1247   for (y_pos=start_y,y=-radius;y_pos<center_y;y_pos++,y++)
1248     for (x_pos=start_x,x=-radius;x_pos<center_x;x_pos++,x++)
1249       if (Pixel_in_circle(x, y, sqradius))
1250       {
1251         // On vient de tomber sur le premier point sur la ligne horizontale
1252         // qui fait partie du cercle.
1253         // Donc on peut l'afficher (lui et ses copains symétriques)
1254 
1255          // Quart Haut-gauche
1256         Pixel_figure(x_pos,y_pos,color);
1257          // Quart Haut-droite
1258         Pixel_figure((center_x<<1)-x_pos,y_pos,color);
1259          // Quart Bas-droite
1260         Pixel_figure((center_x<<1)-x_pos,(center_y<<1)-y_pos,color);
1261          // Quart Bas-gauche
1262         Pixel_figure(x_pos,(center_y<<1)-y_pos,color);
1263 
1264         // On peut ensuite afficher tous les points qui le suivent dont le
1265         // pixel voisin du haut n'appartient pas au cercle:
1266         for (y--,x_pos++,x++;x_pos<center_x;x_pos++,x++)
1267           if (!Pixel_in_circle(x, y, sqradius))
1268           {
1269              // Quart Haut-gauche
1270             Pixel_figure(x_pos,y_pos,color);
1271              // Quart Haut-droite
1272             Pixel_figure((center_x<<1)-x_pos,y_pos,color);
1273              // Quart Bas-gauche
1274             Pixel_figure(x_pos,(center_y<<1)-y_pos,color);
1275              // Quart Bas-droite
1276             Pixel_figure((center_x<<1)-x_pos,(center_y<<1)-y_pos,color);
1277           }
1278           else
1279             break;
1280 
1281         y++;
1282         break;
1283       }
1284 
1285   // On affiche à la fin les points cardinaux:
1286   Pixel_figure(center_x,center_y-radius,color); // Haut
1287   Pixel_figure(center_x-radius,center_y,color); // Gauche
1288   Pixel_figure(center_x+radius,center_y,color); // Droite
1289   Pixel_figure(center_x,center_y+radius,color); // Bas
1290 
1291 }
1292 
1293   // -- Tracé définitif d'un cercle vide --
1294 
Draw_empty_circle_permanent(short center_x,short center_y,long sqradius,byte color)1295 void Draw_empty_circle_permanent(short center_x,short center_y,long sqradius,byte color)
1296 {
1297   short radius = sqrt(sqradius);
1298   Pixel_figure=Pixel_figure_permanent;
1299   Init_permanent_draw();
1300   Draw_empty_circle_general(center_x,center_y,sqradius,color);
1301   Update_part_of_screen(center_x - radius, center_y - radius, 2* radius+1, 2*radius+1);
1302 }
1303 
1304   // -- Tracer la preview d'un cercle vide --
1305 
Draw_empty_circle_preview(short center_x,short center_y,long sqradius,byte color)1306 void Draw_empty_circle_preview(short center_x,short center_y,long sqradius,byte color)
1307 {
1308   short radius = sqrt(sqradius);
1309   Pixel_figure=Pixel_figure_preview;
1310   Draw_empty_circle_general(center_x,center_y,sqradius,color);
1311   Update_part_of_screen(center_x - radius, center_y - radius, 2* radius+1, 2*radius+1);
1312 }
1313 
1314   // -- Effacer la preview d'un cercle vide --
1315 
Hide_empty_circle_preview(short center_x,short center_y,long sqradius)1316 void Hide_empty_circle_preview(short center_x,short center_y,long sqradius)
1317 {
1318   short radius = sqrt(sqradius);
1319   Pixel_figure=Pixel_figure_clear_preview;
1320   Draw_empty_circle_general(center_x,center_y,sqradius,0);
1321   Update_part_of_screen(center_x - radius, center_y - radius, 2* radius+1, 2*radius+1);
1322 }
1323 
1324   // -- Tracer un cercle plein --
1325 
Draw_filled_circle(short center_x,short center_y,long sqradius,byte color)1326 void Draw_filled_circle(short center_x,short center_y,long sqradius,byte color)
1327 {
1328   short start_x;
1329   short start_y;
1330   short x_pos;
1331   short y_pos;
1332   short end_x;
1333   short end_y;
1334   long x, y;
1335   short radius = sqrt(sqradius);
1336 
1337   start_x=center_x-radius;
1338   start_y=center_y-radius;
1339   end_x=center_x+radius;
1340   end_y=center_y+radius;
1341 
1342   // Correction des bornes d'après les limites
1343   if (start_y<Limit_top)
1344     start_y=Limit_top;
1345   if (end_y>Limit_bottom)
1346     end_y=Limit_bottom;
1347   if (start_x<Limit_left)
1348     start_x=Limit_left;
1349   if (end_x>Limit_right)
1350     end_x=Limit_right;
1351 
1352   // Affichage du cercle
1353   for (y_pos=start_y,y=(long)start_y-center_y;y_pos<=end_y;y_pos++,y++)
1354     for (x_pos=start_x,x=(long)start_x-center_x;x_pos<=end_x;x_pos++,x++)
1355       if (Pixel_in_circle(x, y, sqradius))
1356         Display_pixel(x_pos,y_pos,color);
1357 
1358   Update_part_of_screen(start_x,start_y,end_x+1-start_x,end_y+1-start_y);
1359 }
1360 
Circle_squared_diameter(int diameter)1361 int Circle_squared_diameter(int diameter)
1362 {
1363   int result = diameter*diameter;
1364   // Trick to make some circles rounder, even though
1365   // mathematically incorrect.
1366   if (diameter==3 || diameter==9)
1367     return result-2;
1368   if (diameter==11)
1369     return result-6;
1370   if (diameter==14)
1371     return result-4;
1372 
1373   return result;
1374 }
1375 
1376   // -- Tracer général d'une ellipse vide -----------------------------------
1377 
Draw_empty_ellipse_general(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)1378 static void Draw_empty_ellipse_general(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)
1379 {
1380   short start_x;
1381   short start_y;
1382   short x_pos;
1383   short y_pos;
1384   long x, y;
1385   T_Ellipse_limits Ellipse;
1386 
1387   start_x=center_x-horizontal_radius;
1388   start_y=center_y-vertical_radius;
1389 
1390   // Calcul des limites de l'ellipse
1391   Ellipse_compute_limites(horizontal_radius+1, vertical_radius+1, &Ellipse);
1392 
1393   // Affichage des extremitées de l'ellipse sur chaque quart de l'ellipse:
1394   for (y_pos=start_y,y=-vertical_radius;y_pos<center_y;y_pos++,y++)
1395     for (x_pos=start_x,x=-horizontal_radius;x_pos<center_x;x_pos++,x++)
1396       if (Pixel_in_ellipse(x, y, &Ellipse))
1397       {
1398         // On vient de tomber sur le premier point qui sur la ligne
1399         // horizontale fait partie de l'ellipse.
1400 
1401         // Donc on peut l'afficher (lui et ses copains symétriques)
1402 
1403          // Quart Haut-gauche
1404         Pixel_figure(x_pos,y_pos,color);
1405          // Quart Haut-droite
1406         Pixel_figure((center_x<<1)-x_pos,y_pos,color);
1407          // Quart Bas-gauche
1408         Pixel_figure(x_pos,(center_y<<1)-y_pos,color);
1409          // Quart Bas-droite
1410         Pixel_figure((center_x<<1)-x_pos,(center_y<<1)-y_pos,color);
1411 
1412         // On peut ensuite afficher tous les points qui le suivent dont le
1413         // pixel voisin du haut n'appartient pas à l'ellipse:
1414         for (y--,x_pos++,x++;x_pos<center_x;x_pos++,x++)
1415           if (!Pixel_in_ellipse(x, y, &Ellipse))
1416           {
1417              // Quart Haut-gauche
1418             Pixel_figure(x_pos,y_pos,color);
1419              // Quart Haut-droite
1420             Pixel_figure((center_x<<1)-x_pos,y_pos,color);
1421              // Quart Bas-gauche
1422             Pixel_figure(x_pos,(center_y<<1)-y_pos,color);
1423              // Quart Bas-droite
1424             Pixel_figure((center_x<<1)-x_pos,(center_y<<1)-y_pos,color);
1425           }
1426           else
1427             break;
1428 
1429         y++;
1430         break;
1431       }
1432 
1433   // On affiche à la fin les points cardinaux:
1434 
1435   // points verticaux:
1436   x_pos=center_x;
1437   x=-1;
1438   for (y_pos=center_y+1-vertical_radius,y=-vertical_radius+1;y_pos<center_y+vertical_radius;y_pos++,y++)
1439     if (!Pixel_in_ellipse(x, y, &Ellipse))
1440       Pixel_figure(x_pos,y_pos,color);
1441 
1442   // points horizontaux:
1443   y_pos=center_y;
1444   y=-1;
1445   for (x_pos=center_x+1-horizontal_radius,x=-horizontal_radius+1;x_pos<center_x+horizontal_radius;x_pos++,x++)
1446     if (!Pixel_in_ellipse(x, y, &Ellipse))
1447       Pixel_figure(x_pos,y_pos,color);
1448 
1449   Pixel_figure(center_x,center_y-vertical_radius,color);   // Haut
1450   Pixel_figure(center_x-horizontal_radius,center_y,color); // Gauche
1451   Pixel_figure(center_x+horizontal_radius,center_y,color); // Droite
1452   Pixel_figure(center_x,center_y+vertical_radius,color);   // Bas
1453 
1454   Update_part_of_screen(center_x-horizontal_radius,center_y-vertical_radius,2*horizontal_radius+1,2*vertical_radius+1);
1455 }
1456 
Draw_inscribed_ellipse_general(short x1,short y1,short x2,short y2,byte color,byte filled)1457 static void Draw_inscribed_ellipse_general(short x1, short y1, short x2, short y2, byte color, byte filled)
1458 {
1459   short left, right, top, bottom;
1460   short dbl_center_x; // double of center_x
1461   short dbl_center_y; // double of center_y
1462   short dbl_x_radius; // double of horizontal radius
1463   short dbl_y_radius; // double of vertical radius
1464   long sq_dbl_x_radius;
1465   long sq_dbl_y_radius;
1466   qword sq_dbl_radius_product;
1467   short x_pos;
1468   short y_pos;
1469   short x_max;
1470 
1471   if (x1 > x2)
1472   {
1473     left = x2;
1474     right = x1;
1475   }
1476   else
1477   {
1478     left = x1;
1479     right = x2;
1480   }
1481   if (y1 > y2)
1482   {
1483     top = y2;
1484     bottom = y1;
1485   }
1486   else
1487   {
1488     top = y1;
1489     bottom = y2;
1490   }
1491   dbl_center_x = left+right;
1492   dbl_center_y = top+bottom;
1493   dbl_x_radius = right-left+1;
1494   dbl_y_radius = bottom-top+1;
1495   if ((Selected_circle_ellipse_mode & MASK_CIRCLE_ELLIPSE) == MODE_CIRCLE)
1496   {
1497     if (dbl_x_radius > dbl_y_radius)
1498       dbl_x_radius = dbl_y_radius;
1499     else
1500       dbl_y_radius = dbl_x_radius;
1501   }
1502   sq_dbl_x_radius = (long)dbl_x_radius*dbl_x_radius;
1503   sq_dbl_y_radius = (long)dbl_y_radius*dbl_y_radius;
1504   sq_dbl_radius_product = (qword)sq_dbl_x_radius * sq_dbl_y_radius;
1505 
1506   x_max = right;
1507   for (y_pos = top; y_pos <= (dbl_center_y >> 1); y_pos++)
1508   {
1509     long dbl_y = 2*y_pos - dbl_center_y;
1510     long sq_dbl_y = dbl_y*dbl_y;
1511     for (x_pos = left; x_pos <= (dbl_center_x >> 1); x_pos++)
1512     {
1513       long dbl_x = 2*x_pos - dbl_center_x;
1514       long sq_dbl_x = dbl_x*dbl_x;
1515       if (((qword)sq_dbl_x * sq_dbl_y_radius + (qword)sq_dbl_y * sq_dbl_x_radius) < sq_dbl_radius_product)
1516       {
1517         short x_pos_backup = x_pos;
1518         do
1519         {
1520           Pixel_figure(x_pos,y_pos,color);
1521           Pixel_figure(dbl_center_x - x_pos,y_pos,color);
1522           Pixel_figure(x_pos,dbl_center_y - y_pos,color);
1523           Pixel_figure(dbl_center_x - x_pos,dbl_center_y - y_pos,color);
1524           x_pos++;
1525         }
1526         while (x_pos <= (dbl_center_x >> 1) && x_pos < x_max);
1527         if (!filled && x_pos_backup < x_max)
1528           x_max = x_pos_backup;
1529         break;
1530       }
1531     }
1532   }
1533 
1534   Update_part_of_screen(left, top, right-left, bottom-top);
1535 }
1536   // -- Tracé définitif d'une ellipse vide --
1537 
Draw_empty_ellipse_permanent(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)1538 void Draw_empty_ellipse_permanent(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)
1539 {
1540   Pixel_figure=Pixel_figure_permanent;
1541   Init_permanent_draw();
1542   Draw_empty_ellipse_general(center_x,center_y,horizontal_radius,vertical_radius,color);
1543   //Update_part_of_screen(center_x - horizontal_radius, center_y - vertical_radius, 2* horizontal_radius+1, 2*vertical_radius+1);
1544 }
1545 
Draw_empty_inscribed_ellipse_permanent(short x1,short y1,short x2,short y2,byte color)1546 void Draw_empty_inscribed_ellipse_permanent(short x1,short y1,short x2, short y2,byte color)
1547 {
1548   Pixel_figure=Pixel_figure_permanent;
1549   Init_permanent_draw();
1550   Draw_inscribed_ellipse_general(x1, y1, x2, y2, color, 0);
1551 }
1552 
1553   // -- Tracer la preview d'une ellipse vide --
1554 
Draw_empty_ellipse_preview(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)1555 void Draw_empty_ellipse_preview(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)
1556 {
1557   Pixel_figure=Pixel_figure_preview;
1558   Draw_empty_ellipse_general(center_x,center_y,horizontal_radius,vertical_radius,color);
1559   //Update_part_of_screen(center_x - horizontal_radius, center_y - vertical_radius, 2* horizontal_radius+1, 2*vertical_radius +1);
1560 }
1561 
Draw_empty_inscribed_ellipse_preview(short x1,short y1,short x2,short y2,byte color)1562 void Draw_empty_inscribed_ellipse_preview(short x1,short y1,short x2,short y2,byte color)
1563 {
1564   Pixel_figure=Pixel_figure_preview;
1565   Draw_inscribed_ellipse_general(x1, y1, x2, y2, color, 0);
1566 }
1567 
1568   // -- Effacer la preview d'une ellipse vide --
1569 
Hide_empty_ellipse_preview(short center_x,short center_y,short horizontal_radius,short vertical_radius)1570 void Hide_empty_ellipse_preview(short center_x,short center_y,short horizontal_radius,short vertical_radius)
1571 {
1572   Pixel_figure=Pixel_figure_clear_preview;
1573   Draw_empty_ellipse_general(center_x,center_y,horizontal_radius,vertical_radius,0);
1574   //Update_part_of_screen(center_x - horizontal_radius, center_y - vertical_radius, 2* horizontal_radius+1, 2*vertical_radius+1);
1575 }
1576 
Hide_empty_inscribed_ellipse_preview(short x1,short y1,short x2,short y2)1577 void Hide_empty_inscribed_ellipse_preview(short x1,short y1,short x2,short y2)
1578 {
1579   Pixel_figure=Pixel_figure_clear_preview;
1580   Draw_inscribed_ellipse_general(x1,y1,x2,y2,0,0);
1581 }
1582 
1583   // -- Tracer une ellipse pleine --
1584 
Draw_filled_ellipse(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)1585 void Draw_filled_ellipse(short center_x,short center_y,short horizontal_radius,short vertical_radius,byte color)
1586 {
1587   short start_x;
1588   short start_y;
1589   short x_pos;
1590   short y_pos;
1591   short end_x;
1592   short end_y;
1593   long x, y;
1594   T_Ellipse_limits Ellipse;
1595 
1596   start_x=center_x-horizontal_radius;
1597   start_y=center_y-vertical_radius;
1598   end_x=center_x+horizontal_radius;
1599   end_y=center_y+vertical_radius;
1600 
1601   // Calcul des limites de l'ellipse
1602   Ellipse_compute_limites(horizontal_radius+1, vertical_radius+1, &Ellipse);
1603 
1604   // Correction des bornes d'après les limites
1605   if (start_y<Limit_top)
1606     start_y=Limit_top;
1607   if (end_y>Limit_bottom)
1608     end_y=Limit_bottom;
1609   if (start_x<Limit_left)
1610     start_x=Limit_left;
1611   if (end_x>Limit_right)
1612     end_x=Limit_right;
1613 
1614   // Affichage de l'ellipse
1615   for (y_pos=start_y,y=start_y-center_y;y_pos<=end_y;y_pos++,y++)
1616     for (x_pos=start_x,x=start_x-center_x;x_pos<=end_x;x_pos++,x++)
1617       if (Pixel_in_ellipse(x, y, &Ellipse))
1618         Display_pixel(x_pos,y_pos,color);
1619   Update_part_of_screen(center_x-horizontal_radius,center_y-vertical_radius,2*horizontal_radius+1,2*vertical_radius+1);
1620 }
1621 
Draw_filled_inscribed_ellipse(short x1,short y1,short x2,short y2,byte color)1622 void Draw_filled_inscribed_ellipse(short x1,short y1,short x2,short y2,byte color)
1623 {
1624   Pixel_figure = Pixel_clipped;
1625   Draw_inscribed_ellipse_general(x1, y1, x2, y2, color, 1);
1626 }
1627 
1628 /******************
1629 * TRACÉ DE LIGNES *
1630 ******************/
1631 
1632 /// Alters bx and by so the (AX,AY)-(BX,BY) segment becomes either horizontal,
1633 /// vertical, 45degrees, or isometrical for pixelart (ie 2:1 ratio)
Clamp_coordinates_regular_angle(short ax,short ay,short * bx,short * by)1634 void Clamp_coordinates_regular_angle(short ax, short ay, short* bx, short* by)
1635 {
1636   int dx, dy;
1637   float angle;
1638 
1639   dx = *bx-ax;
1640   dy = *by-ay;
1641 
1642   // No mouse move: no need to clamp anything
1643   if (dx==0 || dy == 0) return;
1644 
1645   // Determine angle (heading)
1646   angle = atan2(dx, dy);
1647 
1648   // Get absolute values, useful from now on:
1649   //dx=abs(dx);
1650   //dy=abs(dy);
1651 
1652   // Negative Y
1653   if (angle < M_PI*(-15.0/16.0) || angle > M_PI*(15.0/16.0))
1654   {
1655     *bx=ax;
1656     *by=ay + dy;
1657   }
1658   // Iso close to negative Y
1659   else if (angle < M_PI*(-13.0/16.0))
1660   {
1661     dy=dy | 1; // Round up to next odd number
1662     *bx=ax + dy/2;
1663     *by=ay + dy;
1664   }
1665   // 45deg
1666   else if (angle < M_PI*(-11.0/16.0))
1667   {
1668     *by = (*by + ay + dx)/2;
1669     *bx = ax  - ay + *by;
1670   }
1671   // Iso close to negative X
1672   else if (angle < M_PI*(-9.0/16.0))
1673   {
1674     dx=dx | 1; // Round up to next odd number
1675     *bx=ax + dx;
1676     *by=ay + dx/2;
1677   }
1678   // Negative X
1679   else if (angle < M_PI*(-7.0/16.0))
1680   {
1681     *bx=ax + dx;
1682     *by=ay;
1683   }
1684   // Iso close to negative X
1685   else if (angle < M_PI*(-5.0/16.0))
1686   {
1687     dx=dx | 1; // Round up to next odd number
1688     *bx=ax + dx;
1689     *by=ay - dx/2;
1690   }
1691   // 45 degrees
1692   else if (angle < M_PI*(-3.0/16.0))
1693   {
1694     *by = (*by + ay - dx)/2;
1695     *bx = ax  + ay - *by;
1696   }
1697   // Iso close to positive Y
1698   else if (angle < M_PI*(-1.0/16.0))
1699   {
1700     dy=dy | 1; // Round up to next odd number
1701     *bx=ax - dy/2;
1702     *by=ay + dy;
1703   }
1704   // Positive Y
1705   else if (angle < M_PI*(1.0/16.0))
1706   {
1707     *bx=ax;
1708     *by=ay + dy;
1709   }
1710   // Iso close to positive Y
1711   else if (angle < M_PI*(3.0/16.0))
1712   {
1713     dy=dy | 1; // Round up to next odd number
1714     *bx=ax + dy/2;
1715     *by=ay + dy;
1716   }
1717   // 45 degrees
1718   else if (angle < M_PI*(5.0/16.0))
1719   {
1720     *by = (*by + ay + dx)/2;
1721     *bx = ax  - ay + *by;
1722   }
1723   // Iso close to positive X
1724   else if (angle < M_PI*(7.0/16.0))
1725   {
1726     dx=dx | 1; // Round up to next odd number
1727     *bx=ax + dx;
1728     *by=ay + dx/2;
1729   }
1730   // Positive X
1731   else if (angle < M_PI*(9.0/16.0))
1732   {
1733     *bx=ax + dx;
1734     *by=ay;
1735   }
1736   // Iso close to positive X
1737   else if (angle < M_PI*(11.0/16.0))
1738   {
1739     dx=dx | 1; // Round up to next odd number
1740     *bx=ax + dx;
1741     *by=ay - dx/2;
1742   }
1743   // 45 degrees
1744   else if (angle < M_PI*(13.0/16.0))
1745   {
1746     *by = (*by + ay - dx)/2;
1747     *bx = ax  + ay - *by;
1748   }
1749   // Iso close to negative Y
1750   else //if (angle < M_PI*(15.0/16.0))
1751   {
1752     dy=dy | 1; // Round up to next odd number
1753     *bx=ax - dy/2;
1754     *by=ay + dy;
1755   }
1756 
1757   return;
1758 }
1759 
1760   // -- Tracer général d'une ligne ------------------------------------------
1761 
Draw_line_general(short start_x,short start_y,short end_x,short end_y,byte color)1762 void Draw_line_general(short start_x,short start_y,short end_x,short end_y, byte color)
1763 {
1764   short x_pos,y_pos;
1765   short incr_x,incr_y;
1766   short i,cumul;
1767   short delta_x,delta_y;
1768 
1769   x_pos=start_x;
1770   y_pos=start_y;
1771 
1772   if (start_x<end_x)
1773   {
1774     incr_x=+1;
1775     delta_x=end_x-start_x;
1776   }
1777   else
1778   {
1779     incr_x=-1;
1780     delta_x=start_x-end_x;
1781   }
1782 
1783   if (start_y<end_y)
1784   {
1785     incr_y=+1;
1786     delta_y=end_y-start_y;
1787   }
1788   else
1789   {
1790     incr_y=-1;
1791     delta_y=start_y-end_y;
1792   }
1793 
1794   if (delta_y>delta_x)
1795   {
1796     cumul=delta_y>>1;
1797     for (i=1; i<delta_y; i++)
1798     {
1799       y_pos+=incr_y;
1800       cumul+=delta_x;
1801       if (cumul>=delta_y)
1802       {
1803         cumul-=delta_y;
1804         x_pos+=incr_x;
1805       }
1806       Pixel_figure(x_pos,y_pos,color);
1807     }
1808   }
1809   else
1810   {
1811     cumul=delta_x>>1;
1812     for (i=1; i<delta_x; i++)
1813     {
1814       x_pos+=incr_x;
1815       cumul+=delta_y;
1816       if (cumul>=delta_x)
1817       {
1818         cumul-=delta_x;
1819         y_pos+=incr_y;
1820       }
1821       Pixel_figure(x_pos,y_pos,color);
1822     }
1823   }
1824 
1825   if ( (start_x!=end_x) || (start_y!=end_y) )
1826     Pixel_figure(end_x,end_y,color);
1827 
1828 }
1829 
1830   // -- Tracer définitif d'une ligne --
1831 
Draw_line_permanent(short start_x,short start_y,short end_x,short end_y,byte color)1832 void Draw_line_permanent(short start_x,short start_y,short end_x,short end_y, byte color)
1833 {
1834 
1835   int w = end_x-start_x, h = end_y - start_y;
1836   Pixel_figure=Pixel_figure_permanent;
1837   Init_permanent_draw();
1838   Draw_line_general(start_x,start_y,end_x,end_y,color);
1839   Update_part_of_screen((start_x<end_x)?start_x:end_x,(start_y<end_y)?start_y:end_y,abs(w)+1,abs(h)+1);
1840 }
1841 
1842   // -- Tracer la preview d'une ligne --
1843 
Draw_line_preview(short start_x,short start_y,short end_x,short end_y,byte color)1844 void Draw_line_preview(short start_x,short start_y,short end_x,short end_y,byte color)
1845 {
1846   int w = end_x-start_x, h = end_y - start_y;
1847   Pixel_figure=Pixel_figure_preview;
1848   Draw_line_general(start_x,start_y,end_x,end_y,color);
1849   Update_part_of_screen((start_x<end_x)?start_x:end_x,(start_y<end_y)?start_y:end_y,abs(w)+1,abs(h)+1);
1850 }
1851 
1852   // -- Tracer la preview d'une ligne en xor --
1853 
Draw_line_preview_xor(short start_x,short start_y,short end_x,short end_y,byte color)1854 void Draw_line_preview_xor(short start_x,short start_y,short end_x,short end_y,byte color)
1855 {
1856   int w, h;
1857 
1858   Pixel_figure=(Func_pixel)Pixel_figure_preview_xor;
1859   // Needed a cast because this function supports signed shorts,
1860   // (it's usually in image space), while this time it's in screen space
1861   // and some line endpoints can be out of screen.
1862   Draw_line_general(start_x,start_y,end_x,end_y,color);
1863 
1864   if (start_x<Limit_left)
1865     start_x=Limit_left;
1866   if (start_y<Limit_top)
1867     start_y=Limit_top;
1868   if (end_x<Limit_left)
1869     end_x=Limit_left;
1870   if (end_y<Limit_top)
1871     end_y=Limit_top;
1872   // bottom & right limits are handled by Update_part_of_screen()
1873 
1874   w = end_x-start_x;
1875   h = end_y-start_y;
1876   Update_part_of_screen((start_x<end_x)?start_x:end_x,(start_y<end_y)?start_y:end_y,abs(w)+1,abs(h)+1);
1877 }
1878 
1879   // -- Tracer la preview d'une ligne en xor additif --
1880 
Draw_line_preview_xorback(short start_x,short start_y,short end_x,short end_y,byte color)1881 void Draw_line_preview_xorback(short start_x,short start_y,short end_x,short end_y,byte color)
1882 {
1883   int w = end_x-start_x, h = end_y - start_y;
1884   Pixel_figure=Pixel_figure_preview_xorback;
1885   Draw_line_general(start_x,start_y,end_x,end_y,color);
1886   Update_part_of_screen((start_x<end_x)?start_x:end_x,(start_y<end_y)?start_y:end_y,abs(w)+1,abs(h)+1);
1887 }
1888 
1889   // -- Effacer la preview d'une ligne --
1890 
Hide_line_preview(short start_x,short start_y,short end_x,short end_y)1891 void Hide_line_preview(short start_x,short start_y,short end_x,short end_y)
1892 {
1893   int w = end_x-start_x, h = end_y - start_y;
1894   Pixel_figure=Pixel_figure_clear_preview;
1895   Draw_line_general(start_x,start_y,end_x,end_y,0);
1896   Update_part_of_screen((start_x<end_x)?start_x:end_x,(start_y<end_y)?start_y:end_y,abs(w)+1,abs(h)+1);
1897 }
1898 
1899 
1900   // -- Tracer un rectangle vide --
1901 
Draw_empty_rectangle(short start_x,short start_y,short end_x,short end_y,byte color)1902 void Draw_empty_rectangle(short start_x,short start_y,short end_x,short end_y,byte color)
1903 {
1904   short temp;
1905   short x_pos;
1906   short y_pos;
1907 
1908 
1909   // On vérifie que les bornes soient dans le bon sens:
1910   if (start_x>end_x)
1911   {
1912     temp=start_x;
1913     start_x=end_x;
1914     end_x=temp;
1915   }
1916   if (start_y>end_y)
1917   {
1918     temp=start_y;
1919     start_y=end_y;
1920     end_y=temp;
1921   }
1922 
1923   // On trace le rectangle:
1924   Init_permanent_draw();
1925 
1926   for (x_pos=start_x;x_pos<=end_x;x_pos++)
1927   {
1928     Pixel_figure_permanent(x_pos,start_y,color);
1929     Pixel_figure_permanent(x_pos,  end_y,color);
1930   }
1931 
1932   for (y_pos=start_y+1;y_pos<end_y;y_pos++)
1933   {
1934     Pixel_figure_permanent(start_x,y_pos,color);
1935     Pixel_figure_permanent(  end_x,y_pos,color);
1936   }
1937 
1938 #if defined(__macosx__) || defined(__FreeBSD__)
1939   Update_part_of_screen(start_x,end_x,end_x-start_x,end_y-start_y);
1940 #endif
1941 }
1942 
1943   // -- Tracer un rectangle plein --
1944 
Draw_filled_rectangle(short start_x,short start_y,short end_x,short end_y,byte color)1945 void Draw_filled_rectangle(short start_x,short start_y,short end_x,short end_y,byte color)
1946 {
1947   short temp;
1948   short x_pos;
1949   short y_pos;
1950 
1951 
1952   // On vérifie que les bornes sont dans le bon sens:
1953   if (start_x>end_x)
1954   {
1955     temp=start_x;
1956     start_x=end_x;
1957     end_x=temp;
1958   }
1959   if (start_y>end_y)
1960   {
1961     temp=start_y;
1962     start_y=end_y;
1963     end_y=temp;
1964   }
1965 
1966   // Correction en cas de dépassement des limites de l'image
1967   if (end_x>Limit_right)
1968     end_x=Limit_right;
1969   if (end_y>Limit_bottom)
1970     end_y=Limit_bottom;
1971 
1972   // On trace le rectangle:
1973   for (y_pos=start_y;y_pos<=end_y;y_pos++)
1974     for (x_pos=start_x;x_pos<=end_x;x_pos++)
1975       // Display_pixel traite chaque pixel avec tous les effets ! (smear, ...)
1976       // Donc on ne peut pas otimiser en traçant ligne par ligne avec memset :(
1977       Display_pixel(x_pos,y_pos,color);
1978   Update_part_of_screen(start_x,start_y,end_x-start_x,end_y-start_y);
1979 
1980 }
1981 
1982 
1983 
1984 
1985   // -- Tracer une courbe de Bézier --
1986 
Draw_curve_general(short x1,short y1,short x2,short y2,short x3,short y3,short x4,short y4,byte color)1987 void Draw_curve_general(short x1, short y1,
1988                            short x2, short y2,
1989                            short x3, short y3,
1990                            short x4, short y4,
1991                            byte color)
1992 {
1993   float delta,t,t2,t3;
1994   short x,y,old_x,old_y;
1995   word  i;
1996   int   cx[4];
1997   int   cy[4];
1998 
1999   // Calcul des vecteurs de coefficients
2000   cx[0]= -   x1 + 3*x2 - 3*x3 + x4;
2001   cx[1]= + 3*x1 - 6*x2 + 3*x3;
2002   cx[2]= - 3*x1 + 3*x2;
2003   cx[3]= +   x1;
2004   cy[0]= -   y1 + 3*y2 - 3*y3 + y4;
2005   cy[1]= + 3*y1 - 6*y2 + 3*y3;
2006   cy[2]= - 3*y1 + 3*y2;
2007   cy[3]= +   y1;
2008 
2009   // Traçage de la courbe
2010   old_x=x1;
2011   old_y=y1;
2012   Pixel_figure(old_x,old_y,color);
2013   delta=0.05f; // 1.0/20
2014   t=0;
2015   for (i=1; i<=20; i++)
2016   {
2017     t=t+delta; t2=t*t; t3=t2*t;
2018     x=Round(t3*cx[0] + t2*cx[1] + t*cx[2] + cx[3]);
2019     y=Round(t3*cy[0] + t2*cy[1] + t*cy[2] + cy[3]);
2020     Draw_line_general(old_x,old_y,x,y,color);
2021     old_x=x;
2022     old_y=y;
2023   }
2024 
2025   x = Min(Min(x1,x2),Min(x3,x4));
2026   y = Min(Min(y1,y2),Min(y3,y4));
2027   old_x = Max(Max(x1,x2),Max(x3,x4)) - x;
2028   old_y = Max(Max(y1,y2),Max(y3,y4)) - y;
2029   Update_part_of_screen(x,y,old_x+1,old_y+1);
2030 }
2031 
2032   // -- Tracer une courbe de Bézier définitivement --
2033 
Draw_curve_permanent(short x1,short y1,short x2,short y2,short x3,short y3,short x4,short y4,byte color)2034 void Draw_curve_permanent(short x1, short y1,
2035                              short x2, short y2,
2036                              short x3, short y3,
2037                              short x4, short y4,
2038                              byte color)
2039 {
2040   Pixel_figure=Pixel_figure_permanent;
2041   Init_permanent_draw();
2042   Draw_curve_general(x1,y1,x2,y2,x3,y3,x4,y4,color);
2043 }
2044 
2045   // -- Tracer la preview d'une courbe de Bézier --
2046 
Draw_curve_preview(short x1,short y1,short x2,short y2,short x3,short y3,short x4,short y4,byte color)2047 void Draw_curve_preview(short x1, short y1,
2048                            short x2, short y2,
2049                            short x3, short y3,
2050                            short x4, short y4,
2051                            byte color)
2052 {
2053   Pixel_figure=Pixel_figure_preview;
2054   Draw_curve_general(x1,y1,x2,y2,x3,y3,x4,y4,color);
2055 }
2056 
2057   // -- Effacer la preview d'une courbe de Bézier --
2058 
Hide_curve_preview(short x1,short y1,short x2,short y2,short x3,short y3,short x4,short y4,byte color)2059 void Hide_curve_preview(short x1, short y1,
2060                             short x2, short y2,
2061                             short x3, short y3,
2062                             short x4, short y4,
2063                             byte color)
2064 {
2065   Pixel_figure=Pixel_figure_clear_preview;
2066   Draw_curve_general(x1,y1,x2,y2,x3,y3,x4,y4,color);
2067 }
2068 
2069 
2070 
2071 
2072   // -- Spray : un petit coup de Pschiitt! --
2073 
Airbrush(short clicked_button)2074 void Airbrush(short clicked_button)
2075 {
2076   short x_pos,y_pos;
2077   short radius=Airbrush_size>>1;
2078   long  radius_squared=(long)radius*radius;
2079   short index,count;
2080   byte  color_index;
2081   byte  direction;
2082 
2083 
2084   Hide_cursor();
2085 
2086   if (Airbrush_mode)
2087   {
2088     for (count=1; count<=Airbrush_mono_flow; count++)
2089     {
2090       x_pos=(rand()%Airbrush_size)-radius;
2091       y_pos=(rand()%Airbrush_size)-radius;
2092       if ( (x_pos*x_pos)+(y_pos*y_pos) <= radius_squared )
2093       {
2094         x_pos+=Paintbrush_X;
2095         y_pos+=Paintbrush_Y;
2096         if (clicked_button==1)
2097           Draw_paintbrush(x_pos,y_pos,Fore_color);
2098         else
2099           Draw_paintbrush(x_pos,y_pos,Back_color);
2100       }
2101     }
2102   }
2103   else
2104   {
2105     //   On essaye de se balader dans la table des flux de façon à ce que ce
2106     // ne soit pas toujours la dernière couleur qui soit affichée en dernier
2107     // Pour ça, on part d'une couleur au pif dans une direction aléatoire.
2108     direction=rand()&1;
2109     for (index=0,color_index=rand()/*%256*/; index<256; index++)
2110     {
2111       for (count=1; count<=Airbrush_multi_flow[color_index]; count++)
2112       {
2113         x_pos=(rand()%Airbrush_size)-radius;
2114         y_pos=(rand()%Airbrush_size)-radius;
2115         if ( (x_pos*x_pos)+(y_pos*y_pos) <= radius_squared )
2116         {
2117           x_pos+=Paintbrush_X;
2118           y_pos+=Paintbrush_Y;
2119           if (clicked_button==LEFT_SIDE)
2120             Draw_paintbrush(x_pos,y_pos,color_index);
2121           else
2122             Draw_paintbrush(x_pos,y_pos,Back_color);
2123         }
2124       }
2125       if (direction)
2126         color_index++;
2127       else
2128         color_index--;
2129     }
2130   }
2131 
2132   Display_cursor();
2133 }
2134 
2135 
2136 
2137   //////////////////////////////////////////////////////////////////////////
2138   ////////////////////////// GESTION DES DEGRADES //////////////////////////
2139   //////////////////////////////////////////////////////////////////////////
2140 
2141 
2142   // -- Gestion d'un dégradé de base (le plus moche) --
2143 
Gradient_basic(long index,short x_pos,short y_pos)2144 void Gradient_basic(long index,short x_pos,short y_pos)
2145 {
2146   long position;
2147 
2148   // On fait un premier calcul partiel
2149   position=(index*Gradient_bounds_range);
2150 
2151   // On gère un déplacement au hasard
2152   position+=(Gradient_total_range*(rand()%Gradient_random_factor)) >>6;
2153   position-=(Gradient_total_range*Gradient_random_factor) >>7;
2154 
2155   position/=Gradient_total_range;
2156 
2157   //   On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors
2158   // des valeurs autorisées par le dégradé défini par l'utilisateur.
2159 
2160   if (position<0)
2161     position=0;
2162   else if (position>=Gradient_bounds_range)
2163     position=Gradient_bounds_range-1;
2164 
2165   // On ramène ensuite la position dans le dégradé vers un numéro de couleur
2166   if (Gradient_is_inverted)
2167     Gradient_pixel(x_pos,y_pos,Gradient_upper_bound-position);
2168   else
2169     Gradient_pixel(x_pos,y_pos,Gradient_lower_bound+position);
2170 }
2171 
2172 
2173   // -- Gestion d'un dégradé par trames simples --
2174 
Gradient_dithered(long index,short x_pos,short y_pos)2175 void Gradient_dithered(long index,short x_pos,short y_pos)
2176 {
2177   long position_in_gradient;
2178   long position_in_segment;
2179 
2180   //
2181   //   But de l'opération: en plus de calculer la position de base (désignée
2182   // dans cette procédure par "position_in_gradient", on calcule la position
2183   // de l'indice dans le schéma suivant:
2184   //
2185   //         | Les indices qui traînent de ce côté du segment se voient subir
2186   //         | une incrémentation conditionnelle à leur position dans l'écran.
2187   //         v
2188   //  |---|---|---|---- - - -
2189   //   ^
2190   //   |_ Les indices qui traînent de ce côté du segment se voient subir une
2191   //      décrémentation conditionnelle à leur position dans l'écran.
2192 
2193   // On fait d'abord un premier calcul partiel
2194   position_in_gradient=(index*Gradient_bounds_range);
2195 
2196   // On gère un déplacement au hasard...
2197   position_in_gradient+=(Gradient_total_range*(rand()%Gradient_random_factor)) >>6;
2198   position_in_gradient-=(Gradient_total_range*Gradient_random_factor) >>7;
2199 
2200   if (position_in_gradient<0)
2201     position_in_gradient=0;
2202 
2203   // ... qui nous permet de calculer la position dans le segment
2204   position_in_segment=((position_in_gradient<<2)/Gradient_total_range)&3;
2205 
2206   // On peut ensuite terminer le calcul de l'indice dans le dégradé
2207   position_in_gradient/=Gradient_total_range;
2208 
2209   // On va pouvoir discuter de la valeur de position_in_gradient en fonction
2210   // de la position dans l'écran et de la position_in_segment.
2211 
2212   switch (position_in_segment)
2213   {
2214     case 0 : // On est sur la gauche du segment
2215       if (((x_pos+y_pos)&1)==0)
2216         position_in_gradient--;
2217       break;
2218 
2219       // On n'a pas à traiter les cas 1 et 2 car ils représentent des valeurs
2220       // suffisament au centre du segment pour ne pas avoir à subir la trame
2221 
2222     case 3 : // On est sur la droite du segment
2223       if (((x_pos+y_pos)&1)!=0) // Note: on doit faire le test inverse au cas gauche pour synchroniser les 2 côtés de la trame.
2224         position_in_gradient++;
2225   }
2226 
2227   //   On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors
2228   // des valeurs autorisées par le dégradé défini par l'utilisateur.
2229 
2230   if (position_in_gradient<0)
2231     position_in_gradient=0;
2232   else if (position_in_gradient>=Gradient_bounds_range)
2233     position_in_gradient=Gradient_bounds_range-1;
2234 
2235   // On ramène ensuite la position dans le dégradé vers un numéro de couleur
2236   if (Gradient_is_inverted)
2237     position_in_gradient=Gradient_upper_bound-position_in_gradient;
2238   else
2239     position_in_gradient=Gradient_lower_bound+position_in_gradient;
2240 
2241   Gradient_pixel(x_pos,y_pos,position_in_gradient);
2242 }
2243 
2244 
2245   // -- Gestion d'un dégradé par trames étendues --
2246 
Gradient_extra_dithered(long index,short x_pos,short y_pos)2247 void Gradient_extra_dithered(long index,short x_pos,short y_pos)
2248 {
2249   long position_in_gradient;
2250   long position_in_segment;
2251 
2252 //
2253   //   But de l'opération: en plus de calculer la position de base (désignée
2254   // dans cette procédure par "position_in_gradient", on calcule la position
2255   // de l'indice dans le schéma suivant:
2256   //
2257   //         | Les indices qui traînent de ce côté du segment se voient subir
2258   //         | une incrémentation conditionnelle à leur position dans l'écran.
2259   //         v
2260   //  |---|---|---|---- - - -
2261   //   ^
2262   //   |_ Les indices qui traînent de ce côté du segment se voient subir une
2263   //      décrémentation conditionnelle à leur position dans l'écran.
2264 
2265   // On fait d'abord un premier calcul partiel
2266   position_in_gradient=(index*Gradient_bounds_range);
2267 
2268   // On gère un déplacement au hasard
2269   position_in_gradient+=(Gradient_total_range*(rand()%Gradient_random_factor)) >>6;
2270   position_in_gradient-=(Gradient_total_range*Gradient_random_factor) >>7;
2271 
2272   if (position_in_gradient<0)
2273     position_in_gradient=0;
2274 
2275   // Qui nous permet de calculer la position dans le segment
2276   position_in_segment=((position_in_gradient<<3)/Gradient_total_range)&7;
2277 
2278   // On peut ensuite terminer le calcul de l'indice dans le dégradé
2279   position_in_gradient/=Gradient_total_range;
2280 
2281   // On va pouvoir discuter de la valeur de position_in_gradient en fonction
2282   // de la position dans l'écran et de la position_in_segment.
2283 
2284   switch (position_in_segment)
2285   {
2286     case 0 : // On est sur l'extrême gauche du segment
2287       if (((x_pos+y_pos)&1)==0)
2288         position_in_gradient--;
2289       break;
2290 
2291     case 1 : // On est sur la gauche du segment
2292     case 2 : // On est sur la gauche du segment
2293       if (((x_pos & 1)==0) && ((y_pos & 1)==0))
2294         position_in_gradient--;
2295       break;
2296 
2297       // On n'a pas à traiter les cas 3 et 4 car ils représentent des valeurs
2298       // suffisament au centre du segment pour ne pas avoir à subir la trame
2299 
2300     case 5 : // On est sur la droite du segment
2301     case 6 : // On est sur la droite du segment
2302       if (((x_pos & 1)==0) && ((y_pos & 1)!=0))
2303         position_in_gradient++;
2304       break;
2305 
2306     case 7 : // On est sur l'extreme droite du segment
2307       if (((x_pos+y_pos)&1)!=0) // Note: on doit faire le test inverse au cas gauche pour synchroniser les 2 côtés de la trame.
2308         position_in_gradient++;
2309   }
2310 
2311   //   On va vérifier que nos petites idioties n'ont pas éjecté la valeur hors
2312   // des valeurs autorisées par le dégradé défini par l'utilisateur.
2313 
2314   if (position_in_gradient<0)
2315     position_in_gradient=0;
2316   else if (position_in_gradient>=Gradient_bounds_range)
2317     position_in_gradient=Gradient_bounds_range-1;
2318 
2319   // On ramène ensuite la position dans le dégradé vers un numéro de couleur
2320   if (Gradient_is_inverted)
2321     position_in_gradient=Gradient_upper_bound-position_in_gradient;
2322   else
2323     position_in_gradient=Gradient_lower_bound+position_in_gradient;
2324 
2325   Gradient_pixel(x_pos,y_pos,position_in_gradient);
2326 }
2327 
2328 
2329 
2330   // -- Tracer un cercle degradé (une sphère) --
2331 
Draw_grad_circle(short center_x,short center_y,long sqradius,short spot_x,short spot_y)2332 void Draw_grad_circle(short center_x,short center_y,long sqradius,short spot_x,short spot_y)
2333 {
2334   long start_x;
2335   long start_y;
2336   long x_pos;
2337   long y_pos;
2338   long end_x;
2339   long end_y;
2340   long distance_x; // Distance (au carré) sur les X du point en cours au centre d'éclairage
2341   long distance_y; // Distance (au carré) sur les Y du point en cours au centre d'éclairage
2342   long x, y;
2343   short radius = sqrt(sqradius);
2344 
2345   start_x=center_x-radius;
2346   start_y=center_y-radius;
2347   end_x=center_x+radius;
2348   end_y=center_y+radius;
2349 
2350   // Correction des bornes d'après les limites
2351   if (start_y<Limit_top)
2352     start_y=Limit_top;
2353   if (end_y>Limit_bottom)
2354     end_y=Limit_bottom;
2355   if (start_x<Limit_left)
2356     start_x=Limit_left;
2357   if (end_x>Limit_right)
2358     end_x=Limit_right;
2359 
2360   Gradient_total_range=sqradius+
2361                            ((center_x-spot_x)*(center_x-spot_x))+
2362                            ((center_y-spot_y)*(center_y-spot_y))+
2363                            (2L*radius*sqrt(
2364                            ((center_x-spot_x)*(center_x-spot_x))+
2365                            ((center_y-spot_y)*(center_y-spot_y))));
2366 
2367   if (Gradient_total_range==0)
2368     Gradient_total_range=1;
2369 
2370   // Affichage du cercle
2371   for (y_pos=start_y,y=(long)start_y-center_y;y_pos<=end_y;y_pos++,y++)
2372   {
2373     distance_y =(y_pos-spot_y);
2374     distance_y*=distance_y;
2375     for (x_pos=start_x,x=(long)start_x-center_x;x_pos<=end_x;x_pos++,x++)
2376       if (Pixel_in_circle(x, y, sqradius))
2377       {
2378         distance_x =(x_pos-spot_x);
2379         distance_x*=distance_x;
2380         Gradient_function(distance_x+distance_y,x_pos,y_pos);
2381       }
2382   }
2383 
2384   Update_part_of_screen(center_x-radius,center_y-radius,2*radius+1,2*radius+1);
2385 }
2386 
2387 
2388   // -- Tracer une ellipse degradée --
2389 
Draw_grad_ellipse(short center_x,short center_y,short horizontal_radius,short vertical_radius,short spot_x,short spot_y)2390 void Draw_grad_ellipse(short center_x,short center_y,short horizontal_radius,short vertical_radius,short spot_x,short spot_y)
2391 {
2392   long start_x;
2393   long start_y;
2394   long x_pos;
2395   long y_pos;
2396   long end_x;
2397   long end_y;
2398   long distance_x; // Distance (au carré) sur les X du point en cours au centre d'éclairage
2399   long distance_y; // Distance (au carré) sur les Y du point en cours au centre d'éclairage
2400   long x, y;
2401   T_Ellipse_limits Ellipse;
2402 
2403   start_x=center_x-horizontal_radius;
2404   start_y=center_y-vertical_radius;
2405   end_x=center_x+horizontal_radius;
2406   end_y=center_y+vertical_radius;
2407 
2408   // Calcul des limites de l'ellipse
2409   Ellipse_compute_limites(horizontal_radius+1, vertical_radius+1, &Ellipse);
2410 
2411   // On calcule la distance maximale:
2412   Gradient_total_range=(horizontal_radius*horizontal_radius)+
2413                            (vertical_radius*vertical_radius)+
2414                            ((center_x-spot_x)*(center_x-spot_x))+
2415                            ((center_y-spot_y)*(center_y-spot_y))+
2416                            (2L
2417                            *sqrt(
2418                            (horizontal_radius*horizontal_radius)+
2419                            (vertical_radius  *vertical_radius  ))
2420                            *sqrt(
2421                            ((center_x-spot_x)*(center_x-spot_x))+
2422                            ((center_y-spot_y)*(center_y-spot_y))));
2423 
2424   if (Gradient_total_range==0)
2425     Gradient_total_range=1;
2426 
2427   // Correction des bornes d'après les limites
2428   if (start_y<Limit_top)
2429     start_y=Limit_top;
2430   if (end_y>Limit_bottom)
2431     end_y=Limit_bottom;
2432   if (start_x<Limit_left)
2433     start_x=Limit_left;
2434   if (end_x>Limit_right)
2435     end_x=Limit_right;
2436 
2437   // Affichage de l'ellipse
2438   for (y_pos=start_y,y=start_y-center_y;y_pos<=end_y;y_pos++,y++)
2439   {
2440     distance_y =(y_pos-spot_y);
2441     distance_y*=distance_y;
2442     for (x_pos=start_x,x=start_x-center_x;x_pos<=end_x;x_pos++,x++)
2443       if (Pixel_in_ellipse(x, y, &Ellipse))
2444       {
2445         distance_x =(x_pos-spot_x);
2446         distance_x*=distance_x;
2447         Gradient_function(distance_x+distance_y,x_pos,y_pos);
2448       }
2449   }
2450 
2451   Update_part_of_screen(start_x,start_y,end_x-start_x+1,end_y-start_y+1);
2452 }
2453 
Draw_grad_inscribed_ellipse(short x1,short y1,short x2,short y2,short spot_x,short spot_y)2454 void Draw_grad_inscribed_ellipse(short x1, short y1, short x2, short y2, short spot_x, short spot_y)
2455 {
2456   short left, right, top, bottom;
2457   short dbl_center_x; // double of center_x
2458   short dbl_center_y; // double of center_y
2459   short dbl_x_radius; // double of horizontal radius
2460   short dbl_y_radius; // double of vertical radius
2461   long sq_dbl_x_radius;
2462   long sq_dbl_y_radius;
2463   qword sq_dbl_radius_product;
2464   short x_pos;
2465   short y_pos;
2466   long sq_dist_x; // Square horizontal distance with the lightning point
2467   long sq_dist_y; // Square vertical distance with the lightning point
2468 
2469   if (x1 > x2)
2470   {
2471     left = x2;
2472     right = x1;
2473   }
2474   else
2475   {
2476     left = x1;
2477     right = x2;
2478   }
2479   if (y1 > y2)
2480   {
2481     top = y2;
2482     bottom = y1;
2483   }
2484   else
2485   {
2486     top = y1;
2487     bottom = y2;
2488   }
2489   dbl_center_x = left+right;
2490   dbl_center_y = top+bottom;
2491   dbl_x_radius = right-left+1;
2492   dbl_y_radius = bottom-top+1;
2493   if ((Selected_circle_ellipse_mode & MASK_CIRCLE_ELLIPSE) == MODE_CIRCLE)
2494   {
2495     if (dbl_x_radius > dbl_y_radius)
2496       dbl_x_radius = dbl_y_radius;
2497     else
2498       dbl_y_radius = dbl_x_radius;
2499   }
2500   sq_dbl_x_radius = (long)dbl_x_radius*dbl_x_radius;
2501   sq_dbl_y_radius = (long)dbl_y_radius*dbl_y_radius;
2502   sq_dbl_radius_product = (qword)sq_dbl_x_radius * sq_dbl_y_radius;
2503 
2504   // calculate grandient range
2505   Gradient_total_range= (sq_dbl_x_radius + sq_dbl_y_radius) / 4 +
2506                            ((dbl_center_x/2-spot_x)*(dbl_center_x/2-spot_x))+
2507                            ((dbl_center_y/2-spot_y)*(dbl_center_y/2-spot_y))+
2508                            (sqrt(sq_dbl_x_radius + sq_dbl_y_radius)
2509                            *sqrt(
2510                            ((dbl_center_x/2-spot_x)*(dbl_center_x/2-spot_x))+
2511                            ((dbl_center_y/2-spot_y)*(dbl_center_y/2-spot_y))));
2512 
2513   if (Gradient_total_range==0)
2514     Gradient_total_range=1;
2515 
2516   // Apply limits to final dimensions to draw.
2517   if (left < Limit_left)
2518     left = Limit_left;
2519   if (top < Limit_top)
2520     top = Limit_top;
2521   if (right > Limit_right)
2522     right = Limit_right;
2523   if (bottom > Limit_bottom)
2524     bottom = Limit_bottom;
2525 
2526   for (y_pos = top; y_pos <= bottom; y_pos++)
2527   {
2528     long dbl_y = 2*y_pos - dbl_center_y;
2529     long sq_dbl_y = dbl_y*dbl_y;
2530     sq_dist_y =(y_pos-spot_y);
2531     sq_dist_y *= sq_dist_y;
2532     for (x_pos = left; x_pos <= right; x_pos++)
2533     {
2534       long dbl_x = 2*x_pos - dbl_center_x;
2535       long sq_dbl_x = dbl_x*dbl_x;
2536       if (((qword)sq_dbl_x * sq_dbl_y_radius + (qword)sq_dbl_y * sq_dbl_x_radius) < sq_dbl_radius_product)
2537       {
2538         sq_dist_x =(x_pos-spot_x);
2539         sq_dist_x *= sq_dist_x;
2540         Gradient_function(sq_dist_x+sq_dist_y,x_pos,y_pos);
2541       }
2542     }
2543   }
2544 
2545   Update_part_of_screen(left, top, right-left+1, bottom-top+1);
2546 }
2547 
2548 // Tracé d'un rectangle (rax ray - rbx rby) dégradé selon le vecteur (vax vay - vbx - vby)
Draw_grad_rectangle(short rax,short ray,short rbx,short rby,short vax,short vay,short vbx,short vby)2549 void Draw_grad_rectangle(short rax,short ray,short rbx,short rby,short vax,short vay, short vbx, short vby)
2550 {
2551     short y_pos, x_pos;
2552 
2553     // On commence par s'assurer que le rectangle est à l'endroit
2554     if(rbx < rax)
2555     {
2556       x_pos = rbx;
2557       rbx = rax;
2558       rax = x_pos;
2559     }
2560 
2561     if(rby < ray)
2562     {
2563       y_pos = rby;
2564       rby = ray;
2565       ray = y_pos;
2566     }
2567 
2568     // Correction des bornes d'après les limites
2569     if (ray<Limit_top)
2570       ray=Limit_top;
2571     if (rby>Limit_bottom)
2572       rby=Limit_bottom;
2573     if (rax<Limit_left)
2574       rax=Limit_left;
2575     if (rbx>Limit_right)
2576       rbx=Limit_right;
2577 
2578     if(vbx == vax)
2579     {
2580       // Le vecteur est vertical, donc on évite la partie en dessous qui foirerait avec une division par 0...
2581       if (vby == vay) return;  // L'utilisateur fait n'importe quoi
2582       Gradient_total_range = abs(vby - vay);
2583       for(y_pos=ray;y_pos<=rby;y_pos++)
2584         for(x_pos=rax;x_pos<=rbx;x_pos++)
2585           Gradient_function(abs(vby - y_pos),x_pos,y_pos);
2586 
2587     }
2588     else
2589     {
2590       float a;
2591       float b;
2592       float distance_x, distance_y;
2593 
2594       Gradient_total_range = sqrt(pow(vby - vay,2)+pow(vbx - vax,2));
2595       a = (float)(vby - vay)/(float)(vbx - vax);
2596       b = vay - a*vax;
2597 
2598       for (y_pos=ray;y_pos<=rby;y_pos++)
2599         for (x_pos = rax;x_pos<=rbx;x_pos++)
2600         {
2601           // On calcule ou on en est dans le dégradé
2602           distance_x = pow((y_pos - vay),2)+pow((x_pos - vax),2);
2603           distance_y = pow((-a * x_pos + y_pos - b),2)/(a*a+1);
2604 
2605           Gradient_function((int)sqrt(distance_x - distance_y),x_pos,y_pos);
2606         }
2607     }
2608     Update_part_of_screen(rax,ray,rbx,rby);
2609 }
2610 
2611 
2612 
2613 
2614 // -- Tracer un polygône plein --
2615 
2616 typedef struct T_Polygon_edge      /* an active edge */
2617 {
2618     short top;                     /* top y position */
2619     short bottom;                  /* bottom y position */
2620     float x, dx;                   /* floating point x position and gradient */
2621     float w;                       /* width of line segment */
2622     struct T_Polygon_edge *prev;     /* doubly linked list */
2623     struct T_Polygon_edge *next;
2624 } T_Polygon_edge;
2625 
2626 
2627 
2628 /* Fill_edge_structure:
2629  *  Polygon helper function: initialises an edge structure for the 2d
2630  *  rasteriser.
2631  */
Fill_edge_structure(T_Polygon_edge * edge,short * i1,short * i2)2632 void Fill_edge_structure(T_Polygon_edge *edge, short *i1, short *i2)
2633 {
2634   short *it;
2635 
2636   if (i2[1] < i1[1])
2637   {
2638     it = i1;
2639     i1 = i2;
2640     i2 = it;
2641   }
2642 
2643   edge->top = i1[1];
2644   edge->bottom = i2[1] - 1;
2645   edge->dx = ((float) i2[0] - (float) i1[0]) / ((float) i2[1] - (float) i1[1]);
2646   edge->x = i1[0] + 0.4999999;
2647   edge->prev = NULL;
2648   edge->next = NULL;
2649 
2650   if (edge->dx+1 < 0.0)
2651     edge->x += edge->dx+1;
2652 
2653   if (edge->dx >= 0.0)
2654     edge->w = edge->dx;
2655   else
2656     edge->w = -(edge->dx);
2657 
2658   if (edge->w-1.0<0.0)
2659     edge->w = 0.0;
2660   else
2661     edge->w = edge->w-1;
2662 }
2663 
2664 
2665 
2666 /* Add_edge:
2667  *  Adds an edge structure to a linked list, returning the new head pointer.
2668  */
Add_edge(T_Polygon_edge * list,T_Polygon_edge * edge,int sort_by_x)2669 T_Polygon_edge * Add_edge(T_Polygon_edge *list, T_Polygon_edge *edge, int sort_by_x)
2670 {
2671   T_Polygon_edge *pos = list;
2672   T_Polygon_edge *prev = NULL;
2673 
2674   if (sort_by_x)
2675   {
2676     while ( (pos) && ((pos->x+((pos->w+pos->dx)/2)) < (edge->x+((edge->w+edge->dx)/2))) )
2677     {
2678       prev = pos;
2679       pos = pos->next;
2680     }
2681   }
2682   else
2683   {
2684     while ((pos) && (pos->top < edge->top))
2685     {
2686       prev = pos;
2687       pos = pos->next;
2688     }
2689   }
2690 
2691   edge->next = pos;
2692   edge->prev = prev;
2693 
2694   if (pos)
2695     pos->prev = edge;
2696 
2697   if (prev)
2698   {
2699     prev->next = edge;
2700     return list;
2701   }
2702   else
2703     return edge;
2704 }
2705 
2706 
2707 
2708 /* Remove_edge:
2709  *  Removes an edge structure from a list, returning the new head pointer.
2710  */
Remove_edge(T_Polygon_edge * list,T_Polygon_edge * edge)2711 T_Polygon_edge * Remove_edge(T_Polygon_edge *list, T_Polygon_edge *edge)
2712 {
2713   if (edge->next)
2714     edge->next->prev = edge->prev;
2715 
2716   if (edge->prev)
2717   {
2718     edge->prev->next = edge->next;
2719     return list;
2720   }
2721   else
2722     return edge->next;
2723 }
2724 
2725 
2726 
2727 /* polygon:
2728  *  Draws a filled polygon with an arbitrary number of corners. Pass the
2729  *  number of vertices, then an array containing a series of x, y points
2730  *  (a total of vertices*2 values).
2731  */
Polyfill_general(int vertices,short * points,int color)2732 void Polyfill_general(int vertices, short * points, int color)
2733 {
2734   short c;
2735   short top;
2736   short bottom;
2737   short *i1, *i2;
2738   short x_pos,end_x;
2739   T_Polygon_edge *edge, *next_edge, *initial_edge;
2740   T_Polygon_edge *active_edges = NULL;
2741   T_Polygon_edge *inactive_edges = NULL;
2742 
2743   if (vertices < 1)
2744     return;
2745 
2746   top = bottom = points[1];
2747 
2748   /* allocate some space and fill the edge table */
2749   initial_edge=edge=(T_Polygon_edge *) malloc(sizeof(T_Polygon_edge) * vertices);
2750 
2751   i1 = points;
2752   i2 = points + ((vertices-1)<<1);
2753 
2754   for (c=0; c<vertices; c++)
2755   {
2756     if (i1[1] != i2[1])
2757     {
2758       Fill_edge_structure(edge, i1, i2);
2759 
2760       if (edge->bottom >= edge->top)
2761       {
2762         if (edge->top < top)
2763           top = edge->top;
2764 
2765         if (edge->bottom > bottom)
2766           bottom = edge->bottom;
2767 
2768         inactive_edges = Add_edge(inactive_edges, edge, 0);
2769         edge++;
2770       }
2771     }
2772     i2 = i1;
2773     i1 += 2;
2774   }
2775 
2776   /* for each scanline in the polygon... */
2777   for (c=top; c<=bottom; c++)
2778   {
2779     /* check for newly active edges */
2780     edge = inactive_edges;
2781     while ((edge) && (edge->top == c))
2782     {
2783       next_edge = edge->next;
2784       inactive_edges = Remove_edge(inactive_edges, edge);
2785       active_edges = Add_edge(active_edges, edge, 1);
2786       edge = next_edge;
2787     }
2788 
2789     /* draw horizontal line segments */
2790     if ((c>=Limit_top) && (c<=Limit_bottom))
2791     {
2792       edge = active_edges;
2793       while ((edge) && (edge->next))
2794       {
2795         x_pos=/*Round*/(edge->x);
2796         end_x=/*Round*/(edge->next->x+edge->next->w);
2797         if (x_pos<Limit_left)
2798           x_pos=Limit_left;
2799         if (end_x>Limit_right)
2800           end_x=Limit_right;
2801         for (; x_pos<=end_x; x_pos++)
2802           Pixel_figure(x_pos,c,color);
2803         edge = edge->next->next;
2804       }
2805     }
2806 
2807     /* update edges, sorting and removing dead ones */
2808     edge = active_edges;
2809     while (edge)
2810     {
2811       next_edge = edge->next;
2812       if (c >= edge->bottom)
2813         active_edges = Remove_edge(active_edges, edge);
2814       else
2815       {
2816         edge->x += edge->dx;
2817         while ((edge->prev) && ( (edge->x+(edge->w/2)) < (edge->prev->x+(edge->prev->w/2))) )
2818         {
2819           if (edge->next)
2820             edge->next->prev = edge->prev;
2821           edge->prev->next = edge->next;
2822           edge->next = edge->prev;
2823           edge->prev = edge->prev->prev;
2824           edge->next->prev = edge;
2825           if (edge->prev)
2826             edge->prev->next = edge;
2827           else
2828             active_edges = edge;
2829         }
2830       }
2831       edge = next_edge;
2832     }
2833   }
2834 
2835   free(initial_edge);
2836   initial_edge = NULL;
2837 
2838   // On ne connait pas simplement les xmin et xmax ici, mais de toutes façon ce n'est pas utilisé en preview
2839   Update_part_of_screen(0,top,Main.image_width,bottom-top+1);
2840 }
2841 
2842 
Polyfill(int vertices,short * points,int color)2843 void Polyfill(int vertices, short * points, int color)
2844 {
2845   int index;
2846 
2847   Pixel_clipped(points[0],points[1],color);
2848   if (vertices==1)
2849   {
2850     Update_part_of_screen(points[0],points[1],1,1);
2851     return;
2852   }
2853 
2854   // Comme pour le Fill, cette operation fait un peu d'"overdraw"
2855   // (pixels dessinés plus d'une fois) alors on force le FX Feedback à OFF
2856   Update_FX_feedback(0);
2857 
2858   Pixel_figure=Pixel_clipped;
2859   Polyfill_general(vertices,points,color);
2860 
2861   // Remarque: pour dessiner la bordure avec la brosse en cours au lieu
2862   // d'un pixel de couleur premier-plan, il suffit de mettre ici:
2863   // Pixel_figure=Pixel_figure_permanent;
2864 
2865   // Dessin du contour
2866   for (index=0; index<vertices-1;index+=1)
2867     Draw_line_general(points[index*2],points[index*2+1],points[index*2+2],points[index*2+3],color);
2868   Draw_line_general(points[0],points[1],points[index*2],points[index*2+1],color);
2869 
2870   // Restore original feedback value
2871   Update_FX_feedback(Config.FX_Feedback);
2872 
2873 }
2874 
2875 
2876 
2877 //------------ Remplacement de la couleur pointée par une autre --------------
2878 
Replace(byte new_color)2879 void Replace(byte new_color)
2880 {
2881   byte old_color;
2882 
2883   if ((Paintbrush_X<Main.image_width)
2884    && (Paintbrush_Y<Main.image_height))
2885   {
2886     old_color=Read_pixel_from_current_layer(Paintbrush_X,Paintbrush_Y);
2887     if ( (old_color!=new_color)
2888       && ((!Stencil_mode) || (!Stencil[old_color])) )
2889     {
2890       word x;
2891       word y;
2892 
2893       // Update all pixels
2894       for (y=0; y<Main.image_height; y++)
2895         for (x=0; x<Main.image_width; x++)
2896           if (Read_pixel_from_current_layer(x,y) == old_color)
2897             Pixel_in_current_screen(x,y,new_color);
2898     }
2899   }
2900 }
2901 
2902 
2903 
2904 /******************************************************************************/
2905 /********************************** SHADES ************************************/
2906 
2907 // Transformer une liste de shade en deux tables de conversion
Shade_list_to_lookup_tables(word * list,short step,byte mode,byte * table_inc,byte * table_dec)2908 void Shade_list_to_lookup_tables(word * list,short step,byte mode,byte * table_inc,byte * table_dec)
2909 {
2910   int index;
2911   int first;
2912   int last;
2913   int color;
2914   int temp;
2915 
2916 
2917   // On initialise les deux tables de conversion en Identité
2918   for (index=0;index<256;index++)
2919   {
2920     table_inc[index]=index;
2921     table_dec[index]=index;
2922   }
2923 
2924   // On s'apprête à examiner l'ensemble de la liste
2925   for (index=0;index<512;index++)
2926   {
2927     // On recherche la première case de la liste non vide (et non inhibée)
2928     while ((index<512) && (list[index]>255))
2929       index++;
2930 
2931     // On note la position de la première case de la séquence
2932     first=index;
2933 
2934     // On recherche la position de la dernière case de la séquence
2935     for (last=first;list[last+1]<256;last++);
2936 
2937     // Pour toutes les cases non vides (et non inhibées) qui suivent
2938     switch (mode)
2939     {
2940       case SHADE_MODE_NORMAL :
2941         for (;(index<512) && (list[index]<256);index++)
2942         { // On met à jour les tables de conversion
2943           color=list[index];
2944           table_inc[color]=list[(index+step<=last)?index+step:last];
2945           table_dec[color]=list[(index-step>=first)?index-step:first];
2946         }
2947         break;
2948       case SHADE_MODE_LOOP :
2949         temp=1+last-first;
2950         for (;(index<512) && (list[index]<256);index++)
2951         { // On met à jour les tables de conversion
2952           color=list[index];
2953           table_inc[color]=list[first+((step+index-first)%temp)];
2954           table_dec[color]=list[first+(((temp-step)+index-first)%temp)];
2955         }
2956         break;
2957       default : // SHADE_MODE_NOSAT
2958         for (;(index<512) && (list[index]<256);index++)
2959         { // On met à jour les tables de conversion
2960           color=list[index];
2961           if (index+step<=last)
2962             table_inc[color]=list[index+step];
2963           if (index-step>=first)
2964             table_dec[color]=list[index-step];
2965         }
2966     }
2967   }
2968 }
2969 
2970 
2971 
2972 // -- Interface avec l'image, affectée par le facteur de grossissement -------
2973 
2974   // fonction d'affichage "Pixel" utilisée pour les opérations définitivement
2975   // Ne doit à aucune condition être appelée en dehors de la partie visible
2976   // de l'image dans l'écran (ça pourrait être grave)
Display_pixel(word x,word y,byte color)2977 void Display_pixel(word x,word y,byte color)
2978   // x & y    sont la position d'un point dans l'IMAGE
2979   // color  est la couleur du point
2980   // Le Stencil est géré.
2981   // Les effets sont gérés par appel à Effect_function().
2982   // La Loupe est gérée par appel à Pixel_preview().
2983 {
2984   if ( ( (!Sieve_mode)   || (Effect_sieve(x,y)) )
2985     && (!((Stencil_mode) && (Stencil[Read_pixel_from_current_layer(x,y)])))
2986     && (!((Mask_mode)    && (Mask_table[Read_pixel_from_spare_screen(x,y)]))) )
2987   {
2988     color=Effect_function(x,y,color);
2989     if (Main.tilemap_mode)
2990     {
2991       Tilemap_draw(x,y, color);
2992     }
2993     else
2994       Pixel_in_current_screen_with_preview(x,y,color);
2995   }
2996 }
2997 
2998 
2999 
3000 // -- Calcul des différents effets -------------------------------------------
3001 
3002   // -- Aucun effet en cours --
3003 
No_effect(word x,word y,byte color)3004 byte No_effect(word x, word y, byte color)
3005 {
3006   (void)x; // unused
3007   (void)y; // unused
3008 
3009   return color;
3010 }
3011 
3012   // -- Effet de Shading --
3013 
Effect_shade(word x,word y,byte color)3014 byte Effect_shade(word x,word y,byte color)
3015 {
3016   (void)color; // unused
3017 
3018   return Shade_table[Read_pixel_from_feedback_screen(x,y)];
3019 }
3020 
Effect_quick_shade(word x,word y,byte color)3021 byte Effect_quick_shade(word x,word y,byte color)
3022 {
3023   int c=color=Read_pixel_from_feedback_screen(x,y);
3024   int direction=(Fore_color<=Back_color);
3025   byte start,end;
3026   int width;
3027 
3028   if (direction)
3029   {
3030     start=Fore_color;
3031     end  =Back_color;
3032   }
3033   else
3034   {
3035     start=Back_color;
3036     end  =Fore_color;
3037   }
3038 
3039   if ((c>=start) && (c<=end) && (start!=end))
3040   {
3041     width=1+end-start;
3042 
3043     if ( ((Shade_table==Shade_table_left) && direction) || ((Shade_table==Shade_table_right) && (!direction)) )
3044       c-=Quick_shade_step%width;
3045     else
3046       c+=Quick_shade_step%width;
3047 
3048     if (c<start)
3049       switch (Quick_shade_loop)
3050       {
3051         case SHADE_MODE_NORMAL : return start;
3052         case SHADE_MODE_LOOP : return (width+c);
3053         default : return color;
3054       }
3055 
3056     if (c>end)
3057       switch (Quick_shade_loop)
3058       {
3059         case SHADE_MODE_NORMAL : return end;
3060         case SHADE_MODE_LOOP : return (c-width);
3061         default : return color;
3062       }
3063   }
3064 
3065   return c;
3066 }
3067 
3068   // -- Effet de Tiling --
3069 
Effect_tiling(word x,word y,byte color)3070 byte Effect_tiling(word x,word y,byte color)
3071 {
3072   (void)color; // unused
3073 
3074   return Read_pixel_from_brush((x+Brush_width-Tiling_offset_X)%Brush_width,
3075                                (y+Brush_height-Tiling_offset_Y)%Brush_height);
3076 }
3077 
3078   // -- Effet de Smooth --
3079 
Effect_smooth(word x,word y,byte color)3080 byte Effect_smooth(word x,word y,byte color)
3081 {
3082   int r,g,b;
3083   byte c;
3084   int weight,total_weight;
3085   byte x2=((x+1)<Main.image_width);
3086   byte y2=((y+1)<Main.image_height);
3087   (void)color; // unused
3088 
3089   // On commence par le pixel central
3090   c=Read_pixel_from_feedback_screen(x,y);
3091   total_weight=Smooth_matrix[1][1];
3092   r=total_weight*Main.palette[c].R;
3093   g=total_weight*Main.palette[c].G;
3094   b=total_weight*Main.palette[c].B;
3095 
3096   if (x)
3097   {
3098     c=Read_pixel_from_feedback_screen(x-1,y);
3099     total_weight+=(weight=Smooth_matrix[0][1]);
3100     r+=weight*Main.palette[c].R;
3101     g+=weight*Main.palette[c].G;
3102     b+=weight*Main.palette[c].B;
3103 
3104     if (y)
3105     {
3106       c=Read_pixel_from_feedback_screen(x-1,y-1);
3107       total_weight+=(weight=Smooth_matrix[0][0]);
3108       r+=weight*Main.palette[c].R;
3109       g+=weight*Main.palette[c].G;
3110       b+=weight*Main.palette[c].B;
3111 
3112       if (y2)
3113       {
3114         c=Read_pixel_from_feedback_screen(x-1,y+1);
3115         total_weight+=(weight=Smooth_matrix[0][2]);
3116         r+=weight*Main.palette[c].R;
3117         g+=weight*Main.palette[c].G;
3118         b+=weight*Main.palette[c].B;
3119       }
3120     }
3121   }
3122 
3123   if (x2)
3124   {
3125     c=Read_pixel_from_feedback_screen(x+1,y);
3126     total_weight+=(weight=Smooth_matrix[2][1]);
3127     r+=weight*Main.palette[c].R;
3128     g+=weight*Main.palette[c].G;
3129     b+=weight*Main.palette[c].B;
3130 
3131     if (y)
3132     {
3133       c=Read_pixel_from_feedback_screen(x+1,y-1);
3134       total_weight+=(weight=Smooth_matrix[2][0]);
3135       r+=weight*Main.palette[c].R;
3136       g+=weight*Main.palette[c].G;
3137       b+=weight*Main.palette[c].B;
3138 
3139       if (y2)
3140       {
3141         c=Read_pixel_from_feedback_screen(x+1,y+1);
3142         total_weight+=(weight=Smooth_matrix[2][2]);
3143         r+=weight*Main.palette[c].R;
3144         g+=weight*Main.palette[c].G;
3145         b+=weight*Main.palette[c].B;
3146       }
3147     }
3148   }
3149 
3150   if (y)
3151   {
3152     c=Read_pixel_from_feedback_screen(x,y-1);
3153     total_weight+=(weight=Smooth_matrix[1][0]);
3154     r+=weight*Main.palette[c].R;
3155     g+=weight*Main.palette[c].G;
3156     b+=weight*Main.palette[c].B;
3157   }
3158 
3159   if (y2)
3160   {
3161     c=Read_pixel_from_feedback_screen(x,y+1);
3162     total_weight+=(weight=Smooth_matrix[1][2]);
3163     r+=weight*Main.palette[c].R;
3164     g+=weight*Main.palette[c].G;
3165     b+=weight*Main.palette[c].B;
3166   }
3167 
3168   return (total_weight)? // On regarde s'il faut éviter le 0/0.
3169     Best_color(Round_div(r,total_weight),
3170                       Round_div(g,total_weight),
3171                       Round_div(b,total_weight)):
3172     Read_pixel_from_current_screen(x,y); // C'est bien l'écran courant et pas
3173                                        // l'écran feedback car il s'agit de ne
3174 }                                      // pas modifier l'écran courant.
3175 
Effect_layer_copy(word x,word y,byte color)3176 byte Effect_layer_copy(word x,word y,byte color)
3177 {
3178   if (color<Main.backups->Pages->Nb_layers)
3179   {
3180     return Read_pixel_from_layer(color, x, y);
3181   }
3182   return Read_pixel_from_feedback_screen(x,y);
3183 }
3184 
Horizontal_grid_line(word x_pos,word y_pos,word width)3185 void Horizontal_grid_line(word x_pos,word y_pos,word width)
3186 {
3187   int x;
3188 
3189   for (x=!(x_pos&1);x<width;x+=2)
3190     Pixel(x_pos+x, y_pos, xor_lut[Get_Screen_pixel((x_pos+x)*Pixel_width, (y_pos-1)*Pixel_height)]);
3191 }
3192 
Vertical_grid_line(word x_pos,word y_pos,word height)3193 void Vertical_grid_line(word x_pos,word y_pos,word height)
3194 {
3195   int y;
3196 
3197   for (y=!(y_pos&1);y<height;y+=2)
3198     Pixel(x_pos, y_pos+y, xor_lut[Get_Screen_pixel(x_pos*Pixel_width-1, (y_pos+y)*Pixel_height)]);
3199 }
3200 
3201 // Tile Grid
Redraw_grid(short x,short y,unsigned short w,unsigned short h)3202 void Redraw_grid(short x, short y, unsigned short w, unsigned short h)
3203 {
3204   int row, col;
3205   if (!Show_grid)
3206     return;
3207 
3208   row=y+((Snap_height*1000-(y-0)/Main.magnifier_factor-Main.magnifier_offset_Y+Snap_offset_Y-1)%Snap_height)*Main.magnifier_factor+Main.magnifier_factor-1;
3209   while (row < y+h)
3210   {
3211     Horizontal_grid_line(x, row, w);
3212     row+= Snap_height*Main.magnifier_factor;
3213   }
3214 
3215   col=x+((Snap_width*1000-(x-Main.X_zoom)/Main.magnifier_factor-Main.magnifier_offset_X+Snap_offset_X-1)%Snap_width)*Main.magnifier_factor+Main.magnifier_factor-1;
3216   while (col < x+w)
3217   {
3218     Vertical_grid_line(col, y, h);
3219     col+= Snap_width*Main.magnifier_factor;
3220   }
3221 }
3222 
Read_pixel_from_current_screen(word x,word y)3223 byte Read_pixel_from_current_screen(word x,word y)
3224 {
3225   byte depth;
3226   byte color;
3227 
3228   if (Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
3229   {
3230     return Read_pixel_from_current_layer(x, y);
3231   }
3232 
3233   if (Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5
3234   	|| Main.backups->Pages->Image_mode == IMAGE_MODE_RASTER)
3235     if (Main.current_layer==4)
3236       return Read_pixel_from_current_layer(x, y);
3237 
3238   color = *(Main_screen+y*Main.image_width+x);
3239   if (color != Main.backups->Pages->Transparent_color) // transparent color
3240     return color;
3241 
3242   depth = *(Main_visible_image_depth_buffer.Image+x+y*Main.image_width);
3243   return Read_pixel_from_layer(depth, x, y);
3244 }
3245 
3246 /// Paint a a single pixel in image and optionnaly on screen: as-is.
Pixel_in_screen_direct_with_opt_preview(word x,word y,byte color,int preview)3247 static void Pixel_in_screen_direct_with_opt_preview(word x, word y, byte color, int preview)
3248 {
3249   Pixel_in_current_layer(x, y, color);
3250   if (preview)
3251     Pixel_preview(x,y,color);
3252 }
3253 
3254 /// Paint a a single pixel in image and on optionnaly on screen : using layered display.
Pixel_in_screen_layered_with_opt_preview(word x,word y,byte color,int preview)3255 static void Pixel_in_screen_layered_with_opt_preview(word x,word y,byte color, int preview)
3256 {
3257   byte depth = *(Main_visible_image_depth_buffer.Image+x+y*Main.image_width);
3258   Pixel_in_current_layer(x, y, color);
3259   if ( depth <= Main.current_layer)
3260   {
3261     if (color == Main.backups->Pages->Transparent_color) // transparent color
3262       // fetch pixel color from the topmost visible layer
3263       color = Read_pixel_from_layer(depth, x, y);
3264 
3265     *(x+y*Main.image_width+Main_screen)=color;
3266 
3267     if (preview)
3268       Pixel_preview(x,y,color);
3269   }
3270 }
3271 
3272 /// Paint in a specific layer and update optionnaly the screen
Pixel_in_layer_with_opt_preview(int layer,word x,word y,byte color,int preview)3273 static void Pixel_in_layer_with_opt_preview(int layer, word x,word y,byte color, int preview)
3274 {
3275   byte depth = *(Main_visible_image_depth_buffer.Image+x+y*Main.image_width);
3276 
3277   Pixel_in_layer(layer, x, y, color);
3278   // if (depth > layer) => another layer hides this one
3279   if (depth <= layer && ((1 << layer) & Main.layers_visible))
3280   {
3281     if (color == Main.backups->Pages->Transparent_color) // transparent color
3282       // fetch pixel color from the topmost visible layer
3283       color = Read_pixel_from_layer(depth, x, y);
3284 
3285     Main_screen[x+y*Main.image_width]=color;
3286 
3287     if (preview)
3288       Pixel_preview(x,y,color);
3289   }
3290 }
3291 
3292 /// @defgroup constraints Special constaints drawing modes
3293 /// For 8bits machines modes (ZX Spectrum, C64, etc.)
3294 /// @{
3295 
3296 /// Paint a pixel in CPC EGX mode
3297 ///
3298 /// even lines have 2x more pixel than odd lines, but less colors
Pixel_in_screen_egx_with_opt_preview(word x,word y,byte color,int preview)3299 static void Pixel_in_screen_egx_with_opt_preview(word x,word y,byte color,int preview)
3300 {
3301   uint8_t mask;
3302   if (Main.backups->Pages->Image_mode == IMAGE_MODE_EGX)
3303   {
3304     mask = 0xF3;  // 11110011
3305   } else {
3306     mask = 0xFD;  // 11111101
3307   }
3308 
3309   if (y & 1)
3310   {
3311     Pixel_in_screen_layered_with_opt_preview(x & ~1,y,color,preview);
3312     Pixel_in_screen_layered_with_opt_preview(x |  1,y,color,preview);
3313   }
3314   else
3315     Pixel_in_screen_layered_with_opt_preview(x,y,color & mask,preview);
3316 }
3317 
3318 /// Paint a pixel in 40col Thomson MO/TO mode
3319 /// or TMS9918 Graphics 2
3320 ///
3321 /// Only 2 colors in a 8x1 pixel block
Pixel_in_screen_thomson_with_opt_preview(word x,word y,byte color,int preview)3322 static void Pixel_in_screen_thomson_with_opt_preview(word x,word y,byte color,int preview)
3323 {
3324   word start = x & 0xFFF8;
3325   word x2;
3326   uint8_t c1, c2;
3327 
3328   // The color we are going to replace
3329   c1 = Read_pixel_from_current_layer(x, y);
3330 
3331   if (c1 == color)
3332     return;
3333 
3334   for (x2 = 0; x2 < 8; x2++)
3335   {
3336     c2 = Read_pixel_from_current_layer(start+x2, y);
3337     if (c2 == color)
3338       continue;
3339     if (c2 != c1)
3340       break;
3341   }
3342 
3343   if (c2 == c1 || c2 == color)
3344   {
3345     // There was only one color, so we can add a second one.
3346     Pixel_in_screen_layered_with_opt_preview(x,y,color,preview);
3347     return;
3348   }
3349 
3350   for (x2 = 0; x2 < 8; x2++)
3351   {
3352     c2 = Read_pixel_from_current_layer(start+x2, y);
3353     if (c2 == c1) {
3354       Pixel_in_screen_layered_with_opt_preview(x2+start,y,color,preview);
3355     }
3356   }
3357 }
3358 
3359 /// Paint a pixel with 8x8 block constraints
3360 ///
3361 /// Used for ZX Spectrum and C64 HiRes modes.
3362 /// Only 2 colors in a 8x8 block, and for the ZX Spectrum both must be either bight or not.
Pixel_in_screen_zx_with_opt_preview(word x,word y,byte color,int preview)3363 static void Pixel_in_screen_zx_with_opt_preview(word x,word y,byte color,int preview)
3364 {
3365   word start = x & 0xFFF8;
3366   word starty = y & 0xFFF8;
3367   word x2, y2;
3368   uint8_t c1, c2;
3369 
3370   // The color we are going to replace
3371   c1 = Read_pixel_from_current_layer(x, y);
3372 
3373   // Pixel is already of the wanted color: nothing to do
3374   if (c1 == color)
3375     return;
3376 
3377   // Check the whole cell
3378   for (x2 = 0; x2 < 8; x2++)
3379   for (y2 = 0; y2 < 8; y2++)
3380   {
3381     c2 = Read_pixel_from_current_layer(x2 + start, y2 + starty);
3382     // Pixel is already of the color we are going to add, it is no problem
3383     if (c2 == color)
3384       continue;
3385     // We have found another color, which is the one we will keep from the cell
3386     if (c2 != c1)
3387       goto done;
3388   }
3389 done:
3390 
3391   if ((c2 == c1 || c2 == color))
3392   {
3393     // There was only one color, so we can add a second one
3394 
3395     // First make sure we have a single brightness
3396     if (Main.backups->Pages->Image_mode == IMAGE_MODE_ZX
3397        && (c2 & 8) != (color & 8))
3398     {
3399       for (x2 = 0; x2 < 8; x2++)
3400       for (y2 = 0; y2 < 8; y2++)
3401       {
3402         Pixel_in_screen_layered_with_opt_preview(x2+start,y2+starty,c2 ^ 8,preview);
3403       }
3404     }
3405 
3406     Pixel_in_screen_layered_with_opt_preview(x,y,color,preview);
3407     return;
3408   }
3409 
3410   // Replace all C1 with color
3411   for (x2 = 0; x2 < 8; x2++)
3412   for (y2 = 0; y2 < 8; y2++)
3413   {
3414     c2 = Read_pixel_from_current_layer(x2 + start, y2 + starty);
3415     if (c2 == c1)
3416       Pixel_in_screen_layered_with_opt_preview(x2+start,y2+starty,color,preview);
3417     else if (Main.backups->Pages->Image_mode == IMAGE_MODE_ZX)  // Force the brightness bit
3418       Pixel_in_screen_layered_with_opt_preview(x2+start,y2+starty,(c2 & ~8) | (color & 8),preview);
3419   }
3420 }
3421 
3422 /// Paint a pixel with GBC constraints
3423 ///
3424 /// Same 4 color palette for all pixels in a 8x8 block.
3425 ///
3426 /// Megadrive constraints are nearly the same : same 16 color palette in a 8x8 tile
Pixel_in_screen_gbc_with_opt_preview(word x,word y,byte color,int preview)3427 static void Pixel_in_screen_gbc_with_opt_preview(word x,word y,byte color,int preview)
3428 {
3429   word startx = x & ~7;
3430   word starty = y & ~7;
3431   word x2, y2;
3432   byte palette;
3433   byte col_mask, pal_mask;
3434 
3435   if (Main.backups->Pages->Image_mode == IMAGE_MODE_MEGADRIVE)
3436     col_mask = 15;
3437   else
3438     col_mask = 3;
3439   pal_mask = ~col_mask;
3440 
3441   // first set the pixel
3442   Pixel_in_screen_layered_with_opt_preview(x,y,color,preview);
3443   palette = color & pal_mask;
3444   // force all pixels of the block to the same palette
3445   for (y2 = 0; y2 < 8; y2++)
3446   {
3447     for (x2 = 0; x2 < 8; x2++)
3448     {
3449       byte col = Read_pixel_from_current_layer(startx+x2, starty+y2);
3450       if ((col & pal_mask) != palette)
3451         Pixel_in_screen_layered_with_opt_preview(startx+x2, starty+y2, palette | (col & col_mask), preview);
3452     }
3453   }
3454 }
3455 
3456 /// Paint a pixel with C64 MultiColor constraints
3457 ///
3458 /// Only 4 colors in a 4x8 block, including the background color
3459 /// which is common for all blocks.
3460 ///
3461 /// @todo support for any background color (fixed to 0 now)
Pixel_in_screen_c64multi_with_opt_preview(word x,word y,byte color,int preview)3462 static void Pixel_in_screen_c64multi_with_opt_preview(word x,word y,byte color,int preview)
3463 {
3464   word startx = x & ~3;
3465   word starty = y & ~7;
3466   word x2, y2;
3467   byte col, old_color;
3468   byte c[4] = { 0, 0, 0, 0 };  // palette of 4 colors for the block
3469   int i, n;
3470 
3471   old_color = Read_pixel_from_current_layer(x, y);
3472   if (old_color == color)
3473     return; // nothing to do if the color doesn't change !
3474 
3475   c[0] = 0; // assume background is 0
3476   n = 1;  // counted colors
3477   for (y2 = 0; y2 < 8; y2++)
3478   {
3479     for (x2 = 0; x2 < 4; x2++)
3480     {
3481       col = Read_pixel_from_current_layer(startx+x2, starty+y2);
3482       // search color in our mini 4 colors palette
3483       for (i = 0; i < n; i++)
3484       {
3485         if (col == c[i])
3486           break;  // found
3487       }
3488       if (i == n) // not found
3489       {
3490         if (n < 4)
3491           c[n++] = col; // set color in palette
3492         else  // already more than 3 colors (+ background) in the block. Fix it
3493           Pixel_in_screen_layered_with_opt_preview(startx+x2,starty+y2,color,preview);
3494       }
3495     }
3496   }
3497   if (n < 4)
3498   {
3499     // there is less than 4 colors in the block : nothing special to do
3500     Pixel_in_screen_layered_with_opt_preview(x,y,color,preview);
3501     return;
3502   }
3503   for (i = 0; i < n; i++)
3504     if (color == c[i])
3505     {
3506       // The new color is already in the palette, nothing special to do
3507       Pixel_in_screen_layered_with_opt_preview(x,y,color,preview);
3508       return;
3509     }
3510   // The execution reaches this point only if plotting the new color
3511   // would violate the constraints.
3512   // replace old_color with color, except if old_color is the background.
3513   // replace the last color of the palette instead.
3514   if (old_color == c[0])  // background
3515     old_color = c[3];
3516   for (y2 = 0; y2 < 8; y2++)
3517   {
3518     for (x2 = 0; x2 < 4; x2++)
3519     {
3520       col = Read_pixel_from_current_layer(startx+x2, starty+y2);
3521       if (col == old_color)
3522         Pixel_in_screen_layered_with_opt_preview(startx+x2,starty+y2,color,preview);
3523     }
3524   }
3525 }
3526 
3527 
3528 /// Paint in the background or Color RAM layer of C64 FLI
3529 ///
3530 /// Update the bitmap layer pixel if needed
Pixel_in_screen_c64fli_underlay_with_opt_preview(word x,word y,byte color,int preview)3531 static void Pixel_in_screen_c64fli_underlay_with_opt_preview(word x,word y,byte color,int preview)
3532 {
3533   byte oldcolor = Read_pixel_from_current_layer(x, y);
3534 
3535   // does it changes the upper layer (3) ?
3536   if (oldcolor != Read_pixel_from_layer(Main.current_layer ^ 1, x, y)
3537       && oldcolor == Read_pixel_from_layer(2, x, y))
3538   {
3539     Pixel_in_layer(2, x, y, color);
3540     if (((1 << 2) & Main.layers_visible) != 0
3541       && (Main_visible_image_depth_buffer.Image[+x+y*Main.image_width] <= 2))
3542     {
3543       Main_screen[x+y*Main.image_width]=color;
3544       if (preview)
3545         Pixel_preview(x,y,color);
3546     }
3547   }
3548 
3549   Pixel_in_screen_layered_with_opt_preview(x, y, color, preview);
3550 }
3551 
3552 /// Paint in the bitmap layer of C64 FLI
3553 ///
3554 /// enforce C64 FLI mode constraints.
Pixel_in_screen_c64fli_bitmap_with_opt_preview(word x,word y,byte color,int preview)3555 static void Pixel_in_screen_c64fli_bitmap_with_opt_preview(word x,word y,byte color,int preview)
3556 {
3557   word startx = x & ~3;
3558   word x2;
3559   byte c[4];
3560   byte oldcolor = Read_pixel_from_current_layer(x, y);
3561 
3562   if (oldcolor == color)
3563     return; // nothing to do !
3564   c[0] = Read_pixel_from_layer(0, x, y);
3565   c[1] = c[0];
3566   c[2] = c[0];
3567   c[3] = Read_pixel_from_layer(1, x, y);
3568 
3569   Pixel_in_screen_layered_with_opt_preview(x,y,color,preview);
3570 
3571   // if the color is the background or the color RAM color,
3572   // no clash is possible !
3573   if (color == c[0] || color == c[1])
3574     return;
3575 
3576   // check the number of color used
3577   for (x2 = startx; x2 < startx+4; x2++)
3578   {
3579     int i;
3580     byte col = Read_pixel_from_current_layer(x2, y);
3581     // look for the color in the 4 color "palette"
3582     for (i = 0; i < 4; i++)
3583     {
3584       if (col == c[i])
3585         break;
3586     }
3587     if (i >= 4)
3588     {
3589       if (c[1] == c[0])
3590         c[1] = col; // assign 1st color of Screen RAM
3591       else if (c[2] == c[0])
3592         c[2] = col; // assign 2nd color of Screen RAM
3593       else // color clash !
3594       {
3595         if (oldcolor == c[0] || oldcolor == c[3])
3596           oldcolor = c[2];  // pick another one !
3597         for (x2 = startx; x2 < startx+4; x2++)
3598         {
3599           col = Read_pixel_from_current_layer(x2, y);
3600           if (col == oldcolor)
3601             Pixel_in_screen_layered_with_opt_preview(x2,y,color,preview);
3602         }
3603         return;
3604       }
3605     }
3606   }
3607 }
3608 
3609 /// Paint a a single pixel in image and optionnaly on screen : in a layer under one that acts as a layer-selector (mode 5).
Pixel_in_screen_underlay_with_opt_preview(word x,word y,byte color,int preview)3610 static void Pixel_in_screen_underlay_with_opt_preview(word x,word y,byte color,int preview)
3611 {
3612   byte depth;
3613 
3614   // Paste in layer
3615   Pixel_in_current_layer(x, y, color);
3616   // Search depth
3617   depth = Read_pixel_from_layer(4, x, y);
3618 
3619   if ( depth == Main.current_layer)
3620   {
3621     // Draw that color on the visible image buffer
3622     *(x+y*Main.image_width+Main_screen)=color;
3623 
3624     if (preview)
3625       Pixel_preview(x,y,color);
3626   }
3627 }
3628 
3629 /// Paint a single pixel in the layer 5 of CPC rasterized modes
3630 ///
3631 /// used when @ref IMAGE_MODE_MODE5 or @ref IMAGE_MODE_RASTER is active.
3632 ///
3633 /// The layer 5 acts as a layer-selector.
3634 /// it contains INK 0 to 3 which select one of the layer from 1 to 4, the
3635 /// display color is retrieved from this one.
Pixel_in_screen_overlay_with_opt_preview(word x,word y,byte color,int preview)3636 static void Pixel_in_screen_overlay_with_opt_preview(word x,word y,byte color,int preview)
3637 {
3638   byte ink; // 0 to 3
3639 
3640   if (color < 4)
3641     ink = color;  // the argument was the ink!
3642   else
3643   {
3644     // search for this color in the 4 defined inks
3645     for (ink = 0; ink < 4; ink++)
3646     {
3647       if (color == Read_pixel_from_layer(ink, x, y))
3648         break;
3649     }
3650     if (ink >= 4)
3651       return; // not found ? do nothing
3652   }
3653 
3654   // Paste ink in layer 5
3655   Pixel_in_current_layer(x, y, ink);
3656   // Paste in depth buffer
3657   *(Main_visible_image_depth_buffer.Image+x+y*Main.image_width) = ink;
3658   // Fetch pixel color from the target raster layer
3659   if (Main.layers_visible & (1 << ink))
3660     color = Read_pixel_from_layer(ink, x, y);
3661   else
3662     color = ink;
3663 
3664   // Draw that color on the visible image buffer
3665   *(x+y*Main.image_width+Main_screen)=color;
3666 
3667   if (preview)
3668     Pixel_preview(x,y,color);
3669 }
3670 
3671 /// generate color pixels in layer 2 from the monochrome layer 1
3672 ///
3673 /// For Apple II HGR mode ::IMAGE_MODE_HGR
Update_color_hgr_pixel(word x,word y,int preview)3674 void Update_color_hgr_pixel(word x, word y, int preview)
3675 {
3676   byte b2, b1, b0, pal;
3677 
3678   // read monochrome pixels
3679   b1 = Read_pixel_from_layer(0, x, y);
3680   b2 = (x > 0) ? Read_pixel_from_layer(0, x - 1, y) : 0;
3681   b0 = (x < Main.image_width - 1) ? Read_pixel_from_layer(0, x + 1, y) : 0;
3682   pal = b1 & 4;
3683   switch (((b2 & 1) << 2) | ((b1 & 1) << 1) | (b0 & 1))
3684   {
3685     case 7: // 111
3686     case 6: // 110
3687     case 3: // 011
3688       Pixel_in_layer_with_opt_preview(1, x, y, pal + 3, preview); // white
3689       break;
3690     case 0: // 000
3691     case 1: // 001
3692     case 4: // 100
3693       Pixel_in_layer_with_opt_preview(1, x, y, pal, preview); // black
3694       break;
3695     default:  // 010 or 101
3696       Pixel_in_layer_with_opt_preview(1, x, y, pal + 1 + ((x & 1) ^ (b1 & 1)), preview); // black
3697   }
3698 }
3699 
3700 /// Paint a pixel in HGR mode in the monochrome layer
3701 ///
3702 /// - Layer 1 is the monochrome screen. Pixels are either black or white. 2 different values to reflect high bit
3703 /// - Layer 2 is the screen seen as color
Pixel_in_screen_hgr_mono_with_opt_preview(word x,word y,byte color,int preview)3704 static void Pixel_in_screen_hgr_mono_with_opt_preview(word x,word y,byte color,int preview)
3705 {
3706   byte oldcolor;
3707   word column;
3708   int x2;
3709 
3710   if (color >= 8)
3711     return;
3712   if ((color & 3) != 0)
3713     color |= 3; // force black or white.
3714 
3715   // put pixel
3716   oldcolor = Read_pixel_from_layer(0, x, y);
3717   if (color == oldcolor)
3718     return; // nothing to do !
3719   Pixel_in_layer_with_opt_preview(0, x, y, color, preview);
3720 
3721   if ((color & 4) == (oldcolor & 4))
3722   { // no palette change
3723     if (x > 0)
3724       Update_color_hgr_pixel(x - 1, y, preview);
3725     Update_color_hgr_pixel(x, y, preview);
3726     if (x < Main.image_width - 1)
3727       Update_color_hgr_pixel(x + 1, y, preview);
3728     return;
3729   }
3730 
3731   column = x / 7;
3732   // update the palette bit of the whole column (byte)
3733   for (x2 = column * 7; x2 < column * 7 + 7; x2++)
3734   {
3735     byte pixel = Read_pixel_from_layer(0, x2, y);
3736     Pixel_in_layer_with_opt_preview(0, x2, y, (color & 4) | (pixel & 3), preview);
3737   }
3738   // update color pixels !
3739   for (x2 = MAX(0, (column * 7) - 1); x2 < column * 7 + 8; x2++)
3740   {
3741     if (x2 >= Main.image_width)
3742       break;
3743     Update_color_hgr_pixel((word)x2, y, preview);
3744   }
3745 }
3746 
3747 /// Paint in the color layer of HGR.
3748 ///
3749 /// - For B&W the 1st press change the pixel,
3750 /// the second one change the other pixel of the pair.
3751 /// - For colors, change both monochrome pixels
Pixel_in_screen_hgr_color_with_opt_preview(word x,word y,byte color,int preview)3752 static void Pixel_in_screen_hgr_color_with_opt_preview(word x,word y,byte color,int preview)
3753 {
3754   byte oldcolor;
3755   switch (color & 3)
3756   {
3757     case 0: // black
3758     case 3: // white
3759       oldcolor = Read_pixel_from_layer(0, x, y);
3760       if (oldcolor == color)
3761         Pixel_in_screen_hgr_mono_with_opt_preview(x ^ 1, y, color, preview);
3762       else
3763         Pixel_in_screen_hgr_mono_with_opt_preview(x, y, color, preview);
3764       break;
3765     default:
3766       x &= ~1;
3767       Pixel_in_screen_hgr_mono_with_opt_preview(x, y, color & 6, preview);      // palette bit(4) + upper bit(2)
3768       Pixel_in_screen_hgr_mono_with_opt_preview(x + 1, y, color & 5, preview);  // palette bit(4) + lower bit(1)
3769   }
3770 }
3771 
3772 /// Update the color layer of DHGR according to the monochrome layer
3773 ///
3774 /// Emulate \"Le Chat Mauve\" mode 3 (mixed mode).
Update_color_dhgr_pixel(word x,word y,int preview)3775 void Update_color_dhgr_pixel(word x, word y, int preview)
3776 {
3777   byte b3, b2, b1, b0, color;
3778 
3779   x &= ~3;
3780   // read monochrome pixels
3781   b3 = Read_pixel_from_layer(0, x, y);
3782   b2 = Read_pixel_from_layer(0, x + 1, y);
3783   b1 = Read_pixel_from_layer(0, x + 2, y);
3784   b0 = Read_pixel_from_layer(0, x + 3, y);
3785   if (b3 & 16)
3786   {
3787     // monochrome pixel
3788     Pixel_in_layer_with_opt_preview(1, x, y, b3, preview);
3789     Pixel_in_layer_with_opt_preview(1, x + 1, y, b2, preview);
3790     Pixel_in_layer_with_opt_preview(1, x + 2, y, b1, preview);
3791     Pixel_in_layer_with_opt_preview(1, x + 3, y, b0, preview);
3792   }
3793   else
3794   {
3795     // color pixel
3796     color = (b3 & 8) | (b2 & 4) | (b1 & 2) | (b0 & 1);
3797     Pixel_in_layer_with_opt_preview(1, x, y, color, preview);
3798     Pixel_in_layer_with_opt_preview(1, x + 1, y, color, preview);
3799     Pixel_in_layer_with_opt_preview(1, x + 2, y, color, preview);
3800     Pixel_in_layer_with_opt_preview(1, x + 3, y, color, preview);
3801   }
3802 }
3803 
3804 
3805 /// Paint in the monochrome layer of DHGR
3806 ///
3807 /// also update the color pixels.
Pixel_in_screen_dhgr_mono_with_opt_preview(word x,word y,byte color,int preview)3808 static void Pixel_in_screen_dhgr_mono_with_opt_preview(word x,word y,byte color,int preview)
3809 {
3810   byte oldcolor;
3811 
3812   if (color >= 32)
3813     return;
3814   if ((color & 15) != 0)
3815     color |= 15; // force black or white.
3816 
3817   // put pixel
3818   oldcolor = Read_pixel_from_layer(0, x, y);
3819   if (color == oldcolor)
3820     return; // nothing to do !
3821   Pixel_in_layer_with_opt_preview(0, x, y, color, preview);
3822   Update_color_dhgr_pixel(x, y, preview);
3823 
3824   // change bit7 if needed.
3825   if ((color & 16) != (oldcolor & 16))
3826   {
3827     int i;
3828     x -= (x % 7);
3829     for (i = 0; i < 7; i++)
3830     {
3831       oldcolor = Read_pixel_from_layer(0, x, y);
3832       if ((oldcolor & 16) != (color & 16))
3833       {
3834         Pixel_in_layer_with_opt_preview(0, x, y, (color & 16) | (oldcolor & 15), preview);
3835         Update_color_dhgr_pixel(x, y, preview);
3836       }
3837       x++;
3838     }
3839   }
3840 }
3841 
3842 /// Paint in the color layer of DHGR
3843 ///
3844 /// use of color 16-31 forces the cell to be monochrome.
Pixel_in_screen_dhgr_color_with_opt_preview(word x,word y,byte color,int preview)3845 static void Pixel_in_screen_dhgr_color_with_opt_preview(word x,word y,byte color,int preview)
3846 {
3847   if (color & 16)
3848   {
3849     // monochrome pixel
3850     Pixel_in_screen_dhgr_mono_with_opt_preview(x, y, color, preview);
3851     // force monochrome for this cell
3852     if ((x & 3) != 0)
3853       Pixel_in_screen_dhgr_mono_with_opt_preview(x & ~3, y, Read_pixel_from_layer(0, x & ~3, y) | 16, preview);
3854   }
3855   else
3856   {
3857     // color pixel
3858     x &= ~3;
3859     Pixel_in_screen_dhgr_mono_with_opt_preview(x, y, color & 8, preview); // also set this cell in color mode
3860     Pixel_in_screen_dhgr_mono_with_opt_preview(x + 1, y, color & 4, preview);
3861     Pixel_in_screen_dhgr_mono_with_opt_preview(x + 2, y, color & 2, preview);
3862     Pixel_in_screen_dhgr_mono_with_opt_preview(x + 3, y, color & 1, preview);
3863   }
3864 }
3865 
3866 // end of constraints group
3867 /// @}
3868 
3869 Func_pixel_opt_preview Pixel_in_current_screen_with_opt_preview=Pixel_in_screen_direct_with_opt_preview;
3870 
3871 /**
3872  * Put a pixel in the current layer of a "Document"
3873  *
3874  * @param doc pointer to either @ref Main or @ref Spare
3875  * @param x x coordinate of the pixel to put
3876  * @param y y coordinate of the pixel to put
3877  * @param color the new color for the pixel
3878  */
Pixel_in_document_current_layer(T_Document * doc,word x,word y,byte color)3879 void Pixel_in_document_current_layer(T_Document * doc, word x, word y, byte color)
3880 {
3881   doc->backups->Pages->Image[doc->current_layer].Pixels[x + y*doc->image_width] = color;
3882 }
3883 
Pixel_in_spare(word x,word y,byte color)3884 void Pixel_in_spare(word x,word y, byte color)
3885 {
3886   Pixel_in_document_current_layer(&Spare, x, y, color);
3887 }
3888 
Pixel_in_current_layer(word x,word y,byte color)3889 void Pixel_in_current_layer(word x,word y, byte color)
3890 {
3891   Pixel_in_document_current_layer(&Main, x, y, color);
3892 }
3893 
3894 /// put a pixel in a specific layer of Main Page
Pixel_in_layer(int layer,word x,word y,byte color)3895 void Pixel_in_layer(int layer, word x,word y, byte color)
3896 {
3897   T_Document * doc = &Main;
3898   doc->backups->Pages->Image[layer].Pixels[x + y*doc->image_width] = color;
3899 }
3900 
Read_pixel_from_current_layer(word x,word y)3901 byte Read_pixel_from_current_layer(word x,word y)
3902 {
3903   return Read_pixel_from_layer(Main.current_layer, x, y);
3904 }
3905 
3906 /// Read a pixel from a specific layer of Main Page
Read_pixel_from_layer(int layer,word x,word y)3907 byte Read_pixel_from_layer(int layer, word x,word y)
3908 {
3909   return Main.backups->Pages->Image[layer].Pixels[x + y*Main.image_width];
3910 }
3911 
Update_pixel_renderer(void)3912 void Update_pixel_renderer(void)
3913 {
3914   switch (Main.backups->Pages->Image_mode)
3915   {
3916   case IMAGE_MODE_ANIMATION:
3917     // direct
3918     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_direct_with_opt_preview;
3919     break;
3920   case IMAGE_MODE_LAYERED:
3921     // layered
3922     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_layered_with_opt_preview;
3923     break;
3924   case IMAGE_MODE_EGX:
3925   case IMAGE_MODE_EGX2:
3926     // special "EGX" mode
3927     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_egx_with_opt_preview;
3928     break;
3929   case IMAGE_MODE_THOMSON:
3930   case IMAGE_MODE_TMS9918G2:
3931     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_thomson_with_opt_preview;
3932     break;
3933   case IMAGE_MODE_GBC:
3934   case IMAGE_MODE_MEGADRIVE:
3935     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_gbc_with_opt_preview;
3936     break;
3937   case IMAGE_MODE_C64HIRES:
3938   case IMAGE_MODE_ZX:
3939     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_zx_with_opt_preview;
3940     break;
3941   case IMAGE_MODE_C64MULTI:
3942     Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_c64multi_with_opt_preview;
3943     break;
3944   case IMAGE_MODE_MODE5:
3945   case IMAGE_MODE_RASTER:
3946     if ( Main.current_layer == 4)                                     // overlay
3947       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_overlay_with_opt_preview;
3948     else if (Main.current_layer<4 && (Main.layers_visible & (1<<4)))  // underlay
3949       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_underlay_with_opt_preview;
3950     else                              // layered (again, for layers > 4 in MODE5 and RASTER)
3951       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_layered_with_opt_preview;
3952     break;
3953   case IMAGE_MODE_C64FLI:
3954     if (Main.current_layer < 2)
3955       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_c64fli_underlay_with_opt_preview;
3956     else if (Main.current_layer == 2)
3957       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_c64fli_bitmap_with_opt_preview;
3958     else
3959       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_layered_with_opt_preview;
3960     break;
3961   case IMAGE_MODE_HGR:
3962     if (Main.current_layer == 0)  // monochrome layer
3963       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_hgr_mono_with_opt_preview;
3964     else if (Main.current_layer == 1)  // color layer
3965       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_hgr_color_with_opt_preview;
3966     else
3967       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_layered_with_opt_preview;
3968     break;
3969   case IMAGE_MODE_DHGR:
3970     if (Main.current_layer == 0)  // monochrome layer
3971       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_dhgr_mono_with_opt_preview;
3972     else if (Main.current_layer == 1)  // color layer
3973       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_dhgr_color_with_opt_preview;
3974     else
3975       Pixel_in_current_screen_with_opt_preview = Pixel_in_screen_layered_with_opt_preview;
3976   }
3977 }
3978