diff --git a/Makefile b/Makefile index 0bf5c4f1..f8830ffe 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ ifdef COMSPEC RMDIR = rmdir CP = cp BIN = grafx2.exe - COPT = -W -Wall -Wdeclaration-after-statement -O$(OPTIM) -g -ggdb `sdl-config --cflags` $(TTFCOPT) $(JOYCOPT) $(LUACOPT) + COPT = -W -Wall -Wdeclaration-after-statement -O$(OPTIM) -g -ggdb `sdl-config --cflags` $(TTFCOPT) $(JOYCOPT) $(LUACOPT) $(LAYERCOPT) LOPT = `sdl-config --libs` -lSDL_image $(TTFLOPT) -lpng $(LUALOPT) CC = gcc OBJDIR = obj/win32 @@ -274,6 +274,14 @@ else JOYCOPT = endif +#To speed up rendering, can disable the layered editing +# with NOLAYERS=1 +ifeq ($(NOLAYERS),1) + LAYERCOPT = -DNOLAYERS +else + LAYERCOPT = +endif + ### And now for the real build rules ### .PHONY : all debug release clean depend zip version force install uninstall diff --git a/Makefile.dep b/Makefile.dep index bf29f1e0..b95a0802 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -7,7 +7,11 @@ $(OBJDIR)/buttons.o: buttons.c const.h struct.h global.h misc.h graph.h engine.h sdlscreen.h windows.h brush.h input.h special.h $(OBJDIR)/engine.o: engine.c const.h struct.h global.h graph.h misc.h special.h \ buttons.h operatio.h shade.h errors.h sdlscreen.h windows.h brush.h \ - input.h engine.h + input.h engine.h pages.h +$(OBJDIR)/factory.o: factory.c brush.h struct.h const.h buttons.h engine.h errors.h \ + filesel.h global.h graph.h io.h misc.h readline.h sdlscreen.h windows.h +$(OBJDIR)/fileformats.o: fileformats.c errors.h global.h struct.h const.h \ + loadsave.h misc.h $(OBJDIR)/filesel.o: filesel.c const.h struct.h global.h misc.h errors.h io.h \ windows.h sdlscreen.h loadsave.h mountlist.h engine.h readline.h \ input.h help.h filesel.h @@ -16,23 +20,27 @@ $(OBJDIR)/graph.o: graph.c global.h struct.h const.h engine.h buttons.h pages.h pxdouble.h pxtriple.h pxwide2.h pxtall2.h pxquad.h windows.h input.h $(OBJDIR)/help.o: help.c const.h struct.h global.h misc.h engine.h helpfile.h \ help.h sdlscreen.h text.h keyboard.h windows.h input.h hotkeys.h \ - errors.h + errors.h pages.h $(OBJDIR)/hotkeys.o: hotkeys.c struct.h const.h global.h hotkeys.h -$(OBJDIR)/init.o: init.c const.h struct.h global.h graph.h buttons.h palette.h \ - help.h operatio.h misc.h errors.h keyboard.h io.h hotkeys.h setup.h \ - windows.h sdlscreen.h mountlist.h loadsave.h init.h transform.h +$(OBJDIR)/init.o: init.c buttons.h struct.h const.h errors.h global.h graph.h \ + init.h io.h factory.h help.h hotkeys.h keyboard.h loadsave.h misc.h \ + mountlist.h operatio.h palette.h sdlscreen.h setup.h transform.h \ + windows.h $(OBJDIR)/input.o: input.c global.h struct.h const.h keyboard.h sdlscreen.h \ windows.h errors.h misc.h input.h $(OBJDIR)/io.o: io.c struct.h const.h io.h realpath.h $(OBJDIR)/keyboard.o: keyboard.c global.h struct.h const.h keyboard.h +$(OBJDIR)/libraw2crtc.o: libraw2crtc.c global.h struct.h const.h $(OBJDIR)/loadsave.o: loadsave.c buttons.h struct.h const.h errors.h global.h io.h \ - loadsave.h misc.h op_c.h pages.h palette.h sdlscreen.h windows.h \ - engine.h + loadsave.h misc.h graph.h op_c.h pages.h palette.h sdlscreen.h \ + windows.h engine.h $(OBJDIR)/main.o: main.c const.h struct.h global.h graph.h misc.h init.h buttons.h \ engine.h pages.h loadsave.h sdlscreen.h errors.h readini.h saveini.h \ io.h text.h setup.h windows.h brush.h palette.h realpath.h $(OBJDIR)/misc.o: misc.c struct.h const.h sdlscreen.h global.h errors.h buttons.h \ - engine.h misc.h keyboard.h windows.h palette.h input.h + engine.h misc.h keyboard.h windows.h palette.h input.h graph.h +$(OBJDIR)/miscfileformats.o: miscfileformats.c engine.h struct.h const.h errors.h \ + global.h io.h libraw2crtc.h loadsave.h misc.h sdlscreen.h windows.h $(OBJDIR)/mountlist.o: mountlist.c $(OBJDIR)/op_c.o: op_c.c op_c.h struct.h const.h errors.h $(OBJDIR)/operatio.o: operatio.c const.h struct.h global.h misc.h engine.h graph.h \ @@ -68,15 +76,15 @@ $(OBJDIR)/saveini.o: saveini.c const.h global.h struct.h readini.h io.h errors.h $(OBJDIR)/sdlscreen.o: sdlscreen.c global.h struct.h const.h sdlscreen.h errors.h \ misc.h $(OBJDIR)/setup.o: setup.c struct.h const.h io.h setup.h -$(OBJDIR)/shade.o: shade.c global.h struct.h const.h graph.h engine.h misc.h \ - readline.h help.h sdlscreen.h windows.h input.h shade.h +$(OBJDIR)/shade.o: shade.c global.h struct.h const.h graph.h engine.h errors.h \ + misc.h readline.h help.h sdlscreen.h windows.h input.h shade.h $(OBJDIR)/special.o: special.c const.h struct.h global.h graph.h engine.h windows.h \ - special.h + special.h pages.h misc.h buttons.h $(OBJDIR)/text.o: text.c SFont.h struct.h const.h global.h sdlscreen.h io.h \ errors.h $(OBJDIR)/transform.o: transform.c global.h struct.h const.h transform.h engine.h \ sdlscreen.h windows.h input.h help.h misc.h readline.h buttons.h \ pages.h $(OBJDIR)/version.o: version.c -$(OBJDIR)/windows.o: windows.c windows.h struct.h const.h global.h graph.h engine.h \ - misc.h sdlscreen.h errors.h input.h +$(OBJDIR)/windows.o: windows.c windows.h struct.h const.h engine.h errors.h \ + global.h graph.h input.h misc.h readline.h sdlscreen.h diff --git a/brush.c b/brush.c index df1179f9..21f4dc01 100644 --- a/brush.c +++ b/brush.c @@ -730,8 +730,7 @@ void Capture_brush(short start_x,short start_y,short end_x,short end_y,short cle for (y_pos=start_y;y_posPages->Nb_layers; i++) + { + if (i == Spare_current_layer) + { + // Copy the current layer + memcpy(Spare_backups->Pages->Image[i],Main_backups->Pages->Image[Main_current_layer],Main_image_width*Main_image_height); + } + else + { + // Resize the original layer + Copy_part_of_image_to_another( + Spare_backups->Pages->Next->Image[i],0,0,Min(old_width,Spare_image_width), + Min(old_height,Spare_image_height),old_width, + Spare_backups->Pages->Image[i],0,0,Spare_image_width); + } + } // Copie des dimensions de l'image /* C'est inutile, le "Backuper et redimensionner brouillon" a déjà modifié ces valeurs pour qu'elles soient correctes. */ + /* Spare_image_width=Main_image_width; Spare_image_height=Main_image_height; + */ // Copie des décalages de la fenêtre principale (non zoomée) de l'image Spare_offset_X=Main_offset_X; @@ -1430,10 +1458,10 @@ void Button_Copy_page(void) // -- Suppression d'une page ------------------------------------------------- void Button_Kill(void) { - if ( (Main_backups->Nb_pages_allocated==1) + if ( (Main_backups->List_size==1) || (!Confirmation_box("Delete the current page?")) ) { - if (Main_backups->Nb_pages_allocated==1) + if (Main_backups->List_size==1) Warning_message("You can't delete the last page."); Hide_cursor(); Unselect_button(BUTTON_KILL); @@ -2467,33 +2495,33 @@ void Button_Paintbrush_menu(void) Hide_cursor(); Display_stored_brush_in_window(x_pos+2, y_pos+2, index); Display_cursor(); - } + } else { // Restore and exit - + if (Restore_brush(index)) { - Close_window(); + Close_window(); break; } } - + } else if (clicked_button>1 && Window_attribute1==LEFT_SIDE) // Standard paintbrushes - { + { Close_window(); - index=clicked_button-2; - Paintbrush_shape=Gfx->Paintbrush_type[index]; - Paintbrush_width=Gfx->Preset_paintbrush_width[index]; - Paintbrush_height=Gfx->Preset_paintbrush_height[index]; - Paintbrush_offset_X=Gfx->Preset_paintbrush_offset_X[index]; - Paintbrush_offset_Y=Gfx->Preset_paintbrush_offset_Y[index]; - for (y_pos=0; y_posPaintbrush_sprite[index][y_pos][x_pos]; - Change_paintbrush_shape(Gfx->Paintbrush_type[index]); + index=clicked_button-2; + Paintbrush_shape=Gfx->Paintbrush_type[index]; + Paintbrush_width=Gfx->Preset_paintbrush_width[index]; + Paintbrush_height=Gfx->Preset_paintbrush_height[index]; + Paintbrush_offset_X=Gfx->Preset_paintbrush_offset_X[index]; + Paintbrush_offset_Y=Gfx->Preset_paintbrush_offset_Y[index]; + for (y_pos=0; y_posPaintbrush_sprite[index][y_pos][x_pos]; + Change_paintbrush_shape(Gfx->Paintbrush_type[index]); break; } @@ -2802,6 +2830,7 @@ void Load_picture(byte image) } Compute_optimal_menu_colors(Main_palette); + Redraw_layered_image(); Display_all_screen(); if (image) @@ -2903,7 +2932,7 @@ void Button_Reload(void) Compute_limits(); Compute_paintbrush_coordinates(); } - + Redraw_layered_image(); Display_all_screen(); Main_image_is_modified=0; @@ -4158,56 +4187,6 @@ void Effects_off(void) Snap_mode=0; } -void Transparency_set(byte amount) -{ - const int doubleclick_delay = Config.Double_key_speed; - static long time_click = 0; - long time_previous; - - if (!Colorize_mode) - { - // Activate mode - switch(Colorize_current_mode) - { - case 0 : - Effect_function=Effect_interpolated_colorize; - break; - case 1 : - Effect_function=Effect_additive_colorize; - break; - case 2 : - Effect_function=Effect_substractive_colorize; - } - Shade_mode=0; - Quick_shade_mode=0; - Smooth_mode=0; - Tiling_mode=0; - - Colorize_mode=1; - } - - time_previous = time_click; - time_click = SDL_GetTicks(); - - // Check if it's a quick re-press - if (time_click - time_previous < doubleclick_delay) - { - // Use the typed amount as units, keep the tens. - Colorize_opacity = ((Colorize_opacity%100) /10 *10) + amount; - if (Colorize_opacity == 0) - Colorize_opacity = 100; - } - else - { - // Use 10% units: "1"=10%, ... "0"=100% - if (amount == 0) - Colorize_opacity = 100; - else - Colorize_opacity = amount*10; - } - Compute_colorize_table(); -} - //---------------------------- Courbes de Bézier ---------------------------- void Button_Curves(void) @@ -5396,9 +5375,11 @@ void Button_Effects(void) break; case 13 : // Feedback (pour Colorize et Shade) if ((Config.FX_Feedback=!Config.FX_Feedback)) //!!! - FX_feedback_screen=Main_screen; + FX_feedback_screen=Main_backups->Pages->Image[Main_current_layer]; + // Main_screen else - FX_feedback_screen=Screen_backup; + FX_feedback_screen=Main_backups->Pages->Next->Image[Main_current_layer]; + // Screen_backup Hide_cursor(); Display_feedback_state(); Display_cursor(); @@ -5706,10 +5687,10 @@ void Display_stored_brush_in_window(word x_pos,word y_pos,int index) offset_y = (BRUSH_CONTAINER_PREVIEW_HEIGHT-Brush_container[index].Height)/2; // Determine corner pixel of paintbrush to draw (if bigger than preview area) // - + // Clear Window_rectangle(x_pos,y_pos,BRUSH_CONTAINER_PREVIEW_WIDTH,BRUSH_CONTAINER_PREVIEW_HEIGHT,MC_Light); - + // Draw up to 16x16 for (y=0; y0) + { + // Backup layer below the current + Backup_layers(1<<(Main_current_layer-1)); + + Merge_layer(); + + Update_screen_targets(); + Redraw_layered_image(); + Hide_cursor(); + Display_all_screen(); + Display_cursor(); + End_of_modification(); + } + action++; + break; + case SPECIAL_LAYER_SWAP_UP: + if (Main_current_layer < (Main_backups->Pages->Nb_layers-1)) + { + byte * tmp; + dword layer_flags; + + // Backup with unchanged layers + Backup_layers(0); + + // swap + tmp = Main_backups->Pages->Image[Main_current_layer]; + Main_backups->Pages->Image[Main_current_layer] = Main_backups->Pages->Image[Main_current_layer+1]; + Main_backups->Pages->Image[Main_current_layer+1] = tmp; + + // Swap visibility indicators + layer_flags = (Main_layers_visible >> Main_current_layer) & 3; + // Only needed if they are different. + if (layer_flags == 1 || layer_flags == 2) + { + // One is on, the other is off. Negating them will + // perform the swap. + Main_layers_visible ^= (3 << Main_current_layer); + } + Main_current_layer++; + + Update_screen_targets(); + Redraw_layered_image(); + Hide_cursor(); + Display_all_screen(); + Display_cursor(); + End_of_modification(); + } + action++; + break; + + case SPECIAL_LAYER_SWAP_DOWN: + if (Main_current_layer > 0) + { + byte * tmp; + dword layer_flags; + + // Backup with unchanged layers + Backup_layers(0); + + // swap + tmp = Main_backups->Pages->Image[Main_current_layer]; + Main_backups->Pages->Image[Main_current_layer] = Main_backups->Pages->Image[Main_current_layer-1]; + Main_backups->Pages->Image[Main_current_layer-1] = tmp; + + // Swap visibility indicators + layer_flags = (Main_layers_visible >> (Main_current_layer-1)) & 3; + // Only needed if they are different. + if (layer_flags == 1 || layer_flags == 2) + { + // Only needed if they are different. + // One is on, the other is off. Negating them will + // perform the swap. + Main_layers_visible ^= (3 << (Main_current_layer-1)); + } + Main_current_layer--; + Update_screen_targets(); + Redraw_layered_image(); + Hide_cursor(); + Display_all_screen(); + Display_cursor(); + End_of_modification(); + } + action++; + break; } } } // End of special keys diff --git a/factory.c b/factory.c index 2ca223a2..32142429 100644 --- a/factory.c +++ b/factory.c @@ -86,14 +86,14 @@ int L_GetPictureSize(lua_State* L) int L_PutPicturePixel(lua_State* L) { - Pixel_in_current_screen(lua_tonumber(L, 1), lua_tonumber(L, 2), + Pixel_in_current_layer(lua_tonumber(L, 1), lua_tonumber(L, 2), lua_tonumber(L, 3)); return 0; // no values returned for lua } int L_GetPicturePixel(lua_State* L) { - uint8_t c = Read_pixel_from_current_screen(lua_tonumber(L, 1), + uint8_t c = Read_pixel_from_current_layer(lua_tonumber(L, 1), lua_tonumber(L, 2)); lua_pushinteger(L, c); return 1; diff --git a/fileformats.c b/fileformats.c index 35dbd2e1..211367c3 100644 --- a/fileformats.c +++ b/fileformats.c @@ -1544,24 +1544,38 @@ void Save_BMP(void) #pragma pack(1) typedef struct { - word Width; // width de l'écran virtuel - word Height; // height de l'écran virtuel - byte Resol; // Informations sur la résolution (et autres) - byte Backcol; // color de fond - byte Aspect; // Informations sur l'aspect ratio (et autres) -} T_GIF_LSDB; // Logical Screen Descriptor Block + word Width; // Width of the complete image area + word Height; // Height of the complete image area + byte Resol; // Informations about the resolution (and other) + byte Backcol; // Proposed background color + byte Aspect; // Informations about aspect ratio (and other) +} T_GIF_LSDB; // Logical Screen Descriptor Block typedef struct { - word Pos_X; // Abscisse où devrait être affichée l'image - word Pos_Y; // Ordonnée où devrait être affichée l'image - word Image_width; // width de l'image - word Image_height; // height de l'image - byte Indicator; // Informations diverses sur l'image + word Pos_X; // X offset where the image should be pasted + word Pos_Y; // Y offset where the image should be pasted + word Image_width; // Width of image + word Image_height; // Height of image + byte Indicator; // Misc image information byte Nb_bits_pixel; // Nb de bits par pixel -} T_GIF_IDB; // Image Descriptor Block +} T_GIF_IDB; // Image Descriptor Block #pragma pack() +typedef struct +{ + // byte Block_identifier : 0x21 + // byte Function : 0xF9 + // byte Block_size // 4 + byte Packed_fields; // 11100000 : Reserved + // 00011100 : Disposal method + // 00000010 : User input flag + // 00000001 : Transparent flag + word Delay_time; // Time for this frame to stay displayed + byte Transparent_color; // Which color index acts as transparent + //word Bloc_terminator; // 0x00 +} T_GIF_GCE; // Graphic Control Extension + // -- Tester si un fichier est au format GIF -------------------------------- void Test_GIF(void) @@ -1587,104 +1601,106 @@ void Test_GIF(void) } +// -- Lire un fichier au format GIF ----------------------------------------- // -- Lire un fichier au format GIF ----------------------------------------- - // Définition de quelques variables globales au chargement du GIF87a - word GIF_nb_bits; // Nb de bits composants un code complet - word GIF_remainder_bits; // Nb de bits encore dispos dans GIF_last_byte - byte GIF_remainder_byte; // Nb d'octets avant le prochain bloc de Raster Data - word GIF_current_code; // Code traité (qui vient d'être lu en général) - byte GIF_last_byte; // Octet de lecture des bits - word GIF_pos_X; // Coordonnées d'affichage de l'image - word GIF_pos_Y; - word GIF_interlaced; // L'image est entrelacée - word GIF_finished_interlaced_image; // L'image entrelacée est finie de charger - word GIF_pass; // index de passe de l'image entrelacée - FILE *GIF_file; // L'handle du fichier +// Définition de quelques variables globales au chargement du GIF87a +word GIF_nb_bits; // Nb de bits composants un code complet +word GIF_remainder_bits; // Nb de bits encore dispos dans GIF_last_byte +byte GIF_remainder_byte; // Nb d'octets avant le prochain bloc de Raster Data +word GIF_current_code; // Code traité (qui vient d'être lu en général) +byte GIF_last_byte; // Octet de lecture des bits +word GIF_pos_X; // Coordonnées d'affichage de l'image +word GIF_pos_Y; +word GIF_interlaced; // L'image est entrelacée +word GIF_finished_interlaced_image; // L'image entrelacée est finie de charger +word GIF_pass; // index de passe de l'image entrelacée +FILE *GIF_file; // L'handle du fichier - // -- Lit le code à GIF_nb_bits suivant -- +// -- Lit le code à GIF_nb_bits suivant -- - word GIF_get_next_code(void) +word GIF_get_next_code(void) +{ + word nb_bits_to_process=GIF_nb_bits; + word nb_bits_processed =0; + word current_nb_bits; + + GIF_current_code=0; + + while (nb_bits_to_process) { - word nb_bits_to_process=GIF_nb_bits; - word nb_bits_processed =0; - word current_nb_bits; - - GIF_current_code=0; - - while (nb_bits_to_process) + if (GIF_remainder_bits==0) // Il ne reste plus de bits... { - if (GIF_remainder_bits==0) // Il ne reste plus de bits... - { - // Lire l'octet suivant: + // Lire l'octet suivant: - // Si on a atteint la fin du bloc de Raster Data - if (GIF_remainder_byte==0) - // Lire l'octet nous donnant la taille du bloc de Raster Data suivant - if(Read_byte(GIF_file, &GIF_remainder_byte)!=1) - File_error=2; + // Si on a atteint la fin du bloc de Raster Data + if (GIF_remainder_byte==0) + // Lire l'octet nous donnant la taille du bloc de Raster Data suivant + if(Read_byte(GIF_file, &GIF_remainder_byte)!=1) + File_error=2; - if(Read_byte(GIF_file,&GIF_last_byte)!=1) - File_error = 2; - GIF_remainder_byte--; - GIF_remainder_bits=8; - } - - current_nb_bits=(nb_bits_to_process<=GIF_remainder_bits)?nb_bits_to_process:GIF_remainder_bits; - - GIF_current_code|=(GIF_last_byte & ((1<>=current_nb_bits; - nb_bits_processed +=current_nb_bits; - nb_bits_to_process-=current_nb_bits; - GIF_remainder_bits -=current_nb_bits; + if(Read_byte(GIF_file,&GIF_last_byte)!=1) + File_error = 2; + GIF_remainder_byte--; + GIF_remainder_bits=8; } - return GIF_current_code; + current_nb_bits=(nb_bits_to_process<=GIF_remainder_bits)?nb_bits_to_process:GIF_remainder_bits; + + GIF_current_code|=(GIF_last_byte & ((1<>=current_nb_bits; + nb_bits_processed +=current_nb_bits; + nb_bits_to_process-=current_nb_bits; + GIF_remainder_bits -=current_nb_bits; } - // -- Affiche un nouveau pixel -- + return GIF_current_code; +} - void GIF_new_pixel(byte color) +// -- Affiche un nouveau pixel -- + +void GIF_new_pixel(T_GIF_IDB *idb, byte color) +{ + if (color != Main_backups->Pages->Transparent_color) // transparent color + Pixel_load_function(idb->Pos_X+GIF_pos_X, idb->Pos_Y+GIF_pos_Y,color); + + GIF_pos_X++; + + if (GIF_pos_X>=idb->Image_width) { - Pixel_load_function(GIF_pos_X,GIF_pos_Y,color); + GIF_pos_X=0; - GIF_pos_X++; - - if (GIF_pos_X>=Main_image_width) + if (!GIF_interlaced) + GIF_pos_Y++; + else { - GIF_pos_X=0; - - if (!GIF_interlaced) - GIF_pos_Y++; - else + switch (GIF_pass) { - switch (GIF_pass) - { - case 0 : GIF_pos_Y+=8; - break; - case 1 : GIF_pos_Y+=8; - break; - case 2 : GIF_pos_Y+=4; - break; - default: GIF_pos_Y+=2; - } + case 0 : GIF_pos_Y+=8; + break; + case 1 : GIF_pos_Y+=8; + break; + case 2 : GIF_pos_Y+=4; + break; + default: GIF_pos_Y+=2; + } - if (GIF_pos_Y>=Main_image_height) + if (GIF_pos_Y>=idb->Image_height) + { + switch(++GIF_pass) { - switch(++GIF_pass) - { - case 1 : GIF_pos_Y=4; - break; - case 2 : GIF_pos_Y=2; - break; - case 3 : GIF_pos_Y=1; - break; - case 4 : GIF_finished_interlaced_image=1; - } + case 1 : GIF_pos_Y=4; + break; + case 2 : GIF_pos_Y=2; + break; + case 3 : GIF_pos_Y=1; + break; + case 4 : GIF_finished_interlaced_image=1; } } } } +} void Load_GIF(void) @@ -1701,11 +1717,12 @@ void Load_GIF(void) T_GIF_LSDB LSDB; T_GIF_IDB IDB; + T_GIF_GCE GCE; word nb_colors; // Nombre de couleurs dans l'image word color_index; // index de traitement d'une couleur byte size_to_read; // Nombre de données à lire (divers) - byte block_indentifier; // Code indicateur du type de bloc en cours + byte block_identifier; // Code indicateur du type de bloc en cours word initial_nb_bits; // Nb de bits au début du traitement LZW word special_case=0; // Mémoire pour le cas spécial word old_code=0; // Code précédent @@ -1718,11 +1735,6 @@ void Load_GIF(void) /////////////////////////////////////////////////// FIN DES DECLARATIONS // - GIF_pos_X=0; - GIF_pos_Y=0; - GIF_last_byte=0; - GIF_remainder_bits=0; - GIF_remainder_byte=0; number_LID=0; Get_full_filename(filename,0); @@ -1752,16 +1764,15 @@ void Load_GIF(void) Original_screen_X=LSDB.Width; Original_screen_Y=LSDB.Height; + Init_preview(LSDB.Width,LSDB.Height,file_size,FORMAT_GIF,PIXEL_SIMPLE); + Main_image_width=LSDB.Width; + Main_image_height=LSDB.Height; + // Palette globale dispo = (LSDB.Resol and $80) // Profondeur de couleur =((LSDB.Resol and $70) shr 4)+1 // Nombre de bits/pixel = (LSDB.Resol and $07)+1 // Ordre de Classement = (LSDB.Aspect and $80) - alphabet_stack_pos=0; - GIF_last_byte =0; - GIF_remainder_bits =0; - GIF_remainder_byte =0; - nb_colors=(1 << ((LSDB.Resol & 0x07)+1)); initial_nb_bits=(LSDB.Resol & 0x07)+2; @@ -1795,10 +1806,10 @@ void Load_GIF(void) } // On lit un indicateur de block - Read_byte(GIF_file,&block_indentifier); - while (block_indentifier!=0x3B && !File_error) + Read_byte(GIF_file,&block_identifier); + while (block_identifier!=0x3B && !File_error) { - switch (block_indentifier) + switch (block_identifier) { case 0x21: // Bloc d'extension { @@ -1828,7 +1839,20 @@ void Load_GIF(void) break; case 0xF9: // Graphics Control Extension // Prévu pour la transparence + if ( Read_byte(GIF_file,&(GCE.Packed_fields)) + && Read_word_le(GIF_file,&(GCE.Delay_time)) + && Read_byte(GIF_file,&(GCE.Transparent_color))) + { + if (GCE.Packed_fields & 1) + Main_backups->Pages->Transparent_color= GCE.Transparent_color; + else + Main_backups->Pages->Transparent_color = -1; + } + else + File_error=2; + break; + default: // On saute le bloc: fseek(GIF_file,size_to_read,SEEK_CUR); @@ -1842,10 +1866,11 @@ void Load_GIF(void) case 0x2C: // Local Image Descriptor { // Si on a deja lu une image, c'est une GIF animée ou bizarroide, on sort. - if (number_LID!=0) + if (number_LID!=0 && Pixel_load_function==Pixel_load_in_current_screen) { - File_error=2; - break; + // This a second layer/frame, or more. + // Attempt to add a layer to current image + Add_layer(Main_backups, Main_current_layer+1); } number_LID++; @@ -1858,10 +1883,6 @@ void Load_GIF(void) && Read_byte(GIF_file,&(IDB.Nb_bits_pixel)) && IDB.Image_width && IDB.Image_height) { - Main_image_width=IDB.Image_width; - Main_image_height=IDB.Image_height; - - Init_preview(IDB.Image_width,IDB.Image_height,file_size,FORMAT_GIF,PIXEL_SIMPLE); // Palette locale dispo = (IDB.Indicator and $80) // Image entrelacée = (IDB.Indicator and $40) @@ -1913,6 +1934,13 @@ void Load_GIF(void) //////////////////////////////////////////// DECOMPRESSION LZW // + GIF_pos_X=0; + GIF_pos_Y=0; + alphabet_stack_pos=0; + GIF_last_byte =0; + GIF_remainder_bits =0; + GIF_remainder_byte =0; + while ( (GIF_get_next_code()!=value_eof) && (!File_error) ) { if (GIF_current_code<=alphabet_free) @@ -1934,7 +1962,7 @@ void Load_GIF(void) special_case=alphabet_stack[alphabet_stack_pos++]=GIF_current_code; do - GIF_new_pixel(alphabet_stack[--alphabet_stack_pos]); + GIF_new_pixel(&IDB, alphabet_stack[--alphabet_stack_pos]); while (alphabet_stack_pos!=0); alphabet_prefix[alphabet_free ]=old_code; @@ -1954,7 +1982,7 @@ void Load_GIF(void) alphabet_free =nb_colors+2; special_case =GIF_get_next_code(); old_code =GIF_current_code; - GIF_new_pixel(GIF_current_code); + GIF_new_pixel(&IDB, GIF_current_code); } } else @@ -1965,7 +1993,7 @@ void Load_GIF(void) if (File_error>=0) if ( /* (GIF_pos_X!=0) || */ - ( ( (!GIF_interlaced) && (GIF_pos_Y!=Main_image_height) ) || + ( ( (!GIF_interlaced) && (GIF_pos_Y!=IDB.Image_height) ) || ( (GIF_interlaced) && (!GIF_finished_interlaced_image) ) ) ) File_error=2; @@ -1977,7 +2005,7 @@ void Load_GIF(void) break; } // Lecture du code de fonction suivant: - Read_byte(GIF_file,&block_indentifier); + Read_byte(GIF_file,&block_identifier); } } // Le fichier contenait un LSDB else @@ -2058,16 +2086,16 @@ void Load_GIF(void) // -- Lire le pixel suivant -- - byte GIF_next_pixel(void) + byte GIF_next_pixel(T_GIF_IDB *idb) { byte temp; temp=Read_pixel_function(GIF_pos_X,GIF_pos_Y); - if (++GIF_pos_X>=Main_image_width) + if (++GIF_pos_X>=idb->Image_width) { GIF_pos_X=0; - if (++GIF_pos_Y>=Main_image_height) + if (++GIF_pos_Y>=idb->Image_height) GIF_stop=1; } @@ -2093,21 +2121,17 @@ void Save_GIF(void) T_GIF_IDB IDB; - byte block_indentifier; // Code indicateur du type de bloc en cours + byte block_identifier; // Code indicateur du type de bloc en cours word current_string; // Code de la chaîne en cours de traitement byte current_char; // Caractère à coder word index; // index de recherche de chaîne + byte old_current_layer=Main_current_layer; /////////////////////////////////////////////////// FIN DES DECLARATIONS // - - - GIF_pos_X=0; - GIF_pos_Y=0; - GIF_last_byte=0; - GIF_remainder_bits=0; - GIF_remainder_byte=0; - + + File_error=0; + Get_full_filename(filename,0); if ((GIF_file=fopen(filename,"wb"))) @@ -2140,12 +2164,11 @@ void Save_GIF(void) // On sauve le LSDB dans le fichier -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - LSDB.Width = SDL_Swap16(LSDB.Width); - LSDB.Height = SDL_Swap16(LSDB.Height); -#endif - - if (Write_bytes(GIF_file,&LSDB,sizeof(T_GIF_LSDB))) + if (Write_word_le(GIF_file,LSDB.Width) && + Write_word_le(GIF_file,LSDB.Height) && + Write_byte(GIF_file,LSDB.Resol) && + Write_byte(GIF_file,LSDB.Backcol) && + Write_byte(GIF_file,LSDB.Aspect) ) { // Le LSDB a été correctement écrit. @@ -2160,6 +2183,11 @@ void Save_GIF(void) // Ecriture de la transparence //Write_bytes(GIF_file,"\x21\xF9\x04\x01\x00\x00\xNN\x00",8); + // "Netscape" animation extension + // Write_bytes(GIF_file,"\x21\xFF\x0BNETSCAPE2.0\x03\xLL\xSS\xSS\x00",19); + // LL : 01 to loop + // SSSS : number of loops + // Ecriture du commentaire if (Main_comment[0]) { @@ -2167,180 +2195,208 @@ void Save_GIF(void) Write_byte(GIF_file,strlen(Main_comment)); Write_bytes(GIF_file,Main_comment,strlen(Main_comment)+1); } - - - // On va écrire un block indicateur d'IDB et l'IDB du fichier - - block_indentifier=0x2C; - IDB.Pos_X=0; - IDB.Pos_Y=0; - IDB.Image_width=Main_image_width; - IDB.Image_height=Main_image_height; - IDB.Indicator=0x07; // Image non entrelacée, pas de palette locale. - IDB.Nb_bits_pixel=8; // Image 256 couleurs; - -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - IDB.Image_width = SDL_Swap16(IDB.Image_width); - IDB.Image_height = SDL_Swap16(IDB.Image_height); -#endif - - if ( Write_bytes(GIF_file,&block_indentifier,1) && - Write_bytes(GIF_file,&IDB,sizeof(T_GIF_IDB)) ) + // Loop on all layers + for (Main_current_layer=0; + Main_current_layer < Main_backups->Pages->Nb_layers && !File_error; + Main_current_layer++) { - // Le block indicateur d'IDB et l'IDB ont étés correctements - // écrits. - - Init_write_buffer(); - - index=4096; - File_error=0; - GIF_stop=0; - - // Réintialisation de la table: - alphabet_free=258; - GIF_nb_bits =9; - alphabet_max =511; - GIF_set_code(256); - for (start=0;start<4096;start++) + // Write a Graphic Control Extension + char * GCE_block = "\x21\xF9\x04\x05\x05\x00\x00\x00"; + //if (Main_current_layer > 0) + // GCE_block[3] = '\x05'; + if (Main_current_layer == Main_backups->Pages->Nb_layers -1) { - alphabet_daughter[start]=4096; - alphabet_sister[start]=4096; + // "Infinite" delay for last frame + GCE_block[4] = 255; + GCE_block[5] = 255; } - - ////////////////////////////////////////////// COMPRESSION LZW // - - start=current_string=GIF_next_pixel(); - descend=1; - - do + if (Write_bytes(GIF_file,GCE_block,8)) { - current_char=GIF_next_pixel(); - - // On regarde si dans la table on aurait pas une chaîne - // équivalente à current_string+Caractere - - while ( (index0xFFF) + // Le block indicateur d'IDB et l'IDB ont étés correctements + // écrits. + + Init_write_buffer(); + GIF_pos_X=0; + GIF_pos_Y=0; + GIF_last_byte=0; + GIF_remainder_bits=0; + GIF_remainder_byte=0; + + index=4096; + File_error=0; + GIF_stop=0; + + // Réintialisation de la table: + alphabet_free=258; + GIF_nb_bits =9; + alphabet_max =511; + GIF_set_code(256); + for (start=0;start<4096;start++) { - // Réintialisation de la table: - GIF_set_code(256); - alphabet_free=258; - GIF_nb_bits =9; - alphabet_max =511; - for (start=0;start<4096;start++) + alphabet_daughter[start]=4096; + alphabet_sister[start]=4096; + } + + ////////////////////////////////////////////// COMPRESSION LZW // + + start=current_string=GIF_next_pixel(&IDB); + descend=1; + + do + { + current_char=GIF_next_pixel(&IDB); + + // On regarde si dans la table on aurait pas une chaîne + // équivalente à current_string+Caractere + + while ( (index0xFFF) + { + // Réintialisation de la table: + GIF_set_code(256); + alphabet_free=258; + GIF_nb_bits =9; + alphabet_max =511; + for (start=0;start<4096;start++) + { + alphabet_daughter[start]=4096; + alphabet_sister[start]=4096; + } + } + else if (alphabet_free>alphabet_max+1) + { + // On augmente le nb de bits + + GIF_nb_bits++; + alphabet_max=(1<alphabet_max+1) + while ((!GIF_stop) && (!File_error)); + + if (!File_error) { - // On augmente le nb de bits - - GIF_nb_bits++; - alphabet_max=(1<Pages->Nb_layers,chosen_width,chosen_height)) { // La nouvelle page a pu être allouée, elle est pour l'instant pleine de // 0s. Elle fait Main_image_width de large. @@ -603,10 +605,14 @@ void Resize_image(word chosen_width,word chosen_height) Main_image_is_modified=1; // On copie donc maintenant la partie C dans la nouvelle image. - Copy_part_of_image_to_another( - Screen_backup,0,0,Min(old_width,Main_image_width), - Min(old_height,Main_image_height),old_width, - Main_screen,0,0,Main_image_width); + for (i=0; iPages->Nb_layers; i++) + { + Copy_part_of_image_to_another( + Main_backups->Pages->Next->Image[i],0,0,Min(old_width,Main_image_width), + Min(old_height,Main_image_height),old_width, + Main_backups->Pages->Image[i],0,0,Main_image_width); + } + Redraw_layered_image(); } else { @@ -663,7 +669,8 @@ void Get_colors_from_brush(void) if (Confirmation_box("Modify current palette ?")) { - Backup(); + // Backup with unchanged layers, only palette is modified + Backup_layers(0); // On commence par initialiser le tableau de booléen à faux for (color=0;color<=255;color++) @@ -731,7 +738,7 @@ void Fill(short * top_reached , short * bottom_reached, current_limit_bottom =Min(Paintbrush_Y+1,Limit_bottom); *left_reached=Paintbrush_X; *right_reached=Paintbrush_X+1; - Pixel_in_current_screen(Paintbrush_X,Paintbrush_Y,2); + Pixel_in_current_layer(Paintbrush_X,Paintbrush_Y,2); while (changes_made) { @@ -750,7 +757,7 @@ void Fill(short * top_reached , short * bottom_reached, { // On cherche son début while((start_x<=Limit_right) && - (Read_pixel_from_current_screen(start_x,line)!=1)) + (Read_pixel_from_current_layer(start_x,line)!=1)) start_x++; if (start_x<=Limit_right) @@ -758,7 +765,7 @@ void Fill(short * top_reached , short * bottom_reached, // Un segment de couleur 1 existe et commence à la position start_x. // On va donc en chercher la fin. for (end_x=start_x+1;(end_x<=Limit_right) && - (Read_pixel_from_current_screen(end_x,line)==1);end_x++); + (Read_pixel_from_current_layer(end_x,line)==1);end_x++); // On sait qu'il existe un segment de couleur 1 qui commence en // start_x et qui se termine en end_x-1. @@ -769,16 +776,16 @@ void Fill(short * top_reached , short * bottom_reached, can_propagate=( // Test de la présence d'un point à gauche du segment ((start_x>Limit_left) && - (Read_pixel_from_current_screen(start_x-1,line)==2)) || + (Read_pixel_from_current_layer(start_x-1,line)==2)) || // Test de la présence d'un point à droite du segment ((end_x-1Limit_top)) for (x_pos=start_x;x_posLimit_left) && - (Read_pixel_from_current_screen(start_x-1,line)==2)) || + (Read_pixel_from_current_layer(start_x-1,line)==2)) || // Test de la présence d'un point à droite du segment ((end_x-1Pages->Next->Image[Main_current_layer]); +} void Fill_general(byte fill_color) // @@ -935,11 +946,11 @@ void Fill_general(byte fill_color) // On va maintenant "épurer" la zone visible de l'image: memset(replace_table,0,256); - replace_table[Read_pixel_from_current_screen(Paintbrush_X,Paintbrush_Y)]=1; + replace_table[Read_pixel_from_backup_layer(Paintbrush_X,Paintbrush_Y)]=1; Replace_colors_within_limits(replace_table); // On fait maintenant un remplissage classique de la couleur 1 avec la 2 - Fill(&top_reached ,&bottom_reached, + Fill(&top_reached ,&bottom_reached, &left_reached,&right_reached); // On s'apprête à faire des opérations qui nécessitent un affichage. Il @@ -950,39 +961,39 @@ void Fill_general(byte fill_color) // Il va maintenant falloir qu'on "turn" ce gros caca "into" un truc qui // ressemble un peu plus à ce à quoi l'utilisateur peut s'attendre. if (top_reached>Limit_top) - Copy_part_of_image_to_another(Screen_backup, // source + Copy_part_of_image_to_another(Main_backups->Pages->Next->Image[Main_current_layer], // source Limit_left,Limit_top, // Pos X et Y dans source (Limit_right-Limit_left)+1, // width copie top_reached-Limit_top,// height copie Main_image_width, // width de la source - Main_screen, // Destination + Main_backups->Pages->Image[Main_current_layer], // Destination Limit_left,Limit_top, // Pos X et Y destination Main_image_width); // width destination if (bottom_reachedPages->Next->Image[Main_current_layer], Limit_left,bottom_reached+1, (Limit_right-Limit_left)+1, Limit_bottom-bottom_reached, - Main_image_width,Main_screen, + Main_image_width,Main_backups->Pages->Image[Main_current_layer], Limit_left,bottom_reached+1,Main_image_width); if (left_reached>Limit_left) - Copy_part_of_image_to_another(Screen_backup, + Copy_part_of_image_to_another(Main_backups->Pages->Next->Image[Main_current_layer], Limit_left,top_reached, left_reached-Limit_left, (bottom_reached-top_reached)+1, - Main_image_width,Main_screen, + Main_image_width,Main_backups->Pages->Image[Main_current_layer], Limit_left,top_reached,Main_image_width); if (right_reachedPages->Next->Image[Main_current_layer], right_reached+1,top_reached, Limit_right-right_reached, (bottom_reached-top_reached)+1, - Main_image_width,Main_screen, + Main_image_width,Main_backups->Pages->Image[Main_current_layer], right_reached+1,top_reached,Main_image_width); for (y_pos=top_reached;y_pos<=bottom_reached;y_pos++) for (x_pos=left_reached;x_pos<=right_reached;x_pos++) - if (Read_pixel_from_current_screen(x_pos,y_pos)==2) + if (Read_pixel_from_current_layer(x_pos,y_pos)==2) { // Si le pixel en cours de traitement a été touché par le Fill() // on se doit d'afficher le pixel modifié par la couleur de @@ -991,14 +1002,14 @@ void Fill_general(byte fill_color) // Ceci se fait en commençant par restaurer la couleur qu'il y avait // précédemment (c'est important pour que les effets ne s'emmèlent // pas le pinceaux) - Pixel_in_current_screen(x_pos,y_pos,Read_pixel_from_backup_screen(x_pos,y_pos)); + Pixel_in_current_screen(x_pos,y_pos,Read_pixel_from_backup_layer(x_pos,y_pos),0); // Enfin, on peut afficher le pixel, en le soumettant aux effets en // cours: Display_pixel(x_pos,y_pos,fill_color); } else - Pixel_in_current_screen(x_pos,y_pos,Read_pixel_from_backup_screen(x_pos,y_pos)); + Pixel_in_current_screen(x_pos,y_pos,Read_pixel_from_backup_layer(x_pos,y_pos),0); FX_feedback_screen=old_fx_feedback_screen; @@ -1007,7 +1018,6 @@ void Fill_general(byte fill_color) // puisque les seuls points qui ont changé dans l'image ont été raffichés // par l'utilisation de "Display_pixel()", et que les autres... eh bein // on n'y a jamais touché à l'écran les autres: ils sont donc corrects. - if(Main_magnifier_mode) { short w,h; @@ -2551,7 +2561,7 @@ void Replace(byte New_color) if ((Paintbrush_XPages->Transparent_color) // transparent color + return color; + + depth = *(Visible_image_depth_buffer.Image+x+y*Main_image_width); + return *(Main_backups->Pages->Image[depth] + x+y*Main_image_width); + #else + return *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer]); + #endif +} + +void Pixel_in_current_screen (word x,word y,byte color,int with_preview) +{ + #ifndef NOLAYERS + byte depth = *(Visible_image_depth_buffer.Image+x+y*Main_image_width); + *(Main_backups->Pages->Image[Main_current_layer] + x+y*Main_image_width)=color; + if ( depth <= Main_current_layer) + { + if (color == Main_backups->Pages->Transparent_color) // transparent color + // fetch pixel color from the topmost visible layer + color=*(Main_backups->Pages->Image[depth] + x+y*Main_image_width); + + *(x+y*Main_image_width+Main_screen)=color; + + if (with_preview) + Pixel_preview(x,y,color); + } + #else + *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer])=color; + if (with_preview) + Pixel_preview(x,y,color); + #endif +} + +void Pixel_in_current_layer(word x,word y, byte color) +{ + *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer])=color; +} + +byte Read_pixel_from_current_layer(word x,word y) +{ + return *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer]); +} diff --git a/graph.h b/graph.h index ec124732..34a6f634 100644 --- a/graph.h +++ b/graph.h @@ -113,3 +113,8 @@ extern Func_pixel Pixel_figure; void Update_part_of_screen(short x, short y, short width, short height); void Redraw_grid(short x, short y, unsigned short w, unsigned short h); + +void Pixel_in_current_screen (word x,word y,byte color,int with_preview); +void Pixel_in_current_layer(word x,word y, byte color); +byte Read_pixel_from_current_screen (word x,word y); +byte Read_pixel_from_current_layer(word x,word y); diff --git a/help.c b/help.c index 9c6415b6..f498ab76 100644 --- a/help.c +++ b/help.c @@ -47,6 +47,7 @@ #include "input.h" #include "hotkeys.h" #include "errors.h" +#include "pages.h" extern char Program_version[]; // generated in pversion.c extern char SVN_revision[]; // generated in pversion.c @@ -642,6 +643,16 @@ void Button_Stats(void) sprintf(buffer,"%u bytes",(unsigned int)freeRam); Print_in_window(114,51,buffer,STATS_DATA_COLOR,MC_Black); + // Used memory + Print_in_window(10,59,"Used memory pages: ",STATS_TITLE_COLOR,MC_Black); + if(Stats_pages_memory > (100LL*1024*1024*1024)) + sprintf(buffer,"%u (%u Gb)",Stats_pages_number, (unsigned int)(Stats_pages_memory/(1024*1024*1024))); + else if(Stats_pages_memory > (100*1024*1024)) + sprintf(buffer,"%u (%u Mb)",Stats_pages_number, (unsigned int)(Stats_pages_memory/(1024*1024))); + else + sprintf(buffer,"%u (%u Kb)",Stats_pages_number, (unsigned int)(Stats_pages_memory/1024)); + Print_in_window(162,59,buffer,STATS_DATA_COLOR,MC_Black); + // Affichage de l'espace disque libre sprintf(buffer,"Free space on %c:",Main_current_directory[0]); Print_in_window(10,67,buffer,STATS_TITLE_COLOR,MC_Black); diff --git a/init.c b/init.c index 5538d5c0..9a33e38a 100644 --- a/init.c +++ b/init.c @@ -1593,12 +1593,12 @@ void Init_operations(void) Scroll_12_0,HIDE_CURSOR,FAST_MOUSE); Init_operation(OPERATION_SCROLL,2,0, Scroll_12_0,HIDE_CURSOR,FAST_MOUSE); - Init_operation(OPERATION_SCROLL,1,4, - Scroll_12_4,0,FAST_MOUSE); - Init_operation(OPERATION_SCROLL,2,4, - Scroll_12_4,0,FAST_MOUSE); - Init_operation(OPERATION_SCROLL,0,4, - Scroll_0_4,HIDE_CURSOR,FAST_MOUSE); + Init_operation(OPERATION_SCROLL,1,5, + Scroll_12_5,0,FAST_MOUSE); + Init_operation(OPERATION_SCROLL,2,5, + Scroll_12_5,0,FAST_MOUSE); + Init_operation(OPERATION_SCROLL,0,5, + Scroll_0_5,HIDE_CURSOR,FAST_MOUSE); Init_operation(OPERATION_GRAD_CIRCLE,1,0,Grad_circle_12_0,HIDE_CURSOR,FAST_MOUSE); Init_operation(OPERATION_GRAD_CIRCLE,2,0,Grad_circle_12_0,HIDE_CURSOR,FAST_MOUSE); diff --git a/loadsave.c b/loadsave.c index 99b450a0..e62d9d02 100644 --- a/loadsave.c +++ b/loadsave.c @@ -39,6 +39,7 @@ #include "io.h" #include "loadsave.h" #include "misc.h" +#include "graph.h" #include "op_c.h" #include "pages.h" #include "palette.h" @@ -134,29 +135,29 @@ void Load_SDL_Image(void); void Init_preview(short width,short height,long size,int format,enum PIXEL_RATIO ratio); -// ENUM Name TestFunc LoadFunc SaveFunc Backup Comment Ext Exts +// ENUM Name TestFunc LoadFunc SaveFunc Backup Comment Layers Ext Exts T_Format File_formats[NB_KNOWN_FORMATS] = { - {FORMAT_ALL_IMAGES,"(all)",NULL, NULL, NULL, 0, 0, "", "gif;png;bmp;pcx;pkm;lbm;iff;img;sci;scq;scf;scn;sco;pi1;pc1;cel;neo;kcf;pal;c64;koa;tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico"}, - {FORMAT_ALL_FILES,"(*.*)",NULL, NULL, NULL, 0, 0, "", "*"}, - {FORMAT_GIF, " gif", Test_GIF, Load_GIF, Save_GIF, 1, 1, "gif", "gif"}, + {FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "", "gif;png;bmp;pcx;pkm;lbm;iff;img;sci;scq;scf;scn;sco;pi1;pc1;cel;neo;kcf;pal;c64;koa;tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico"}, + {FORMAT_ALL_FILES, "(*.*)", NULL, NULL, NULL, 0, 0, 0, "", "*"}, + {FORMAT_GIF, " gif", Test_GIF, Load_GIF, Save_GIF, 1, 1, 1, "gif", "gif"}, #ifndef __no_pnglib__ - {FORMAT_PNG, " png", Test_PNG, Load_PNG, Save_PNG, 1, 1, "png", "png"}, + {FORMAT_PNG, " png", Test_PNG, Load_PNG, Save_PNG, 1, 1, 0, "png", "png"}, #endif - {FORMAT_BMP, " bmp", Test_BMP, Load_BMP, Save_BMP, 1, 0, "bmp", "bmp"}, - {FORMAT_PCX, " pcx", Test_PCX, Load_PCX, Save_PCX, 1, 0, "pcx", "pcx"}, - {FORMAT_PKM, " pkm", Test_PKM, Load_PKM, Save_PKM, 0, 1, "pkm", "pkm"}, // Not a backup since it does not save the full palette - {FORMAT_LBM, " lbm", Test_LBM, Load_LBM, Save_LBM, 1, 0, "lbm", "lbm;iff"}, - {FORMAT_IMG, " img", Test_IMG, Load_IMG, Save_IMG, 1, 0, "img", "img"}, - {FORMAT_SCx, " sc?", Test_SCx, Load_SCx, Save_SCx, 1, 0, "sc?", "sci;scq;scf;scn;sco"}, - {FORMAT_PI1, " pi1", Test_PI1, Load_PI1, Save_PI1, 0, 0, "pi1", "pi1"}, - {FORMAT_PC1, " pc1", Test_PC1, Load_PC1, Save_PC1, 0, 0, "pc1", "pc1"}, - {FORMAT_CEL, " cel", Test_CEL, Load_CEL, Save_CEL, 1, 0, "cel", "cel"}, - {FORMAT_NEO, " neo", Test_NEO, Load_NEO, Save_NEO, 0, 0, "neo", "neo"}, - {FORMAT_KCF, " kcf", Test_KCF, Load_KCF, Save_KCF, 0, 0, "kcf", "kcf"}, - {FORMAT_PAL, " pal", Test_PAL, Load_PAL, Save_PAL, 0, 0, "pal", "pal"}, - {FORMAT_C64, " c64", Test_C64, Load_C64, Save_C64, 0, 1, "c64", "c64;koa"}, - {FORMAT_SCR, " cpc", NULL, NULL, Save_SCR, 0, 0, "cpc", "cpc;scr"}, - {FORMAT_MISC,"misc.", NULL, NULL, NULL, 1, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico"}, + {FORMAT_BMP, " bmp", Test_BMP, Load_BMP, Save_BMP, 1, 0, 0, "bmp", "bmp"}, + {FORMAT_PCX, " pcx", Test_PCX, Load_PCX, Save_PCX, 1, 0, 0, "pcx", "pcx"}, + {FORMAT_PKM, " pkm", Test_PKM, Load_PKM, Save_PKM, 0, 1, 0, "pkm", "pkm"}, // Not a backup since it does not save the full palette + {FORMAT_LBM, " lbm", Test_LBM, Load_LBM, Save_LBM, 1, 0, 0, "lbm", "lbm;iff"}, + {FORMAT_IMG, " img", Test_IMG, Load_IMG, Save_IMG, 1, 0, 0, "img", "img"}, + {FORMAT_SCx, " sc?", Test_SCx, Load_SCx, Save_SCx, 1, 0, 0, "sc?", "sci;scq;scf;scn;sco"}, + {FORMAT_PI1, " pi1", Test_PI1, Load_PI1, Save_PI1, 0, 0, 0, "pi1", "pi1"}, + {FORMAT_PC1, " pc1", Test_PC1, Load_PC1, Save_PC1, 0, 0, 0, "pc1", "pc1"}, + {FORMAT_CEL, " cel", Test_CEL, Load_CEL, Save_CEL, 1, 0, 0, "cel", "cel"}, + {FORMAT_NEO, " neo", Test_NEO, Load_NEO, Save_NEO, 0, 0, 0, "neo", "neo"}, + {FORMAT_KCF, " kcf", Test_KCF, Load_KCF, Save_KCF, 0, 0, 0, "kcf", "kcf"}, + {FORMAT_PAL, " pal", Test_PAL, Load_PAL, Save_PAL, 0, 0, 0, "pal", "pal"}, + {FORMAT_C64, " c64", Test_C64, Load_C64, Save_C64, 0, 1, 0, "c64", "c64;koa"}, + {FORMAT_SCR, " cpc", NULL, NULL, Save_SCR, 0, 0, 0, "cpc", "cpc;scr"}, + {FORMAT_MISC,"misc.",NULL, NULL, NULL, 1, 0, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico"}, }; // Cette variable est alimentée après chargement réussi d'une image. @@ -168,7 +169,7 @@ void Pixel_load_in_current_screen(word x_pos,word y_pos,byte color) { //if ((x_pos>=0) && (y_pos>=0)) //Toujours vrai ? if ((x_posPages->Nb_layers > 1) + { + if (! Confirmation_box("This format will save a flattened copy.")) + { + // File_error is already set to 1. + return; + } + Read_pixel_function=Read_pixel_from_current_screen; + } + else + { + Read_pixel_function=Read_pixel_from_current_layer; + } + } + else + { + Read_pixel_function=Read_pixel_from_brush; + } + format = Get_fileformat(Main_fileformat); if (format->Save) diff --git a/loadsave.h b/loadsave.h index 126e5f33..5238321f 100644 --- a/loadsave.h +++ b/loadsave.h @@ -50,6 +50,7 @@ typedef struct { Func_action Save; ///< Function which saves an image of this format byte Backup_done; ///< Boolean, true if this format saves all the image, and considers it backed up. Set false for formats which only save the palette. byte Comment; ///< This file format allows a text comment + byte Supports_layers; ///< Boolean, true if this format preserves layers on saving char *Default_extension; ///< Default file extension char *Extensions; ///< List of semicolon-separated file extensions } T_Format; diff --git a/main.c b/main.c index 10cf427a..9912847f 100644 --- a/main.c +++ b/main.c @@ -376,6 +376,11 @@ int Init_program(int argc,char * argv[]) Main_fileselector_position=0; // Au début, le fileselect est en haut de la liste des fichiers Main_fileselector_offset=0; // Au début, le fileselect est en haut de la liste des fichiers Main_format=FORMAT_ALL_IMAGES; + Main_current_layer=0; + Main_layers_visible=0xFFFFFFFF; + Spare_current_layer=0; + Spare_layers_visible=0xFFFFFFFF; + Spare_fileselector_position=0; Spare_fileselector_offset=0; Spare_format=FORMAT_ALL_IMAGES; @@ -635,8 +640,21 @@ int Init_program(int argc,char * argv[]) Main_image_height=Screen_height/Pixel_height; Spare_image_width=Screen_width/Pixel_width; Spare_image_height=Screen_height/Pixel_height; + + #ifndef NOLAYERS + Visible_image[0].Width = 0; + Visible_image[0].Height = 0; + Visible_image[0].Image = NULL; + Visible_image[1].Width = 0; + Visible_image[1].Height = 0; + Visible_image[1].Image = NULL; + Visible_image_depth_buffer.Width = 0; + Visible_image_depth_buffer.Height = 0; + Visible_image_depth_buffer.Image = NULL; + #endif + // Allocation de mémoire pour les différents écrans virtuels (et brosse) - if (Init_all_backup_lists(Config.Max_undo_pages+1,Screen_width,Screen_height)==0) + if (Init_all_backup_lists(Screen_width,Screen_height)==0) Error(ERROR_MEMORY); // On remet le nom par défaut pour la page de brouillon car il été modifié // par le passage d'un fichier en paramètre lors du traitement précédent. diff --git a/misc.c b/misc.c index 899ed8fd..440ee099 100644 --- a/misc.c +++ b/misc.c @@ -36,6 +36,7 @@ #include "windows.h" #include "palette.h" #include "input.h" +#include "graph.h" ///Count used palette indexes in the whole picture ///Return the total number of different colors @@ -177,16 +178,16 @@ void Hide_current_image_with_stencil(byte color, byte * stencil) int nb_pixels=0; //ECX //al=color //edi=Screen_pixels - byte* Pixel_Courant=Screen_pixels; //dl + byte* pixel=Main_backups->Pages->Image[Main_current_layer]; int i; nb_pixels=Main_image_height*Main_image_width; for(i=0;iPages->Image[Main_current_layer], color , Main_image_width * Main_image_height ); @@ -218,26 +219,16 @@ byte Read_pixel_from_brush (word x, word y) return *(Brush + y * Brush_width + x); } - -byte Read_pixel_from_current_screen (word x,word y) +void Replace_a_color(byte old_color, byte new_color) { - return *(Main_screen+y*Main_image_width+x); -} - -void Pixel_in_current_screen (word x,word y,byte color) -{ - byte* dest=(x+y*Main_image_width+Main_screen); - *dest=color; -} - -void Replace_a_color(byte old_color, byte New_color) -{ - byte* edi; - - // pour chaque pixel : - for(edi = Main_screen;edi < Main_screen + Main_image_height * Main_image_width;edi++) - if (*edi == old_color) - *edi = New_color; + word x; + word y; + + // Update all pixels + for (y=0; yPages->Image[Main_current_layer]; //Adr départ image (ESI) + byte* dest=Brush; //Adr dest brosse (EDI) + int dx; for (dx=Brush_height;dx!=0;dx--) //Pour chaque ligne @@ -383,21 +374,18 @@ byte Effect_sieve(word x,word y) void Replace_colors_within_limits(byte * replace_table) { - int line; - int counter; - byte* Adresse; - - byte old; + int y; + int x; + byte* pixel; // Pour chaque ligne : - for(line = Limit_top;line <= Limit_bottom; line++) + for(y = Limit_top;y <= Limit_bottom; y++) { // Pour chaque pixel sur la ligne : - for (counter = Limit_left;counter <= Limit_right;counter ++) + for (x = Limit_left;x <= Limit_right;x ++) { - Adresse = Main_screen+line*Main_image_width+counter; - old=*Adresse; - *Adresse = replace_table[old]; + pixel = Main_backups->Pages->Image[Main_current_layer]+y*Main_image_width+x; + *pixel = replace_table[*pixel]; } } } @@ -663,33 +651,33 @@ void Slider_timer(byte speed) } while (Mouse_K == original_mouse_k && SDL_GetTicks()0;dx--) + byte* src = main_src; //source de la copie + byte* dest = main_dest + y_offset * Main_image_width + x_offset; + const word length = Main_image_width - x_offset; // Nombre de pixels à copier à droite + word y; + for(y = Main_image_height - y_offset;y>0;y--) { // Pour chaque ligne - memcpy(edi,esi,ax); - memcpy(edi - x_offset,esi+ax,x_offset); + memcpy(dest,src,length); + memcpy(dest - x_offset,src+length,x_offset); // On passe à la ligne suivante - edi += Main_image_width; - esi += Main_image_width; + dest += Main_image_width; + src += Main_image_width; } // On vient de faire le traitement pour otutes les lignes au-dessous de y_offset // Maintenant on traite celles au dessus - edi = x_offset + Main_screen; - for(dx = y_offset;dx>0;dx--) + dest = x_offset + main_dest; + for(y = y_offset;y>0;y--) { - memcpy(edi,esi,ax); - memcpy(edi - x_offset,esi+ax,x_offset); + memcpy(dest,src,length); + memcpy(dest - x_offset,src+length,x_offset); - edi += Main_image_width; - esi += Main_image_width; + dest += Main_image_width; + src += Main_image_width; } Update_rect(0,0,0,0); diff --git a/misc.h b/misc.h index 65ada702..6465b9e1 100644 --- a/misc.h +++ b/misc.h @@ -25,7 +25,7 @@ void Copy_image_to_brush(short start_x,short start_y,short Brush_width,short Brush_height,word image_width); void Remap_general_lowlevel(byte * conversion_table,byte * buffer,short width,short height,short buffer_width); -void Scroll_picture(short x_offset,short y_offset); +void Scroll_picture(byte * main_src, byte * main_dest, short x_offset,short y_offset); void Wait_end_of_click(void); void Set_color(byte color, byte red, byte green, byte blue); void Set_palette(T_Palette palette); @@ -38,9 +38,7 @@ dword Round_div(dword numerator,dword divisor); word Count_used_colors(dword * usage); word Count_used_colors_area(dword* usage, word start_x, word start_y, word width, word height); word Count_used_colors_screen_area(dword* usage, word start_x, word start_y, word width, word height); -void Pixel_in_current_screen (word x,word y,byte color); void Pixel_in_brush (word x,word y,byte color); -byte Read_pixel_from_current_screen (word x,word y); byte Read_pixel_from_spare_screen(word x,word y); byte Read_pixel_from_backup_screen (word x,word y); byte Read_pixel_from_feedback_screen (word x,word y); diff --git a/operatio.c b/operatio.c index f504e54b..728c4e08 100644 --- a/operatio.c +++ b/operatio.c @@ -3007,7 +3007,8 @@ void Brush_0_5(void) 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(); } @@ -3911,11 +3912,16 @@ void Scroll_12_0(void) // { Init_start_operation(); - Backup(); Operation_push(Paintbrush_X); Operation_push(Paintbrush_Y); Operation_push(Paintbrush_X); Operation_push(Paintbrush_Y); + Operation_push(Mouse_K); // LEFT_SIDE or RIGHT_SIDE + if (Mouse_K == LEFT_SIDE) + Backup(); + else + Backup_layers(Main_layers_visible); + Cursor_hidden_before_scroll=Cursor_hidden; Cursor_hidden=1; if ((Config.Coords_rel) && (Menu_is_visible)) @@ -3923,11 +3929,11 @@ void Scroll_12_0(void) } -void Scroll_12_4(void) +void Scroll_12_5(void) // // Opération : OPERATION_SCROLL // Click Souris: 1 ou 2 -// Taille_Pile : 4 +// Taille_Pile : 5 // // Souris effacée: Non // @@ -3938,8 +3944,10 @@ void Scroll_12_4(void) short y_pos; short x_offset; short y_offset; + short side; //char str[5]; + Operation_pop(&side); Operation_pop(&y_pos); Operation_pop(&x_pos); Operation_pop(¢er_y); @@ -3961,7 +3969,18 @@ void Scroll_12_4(void) Display_coords_rel_or_abs(center_x,center_y); - Scroll_picture(x_offset,y_offset); + if (side == RIGHT_SIDE) + { + // All layers at once + Scroll_picture(Screen_backup, Main_screen, x_offset,y_offset); + } + else + { + // One layer at once + Scroll_picture(Main_backups->Pages->Next->Image[Main_current_layer], Main_backups->Pages->Image[Main_current_layer], x_offset, y_offset); + //Redraw_layered_image(); + Redraw_current_layer(); + } Display_all_screen(); } @@ -3970,19 +3989,64 @@ void Scroll_12_4(void) Operation_push(center_y); Operation_push(Paintbrush_X); Operation_push(Paintbrush_Y); + Operation_push(side); } -void Scroll_0_4(void) +void Scroll_0_5(void) // // Opération : OPERATION_SCROLL // Click Souris: 0 -// Taille_Pile : 4 +// Taille_Pile : 5 // // Souris effacée: Oui // { - Operation_stack_size-=4; + // All layers at once + short center_x; + short center_y; + short x_pos; + short y_pos; + short x_offset; + short y_offset; + short side; + int i; + + + Operation_pop(&side); + Operation_pop(&y_pos); + Operation_pop(&x_pos); + Operation_pop(¢er_y); + Operation_pop(¢er_x); + + if (side == RIGHT_SIDE) + { + // All layers at once + if (x_pos>=center_x) + x_offset=(x_pos-center_x)%Main_image_width; + else + x_offset=Main_image_width-((center_x-x_pos)%Main_image_width); + + if (y_pos>=center_y) + y_offset=(y_pos-center_y)%Main_image_height; + else + y_offset=Main_image_height-((center_y-y_pos)%Main_image_height); + + + // Do the actual scroll operation on all layers. + for (i=0; iPages->Nb_layers; i++) + Scroll_picture(Main_backups->Pages->Next->Image[i], Main_backups->Pages->Image[i], x_offset, y_offset); + // Update the depth buffer too ... + // It would be faster to scroll it, but we don't have method + // for in-place scrolling. + Update_depth_buffer(); + } + else + { + // One layer : everything was done while dragging the mouse + } + Cursor_hidden=Cursor_hidden_before_scroll; + End_of_modification(); if ((Config.Coords_rel) && (Menu_is_visible)) { diff --git a/operatio.h b/operatio.h index 928ed7bb..97cdde15 100644 --- a/operatio.h +++ b/operatio.h @@ -183,8 +183,8 @@ void Filled_contour_0_8(void); //////////////////////////////////////////////////////////// OPERATION_SCROLL void Scroll_12_0(void); -void Scroll_12_4(void); -void Scroll_0_4(void); +void Scroll_12_5(void); +void Scroll_0_5(void); //////////////////////////////////////////////////// OPERATION_GRAD_CIRCLE diff --git a/pages.c b/pages.c index d532d682..00ad3a4f 100644 --- a/pages.c +++ b/pages.c @@ -33,17 +33,38 @@ #include "misc.h" #include "windows.h" +// -- Layers data + +/// Array of two images, that contains the "flattened" version of the visible layers. +#ifndef NOLAYERS +T_Image Visible_image[2]; +T_Image Visible_image_depth_buffer; +#endif + + /// /// GESTION DES PAGES /// -void Init_page(T_Page * page) -{ - // Important: appeler cette fonction sur toute nouvelle structure T_Page! +/// Bitfield which records which layers are backed up in Page 0. +static dword Last_backed_up_layers=0; +/// Total number of unique bitmaps (layers, animation frames, backups) +long Stats_pages_number=0; +/// Total memory used by bitmaps (layers, animation frames, backups) +long long Stats_pages_memory=0; + +/// Allocate and initialize a new page. +T_Page * New_page(byte nb_layers) +{ + T_Page * page; + + page = (T_Page *)malloc(sizeof(T_Page)+nb_layers*sizeof(byte *)); if (page!=NULL) { - page->Image=NULL; + int i; + for (i=0; iImage[i]=NULL; page->Width=0; page->Height=0; memset(page->Palette,0,sizeof(T_Palette)); @@ -51,21 +72,81 @@ void Init_page(T_Page * page) page->File_directory[0]='\0'; page->Filename[0]='\0'; page->File_format=DEFAULT_FILEFORMAT; + page->Nb_layers=nb_layers; + page->Next = page->Prev = NULL; } + return page; } +// ============================================================== +// Layers allocation functions. +// +// Layers are made of a "number of users" (short), followed by +// the actual pixel data (a large number of bytes). +// Every time a layer is 'duplicated' as a reference, the number +// of users is incremented. +// Every time a layer is freed, the number of users is decreased, +// and only when it reaches zero the pixel data is freed. +// ============================================================== + +/// Allocate a new layer +byte * New_layer(long pixel_size) +{ + short * ptr = malloc(sizeof(short)+pixel_size); + if (ptr==NULL) + return NULL; + + // Stats + Stats_pages_number++; + Stats_pages_memory+=pixel_size; + + *ptr = 1; + return (byte *)(ptr+1); +} + +/// Free a layer +void Free_layer(T_Page * page, byte layer) +{ + short * ptr; + if (page->Image[layer]==NULL) + return; + + ptr = (short *)(page->Image[layer]); + if (-- (*(ptr-1))) // Users-- + return; + else + free(ptr-1); + + // Stats + Stats_pages_number--; + Stats_pages_memory-=page->Width * page->Height; +} + +/// Duplicate a layer (new reference) +byte * Dup_layer(byte * layer) +{ + short * ptr = (short *)(layer); + + if (layer==NULL) + return NULL; + + (*(ptr-1)) ++; // Users ++ + return layer; +} + +// ============================================================== + void Download_infos_page_main(T_Page * page) // Affiche la page à l'écran { //int factor_index; int size_is_modified; - + if (page!=NULL) { size_is_modified=(Main_image_width!=page->Width) || (Main_image_height!=page->Height); - Main_screen=page->Image; Main_image_width=page->Width; Main_image_height=page->Height; memcpy(Main_palette,page->Palette,sizeof(T_Palette)); @@ -83,7 +164,130 @@ void Download_infos_page_main(T_Page * page) Compute_limits(); Compute_paintbrush_coordinates(); } + } + //Update_buffers( page->Width, page->Height); + //memcpy(Main_screen, page->Image[Main_current_layer], page->Width*page->Height); + +} + +void Redraw_layered_image(void) +{ + #ifndef NOLAYERS + // Re-construct the image with the visible layers + int layer; + // First layer + for (layer=0; layerPages->Nb_layers; layer++) + { + if ((1<Pages->Image[layer], + Main_image_width*Main_image_height); + + // Initialize the depth buffer + memset(Visible_image_depth_buffer.Image, + layer, + Main_image_width*Main_image_height); + + // skip all other layers + layer++; + break; + } + } + // subsequent layer(s) + for (; layerPages->Nb_layers; layer++) + { + if ((1<Pages->Image[layer]+i); + if (color != Main_backups->Pages->Transparent_color) // transparent color + { + *(Visible_image[0].Image+i) = color; + if (layer != Main_current_layer) + *(Visible_image_depth_buffer.Image+i) = layer; + } + } + } + } + #else + Update_screen_targets(); + #endif + Download_infos_backup(Main_backups); +} + +void Update_depth_buffer(void) +{ + #ifndef NOLAYERS + // Re-construct the depth buffer with the visible layers. + // This function doesn't touch the visible buffer, it assumes + // that it was already up-to-date. (Ex. user only changed active layer) + + int layer; + // First layer + for (layer=0; layerPages->Nb_layers; layer++) + { + if ((1<Pages->Nb_layers; layer++) + { + // skip the current layer, whenever we reach it + if (layer == Main_current_layer) + continue; + + if ((1<Pages->Image[layer]+i); + if (color != Main_backups->Pages->Transparent_color) // transparent color + { + *(Visible_image_depth_buffer.Image+i) = layer; + } + } + } + } + #endif + Download_infos_backup(Main_backups); +} + +void Redraw_current_layer(void) +{ +#ifndef NOLAYERS + int i; + for (i=0; iPages->Image[Main_current_layer]+i); + if (color != Main_backups->Pages->Transparent_color) // transparent color + { + *(Visible_image[0].Image+i) = color; + } + else + { + *(Visible_image[0].Image+i) = *(Main_backups->Pages->Image[depth]+i); + } + } + } +#endif } void Upload_infos_page_main(T_Page * page) @@ -91,7 +295,7 @@ void Upload_infos_page_main(T_Page * page) { if (page!=NULL) { - page->Image=Main_screen; + //page->Image[Main_current_layer]=Main_screen; page->Width=Main_image_width; page->Height=Main_image_height; memcpy(page->Palette,Main_palette,sizeof(T_Palette)); @@ -106,7 +310,7 @@ void Download_infos_page_spare(T_Page * page) { if (page!=NULL) { - Spare_screen=page->Image; + //Spare_screen=page->Image[Spare_current_layer]; Spare_image_width=page->Width; Spare_image_height=page->Height; memcpy(Spare_palette,page->Palette,sizeof(T_Palette)); @@ -121,7 +325,7 @@ void Upload_infos_page_spare(T_Page * page) { if (page!=NULL) { - page->Image=Spare_screen; + //page->Image[Spare_current_layer]=Spare_screen; page->Width=Spare_image_width; page->Height=Spare_image_height; memcpy(page->Palette,Spare_palette,sizeof(T_Palette)); @@ -134,21 +338,23 @@ void Upload_infos_page_spare(T_Page * page) void Download_infos_backup(T_List_of_pages * list) { - Screen_backup=list->Pages[1].Image; + //list->Pages->Next->Image[Main_current_layer]; if (Config.FX_Feedback) - FX_feedback_screen=list->Pages[0].Image; + FX_feedback_screen=list->Pages->Image[Main_current_layer]; else - FX_feedback_screen=list->Pages[1].Image; + FX_feedback_screen=list->Pages->Next->Image[Main_current_layer]; } -void Free_a_page(T_Page * page) +void Clear_page(T_Page * page) { // On peut appeler cette fonction sur une page non allouée. - - if (page->Image!=NULL) - free(page->Image); - page->Image=NULL; + int i; + for (i=0; iNb_layers; i++) + { + Free_layer(page, i); + page->Image[i]=NULL; + } page->Width=0; page->Height=0; // On ne se préoccupe pas de ce que deviens le reste des infos de l'image. @@ -159,15 +365,6 @@ void Copy_S_page(T_Page * dest,T_Page * source) *dest=*source; } -int Size_of_a_page(T_Page * page) -{ - return sizeof(T_Page)+(page->Width*page->Height)+8; - // 8 = 4 + 4 - // (Toute zone allouée en mémoire est précédée d'un mot double indiquant sa - // taille, or la taille d'un mot double est de 4 octets, et on utilise deux - // allocations de mémoires: une pour la T_Page et une pour l'image) -} - /// /// GESTION DES LISTES DE PAGES @@ -179,62 +376,30 @@ void Init_list_of_pages(T_List_of_pages * list) // T_List_of_pages! list->List_size=0; - list->Nb_pages_allocated=0; list->Pages=NULL; } -int Allocate_list_of_pages(T_List_of_pages * list,int size) +int Allocate_list_of_pages(T_List_of_pages * list) { // Important: la T_List_of_pages ne doit pas déjà désigner une liste de // pages allouée auquel cas celle-ci serait perdue. - int index; + T_Page * page; - /* Debug : if (list->Pages!=NULL) exit(666); */ + // On initialise chacune des nouvelles pages + page=New_page(NB_LAYERS); + if (!page) + return 0; + + // Set as first page of the list + page->Next = page; + page->Prev = page; + list->Pages = page; - // On alloue la mémoire pour la liste - list->Pages=(T_Page *)malloc(size*sizeof(T_Page)); + list->List_size=1; - // On vérifie que l'allocation se soit bien passée - if (list->Pages==NULL) - return 0; // Echec - else - { - // On initialise chacune des nouvelles pages - for (index=0;indexPages+index); - list->List_size=size; - list->Nb_pages_allocated=0; - - return 1; // Succès - } + return 1; // Succès } -void Free_a_list_of_pages(T_List_of_pages * list) -{ - // On peut appeler cette fonction sur une liste de pages non allouée. - - // Important: cette fonction ne libère pas les pages de la liste. Il faut - // donc le faire préalablement si nécessaire. - - if (list->Pages!=NULL) - free(list->Pages); - list->Pages=NULL; - list->List_size=0; - list->Nb_pages_allocated=0; -} - -int Size_of_a_list_of_pages(T_List_of_pages * list) -{ - int result=0; - int index; - - for (index=0;indexNb_pages_allocated;index++) - result+=Size_of_a_page(list->Pages+index); - - return result+sizeof(T_List_of_pages)+4; - - // C.F. la remarque à propos de Size_of_a_page pour la valeur 4. -} void Backward_in_list_of_pages(T_List_of_pages * list) { @@ -254,29 +419,26 @@ void Backward_in_list_of_pages(T_List_of_pages * list) // sortie, ainsi que celles relatives à la plus récente page d'undo (1ère // page de la liste). - int index; - T_Page * temp_page; - - if (list->Nb_pages_allocated>1) + if (Last_backed_up_layers) { - // On crée la page tempo - temp_page=(T_Page *)malloc(sizeof(T_Page)); - Init_page(temp_page); + // First page contains a ready-made backup of its ->Next. + // We have swap the first two pages, so the original page 0 + // will end up in position 0 again, and then overwrite it with a backup + // of the 'new' page1. + T_Page * page0; + T_Page * page1; - // On copie la 1ère page (page 0) dans la page temporaire - Copy_S_page(temp_page,list->Pages); - - // On copie toutes les pages 1-A à leur gauche - for (index=1;indexNb_pages_allocated;index++) - Copy_S_page(list->Pages+index-1,list->Pages+index); - - // On copie la page 0 (dont la sauvegarde a été effectuée dans la page - // temporaire) en dernière position - Copy_S_page(list->Pages+list->Nb_pages_allocated-1,temp_page); - - // On détruit la page tempo - free(temp_page); + page0 = list->Pages; + page1 = list->Pages->Next; + + page0->Next = page1->Next; + page1->Prev = page0->Prev; + page0->Prev = page1; + page1->Next = page0; + list->Pages = page0; + return; } + list->Pages = list->Pages->Next; } void Advance_in_list_of_pages(T_List_of_pages * list) @@ -296,242 +458,102 @@ void Advance_in_list_of_pages(T_List_of_pages * list) // de page courante à jour avant l'appel, puis en réextraire les infos en // sortie, ainsi que celles relatives à la plus récente page d'undo (1ère // page de la liste). - - int index; - T_Page * temp_page; - - if (list->Nb_pages_allocated>1) + if (Last_backed_up_layers) { - // On crée la page tempo - temp_page=(T_Page *)malloc(sizeof(T_Page)); - Init_page(temp_page); + // First page contains a ready-made backup of its ->Next. + // We have swap the first two pages, so the original page 0 + // will end up in position -1 again, and then overwrite it with a backup + // of the 'new' page1. + T_Page * page0; + T_Page * page1; - // On copie la dernière page dans la page temporaire - Copy_S_page(temp_page,list->Pages+list->Nb_pages_allocated-1); - - // On copie toutes les pages 0-9 à leur droite - for (index=list->Nb_pages_allocated-1;index>0;index--) - Copy_S_page(list->Pages+index,list->Pages+index-1); - - // On copie la page plus ancienne page (la "A", dont la sauvegarde a été - // effectuée dans la page temporaire) en 1ère position - Copy_S_page(list->Pages,temp_page); - - // On détruit la page tempo - free(temp_page); + page0 = list->Pages; + page1 = list->Pages->Prev; + + page0->Prev = page1->Prev; + page1->Next = page0->Next; + page0->Next = page1; + page1->Prev = page0; + list->Pages = page1; + return; } -} - -int New_page_is_possible( - T_Page * new_page, - T_List_of_pages * current_list, - T_List_of_pages * secondary_list -) -{ - long long mem_available_now; - unsigned long current_list_size; - unsigned long spare_list_size; - unsigned long current_page_size; - unsigned long spare_page_size; - unsigned long new_page_size; - - mem_available_now = Memory_free() - - MINIMAL_MEMORY_TO_RESERVE; - current_list_size =Size_of_a_list_of_pages(current_list); - spare_list_size=Size_of_a_list_of_pages(secondary_list); - current_page_size =Size_of_a_page(current_list->Pages); - spare_page_size =Size_of_a_page(secondary_list->Pages); - new_page_size =Size_of_a_page(new_page); - - // Il faut pouvoir loger la nouvelle page et son backup dans la page - // courante, en conservant au pire la 1ère page de brouillon. - if ( (mem_available_now + current_list_size + - spare_list_size - spare_page_size) - < (2*new_page_size) ) - return 0; - - // Il faut pouvoir loger le brouillon et son backup dans la page de - // brouillon, en conservant au pire un exemplaire de la nouvelle page dans - // la page courante. (pour permettre à l'utilisateur de travailler sur son - // brouillon) - if ((mem_available_now+current_list_size+ - spare_list_size-new_page_size)<(2*spare_page_size)) - return 0; - - return 1; + list->Pages = list->Pages->Prev; } void Free_last_page_of_list(T_List_of_pages * list) { if (list!=NULL) { - if (list->Nb_pages_allocated>0) + if (list->List_size>0) { - list->Nb_pages_allocated--; - Free_a_page(list->Pages+list->Nb_pages_allocated); + T_Page * page; + // The last page is the one before first + page = list->Pages->Prev; + + page->Next->Prev = page->Prev; + page->Prev->Next = page->Next; + Clear_page(page); + free(page); + list->List_size--; } } } -void Create_new_page(T_Page * new_page,T_List_of_pages * current_list,T_List_of_pages * secondary_list) +// layer_mask tells which layers have to be fresh copies instead of references +int Create_new_page(T_Page * new_page, T_List_of_pages * list, dword layer_mask) { // Cette fonction crée une nouvelle page dont les attributs correspondent à // ceux de new_page (width,height,...) (le champ Image est invalide // à l'appel, c'est la fonction qui le met à jour), et l'enfile dans -// current_list. -// Il est impératif que la création de cette page soit possible, -// éventuellement au détriment des backups de la page de brouillon -// (secondary_list). Afin de s'en assurer, il faut vérifier cette -// possibilité à l'aide de -// New_page_is_possible(new_page,current_list,secondary_list) avant -// l'appel à cette fonction. -// De plus, il faut qu'il y ait au moins une page dans chacune des listes. +// list. - int need_to_free; - T_List_of_pages * list_to_reduce=NULL; - T_Page * page_to_delete; - int index; - // On regarde s'il faut libérer des pages: - need_to_free= - // C'est le cas si la current_list est pleine - ( (current_list->List_size==current_list->Nb_pages_allocated) - // ou qu'il ne reste plus assez de place pour allouer la new_page - || ( (Memory_free()-MINIMAL_MEMORY_TO_RESERVE)< - (unsigned long)(new_page->Height*new_page->Width) ) ); - - if (!need_to_free) + if (list->List_size >= (Config.Max_undo_pages+1)) { - // On a assez de place pour allouer une page, et de plus la current_list - // n'est pas pleine. On n'a donc aucune page à supprimer. On peut en - // allouer une directement. - new_page->Image=(byte *)malloc(new_page->Height*new_page->Width); + // On manque de mémoire ou la list est pleine. Dans tous les + // cas, il faut libérer une page. + + // Détruire la dernière page allouée dans la Liste_à_raboter + Free_last_page_of_list(list); } - else { - // On manque de mémoire ou la current_list est pleine. Dans tous les - // cas, il faut libérer une page... qui peut-être pourra re-servir. - - // Tant qu'il faut libérer - while (need_to_free) + int i; + for (i=0; iNb_layers; i++) { - // On cherche sur quelle liste on va virer une page - - // S'il reste des pages à libérer dans la current_list - if (current_list->Nb_pages_allocated>1) - // Alors on va détruire la dernière page allouée de la current_list - list_to_reduce=current_list; + if ((1<Image[i]=New_layer(new_page->Height*new_page->Width); else - { - if (secondary_list->Nb_pages_allocated>1) - { - // Sinon on va détruire la dernière page allouée de la - // secondary_list - list_to_reduce=secondary_list; - } - else - { - // Bon, alors là, on vient de vider toutes les pages et on a toujours pas asez de mémoire... C'est donc qu'un vilain programmeur a oublié de vérifier avec Noiuvelle_page_possible avant de venir ici. - // On sort méchament du programme sans sauvegarde ni rien. De toutes façons, ça ne devrait jamais se produire... - Error(ERROR_SORRY_SORRY_SORRY); - } - } - - // Puis on détermine la page que l'on va supprimer (c'est la dernière de - // la liste) - page_to_delete=list_to_reduce->Pages+(list_to_reduce->Nb_pages_allocated)-1; - - // On regarde si on peut recycler directement la page (cas où elle - // aurait la même surface que la new_page) - if ((page_to_delete->Height*page_to_delete->Width)== - (new_page->Height*new_page->Width)) - { - // Alors - // On récupère le bitmap de la page à supprimer (évite de faire des - // allocations/désallocations fastidieuses et inutiles) - new_page->Image=page_to_delete->Image; - - // On fait semblant que la dernière page allouée ne l'est pas - list_to_reduce->Nb_pages_allocated--; - - // On n'a plus besoin de libérer de la mémoire puisqu'on a refilé à - // new_page un bitmap valide - need_to_free=0; - } - else - { - // Sinon - - // Détruire la dernière page allouée dans la Liste_à_raboter - Free_last_page_of_list(list_to_reduce); - - // On regarde s'il faut continuer à libérer de la place - need_to_free=(Memory_free()-MINIMAL_MEMORY_TO_RESERVE) - <(unsigned long)(new_page->Height*new_page->Width); - - // S'il ne faut pas, c'est qu'on peut allouer un bitmap - // pour la new_page - if (!need_to_free) - new_page->Image=(byte *)malloc(new_page->Height*new_page->Width); - } + new_page->Image[i]=Dup_layer(list->Pages->Image[i]); } } - // D'après l'hypothèse de départ, la boucle ci-dessus doit s'arrêter car - // on a assez de mémoire pour allouer la nouvelle page. - // Désormais new_page contient un pointeur sur une zone bitmap valide. - - // Décaler la current_list d'un cran vers le passé. - for (index=current_list->List_size-1;index>0;index--) - Copy_S_page(current_list->Pages+index,current_list->Pages+index-1); - - // Recopier la new_page en 1ère position de la current_list - Copy_S_page(current_list->Pages,new_page); - current_list->Nb_pages_allocated++; + + // Insert as first + new_page->Next = list->Pages; + new_page->Prev = list->Pages->Prev; + list->Pages->Prev->Next = new_page; + list->Pages->Prev = new_page; + list->Pages = new_page; + list->List_size++; + + return 1; } void Change_page_number_of_list(T_List_of_pages * list,int number) { - int index; - T_Page * new_pages; - - // Si la liste a déjà la taille demandée - if (list->List_size==number) - // Alors il n'y a rien à faire - return; - - // Si la liste contient plus de pages que souhaité - if (list->List_size>number) - // Alors pour chaque page en excés - for (index=number;indexList_size;index++) - // On libère la page - Free_a_page(list->Pages+index); - - // On fait une nouvelle liste de pages: - new_pages=(T_Page *)malloc(number*sizeof(T_Page)); - for (index=0;indexList_size);index++) - Copy_S_page(new_pages+index,list->Pages+index); - - // On libère l'ancienne liste - free(list->Pages); - - // On met à jour les champs de la nouvelle liste - list->Pages=new_pages; - list->List_size=number; - if (list->Nb_pages_allocated>number) - list->Nb_pages_allocated=number; + // Truncate the list if larger than requested + while(list->List_size > number) + { + Free_last_page_of_list(list); + } } void Free_page_of_a_list(T_List_of_pages * list) { // On ne peut pas détruire la page courante de la liste si après // destruction il ne reste pas encore au moins une page. - if (list->Nb_pages_allocated>1) + if (list->List_size>1) { // On fait faire un undo à la liste, comme ça, la nouvelle page courante // est la page précédente @@ -542,82 +564,130 @@ void Free_page_of_a_list(T_List_of_pages * list) } } - - /// - /// GESTION DES BACKUPS - /// - -int Init_all_backup_lists(int size,int width,int height) +/// Resize one of the special bitmap buffers, if necessary. +int Update_buffer(T_Image * image, int width, int height) +{ + // At least one dimension is different + if (image->Width != width || image->Height != height) + { + // Actual size difference + if (image->Width * image->Height != width * height) + { + free(image->Image); + image->Image = (byte *)malloc(width * height); + if (image->Image == NULL) + return 0; + } + image->Width = width; + image->Height = height; + } + return 1; +} + +void Update_screen_targets(void) +{ + #ifndef NOLAYERS + Main_screen=Visible_image[0].Image; + Screen_backup=Visible_image[1].Image; + #else + Main_screen=Main_backups->Pages->Image[Main_current_layer]; + Screen_backup=Main_backups->Pages->Next->Image[Main_current_layer]; + #endif +} + +/// Update all the special image buffers, if necessary. +int Update_buffers(int width, int height) +{ +#ifndef NOLAYERS + if (! Update_buffer(&Visible_image_depth_buffer, width, height)) + return 0; + if (! Update_buffer(&Visible_image[0], width, height)) + return 0; + if (! Update_buffer(&Visible_image[1], width, height)) + return 0; +#endif + Update_screen_targets(); + return 1; +} + +/// +/// GESTION DES BACKUPS +/// + +int Init_all_backup_lists(int width,int height) { - // size correspond au nombre de pages que l'on souhaite dans chaque liste - // (1 pour la page courante, puis 1 pour chaque backup, soit 2 au minimum). // width et height correspondent à la dimension des images de départ. + int i; - T_Page * page; - int return_code=0; + if (! Allocate_list_of_pages(Main_backups) || + ! Allocate_list_of_pages(Spare_backups)) + return 0; + // On a réussi à allouer deux listes de pages dont la taille correspond à + // celle demandée par l'utilisateur. - if (Allocate_list_of_pages(Main_backups,size) && - Allocate_list_of_pages(Spare_backups,size)) + // On crée un descripteur de page correspondant à la page principale + Upload_infos_page_main(Main_backups->Pages); + // On y met les infos sur la dimension de démarrage + Main_backups->Pages->Width=width; + Main_backups->Pages->Height=height; + for (i=0; iPages->Nb_layers; i++) { - // On a réussi à allouer deux listes de pages dont la taille correspond à - // celle demandée par l'utilisateur. - - // On crée un descripteur de page correspondant à la page principale - page=(T_Page *)malloc(sizeof(T_Page)); - Init_page(page); - Upload_infos_page_main(page); - // On y met les infos sur la dimension de démarrage - page->Width=width; - page->Height=height; - - // On regarde si on peut ajouter cette page - if (New_page_is_possible(page,Main_backups,Spare_backups)) - { - // On peut, donc on va la créer - Create_new_page(page,Main_backups,Spare_backups); - Download_infos_page_main(page); - Download_infos_backup(Main_backups); - - // Maintenant, on regarde si on a le droit de créer la même page dans - // la page de brouillon. - if (New_page_is_possible(page,Spare_backups,Main_backups)) - { - // On peut donc on le fait - Create_new_page(page,Spare_backups,Main_backups); - Download_infos_page_spare(page); - - // Et on efface les 2 images en les remplacant de "0" - memset(Main_screen,0,Main_image_width*Main_image_height); - memset(Spare_screen,0,Spare_image_width*Spare_image_height); - - return_code=1; - } - else - { - // Il n'est pas possible de démarrer le programme avec la page - // principale et la page de brouillon aux dimensions demandée par - // l'utilisateur. ==> On l'envoie ballader - return_code=0; - } - } - else - { - // On ne peut pas démarrer le programme avec ne serait-ce qu'une - // page de la dimension souhaitée, donc on laisse tout tomber et on - // le renvoie chier. - free(page); - return_code=0; - } + Main_backups->Pages->Image[i]=New_layer(width*height); + if (! Main_backups->Pages->Image[i]) + return 0; + memset(Main_backups->Pages->Image[i], 0, width*height); } - else +#ifndef NOLAYERS + Visible_image[0].Width = 0; + Visible_image[0].Height = 0; + Visible_image[0].Image = NULL; + + Visible_image[1].Width = 0; + Visible_image[1].Height = 0; + Visible_image[1].Image = NULL; + + Visible_image_depth_buffer.Width = 0; + Visible_image_depth_buffer.Height = 0; + Visible_image_depth_buffer.Image = NULL; +#endif + if (!Update_buffers(width, height)) + return 0; +#ifndef NOLAYERS + // For speed, instead of Redraw_layered_image() we'll directly set the buffers. + memset(Visible_image[0].Image, 0, width*height); + memset(Visible_image[1].Image, 0, width*height); + memset(Visible_image_depth_buffer.Image, 0, width*height); +#endif + Download_infos_page_main(Main_backups->Pages); + Download_infos_backup(Main_backups); + + // Default values for spare page + Spare_backups->Pages->Width = width; + Spare_backups->Pages->Height = height; + memcpy(Spare_backups->Pages->Palette,Main_palette,sizeof(T_Palette)); + strcpy(Spare_backups->Pages->Comment,""); + strcpy(Spare_backups->Pages->File_directory,Spare_current_directory); + strcpy(Spare_backups->Pages->Filename,"NO_NAME.GIF"); + Spare_backups->Pages->File_format=DEFAULT_FILEFORMAT; + // Copy this informations in the global Spare_ variables + Download_infos_page_spare(Spare_backups->Pages); + + // Clear the initial Visible buffer + //memset(Main_screen,0,Main_image_width*Main_image_height); + + // Spare + for (i=0; iPages->Image[i]=New_layer(width*height); + if (! Spare_backups->Pages->Image[i]) + return 0; + memset(Spare_backups->Pages->Image[i], 0, width*height); - return return_code; + } + //memset(Spare_screen,0,Spare_image_width*Spare_image_height); + + End_of_modification(); + return 1; } void Set_number_of_backups(int nb_backups) @@ -630,13 +700,14 @@ void Set_number_of_backups(int nb_backups) // (nb_backups = Nombre de backups, sans compter les pages courantes) } -int Backup_with_new_dimensions(int upload,int width,int height) +int Backup_with_new_dimensions(int upload,byte layers,int width,int height) { // Retourne 1 si une nouvelle page est disponible (alors pleine de 0) et // 0 sinon. T_Page * new_page; int return_code=0; + int i; if (upload) // On remet à jour l'état des infos de la page courante (pour pouvoir les @@ -644,25 +715,29 @@ int Backup_with_new_dimensions(int upload,int width,int height) Upload_infos_page_main(Main_backups->Pages); // On crée un descripteur pour la nouvelle page courante - new_page=(T_Page *)malloc(sizeof(T_Page)); - Init_page(new_page); - + new_page=New_page(layers); + if (!new_page) + { + Error(0); + return 0; + } Upload_infos_page_main(new_page); new_page->Width=width; new_page->Height=height; - if (New_page_is_possible(new_page,Main_backups,Spare_backups)) + if (Create_new_page(new_page,Main_backups,0xFFFFFFFF)) { - Create_new_page(new_page,Main_backups,Spare_backups); - Download_infos_page_main(new_page); + for (i=0; iPages->Image[i], 0, width*height); + } + + Update_buffers(width, height); + + Download_infos_page_main(Main_backups->Pages); Download_infos_backup(Main_backups); - // On nettoie la nouvelle image: - memset(Main_screen,0,Main_image_width*Main_image_height); + return_code=1; } - - // On détruit le descripteur de la page courante - free(new_page); - return return_code; } @@ -673,28 +748,39 @@ int Backup_and_resize_the_spare(int width,int height) T_Page * new_page; int return_code=0; + byte nb_layers; + // On remet à jour l'état des infos de la page de brouillon (pour pouvoir // les retrouver plus tard) Upload_infos_page_spare(Spare_backups->Pages); + nb_layers=Spare_backups->Pages->Nb_layers; // On crée un descripteur pour la nouvelle page de brouillon - new_page=(T_Page *)malloc(sizeof(T_Page)); - Init_page(new_page); - + new_page=New_page(nb_layers); + if (!new_page) + { + Error(0); + return 0; + } Upload_infos_page_spare(new_page); new_page->Width=width; new_page->Height=height; - if (New_page_is_possible(new_page,Spare_backups,Main_backups)) + if (Create_new_page(new_page,Spare_backups,0xFFFFFFFF)) { - Create_new_page(new_page,Spare_backups,Main_backups); - Download_infos_page_spare(new_page); + byte i; + + for (i=0; iPages->Image[i], 0, width*height); + } + + // Update_buffers(width, height); // Not for spare + + Download_infos_page_spare(Spare_backups->Pages); + return_code=1; } - - // On détruit le descripteur de la page courante - free(new_page); - return return_code; } @@ -702,56 +788,70 @@ void Backup(void) // Sauve la page courante comme première page de backup et crée une nouvelle page // pur continuer à dessiner. Utilisé par exemple pour le fill { - #if defined(__macosx__) || defined(__FreeBSD__) - T_Page new_page; - #else - T_Page *new_page; - #endif + Backup_layers(1<Pages); // On crée un descripteur pour la nouvelle page courante -#if defined(__macosx__) || defined(__FreeBSD__) - Init_page(&new_page); - - // Enrichissement de l'historique - Copy_S_page(&new_page,Main_backups->Pages); - Create_new_page(&new_page,Main_backups,Spare_backups); - Download_infos_page_main(&new_page); -#else - new_page=(T_Page *)malloc(sizeof(T_Page)); - Init_page(new_page); - + new_page=New_page(Main_backups->Pages->Nb_layers); + if (!new_page) + { + Error(0); + return; + } + // Enrichissement de l'historique Copy_S_page(new_page,Main_backups->Pages); - Create_new_page(new_page,Main_backups,Spare_backups); + Create_new_page(new_page,Main_backups,layer_mask); Download_infos_page_main(new_page); -#endif Download_infos_backup(Main_backups); // On copie l'image du backup vers la page courante: - memcpy(Main_screen,Screen_backup,Main_image_width*Main_image_height); - - // On détruit le descripteur de la page courante -#if !(defined(__macosx__) || defined(__FreeBSD__)) - free(new_page); -#endif - + for (i=0; iPages->Nb_layers;i++) + { + if ((1<Pages->Image[i], + Main_backups->Pages->Next->Image[i], + Main_image_width*Main_image_height); + } // On allume l'indicateur de modification de l'image Main_image_is_modified=1; + + /* + Last_backed_up_layers = 1<Pages); // On fait faire un undo à la liste des backups de la page principale Backward_in_list_of_pages(Main_backups); + Update_buffers(Main_backups->Pages->Width, Main_backups->Pages->Height); + // On extrait ensuite les infos sur la nouvelle page courante Download_infos_page_main(Main_backups->Pages); // Et celles du backup @@ -760,16 +860,29 @@ void Undo(void) // palette que la page courante. Mais en temps normal, le backup // n'est pas utilisé à la suite d'un Undo. Donc ça ne devrait pas // poser de problèmes. + + if (Main_current_layer > Main_backups->Pages->Nb_layers-1) + Main_current_layer = Main_backups->Pages->Nb_layers-1; + + Redraw_layered_image(); + } void Redo(void) { + if (Last_backed_up_layers) + { + Free_page_of_a_list(Main_backups); + Last_backed_up_layers=0; + } // On remet à jour l'état des infos de la page courante (pour pouvoir les // retrouver plus tard) Upload_infos_page_main(Main_backups->Pages); // On fait faire un redo à la liste des backups de la page principale Advance_in_list_of_pages(Main_backups); + Update_buffers(Main_backups->Pages->Width, Main_backups->Pages->Height); + // On extrait ensuite les infos sur la nouvelle page courante Download_infos_page_main(Main_backups->Pages); // Et celles du backup @@ -778,12 +891,22 @@ void Redo(void) // palette que la page courante. Mais en temps normal, le backup // n'est pas utilisé à la suite d'un Redo. Donc ça ne devrait pas // poser de problèmes. + + if (Main_current_layer > Main_backups->Pages->Nb_layers-1) + Main_current_layer = Main_backups->Pages->Nb_layers-1; + + Redraw_layered_image(); + } void Free_current_page(void) { // On détruit la page courante de la liste principale Free_page_of_a_list(Main_backups); + + Update_buffers(Main_backups->Pages->Width, Main_backups->Pages->Height); + Redraw_layered_image(); + // On extrait ensuite les infos sur la nouvelle page courante Download_infos_page_main(Main_backups->Pages); // Et celles du backup @@ -791,7 +914,7 @@ void Free_current_page(void) // Note: le backup n'a pas obligatoirement les mêmes dimensions ni la même // palette que la page courante. Mais en temps normal, le backup // n'est pas utilisé à la suite d'une destruction de page. Donc ça ne - // devrait pas poser de problèmes. + // devrait pas poser de problèmes. } void Exchange_main_and_spare(void) @@ -812,127 +935,203 @@ void Exchange_main_and_spare(void) // On extrait ensuite les infos sur les nouvelles pages courante, brouillon // et backup. - /* SECTION GROS CACA PROUT PROUT */ - // Auparavant on ruse en mettant déjà à jour les dimensions de la - // nouvelle page courante. Si on ne le fait pas, le "Download" va détecter - // un changement de dimensions et va bêtement sortir du mode loupe, alors - // que lors d'un changement de page, on veut bien conserver l'état du mode - // loupe du brouillon. - Main_image_width=Main_backups->Pages->Width; - Main_image_height=Main_backups->Pages->Height; + Update_buffers(Main_backups->Pages->Width, Main_backups->Pages->Height); + + /* SECTION GROS CACA PROUT PROUT */ + // Auparavant on ruse en mettant déjà à jour les dimensions de la + // nouvelle page courante. Si on ne le fait pas, le "Download" va détecter + // un changement de dimensions et va bêtement sortir du mode loupe, alors + // que lors d'un changement de page, on veut bien conserver l'état du mode + // loupe du brouillon. + Main_image_width=Main_backups->Pages->Width; + Main_image_height=Main_backups->Pages->Height; Download_infos_page_main(Main_backups->Pages); - Download_infos_page_spare(Spare_backups->Pages); Download_infos_backup(Main_backups); -} - - -int Can_borrow_memory_from_page(int size) -{ - int mem_available_now; - int current_list_size; - int spare_list_size; - int current_page_size; - int spare_page_size; - - mem_available_now=Memory_free()-MINIMAL_MEMORY_TO_RESERVE; - current_list_size =Size_of_a_list_of_pages(Main_backups); - spare_list_size=Size_of_a_list_of_pages(Spare_backups); - current_page_size =Size_of_a_page(Main_backups->Pages); - spare_page_size =Size_of_a_page(Spare_backups->Pages); - - // Il faut pouvoir loger la zone mémoire ainsi qu'un exemplaire de la page - // courante, en conservant au pire la 1ère page de brouillon. - if ((mem_available_now - +current_list_size - +spare_list_size - -current_page_size - -spare_page_size)Nb_pages_allocated>1) - // Alors on va détruire la dernière page allouée de la liste des - // brouillons - list_to_reduce=Spare_backups; - else - { - if (Main_backups->Nb_pages_allocated>1) - { - // Sinon on va détruire la dernière page allouée de la - // liste principale - list_to_reduce=Main_backups; - } - else - { - // Dans cette branche, il était prévu qu'on obtienne la mémoire - // nécessaire mais on n'arrive pas à la trouver. On indique donc - // qu'elle n'est pas disponible, et on aura perdu des backups - // pour rien - return 0; - } - } - - // Puis on détermine la page que l'on va supprimer (c'est la dernière - // de la liste) - page_to_delete=list_to_reduce->Pages+(list_to_reduce->Nb_pages_allocated)-1; - - // Détruire la dernière page allouée dans la Liste_à_raboter - Free_last_page_of_list(list_to_reduce); - - // On regarde s'il faut continuer à libérer de la place - need_to_free= - (Memory_free()-MINIMAL_MEMORY_TO_RESERVE)<(unsigned long)size; - - // S'il ne faut pas, c'est qu'on peut allouer un bitmap - // pour la new_page - if (!need_to_free) - return malloc(size); - } - } - } - else - { - // Il n'y a pas assez de place pour allouer la mémoire temporaire dans - // la mémoire réservée aux pages. - return 0; - } - - // Pour que le compilateur ne dise pas qu'il manque une valeur de sortie: - return 0; + Download_infos_page_spare(Spare_backups->Pages); + Redraw_layered_image(); } void End_of_modification(void) { + + //Update_buffers(Main_image_width, Main_image_height); + +#ifndef NOLAYERS + memcpy(Visible_image[1].Image, + Visible_image[0].Image, + Main_image_width*Main_image_height); +#else + Update_screen_targets(); +#endif + + Download_infos_backup(Main_backups); +/* + Last_backed_up_layers = 0; + Backup(); + */ +} + +/// Add a new layer to latest page of a list. Returns 0 on success. +byte Add_layer(T_List_of_pages *list, byte layer) +{ + T_Page * source_page; + T_Page * new_page; + byte * new_image; + int i; + + source_page = list->Pages; + + // Hard limit of 32 at the moment, because layer bitmasks are 32bit. + if (list->Pages->Nb_layers == 32) + return 1; + + // Keep the position reasonable + if (layer > list->Pages->Nb_layers) + layer = list->Pages->Nb_layers; + + // Allocate the pixel data + new_image = New_layer(list->Pages->Height*list->Pages->Width); + if (! new_image) + { + Error(0); + return 1; + } + // Re-allocate the page itself, with room for one more pointer + new_page = realloc(source_page, sizeof(T_Page)+(list->Pages->Nb_layers+1)*sizeof(byte *)); + if (!new_page) + { + Error(0); + return 1; + } + if (new_page != source_page) + { + // Need some housekeeping because the page moved in memory. + // Update all pointers that pointed to it: + new_page->Prev->Next = new_page; + new_page->Next->Prev = new_page; + list->Pages = new_page; + } + list->Pages->Nb_layers++; + // Move around the pointers. This part is going to be tricky when we + // have 'animations x layers' in this vector. + for (i=list->Pages->Nb_layers-1; i>layer ; i--) + { + new_page->Image[i]=new_page->Image[i-1]; + } + new_page->Image[layer]=new_image; + // Fill with transparency, initially + memset(new_image, Main_backups->Pages->Transparent_color, list->Pages->Height*list->Pages->Width); // transparent color + + // Done. Note that the visible buffer is already ok since we + // only inserted a transparent "slide" somewhere. + // The depth buffer is all wrong though. + + // Update the flags of visible layers. + { + dword layers_before; + dword layers_after; + dword *visible_layers_flag; + + // Determine if we're modifying the spare or the main page. + if (list == Main_backups) + { + visible_layers_flag = &Main_layers_visible; + Main_current_layer = layer; + } + else + { + visible_layers_flag = &Spare_layers_visible; + Spare_current_layer = layer; + } + + // Fun with binary! + layers_before = ((1<Pages; + + // Keep the position reasonable + if (layer >= list->Pages->Nb_layers) + layer = list->Pages->Nb_layers - 1; + if (list->Pages->Nb_layers == 1) + return 1; + + // For simplicity, we won't actually shrink the page in terms of allocation. + // It would only save the size of a pointer, and anyway, as the user draws, + // this page is going to fall off the end of the Undo-list + // and so it will be cleared anyway. + + // Smart freeing of the pixel data + Free_layer(list->Pages, layer); + + list->Pages->Nb_layers--; + // Move around the pointers. This part is going to be tricky when we + // have 'animations x layers' in this vector. + for (i=layer; i < list->Pages->Nb_layers; i++) + { + list->Pages->Image[i]=list->Pages->Image[i+1]; + } + + // Done. At this point the visible buffer and the depth buffer are + // all wrong. + + // Update the flags of visible layers. + { + dword layers_before; + dword layers_after; + dword *visible_layers_flag; + byte new_current_layer; + + // Determine if we're modifying the spare or the main page. + if (list == Main_backups) + { + visible_layers_flag = &Main_layers_visible; + if (Main_current_layer>=layer && Main_current_layer>0) + Main_current_layer--; + new_current_layer = Main_current_layer; + } + else + { + visible_layers_flag = &Spare_layers_visible; + if (Spare_current_layer>=layer && Spare_current_layer>0) + Spare_current_layer--; + new_current_layer = Spare_current_layer; + } + + // Fun with binary! + layers_before = ((1<>1; + *visible_layers_flag = layers_before | layers_after; + // Ensure the current layer is part what is shown. + *visible_layers_flag |= 1<Pages->Image[Main_current_layer]+i); + if (color != Main_backups->Pages->Transparent_color) // transparent color + *(Main_backups->Pages->Image[Main_current_layer-1]+i) = color; + } + return Delete_layer(Main_backups,Main_current_layer); } diff --git a/pages.h b/pages.h index 84bf9d65..7ec8658f 100644 --- a/pages.h +++ b/pages.h @@ -32,6 +32,8 @@ /////////////////////////// BACKUP /////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +extern T_Image Visible_image[2]; +extern T_Image Visible_image_depth_buffer; /// /// INDIVIDUAL PAGES @@ -39,15 +41,20 @@ void Download_infos_page_main(T_Page * page); void Upload_infos_page_main(T_Page * page); +/// Add a new layer to latest page of a list. Returns 0 on success. +byte Add_layer(T_List_of_pages *list, byte layer); +/// Delete a layer from the latest page of a list. Returns 0 on success. +byte Delete_layer(T_List_of_pages *list, byte layer); +/// Merges the current layer onto the one below it. +byte Merge_layer(); // private -void Init_page(T_Page * page); +T_Page * New_page(byte nb_layers); void Download_infos_page_spare(T_Page * page); void Upload_infos_page_spare(T_Page * page); void Download_infos_backup(T_List_of_pages * list); -void Free_a_page(T_Page * page); +void Clear_page(T_Page * page); void Copy_S_page(T_Page * dest,T_Page * source); -int Size_of_a_page(T_Page * page); @@ -57,14 +64,11 @@ int Size_of_a_page(T_Page * page); void Init_list_of_pages(T_List_of_pages * list); // private -int Allocate_list_of_pages(T_List_of_pages * list,int size); -void Free_a_list_of_pages(T_List_of_pages * list); -int Size_of_a_list_of_pages(T_List_of_pages * list); +int Allocate_list_of_pages(T_List_of_pages * list); void Backward_in_list_of_pages(T_List_of_pages * list); void Advance_in_list_of_pages(T_List_of_pages * list); -int New_page_is_possible(T_Page * new_page,T_List_of_pages * current_list,T_List_of_pages * secondary_list); void Free_last_page_of_list(T_List_of_pages * list); -void Create_new_page(T_Page * new_page,T_List_of_pages * current_list,T_List_of_pages * secondary_list); +int Create_new_page(T_Page * new_page,T_List_of_pages * current_list, dword layer_mask); void Change_page_number_of_list(T_List_of_pages * list,int number); void Free_page_of_a_list(T_List_of_pages * list); @@ -74,25 +78,33 @@ void Free_page_of_a_list(T_List_of_pages * list); /// BACKUP HIGH-LEVEL FUNCTIONS /// -int Init_all_backup_lists(int size,int width,int height); +int Init_all_backup_lists(int width,int height); void Set_number_of_backups(int nb_backups); -int Backup_with_new_dimensions(int upload,int width,int height); +int Backup_with_new_dimensions(int upload,byte layers,int width,int height); int Backup_and_resize_the_spare(int width,int height); +/// Backup with a new copy for the working layer, and references for all others. void Backup(void); +/// Backup with a new copy of some layers (the others are references). +void Backup_layers(dword layer_mask); void Undo(void); void Redo(void); void Free_current_page(void); // 'Kill' button void Exchange_main_and_spare(void); void End_of_modification(void); +void Update_depth_buffer(void); +void Redraw_layered_image(void); +void Redraw_current_layer(void); + +void Update_screen_targets(void); /// -/// BORROWING MEMORY FROM PAGE +/// STATISTICS /// -void * Borrow_memory_from_page(int size); -// private -int Can_borrow_memory_from_page(int size); - +/// Total number of unique bitmaps (layers, animation frames, backups) +extern long Stats_pages_number; +/// Total memory used by bitmaps (layers, animation frames, backups) +extern long long Stats_pages_memory; #endif diff --git a/palette.c b/palette.c index 75bae4ce..e58ff380 100644 --- a/palette.c +++ b/palette.c @@ -1316,7 +1316,7 @@ void Button_Palette(void) // est en mode X-SWAP. if (!image_is_backed_up) { - Backup(); + Backup_layers(-1); image_is_backed_up=1; } @@ -1472,12 +1472,11 @@ void Button_Palette(void) reduce_colors_number = -1; break; } - if (reduce_colors_number > 0) { if (!image_is_backed_up) { - Backup(); + Backup_layers(-1); image_is_backed_up = 1; } @@ -1846,7 +1845,7 @@ void Button_Palette(void) { if (!image_is_backed_up) { - Backup(); + Backup_layers(-1); image_is_backed_up=1; } Hide_cursor(); @@ -1923,7 +1922,7 @@ void Button_Palette(void) // Make a backup because remapping is an undoable modification if (!image_is_backed_up) { - Backup(); + Backup_layers(-1); image_is_backed_up=1; } @@ -2067,7 +2066,7 @@ void Button_Palette(void) // de ne pas trop modifier l'image. if (!image_is_backed_up) { - Backup(); + Backup_layers(-1); image_is_backed_up=1; } if (used_colors==-1) @@ -2169,9 +2168,9 @@ void Button_Palette(void) { if ( (!image_is_backed_up) && memcmp(Main_palette,working_palette,sizeof(T_Palette)) ) - Backup(); + Backup_layers(-1); memcpy(Main_palette,working_palette,sizeof(T_Palette)); - // End_of_modification(); + End_of_modification(); // Not really needed, the change was in palette entries } diff --git a/pversion.c b/pversion.c index 61c474c5..a1c9e4de 100644 --- a/pversion.c +++ b/pversion.c @@ -1,3 +1,4 @@ /* vim:expandtab:ts=2 sw=2: */ char Program_version[]="2.2wip"; + diff --git a/special.c b/special.c index d4006c0d..e00ea456 100644 --- a/special.c +++ b/special.c @@ -27,6 +27,11 @@ #include "engine.h" #include "windows.h" #include "special.h" +#include "pages.h" +#include "misc.h" +#include "buttons.h" + + @@ -413,3 +418,126 @@ void Zoom_set(int index) } Display_cursor(); } + +void Transparency_set(byte amount) +{ + const int doubleclick_delay = Config.Double_key_speed; + static long time_click = 0; + long time_previous; + + if (!Colorize_mode) + { + // Activate mode + switch(Colorize_current_mode) + { + case 0 : + Effect_function=Effect_interpolated_colorize; + break; + case 1 : + Effect_function=Effect_additive_colorize; + break; + case 2 : + Effect_function=Effect_substractive_colorize; + } + Shade_mode=0; + Quick_shade_mode=0; + Smooth_mode=0; + Tiling_mode=0; + + Colorize_mode=1; + } + + time_previous = time_click; + time_click = SDL_GetTicks(); + + // Check if it's a quick re-press + if (time_click - time_previous < doubleclick_delay) + { + // Use the typed amount as units, keep the tens. + Colorize_opacity = ((Colorize_opacity%100) /10 *10) + amount; + if (Colorize_opacity == 0) + Colorize_opacity = 100; + } + else + { + // Use 10% units: "1"=10%, ... "0"=100% + if (amount == 0) + Colorize_opacity = 100; + else + Colorize_opacity = amount*10; + } + Compute_colorize_table(); +} + +void Layer_activate(short layer, short side) +{ + word old_layers; + + if (layer >= Main_backups->Pages->Nb_layers) + return; + + // Keep a copy of which layers were visible + old_layers = Main_layers_visible; + + #ifndef NOLAYERS + + if (side == RIGHT_SIDE) + { + // Right-click on current layer + if (Main_current_layer == layer) + { + if (Main_layers_visible == (dword)(1<Pages); + //Download_infos_backup(Main_backups); + Display_all_screen(); + Display_cursor(); +} + +void Special_add_layer() +{ + + +} +void Special_delete_layer() +{ + + +} diff --git a/special.h b/special.h index ac651126..d28fa68d 100644 --- a/special.h +++ b/special.h @@ -50,3 +50,15 @@ void Zoom_set(int index); void Display_stored_brush_in_window(word x,word y,int number); void Store_brush(int index); byte Restore_brush(int index); + +/*! + Command that sets the transparency level. +*/ +void Transparency_set(byte amount); + +void Layer_activate(short layer, short side); + +void Special_add_layer(void); + +void Special_delete_layer(void); + diff --git a/struct.h b/struct.h index 56a2cf2b..e229d64d 100644 --- a/struct.h +++ b/struct.h @@ -327,9 +327,8 @@ typedef struct // backup dans "graph.c". /// This is the data for one step of Undo/Redo, for one image. -typedef struct +typedef struct T_Page { - byte * Image; ///< Pixel data for the image. int Width; ///< Image width in pixels. int Height; ///< Image height in pixels. T_Palette Palette; ///< Image palette. @@ -339,17 +338,29 @@ typedef struct char File_directory[MAX_PATH_CHARACTERS];///< Directory that contains the file. char Filename[MAX_PATH_CHARACTERS]; ///< Filename without directory. byte File_format; ///< File format, in enum ::FILE_FORMATS - + struct T_Page *Next; ///< Pointer to the next backup + struct T_Page *Prev; ///< Pointer to the previous backup + word Transparent_color; ///< Index of transparent color. -1 or 0 to 255. + byte Nb_layers; ///< Number of layers + byte * Image[0]; ///< Pixel data for the (first layer of) image. + // No field after Image[] ! Dynamic layer allocation for Image[1], [2] etc. } T_Page; /// Collection of undo/redo steps. typedef struct { int List_size; ///< Number of ::T_Page in the vector "Pages". - int Nb_pages_allocated;///< Number of ::T_Page used so far in the vector "Pages". - T_Page * Pages; ///< Vector of Pages, each one being a undo/redo step. + T_Page * Pages; ///< Head of a linked list of pages, each one being a undo/redo step. } T_List_of_pages; +/// A single image bitmap +typedef struct +{ + int Width; ///< Image width in pixels. + int Height; ///< Image height in pixels. + byte * Image; ///< Pixel data for the image. +} T_Image; + /// A single memorized brush from the Brush Container typedef struct { @@ -363,7 +374,6 @@ typedef struct byte Transp_color; } T_Brush_template; - /// GUI skin data typedef struct { diff --git a/transform.c b/transform.c index 68874dde..a4758bfb 100644 --- a/transform.c +++ b/transform.c @@ -370,7 +370,7 @@ void Button_Transform_menu(void) old_height=Main_image_height; // Allocate a new page - if (Backup_with_new_dimensions(1,new_width,new_height)) + if (Backup_with_new_dimensions(1,Main_backups->Pages->Nb_layers,new_width,new_height)) { // The new image is allocated, the new dimensions are already updated. @@ -379,29 +379,60 @@ void Button_Transform_menu(void) // Process the transformation: switch(clicked_button) { + int i; + case 2 : // Flip X - memcpy(Main_screen,Screen_backup,Main_image_width*Main_image_height); - Flip_X_lowlevel(Main_screen, Main_image_width, Main_image_height); + for (i=0; iPages->Nb_layers; i++) + { + memcpy(Main_backups->Pages->Image[i],Main_backups->Pages->Next->Image[i],Main_image_width*Main_image_height); + Flip_X_lowlevel(Main_backups->Pages->Image[i], Main_image_width, Main_image_height); + } break; case 3 : // Flip Y - memcpy(Main_screen,Screen_backup,Main_image_width*Main_image_height); - Flip_Y_lowlevel(Main_screen, Main_image_width, Main_image_height); + for (i=0; iPages->Nb_layers; i++) + { + memcpy(Main_backups->Pages->Image[i],Main_backups->Pages->Next->Image[i],Main_image_width*Main_image_height); + Flip_Y_lowlevel(Main_backups->Pages->Image[i], Main_image_width, Main_image_height); + } break; case 4 : // -90° Rotation - Rotate_270_deg_lowlevel(Screen_backup, Main_screen, old_width, old_height); + for (i=0; iPages->Nb_layers; i++) + { + Rotate_270_deg_lowlevel(Main_backups->Pages->Next->Image[i], Main_backups->Pages->Image[i], old_width, old_height); + } break; case 5 : // +90° Rotation - Rotate_90_deg_lowlevel(Screen_backup, Main_screen, old_width, old_height); + for (i=0; iPages->Nb_layers; i++) + { + Rotate_90_deg_lowlevel(Main_backups->Pages->Next->Image[i], Main_backups->Pages->Image[i], old_width, old_height); + } break; case 6 : // 180° Rotation - memcpy(Main_screen,Screen_backup,Main_image_width*Main_image_height); - Rotate_180_deg_lowlevel(Main_screen, Main_image_width, Main_image_height); + for (i=0; iPages->Nb_layers; i++) + { + memcpy(Main_backups->Pages->Image[i],Main_backups->Pages->Next->Image[i],Main_image_width*Main_image_height); + Rotate_180_deg_lowlevel(Main_backups->Pages->Image[i], Main_image_width, Main_image_height); + } break; case 7 : // Resize - Rescale(Screen_backup, old_width, old_height, Main_screen, Main_image_width, Main_image_height, 0, 0); - break; + for (i=0; iPages->Nb_layers; i++) + { + Rescale(Main_backups->Pages->Next->Image[i], old_width, old_height, Main_backups->Pages->Image[i], Main_image_width, Main_image_height, 0, 0); + } + break; } - Display_all_screen(); + /* + for (i=0; iPages->Next->Image[i],0,0,Min(old_width,Main_image_width), + Min(old_height,Main_image_height),old_width, + Main_backups->Pages->Image[i],0,0,Main_image_width); + } + */ + Redraw_layered_image(); + Display_all_screen(); + End_of_modification(); } else {