/* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2008 Franck Charlet Copyright 2007-2008 Adrien Destugues 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 or write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************** Brush manipulation functions */ #include #include #include // memset() #include "global.h" #include "graph.h" #include "misc.h" #include "errors.h" #include "windows.h" #include "sdlscreen.h" #include "brush.h" // Calcul de redimensionnement du pinceau pour éviter les débordements de // l'écran et de l'image void Compute_clipped_dimensions(short * x,short * y,short * width,short * height) { if ((*x)(Limit_right+1)) { (*width)=(Limit_right-(*x))+1; } if ((*y)(Limit_bottom+1)) { (*height)=(Limit_bottom-(*y))+1; } } // -- Calcul de redimensionnement du pinceau pour éviter les débordements // de l'écran zoomé et de l'image -- void Compute_clipped_dimensions_zoom(short * x,short * y,short * width,short * height) { if ((*x)(Limit_right_zoom+1)) { (*width)=(Limit_right_zoom-(*x))+1; } if ((*y)(Limit_bottom_zoom+1)) { (*height)=(Limit_bottom_zoom-(*y))+1; } } // -- Afficher le pinceau (de façon définitive ou non) -- void Display_paintbrush(short x,short y,byte color,byte is_preview) // x,y: position du centre du pinceau // color: couleur à appliquer au pinceau // is_preview: "Il ne faut l'afficher qu'à l'écran" { short start_x; // Position X (dans l'image) à partir de laquelle on // affiche la brosse/pinceau short start_y; // Position Y (dans l'image) à partir de laquelle on // affiche la brosse/pinceau short width; // width dans l'écran selon laquelle on affiche la // brosse/pinceau short height; // height dans l'écran selon laquelle on affiche la // brosse/pinceau short start_x_counter; // Position X (dans la brosse/pinceau) à partir // de laquelle on affiche la brosse/pinceau short start_y_counter; // Position Y (dans la brosse/pinceau) à partir // de laquelle on affiche la brosse/pinceau short x_pos; // Position X (dans l'image) en cours d'affichage short y_pos; // Position Y (dans l'image) en cours d'affichage short counter_x; // Position X (dans la brosse/pinceau) en cours // d'affichage short counter_y; // Position Y (dans la brosse/pinceau) en cours // d'affichage short end_counter_x; // Position X ou s'arrête l'affichade de la // brosse/pinceau short end_counter_y; // Position Y ou s'arrête l'affichade de la // brosse/pinceau byte temp_color; // color de la brosse en cours d'affichage int position; byte * temp; if (!(is_preview && Mouse_K)) // Si bouton enfoncé & preview > pas de dessin switch (Paintbrush_shape) { case PAINTBRUSH_SHAPE_POINT : // !!! TOUJOURS EN PREVIEW !!! if ( (Paintbrush_X>=Limit_left) && (Paintbrush_X<=Limit_right) && (Paintbrush_Y>=Limit_top) && (Paintbrush_Y<=Limit_bottom) ) { Pixel_preview(Paintbrush_X,Paintbrush_Y,color); Update_part_of_screen(x,y,1,1); } break; case PAINTBRUSH_SHAPE_COLOR_BRUSH : // Brush en couleur start_x=x-Brush_offset_X; start_y=y-Brush_offset_Y; width=Brush_width; height=Brush_height; Compute_clipped_dimensions(&start_x,&start_y,&width,&height); if (width<=0 || height<=0) break; start_x_counter=start_x-(x-Brush_offset_X); start_y_counter=start_y-(y-Brush_offset_Y); end_counter_x=start_x_counter+width; end_counter_y=start_y_counter+height; if (is_preview) { if ( (width>0) && (height>0) ) Display_brush_color( start_x-Main_offset_X, start_y-Main_offset_Y, start_x_counter, start_y_counter, width, height, Back_color, Brush_width ); if (Main_magnifier_mode) { Compute_clipped_dimensions_zoom(&start_x,&start_y,&width, &height ); start_x_counter=start_x-(x-Brush_offset_X); start_y_counter=start_y-(y-Brush_offset_Y); if ( (width>0) && (height>0) ) { // Corrections dues au Zoom: start_x=(start_x-Main_magnifier_offset_X)*Main_magnifier_factor; start_y=(start_y-Main_magnifier_offset_Y)*Main_magnifier_factor; height=start_y+(height*Main_magnifier_factor); if (height>Menu_Y) height=Menu_Y; Display_brush_color_zoom(Main_X_zoom+start_x,start_y, start_x_counter,start_y_counter, width,height,Back_color, Brush_width, Horizontal_line_buffer); } } Update_part_of_screen(x-Brush_offset_X,y-Brush_offset_Y,Brush_width,Brush_height); } else { if ((Smear_mode) && (Shade_table==Shade_table_left)) { if (Smear_start) { if ((width>0) && (height>0)) { Copy_part_of_image_to_another( Main_screen, start_x, start_y, width, height, Main_image_width, Smear_brush, start_x_counter, start_y_counter, Smear_brush_width ); Update_part_of_screen(start_x,start_y,width,height); } Smear_start=0; } else { for (y_pos = start_y, counter_y = start_y_counter; counter_y < end_counter_y; y_pos++, counter_y++ ) for (x_pos = start_x, counter_x = start_x_counter; counter_x < end_counter_x; x_pos++, counter_x++ ) { temp_color = Read_pixel_from_current_screen( x_pos,y_pos ); position = (counter_y * Smear_brush_width)+ counter_x; if ( (Read_pixel_from_brush(counter_x,counter_y) != Back_color) && (counter_y=Smear_min_Y) && (counter_x>=Smear_min_X) ) Display_pixel(x_pos,y_pos,Smear_brush[position]); Smear_brush[position]=temp_color; } Update_part_of_screen(start_x,start_y,width,height); } Smear_min_X=start_x_counter; Smear_min_Y=start_y_counter; Smear_max_X=end_counter_x; Smear_max_Y=end_counter_y; } else { if (Shade_table==Shade_table_left) for (y_pos=start_y,counter_y=start_y_counter;counter_y0) && (height>0) ) Display_brush_mono(start_x-Main_offset_X, start_y-Main_offset_Y, start_x_counter,start_y_counter, width,height, Back_color,Fore_color, Brush_width); if (Main_magnifier_mode) { Compute_clipped_dimensions_zoom(&start_x,&start_y,&width,&height); start_x_counter=start_x-(x-Brush_offset_X); start_y_counter=start_y-(y-Brush_offset_Y); if ( (width>0) && (height>0) ) { // Corrections dues au Zoom: start_x=(start_x-Main_magnifier_offset_X)*Main_magnifier_factor; start_y=(start_y-Main_magnifier_offset_Y)*Main_magnifier_factor; height=start_y+(height*Main_magnifier_factor); if (height>Menu_Y) height=Menu_Y; Display_brush_mono_zoom(Main_X_zoom+start_x,start_y, start_x_counter,start_y_counter, width,height, Back_color,Fore_color, Brush_width, Horizontal_line_buffer); } } Update_part_of_screen(x-Brush_offset_X,y-Brush_offset_Y,Brush_width,Brush_height); } else { if ((Smear_mode) && (Shade_table==Shade_table_left)) { if (Smear_start) { if ((width>0) && (height>0)) { Copy_part_of_image_to_another(Main_screen, start_x,start_y, width,height, Main_image_width, Smear_brush, start_x_counter, start_y_counter, Smear_brush_width); Update_part_of_screen(start_x,start_y,width,height); } Smear_start=0; } else { for (y_pos=start_y,counter_y=start_y_counter;counter_y=Smear_min_Y) && (counter_x>=Smear_min_X) ) Display_pixel(x_pos,y_pos,Smear_brush[position]); Smear_brush[position]=temp_color; } Update_part_of_screen(start_x,start_y,width,height); } Smear_min_X=start_x_counter; Smear_min_Y=start_y_counter; Smear_max_X=end_counter_x; Smear_max_Y=end_counter_y; } else { for (y_pos=start_y,counter_y=start_y_counter;counter_y0) && (height>0) ) Display_brush_mono(start_x-Main_offset_X, start_y-Main_offset_Y, start_x_counter,start_y_counter, width,height, 0,Fore_color, MAX_PAINTBRUSH_SIZE); if (Main_magnifier_mode) { Compute_clipped_dimensions_zoom(&start_x,&start_y,&width,&height); start_x_counter=start_x-(x-Paintbrush_offset_X); start_y_counter=start_y-(y-Paintbrush_offset_Y); if ( (width>0) && (height>0) ) { // Corrections dues au Zoom: start_x=(start_x-Main_magnifier_offset_X)*Main_magnifier_factor; start_y=(start_y-Main_magnifier_offset_Y)*Main_magnifier_factor; height=start_y+(height*Main_magnifier_factor); if (height>Menu_Y) height=Menu_Y; Display_brush_mono_zoom(Main_X_zoom+start_x,start_y, start_x_counter,start_y_counter, width,height, 0,Fore_color, MAX_PAINTBRUSH_SIZE, Horizontal_line_buffer); } } Brush=temp; } else { if ((Smear_mode) && (Shade_table==Shade_table_left)) { if (Smear_start) { if ((width>0) && (height>0)) { Copy_part_of_image_to_another(Main_screen, start_x,start_y, width,height, Main_image_width, Smear_brush, start_x_counter, start_y_counter, Smear_brush_width); Update_part_of_screen(start_x,start_y,width,height); } Smear_start=0; } else { for (y_pos=start_y,counter_y=start_y_counter;counter_y=Smear_min_Y) && (counter_x>=Smear_min_X) ) Display_pixel(x_pos,y_pos,Smear_brush[position]); Smear_brush[position]=temp_color; } Update_part_of_screen(start_x, start_y, width, height); } Smear_min_X=start_x_counter; Smear_min_Y=start_y_counter; Smear_max_X=end_counter_x; Smear_max_Y=end_counter_y; } else { for (y_pos=start_y,counter_y=start_y_counter;counter_y=Limit_left) && (Paintbrush_X<=Limit_right) && (Paintbrush_Y>=Limit_top) && (Paintbrush_Y<=Limit_bottom) ) { Pixel_preview(Paintbrush_X,Paintbrush_Y,Read_pixel_from_current_screen(Paintbrush_X,Paintbrush_Y)); Update_part_of_screen(Paintbrush_X,Paintbrush_Y,1,1); } break; case PAINTBRUSH_SHAPE_COLOR_BRUSH : // Brush en couleur case PAINTBRUSH_SHAPE_MONO_BRUSH : // Brush monochrome start_x=x-Brush_offset_X; start_y=y-Brush_offset_Y; width=Brush_width; height=Brush_height; Compute_clipped_dimensions(&start_x,&start_y,&width,&height); start_x_counter=start_x-(x-Brush_offset_X); start_y_counter=start_y-(y-Brush_offset_Y); end_counter_x=start_x_counter+width; end_counter_y=start_y_counter+height; if ( (width>0) && (height>0) ) Clear_brush(start_x-Main_offset_X, start_y-Main_offset_Y, start_x_counter,start_y_counter, width,height,Back_color, Main_image_width); if (Main_magnifier_mode) { Compute_clipped_dimensions_zoom(&start_x,&start_y,&width,&height); start_x_counter=start_x; start_y_counter=start_y; if ( (width>0) && (height>0) ) { // Corrections dues au Zoom: start_x=(start_x-Main_magnifier_offset_X)*Main_magnifier_factor; start_y=(start_y-Main_magnifier_offset_Y)*Main_magnifier_factor; height=start_y+(height*Main_magnifier_factor); if (height>Menu_Y) height=Menu_Y; Clear_brush_scaled(Main_X_zoom+start_x,start_y, start_x_counter,start_y_counter, width,height,Back_color, Main_image_width, Horizontal_line_buffer); } } break; default: // Pinceau start_x=x-Paintbrush_offset_X; start_y=y-Paintbrush_offset_Y; width=Paintbrush_width; height=Paintbrush_height; Compute_clipped_dimensions(&start_x,&start_y,&width,&height); start_x_counter=start_x-(x-Paintbrush_offset_X); start_y_counter=start_y-(y-Paintbrush_offset_Y); end_counter_x=start_x_counter+width; end_counter_y=start_y_counter+height; temp=Brush; Brush=Paintbrush_sprite; if ( (width>0) && (height>0) ) { Clear_brush(start_x-Main_offset_X, start_y-Main_offset_Y, start_x_counter,start_y_counter, width,height,0, Main_image_width); } if (Main_magnifier_mode) { Compute_clipped_dimensions_zoom(&start_x,&start_y,&width,&height); start_x_counter=start_x; start_y_counter=start_y; if ( (width>0) && (height>0) ) { // Corrections dues au Zoom: start_x=(start_x-Main_magnifier_offset_X)*Main_magnifier_factor; start_y=(start_y-Main_magnifier_offset_Y)*Main_magnifier_factor; height=start_y+(height*Main_magnifier_factor); if (height>Menu_Y) height=Menu_Y; Clear_brush_scaled(Main_X_zoom+start_x,start_y, start_x_counter,start_y_counter, width,height,0, Main_image_width, Horizontal_line_buffer); } } Brush=temp; break; } } void Capture_brush(short start_x,short start_y,short end_x,short end_y,short clear) { short temp; short x_pos; short y_pos; word new_brush_width; word new_brush_height; // On commence par "redresser" les bornes: if (start_x>end_x) { temp=start_x; start_x =end_x; end_x =temp; } if (start_y>end_y) { temp=start_y; start_y =end_y; end_y =temp; } // On ne capture la nouvelle brosse que si elle est au moins partiellement // dans l'image: if ((start_xMain_image_width) new_brush_width=Main_image_width-start_x; if (start_y+new_brush_height>Main_image_height) new_brush_height=Main_image_height-start_y; if ( (((long)Brush_height)*Brush_width) != (((long)new_brush_height)*new_brush_width) ) { free(Brush); Brush=(byte *)malloc(((long)new_brush_height)*new_brush_width); if (!Brush) { Error(0); Brush=(byte *)malloc(1*1); new_brush_height=new_brush_width=1; *Brush=Fore_color; } } Brush_width=new_brush_width; Brush_height=new_brush_height; free(Smear_brush); Smear_brush_width=(Brush_width>MAX_PAINTBRUSH_SIZE)?Brush_width:MAX_PAINTBRUSH_SIZE; Smear_brush_height=(Brush_height>MAX_PAINTBRUSH_SIZE)?Brush_height:MAX_PAINTBRUSH_SIZE; Smear_brush=(byte *)malloc(((long)Smear_brush_height)*Smear_brush_width); if (!Smear_brush) // On ne peut même pas allouer la brosse du smear! { Error(0); free(Brush); Brush=(byte *)malloc(1*1); Brush_height=1; Brush_width=1; Smear_brush=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE); Smear_brush_height=MAX_PAINTBRUSH_SIZE; Smear_brush_width=MAX_PAINTBRUSH_SIZE; } Copy_image_to_brush(start_x,start_y,Brush_width,Brush_height,Main_image_width); // On regarde s'il faut effacer quelque chose: if (clear) { for (y_pos=start_y;y_pos>1); Brush_offset_Y=(Brush_height>>1); } } void Rotate_90_deg() { short temp; byte * new_brush; new_brush=(byte *)malloc(((long)Brush_height)*Brush_width); if (new_brush) { Rotate_90_deg_lowlevel(Brush,new_brush,Brush_width,Brush_height); free(Brush); Brush=new_brush; temp=Brush_width; Brush_width=Brush_height; Brush_height=temp; temp=Smear_brush_width; Smear_brush_width=Smear_brush_height; Smear_brush_height=temp; // On centre la prise sur la brosse Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } else Error(0); } void Remap_brush(void) { short x_pos; // Variable de balayage de la brosse short y_pos; // Variable de balayage de la brosse byte used[256]; // Tableau de booléens "La couleur est utilisée" int color; // On commence par initialiser le tableau de booléens à faux for (color=0;color<=255;color++) used[color]=0; // On calcule la table d'utilisation des couleurs for (y_pos=0;y_pos>1); Brush_offset_Y=(Brush_height>>1); free(temp); // Libération de l'ancienne brosse // Réallocation d'un buffer de Smear free(Smear_brush); Smear_brush_width=(Brush_width>MAX_PAINTBRUSH_SIZE)?Brush_width:MAX_PAINTBRUSH_SIZE; Smear_brush_height=(Brush_height>MAX_PAINTBRUSH_SIZE)?Brush_height:MAX_PAINTBRUSH_SIZE; Smear_brush=(byte *)malloc(((long)Smear_brush_width)*Smear_brush_height); } else Error(0); // Pas assez de mémoire! } void Nibble_brush(void) { long /*Pos,*/x_pos,y_pos; byte state; byte * new_brush; byte * temp; word width; word height; if ( (Brush_width>2) && (Brush_height>2) ) { width=Brush_width-2; height=Brush_height-2; new_brush=(byte *)malloc(((long)width)*height); if (new_brush) { // On copie la brosse courante dans la nouvelle Copy_part_of_image_to_another(Brush, // source 1, 1, width, height, Brush_width, new_brush, // Destination 0, 0, width); // On intervertit la nouvelle et l'ancienne brosse: temp=Brush; Brush=new_brush; Brush_width-=2; Brush_height-=2; width+=2; height+=2; // 1er balayage (horizontal) for (y_pos=0; y_pos0) Pixel_in_brush(x_pos-1,y_pos,Back_color); state=0; } } else { if (!state) { Pixel_in_brush(x_pos,y_pos,Back_color); state=1; } } } // Cas du dernier pixel à droite de la ligne if (temp[((y_pos+1)*width)+x_pos+1]==Back_color) Pixel_in_brush(x_pos-1,y_pos,Back_color); } // 2ème balayage (vertical) for (x_pos=0; x_pos0) Pixel_in_brush(x_pos,y_pos-1,Back_color); state=0; } } else { if (!state) { Pixel_in_brush(x_pos,y_pos,Back_color); state=1; } } } // Cas du dernier pixel en bas de la colonne if (temp[((y_pos+1)*width)+x_pos+1]==Back_color) Pixel_in_brush(x_pos,y_pos-1,Back_color); } // On recentre la prise sur la brosse Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); free(temp); // Libération de l'ancienne brosse // Réallocation d'un buffer de Smear free(Smear_brush); Smear_brush_width=(Brush_width>MAX_PAINTBRUSH_SIZE)?Brush_width:MAX_PAINTBRUSH_SIZE; Smear_brush_height=(Brush_height>MAX_PAINTBRUSH_SIZE)?Brush_height:MAX_PAINTBRUSH_SIZE; Smear_brush=(byte *)malloc(((long)Smear_brush_width)*Smear_brush_height); } else Error(0); // Pas assez de mémoire! } } void Capture_brush_with_lasso(int vertices, short * points,short clear) { short start_x=Limit_right+1; short start_y=Limit_bottom+1; short end_x=Limit_left-1; short end_y=Limit_top-1; short temp; short x_pos; short y_pos; word new_brush_width; word new_brush_height; // On recherche les bornes de la brosse: for (temp=0; tempend_x) end_x=x_pos; if (y_posend_y) end_y=y_pos; } // On clippe ces bornes à l'écran: if (start_xLimit_right) end_x=Limit_right; if (start_yLimit_bottom) end_y=Limit_bottom; // On ne capture la nouvelle brosse que si elle est au moins partiellement // dans l'image: if ((start_xMAX_PAINTBRUSH_SIZE)?Brush_width:MAX_PAINTBRUSH_SIZE; Smear_brush_height=(Brush_height>MAX_PAINTBRUSH_SIZE)?Brush_height:MAX_PAINTBRUSH_SIZE; Smear_brush=(byte *)malloc(((long)Smear_brush_height)*Smear_brush_width); if (!Smear_brush) // On ne peut même pas allouer la brosse du smear! { Error(0); free(Brush); Brush=(byte *)malloc(1*1); Brush_height=1; Brush_width=1; Smear_brush=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE); Smear_brush_height=MAX_PAINTBRUSH_SIZE; Smear_brush_width=MAX_PAINTBRUSH_SIZE; } Brush_offset_X=start_x; Brush_offset_Y=start_y; Pixel_figure=Pixel_figure_in_brush; memset(Brush,Back_color,(long)Brush_width*Brush_height); Polyfill_general(vertices,points,~Back_color); // On retrace les bordures du lasso: for (temp=1; temp>1); Brush_offset_Y=(Brush_height>>1); } } //------------------------- Etirement de la brosse --------------------------- void Stretch_brush(short x1, short y1, short x2, short y2) { byte * new_brush; int new_brush_width; // Width de la nouvelle brosse int new_brush_height; // Height de la nouvelle brosse int x_flipped, y_flipped; // Compute new brush dimensions if ((new_brush_width=x1-x2)<0) { x_flipped=1; new_brush_width=-new_brush_width; } new_brush_width++; if ((new_brush_height=y1-y2)<0) { y_flipped=1; new_brush_height=-new_brush_height; } new_brush_height++; // Free some memory free(Smear_brush); if ((new_brush=((byte *)malloc(new_brush_width*new_brush_height)))) { Rescale(Brush, Brush_width, Brush_height, new_brush, new_brush_width, new_brush_height, x2MAX_PAINTBRUSH_SIZE)?Brush_width:MAX_PAINTBRUSH_SIZE; Smear_brush_height=(Brush_height>MAX_PAINTBRUSH_SIZE)?Brush_height:MAX_PAINTBRUSH_SIZE; Smear_brush=(byte *)malloc(((long)Smear_brush_height)*Smear_brush_width); if (!Smear_brush) // On ne peut même pas allouer la brosse du smear! { Error(0); free(Brush); Brush=(byte *)malloc(1*1); Brush_height=1; Brush_width=1; Smear_brush=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE); Smear_brush_height=MAX_PAINTBRUSH_SIZE; Smear_brush_width=MAX_PAINTBRUSH_SIZE; } Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } else { // Ici la libération de mémoire n'a pas suffi donc on remet dans l'état // où c'etait avant. On a juste à réallouer la Smear_brush car il y a // normalement la place pour elle puisque rien d'autre n'a pu être alloué // entre temps. Smear_brush=(byte *)malloc(((long)Smear_brush_height)*Smear_brush_width); Error(0); } } void Stretch_brush_preview(short x1, short y1, short x2, short y2) { int src_x_pos,src_y_pos; int initial_src_x_pos,initial_src_y_pos; int delta_x,delta_y; int dest_x_pos,dest_y_pos; int initial_dest_x_pos,initial_dest_y_pos; int final_dest_x_pos,final_dest_y_pos; int dest_width,dest_height; byte color; // 1er calcul des positions destination extremes: initial_dest_x_pos=Min(x1,x2); initial_dest_y_pos=Min(y1,y2); final_dest_x_pos =Max(x1,x2); final_dest_y_pos =Max(y1,y2); // Calcul des dimensions de la destination: dest_width=final_dest_x_pos-initial_dest_x_pos+1; dest_height=final_dest_y_pos-initial_dest_y_pos+1; // Calcul des vecteurs d'incrémentation : delta_x=(Brush_width<<16)/dest_width; delta_y=(Brush_height<<16)/dest_height; // 1er calcul de la position X initiale dans la source: initial_src_x_pos=(Brush_width<<16)* (Max(initial_dest_x_pos,Limit_left)- initial_dest_x_pos)/dest_width; // Calcul du clip de la destination: initial_dest_x_pos=Max(initial_dest_x_pos,Limit_left); final_dest_x_pos =Min(final_dest_x_pos ,Limit_visible_right); // On discute selon l'inversion en X if (x1>x2) { // Inversion -> Inversion du signe de delta_x delta_x=-delta_x; initial_src_x_pos=(Brush_width<<16)-1-initial_src_x_pos; } // 1er calcul de la position Y initiale dans la source: initial_src_y_pos=(Brush_height<<16)* (Max(initial_dest_y_pos,Limit_top)- initial_dest_y_pos)/dest_height; // Calcul du clip de la destination: initial_dest_y_pos=Max(initial_dest_y_pos,Limit_top); final_dest_y_pos =Min(final_dest_y_pos ,Limit_visible_bottom); // On discute selon l'inversion en Y if (y1>y2) { // Inversion -> Inversion du signe de delta_y delta_y=-delta_y; initial_src_y_pos=(Brush_height<<16)-1-initial_src_y_pos; } // Pour chaque ligne : src_y_pos=initial_src_y_pos; for (dest_y_pos=initial_dest_y_pos;dest_y_pos<=final_dest_y_pos;dest_y_pos++) { // Pour chaque colonne: src_x_pos=initial_src_x_pos; for (dest_x_pos=initial_dest_x_pos;dest_x_pos<=final_dest_x_pos;dest_x_pos++) { color=Read_pixel_from_brush(src_x_pos>>16,src_y_pos>>16); if (color!=Back_color) Pixel_preview(dest_x_pos,dest_y_pos,color); src_x_pos+=delta_x; } src_y_pos+=delta_y; } Update_part_of_screen(initial_dest_x_pos,initial_dest_y_pos,dest_width,dest_height); } /// Returns the minimum of 4 integers. int Min4(unsigned long int a,unsigned long int b,unsigned long int c,unsigned long int d) { if (ab) if (c>d) return a>c?a:c; else return a>d?a:d; else if (c>d) return b>c?b:c; else return b>d?b:d; } // Recursive function for linear distortion. void Draw_brush_linear_distort(unsigned long int tex_min_x, unsigned long int tex_min_y, unsigned long int tex_max_x, unsigned long int tex_max_y, unsigned long int x1, unsigned long int y1, unsigned long int x2, unsigned long int y2, unsigned long int x3, unsigned long int y3, unsigned long int x4, unsigned long int y4) { static byte color; // bounding rectangle static unsigned long int min_x, max_x, min_y, max_y; min_x=Min4(x1,x2,x3,x4); max_x=Max4(x1,x2,x3,x4); min_y=Min4(y1,y2,y3,y4); max_y=Max4(y1,y2,y3,y4); if ((max_x>>16) - (min_x>>16) <= 1 && (max_y>>16) - (min_y>>16) <= 1) //if (max_x - min_x <= 1<<16 && max_y - min_y <= 1<<16) { if ((min_x<(max_x&0x7FFF0000)) && (min_y<(max_y&0x7FFF0000))) { color=Read_pixel_from_brush((tex_min_x)>>16,(tex_min_y)>>16); if (color!=Back_color) Pixel_for_distort(min_x>>16,min_y>>16,color); } return; } // Cut in 4 quarters and repeat // "top left" Draw_brush_linear_distort(tex_min_x, tex_min_y, (tex_min_x+tex_max_x)>>1, (tex_min_y+tex_max_y)>>1, x1, y1, (x1+x2)>>1, (y1+y2)>>1, (x1+x2+x3+x4)>>2, (y1+y2+y3+y4)>>2, (x1+x4)>>1, (y1+y4)>>1); // "top right" Draw_brush_linear_distort((tex_min_x+tex_max_x)>>1, tex_min_y, tex_max_x, (tex_min_y+tex_max_y)>>1, (x1+x2)>>1, (y1+y2)>>1, x2, y2, (x2+x3)>>1, (y2+y3)>>1, (x1+x2+x3+x4)>>2, (y1+y2+y3+y4)>>2); // "bottom right" Draw_brush_linear_distort((tex_min_x+tex_max_x)>>1, (tex_min_y+tex_max_y)>>1, tex_max_x, tex_max_y, (x1+x2+x3+x4)>>2, (y1+y2+y3+y4)>>2, (x2+x3)>>1, (y2+y3)>>1, x3, y3, (x3+x4)>>1, (y3+y4)>>1); // "bottom left" Draw_brush_linear_distort(tex_min_x, (tex_min_y+tex_max_y)>>1, (tex_min_x+tex_max_x)>>1, tex_max_y, (x1+x4)>>1, (y1+y4)>>1, (x1+x2+x3+x4)>>2, (y1+y2+y3+y4)>>2, (x3+x4)>>1, (y3+y4)>>1, x4, y4); return; } /// Draws a distorted version of the brush, mapped over the given quad (picture coordinates). void Distort_brush_preview(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2, unsigned short x3, unsigned short y3, unsigned short x4, unsigned short y4) { Pixel_for_distort=Pixel_figure_preview; Draw_brush_linear_distort(0, 0, (Brush_width<<16), (Brush_height<<16), (x1<<16), (y1<<16), (x2<<16), (y2<<16), (x3<<16), (y3<<16), (x4<<16), (y4<<16)); } /// Modifies the current brush, mapping it over the given quad. void Distort_brush(short x1, short y1, short x2, short y2, short x3, short y3, short x4, short y4) { short min_x, max_x, min_y, max_y; short width, height; byte * new_brush; byte * new_smear_brush; short new_smear_brush_width; short new_smear_brush_height; // Move all coordinates to start on (0,0) min_x=Min4(x1,x2,x3,x4); max_x=Max4(x1,x2,x3,x4); min_y=Min4(y1,y2,y3,y4); max_y=Max4(y1,y2,y3,y4); x1-=min_x; x2-=min_x; x3-=min_x; x4-=min_x; y1-=min_y; y2-=min_y; y3-=min_y; y4-=min_y; width=Max(max_x-min_x, 1); height=Max(max_y-min_y, 1); new_smear_brush_width=(width>MAX_PAINTBRUSH_SIZE)?width:MAX_PAINTBRUSH_SIZE; new_smear_brush_height=(height>MAX_PAINTBRUSH_SIZE)?height:MAX_PAINTBRUSH_SIZE; new_smear_brush=(byte *)malloc(((long)new_smear_brush_height)*new_smear_brush_width); if (! new_smear_brush) { // Out of memory while allocating new smear brush Error(0); return; } new_brush=((byte *)malloc((long)width*height)); if (!new_brush) { // Out of memory while allocating new brush Error(0); free(new_smear_brush); return; } // Fill the new brush with backcolor, originally. memset(new_brush,Back_color,((long)width)*height); // Call distort routine Pixel_for_distort=Pixel_in_distort_buffer; Distort_buffer=new_brush; Distort_buffer_width=width; Draw_brush_linear_distort(0, 0, (Brush_width<<16), (Brush_height<<16), (x1<<16), (y1<<16), (x2<<16), (y2<<16), (x3<<16), (y3<<16), (x4<<16), (y4<<16)); // Free old brushes free(Smear_brush); free(Brush); // Point to the new ones Brush=new_brush; Brush_width=width; Brush_height=height; Smear_brush=new_smear_brush; Smear_brush_width=new_smear_brush_width; Smear_brush_height=new_smear_brush_height; } //------------------------- Rotation de la brosse --------------------------- #define UNDEFINED (-1.0e20F) float * ScanY_Xt[2]; float * ScanY_Yt[2]; float * ScanY_X[2]; void Interpolate_texture(int start_x,int start_y,int xt1,int yt1, int end_x ,int end_y ,int xt2,int yt2,int height) { int x_pos,y_pos; int incr_x,incr_y; int i,cumul; int delta_x,delta_y; int delta_xt=xt2-xt1; int delta_yt=yt2-yt1; int delta_x2=end_x-start_x; int delta_y2=end_y-start_y; float xt,yt; x_pos=start_x; y_pos=start_y; if (start_xdelta_y) { cumul=delta_x>>1; for (i=0; i<=delta_x; i++) { if (cumul>=delta_x) { cumul-=delta_x; y_pos+=incr_y; } if ((y_pos>=0) && (y_pos=ScanY_X[0][y_pos]) { if ((ScanY_X[1][y_pos]==UNDEFINED) // Droit non défini || (x_pos>ScanY_X[1][y_pos])) { ScanY_X[1][y_pos]=x_pos; ScanY_Xt[1][y_pos]=xt; ScanY_Yt[1][y_pos]=yt; } } else { if (ScanY_X[1][y_pos]==UNDEFINED) // Droit non défini { ScanY_X[1][y_pos]=ScanY_X[0][y_pos]; ScanY_Xt[1][y_pos]=ScanY_Xt[0][y_pos]; ScanY_Yt[1][y_pos]=ScanY_Yt[0][y_pos]; ScanY_X[0][y_pos]=x_pos; ScanY_Xt[0][y_pos]=xt; ScanY_Yt[0][y_pos]=yt; } else { ScanY_X[0][y_pos]=x_pos; ScanY_Xt[0][y_pos]=xt; ScanY_Yt[0][y_pos]=yt; } } } } x_pos+=incr_x; cumul+=delta_y; } } else { cumul=delta_y>>1; for (i=0; i<=delta_y; i++) { if (cumul>=delta_y) { cumul-=delta_y; x_pos+=incr_x; } if ((y_pos>=0) && (y_pos=ScanY_X[0][y_pos]) { if ((ScanY_X[1][y_pos]==UNDEFINED) // Droit non défini || (x_pos>ScanY_X[1][y_pos])) { ScanY_X[1][y_pos]=x_pos; ScanY_Xt[1][y_pos]=xt; ScanY_Yt[1][y_pos]=yt; } } else { if (ScanY_X[1][y_pos]==UNDEFINED) // Droit non défini { ScanY_X[1][y_pos]=ScanY_X[0][y_pos]; ScanY_Xt[1][y_pos]=ScanY_Xt[0][y_pos]; ScanY_Yt[1][y_pos]=ScanY_Yt[0][y_pos]; ScanY_X[0][y_pos]=x_pos; ScanY_Xt[0][y_pos]=xt; ScanY_Yt[0][y_pos]=yt; } else { ScanY_X[0][y_pos]=x_pos; ScanY_Xt[0][y_pos]=xt; ScanY_Yt[0][y_pos]=yt; } } } } y_pos+=incr_y; cumul+=delta_x; } } } void Compute_quad_texture(int x1,int y1,int xt1,int yt1, int x2,int y2,int xt2,int yt2, int x3,int y3,int xt3,int yt3, int x4,int y4,int xt4,int yt4, byte * buffer, int width, int height) { int x_min,/*x_max,*/y_min/*,y_max*/; int x,y,xt,yt; int start_x,end_x,line_width; float temp; //byte color; x_min=Min(Min(x1,x2),Min(x3,x4)); y_min=Min(Min(y1,y2),Min(y3,y4)); ScanY_Xt[0]=(float *)malloc(height*sizeof(float)); ScanY_Xt[1]=(float *)malloc(height*sizeof(float)); ScanY_Yt[0]=(float *)malloc(height*sizeof(float)); ScanY_Yt[1]=(float *)malloc(height*sizeof(float)); ScanY_X[0] =(float *)malloc(height*sizeof(float)); ScanY_X[1] =(float *)malloc(height*sizeof(float)); // Fill_general avec des valeurs égales à UNDEFINED. for (y=0; y=0 && yt>=0) buffer[x+(y*width)]=Read_pixel_from_brush(xt,yt); } for (; x>1); start_y=1-(Brush_height>>1); end_x=start_x+Brush_width-1; end_y=start_y+Brush_height-1; Transform_point(start_x,start_y, cos_a,sin_a, &x1,&y1); Transform_point(end_x ,start_y, cos_a,sin_a, &x2,&y2); Transform_point(start_x,end_y , cos_a,sin_a, &x3,&y3); Transform_point(end_x ,end_y , cos_a,sin_a, &x4,&y4); // Calcul des nouvelles dimensions de la brosse: x_min=Min(Min((int)x1,(int)x2),Min((int)x3,(int)x4)); x_max=Max(Max((int)x1,(int)x2),Max((int)x3,(int)x4)); y_min=Min(Min((int)y1,(int)y2),Min((int)y3,(int)y4)); y_max=Max(Max((int)y1,(int)y2),Max((int)y3,(int)y4)); new_brush_width=x_max+1-x_min; new_brush_height=y_max+1-y_min; free(Smear_brush); // On libère un peu de mémoire if ((new_brush=((byte *)malloc(new_brush_width*new_brush_height)))) { // Et maintenant on calcule la nouvelle brosse tournée. Compute_quad_texture(x1,y1, 0, 0, x2,y2,Brush_width-1, 0, x3,y3, 0,Brush_height-1, x4,y4,Brush_width-1,Brush_height-1, new_brush,new_brush_width,new_brush_height); free(Brush); Brush=new_brush; Brush_width=new_brush_width; Brush_height=new_brush_height; Smear_brush_width=(Brush_width>MAX_PAINTBRUSH_SIZE)?Brush_width:MAX_PAINTBRUSH_SIZE; Smear_brush_height=(Brush_height>MAX_PAINTBRUSH_SIZE)?Brush_height:MAX_PAINTBRUSH_SIZE; Smear_brush=(byte *)malloc(((long)Smear_brush_height)*Smear_brush_width); if (!Smear_brush) // On ne peut même pas allouer la brosse du smear! { Error(0); free(Brush); Brush=(byte *)malloc(1*1); Brush_height=1; Brush_width=1; Smear_brush=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE); Smear_brush_height=MAX_PAINTBRUSH_SIZE; Smear_brush_width=MAX_PAINTBRUSH_SIZE; } Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } else { // Ici la libération de mémoire n'a pas suffit donc on remet dans l'état // où c'etait avant. On a juste à réallouer la Smear_brush car il y a // normalement la place pour elle puisque rien d'autre n'a pu être alloué // entre temps. Smear_brush=(byte *)malloc(((long)Smear_brush_height)*Smear_brush_width); Error(0); } } void Draw_quad_texture_preview(int x1,int y1,int xt1,int yt1, int x2,int y2,int xt2,int yt2, int x3,int y3,int xt3,int yt3, int x4,int y4,int xt4,int yt4) { int x_min,x_max,y_min,y_max; int x,y,xt,yt; int y_,y_min_; int start_x,end_x,width,height; float temp; byte color; x_min=Min(Min(x1,x2),Min(x3,x4)); x_max=Max(Max(x1,x2),Max(x3,x4)); y_min=Min(Min(y1,y2),Min(y3,y4)); y_max=Max(Max(y1,y2),Max(y3,y4)); height=1+y_max-y_min; ScanY_Xt[0]=(float *)malloc(height*sizeof(float)); ScanY_Xt[1]=(float *)malloc(height*sizeof(float)); ScanY_Yt[0]=(float *)malloc(height*sizeof(float)); ScanY_Yt[1]=(float *)malloc(height*sizeof(float)); ScanY_X[0] =(float *)malloc(height*sizeof(float)); ScanY_X[1] =(float *)malloc(height*sizeof(float)); // Fill_general avec des valeurs égales à UNDEFINED. for (y=0; yLimit_bottom) y_max=Limit_bottom; for (y_=y_min; y_<=y_max; y_++) { y=y_-y_min_; start_x=Round(ScanY_X[0][y]); end_x =Round(ScanY_X[1][y]); width=1+end_x-start_x; if (start_xLimit_right) end_x=Limit_right; for (x=start_x; x<=end_x; x++) { temp=(float)(0.5+(float)x-ScanY_X[0][y])/(float)width; xt=Round((float)(ScanY_Xt[0][y])+(temp*(ScanY_Xt[1][y]-ScanY_Xt[0][y]))); yt=Round((float)(ScanY_Yt[0][y])+(temp*(ScanY_Yt[1][y]-ScanY_Yt[0][y]))); if (xt>=0 && yt>=0) { color=Read_pixel_from_brush(xt,yt); if (color!=Back_color) Pixel_preview(x,y_,color); } } } free(ScanY_Xt[0]); free(ScanY_Xt[1]); free(ScanY_Yt[0]); free(ScanY_Yt[1]); free(ScanY_X[0]); free(ScanY_X[1]); } void Rotate_brush_preview(float angle) { short x1,y1,x2,y2,x3,y3,x4,y4; int start_x,end_x,start_y,end_y; float cos_a=cos(angle); float sin_a=sin(angle); // Calcul des coordonnées des 4 coins: // 1 2 // 3 4 start_x=1-(Brush_width>>1); start_y=1-(Brush_height>>1); end_x=start_x+Brush_width-1; end_y=start_y+Brush_height-1; Transform_point(start_x,start_y, cos_a,sin_a, &x1,&y1); Transform_point(end_x ,start_y, cos_a,sin_a, &x2,&y2); Transform_point(start_x,end_y , cos_a,sin_a, &x3,&y3); Transform_point(end_x ,end_y , cos_a,sin_a, &x4,&y4); x1+=Brush_rotation_center_X; y1+=Brush_rotation_center_Y; x2+=Brush_rotation_center_X; y2+=Brush_rotation_center_Y; x3+=Brush_rotation_center_X; y3+=Brush_rotation_center_Y; x4+=Brush_rotation_center_X; y4+=Brush_rotation_center_Y; // Et maintenant on dessine la brosse tournée. Draw_quad_texture_preview(x1,y1, 0, 0, x2,y2,Brush_width-1, 0, x3,y3, 0,Brush_height-1, x4,y4,Brush_width-1,Brush_height-1); start_x=Min(Min(x1,x2),Min(x3,x4)); end_x=Max(Max(x1,x2),Max(x3,x4)); start_y=Min(Min(y1,y2),Min(y3,y4)); end_y=Max(Max(y1,y2),Max(y3,y4)); Update_part_of_screen(start_x,start_y,end_x-start_x+1,end_y-start_y+1); }