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 ¢er, 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 ¢er, 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