1 /*
2 Falcon VIDEL emulation, with zoom
3
4 (C) 2006-2007 ARAnyM developer team
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22 #include "parameters.h"
23 #include "hostscreen.h"
24 #include "host.h"
25 #include "icio.h"
26 #include "host_surface.h"
27 #include "videl.h"
28 #include "videl_zoom.h"
29
30 #define DEBUG 0
31 #include "debug.h"
32
33 #define HW getHWoffset()
34
VidelZoom(memptr addr,uint32 size)35 VidelZoom::VidelZoom(memptr addr, uint32 size) :
36 VIDEL(addr, size), surface(NULL),
37 zoomWidth(0), zoomHeight(0),
38 prevWidth(0), prevHeight(0), prevBpp(0),
39 xtable(NULL), ytable(NULL)
40 {
41 reset();
42 }
43
~VidelZoom(void)44 VidelZoom::~VidelZoom(void)
45 {
46 if (surface) {
47 host->video->destroySurface(surface);
48 }
49 if (xtable) {
50 delete [] xtable;
51 }
52 if (ytable) {
53 delete [] ytable;
54 }
55 }
56
reset(void)57 void VidelZoom::reset(void)
58 {
59 VIDEL::reset();
60 }
61
getSurface(void)62 HostSurface *VidelZoom::getSurface(void)
63 {
64 HostSurface *videl_hsurf = VIDEL::getSurface();
65 if (!videl_hsurf) {
66 return NULL;
67 }
68
69 int videlWidth = videl_hsurf->getWidth();
70 int videlHeight = videl_hsurf->getHeight();
71 int videlBpp = videl_hsurf->getBpp();
72
73 int hostWidth = host->video->getWidth();
74 int hostHeight = host->video->getHeight();
75 int autozoom_enabled = bx_options.autozoom.enabled;
76
77 zoomWidth = hostWidth;
78 zoomHeight = hostHeight;
79 if ((hostWidth>=videlWidth) && (hostHeight>=videlHeight)) {
80 if (autozoom_enabled) {
81 float coefx, coefy;
82 coefx = (float) hostWidth / (videlWidth * aspect_x);
83 coefy = (float) hostHeight / (videlHeight * aspect_y);
84 if (bx_options.autozoom.integercoefs) {
85 /* Integer coefs */
86 coefx = (float) ((int) coefx);
87 coefy = (float) ((int) coefy);
88 }
89 /* Keep aspect ratio by using smallest coef */
90 if (coefx < coefy) {
91 coefy = coefx;
92 } else {
93 coefx = coefy;
94 }
95 zoomWidth = videlWidth * aspect_x * coefx;
96 zoomHeight = videlHeight * aspect_y * coefy;
97 } else if ((aspect_x>1) || (aspect_y>1)) {
98 zoomWidth = videlWidth * aspect_x;
99 zoomHeight = videlHeight * aspect_y;
100 autozoom_enabled = 1;
101 }
102 }
103
104 /* Return non zoomed surface if correct size, or zoom not suitable */
105 if (bx_options.opengl.enabled || !autozoom_enabled ||
106 ((zoomWidth==videlWidth) && (zoomHeight==videlHeight))) {
107 return videl_hsurf;
108 }
109
110 /* Recalc zoom table if videl or host screen size changes */
111 bool updateZoomTable = (!xtable || !ytable);
112 if (prevVidelBpp!=videlBpp) {
113 updateZoomTable=true;
114 prevVidelBpp=videlBpp;
115 }
116 if (prevVidelWidth!=videlWidth) {
117 updateZoomTable=true;
118 prevVidelWidth=videlWidth;
119 }
120 if (prevVidelHeight!=videlHeight) {
121 updateZoomTable=true;
122 prevVidelHeight=videlHeight;
123 }
124
125 /* Recreate surface if needed */
126 if (surface) {
127 if (prevBpp == videlBpp) {
128 if ((prevWidth!=zoomWidth) || (prevHeight!=zoomHeight)) {
129 surface->resize(zoomWidth, zoomHeight);
130 updateZoomTable = true;
131 }
132 } else {
133 delete surface;
134 surface = NULL;
135 }
136 }
137 if (surface==NULL) {
138 surface = host->video->createSurface(zoomWidth,zoomHeight,videlBpp);
139 updateZoomTable = true;
140 }
141
142 prevWidth = zoomWidth;
143 prevHeight = zoomHeight;
144 prevBpp = videlBpp;
145
146 /* Update zoom tables if needed */
147 if (updateZoomTable) {
148 int i;
149
150 if (xtable) {
151 delete [] xtable;
152 }
153 xtable = new int[zoomWidth];
154 for (i=0; i<zoomWidth; i++) {
155 xtable[i] = (i*videlWidth)/zoomWidth;
156 }
157
158 if (ytable) {
159 delete [] ytable;
160 }
161 ytable = new int[zoomHeight];
162 for (i=0; i<zoomHeight; i++) {
163 ytable[i] = (i*videlHeight)/zoomHeight;
164 }
165 }
166
167 /* Refresh dirty parts of non zoomed surface to zoomed surface */
168 refreshScreen();
169
170 return surface;
171 }
172
forceRefresh(void)173 void VidelZoom::forceRefresh(void)
174 {
175 VIDEL::forceRefresh();
176
177 if (!surface) {
178 return;
179 }
180
181 surface->setDirtyRect(0,0,
182 surface->getWidth(), surface->getHeight());
183 }
184
refreshScreen(void)185 void VidelZoom::refreshScreen(void)
186 {
187 HostSurface *videl_hsurf = VIDEL::getSurface();
188 if (!videl_hsurf) {
189 return;
190 }
191 SDL_Surface *videl_surf = videl_hsurf->getSdlSurface();
192 if (!videl_surf) {
193 return;
194 }
195
196 if (!surface) {
197 return;
198 }
199 SDL_Surface *sdl_surf = surface->getSdlSurface();
200 if (!sdl_surf) {
201 return;
202 }
203
204 /* Update palette from non zoomed surface */
205 if ((videl_hsurf->getBpp()==8) && (surface->getBpp()==8)) {
206 int i;
207 SDL_Color palette[256];
208 for (i=0;i<256;i++) {
209 palette[i].r = videl_surf->format->palette->colors[i].r;
210 palette[i].g = videl_surf->format->palette->colors[i].g;
211 palette[i].b = videl_surf->format->palette->colors[i].b;
212 #if SDL_VERSION_ATLEAST(2, 0, 0)
213 palette[i].a = videl_surf->format->palette->colors[i].a;
214 #endif
215 }
216 surface->setPalette(palette, 0, 256);
217 }
218
219 int videlWidth = videl_hsurf->getWidth();
220 int videlHeight = videl_hsurf->getHeight();
221 int videlBpp = videl_hsurf->getBpp();
222
223 Uint8 *dirtyRects = videl_hsurf->getDirtyRects();
224 if (!dirtyRects) {
225 return;
226 }
227
228 int dirty_w = videl_hsurf->getDirtyWidth();
229 int dirty_h = videl_hsurf->getDirtyHeight();
230
231 int x,y;
232 for (y=0;y<dirty_h;y++) {
233 /* Atari screen may not have a multiple of 16 lines */
234 int num_lines = videl_hsurf->getHeight() - (y<<4);
235 if (num_lines>16) {
236 num_lines=16;
237 }
238 for (x=0;x<dirty_w;x++) {
239 if (!dirtyRects[y * dirty_w + x]) {
240 continue;
241 }
242
243 /* Zoom 16x16 block */
244 int dst_x1 = ((x<<4) * zoomWidth) / videlWidth;
245 int dst_x2 = (((x+1)<<4) * zoomWidth) / videlWidth;
246 int dst_y1 = ((y<<4) * zoomHeight) / videlHeight;
247 int dst_y2 = (((y<<4)+num_lines) * zoomHeight) / videlHeight;
248
249 int i,j;
250
251 Uint8 *dst = (Uint8 *) sdl_surf->pixels;
252 dst += dst_y1 * sdl_surf->pitch;
253 dst += dst_x1 * (videlBpp>>3);
254
255 if (videlBpp==16) {
256 /* True color, 16 bits surface */
257 Uint16 *dst_line = (Uint16 *) dst;
258 for(j=dst_y1;j<dst_y2;j++) {
259 Uint16 *src_col = (Uint16 *) videl_surf->pixels;
260 src_col += ytable[j] * (videl_surf->pitch>>1);
261 Uint16 *dst_col = dst_line;
262 for(i=dst_x1;i<dst_x2;i++) {
263 *dst_col++ = src_col[xtable[i]];
264 }
265 dst_line += sdl_surf->pitch >> 1;
266 }
267 } else {
268 /* Bitplanes, 8 bits surface */
269 Uint8 *dst_line = (Uint8 *) dst;
270 for(j=dst_y1;j<dst_y2;j++) {
271 Uint8 *src_col = (Uint8 *) videl_surf->pixels;
272 src_col += ytable[j] * videl_surf->pitch;
273 Uint8 *dst_col = dst_line;
274 for(i=dst_x1;i<dst_x2;i++) {
275 *dst_col++ = src_col[xtable[i]];
276 }
277 dst_line += sdl_surf->pitch;
278 }
279 }
280
281 surface->setDirtyRect(dst_x1,dst_y1,
282 dst_x2-dst_x1,dst_y2-dst_y1);
283 }
284 }
285
286 /* Mark original surface as updated */
287 videl_hsurf->clearDirtyRects();
288 }
289