/* vim:expandtab:ts=2 sw=2: */ /* 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 ******************************************************************************** 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" #include "tiles.h" // Data used during brush rotation operation static byte * Brush_rotate_buffer; static int Brush_rotate_width; static int Brush_rotate_height; // 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; } } /// Display the paintbrush (preview : on screen only) void Display_paintbrush(short x,short y,byte color) // x,y: position du centre du pinceau // color: couleur à appliquer au pinceau { 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 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; if (Mouse_K) // pas de curseur si on est en preview et return; // en train de cliquer if (Main_backups->Pages->Image_mode == IMAGE_MODE_MODE5 && Main_current_layer < 4) { goto single_pixel; } switch (Paintbrush_shape) { case PAINTBRUSH_SHAPE_NONE : // No paintbrush. for colorpicker for example break; case PAINTBRUSH_SHAPE_POINT : // A single point single_pixel: 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 : // The color brush, displayed in color case PAINTBRUSH_SHAPE_MONO_BRUSH : // or in monochrome with FG color 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 (Paintbrush_shape==PAINTBRUSH_SHAPE_COLOR_BRUSH) 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); else // mono preview 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); Update_part_of_screen(x-Brush_offset_X,y-Brush_offset_Y,Brush_width,Brush_height); if (Main_magnifier_mode != 0) { 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; if (Paintbrush_shape==PAINTBRUSH_SHAPE_COLOR_BRUSH) 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); else // mono preview 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); } } break; default : // a paintbrush 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) ) 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 != 0) { 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; } } /// Draw the paintbrush in the image buffer void Draw_paintbrush(short x,short y,byte color) // x,y: position du centre du pinceau // color: couleur à appliquer au pinceau { 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 old_color; if (Main_backups->Pages->Image_mode == IMAGE_MODE_MODE5 && Main_current_layer < 4) { // Flood-fill the enclosing area if (x= 0 && y >= 0 && (color=Effect_function(x,y,color)) != (old_color=Read_pixel_from_current_layer(x,y)) && (!((Stencil_mode) && (Stencil[old_color]))) && (!((Mask_mode) && (Mask_table[Read_pixel_from_spare_screen(x,y)]))) ) { short min_x,width,min_y,height; short xx,yy; // determine area switch(Main_current_layer) { case 0: default: // Full layer min_x=0; min_y=0; width=Main_image_width; height=Main_image_height; break; case 1: case 2: // Line min_x=0; min_y=y; width=Main_image_width; height=1; break; case 3: // Segment min_x=x / 48 * 48; min_y=y; width=48; height=1; break; //case 4: // // 8x8 // min_x=x / 8 * 8; // min_y=y / 8 * 8; // width=8; // height=8; // break; } // Clip the bottom edge. // (Necessary if image height is not a multiple) if (min_y+height>=Main_image_height) height=Main_image_height-min_y; // Clip the right edge. // (Necessary if image width is not a multiple) if (min_x+width>=Main_image_width) width=Main_image_width-min_x; for (yy=min_y; yy0) && (height>0) ) Clear_brush(min_x-Main_offset_X, min_y-Main_offset_Y, 0,0, width,height,0, Main_image_width); if (Main_magnifier_mode != 0) { Compute_clipped_dimensions_zoom(&min_x,&min_y,&width,&height); xx=min_x; yy=min_y; if ( (width>0) && (height>0) ) { // Corrections dues au Zoom: min_x=(min_x-Main_magnifier_offset_X)*Main_magnifier_factor; min_y=(min_y-Main_magnifier_offset_Y)*Main_magnifier_factor; height=min_y+(height*Main_magnifier_factor); if (height>Menu_Y) height=Menu_Y; Clear_brush_scaled(Main_X_zoom+min_x,min_y, xx,yy, width,height,0, Main_image_width, Horizontal_line_buffer); } } // End of graphic feedback } return; } switch (Paintbrush_shape) { case PAINTBRUSH_SHAPE_NONE : // No paintbrush. for colorpicker for example case PAINTBRUSH_SHAPE_POINT : // !!! TOUJOURS EN PREVIEW !!! 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 ((Smear_mode != 0) && (Shade_table==Shade_table_left)) { if (Smear_start != 0) { 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)) { 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)) { 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) // On clippe l'effet smear entre Smear_Min et Smear_Max ) 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_yMAX_PAINTBRUSH_SIZE)?new_brush_width:MAX_PAINTBRUSH_SIZE; new_smear_brush_height=(new_brush_height>MAX_PAINTBRUSH_SIZE)?new_brush_height:MAX_PAINTBRUSH_SIZE; new_smear_brush=NULL; if ( (((long)Smear_brush_height)*Smear_brush_width) != (((long)new_smear_brush_width)*new_smear_brush_height) ) { new_smear_brush=(byte *)malloc(((long)new_smear_brush_height)*new_smear_brush_width); if (new_smear_brush == NULL) { Error(0); if (old_brush) *old_brush=NULL; if (!new_brush_is_provided) free(new_brush); return 2; } } new_brush_remapped=NULL; if ( (((long)Brush_height)*Brush_width) != (((long)new_brush_height)*new_brush_width) ) { new_brush_remapped=(byte *)malloc(((long)new_brush_height)*new_brush_width); if (new_brush_remapped == NULL) { Error(0); free(new_smear_brush); if (old_brush) *old_brush=NULL; if (!new_brush_is_provided) free(new_brush); return 3; } } // All allocations successful: can replace globals Brush_width=new_brush_width; Brush_height=new_brush_height; Brush_original_back_color=Back_color; if (new_smear_brush) { free(Smear_brush); Smear_brush=new_smear_brush; } Smear_brush_width=new_smear_brush_width; Smear_brush_height=new_smear_brush_height; // Save or free the old brush pixels if (old_brush) *old_brush=Brush_original_pixels; else free(old_brush); Brush_original_pixels=new_brush; // Assign new brush if (new_brush_remapped) { free(Brush); Brush=new_brush_remapped; } return 0; } // -- Effacer le pinceau -- // // void Hide_paintbrush(short x,short y) // x,y: position du centre du pinceau { 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'affichage de la brosse/pinceau short end_counter_y; // Position Y ou s'arrête l'affichage de la brosse/pinceau byte * temp; if (Mouse_K == 0) switch (Paintbrush_shape) { case PAINTBRUSH_SHAPE_POINT : if ( (Paintbrush_X>=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 != 0) { 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 != 0) { 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 (Realloc_brush(new_brush_width, new_brush_height, NULL, NULL)) return; // Unable to allocate the new brush, keep the old one. 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) { if (Main_tilemap_mode) { for (y_pos=start_y;y_pos>1); Brush_offset_Y=(Brush_height>>1); } } void Rotate_90_deg(void) { byte * old_brush; if (Realloc_brush(Brush_height, Brush_width, NULL, &old_brush)) { Error(0); return; } Rotate_90_deg_lowlevel(old_brush,Brush_original_pixels,Brush_height,Brush_width); free(old_brush); // Remap according to the last used remap table Remap_general_lowlevel(Brush_colormap,Brush_original_pixels,Brush,Brush_width,Brush_height,Brush_width); // On centre la prise sur la brosse Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } void Remap_brush(void) { short x_pos; // Variable de balayage de la brosse short y_pos; // Variable de balayage de la brosse int color; // On commence par initialiser le tableau de booléens à faux for (color=0;color<=255;color++) Brush_colormap[color]=0; // On calcule la table d'utilisation des couleurs for (y_pos=0;y_pos>1); Brush_offset_Y=(Brush_height>>1); free(old_brush); // Libération de l'ancienne brosse } void Nibble_brush(void) { long x_pos,y_pos; byte state; byte * old_brush; word old_width; word old_height; int i; if ( (Brush_width>2) && (Brush_height>2) ) { old_width=Brush_width; old_height=Brush_height; SWAP_PBYTES(Brush, Brush_original_pixels); if (Realloc_brush(Brush_width-2, Brush_height-2, NULL, &old_brush)) { Error(0); SWAP_PBYTES(Brush, Brush_original_pixels); return; } // On copie l'ancienne brosse dans la nouvelle Copy_part_of_image_to_another(old_brush, // source 1, 1, old_width-2, old_height-2, old_width, Brush, // Destination 0, 0, Brush_width); // 1er balayage (horizontal) for (y_pos=0; y_pos0) Pixel_in_brush(x_pos-1,y_pos,Back_color); state=0; } } else { if (state == 0) { Pixel_in_brush(x_pos,y_pos,Back_color); state=1; } } } // Cas du dernier pixel à droite de la ligne if (old_brush[((y_pos+1)*old_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 == 0) { Pixel_in_brush(x_pos,y_pos,Back_color); state=1; } } } // Cas du dernier pixel en bas de la colonne if (old_brush[((y_pos+1)*old_width)+x_pos+1]==Back_color) Pixel_in_brush(x_pos,y_pos-1,Back_color); } free(old_brush); // Adopt the current palette. memcpy(Brush_original_palette, Main_palette,sizeof(T_Palette)); memcpy(Brush_original_pixels, Brush, (long)Brush_width*Brush_height); for (i=0; i<256; i++) Brush_colormap[i]=i; //-- // On recentre la prise sur la brosse Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } } 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; unsigned 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; temp<2*vertices; temp+=2) { x_pos=points[temp]; y_pos=points[temp+1]; if (x_posend_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_x>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++; new_brush=((byte *)malloc(new_brush_width*new_brush_height)); if (!new_brush) { Error(0); return; } Rescale(Brush_original_pixels, Brush_width, Brush_height, new_brush, new_brush_width, new_brush_height, x2>1); Brush_offset_Y=(Brush_height>>1); } 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(long int a, long int b, long int c, 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, long int x1, long int y1, long int x2, long int y2, long int x3, long int y3, long int x4, long int y4) { static byte color; // bounding rectangle static 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(short x1, short y1, short x2, short y2, short x3, short y3, short x4, 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; // 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_brush=((byte *)malloc((long)width*height)); if (!new_brush) { // Out of memory while allocating new brush Error(0); 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)); if (Realloc_brush(width, height, new_brush, NULL)) { free(new_brush); Error(0); return; } // Remap according to the last used remap table Remap_general_lowlevel(Brush_colormap,Brush_original_pixels,Brush,Brush_width,Brush_height,Brush_width); // Re-center brush handle Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } //------------------------- Rotation de la brosse --------------------------- #ifndef NAN #define NAN (-1.0e20F) #define isnan(x) ((x)==NAN) #endif 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 (isnan(ScanY_X[1][y_pos]) // Droit non défini || (x_pos>ScanY_X[1][y_pos])) { ScanY_X[1][y_pos]=(float)x_pos; ScanY_Xt[1][y_pos]=xt; ScanY_Yt[1][y_pos]=yt; } } else { if (isnan(ScanY_X[1][y_pos])) // 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]=(float)x_pos; ScanY_Xt[0][y_pos]=xt; ScanY_Yt[0][y_pos]=yt; } else { ScanY_X[0][y_pos]=(float)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 (isnan(ScanY_X[1][y_pos]) // Droit non défini || (x_pos>ScanY_X[1][y_pos])) { ScanY_X[1][y_pos]=(float)x_pos; ScanY_Xt[1][y_pos]=xt; ScanY_Yt[1][y_pos]=yt; } } else { if (isnan(ScanY_X[1][y_pos])) // 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]=(float)x_pos; ScanY_Xt[0][y_pos]=xt; ScanY_Yt[0][y_pos]=yt; } else { ScanY_X[0][y_pos]=(float)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( byte *texture, int texture_width, 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 à NAN. for (y=0; y=0 && yt>=0) buffer[x+(y*width)]=*(texture + yt * texture_width + xt); } for (; x>1); start_y=1-(Brush_height>>1); end_x=start_x+Brush_width-1; end_y=start_y+Brush_height-1; //offset = Brush_rotate_width/Brush_width-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; new_brush=(byte *)malloc(new_brush_width*new_brush_height); if (!new_brush) { Error(0); return; } // Et maintenant on calcule la nouvelle brosse tournée. Compute_quad_texture( Brush_rotate_buffer, Brush_rotate_width, x1,y1, offset, offset, x2,y2,Brush_rotate_width-offset-1, offset, x3,y3, offset,Brush_rotate_height-offset-1, x4,y4,Brush_rotate_width-offset-1,Brush_rotate_height-offset-1, new_brush,new_brush_width,new_brush_height); if (Realloc_brush(new_brush_width, new_brush_height, new_brush, NULL)) { free(new_brush); return; } // Remap according to the last used remap table Remap_general_lowlevel(Brush_colormap,Brush_original_pixels,Brush,Brush_width,Brush_height,Brush_width); // Center offsets Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); } void Draw_quad_texture_preview(byte *texture, int texture_width, 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 à NAN. 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=Brush_colormap[*(texture+xt+yt*texture_width)]; 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]); ScanY_Xt[0] = ScanY_Xt[1] = ScanY_Yt[0] = ScanY_Yt[1] = ScanY_X[0] = ScanY_X[1] = NULL; } 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); int offset=0; // 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; //offset = Brush_rotate_width/Brush_width-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(Brush_rotate_buffer, Brush_rotate_width, x1, y1, offset, offset, x2, y2, Brush_rotate_width-offset-1, offset, x3, y3, offset, Brush_rotate_height-offset-1, x4, y4, Brush_rotate_width-offset-1, Brush_rotate_height-offset-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); } /* /// Sets brush's original palette and color mapping. void Brush_set_palette(T_Palette *palette) { int i; byte need_remap; need_remap=0; memcpy(Brush_original_palette,palette,sizeof(T_Palette)); for (i=0;i<256;i++) { if (Brush_original_palette[i].R!=Main_palette[i].R || Brush_original_palette[i].G!=Main_palette[i].G || Brush_original_palette[i].B!=Main_palette[i].B) { need_remap=1; } } } */