1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-10-24 05:39:19 +03:00
Files
fuse/display_border.c
Fredrick Meunier 860baae489 Preserve display image data across refreshes (bug #537)
(thanks, Alberto Garcia)
2026-10-17 11:47:35 +11:00

229 lines
6.1 KiB
C

/* display_border.c: Spectrum border display handling
Copyright (c) 1999-2026 Philip Kendall, Thomas Harte, Witold Filipczyk
and Fredrick Meunier
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "config.h"
#include <string.h>
#include "display.h"
#include "display_internal.h"
#include "peripherals/scld.h"
#include "ui/uidisplay.h"
/* The current border colour */
libspectrum_byte display_lores_border;
libspectrum_byte display_hires_border;
libspectrum_byte display_last_border;
/* The current border colour */
int current_border[ DISPLAY_SCREEN_HEIGHT ][ DISPLAY_SCREEN_WIDTH_COLS ];
/* The border colour changes which have occurred in this frame */
struct border_change_t {
int x, y;
int colour;
};
static struct border_change_t border_change_end_sentinel =
{ DISPLAY_SCREEN_WIDTH_COLS, DISPLAY_SCREEN_HEIGHT - 1, 0 };
static int border_changes_last;
static struct border_change_t *border_changes;
static struct border_change_t *
alloc_change( void )
{
static int border_changes_size;
if( border_changes_size == border_changes_last ) {
border_changes_size += 10;
border_changes = libspectrum_renew( struct border_change_t,
border_changes, border_changes_size );
}
return border_changes + border_changes_last++;
}
static int
add_border_sentinel( void )
{
struct border_change_t *sentinel = alloc_change();
sentinel->x = sentinel->y = 0;
sentinel->colour = scld_last_dec.name.hires ?
display_hires_border : display_lores_border;
return 0;
}
int
display_border_init( void )
{
int error;
border_changes_last = 0;
if( border_changes ) libspectrum_free( border_changes );
border_changes = NULL;
error = add_border_sentinel();
if( error ) return error;
display_last_border = scld_last_dec.name.hires ?
display_hires_border : display_lores_border;
return 0;
}
static void
push_border_change( int colour )
{
int beam_x, beam_y;
struct border_change_t *change;
display_get_beam_position( &beam_x, &beam_y );
if( beam_y >= DISPLAY_SCREEN_HEIGHT ) return;
if( beam_x < 0 ) beam_x = 0;
if( beam_x > DISPLAY_SCREEN_WIDTH_COLS ) beam_x = DISPLAY_SCREEN_WIDTH_COLS;
if( beam_y < 0 ) beam_y = 0;
change = alloc_change();
change->x = beam_x;
change->y = beam_y;
change->colour = colour;
}
/* Change border colour if the colour in use changes */
static void
check_border_change( void )
{
if( scld_last_dec.name.hires &&
display_hires_border != display_last_border ) {
push_border_change( display_hires_border );
display_last_border = display_hires_border;
} else if( !scld_last_dec.name.hires &&
display_lores_border != display_last_border ) {
push_border_change( display_lores_border );
display_last_border = display_lores_border;
}
}
void
display_set_lores_border( int colour )
{
if( display_lores_border != colour ) display_lores_border = colour;
check_border_change();
}
void
display_set_hires_border( int colour )
{
if( display_hires_border != colour ) display_hires_border = colour;
check_border_change();
}
static void
set_border( int y, int start, int end, int colour )
{
libspectrum_dword chunk_detail = colour << 11;
int index = start + y * DISPLAY_SCREEN_WIDTH_COLS;
for( ; start < end; start++ ) {
/* Draw it if it is different to what was there last time. */
if( display_force_redraw[ index ] ||
display_last_screen[ index ] != chunk_detail ) {
display_force_redraw[ index ] = 0;
uidisplay_plot8( start, y, 0x00, 0, colour );
display_last_screen[ index ] = chunk_detail;
display_mark_screen_dirty( start, y );
}
index++;
}
}
static void
border_change_write( int y, int start, int end, int colour )
{
if( y < DISPLAY_BORDER_HEIGHT ||
y >= DISPLAY_BORDER_HEIGHT + DISPLAY_HEIGHT ) {
set_border( y, start, end, colour );
return;
}
if( start < DISPLAY_BORDER_WIDTH_COLS ) {
int left_end =
end > DISPLAY_BORDER_WIDTH_COLS ? DISPLAY_BORDER_WIDTH_COLS : end;
set_border( y, start, left_end, colour );
}
if( end > DISPLAY_BORDER_WIDTH_COLS + DISPLAY_WIDTH_COLS ) {
if( start < DISPLAY_BORDER_WIDTH_COLS + DISPLAY_WIDTH_COLS )
start = DISPLAY_BORDER_WIDTH_COLS + DISPLAY_WIDTH_COLS;
set_border( y, start, end, colour );
}
}
static void
border_change_line_part( int y, int start, int end, int colour )
{
border_change_write( y, start, end, colour );
}
static void
border_change_line( int y, int colour )
{
border_change_write( y, 0, DISPLAY_SCREEN_WIDTH_COLS, colour );
}
static void
do_border_change( struct border_change_t *first,
struct border_change_t *second )
{
if( first->x ) {
if( first->x != DISPLAY_SCREEN_WIDTH_COLS )
border_change_line_part( first->y, first->x, DISPLAY_SCREEN_WIDTH_COLS,
first->colour );
if( first->y < DISPLAY_SCREEN_HEIGHT - 1 ) first->y++;
}
for( ; first->y < second->y; first->y++ )
border_change_line( first->y, first->colour );
if( second->x ) {
if( second->x == DISPLAY_SCREEN_WIDTH_COLS )
border_change_line( first->y, first->colour );
else
border_change_line_part( first->y, 0, second->x, first->colour );
}
}
void
display_border_frame( void )
{
int pos;
int error;
struct border_change_t *end_sentinel = alloc_change();
memcpy( end_sentinel, &border_change_end_sentinel,
sizeof( struct border_change_t ) );
for( pos = 0; pos < border_changes_last - 1; pos++ )
do_border_change( border_changes + pos, border_changes + pos + 1 );
border_changes_last = 0;
error = add_border_sentinel();
if( error ) return;
}