From f2b04e08d0b90581fe032d4313217fa39dde725f Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Sun, 28 May 2017 11:11:03 +0200 Subject: [PATCH] Constraint modes: Thomson, ZX --- src/buttons.c | 3 +- src/buttons_effects.c | 11 ++- src/const.h | 1 + src/graph.c | 214 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 209 insertions(+), 20 deletions(-) diff --git a/src/buttons.c b/src/buttons.c index 74f939b7..12f0e47b 100644 --- a/src/buttons.c +++ b/src/buttons.c @@ -5610,5 +5610,6 @@ void Button_Brush_container(void) byte Any_effect_active(void) { - return Shade_mode||Quick_shade_mode||Colorize_mode||Smooth_mode||Tiling_mode||Smear_mode||Stencil_mode||Mask_mode||Sieve_mode||Snap_mode||Main_tilemap_mode; + return Shade_mode||Quick_shade_mode||Colorize_mode||Smooth_mode||Tiling_mode||Smear_mode + ||Stencil_mode||Mask_mode||Sieve_mode||Snap_mode||Main_tilemap_mode || (Main_backups->Pages->Image_mode > IMAGE_MODE_ANIMATION); } diff --git a/src/buttons_effects.c b/src/buttons_effects.c index 3af15dd8..031d0d78 100644 --- a/src/buttons_effects.c +++ b/src/buttons_effects.c @@ -215,11 +215,12 @@ void Button_Constraint_menu(void) Window_set_normal_button(82,55,51,14,"OK" ,0,1,SDLK_RETURN); // 2 dropdown = Window_set_dropdown_button(17, 21, 120, 14, 120, "Constraints", 1, 0, 1, RIGHT_SIDE|LEFT_SIDE, 0); // 3 - //Window_dropdown_add_item(dropdown, IMAGE_MODE_ZX, "ZX Spectrum"); + Window_dropdown_add_item(dropdown, IMAGE_MODE_ZX, "ZX Spectrum"); //Window_dropdown_add_item(dropdown, IMAGE_MODE_GBC, "Game Boy Color"); - Window_dropdown_add_item(dropdown, IMAGE_MODE_EGX, "EGX (CPC)"); - Window_dropdown_add_item(dropdown, IMAGE_MODE_EGX2, "EGX2 (CPC)"); - Window_dropdown_add_item(dropdown, IMAGE_MODE_MODE5, "Mode 5 (CPC)"); + Window_dropdown_add_item(dropdown, IMAGE_MODE_THOMSON, "40col (MO/TO)"); + Window_dropdown_add_item(dropdown, IMAGE_MODE_EGX, "EGX (CPC)"); + Window_dropdown_add_item(dropdown, IMAGE_MODE_EGX2, "EGX2 (CPC)"); + Window_dropdown_add_item(dropdown, IMAGE_MODE_MODE5, "Mode 5 (CPC)"); Update_window_area(0,0,Window_width, Window_height); Display_cursor(); @@ -237,6 +238,8 @@ void Button_Constraint_menu(void) if (clicked_button==2) // OK { + if ((Selected_Constraint_Mode > IMAGE_MODE_ANIMATION) + && (Main_backups->Pages->Image_mode <= IMAGE_MODE_ANIMATION)) Button_Constraint_mode(); } diff --git a/src/const.h b/src/const.h index acab391f..a70a4b04 100644 --- a/src/const.h +++ b/src/const.h @@ -591,6 +591,7 @@ enum IMAGE_MODES IMAGE_MODE_ANIMATION, ///< Animation IMAGE_MODE_ZX, ///< ZX Spectrum (note "SPECTRUM" is kept for later... Spectrum 512 anyone?) IMAGE_MODE_GBC, ///< Game Boy Color + IMAGE_MODE_THOMSON, ///< "40 columns" mode on Thomson machines IMAGE_MODE_EGX, ///< CPC EGX IMAGE_MODE_EGX2, ///< CPC EGX2 IMAGE_MODE_MODE5, ///< CPC mode 5 diff --git a/src/graph.c b/src/graph.c index 5f5c5df7..c90011bd 100644 --- a/src/graph.c +++ b/src/graph.c @@ -59,8 +59,11 @@ // Generic pixel-drawing function. Func_pixel Pixel_figure; -// Fonction qui met à jour la zone de l'image donnée en paramètre sur l'écran. -// Tient compte du décalage X et Y et du zoom, et fait tous les controles nécessaires +/** Update the picture on screen, for the area passed in parameters. + * + * Takes into account the X/Y scrolling and zoom, and performs all safety checks so no updates will + * go outside the display area. + */ void Update_part_of_screen(short x, short y, short width, short height) { short effective_w, effective_h; @@ -68,7 +71,7 @@ void Update_part_of_screen(short x, short y, short width, short height) short effective_Y; short diff; - // Première étape, si L ou H est négatif, on doit remettre la zone à l'endroit + // First make sure the zone is in forward direction (positive width/height) if (width < 0) { x += width; @@ -81,7 +84,14 @@ void Update_part_of_screen(short x, short y, short width, short height) height = - height; } - // D'abord on met à jour dans la zone écran normale + // Round up to a multiple of 8 pixels, because some special modes (ZX, Thomson, ...) can change + // more pixels than expected (attribute clash) + x &= 0xFFF8; + y &= 0xFFF8; + width = ((width - 1) | 0x7) + 1; + height = ((height - 1) | 0x7) + 1; + + // Update "normal" view diff = x-Main_offset_X; if (diff<0) { @@ -105,10 +115,10 @@ void Update_part_of_screen(short x, short y, short width, short height) effective_Y = diff; } - // Normalement il ne faudrait pas updater au delà du split quand on est en mode loupe, - // mais personne ne devrait demander d'update en dehors de cette limite, même le fill est contraint - // a rester dans la zone visible de l'image - // ...Sauf l'affichage de brosse en preview - yr + // Clamp to actually visible area. All tools are normally constrained to this, but there are some + // exceptions: + // - Brush preview requests updates outside the visible screen, + // - ZX/Thomson constraints can lead to pixel changes outside the visible area. if(Main_magnifier_mode && effective_X + effective_w > Main_separator_position) effective_w = Main_separator_position - effective_X; else if(effective_X + effective_w > Screen_width) @@ -116,8 +126,8 @@ void Update_part_of_screen(short x, short y, short width, short height) if(effective_Y + effective_h > Menu_Y) effective_h = Menu_Y - effective_Y; - - /* + + /* (for debug purposes, highlight the rectangle that is updated) SDL_Rect r; r.x=effective_X; r.y=effective_Y; @@ -127,15 +137,16 @@ void Update_part_of_screen(short x, short y, short width, short height) */ Update_rect(effective_X,effective_Y,effective_w,effective_h); - // Et ensuite dans la partie zoomée + // Now update the "zoomed" part of the display if(Main_magnifier_mode) { - // Clipping en X + // Convert picture to zoomed-screen coordinates effective_X = (x-Main_magnifier_offset_X)*Main_magnifier_factor; effective_Y = (y-Main_magnifier_offset_Y)*Main_magnifier_factor; effective_w = width * Main_magnifier_factor; effective_h = height * Main_magnifier_factor; + // Apply horizontal clipping if (effective_X < 0) { effective_w+=effective_X; @@ -155,7 +166,7 @@ void Update_part_of_screen(short x, short y, short width, short height) } - // Clipping en Y + // Vertical clipping if (effective_Y < 0) { effective_h+=effective_Y; @@ -172,7 +183,7 @@ void Update_part_of_screen(short x, short y, short width, short height) } - // Très utile pour le debug :) + // Again, for debugging purposes, display the touched rectangle /*SDL_Rect r; r.x=effective_X; r.y=effective_Y; @@ -3041,6 +3052,167 @@ void Pixel_in_screen_egx_with_preview(word x,word y,byte color) Pixel_in_screen_layered_with_preview(x,y,color & mask); } +void Pixel_in_screen_thomson(word x,word y,byte color) +{ + word start = x & 0xFFF8; + word x2; + uint8_t c1, c2; + + // The color we are going to replace + c1 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + x+y*Main_image_width); + + if (c1 == color) + return; + + for (x2 = 0; x2 < 8; x2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+y*Main_image_width); + if (c2 == color) + continue; + if (c2 != c1) + break; + } + + if (c2 == c1 || c2 == color) + { + // There was only one color, so we can add a second one. + Pixel_in_screen_layered(x,y,color); + return; + } + + for (x2 = 0; x2 < 8; x2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+y*Main_image_width); + if (c2 == c1) { + Pixel_in_screen_layered(x2+start,y,color); + } + } +} + +void Pixel_in_screen_thomson_with_preview(word x,word y,byte color) +{ + word start = x & 0xFFF8; + word x2; + uint8_t c1, c2; + + // The color we are going to replace + c1 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + x+y*Main_image_width); + + if (c1 == color) + return; + + for (x2 = 0; x2 < 8; x2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+y*Main_image_width); + if (c2 == color) + continue; + if (c2 != c1) + break; + } + + if (c2 == c1 || c2 == color) + { + // There was only one color, so we can add a second one. + Pixel_in_screen_layered_with_preview(x,y,color); + return; + } + + for (x2 = 0; x2 < 8; x2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+y*Main_image_width); + if (c2 == c1) { + Pixel_in_screen_layered_with_preview(x2+start,y,color); + } + } +} + +void Pixel_in_screen_zx(word x,word y,byte color) +{ + word start = x & 0xFFF8; + word starty = y & 0xFFF8; + word x2, y2; + uint8_t c1, c2; + + // The color we are going to replace + c1 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + x+y*Main_image_width); + + if (c1 == color) + return; + + for (x2 = 0; x2 < 8; x2++) + for (y2 = 0; y2 < 8; y2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+(y2+starty)*Main_image_width); + if (c2 == color) + continue; + if (c2 != c1) + goto done; + } +done: + + if (c2 == c1 || c2 == color) + { + // There was only one color, so we can add a second one. + Pixel_in_screen_layered(x,y,color); + return; + } + + for (x2 = 0; x2 < 8; x2++) + for (y2 = 0; y2 < 8; y2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+(y2+starty)*Main_image_width); + if (c2 == c1) { + Pixel_in_screen_layered(x2+start,y2+starty,color); + } + } +} + +void Pixel_in_screen_zx_with_preview(word x,word y,byte color) +{ + word start = x & 0xFFF8; + word starty = y & 0xFFF8; + word x2,y2; + uint8_t c1, c2; + + // The color we are going to replace + c1 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + x+y*Main_image_width); + + // Pixel is already of the wanted color: nothing to do + if (c1 == color) + return; + + // Check the whole cell + for (x2 = 0; x2 < 8; x2++) + for (y2 = 0; y2 < 8; y2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+(y2+starty)*Main_image_width); + // Pixel is already of the color we are going to add, it is no problem + if (c2 == color) + continue; + // We have found another color, which is the one we will keep from the cell + if (c2 != c1) + goto done; + } +done: + + if (c2 == c1 || c2 == color) + { + // There was only one color, so we can add a second one. + Pixel_in_screen_layered_with_preview(x,y,color); + return; + } + + // Replace all C1 with C2 + for (x2 = 0; x2 < 8; x2++) + for (y2 = 0; y2 < 8; y2++) + { + c2 = *(Main_backups->Pages->Image[Main_current_layer].Pixels + (x2+start)+(y2+starty)*Main_image_width); + if (c2 == c1) { + Pixel_in_screen_layered_with_preview(x2+start,y2+starty,color); + } + } +} + /// Paint a a single pixel in image only : in a layer under one that acts as a layer-selector (mode 5). void Pixel_in_screen_underlay(word x,word y,byte color) { @@ -3150,10 +3322,22 @@ void Update_pixel_renderer(void) if (Main_backups->Pages->Image_mode == IMAGE_MODE_EGX || Main_backups->Pages->Image_mode == IMAGE_MODE_EGX2) { - // layered + // special "EGX" mode Pixel_in_current_screen = Pixel_in_screen_egx; Pixel_in_current_screen_with_preview = Pixel_in_screen_egx_with_preview; } + else + if (Main_backups->Pages->Image_mode == IMAGE_MODE_THOMSON) + { + Pixel_in_current_screen = Pixel_in_screen_thomson; + Pixel_in_current_screen_with_preview = Pixel_in_screen_thomson_with_preview; + } + else + if (Main_backups->Pages->Image_mode == IMAGE_MODE_ZX) + { + Pixel_in_current_screen = Pixel_in_screen_zx; + Pixel_in_current_screen_with_preview = Pixel_in_screen_zx_with_preview; + } // Implicit else : Image_mode must be IMAGE_MODE_MODE5 else if ( Main_current_layer == 4) {