/* 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 */ ////////////////////////////////////////////////////////////////////////////// ///@file brush_ops.c /// Code for operations about the brush (grabbing, rotating, ...) and magnifier ////////////////////////////////////////////////////////////////////////////// #include #include #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=0) && (Paintbrush_Y>=0) && (Paintbrush_X=0) && (Paintbrush_Y>=0) && (Paintbrush_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_x1) 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_x1)?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; short cursor_x,cursor_y; 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... cursor_x = Paintbrush_X; cursor_y = Paintbrush_Y; if(SDL_GetModState() & KMOD_SHIFT) Clamp_coordinates_regular_angle(Brush_rotation_center_X,Brush_rotation_center_Y,&cursor_x,&cursor_y); if ( (cursor_x!=old_x) || (cursor_y!=old_y) || (prev_state!=2) ) { if ( (Brush_rotation_center_X==cursor_x) && (Brush_rotation_center_Y==cursor_y) ) angle=0.0; else { dx=cursor_x-Brush_rotation_center_X; dy=cursor_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(cursor_x); Operation_push(cursor_y); } Operation_push(cursor_x); Operation_push(cursor_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; short cursor_x, cursor_y; 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... cursor_x = Paintbrush_X; cursor_y = Paintbrush_Y; if(SDL_GetModState() & KMOD_SHIFT) Clamp_coordinates_regular_angle(Brush_rotation_center_X,Brush_rotation_center_Y,&cursor_x,&cursor_y); if ((cursor_x!=old_x) || (cursor_y!=old_y) || (prev_state!=3)) { Hide_cursor(); if ( (Brush_rotation_center_X==cursor_x) && (Brush_rotation_center_Y==cursor_y) ) angle=0.0; else { dx=cursor_x-Brush_rotation_center_X; dy=cursor_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(); } Display_cursor(); } // 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(cursor_x); Operation_push(cursor_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=Limit_top && y<=Limit_visible_bottom) for (x=x_pos-1;x=Limit_left && x<=Limit_visible_right) Pixel_preview(x,y,xor_lut[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-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(); }