Animation now has correct visual display and input feedback. Loading GIF guesses it's an anim if it loops, layers otherwise : Some rare non-looping GIF anims (usaully broken) will be misunderstood as layered. Editing of anims and layers seems flawless and stable. Still requires an auto-switch to the best toolbar (anim/layers) when relevant, but you can already switch manually.

git-svn-id: svn://pulkomandy.tk/GrafX2/trunk@1910 416bcca6-2ee7-4201-b75f-2eb2f807beb1
This commit is contained in:
Yves Rizoud 2012-03-02 23:19:47 +00:00
parent db50b4ebdc
commit e73eb2786a
8 changed files with 358 additions and 335 deletions

View File

@ -548,11 +548,23 @@ void Button_Toggle_toolbar(void)
case 0: // tools
Set_bar_visibility(MENUBAR_TOOLS, !Menu_bars[MENUBAR_TOOLS].Visible);
break;
case 1: // anim
case 1: // layers
if (Menu_bars[MENUBAR_ANIMATION].Visible && !Menu_bars[MENUBAR_LAYERS].Visible)
Set_bar_visibility(MENUBAR_ANIMATION, 0);
Set_bar_visibility(MENUBAR_LAYERS, !Menu_bars[MENUBAR_LAYERS].Visible);
if (Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// Exceptionally, this doesn't require a backup because a single-layer
// image is the same as a single-frame animation.
Main_backups->Pages->Image_mode = IMAGE_MODE_LAYERED;
}
break;
case 2: // anim
if (Menu_bars[MENUBAR_LAYERS].Visible && !Menu_bars[MENUBAR_ANIMATION].Visible)
Set_bar_visibility(MENUBAR_LAYERS, 0);
Set_bar_visibility(MENUBAR_ANIMATION, !Menu_bars[MENUBAR_ANIMATION].Visible);
if (Main_backups->Pages->Image_mode == IMAGE_MODE_LAYERED)
{
// Exceptionally, this doesn't require a backup because a single-frame
@ -560,18 +572,6 @@ void Button_Toggle_toolbar(void)
Main_backups->Pages->Image_mode = IMAGE_MODE_ANIMATION;
}
break;
case 2: // layers
if (Menu_bars[MENUBAR_LAYERS].Visible && !Menu_bars[MENUBAR_ANIMATION].Visible)
Set_bar_visibility(MENUBAR_LAYERS, 0);
Set_bar_visibility(MENUBAR_ANIMATION, !Menu_bars[MENUBAR_ANIMATION].Visible);
if (Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// Exceptionally, this doesn't require a backup because a single-layer
// image is the same as a single-frame animation.
Main_backups->Pages->Image_mode = IMAGE_MODE_LAYERED;
}
break;
}
}

View File

@ -70,11 +70,8 @@
/// Character to display in menus for an ellipsis.
#define ELLIPSIS_CHARACTER '…'
#define NB_LAYERS 1 ///< Initial number of layers for a new image
#ifdef NOLAYERS
#define MAX_NB_LAYERS 999 ///< Maximum number of layers that can be used in grafx2. Note that 32 is upper limit because of a few bit fields.
#else
#define MAX_NB_FRAMES 999 ///< Maximum number of frames that can be used in a grafx2 animation.
#define MAX_NB_LAYERS 16 ///< Maximum number of layers that can be used in grafx2. Note that 32 is upper limit because of a few bit fields.
#endif
#define BRUSH_CONTAINER_PREVIEW_WIDTH 16 ///< Size for preview of a brush in Brush container
#define BRUSH_CONTAINER_PREVIEW_HEIGHT 16 ///< Size for preview of a brush in Brush container
#define BRUSH_CONTAINER_COLUMNS 4 ///< Number of columns in the Brush container

View File

@ -60,7 +60,6 @@
#include "struct.h"
#include "io.h"
#include "windows.h" // Best_color()
#include "pages.h" // Add_layer()
//////////////////////////////////// IMG ////////////////////////////////////
@ -2025,7 +2024,13 @@ void Load_GIF(T_IO_Context * context)
else if (!memcmp(aeb,"NETSCAPE2.0",0x0B))
{
// The well-known Netscape extension.
// Nothing to do, just skip sub-block
// Load as an animation
if (context->Type == CONTEXT_MAIN_IMAGE)
{
Main_backups->Pages->Image_mode = IMAGE_MODE_ANIMATION;
Update_screen_targets();
}
// Skip sub-block
do
{
if (! Read_byte(GIF_file,&size_to_read))
@ -2139,8 +2144,7 @@ void Load_GIF(T_IO_Context * context)
// Attempt to add a layer to current image
current_layer++;
Set_loading_layer(context, current_layer);
#ifdef NOLAYERS
if (context->Type == CONTEXT_MAIN_IMAGE)
if (context->Type == CONTEXT_MAIN_IMAGE && Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
if (is_transparent)
// Copy the content of previous layer, in case of loading a GIF
@ -2155,7 +2159,6 @@ void Load_GIF(T_IO_Context * context)
LSDB.Backcol,
Main_backups->Pages->Width*Main_backups->Pages->Height);
}
#endif
}
else
{
@ -2165,7 +2168,6 @@ void Load_GIF(T_IO_Context * context)
LSDB.Backcol,
Main_backups->Pages->Width*Main_backups->Pages->Height);
}
// Duration was set in the previously loaded GCE
Set_frame_duration(context, last_delay*10);
number_LID++;
@ -2497,10 +2499,9 @@ void Save_GIF(T_IO_Context * context)
// Write_bytes(GIF_file,"\x21\xFF\x0BNETSCAPE2.0\x03\xLL\xSS\xSS\x00",19);
// LL : 01 to loop
// SSSS : number of loops
#ifdef NOLAYERS
if (context->Type == CONTEXT_MAIN_IMAGE && Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
if (context->Nb_layers>1)
Write_bytes(GIF_file,"\x21\xFF\x0BNETSCAPE2.0\x03\x01\x00\x00\x00",19);
#endif
// Ecriture du commentaire
if (context->Comment[0])
@ -2543,10 +2544,16 @@ void Save_GIF(T_IO_Context * context)
GCE.Block_identifier = 0x21;
GCE.Function = 0xF9;
GCE.Block_size=4;
#ifdef NOLAYERS
if (context->Type == CONTEXT_MAIN_IMAGE && Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// Animation frame
GCE.Packed_fields=(2<<2)|(context->Background_transparent);
GCE.Delay_time=Get_frame_duration(context)/10;
#else
}
else
{
// Layered image
if (current_layer==0)
GCE.Packed_fields=(1<<2)|(context->Background_transparent);
else
@ -2554,7 +2561,7 @@ void Save_GIF(T_IO_Context * context)
GCE.Delay_time=5; // Duration 5/100s (minimum viable value for current web browsers)
if (current_layer == context->Nb_layers -1)
GCE.Delay_time=0xFFFF; // Infinity (10 minutes)
#endif
}
GCE.Transparent_color=context->Transparent_color;
GCE.Block_terminator=0x00;

View File

@ -3005,10 +3005,15 @@ void Redraw_grid(short x, short y, unsigned short w, unsigned short h)
byte Read_pixel_from_current_screen (word x,word y)
{
#ifndef NOLAYERS
byte depth;
byte color;
if (Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
return *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer].Pixels);
}
if (Main_backups->Pages->Image_mode == IMAGE_MODE_MODE5)
if (Main_current_layer==4)
return *(Main_backups->Pages->Image[Main_current_layer].Pixels + x+y*Main_image_width);
@ -3019,26 +3024,21 @@ byte Read_pixel_from_current_screen (word x,word y)
depth = *(Main_visible_image_depth_buffer.Image+x+y*Main_image_width);
return *(Main_backups->Pages->Image[depth].Pixels + x+y*Main_image_width);
#else
return *((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer].Pixels);
#endif
}
/// Paint a a single pixel in image only : as-is.
void Pixel_in_screen_direct(word x,word y,byte color)
{
*((y)*Main_image_width+(x)+/*Main_backups->Pages->Image[Main_current_layer].Pixels*/Main_screen)=color;
*((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer].Pixels)=color;
}
/// Paint a a single pixel in image and on screen: as-is.
void Pixel_in_screen_direct_with_preview(word x,word y,byte color)
{
*((y)*Main_image_width+(x)+/*Main_backups->Pages->Image[Main_current_layer].Pixels*/Main_screen)=color;
*((y)*Main_image_width+(x)+Main_backups->Pages->Image[Main_current_layer].Pixels)=color;
Pixel_preview(x,y,color);
}
#ifndef NOLAYERS
/// Paint a a single pixel in image only : using layered display.
void Pixel_in_screen_layered(word x,word y,byte color)
{
@ -3140,7 +3140,6 @@ void Pixel_in_screen_overlay_with_preview(word x,word y,byte color)
Pixel_preview(x,y,color);
}
}
#endif
Func_pixel Pixel_in_current_screen=Pixel_in_screen_direct;
Func_pixel Pixel_in_current_screen_with_preview=Pixel_in_screen_direct_with_preview;
@ -3162,7 +3161,13 @@ byte Read_pixel_from_current_layer(word x,word y)
void Update_pixel_renderer(void)
{
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// direct
Pixel_in_current_screen = Pixel_in_screen_direct;
Pixel_in_current_screen_with_preview = Pixel_in_screen_direct_with_preview;
}
else
if (Main_backups->Pages->Image_mode == IMAGE_MODE_LAYERED)
{
// layered
@ -3188,9 +3193,4 @@ void Update_pixel_renderer(void)
Pixel_in_current_screen = Pixel_in_screen_layered;
Pixel_in_current_screen_with_preview = Pixel_in_screen_layered_with_preview;
}
#else
// direct
Pixel_in_current_screen = Pixel_in_screen_direct;
Pixel_in_current_screen_with_preview = Pixel_in_screen_direct_with_preview;
#endif
}

View File

@ -41,8 +41,8 @@ void Layer_activate(int layer, short side)
// Keep a copy of which layers were visible
old_layers = Main_layers_visible;
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
if (side == RIGHT_SIDE)
{
// Right-click on current layer
@ -71,8 +71,10 @@ void Layer_activate(int layer, short side)
Main_current_layer = layer;
Main_layers_visible |= 1<<layer;
}
#else
// Handler for limited layers support: only allow one visible at a time
}
else
{
// Only allow one visible at a time
if (side == LEFT_SIDE)
{
Main_current_layer = layer;
@ -80,7 +82,7 @@ void Layer_activate(int layer, short side)
Update_screen_targets();
}
#endif
}
Hide_cursor();
if (Main_layers_visible != old_layers)
@ -97,26 +99,31 @@ void Layer_activate(int layer, short side)
void Button_Layer_add(void)
{
int max[] = {MAX_NB_LAYERS, MAX_NB_FRAMES, 5};
Hide_cursor();
if (Main_backups->Pages->Nb_layers < MAX_NB_LAYERS)
if (Main_backups->Pages->Nb_layers < max[Main_backups->Pages->Image_mode])
{
// Backup with unchanged layers
Backup_layers(0);
if (!Add_layer(Main_backups,Main_current_layer+1))
{
#ifdef NOLAYERS
if (Main_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
// Make a copy of current image, so the display is unchanged
memcpy(
Main_backups->Pages->Image[Main_current_layer].Pixels,
Main_backups->Pages->Image[Main_current_layer-1].Pixels,
Main_backups->Pages->Width*Main_backups->Pages->Height);
#else
}
else
{
Update_depth_buffer();
// I just noticed this might be unneeded, since the new layer
// is transparent, it shouldn't have any visible effect.
Display_all_screen();
#endif
}
Display_layerbar();
End_of_modification();
}

View File

@ -303,12 +303,14 @@ byte Read_pixel_from_spare_screen(word x,word y)
// (can be a bigger or smaller image)
if (x>=Spare_image_width || y>=Spare_image_height)
return Spare_backups->Pages->Transparent_color;
#ifndef NOLAYERS
return *(Spare_visible_image.Image + y*Spare_image_width + x);
#else
if (Spare_backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
{
return *(Spare_backups->Pages->Image[Spare_current_layer].Pixels + y*Spare_image_width + x);
#endif
}
else
{
return *(Spare_visible_image.Image + y*Spare_image_width + x);
}
}
void Rotate_90_deg_lowlevel(byte * source, byte * dest, short width, short height)

View File

@ -2785,13 +2785,14 @@ void Scroll_12_0(void)
else
{
Backup_layers(-1); // Main_layers_visible
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// Ensure the backup visible image is up-to-date
// (after swapping some layers on/off, it gets outdated)
memcpy(Main_visible_image_backup.Image,
Main_visible_image.Image,
Main_image_width*Main_image_height);
#endif
}
}
Update_screen_targets();

View File

@ -38,12 +38,10 @@
// -- Layers data
/// Array of two images, that contains the "flattened" version of the visible layers.
#ifndef NOLAYERS
T_Bitmap Main_visible_image;
T_Bitmap Main_visible_image_backup;
T_Bitmap Main_visible_image_depth_buffer;
T_Bitmap Spare_visible_image;
#endif
///
/// GESTION DES PAGES
@ -200,7 +198,8 @@ void Download_infos_page_main(T_Page * page)
void Redraw_layered_image(void)
{
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// Re-construct the image with the visible layers
byte layer=0;
// First layer
@ -263,15 +262,18 @@ void Redraw_layered_image(void)
}
}
}
#else
}
else
{
Update_screen_targets();
#endif
}
Update_FX_feedback(Config.FX_Feedback);
}
void Update_depth_buffer(void)
{
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// 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)
@ -312,13 +314,14 @@ void Update_depth_buffer(void)
}
}
}
#endif
}
Update_FX_feedback(Config.FX_Feedback);
}
void Redraw_spare_image(void)
{
#ifndef NOLAYERS
if (Spare_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// Re-construct the image with the visible layers
byte layer;
// First layer
@ -359,12 +362,13 @@ void Redraw_spare_image(void)
}
}
}
#endif
}
}
void Redraw_current_layer(void)
{
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
int i;
for (i=0; i<Main_image_width*Main_image_height; i++)
{
@ -382,7 +386,7 @@ void Redraw_current_layer(void)
}
}
}
#endif
}
}
void Upload_infos_page_main(T_Page * page)
@ -672,24 +676,24 @@ void Free_page_of_a_list(T_List_of_pages * list)
void Update_screen_targets(void)
{
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
Main_screen=Main_visible_image.Image;
Screen_backup=Main_visible_image_backup.Image;
#else
}
else
{
Main_screen=Main_backups->Pages->Image[Main_current_layer].Pixels;
Screen_backup=Main_backups->Pages->Next->Image[Main_current_layer].Pixels;
#endif
}
Update_pixel_renderer();
}
/// Update all the special image buffers, if necessary.
int Update_buffers(int width, int height)
{
#ifdef NOLAYERS
// unused args
(void) width;
(void) height;
#else
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// At least one dimension is different
if (Main_visible_image.Width*Main_visible_image.Height != width*height)
{
@ -723,19 +727,15 @@ int Update_buffers(int width, int height)
}
Main_visible_image_depth_buffer.Width = width;
Main_visible_image_depth_buffer.Height = height;
#endif
}
Update_screen_targets();
return 1;
}
/// Update all the special image buffers of the spare page, if necessary.
int Update_spare_buffers(int width, int height)
{
#ifdef NOLAYERS
// unused args
(void) width;
(void) height;
#else
if (Spare_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// At least one dimension is different
if (Spare_visible_image.Width*Spare_visible_image.Height != width*height)
{
@ -748,7 +748,7 @@ int Update_spare_buffers(int width, int height)
Spare_visible_image.Width = width;
Spare_visible_image.Height = height;
#endif
}
return 1;
}
@ -783,7 +783,6 @@ int Init_all_backup_lists(int width,int height)
return 0;
memset(Main_backups->Pages->Image[i].Pixels, 0, width*height);
}
#ifndef NOLAYERS
Main_visible_image.Width = 0;
Main_visible_image.Height = 0;
Main_visible_image.Image = NULL;
@ -793,20 +792,17 @@ int Init_all_backup_lists(int width,int height)
Spare_visible_image.Height = 0;
Spare_visible_image.Image = NULL;
#endif
if (!Update_buffers(width, height))
return 0;
if (!Update_spare_buffers(width, height))
return 0;
#ifndef NOLAYERS
// For speed, instead of Redraw_layered_image() we'll directly set the buffers.
memset(Main_visible_image.Image, 0, width*height);
memset(Main_visible_image_backup.Image, 0, width*height);
memset(Main_visible_image_depth_buffer.Image, 0, width*height);
memset(Spare_visible_image.Image, 0, width*height);
#endif
Download_infos_page_main(Main_backups->Pages);
Update_FX_feedback(Config.FX_Feedback);
@ -872,6 +868,7 @@ int Backup_new_image(int layers,int width,int height)
}
Update_buffers(width, height);
memset(Main_visible_image_depth_buffer.Image, 0, width*height);
Download_infos_page_main(Main_backups->Pages);
@ -926,13 +923,21 @@ int Backup_with_new_dimensions(int width,int height)
// Same code as in End_of_modification(),
// Without saving a safety backup:
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
memcpy(Main_visible_image_backup.Image,
Main_visible_image.Image,
Main_image_width*Main_image_height);
#else
}
else
{
// Clear the depth buffer anyway, because we may use it later
memset(Main_visible_image_depth_buffer.Image, 0,
Main_image_width*Main_image_height);
Update_screen_targets();
#endif
}
Update_FX_feedback(Config.FX_Feedback);
// --
@ -989,7 +994,8 @@ int Backup_in_place(int width,int height)
// The following is part of Update_buffers()
// (without changing the backup buffer)
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// At least one dimension is different
if (Main_visible_image.Width*Main_visible_image.Height != width*height)
{
@ -1013,7 +1019,7 @@ int Backup_in_place(int width,int height)
Main_visible_image_depth_buffer.Width = width;
Main_visible_image_depth_buffer.Height = height;
#endif
}
Update_screen_targets();
return 1;
@ -1278,7 +1284,8 @@ void End_of_modification(void)
//Update_buffers(Main_image_width, Main_image_height);
#ifndef NOLAYERS
if (Main_backups->Pages->Image_mode != IMAGE_MODE_ANIMATION)
{
// Backup buffer can have "wrong" size if a Lua script
// performs a resize.
Update_buffers(Main_image_width, Main_image_height);
@ -1287,10 +1294,11 @@ void End_of_modification(void)
memcpy(Main_visible_image_backup.Image,
Main_visible_image.Image,
Main_image_width*Main_image_height);
#else
}
else
{
Update_screen_targets();
#endif
}
Update_FX_feedback(Config.FX_Feedback);
/*
Last_backed_up_layers = 0;
@ -1306,6 +1314,7 @@ void End_of_modification(void)
/// Add a new layer to latest page of a list. Returns 0 on success.
byte Add_layer(T_List_of_pages *list, int layer)
{
int max[] = {MAX_NB_LAYERS, MAX_NB_FRAMES, 5};
T_Page * source_page;
T_Page * new_page;
byte * new_image;
@ -1313,7 +1322,7 @@ byte Add_layer(T_List_of_pages *list, int layer)
source_page = list->Pages;
if (list->Pages->Nb_layers == MAX_NB_LAYERS)
if (list->Pages->Nb_layers >= max[list->Pages->Image_mode]) // MAX_NB_LAYERS
return 1;
// Keep the position reasonable