grafX2/src/brush_ops.c
2011-06-04 15:22:13 +00:00

1391 lines
32 KiB
C

/* vim:expandtab:ts=2 sw=2:
*/
/* Grafx2 - The Ultimate 256-color bitmap paint program
Copyright 2007 Adrien Destugues
Copyright 2009 Franck Charlet
Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
Grafx2 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
Grafx2 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grafx2; if not, see <http://www.gnu.org/licenses/>
*/
//////////////////////////////////////////////////////////////////////////////
///@file brush_ops.c
/// Code for operations about the brush (grabbing, rotating, ...) and magnifier
//////////////////////////////////////////////////////////////////////////////
#include <math.h>
#include <stdlib.h>
#include "brush.h"
#include "buttons.h"
#include "engine.h"
#include "global.h"
#include "graph.h"
#include "misc.h"
#include "operatio.h"
#include "pages.h"
#include "sdlscreen.h"
#include "windows.h"
#if defined(__VBCC__) || defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__)
#define M_PI 3.141592653589793238462643
#endif
/// Simulates clicking the "Draw" button.
void Return_to_draw_mode(void)
{
// Comme l'enclenchement du bouton efface le curseur, il faut l'afficher au
// préalable:
Display_cursor();
if (Mouse_K)
Wait_end_of_click();
// !!! Efface la croix puis affiche le viseur !!!
Select_button(BUTTON_DRAW,LEFT_SIDE); // Désenclenche au passage le bouton brosse
if (Config.Auto_discontinuous)
{
// On se place en mode Dessin discontinu à la main
while (Current_operation!=OPERATION_DISCONTINUOUS_DRAW)
Select_button(BUTTON_DRAW,RIGHT_SIDE);
}
// Maintenant, il faut réeffacer le curseur parce qu'il sera raffiché en fin
// d'appel à cette action:
Hide_cursor();
// On passe en brosse couleur:
Change_paintbrush_shape(PAINTBRUSH_SHAPE_COLOR_BRUSH);
if ((Config.Coords_rel) && (Menu_is_visible))
Print_in_menu("X: Y:",0);
Print_coordinates();
}
// ---------------------------------------------------------- OPERATION_MAGNIFY
void Magnifier_12_0(void)
// Opération : 4 (item d'une Loupe)
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
{
// On passe en mode loupe
Main_magnifier_mode=1;
// La fonction d'affichage dans la partie image est désormais un affichage
// spécial loupe.
Pixel_preview=Pixel_preview_magnifier;
// On calcule l'origine de la loupe
Main_magnifier_offset_X=Mouse_X-(Main_magnifier_width>>1);
Main_magnifier_offset_Y=Mouse_Y-(Main_magnifier_height>>1);
// Calcul des coordonnées absolues de ce coin DANS L'IMAGE
Main_magnifier_offset_X+=Main_offset_X;
Main_magnifier_offset_Y+=Main_offset_Y;
Clip_magnifier_offsets(&Main_magnifier_offset_X, &Main_magnifier_offset_Y);
// On calcule les bornes visibles dans l'écran
Position_screen_according_to_zoom();
Compute_limits();
Display_all_screen();
// Repositionner le curseur en fonction des coordonnées visibles
Compute_paintbrush_coordinates();
// On fait de notre mieux pour restaurer l'ancienne opération:
Start_operation_stack(Operation_before_interrupt);
Display_cursor();
Wait_end_of_click();
}
/////////////////////////////////////////////////////////// OPERATION_COLORPICK
void Colorpicker_12_0(void)
//
// Opération : OPERATION_COLORPICK
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
Init_start_operation();
if (Mouse_K==LEFT_SIDE)
{
Set_fore_color(Colorpicker_color);
}
else
{
Set_back_color(Colorpicker_color);
}
Operation_push(Mouse_K);
}
void Colorpicker_1_1(void)
//
// Opération : OPERATION_COLORPICK
// Click Souris: 1
// Taille_Pile : 1
//
// Souris effacée: Non
//
{
char str[4];
if ( (Paintbrush_X>=0) && (Paintbrush_Y>=0)
&& (Paintbrush_X<Main_image_width)
&& (Paintbrush_Y<Main_image_height) )
Colorpicker_color=Read_pixel_from_current_screen(Paintbrush_X,Paintbrush_Y);
else
Colorpicker_color=0;
if ( (Colorpicker_X!=Paintbrush_X)
|| (Colorpicker_Y!=Paintbrush_Y) )
{
Hide_cursor();
Colorpicker_X=Paintbrush_X;
Colorpicker_Y=Paintbrush_Y;
if (Colorpicker_color!=Fore_color)
{
Set_fore_color(Colorpicker_color);
}
if (Menu_is_visible)
{
Print_coordinates();
Num2str(Colorpicker_color,str,3);
Print_in_menu(str,20);
Print_general(170*Menu_factor_X,Menu_status_Y," ",0,Colorpicker_color);
}
Display_cursor();
}
}
void Colorpicker_2_1(void)
//
// Opération : OPERATION_COLORPICK
// Click Souris: 2
// Taille_Pile : 1
//
// Souris effacée: Non
//
{
char str[4];
if ( (Paintbrush_X>=0) && (Paintbrush_Y>=0)
&& (Paintbrush_X<Main_image_width)
&& (Paintbrush_Y<Main_image_height) )
Colorpicker_color=Read_pixel_from_current_screen(Paintbrush_X,Paintbrush_Y);
else
Colorpicker_color=0;
if ( (Colorpicker_X!=Paintbrush_X)
|| (Colorpicker_Y!=Paintbrush_Y) )
{
Hide_cursor();
Colorpicker_X=Paintbrush_X;
Colorpicker_Y=Paintbrush_Y;
if (Colorpicker_color!=Back_color)
{
Set_back_color(Colorpicker_color);
}
if (Menu_is_visible)
{
Print_coordinates();
Num2str(Colorpicker_color,str,3);
Print_in_menu(str,20);
Print_general(170*Menu_factor_X,Menu_status_Y," ",0,Colorpicker_color);
}
Display_cursor();
}
}
void Colorpicker_0_1(void)
//
// Opération : OPERATION_COLORPICK
// Click Souris: 0
// Taille_Pile : 1
//
// Souris effacée: Oui
//
{
short click;
Operation_pop(&click);
if (click==LEFT_SIDE)
{
Set_fore_color(Colorpicker_color);
}
else
{
Set_back_color(Colorpicker_color);
}
Unselect_button(BUTTON_COLORPICKER);
}
/////////////////////////////////////////////////////////// OPERATION_RMB_COLORPICK
byte Rightclick_colorpick(byte cursor_visible)
{
// Check if the rightclick colorpick should take over:
if (!Config.Right_click_colorpick)
return 0;
if (Mouse_K!=RIGHT_SIDE)
return 0;
// In these modes, the Foreground color is ignored,
// so the RMB should act as normal.
if (Shade_mode||Quick_shade_mode||Tiling_mode)
return 0;
Colorpicker_color=-1;
Colorpicker_X=-1;
Colorpicker_Y=-1;
if (cursor_visible)
Hide_cursor();
Start_operation_stack(OPERATION_RMB_COLORPICK);
Init_start_operation();
// Just an indicator to go to next step
Operation_push(1);
Rightclick_colorpick_2_1();
if (cursor_visible)
Display_cursor();
return 1;
}
void Rightclick_colorpick_2_1(void)
//
// Opération : OPERATION_RMB_COLORPICK
// Click Souris: 2
// Taille_Pile : 1
//
// Souris effacée: Non
//
{
char str[4];
if ( (Colorpicker_X!=Paintbrush_X)
|| (Colorpicker_Y!=Paintbrush_Y) )
{
if ( (Paintbrush_X>=0) && (Paintbrush_Y>=0)
&& (Paintbrush_X<Main_image_width)
&& (Paintbrush_Y<Main_image_height) )
Colorpicker_color=Read_pixel_from_current_screen(Paintbrush_X,Paintbrush_Y);
else
Colorpicker_color=0;
Colorpicker_X=Paintbrush_X;
Colorpicker_Y=Paintbrush_Y;
if (Menu_is_visible)
{
Print_coordinates();
Num2str(Colorpicker_color,str,3);
Print_in_menu(str,20);
Print_general(170*Menu_factor_X,Menu_status_Y," ",0,Colorpicker_color);
}
}
}
void Rightclick_colorpick_0_1(void)
//
// Opération : OPERATION_RMB_COLORPICK
// Click Souris: 0
// Taille_Pile : 1
//
// Souris effacée: Non
//
{
short dummy;
Hide_cursor();
Operation_pop(&dummy);
Set_fore_color(Colorpicker_color);
// Restore previous operation
Start_operation_stack(Operation_before_interrupt);
// Erase the color block which shows the picked color
if (Operation_before_interrupt!=OPERATION_REPLACE)
if ( (Mouse_Y<Menu_Y) && (Menu_is_visible) &&
( (!Main_magnifier_mode) || (Mouse_X<Main_separator_position) || (Mouse_X>=Main_X_zoom) ) )
Print_in_menu("X: Y: ",0);
Print_coordinates();
Display_cursor();
}
////////////////////////////////////////////////////// OPERATION_GRAB_BRUSH
void Brush_12_0(void)
//
// Opération : OPERATION_GRAB_BRUSH
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
Init_start_operation();
if (Mouse_K==RIGHT_SIDE) // Besoin d'effacer la brosse après ?
{
Operation_push(1);
// Puisque la zone où on prend la brosse sera effacée, on fait un backup
Backup();
}
else
Operation_push(0);
// On laisse une trace du curseur pour que l'utilisateur puisse visualiser
// où demarre sa brosse:
Display_cursor();
Operation_push(Paintbrush_X); // Début X
Operation_push(Paintbrush_Y); // Début Y
Operation_push(Paintbrush_X); // Dernière position X
Operation_push(Paintbrush_Y); // Dernière position Y
if ((Config.Coords_rel) && (Menu_is_visible))
Print_in_menu("\035: 1 \022: 1",0);
}
void Brush_12_5(void)
//
// Opération : OPERATION_GRAB_BRUSH
// Click Souris: 1 ou 2
// Taille_Pile : 5
//
// Souris effacée: Non
//
{
char str[5];
short start_x;
short start_y;
short old_x;
short old_y;
short width;
short height;
Operation_pop(&old_y);
Operation_pop(&old_x);
if ( (Menu_is_visible) && ((Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y)) )
{
if (Config.Coords_rel)
{
Operation_pop(&start_y);
Operation_pop(&start_x);
Operation_push(start_x);
Operation_push(start_y);
width=((start_x<Paintbrush_X)?Paintbrush_X-start_x:start_x-Paintbrush_X)+1;
height=((start_y<Paintbrush_Y)?Paintbrush_Y-start_y:start_y-Paintbrush_Y)+1;
Num2str(width,str,4);
Print_in_menu(str,2);
Num2str(height,str,4);
Print_in_menu(str,11);
}
else
Print_coordinates();
}
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
}
void Brush_0_5(void)
//
// Opération : OPERATION_GRAB_BRUSH
// Click Souris: 0
// Taille_Pile : 5
//
// Souris effacée: Oui
//
{
short start_x;
short start_y;
short old_paintbrush_x;
short old_paintbrush_y;
short clear;
// Comme on a demandé l'effacement du curseur, il n'y a plus de croix en
// (Paintbrush_X,Paintbrush_Y). C'est une bonne chose.
Operation_stack_size-=2;
Operation_pop(&start_y);
Operation_pop(&start_x);
Operation_pop(&clear);
// On efface l'ancienne croix:
old_paintbrush_x=Paintbrush_X;
old_paintbrush_y=Paintbrush_Y;
Paintbrush_X=start_x;
Paintbrush_Y=start_y;
Hide_cursor(); // Maintenant, il n'y a plus de croix à l'écran.
Paintbrush_X=old_paintbrush_x;
Paintbrush_Y=old_paintbrush_y;
// Prise de la brosse
if ((Snap_mode) && (Config.Adjust_brush_pick))
{
if (Paintbrush_X<start_x)
{
old_paintbrush_x=start_x;
start_x=Paintbrush_X;
}
if (Paintbrush_Y<start_y)
{
old_paintbrush_y=start_y;
start_y=Paintbrush_Y;
}
if (old_paintbrush_x!=start_x)
old_paintbrush_x--;
if (old_paintbrush_y!=start_y)
old_paintbrush_y--;
}
Capture_brush(start_x,start_y,old_paintbrush_x,old_paintbrush_y,clear);
if ((Snap_mode) && (Config.Adjust_brush_pick))
{
Brush_offset_X=(Brush_offset_X/Snap_width)*Snap_width;
Brush_offset_Y=(Brush_offset_Y/Snap_height)*Snap_height;
}
End_of_modification();
End_of_modification();
Return_to_draw_mode();
}
//////////////////////////////////////////////////////// OPERATION_POLYBRUSH
void Polybrush_12_8(void)
// Opération : OPERATION_POLYBRUSH
// Click Souris: 1 ou 2
// Taille_Pile : 8
//
// Souris effacée: Non
{
short click;
short end_y;
short end_x;
short start_y;
short start_x;
short color;
short initial_y;
short initial_x;
Operation_pop(&click);
Operation_pop(&end_y);
Operation_pop(&end_x);
Operation_pop(&start_y);
Operation_pop(&start_x);
if (click==Mouse_K)
{
// L'utilisateur clique toujours avec le bon bouton de souris
if (((start_x!=Paintbrush_X) || (start_y!=Paintbrush_Y)) &&
(Polyfill_number_of_points<Config.Nb_max_vertices_per_polygon))
{
// Il existe un nouveau segment défini par
// (start_x,start_y)-(Paintbrush_X,Paintbrush_Y)
Hide_cursor();
Print_coordinates();
// On le place à l'écran
Draw_line_preview_xor(start_x,start_y,end_x,end_y,0);
Draw_line_preview_xor(start_x,start_y,Paintbrush_X,Paintbrush_Y,0);
// On peut le rajouter au polygone
Polyfill_table_of_points[Polyfill_number_of_points<<1] =Paintbrush_X;
Polyfill_table_of_points[(Polyfill_number_of_points<<1)+1]=Paintbrush_Y;
Polyfill_number_of_points++;
Operation_push(Paintbrush_X); // Nouveau start_x
Operation_push(Paintbrush_Y); // Nouveau start_y
Operation_push(Paintbrush_X); // Nouveau end_x
Operation_push(Paintbrush_Y); // Nouveau end_y
Operation_push(click);
Display_cursor();
}
else
{
if (Polyfill_number_of_points==Config.Nb_max_vertices_per_polygon)
{
// Le curseur bouge alors qu'on ne peut plus stocker de segments ?
if ((end_x!=Paintbrush_X) || (end_y!=Paintbrush_Y))
{
Hide_cursor();
Print_coordinates();
// On le place à l'écran
Draw_line_preview_xor(start_x,start_y,end_x,end_y,0);
Draw_line_preview_xor(start_x,start_y,Paintbrush_X,Paintbrush_Y,0);
Display_cursor();
}
// On remet les mêmes valeurs (comme si on n'avait pas cliqué):
Operation_push(start_x);
Operation_push(start_y);
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
Operation_push(click);
}
else
{
Operation_push(Paintbrush_X); // Nouveau start_x
Operation_push(Paintbrush_Y); // Nouveau start_y
Operation_push(Paintbrush_X); // Nouveau end_x
Operation_push(Paintbrush_Y); // Nouveau end_y
Operation_push(click);
}
}
}
else
{
// L'utilisateur souhaite arrêter l'opération et refermer le polygone
Operation_pop(&color);
Operation_pop(&initial_y);
Operation_pop(&initial_x);
Hide_cursor();
Print_coordinates();
// Pas besoin d'effacer la ligne (start_x,start_y)-(end_x,end_y)
// puisqu'on les efface toutes d'un coup.
Capture_brush_with_lasso(Polyfill_number_of_points,Polyfill_table_of_points,click==RIGHT_SIDE);
free(Polyfill_table_of_points);
Polyfill_table_of_points = NULL;
// On raffiche l'écran pour effacer les traits en xor et pour raffraichir
// l'écran si on a découpé une partie de l'image en prenant la brosse.
Display_all_screen();
Paintbrush_hidden=0;
if ((Snap_mode) && (Config.Adjust_brush_pick))
{
Brush_offset_X=(Brush_offset_X/Snap_width)*Snap_width;
Brush_offset_Y=(Brush_offset_Y/Snap_height)*Snap_height;
}
if (click==RIGHT_SIDE)
End_of_modification();
Return_to_draw_mode();
Display_cursor();
}
}
///////////////////////////////////////////////////// OPERATION_STRETCH_BRUSH
void Stretch_brush_12_0(void)
//
// Opération : OPERATION_STRETCH_BRUSH
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
Init_start_operation();
if (Mouse_K==LEFT_SIDE)
{
// On laisse une trace du curseur pour que l'utilisateur puisse visualiser
// où demarre sa brosse:
Display_cursor();
Operation_push(Paintbrush_X); // Dernier calcul X
Operation_push(Paintbrush_Y); // Dernier calcul Y
Operation_push(Paintbrush_X); // Début X
Operation_push(Paintbrush_Y); // Début Y
Operation_push(Paintbrush_X); // Dernière position X
Operation_push(Paintbrush_Y); // Dernière position Y
Operation_push(1); // State précédent
if ((Config.Coords_rel) && (Menu_is_visible))
Print_in_menu("\035: 1 \022: 1",0);
}
else
{
Wait_end_of_click();
Start_operation_stack(Operation_before_interrupt);
}
}
void Stretch_brush_1_7(void)
//
// Opération : OPERATION_STRETCH_BRUSH
// Click Souris: 1
// Taille_Pile : 7
//
// Souris effacée: Non
//
{
char str[5];
short start_x;
short start_y;
short old_x;
short old_y;
short width;
short height;
short prev_state;
short dx,dy,x,y;
Operation_pop(&prev_state);
Operation_pop(&old_y);
Operation_pop(&old_x);
if ( (Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=2) )
{
Hide_cursor();
Operation_pop(&start_y);
Operation_pop(&start_x);
if (Menu_is_visible)
{
if (Config.Coords_rel)
{
width=((start_x<Paintbrush_X)?Paintbrush_X-start_x:start_x-Paintbrush_X)+1;
height=((start_y<Paintbrush_Y)?Paintbrush_Y-start_y:start_y-Paintbrush_Y)+1;
if (Snap_mode && Config.Adjust_brush_pick)
{
if (width>1) width--;
if (height>1) height--;
}
Num2str(width,str,4);
Print_in_menu(str,2);
Num2str(height,str,4);
Print_in_menu(str,11);
}
else
Print_coordinates();
}
Display_all_screen();
x=Paintbrush_X;
y=Paintbrush_Y;
if (Snap_mode && Config.Adjust_brush_pick)
{
dx=Paintbrush_X-start_x;
dy=Paintbrush_Y-start_y;
if (dx<0) x++; else {if (dx>0) x--;}
if (dy<0) y++; else {if (dy>0) y--;}
Stretch_brush_preview(start_x,start_y,x,y);
}
else
Stretch_brush_preview(start_x,start_y,Paintbrush_X,Paintbrush_Y);
old_x=Paintbrush_X;
old_y=Paintbrush_Y;
Paintbrush_X=start_x;
Paintbrush_Y=start_y;
Display_cursor();
Paintbrush_X=old_x;
Paintbrush_Y=old_y;
Display_cursor();
Operation_stack_size-=2;
Operation_push(x);
Operation_push(y);
Operation_push(start_x);
Operation_push(start_y);
}
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
Operation_push(2);
}
void Stretch_brush_0_7(void)
//
// Opération : OPERATION_STRETCH_BRUSH
// Click Souris: 0
// Taille_Pile : 7
//
// Souris effacée: Non
//
{
char str[5];
short start_x;
short start_y;
short old_x;
short old_y;
short width=0;
short height=0;
byte size_change;
short prev_state;
Operation_pop(&prev_state);
Operation_pop(&old_y);
Operation_pop(&old_x);
Operation_pop(&start_y);
Operation_pop(&start_x);
if ((Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=3))
{
if (Menu_is_visible)
{
if (Config.Coords_rel)
{
width=((start_x<Paintbrush_X)?Paintbrush_X-start_x:start_x-Paintbrush_X)+1;
height=((start_y<Paintbrush_Y)?Paintbrush_Y-start_y:start_y-Paintbrush_Y)+1;
Num2str(width,str,4);
Print_in_menu(str,2);
Num2str(height,str,4);
Print_in_menu(str,11);
}
else
Print_coordinates();
}
}
// Utilise Key_ANSI au lieu de Key, car Get_input() met ce dernier
// à zero si une operation est en cours (Operation_stack_size!=0)
if (Key_ANSI)
{
size_change=1;
switch (Key_ANSI)
{
case 'd': // Double
width=start_x+(Brush_width<<1)-1;
height=start_y+(Brush_height<<1)-1;
break;
case 'x': // Double X
width=start_x+(Brush_width<<1)-1;
height=start_y+Brush_height-1;
break;
case 'y': // Double Y
width=start_x+Brush_width-1;
height=start_y+(Brush_height<<1)-1;
break;
case 'h': // Moitié
width=(Brush_width>1)?start_x+(Brush_width>>1)-1:1;
height=(Brush_height>1)?start_y+(Brush_height>>1)-1:1;
break;
case 'X': // Moitié X
width=(Brush_width>1)?start_x+(Brush_width>>1)-1:1;
height=start_y+Brush_height-1;
break;
case 'Y': // Moitié Y
width=start_x+Brush_width-1;
height=(Brush_height>1)?start_y+(Brush_height>>1)-1:1;
break;
case 'n': // Normal
width=start_x+Brush_width-1;
height=start_y+Brush_height-1;
break;
default :
size_change=0;
}
Key_ANSI=0;
}
else
size_change=0;
if (size_change)
{
// On efface la preview de la brosse (et la croix)
Display_all_screen();
old_x=Paintbrush_X;
old_y=Paintbrush_Y;
Paintbrush_X=start_x;
Paintbrush_Y=start_y;
Display_cursor();
Paintbrush_X=old_x;
Paintbrush_Y=old_y;
Stretch_brush_preview(start_x,start_y,width,height);
Display_cursor();
Operation_stack_size-=2;
Operation_push(width);
Operation_push(height);
}
Operation_push(start_x);
Operation_push(start_y);
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
Operation_push(3);
}
void Stretch_brush_2_7(void)
//
// Opération : OPERATION_STRETCH_BRUSH
// Click Souris: 2
// Taille_Pile : 7
//
// Souris effacée: Oui
//
{
short computed_x;
short computed_y;
short start_x;
short start_y;
Operation_stack_size-=3;
Operation_pop(&start_y);
Operation_pop(&start_x);
Operation_pop(&computed_y);
Operation_pop(&computed_x);
// On efface la preview de la brosse (et la croix)
Display_all_screen();
// Et enfin on stocke pour de bon la nouvelle brosse étirée
Stretch_brush(start_x,start_y,computed_x,computed_y);
Return_to_draw_mode();
}
//////////////////////////////////////////////////// OPERATION_ROTATE_BRUSH
void Rotate_brush_12_0(void)
//
// Opération : OPERATION_ROTATE_BRUSH
// Click Souris: 1 ou 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
Init_start_operation();
if (Mouse_K==LEFT_SIDE)
{
Brush_rotation_center_X=Paintbrush_X+(Brush_width>>1)-Brush_width;
Brush_rotation_center_Y=Paintbrush_Y;
Brush_rotation_center_is_defined=1;
Operation_push(Paintbrush_X); // Dernière position calculée X
Operation_push(Paintbrush_Y); // Dernière position calculée Y
Operation_push(Paintbrush_X); // Dernière position X
Operation_push(Paintbrush_Y); // Dernière position Y
Operation_push(1); // State précédent
if ((Config.Coords_rel) && (Menu_is_visible))
Print_in_menu("Angle: 0° ",0);
}
else
{
Start_operation_stack(Operation_before_interrupt);
Wait_end_of_click(); // FIXME: celui-la il donne un résultat pas très chouette en visuel
}
}
void Rotate_brush_1_5(void)
//
// Opération : OPERATION_ROTATE_BRUSH
// Click Souris: 1
// Taille_Pile : 5
//
// Souris effacée: Non
//
{
char str[4];
short old_x;
short old_y;
short prev_state;
float angle;
int dx,dy;
Operation_pop(&prev_state);
Operation_pop(&old_y);
Operation_pop(&old_x);
// On corrige les coordonnées de la ligne si la touche shift est appuyée...
if(SDL_GetModState() & KMOD_SHIFT)
Clamp_coordinates_regular_angle(Brush_rotation_center_X,Brush_rotation_center_Y,&Paintbrush_X,&Paintbrush_Y);
if ( (Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=2) )
{
if ( (Brush_rotation_center_X==Paintbrush_X)
&& (Brush_rotation_center_Y==Paintbrush_Y) )
angle=0.0;
else
{
dx=Paintbrush_X-Brush_rotation_center_X;
dy=Paintbrush_Y-Brush_rotation_center_Y;
angle=M_2PI-atan2(dy,dx);
}
if (Menu_is_visible)
{
if (Config.Coords_rel)
{
Num2str((int)(angle*180.0/M_PI),str,3);
Print_in_menu(str,7);
}
else
Print_coordinates();
}
Display_all_screen();
Rotate_brush_preview(angle);
Display_cursor();
Operation_stack_size-=2;
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
}
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
Operation_push(2);
}
void Rotate_brush_0_5(void)
//
// Opération : OPERATION_ROTATE_BRUSH
// Click Souris: 0
// Taille_Pile : 5
//
// Souris effacée: Non
//
{
char str[4];
short old_x;
short old_y;
short computed_x=0;
short computed_y=0;
byte angle_change;
short prev_state;
float angle=0.0;
int dx,dy;
Operation_pop(&prev_state);
Operation_pop(&old_y);
Operation_pop(&old_x);
// On corrige les coordonnées de la ligne si la touche shift est appuyée...
if(SDL_GetModState() & KMOD_SHIFT)
Clamp_coordinates_regular_angle(Brush_rotation_center_X,Brush_rotation_center_Y,&Paintbrush_X,&Paintbrush_Y);
if ((Paintbrush_X!=old_x) || (Paintbrush_Y!=old_y) || (prev_state!=3))
{
if ( (Brush_rotation_center_X==Paintbrush_X)
&& (Brush_rotation_center_Y==Paintbrush_Y) )
angle=0.0;
else
{
dx=Paintbrush_X-Brush_rotation_center_X;
dy=Paintbrush_Y-Brush_rotation_center_Y;
angle=acos(((float)dx)/sqrt((dx*dx)+(dy*dy)));
if (dy>0) angle=M_2PI-angle;
}
if (Menu_is_visible)
{
if (Config.Coords_rel)
{
Num2str(Round(angle*180.0/M_PI),str,3);
Print_in_menu(str,7);
}
else
Print_coordinates();
}
}
// Utilise Key_ANSI au lieu de Key, car Get_input() met ce dernier
// à zero si une operation est en cours (Operation_stack_size!=0)
if (Key_ANSI)
{
angle_change=1;
computed_x=Brush_rotation_center_X;
computed_y=Brush_rotation_center_Y;
switch (Key_ANSI)
{
case '6': angle= 0.0 ; computed_x++; break;
case '9': angle=M_PI*0.25; computed_x++; computed_y--; break;
case '8': angle=M_PI*0.5 ; computed_y--; break;
case '7': angle=M_PI*0.75; computed_x--; computed_y--; break;
case '4': angle=M_PI ; computed_x--; break;
case '1': angle=M_PI*1.25; computed_x--; computed_y++; break;
case '2': angle=M_PI*1.5 ; computed_y++; break;
case '3': angle=M_PI*1.75; computed_x++; computed_y++; break;
default :
angle_change=0;
}
Key_ANSI=0;
}
else
angle_change=0;
if (angle_change)
{
// On efface la preview de la brosse
Display_all_screen();
Rotate_brush_preview(angle);
Display_cursor();
Operation_stack_size-=2;
Operation_push(computed_x);
Operation_push(computed_y);
}
Operation_push(Paintbrush_X);
Operation_push(Paintbrush_Y);
Operation_push(3);
}
void Rotate_brush_2_5(void)
//
// Opération : OPERATION_ROTATE_BRUSH
// Click Souris: 2
// Taille_Pile : 5
//
// Souris effacée: Oui
//
{
short computed_x;
short computed_y;
int dx,dy;
float angle;
// On efface la preview de la brosse
Display_all_screen();
Operation_stack_size-=3;
Operation_pop(&computed_y);
Operation_pop(&computed_x);
// Calcul de l'angle par rapport à la dernière position calculée
if ( (Brush_rotation_center_X==computed_x)
&& (Brush_rotation_center_Y==computed_y) )
angle=0.0;
else
{
dx=computed_x-Brush_rotation_center_X;
dy=computed_y-Brush_rotation_center_Y;
angle=acos(((float)dx)/sqrt((dx*dx)+(dy*dy)));
if (dy>0) angle=M_2PI-angle;
}
// Et enfin on stocke pour de bon la nouvelle brosse étirée
Rotate_brush(angle);
Return_to_draw_mode();
}
///////////////////////////////////////////////////// OPERATION_DISTORT_BRUSH
/// Draws a 2x2 XOR square at the specified picture coordinates, on the screen.
void Draw_stretch_spot(short x_pos, short y_pos)
{
short x,y;
for (y=y_pos-1;y<y_pos+1;y++)
if (y>=Limit_top && y<=Limit_visible_bottom)
for (x=x_pos-1;x<x_pos+1;x++)
if (x>=Limit_left && x<=Limit_visible_right)
Pixel_preview(x,y,~Read_pixel(x-Main_offset_X,y-Main_offset_Y));
Update_part_of_screen(x_pos-1, y_pos-1, 2, 2);
}
void Distort_brush_0_0(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 0
// Taille_Pile : 0
//
// Souris effacée: Non
//
{
if ( Menu_is_visible )
{
Print_in_menu("POSITION BRUSH TO START ",0);
}
}
void Distort_brush_1_0(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 1
// Taille_Pile : 0
//
// Souris effacée: Non
//
{
short x_pos, y_pos;
Init_start_operation();
Paintbrush_hidden=1;
Hide_cursor();
// Top left angle
x_pos=Paintbrush_X-Brush_offset_X;
y_pos=Paintbrush_Y-Brush_offset_Y;
Draw_stretch_spot(x_pos,y_pos);
Operation_push(x_pos);
Operation_push(y_pos);
// Top right angle
x_pos+=Brush_width;
Draw_stretch_spot(x_pos,y_pos);
Operation_push(x_pos);
Operation_push(y_pos);
// Bottom right angle
y_pos+=Brush_height;
Draw_stretch_spot(x_pos,y_pos);
Operation_push(x_pos);
Operation_push(y_pos);
// Bottom left angle
x_pos-=Brush_width;
Draw_stretch_spot(x_pos,y_pos);
Operation_push(x_pos);
Operation_push(y_pos);
Distort_brush_preview(
Operation_stack[1],
Operation_stack[2],
Operation_stack[3],
Operation_stack[4],
Operation_stack[5],
Operation_stack[6],
Operation_stack[7],
Operation_stack[8]);
Display_cursor();
Update_part_of_screen(Paintbrush_X-Brush_offset_X, Paintbrush_Y-Brush_offset_Y, Brush_width, Brush_height);
Wait_end_of_click();
// Erase the message in status bar
if ( (Config.Coords_rel) && (Menu_is_visible) )
{
Print_in_menu("X: Y: ",0);
}
}
void Distort_brush_1_8(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 1
// Taille_Pile : 8
//
// Souris effacée: No
//
{
// How far (in pixels) you can catch a handle
#define REACH_DISTANCE 100
short i;
short x[4];
short y[4];
long best_distance=REACH_DISTANCE;
short best_spot=-1;
for (i=3;i>=0;i--)
{
long distance;
Operation_pop(&y[i]);
Operation_pop(&x[i]);
distance=Distance(Paintbrush_X,Paintbrush_Y,x[i],y[i]);
if (distance<best_distance)
{
best_spot=i;
best_distance=distance;
}
}
for (i=0;i<4;i++)
{
Operation_push(x[i]);
Operation_push(y[i]);
}
if (best_spot>-1)
{
Operation_push(best_spot);
}
if ( (Config.Coords_rel) && (Menu_is_visible) )
{
Print_in_menu("X: Y: ",0);
Print_coordinates();
}
}
void Distort_brush_1_9(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 1
// Taille_Pile : 9
//
// Souris effacée: No
//
{
short i;
short x[4];
short y[4];
short selected_corner;
// Pop all arguments
Operation_pop(&selected_corner);
for (i=3;i>=0;i--)
{
Operation_pop(&y[i]);
Operation_pop(&x[i]);
}
if (Paintbrush_X!=x[selected_corner] || Paintbrush_Y!=y[selected_corner])
{
Hide_cursor();
// Easiest refresh mode: make no assumptions on how the brush was
// displayed before.
Display_all_screen();
x[selected_corner]=Paintbrush_X;
y[selected_corner]=Paintbrush_Y;
for (i=0;i<4;i++)
Draw_stretch_spot(x[i],y[i]);
Distort_brush_preview(x[0],y[0],x[1],y[1],x[2],y[2],x[3],y[3]);
Display_cursor();
if ( (Config.Coords_rel) && (Menu_is_visible) )
{
Print_in_menu("X: Y: ",0);
Print_coordinates();
}
Update_rect(0,0,Screen_width,Menu_Y);
}
// Push back all arguments
for (i=0;i<4;i++)
{
Operation_push(x[i]);
Operation_push(y[i]);
}
Operation_push(selected_corner);
}
void Distort_brush_0_9(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 0
// Taille_Pile : 9
//
// Souris effacée: No
//
{
short selected_corner;
Operation_pop(&selected_corner);
}
void Distort_brush_2_0(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 2
// Taille_Pile : 0
//
// Souris effacée: Oui
//
{
Paintbrush_hidden=0;
Display_all_screen();
// Erase the message in status bar
if ( (Config.Coords_rel) && (Menu_is_visible) )
{
Print_in_menu("X: Y: ",0);
}
Return_to_draw_mode();
}
void Distort_brush_2_8(void)
//
// Opération : OPERATION_DISTORT_BRUSH
// Click Souris: 2
// Taille_Pile : 8
//
// Souris effacée: Oui
//
{
short i;
short x[4];
short y[4];
// Pop all arguments
for (i=3;i>=0;i--)
{
Operation_pop(&y[i]);
Operation_pop(&x[i]);
}
Distort_brush(x[0],y[0],x[1],y[1],x[2],y[2],x[3],y[3]);
Paintbrush_hidden=0;
Display_all_screen();
Return_to_draw_mode();
}