From cedb706326da30e6849e8c297a2ae6f29ca2a466 Mon Sep 17 00:00:00 2001 From: Yves Rizoud Date: Mon, 21 Sep 2009 14:16:41 +0000 Subject: [PATCH] First svn commit of the experiment with layers. Preemptive backup (to shorten delay before drawing) is currently disabled. Basic Undo/Redo works, but not image resizing, the adjust tool, and saving (not even flattened) git-svn-id: svn://pulkomandy.tk/GrafX2/branches/layers@1039 416bcca6-2ee7-4201-b75f-2eb2f807beb1 --- brush.c | 7 +- buttons.c | 93 +++++-- buttons.h | 2 + const.h | 2 +- engine.c | 24 +- global.h | 18 +- graph.c | 16 +- loadsave.c | 4 +- main.c | 18 +- misc.c | 38 ++- misc.h | 3 +- operatio.c | 3 +- pages.c | 789 ++++++++++++++++++++--------------------------------- pages.h | 24 +- struct.h | 20 +- 15 files changed, 489 insertions(+), 572 deletions(-) diff --git a/brush.c b/brush.c index c6fe95ca..c3032587 100644 --- a/brush.c +++ b/brush.c @@ -728,8 +728,7 @@ void Capture_brush(short start_x,short start_y,short end_x,short end_y,short cle for (y_pos=start_y;y_posNb_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); @@ -2540,33 +2542,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; } @@ -4254,6 +4256,45 @@ void Transparency_set(byte amount) Compute_colorize_table(); } +void Layer_activate(short layer, short side) +{ + if (side == RIGHT_SIDE) + { + // Right-click on current layer + if (Main_current_layer == layer) + { + if (Main_layers_visible == (1<Pages); + //Download_infos_backup(Main_backups); + Display_all_screen(); + Display_cursor(); +} + //---------------------------- Courbes de Bézier ---------------------------- void Button_Curves(void) @@ -5442,9 +5483,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(); @@ -5752,10 +5795,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; y=0) && (y_pos>=0)) //Toujours vrai ? if ((x_posPages->Image[depth] + x+y*Main_image_width); + // return *(Main_screen+y*Main_image_width+x); } -void Pixel_in_current_screen (word x,word y,byte color) +void Pixel_in_current_screen (word x,word y,byte color,int with_preview) { - byte* dest=(x+y*Main_image_width+Main_screen); - *dest=color; + 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 == 0) // transparent + // 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); + } +} + +byte Read_pixel_from_current_layer(word x,word y) +{ + return *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer]); } void Replace_a_color(byte old_color, byte New_color) @@ -346,9 +370,9 @@ void Remap_general_lowlevel(byte * conversion_table,byte * buffer,short width,sh void Copy_image_to_brush(short start_x,short start_y,short Brush_width,short Brush_height,word image_width) { - byte* src=start_y*image_width+start_x+Main_screen; //Adr départ image (ESI) - byte* dest=Brush; //Adr dest brosse (EDI) - int dx; + byte* src=start_y*image_width+start_x+Main_backups->Pages->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 diff --git a/misc.h b/misc.h index a4396e02..e0a63fba 100644 --- a/misc.h +++ b/misc.h @@ -36,13 +36,14 @@ 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_current_screen (word x,word y,byte color,int with_preview); 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); byte Read_pixel_from_brush (word x,word y); +byte Read_pixel_from_current_layer(word x,word y); void Ellipse_compute_limites(short horizontal_radius,short vertical_radius); // Calcule les valeurs suivantes en fonction des deux paramètres: diff --git a/operatio.c b/operatio.c index c30a1f1b..ee6a8f54 100644 --- a/operatio.c +++ b/operatio.c @@ -3004,7 +3004,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(); } diff --git a/pages.c b/pages.c index a52c9c22..e1598f94 100644 --- a/pages.c +++ b/pages.c @@ -35,13 +35,20 @@ /// 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 word Last_backed_up_layers=0; +/// Allocate and initialize a new page. +T_Page * New_page(void) +{ + T_Page * page; + + page = (T_Page *)malloc(sizeof(T_Page)); 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)); @@ -50,6 +57,7 @@ void Init_page(T_Page * page) page->Filename[0]='\0'; page->File_format=DEFAULT_FILEFORMAT; } + return page; } void Download_infos_page_main(T_Page * page) @@ -57,13 +65,13 @@ void Download_infos_page_main(T_Page * page) { //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_screen=Visible_image[0].Image; Main_image_width=page->Width; Main_image_height=page->Height; memcpy(Main_palette,page->Palette,sizeof(T_Palette)); @@ -81,7 +89,57 @@ void Download_infos_page_main(T_Page * page) Compute_limits(); Compute_paintbrush_coordinates(); } + } + //Update_visible_page_buffer(Visible_image_index, page->Width, page->Height); + //memcpy(Main_screen, page->Image[Main_current_layer], page->Width*page->Height); + +} + +void Redraw_layered_image(void) +{ + // Re-construct the image with the visible layers + int layer; + // First layer + for (layer=0; layerPages->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->Image[layer]+i); + if (color != 0) /* transp color */ + { + *(Visible_image[0].Image+i) = color; + if (layer != Main_current_layer) + *(Visible_image_depth_buffer.Image+i) = layer; + } + } + } + } + Main_screen=Visible_image[0].Image; + Download_infos_backup(Main_backups); } void Upload_infos_page_main(T_Page * page) @@ -89,7 +147,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)); @@ -104,7 +162,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)); @@ -119,7 +177,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)); @@ -132,21 +190,26 @@ void Upload_infos_page_spare(T_Page * page) void Download_infos_backup(T_List_of_pages * list) { - Screen_backup=list->Pages[1].Image; + Screen_backup=Visible_image[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]; + // Visible_image[0].Image; else - FX_feedback_screen=list->Pages[1].Image; + FX_feedback_screen=list->Pages->Next->Image[Main_current_layer]; + // Visible_image[1].Image; } -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; iImage[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. @@ -157,15 +220,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 @@ -177,62 +231,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(); + 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) { @@ -252,29 +274,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) @@ -294,242 +313,96 @@ 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) +int Create_new_page(T_Page * new_page,T_List_of_pages * list) { // 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) - { - // 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; - 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); - } - } + int i; + for (i=0; iImage[i]=(byte *)malloc(new_page->Height*new_page->Width); } - // 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 @@ -540,82 +413,96 @@ void Free_page_of_a_list(T_List_of_pages * list) } } +int Update_visible_page_buffer(int index, int width, int height) +{ + if (Visible_image[index].Width != width || Visible_image[index].Height != height) + { + Visible_image[index].Width = width; + Visible_image[index].Height = height; + free(Visible_image[index].Image); + Visible_image[index].Image = (byte *)malloc(width * height); + if (Visible_image[index].Image == NULL) + return 0; + } + return 1; +} + +void Advance_visible_page_buffer() +{ + +} /// /// GESTION DES BACKUPS /// -int Init_all_backup_lists(int size,int width,int height) +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,size) && - Allocate_list_of_pages(Spare_backups,size)) + if (Allocate_list_of_pages(Main_backups) && + Allocate_list_of_pages(Spare_backups)) { // 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); + Upload_infos_page_main(Main_backups->Pages); // 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)) + Main_backups->Pages->Width=width; + Main_backups->Pages->Height=height; + for (i=0; iPages->Image[i]=(byte *)malloc(width*height); + } - // 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)) + if (!Update_visible_page_buffer(0, width, height)) + return 0; + if (!Update_visible_page_buffer(1, width, height)) + return 0; + + Main_screen=Visible_image[0].Image; + + Download_infos_page_main(Main_backups->Pages); + 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. + Download_infos_page_spare(Spare_backups->Pages); + + // 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); + + Visible_image[0].Width = width; + Visible_image[0].Height = height; + Visible_image[0].Image = NULL; + + Visible_image[1].Width = width; + Visible_image[1].Height = height; + Visible_image[1].Image = NULL; + + Visible_image_depth_buffer.Width = width; + Visible_image_depth_buffer.Height = height; + Visible_image_depth_buffer.Image = NULL; + + Visible_image[0].Image = (byte *)malloc(Visible_image[0].Width * Visible_image[0].Height); + if (Visible_image[0].Image) + { + Visible_image[1].Image = (byte *)malloc(Visible_image[1].Width * Visible_image[1].Height); + if (Visible_image[1].Image) { - // 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; + Visible_image_depth_buffer.Image = (byte *)malloc(Visible_image_depth_buffer.Width * Visible_image_depth_buffer.Height); + if (Visible_image_depth_buffer.Image) + { + End_of_modification(); + return 1; + } } } - 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; - } } - else - { - // On n'a même pas réussi à créer les listes. Donc c'est même pas la - // peine de continuer : l'utilisateur ne pourra jamais rien faire, - // autant avorter le chargement du programme. - return_code=0; - } - - return return_code; + return 0; } void Set_number_of_backups(int nb_backups) @@ -642,15 +529,18 @@ 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(); + 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)) { - Create_new_page(new_page,Main_backups,Spare_backups); Download_infos_page_main(new_page); Download_infos_backup(Main_backups); // On nettoie la nouvelle image: @@ -677,15 +567,18 @@ int Backup_and_resize_the_spare(int width,int height) Upload_infos_page_spare(Spare_backups->Pages); // 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(); + 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)) { - Create_new_page(new_page,Spare_backups,Main_backups); Download_infos_page_spare(new_page); return_code=1; } @@ -700,50 +593,56 @@ 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 + int i; + + T_Page *new_page; + + /* + if (Last_backed_up_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(); + 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); 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->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); @@ -758,10 +657,17 @@ 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. + 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); @@ -776,6 +682,8 @@ 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. + Redraw_layered_image(); + } void Free_current_page(void) @@ -822,115 +730,22 @@ void Exchange_main_and_spare(void) 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; + Redraw_layered_image(); } void End_of_modification(void) { +/* + Update_visible_page_buffer(1, Main_image_width, Main_image_height); + memcpy(Visible_image[1].Image, + Visible_image[0].Image, + Main_image_width*Main_image_height); + + Main_screen=Visible_image[0].Image; + + Download_infos_backup(Main_backups); + + Last_backed_up_layers = 0; + Backup(); + */ } diff --git a/pages.h b/pages.h index 8a6c8ca4..e715d2e9 100644 --- a/pages.h +++ b/pages.h @@ -39,13 +39,12 @@ void Download_infos_page_main(T_Page * page); void Upload_infos_page_main(T_Page * page); // private -void Init_page(T_Page * page); +T_Page * New_page(void); 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); @@ -55,14 +54,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); void Change_page_number_of_list(T_List_of_pages * list,int number); void Free_page_of_a_list(T_List_of_pages * list); @@ -72,7 +68,7 @@ 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_and_resize_the_spare(int width,int height); @@ -83,14 +79,4 @@ void Free_current_page(void); // 'Kill' button void Exchange_main_and_spare(void); void End_of_modification(void); - -/// -/// BORROWING MEMORY FROM PAGE -/// - -void * Borrow_memory_from_page(int size); -// private -int Can_borrow_memory_from_page(int size); - - #endif diff --git a/struct.h b/struct.h index 7ffed0b4..4b62483f 100644 --- a/struct.h +++ b/struct.h @@ -319,9 +319,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. @@ -331,17 +330,27 @@ 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; + struct T_Page *Prev; + byte * Image[NB_LAYERS];///< Pixel data for the image. } 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. + //int Users; ///< Number of references. + byte * Image; ///< Pixel data for the image. +} T_Image; + /// A single memorized brush from the Brush Container typedef struct { @@ -355,7 +364,6 @@ typedef struct byte Transp_color; } T_Brush_template; - /// GUI skin data typedef struct {