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 #ifndef SCI_GRAPHICS_PLANE32_H
24 #define SCI_GRAPHICS_PLANE32_H
25 
26 #include "common/array.h"
27 #include "common/rect.h"
28 #include "sci/engine/features.h"
29 #include "sci/engine/vm_types.h"
30 #include "sci/graphics/helpers.h"
31 #include "sci/graphics/lists32.h"
32 #include "sci/graphics/screen_item32.h"
33 
34 namespace Sci {
35 enum PlaneType {
36 	kPlaneTypeColored            = 0,
37 	kPlaneTypePicture            = 1,
38 	kPlaneTypeTransparent        = 2,
39 	kPlaneTypeOpaque             = 3,
40 	kPlaneTypeTransparentPicture = 4
41 };
42 
43 enum PlanePictureCodes {
44 	// Any value at or below 65531 means the plane is a kPlaneTypePicture
45 	kPlanePic                   = 65531,
46 	kPlanePicTransparentPicture = 65532,
47 	kPlanePicOpaque             = 65533,
48 	kPlanePicTransparent        = 65534,
49 	kPlanePicColored            = 65535
50 };
51 
52 #pragma mark -
53 #pragma mark RectList
54 
55 typedef StablePointerArray<Common::Rect, 200> RectListBase;
56 class RectList : public RectListBase {
57 public:
add(const Common::Rect & rect)58 	void add(const Common::Rect &rect) {
59 		RectListBase::add(new Common::Rect(rect));
60 	}
61 };
62 
63 #pragma mark -
64 #pragma mark DrawList
65 
66 struct DrawItem {
67 	/**
68 	 * The screen item to draw.
69 	 */
70 	ScreenItem *screenItem;
71 
72 	/**
73 	 * The target rectangle of the draw operation.
74 	 */
75 	Common::Rect rect;
76 
77 	inline bool operator<(const DrawItem &other) const {
78 		return *screenItem < *other.screenItem;
79 	}
80 };
81 
82 typedef StablePointerArray<DrawItem, 250> DrawListBase;
83 class DrawList : public DrawListBase {
84 private:
sortHelper(const DrawItem * a,const DrawItem * b)85 	inline static bool sortHelper(const DrawItem *a, const DrawItem *b) {
86 		return *a < *b;
87 	}
88 public:
89 	void add(ScreenItem *screenItem, const Common::Rect &rect);
sort()90 	inline void sort() {
91 		pack();
92 		Common::sort(begin(), end(), sortHelper);
93 	}
94 };
95 
96 class PlaneList;
97 
98 #pragma mark -
99 #pragma mark Plane
100 
101 /**
102  * A plane is a grouped layer of screen items.
103  */
104 class Plane {
105 private:
106 	/**
107 	 * A serial used for planes that are generated inside the graphics engine,
108 	 * rather than the interpreter.
109 	 */
110 	static uint16 _nextObjectId;
111 
112 	/**
113 	 * A serial used to identify the creation order of planes, to ensure a
114 	 * stable sort order for planes with identical priorities.
115 	 */
116 	static uint32 _nextCreationId;
117 
118 	/**
119 	 * The creation order number, which ensures a stable sort when planes with
120 	 * identical priorities are added to the plane list.
121 	 */
122 	uint32 _creationId;
123 
124 	/**
125 	 * For planes that are used to render picture data, the resource ID of the
126 	 * picture to be displayed. This value may also be one of the special
127 	 * PlanePictureCodes, in which case the plane becomes a non-picture plane.
128 	 */
129 	GuiResourceId _pictureId;
130 
131 	/**
132 	 * Whether or not the contents of picture planes should be drawn
133 	 * horizontally mirrored. Only applies to planes of type kPlaneTypePicture.
134 	 */
135 	bool _mirrored;
136 
137 	/**
138 	 * Whether the picture ID for this plane has changed. This flag is set when
139 	 * the plane is created or updated from a VM object, and is cleared when the
140 	 * plane is synchronised to another plane (which calls changePic).
141 	 */
142 	bool _pictureChanged;
143 
144 	/**
145 	 * Converts the dimensions of the game rect used by scripts to the
146 	 * dimensions of the plane rect used to render content to the screen.
147 	 * Coordinates with remainders are rounded up to the next whole pixel.
148 	 */
149 	void convertGameRectToPlaneRect();
150 
151 	/**
152 	 * Sets the type of the plane according to its assigned picture resource ID.
153 	 */
154 	void setType();
155 
156 public:
157 	/**
158 	 * The type of the plane.
159 	 */
160 	PlaneType _type;
161 
162 	/**
163 	 * The color to use when erasing the plane. Only applies to planes of type
164 	 * kPlaneTypeColored.
165 	 */
166 	byte _back;
167 
168 	/**
169 	 * Whether the priority of this plane has changed. This flag is set when the
170 	 * plane is updated from another plane and cleared when draw list
171 	 * calculation occurs.
172 	 */
173 	int _priorityChanged;
174 
175 	/**
176 	 * A handle to the VM object corresponding to this plane. Some planes are
177 	 * generated purely within the graphics engine and have a numeric object
178 	 * value.
179 	 */
180 	reg_t _object;
181 
182 	/**
183 	 * The rendering priority of the plane. Higher priorities are drawn above
184 	 * lower priorities.
185 	 */
186 	int16 _priority;
187 
188 	/**
189 	 * Whether or not all screen items in this plane should be redrawn on the
190 	 * next frameout, instead of just the screen items marked as updated. This
191 	 * is set when visual changes to the plane itself are made that affect the
192 	 * rendering of the entire plane, and cleared once those changes are
193 	 * rendered by `redrawAll`.
194 	 */
195 	int _redrawAllCount;
196 
197 	/**
198 	 * Flags indicating the state of the plane.
199 	 * - `created` is set when the plane is first created, either from a VM
200 	 *   object or from within the engine itself
201 	 * - `updated` is set when the plane is updated from another plane and the
202 	 *   two planes' `planeRect`s do not match
203 	 * - `deleted` is set when the plane is deleted by a kernel call
204 	 * - `moved` is set when the plane has been moved or resized
205 	 */
206 	int _created, _updated, _deleted, _moved;
207 
208 	/**
209 	 * The vanishing point for the plane. Used when automatically calculating
210 	 * the correct scaling of the plane's screen items according to their
211 	 * position.
212 	 */
213 	Common::Point _vanishingPoint;
214 
215 	/**
216 	 * The position & dimensions of the plane in screen coordinates. This rect
217 	 * is not clipped to the screen, so may include coordinates that are
218 	 * offscreen.
219 	 */
220 	Common::Rect _planeRect;
221 
222 	/**
223 	 * The position & dimensions of the plane in game script coordinates.
224 	 */
225 	Common::Rect _gameRect;
226 
227 	/**
228 	 * The position & dimensions of the plane in screen coordinates. This rect
229 	 * is clipped to the screen.
230 	 */
231 	Common::Rect _screenRect;
232 
233 	/**
234 	 * The list of screen items grouped within this plane.
235 	 */
236 	ScreenItemList _screenItemList;
237 
238 public:
239 	/**
240 	 * Initialises static Plane members.
241 	 */
242 	static void init();
243 
244 	// In SSCI this constructor signature did not accept a picture ID, but some
245 	// calls to construct planes with this signature immediately set the picture
246 	// ID and then called setType again, so it made more sense to just make the
247 	// picture ID a parameter instead.
248 	Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored);
249 
250 	Plane(const reg_t object);
251 
252 	Plane(const Plane &other);
253 
254 	void operator=(const Plane &other);
255 
256 	inline bool operator<(const Plane &other) const {
257 		if (_priority < other._priority) {
258 			return true;
259 		}
260 
261 		if (_priority == other._priority) {
262 			// This is different than SSCI; see ScreenItem::operator< for an
263 			// explanation
264 			return _creationId < other._creationId;
265 		}
266 
267 		return false;
268 	}
269 
270 	/**
271 	 * Clips the screen rect of this plane to fit within the given screen rect.
272 	 */
clipScreenRect(const Common::Rect & screenRect)273 	inline void clipScreenRect(const Common::Rect &screenRect) {
274 		// LSL6 hires creates planes with invalid rects; SSCI does not care
275 		// about this, but `Common::Rect::clip` does, so we need to check
276 		// whether or not the rect is actually valid before clipping and only
277 		// clip valid rects
278 		if (_screenRect.isValidRect() && _screenRect.intersects(screenRect)) {
279 			_screenRect.clip(screenRect);
280 		} else {
281 			_screenRect.left = 0;
282 			_screenRect.top = 0;
283 			_screenRect.right = 0;
284 			_screenRect.bottom = 0;
285 		}
286 	}
287 
288 	void printDebugInfo(Console *con) const;
289 
290 	/**
291 	 * Compares the properties of the current plane against the properties of
292 	 * the `other` plane (which is the corresponding plane from the visible
293 	 * plane list) to discover which properties have been changed on this plane
294 	 * by a call to `update(reg_t)`.
295 	 *
296 	 * @note This method was called UpdatePlane in SSCI.
297 	 */
298 	void sync(const Plane *other, const Common::Rect &screenRect);
299 
300 	/**
301 	 * Updates the plane to match the state of the plane object from the VM.
302 	 *
303 	 * @note This method was called UpdatePlane in SSCI.
304 	 */
305 	void update(const reg_t object);
306 
307 	/**
308 	 * Modifies the position of all non-pic screen items by the given delta. If
309 	 * `scrollPics` is true, pic items are also repositioned.
310 	 */
311 	void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics);
312 
313 	/**
314 	 * The default plane is the first plane generated inside the graphics engine.
315 	 */
isDefaultPlane()316 	inline bool isDefaultPlane() {
317 		return _object.isNumber() && _object.getOffset() == (uint32)g_sci->_features->detectPlaneIdBase();
318 	}
319 
320 #pragma mark -
321 #pragma mark Plane - Pic
322 private:
323 	/**
324 	 * Adds all cels from the specified picture resource to the plane as screen
325 	 * items. If a position is provided, the screen items will be given that
326 	 * position; otherwise, the default relative positions for each cel will be
327 	 * taken from the picture resource data.
328 	 */
329 	inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX);
330 
331 	/**
332 	 * Marks all screen items to be deleted that are within this plane and match
333 	 * the given picture ID.
334 	 */
335 	void deletePic(const GuiResourceId pictureId);
336 
337 	/**
338 	 * Marks all screen items to be deleted that are within this plane and are
339 	 * picture cels.
340 	 */
341 	void deleteAllPics();
342 
343 public:
344 	/**
345 	 * Marks all existing screen items matching the current picture to be
346 	 * deleted, then adds all cels from the new picture resource to the plane at
347 	 * the given position.
348 	 */
349 	GuiResourceId addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX, const bool deleteDuplicate = true);
350 
351 	/**
352 	 * If the plane is a picture plane, re-adds all cels from its picture
353 	 * resource to the plane. Otherwise, just clears the _pictureChanged flag.
354 	 */
355 	void changePic();
356 
357 	/**
358 	 * Marks all screen items to be deleted that are within this plane and match
359 	 * the given picture ID, then sets the picture ID of the plane to the new
360 	 * picture ID without adding any screen items.
361 	 */
362 	void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId);
363 
364 #pragma mark -
365 #pragma mark Plane - Rendering
366 private:
367 	/**
368 	 * Splits all rects in the given draw list at the edges of all
369 	 * higher-priority, non-transparent, intersecting planes.
370 	 */
371 	void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const;
372 
373 	/**
374 	 * Splits all rects in the given erase list at the edges of higher-priority,
375 	 * non-transparent, intersecting planes.
376 	 */
377 	void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const;
378 
379 	/**
380 	 * Adds the screen item at `index` into `drawList`, ensuring it is only
381 	 * drawn within the bounds of `rect`. If an existing draw list entry exists
382 	 * for this screen item, it will be modified. Otherwise, a new entry will be
383 	 * added.
384 	 */
385 	void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const;
386 
387 	/**
388 	 * Merges `rect` with an existing rect in `eraseList`, if possible.
389 	 * Otherwise, adds the rect as a new entry to `eraseList`.
390 	 */
391 	void mergeToRectList(const Common::Rect &rect, RectList &eraseList) const;
392 
393 public:
394 	/**
395 	 * Calculates the location and dimensions of dirty rects of the screen items
396 	 * in this plane and adds them to the given draw and erase lists, and
397 	 * synchronises this plane's list of screen items to the given visible
398 	 * plane.
399 	 */
400 	void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);
401 
402 	/**
403 	 * Synchronises changes to screen items from the current plane to the
404 	 * visible plane and deletes screen items from the current plane that have
405 	 * been marked as deleted. If `forceUpdate` is true, all screen items on the
406 	 * visible plane will be updated, even if they are not marked as having
407 	 * changed.
408 	 */
409 	void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate);
410 
411 	/**
412 	 * This method is called from the highest priority plane to the lowest
413 	 * priority plane.
414 	 *
415 	 * Adds screen items from this plane to the draw list that must be redrawn
416 	 * because they intersect entries in the `higherEraseList`.
417 	 *
418 	 * If this plane is opaque, all intersecting erase rects in `lowerEraseList`
419 	 * are removed, as they would be completely overwritten by the contents of
420 	 * this plane.
421 	 *
422 	 * If this plane is transparent, erase rects from the `lowerEraseList` are
423 	 * added to the erase list for this plane, so that lower planes.
424 	 *
425 	 * @param drawList The draw list for this plane.
426 	 * @param eraseList The erase list for this plane.
427 	 * @param higherEraseList The erase list for a plane above this plane.
428 	 */
429 	void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const;
430 
431 	/**
432 	 * This method is called from the lowest priority plane to the highest
433 	 * priority plane.
434 	 *
435 	 * Adds screen items from this plane to the draw list that must be drawn
436 	 * because the lower plane is being redrawn and potentially transparent
437 	 * screen items from this plane would draw over the lower priority plane's
438 	 * screen items.
439 	 *
440 	 * This method applies only to transparent planes.
441 	 *
442 	 * @param drawList The draw list for this plane.
443 	 * @param eraseList The erase list for a plane below this plane.
444 	 */
445 	void filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const;
446 
447 	/**
448 	 * This method is called from the lowest priority plane to the highest
449 	 * priority plane.
450 	 *
451 	 * Adds screen items from this plane to the draw list that must be drawn
452 	 * because the lower plane is being redrawn and potentially transparent
453 	 * screen items from this plane would draw over the lower priority plane's
454 	 * screen items.
455 	 *
456 	 * This method applies only to transparent planes.
457 	 *
458 	 * @param drawList The draw list for this plane.
459 	 * @param lowerDrawList The draw list for a plane below this plane.
460 	 */
461 	void filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const;
462 
463 	/**
464 	 * Updates all of the plane's non-deleted screen items and adds them to the
465 	 * given draw and erase lists.
466 	 */
467 	void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);
468 
469 	/**
470 	 * Marks all non-deleted remapped screen items within the plane as needing
471 	 * to be updated during the next frameout.
472 	 */
473 	void remapMarkRedraw();
474 };
475 
476 #pragma mark -
477 #pragma mark PlaneList
478 
479 typedef Common::Array<Plane *> PlaneListBase;
480 class PlaneList : public PlaneListBase {
481 private:
sortHelper(const Plane * a,const Plane * b)482 	inline static bool sortHelper(const Plane *a, const Plane *b) {
483 		return *a < *b;
484 	}
485 
486 	using PlaneListBase::push_back;
487 
488 public:
489 	// A method for finding the index of a plane inside a PlaneList is used
490 	// because entries in the main plane list and visible plane list of
491 	// GfxFrameout are synchronised by index
492 	int findIndexByObject(const reg_t object) const;
493 	Plane *findByObject(const reg_t object) const;
494 
495 	/**
496 	 * Gets the priority of the top plane in the plane list.
497 	 */
498 	int16 getTopPlanePriority() const;
499 
500 	/**
501 	 * Gets the priority of the top plane in the plane list created by a game
502 	 * script.
503 	 */
504 	int16 getTopSciPlanePriority() const;
505 
506 	void add(Plane *plane);
507 	void clear();
508 	iterator erase(iterator it);
509 	void erase(Plane *plane);
sort()510 	inline void sort() {
511 		Common::sort(begin(), end(), sortHelper);
512 	}
513 	void remove_at(size_type index);
514 };
515 
516 } // End of namespace Sci
517 
518 #endif
519