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