aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/frameout.h
blob: 78bf2e068aa74684ee25f3d66ed79c87fbe34a7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#ifndef SCI_GRAPHICS_FRAMEOUT_H
#define SCI_GRAPHICS_FRAMEOUT_H

#include "engines/util.h"                // for initGraphics
#include "sci/event.h"
#include "sci/graphics/plane32.h"
#include "sci/graphics/screen_item32.h"

namespace Sci {
typedef Common::Array<DrawList> ScreenItemListList;
typedef Common::Array<RectList> EraseListList;

class GfxCursor32;
class GfxTransitions32;
struct PlaneShowStyle;

/**
 * Frameout class, kFrameOut and relevant functions for SCI32 games.
 * Roughly equivalent to GraphicsMgr in SSCI.
 */
class GfxFrameout {
	friend class GfxTransitions32;

public:
	GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor);
	~GfxFrameout();

	bool _isHiRes;

	void clear();
	void run();

private:
	GfxCursor32 *_cursor;
	GfxPalette32 *_palette;
	GfxTransitions32 *_transitions;
	SegManager *_segMan;

	/**
	 * Determines whether the current game should be rendered in high
	 * resolution.
	 */
	bool gameIsHiRes() const;

#pragma mark -
#pragma mark Screen items
public:
	/**
	 * Adds a screen item.
	 */
	void addScreenItem(ScreenItem &screenItem) const;

	/**
	 * Updates a screen item.
	 */
	void updateScreenItem(ScreenItem &screenItem) const;

	/**
	 * Deletes a screen item.
	 */
	void deleteScreenItem(ScreenItem &screenItem);

	/**
	 * Deletes a screen item from the given plane.
	 */
	void deleteScreenItem(ScreenItem &screenItem, Plane &plane);

	/**
	 * Deletes a screen item from the given plane.
	 */
	void deleteScreenItem(ScreenItem &screenItem, const reg_t plane);

	void kernelAddScreenItem(const reg_t object);
	void kernelUpdateScreenItem(const reg_t object);
	void kernelDeleteScreenItem(const reg_t object);
	bool kernelSetNowSeen(const reg_t screenItemObject) const;
	int16 kernelObjectIntersect(const reg_t object1, const reg_t object2) const;

private:
	void remapMarkRedraw();
	bool getNowSeenRect(const reg_t screenItemObject, Common::Rect &result) const;

#pragma mark -
#pragma mark Planes
public:
	/**
	 * Creates and adds a new plane to the plane list. Ownership of the passed
	 * object is transferred to GfxFrameout.
	 *
	 * @note This method is on Screen in SSCI, but it is only ever called on
	 * `GraphicsMgr.screen`.
	 */
	void addPlane(Plane *plane);

	/**
	 * Deletes a plane within the current plane list.
	 *
	 * @note This method is on Screen in SSCI, but it is only ever called on
	 * `GraphicsMgr.screen`.
	 */
	void deletePlane(Plane &plane);

	const PlaneList &getPlanes() const {
		return _planes;
	}
	const PlaneList &getVisiblePlanes() const {
		return _visiblePlanes;
	}
	void kernelAddPlane(const reg_t object);
	void kernelUpdatePlane(const reg_t object);
	void kernelDeletePlane(const reg_t object);
	void kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics);
	int16 kernelGetHighPlanePri();

private:
	/**
	 * The list of planes (i.e. layers) that will be rendered to the screen on
	 * the next call to `frameOut`.
	 *
	 * @note This field is on `GraphicsMgr.screen` in SSCI.
	 */
	PlaneList _planes;

	/**
	 * Updates an existing plane with properties from the given VM object.
	 */
	void updatePlane(Plane &plane);

#pragma mark -
#pragma mark Pics
public:
	void kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 pictureX, const int16 pictureY, const bool mirrorX, const bool deleteDuplicate);

#pragma mark -
#pragma mark Rendering
public:
	/**
	 * Updates the hardware screen, no more than once per tick.
	 *
	 * @param delta An additional number of ticks that should elapse
	 * since the last time the screen was updated before it gets updated now.
	 * This is used for updating the screen within run_vm, where we normally
	 * expect that a call to kFrameOut will occur later during the current
	 * frame, but if it does not, then update the screen on the second frame
	 * anyway since the game is doing something bad.
	 */
	void updateScreen(const int delta = 0);

	/**
	 * Resets the pixel format of the hardware surface to the given format.
	 */
	void setPixelFormat(const Graphics::PixelFormat &format) const {
		initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes, &format);
	}

	/**
	 * Whether or not to throttle kFrameOut calls.
	 */
	bool _throttleKernelFrameOut;

	/**
	 * Whether `palMorphFrameOut` should be used instead of `frameOut` for
	 * rendering. Used by `kMorphOn` to explicitly enable `palMorphFrameOut` for
	 * one frame.
	 */
	bool _palMorphIsOn;

	inline const Buffer &getCurrentBuffer() const {
		return _currentBuffer;
	}

	void kernelFrameOut(const bool showBits);

	/**
	 * Throttles the engine as necessary to maintain 60fps output.
	 */
	void throttle();

	/**
	 * Updates the internal screen buffer for the next frame. If
	 * `shouldShowBits` is true, also sends the buffer to hardware. If
	 * `eraseRect` is non-empty, it is added to the erase list for this frame.
	 */
	void frameOut(const bool shouldShowBits, const Common::Rect &eraseRect = Common::Rect());

	/**
	 * TODO: Documentation
	 */
	void palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle);

	/**
	 * Draws the given rect from the internal screen buffer to hardware without
	 * processing any other graphics updates except for cursor changes.
	 */
	void directFrameOut(const Common::Rect &showRect);

#ifdef USE_RGB_COLOR
	/**
	 * Sends the entire internal screen buffer and palette to hardware.
	 */
	void resetHardware();
#endif

	/**
	 * Modifies the raw pixel data for the next frame with new palette indexes
	 * based on matched style ranges.
	 */
	void alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges);

	// This function is used within ScreenItem subsystem and assigned to various
	// booleanish fields that seem to represent the state of the screen item
	// (created, updated, deleted). In GK1/DOS, Phant1/m68k, SQ6/DOS, SQ6/Win,
	// and Phant2/Win, this function simply returns 1. If you know of any
	// game/environment where this function returns some value other than 1, or
	// if you used to work at Sierra and can explain why this is a thing (and if
	// anyone needs to care about it), please open a ticket!!
	inline int getScreenCount() const {
		return 1;
	};

	/**
	 * Shakes the screen.
	 */
	void shakeScreen(const int16 numShakes, const ShakeDirection direction);

private:
	/**
	 * The last time the hardware screen was updated.
	 */
	uint32 _lastScreenUpdateTick;

	/**
	 * State tracker to provide more accurate 60fps video throttling.
	 */
	uint8 _throttleState;

	/**
	 * The internal display pixel buffer. During `frameOut`, this buffer is
	 * drawn into according to the draw and erase rects calculated by
	 * `calcLists`, then drawn out to the hardware surface according to the
	 * `_showList` rects (which are also calculated by `calcLists`).
	 */
	Buffer _currentBuffer;

	/**
	 * When true, a change to the remap zone in the palette has occurred and
	 * screen items with remap data need to be redrawn.
	 */
	bool _remapOccurred;

	/**
	 * The dimensions of the output buffer, in display coordinates.
	 *
	 * @note This field is on `GraphicsMgr.screen` in SSCI.
	 */
	Common::Rect _screenRect;

	/**
	 * A list of rectangles, in screen coordinates, that represent portions of
	 * the internal screen buffer that are dirty and should be drawn to the
	 * hardware display surface.
	 *
	 * @note This field is on `GraphicsMgr.screen` in SSCI.
	 */
	RectList _showList;

	/**
	 * The amount of extra overdraw that is acceptable when merging two show
	 * list rectangles together into a single larger rectangle.
	 *
	 * @note This field is on `GraphicsMgr.screen` in SSCI.
	 */
	int _overdrawThreshold;

	/**
	 * The list of planes that are currently drawn to the hardware display
	 * surface. Used to calculate differences in plane properties between the
	 * last frame and current frame.
	 *
	 * @note This field is on `GraphicsMgr.visibleScreen` in SSCI.
	 */
	PlaneList _visiblePlanes;

	/**
	 * Calculates the location and dimensions of dirty rects over the entire
	 * screen for rendering the next frame. The draw and erase lists in
	 * `drawLists` and `eraseLists` each represent one plane on the screen.
	 * The optional `eraseRect` argument allows a specific area of the screen to
	 * be explicitly erased.
	 */
	void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect = Common::Rect());

	/**
	 * Erases the areas in the given erase list from the visible screen buffer
	 * by filling them with the color from the corresponding plane. This is an
	 * optimisation for colored-type planes only; other plane types have to be
	 * redrawn from pixel data.
	 */
	void drawEraseList(const RectList &eraseList, const Plane &plane);

	/**
	 * Draws all screen items from the given draw list to the visible screen
	 * buffer.
	 */
	void drawScreenItemList(const DrawList &screenItemList);

	/**
	 * Adds a new rectangle to the list of regions to write out to the hardware.
	 * The provided rect may be merged into an existing rectangle to reduce the
	 * number of blit operations.
	 */
	void mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold);

	/**
	 * Sends all dirty rects from the internal frame buffer to the backend, then
	 * updates the hardware screen.
	 */
	void showBits();

	/**
	 * Validates whether the given palette index in the style range should copy
	 * a color from the next palette to the source palette during a palette
	 * morph operation.
	 */
	inline bool validZeroStyle(const uint8 style, const int i) const {
		if (style != 0) {
			return false;
		}

		// TODO: Cannot check Shivers or MGDX until those executables can be
		// unwrapped
		switch (g_sci->getGameId()) {
		case GID_KQ7:
		case GID_PHANTASMAGORIA:
		case GID_SQ6:
			return (i > 71 && i < 104);
			break;
		default:
			return true;
		}
	}

#pragma mark -
#pragma mark Mouse cursor
public:
	reg_t kernelIsOnMe(const reg_t object, const Common::Point &position, const bool checkPixel) const;

private:
	void updateMousePositionForRendering() const {
		// In SSCI, mouse events were received via hardware interrupt, so the
		// mouse cursor would always get updated immediately when the user moved
		// the mouse. ScummVM must poll for mouse events from the backend
		// instead, so we poll just before rendering so that the latest mouse
		// position is rendered instead of whatever position it was at the last
		// time kGetEvent was called. Without this, the mouse appears stuck
		// during loops that do not make calls to kGetEvent, like transitions.
		g_sci->getEventManager()->getSciEvent(kSciEventPeek);
	}

	/**
	 * Determines whether or not the point given by `position` is inside of the
	 * given screen item.
	 */
	bool isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const;

#pragma mark -
#pragma mark Debugging
public:
	void printPlaneList(Console *con) const;
	void printVisiblePlaneList(Console *con) const;
	void printPlaneListInternal(Console *con, const PlaneList &planeList) const;
	void printPlaneItemList(Console *con, const reg_t planeObject) const;
	void printVisiblePlaneItemList(Console *con, const reg_t planeObject) const;
	void printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const;
};

} // End of namespace Sci

#endif