1 /* rectangle.c: routines for managing the set of screen area rectangles updated
2 since the last display
3 Copyright (c) 1999-2009 Philip Kendall, Thomas Harte, Witold Filipczyk
4 and Fredrick Meunier
5
6 $Id: rectangle.c 4785 2012-12-07 23:56:40Z sbaldovi $
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22 Author contact information:
23
24 E-mail: philip-fuse@shadowmagic.org.uk
25
26 */
27
28 #include <config.h>
29
30 #include <stdlib.h>
31
32 #include "fuse.h"
33 #include "rectangle.h"
34 #include "settings.h"
35 #include "ui/ui.h"
36
37 /* Those rectangles which were modified on the last line to be displayed */
38 static struct rectangle *rectangle_active = NULL;
39 static size_t rectangle_active_count = 0, rectangle_active_allocated = 0;
40
41 /* Those rectangles which weren't */
42 struct rectangle *rectangle_inactive = NULL;
43 size_t rectangle_inactive_count = 0, rectangle_inactive_allocated = 0;
44
45 /* Add the rectangle { x, line, w, 1 } to the list of rectangles to be
46 redrawn, either by extending an existing rectangle or creating a
47 new one */
48 void
rectangle_add(int y,int x,int w)49 rectangle_add( int y, int x, int w )
50 {
51 size_t i;
52 struct rectangle *ptr;
53
54 /* Check through all 'active' rectangles (those which were modified
55 on the previous line) and see if we can use this new rectangle
56 to extend them */
57 for( i = 0; i < rectangle_active_count; i++ ) {
58
59 if( rectangle_active[i].x == x &&
60 rectangle_active[i].w == w ) {
61 rectangle_active[i].h++;
62 return;
63 }
64 }
65
66 /* We couldn't find a rectangle to extend, so create a new one */
67 if( ++rectangle_active_count > rectangle_active_allocated ) {
68
69 size_t new_alloc;
70
71 new_alloc = rectangle_active_allocated ?
72 2 * rectangle_active_allocated :
73 8;
74
75 ptr = libspectrum_realloc( rectangle_active,
76 new_alloc * sizeof( struct rectangle ) );
77
78 rectangle_active_allocated = new_alloc; rectangle_active = ptr;
79 }
80
81 ptr = &rectangle_active[ rectangle_active_count - 1 ];
82
83 ptr->x = x; ptr->y = y;
84 ptr->w = w; ptr->h = 1;
85 }
86
87 #ifndef MAX
88 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
89 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
90 #endif
91
92 inline static int
compare_and_merge_rectangles(struct rectangle * source)93 compare_and_merge_rectangles( struct rectangle *source )
94 {
95 size_t z;
96
97 /* Now look to see if there is an overlapping rectangle in the inactive
98 list. These occur when frame skip is on and the same lines are
99 covered more than once... */
100 for( z = 0; z < rectangle_inactive_count; z++ ) {
101 if( rectangle_inactive[z].x == source->x &&
102 rectangle_inactive[z].w == source->w ) {
103 if( rectangle_inactive[z].y == source->y &&
104 rectangle_inactive[z].h == source->h )
105 return 1;
106
107 if( ( rectangle_inactive[z].y < source->y &&
108 ( source->y < ( rectangle_inactive[z].y +
109 rectangle_inactive[z].h + 1 ) ) ) ||
110 ( source->y < rectangle_inactive[z].y &&
111 ( rectangle_inactive[z].y < ( source->y + source->h + 1 ) ) ) ) {
112 /* rects overlap or touch in the y dimension, merge */
113 rectangle_inactive[z].h = MAX( rectangle_inactive[z].y +
114 rectangle_inactive[z].h,
115 source->y + source->h ) -
116 MIN( rectangle_inactive[z].y, source->y );
117 rectangle_inactive[z].y = MIN( rectangle_inactive[z].y, source->y );
118
119 return 1;
120 }
121 }
122 if( rectangle_inactive[z].y == source->y &&
123 rectangle_inactive[z].h == source->h ) {
124
125 if( (rectangle_inactive[z].x < source->x &&
126 ( source->x < ( rectangle_inactive[z].x +
127 rectangle_inactive[z].w + 1 ) ) ) ||
128 ( source->x < rectangle_inactive[z].x &&
129 ( rectangle_inactive[z].x < ( source->x + source->w + 1 ) ) ) ) {
130 /* rects overlap or touch in the x dimension, merge */
131 rectangle_inactive[z].w = MAX( rectangle_inactive[z].x +
132 rectangle_inactive[z].w, source->x +
133 source->w ) - MIN( rectangle_inactive[z].x, source->x );
134 rectangle_inactive[z].x = MIN( rectangle_inactive[z].x, source->x );
135 return 1;
136 }
137 }
138 /* Handle overlaps offset by both x and y? how much overlap and hence
139 overdraw can be tolerated? */
140 }
141 return 0;
142 }
143
144 /* Move all rectangles not updated on this line to the inactive list */
145 void
rectangle_end_line(int y)146 rectangle_end_line( int y )
147 {
148 size_t i;
149 struct rectangle *ptr;
150
151 for( i = 0; i < rectangle_active_count; i++ ) {
152
153 /* Skip if this rectangle was updated this line */
154 if( rectangle_active[i].y + rectangle_active[i].h == y + 1 ) continue;
155
156 if ( settings_current.frame_rate > 1 &&
157 compare_and_merge_rectangles( &rectangle_active[i] ) ) {
158
159 /* Mark the active rectangle as done */
160 rectangle_active[i].h = 0;
161 continue;
162 }
163
164 /* We couldn't find a rectangle to extend, so create a new one */
165 if( ++rectangle_inactive_count > rectangle_inactive_allocated ) {
166
167 size_t new_alloc;
168
169 new_alloc = rectangle_inactive_allocated ?
170 2 * rectangle_inactive_allocated :
171 8;
172
173 ptr = libspectrum_realloc( rectangle_inactive,
174 new_alloc * sizeof( struct rectangle ) );
175
176 rectangle_inactive_allocated = new_alloc; rectangle_inactive = ptr;
177 }
178
179 rectangle_inactive[ rectangle_inactive_count - 1 ] = rectangle_active[i];
180
181 /* Mark the active rectangle as done */
182 rectangle_active[i].h = 0;
183 }
184
185 /* Compress the list of active rectangles */
186 for( i = 0, ptr = rectangle_active; i < rectangle_active_count; i++ ) {
187 if( rectangle_active[i].h == 0 ) continue;
188 *ptr = rectangle_active[i]; ptr++;
189 }
190
191 rectangle_active_count = ptr - rectangle_active;
192 }
193