Implement a more generic "rasters" mode for CPC
This is similar to mode 5, but with less constraints: - All inks can have split rasters - Split rasters have a min width of 32 pixels Note that this is nowhere near enough to make sure the image will be renderable (number of colors should be limited, unless there is a span without rasters allowing to reload registers with new colors, and moving from one pen to another also has a cost). The goal is to offer no over-restriction (everything that can be shown on the real machine is drawable). Additional constraints may be checked from a lua script that will generate the code and data for displaying the picture.
This commit is contained in:
parent
1d71a0759f
commit
9d46d1e90f
143
src/brush.c
143
src/brush.c
@ -120,7 +120,8 @@ void Display_paintbrush(short x,short y,byte color)
|
|||||||
if (Mouse_K) // pas de curseur si on est en preview et
|
if (Mouse_K) // pas de curseur si on est en preview et
|
||||||
return; // en train de cliquer
|
return; // en train de cliquer
|
||||||
|
|
||||||
if (Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5 && Main.current_layer < 4)
|
if ((Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5
|
||||||
|
|| Main.backups->Pages->Image_mode == IMAGE_MODE_RASTER) && Main.current_layer < 4)
|
||||||
{
|
{
|
||||||
goto single_pixel;
|
goto single_pixel;
|
||||||
}
|
}
|
||||||
@ -284,7 +285,8 @@ void Draw_paintbrush(short x,short y,byte color)
|
|||||||
int position;
|
int position;
|
||||||
byte old_color;
|
byte old_color;
|
||||||
|
|
||||||
if (Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5 && Main.current_layer < 4)
|
if ((Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5
|
||||||
|
|| Main.backups->Pages->Image_mode == IMAGE_MODE_RASTER) && Main.current_layer < 4)
|
||||||
{
|
{
|
||||||
// Flood-fill the enclosing area
|
// Flood-fill the enclosing area
|
||||||
if (x<Main.image_width && y<Main.image_height && x>= 0 && y >= 0
|
if (x<Main.image_width && y<Main.image_height && x>= 0 && y >= 0
|
||||||
@ -296,40 +298,109 @@ void Draw_paintbrush(short x,short y,byte color)
|
|||||||
short min_x,width,min_y,height;
|
short min_x,width,min_y,height;
|
||||||
short xx,yy;
|
short xx,yy;
|
||||||
|
|
||||||
// determine area
|
if (Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5)
|
||||||
switch(Main.current_layer)
|
{
|
||||||
{
|
// determine area
|
||||||
case 0:
|
switch(Main.current_layer)
|
||||||
default:
|
{
|
||||||
// Full layer
|
case 0:
|
||||||
min_x=0;
|
default:
|
||||||
min_y=0;
|
// Full layer
|
||||||
width=Main.image_width;
|
min_x=0;
|
||||||
height=Main.image_height;
|
min_y=0;
|
||||||
break;
|
width=Main.image_width;
|
||||||
case 1:
|
height=Main.image_height;
|
||||||
case 2:
|
break;
|
||||||
// Line
|
case 1:
|
||||||
min_x=0;
|
case 2:
|
||||||
min_y=y;
|
// Line
|
||||||
width=Main.image_width;
|
min_x=0;
|
||||||
height=1;
|
min_y=y;
|
||||||
break;
|
width=Main.image_width;
|
||||||
case 3:
|
height=1;
|
||||||
// Segment
|
break;
|
||||||
min_x=x / 48 * 48;
|
case 3:
|
||||||
min_y=y;
|
// Segment
|
||||||
width=48;
|
min_x=x / 48 * 48;
|
||||||
height=1;
|
min_y=y;
|
||||||
break;
|
width=48;
|
||||||
//case 4:
|
height=1;
|
||||||
// // 8x8
|
break;
|
||||||
// min_x=x / 8 * 8;
|
}
|
||||||
// min_y=y / 8 * 8;
|
} else {
|
||||||
// width=8;
|
int prev_x;
|
||||||
// height=8;
|
// Raster mode
|
||||||
// break;
|
// No matter what, you can always edit only 1 line at a time here, and you will always
|
||||||
}
|
// draw on "nops" boundaries (8 pixels in mode 1)
|
||||||
|
height=1;
|
||||||
|
min_y=y;
|
||||||
|
min_x=x / 8 * 8;
|
||||||
|
width = 8;
|
||||||
|
|
||||||
|
// ????????
|
||||||
|
// ^
|
||||||
|
// A
|
||||||
|
|
||||||
|
// First look for the previous span to see if it is the same color
|
||||||
|
prev_x = min_x - 8;
|
||||||
|
|
||||||
|
old_color = Read_pixel_from_current_screen(prev_x, y);
|
||||||
|
|
||||||
|
if (old_color == color) {
|
||||||
|
// aaaA????
|
||||||
|
// ^
|
||||||
|
// A
|
||||||
|
// We are just making the previous span larger
|
||||||
|
width = 8;
|
||||||
|
while ((min_x >= 0) && (width < 32)
|
||||||
|
&& (Read_pixel_from_current_screen(min_x, y) == color))
|
||||||
|
{
|
||||||
|
min_x -= 8;
|
||||||
|
width += 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ???B????
|
||||||
|
// ^
|
||||||
|
// A
|
||||||
|
// We are creating a new span. We need to check if the previous span is still large
|
||||||
|
// enough to be allowed. If it is less than 32 pixels, there is no way to render it
|
||||||
|
// on the real hardware (this is the time the OUT instruction to change the palette
|
||||||
|
// needs)
|
||||||
|
while ((min_x - prev_x < 48)
|
||||||
|
&& (prev_x <= 0 || old_color == Read_pixel_from_current_screen(prev_x, y)))
|
||||||
|
{
|
||||||
|
prev_x -= 8;
|
||||||
|
}
|
||||||
|
prev_x += 8;
|
||||||
|
|
||||||
|
// CBBB????
|
||||||
|
// p ^
|
||||||
|
// A
|
||||||
|
// The previous span is too small, so eat it
|
||||||
|
if (min_x - prev_x < 32) {
|
||||||
|
width += min_x - prev_x;
|
||||||
|
min_x = prev_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width < 32)
|
||||||
|
width = 32;
|
||||||
|
}
|
||||||
|
// Now, we also need to check if the next span is still large enough, as we are going to
|
||||||
|
// remove 8 pixels from it. If it is not, we just replace it with the current color and
|
||||||
|
// let the user figure out how to reinsert it without breaking everything.
|
||||||
|
prev_x = min_x + width;
|
||||||
|
old_color = Read_pixel_from_current_screen(prev_x, y);
|
||||||
|
if (old_color != color) {
|
||||||
|
while ((prev_x - (min_x + width) < 40)
|
||||||
|
&& (prev_x > Main.image_width || old_color == Read_pixel_from_current_screen(prev_x, y)))
|
||||||
|
{
|
||||||
|
prev_x += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_x - (min_x + width) < 32)
|
||||||
|
width = prev_x - min_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Clip the bottom edge.
|
// Clip the bottom edge.
|
||||||
// (Necessary if image height is not a multiple)
|
// (Necessary if image height is not a multiple)
|
||||||
if (min_y+height>=Main.image_height)
|
if (min_y+height>=Main.image_height)
|
||||||
|
|||||||
@ -180,19 +180,26 @@ void Button_Constraint_mode(void)
|
|||||||
if (Selected_Constraint_Mode <= IMAGE_MODE_ANIMATION)
|
if (Selected_Constraint_Mode <= IMAGE_MODE_ANIMATION)
|
||||||
Selected_Constraint_Mode = IMAGE_MODE_EGX;
|
Selected_Constraint_Mode = IMAGE_MODE_EGX;
|
||||||
|
|
||||||
if (Selected_Constraint_Mode == IMAGE_MODE_MODE5)
|
if (Selected_Constraint_Mode == IMAGE_MODE_MODE5 && (Main.image_width%48))
|
||||||
{
|
{
|
||||||
|
Verbose_message("Error!", "Emulation of Amstrad CPC's Mode5 can only be used on an image whose width is a multiple of 48.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Selected_Constraint_Mode == IMAGE_MODE_MODE5 || Selected_Constraint_Mode == IMAGE_MODE_RASTER)
|
||||||
|
{
|
||||||
|
/* TODO it would be great to auto-create extra layers */
|
||||||
if (Main.backups->Pages->Image_mode != IMAGE_MODE_LAYERED ||
|
if (Main.backups->Pages->Image_mode != IMAGE_MODE_LAYERED ||
|
||||||
Main.backups->Pages->Nb_layers!=5 || (Main.image_width%48))
|
Main.backups->Pages->Nb_layers!=5)
|
||||||
{
|
{
|
||||||
Verbose_message("Error!", "This emulation of Amstrad CPC's Mode5 can only be used on a 5-layer image whose width is a multiple of 48.");
|
Verbose_message("Error!", "Emulation of Amstrad CPC's rasters requires a 5-layer image.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (pixel=0; pixel < Main.image_width*Main.image_height; pixel++)
|
for (pixel=0; pixel < Main.image_width*Main.image_height; pixel++)
|
||||||
{
|
{
|
||||||
if (Main.backups->Pages->Image[4].Pixels[pixel]>3)
|
if (Main.backups->Pages->Image[4].Pixels[pixel]>3)
|
||||||
{
|
{
|
||||||
Verbose_message("Error!", "This emulation of Amstrad CPC's Mode5 needs all pixels of layer 5 to use colors 0-3.");
|
Verbose_message("Error!", "Emulation of Amstrad CPC's rasters needs all pixels of layer 5 to use colors 0-3.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,6 +227,7 @@ void Button_Constraint_menu(void)
|
|||||||
{IMAGE_MODE_EGX, "EGX (CPC)"},
|
{IMAGE_MODE_EGX, "EGX (CPC)"},
|
||||||
{IMAGE_MODE_EGX2, "EGX2 (CPC)"},
|
{IMAGE_MODE_EGX2, "EGX2 (CPC)"},
|
||||||
{IMAGE_MODE_MODE5, "Mode 5 (CPC)"},
|
{IMAGE_MODE_MODE5, "Mode 5 (CPC)"},
|
||||||
|
{IMAGE_MODE_RASTER, "Rasters (CPC)"},
|
||||||
};
|
};
|
||||||
|
|
||||||
Open_window(154,79,"8-bit constraints");
|
Open_window(154,79,"8-bit constraints");
|
||||||
|
|||||||
@ -614,6 +614,7 @@ enum IMAGE_MODES
|
|||||||
IMAGE_MODE_EGX, ///< CPC EGX
|
IMAGE_MODE_EGX, ///< CPC EGX
|
||||||
IMAGE_MODE_EGX2, ///< CPC EGX2
|
IMAGE_MODE_EGX2, ///< CPC EGX2
|
||||||
IMAGE_MODE_MODE5, ///< CPC mode 5
|
IMAGE_MODE_MODE5, ///< CPC mode 5
|
||||||
|
IMAGE_MODE_RASTER, ///< CPC generic rasters
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Circle / Ellipse Modes
|
/// Circle / Ellipse Modes
|
||||||
|
|||||||
@ -3213,7 +3213,8 @@ byte Read_pixel_from_current_screen (word x,word y)
|
|||||||
return *((y)*Main.image_width+(x)+Main.backups->Pages->Image[Main.current_layer].Pixels);
|
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.backups->Pages->Image_mode == IMAGE_MODE_MODE5
|
||||||
|
|| Main.backups->Pages->Image_mode == IMAGE_MODE_RASTER)
|
||||||
if (Main.current_layer==4)
|
if (Main.current_layer==4)
|
||||||
return *(Main.backups->Pages->Image[Main.current_layer].Pixels + x+y*Main.image_width);
|
return *(Main.backups->Pages->Image[Main.current_layer].Pixels + x+y*Main.image_width);
|
||||||
|
|
||||||
@ -3632,7 +3633,7 @@ void Update_pixel_renderer(void)
|
|||||||
Pixel_in_current_screen = Pixel_in_screen_zx;
|
Pixel_in_current_screen = Pixel_in_screen_zx;
|
||||||
Pixel_in_current_screen_with_preview = Pixel_in_screen_zx_with_preview;
|
Pixel_in_current_screen_with_preview = Pixel_in_screen_zx_with_preview;
|
||||||
}
|
}
|
||||||
// Implicit else : Image_mode must be IMAGE_MODE_MODE5
|
// Implicit else : Image_mode must be IMAGE_MODE_MODE5 or IMAGE_MODE_RASTER
|
||||||
else if ( Main.current_layer == 4)
|
else if ( Main.current_layer == 4)
|
||||||
{
|
{
|
||||||
// overlay
|
// overlay
|
||||||
@ -3647,7 +3648,7 @@ void Update_pixel_renderer(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// layered (again, for layers > 4 in MODE5)
|
// layered (again, for layers > 4 in MODE5 and RASTER)
|
||||||
Pixel_in_current_screen = Pixel_in_screen_layered;
|
Pixel_in_current_screen = Pixel_in_screen_layered;
|
||||||
Pixel_in_current_screen_with_preview = Pixel_in_screen_layered_with_preview;
|
Pixel_in_current_screen_with_preview = Pixel_in_screen_layered_with_preview;
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/pages.c
17
src/pages.c
@ -203,7 +203,8 @@ void Redraw_layered_image(void)
|
|||||||
// Re-construct the image with the visible layers
|
// Re-construct the image with the visible layers
|
||||||
byte layer=0;
|
byte layer=0;
|
||||||
// First layer
|
// First layer
|
||||||
if (Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5 && Main.layers_visible & (1<<4))
|
if ((Main.backups->Pages->Image_mode == IMAGE_MODE_MODE5
|
||||||
|
|| Main.backups->Pages->Image_mode == IMAGE_MODE_RASTER) && Main.layers_visible & (1<<4))
|
||||||
{
|
{
|
||||||
// The raster result layer is visible: start there
|
// The raster result layer is visible: start there
|
||||||
// Copy it in Main_visible_image
|
// Copy it in Main_visible_image
|
||||||
@ -1466,6 +1467,7 @@ byte Delete_layer(T_List_of_pages *list, int layer)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Merges the current layer onto the one below it.
|
/// Merges the current layer onto the one below it.
|
||||||
byte Merge_layer()
|
byte Merge_layer()
|
||||||
{
|
{
|
||||||
@ -1479,6 +1481,7 @@ byte Merge_layer()
|
|||||||
return Delete_layer(Main.backups,Main.current_layer);
|
return Delete_layer(Main.backups,Main.current_layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Switch_layer_mode(enum IMAGE_MODES new_mode)
|
void Switch_layer_mode(enum IMAGE_MODES new_mode)
|
||||||
{
|
{
|
||||||
if (new_mode == Main.backups->Pages->Image_mode)
|
if (new_mode == Main.backups->Pages->Image_mode)
|
||||||
@ -1486,18 +1489,12 @@ void Switch_layer_mode(enum IMAGE_MODES new_mode)
|
|||||||
|
|
||||||
Main.backups->Pages->Image_mode = new_mode;
|
Main.backups->Pages->Image_mode = new_mode;
|
||||||
|
|
||||||
switch (new_mode)
|
if (new_mode != IMAGE_MODE_ANIMATION)
|
||||||
{
|
{
|
||||||
case IMAGE_MODE_MODE5:
|
|
||||||
case IMAGE_MODE_LAYERED:
|
|
||||||
default:
|
|
||||||
Update_buffers(Main.image_width, Main.image_height);
|
Update_buffers(Main.image_width, Main.image_height);
|
||||||
Redraw_layered_image();
|
Redraw_layered_image();
|
||||||
break;
|
|
||||||
case IMAGE_MODE_ANIMATION:
|
|
||||||
// nothing to do.
|
|
||||||
// Eventually, we may clear the buffers to save a bit of memory...
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// TODO Eventually, in animation mode we may clear the buffers to save a bit of memory...
|
||||||
|
|
||||||
Update_pixel_renderer();
|
Update_pixel_renderer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -345,7 +345,8 @@ void Remap_image_highlevel(byte * conversion_table)
|
|||||||
|
|
||||||
// Remap the flatenned image view
|
// Remap the flatenned image view
|
||||||
if (Main.backups->Pages->Image_mode != IMAGE_MODE_ANIMATION
|
if (Main.backups->Pages->Image_mode != IMAGE_MODE_ANIMATION
|
||||||
&& Main.backups->Pages->Image_mode != IMAGE_MODE_MODE5)
|
&& Main.backups->Pages->Image_mode != IMAGE_MODE_MODE5
|
||||||
|
&& Main.backups->Pages->Image_mode != IMAGE_MODE_RASTER)
|
||||||
{
|
{
|
||||||
Remap_general_lowlevel(conversion_table,Main.visible_image.Image,Main.visible_image.Image,
|
Remap_general_lowlevel(conversion_table,Main.visible_image.Image,Main.visible_image.Image,
|
||||||
Main.image_width,Main.image_height,Main.image_width);
|
Main.image_width,Main.image_height,Main.image_width);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user