1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * This code is based on original Tony Tough source code
25  *
26  * Copyright (c) 1997-2003 Nayma Software
27  */
28 
29 #include "common/scummsys.h"
30 #include "graphics/surface.h"
31 #include "engines/util.h"
32 #include "tony/window.h"
33 #include "tony/game.h"
34 #include "tony/tony.h"
35 
36 namespace Tony {
37 
38 /****************************************************************************\
39 *       RMWindow Methods
40 \****************************************************************************/
41 
RMWindow()42 RMWindow::RMWindow() {
43 	reset();
44 }
45 
~RMWindow()46 RMWindow::~RMWindow() {
47 	close();
48 	RMText::unload();
49 	RMGfxTargetBuffer::freeBWPrecalcTable();
50 }
51 
52 /**
53  * Initializes the graphics window
54  */
init()55 void RMWindow::init() {
56 	Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
57 	initGraphics(RM_SX, RM_SY, &pixelFormat);
58 
59 	reset();
60 }
61 
62 /**
63  * Reset the variables
64  */
reset()65 void RMWindow::reset() {
66 	_showDirtyRects = false;
67 	_bGrabScreenshot = false;
68 	_bGrabThumbnail = false;
69 	_bGrabMovie = false;
70 	_wiping = false;
71 
72 	_wThumbBuf = nullptr;
73 }
74 
copyRectToScreen(const byte * buf,int pitch,int x,int y,int w,int h)75 void RMWindow::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
76 	if (GLOBALS._bCfgAnni30) {
77 		if (!RMGfxTargetBuffer::_precalcTable) {
78 			RMGfxTargetBuffer::createBWPrecalcTable();
79 			g_vm->getEngine()->getPointer().updateCursor();
80 		}
81 		Graphics::Surface *screen = g_system->lockScreen();
82 		const uint16 *src = (const uint16 *)buf;
83 		for (int i = 0; i < h; i++) {
84 			uint16 *dst = (uint16 *)screen->getBasePtr(x, y + i);
85 			for (int j = 0; j < w; j++) {
86 				dst[j] = RMGfxTargetBuffer::_precalcTable[src[j]];
87 			}
88 			src += (pitch / 2);
89 		}
90 		g_system->unlockScreen();
91 	} else {
92 		if (RMGfxTargetBuffer::_precalcTable) {
93 			RMGfxTargetBuffer::freeBWPrecalcTable();
94 			g_vm->getEngine()->getPointer().updateCursor();
95 		}
96 		g_system->copyRectToScreen(buf, pitch, x, y, w, h);
97 	}
98  }
99 
100 /**
101  * Close the window
102  */
close()103 void RMWindow::close() {
104 }
105 
grabThumbnail(uint16 * thumbmem)106 void RMWindow::grabThumbnail(uint16 *thumbmem) {
107 	_bGrabThumbnail = true;
108 	_wThumbBuf = thumbmem;
109 }
110 
111 /**
112  * Repaint the screen
113  */
repaint()114 void RMWindow::repaint() {
115 	g_system->updateScreen();
116 }
117 
118 /**
119  * Wipes an area of the screen
120  */
wipeEffect(Common::Rect & rcBoundEllipse)121 void RMWindow::wipeEffect(Common::Rect &rcBoundEllipse) {
122 	if ((rcBoundEllipse.left == 0) && (rcBoundEllipse.top == 0) &&
123 	    (rcBoundEllipse.right == RM_SX) && (rcBoundEllipse.bottom == RM_SY)) {
124 		// Full screen clear wanted, so use shortcut method
125 		g_system->fillScreen(0);
126 	} else {
127 		// Clear the designated area a line at a time
128 		uint16 line[RM_SX];
129 		Common::fill(line, line + RM_SX, 0);
130 
131 		// Loop through each line
132 		for (int yp = rcBoundEllipse.top; yp < rcBoundEllipse.bottom; ++yp) {
133 			copyRectToScreen((const byte *)&line[0], RM_SX * 2, rcBoundEllipse.left, yp, rcBoundEllipse.width(), 1);
134 		}
135 	}
136 }
137 
getNewFrame(RMGfxTargetBuffer & bigBuf,Common::Rect * rcBoundEllipse)138 void RMWindow::getNewFrame(RMGfxTargetBuffer &bigBuf, Common::Rect *rcBoundEllipse) {
139 	// Get a pointer to the bytes of the source buffer
140 	byte *lpBuf = bigBuf;
141 
142 	if (rcBoundEllipse != NULL) {
143 		// Circular wipe effect
144 		getNewFrameWipe(lpBuf, *rcBoundEllipse);
145 		_wiping = true;
146 	} else if (_wiping) {
147 		// Just finished a wiping effect, so copy the full screen
148 		copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
149 		_wiping = false;
150 
151 	} else {
152 		// Standard screen copy - iterate through the dirty rects
153 		Common::List<Common::Rect> dirtyRects = bigBuf.getDirtyRects();
154 		Common::List<Common::Rect>::iterator i;
155 
156 		// If showing dirty rects, copy the entire screen background and set up a surface pointer
157 		Graphics::Surface *s = NULL;
158 		if (_showDirtyRects) {
159 			copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY);
160 			s = g_system->lockScreen();
161 		}
162 
163 		for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
164 			Common::Rect &r = *i;
165 			const byte *lpSrc = lpBuf + (RM_SX * 2) * r.top + (r.left * 2);
166 			copyRectToScreen(lpSrc, RM_SX * 2, r.left, r.top, r.width(), r.height());
167 		}
168 
169 		if (_showDirtyRects) {
170 			for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) {
171 				// Frame the copied area with a rectangle
172 				s->frameRect(*i, 0xffffff);
173 			}
174 
175 			g_system->unlockScreen();
176 		}
177 	}
178 
179 	if (_bGrabThumbnail) {
180 		// Need to generate a thumbnail
181 		RMSnapshot s;
182 
183 		s.grabScreenshot(lpBuf, 4, _wThumbBuf);
184 		_bGrabThumbnail = false;
185 	}
186 
187 	// Clear the dirty rect list
188 	bigBuf.clearDirtyRects();
189 }
190 
191 /**
192  * Copies a section of the game frame in a circle bounded by the specified rectangle
193  */
getNewFrameWipe(byte * lpBuf,Common::Rect & rcBoundEllipse)194 void RMWindow::getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse) {
195 	// Clear the screen
196 	g_system->fillScreen(0);
197 
198 	if (!rcBoundEllipse.isValidRect())
199 		return;
200 
201 	Common::Point center(rcBoundEllipse.left + rcBoundEllipse.width() / 2,
202 	                     rcBoundEllipse.top + rcBoundEllipse.height() / 2);
203 
204 	// The rectangle technically defines the area inside the ellipse, with the corners touching
205 	// the ellipse boundary. Since we're currently simulating the ellipse using a plain circle,
206 	// we need to calculate a necessary width using the hypotenuse of X/2 & Y/2
207 	int x2y2 = (rcBoundEllipse.width() / 2) * (rcBoundEllipse.width() / 2) +
208 	           (rcBoundEllipse.height() / 2) * (rcBoundEllipse.height() / 2);
209 	int radius = 0;
210 	while ((radius * radius) < x2y2)
211 		++radius;
212 
213 	// Proceed copying a circular area of the frame with the calculated radius onto the screen
214 	int error = -radius;
215 	int x = radius;
216 	int y = 0;
217 
218 	while (x >= y) {
219 		plotSplices(lpBuf, center, x, y);
220 
221 		error += y;
222 		++y;
223 		error += y;
224 
225 		if (error >= 0) {
226 			error -= x;
227 			--x;
228 			error -= x;
229 		}
230 	}
231 }
232 
233 /**
234  * Handles drawing the line splices for the circle of viewable area
235  */
plotSplices(const byte * lpBuf,const Common::Point & center,int x,int y)236 void RMWindow::plotSplices(const byte *lpBuf, const Common::Point &center, int x, int y) {
237 	plotLines(lpBuf, center, x, y);
238 	if (x != y)
239 		plotLines(lpBuf, center, y, x);
240 }
241 
242 /**
243  * Handles drawing the line splices for the circle of viewable area
244  */
plotLines(const byte * lpBuf,const Common::Point & center,int x,int y)245 void RMWindow::plotLines(const byte *lpBuf, const Common::Point &center, int x, int y) {
246 	// Skips lines that have no width (i.e. at the top of the circle)
247 	if ((x == 0) || (y > center.y))
248 		return;
249 
250 	const byte *pSrc;
251 	int xs = MAX(center.x - x, 0);
252 	int width = MIN(RM_SX - xs, x * 2);
253 
254 	if ((center.y - y) >= 0) {
255 		// Draw line in top half of circle
256 		pSrc = lpBuf + ((center.y - y) * RM_SX * 2) + xs * 2;
257 		copyRectToScreen(pSrc, RM_SX * 2, xs, center.y - y, width, 1);
258 	}
259 
260 	if ((center.y + y) < RM_SY) {
261 		// Draw line in bottom half of circle
262 		pSrc = lpBuf + ((center.y + y) * RM_SX * 2) + xs * 2;
263 		copyRectToScreen(pSrc, RM_SX * 2, xs, center.y + y, width, 1);
264 	}
265 }
266 
showDirtyRects(bool v)267 void RMWindow::showDirtyRects(bool v) {
268 	_showDirtyRects = v;
269 }
270 
271 /****************************************************************************\
272 *       RMSnapshot Methods
273 \****************************************************************************/
274 
grabScreenshot(byte * lpBuf,int dezoom,uint16 * lpDestBuf)275 void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) {
276 	uint16 *src = (uint16 *)lpBuf;
277 
278 	int dimx = RM_SX / dezoom;
279 	int dimy = RM_SY / dezoom;
280 
281 	uint16 *cursrc;
282 
283 	if (lpDestBuf == NULL)
284 		src += (RM_SY - 1) * RM_BBX;
285 
286 	if (dezoom == 1 && 0) {
287 		byte *curOut = _rgb;
288 
289 		for (int y = 0; y < dimy; y++) {
290 			for (int x = 0; x < dimx; x++) {
291 				cursrc = &src[RM_SKIPX + x];
292 
293 				*curOut++ = ((*cursrc) & 0x1F) << 3;
294 				*curOut++ = (((*cursrc) >> 5) & 0x3F) << 3;
295 				*curOut++ = (((*cursrc) >> 11) & 0x1F) << 3;
296 
297 				if (lpDestBuf)
298 					*lpDestBuf++ = *cursrc;
299 			}
300 
301 			if (lpDestBuf == NULL)
302 				src -= RM_BBX;
303 			else
304 				src += RM_BBX;
305 		}
306 	} else {
307 		uint32 k = 0;
308 		for (int y = 0; y < dimy; y++) {
309 			for (int x = 0; x < dimx; x++) {
310 				cursrc = &src[RM_SKIPX + x * dezoom];
311 				int sommar, sommab, sommag, curv;
312 				sommar = sommab = sommag = 0;
313 
314 				for (int v = 0; v < dezoom; v++) {
315 					for (int u = 0; u < dezoom; u++) {
316 						if (lpDestBuf == NULL)
317 							curv = -v;
318 						else
319 							curv = v;
320 
321 						sommab += cursrc[curv * RM_BBX + u] & 0x1F;
322 						sommag += (cursrc[curv * RM_BBX + u] >> 6) & 0x1F;
323 						sommar += (cursrc[curv * RM_BBX + u] >> 11) & 0x1F;
324 					}
325 				}
326 				_rgb[k + 0] = (byte)(sommab * 8 / (dezoom * dezoom));
327 				_rgb[k + 1] = (byte)(sommag * 8 / (dezoom * dezoom));
328 				_rgb[k + 2] = (byte)(sommar * 8 / (dezoom * dezoom));
329 
330 				if (lpDestBuf != NULL)
331 					lpDestBuf[k / 3] = ((int)_rgb[k + 0] >> 3) | (((int)_rgb[k + 1] >> 3) << 5) |
332 					                   (((int)_rgb[k + 2] >> 3) << 10);
333 
334 				k += 3;
335 			}
336 
337 			if (lpDestBuf == NULL)
338 				src -= RM_BBX * dezoom;
339 			else
340 				src += RM_BBX * dezoom;
341 		}
342 	}
343 
344 #ifdef SCUMM_BIG_ENDIAN
345 	if (lpDestBuf != NULL) {
346 		for (int i = 0; i < dimx * dimy; i++) {
347 			lpDestBuf[i] = SWAP_BYTES_16(lpDestBuf[i]);
348 		}
349 	}
350 #endif
351 }
352 
353 } // End of namespace Tony
354