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