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 * This file contains the clipping rectangle code.
22 */
23
24 #include "tinsel/cliprect.h" // object clip rect defs
25 #include "tinsel/graphics.h" // normal object drawing
26 #include "tinsel/object.h"
27 #include "tinsel/palette.h"
28 #include "tinsel/tinsel.h" // for _vm
29
30 namespace Tinsel {
31
32 /**
33 * Resets the clipping rectangle allocator.
34 */
ResetClipRect()35 void ResetClipRect() {
36 _vm->_clipRects.clear();
37 }
38
39 /**
40 * Allocate a clipping rectangle from the free list.
41 * @param pClip clip rectangle dimensions to allocate
42 */
AddClipRect(const Common::Rect & pClip)43 void AddClipRect(const Common::Rect &pClip) {
44 _vm->_clipRects.push_back(pClip);
45 }
46
GetClipRects()47 const RectList &GetClipRects() {
48 return _vm->_clipRects;
49 }
50
51 /**
52 * Creates the intersection of two rectangles.
53 * Returns True if there is a intersection.
54 * @param pDest Pointer to destination rectangle that is to receive the intersection
55 * @param pSrc1 Pointer to a source rectangle
56 * @param pSrc2 Pointer to a source rectangle
57 */
IntersectRectangle(Common::Rect & pDest,const Common::Rect & pSrc1,const Common::Rect & pSrc2)58 bool IntersectRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
59 pDest.left = MAX(pSrc1.left, pSrc2.left);
60 pDest.top = MAX(pSrc1.top, pSrc2.top);
61 pDest.right = MIN(pSrc1.right, pSrc2.right);
62 pDest.bottom = MIN(pSrc1.bottom, pSrc2.bottom);
63
64 return !pDest.isEmpty();
65 }
66
67 /**
68 * Creates the union of two rectangles.
69 * Returns True if there is a union.
70 * @param pDest destination rectangle that is to receive the new union
71 * @param pSrc1 a source rectangle
72 * @param pSrc2 a source rectangle
73 */
UnionRectangle(Common::Rect & pDest,const Common::Rect & pSrc1,const Common::Rect & pSrc2)74 bool UnionRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
75 pDest.left = MIN(pSrc1.left, pSrc2.left);
76 pDest.top = MIN(pSrc1.top, pSrc2.top);
77 pDest.right = MAX(pSrc1.right, pSrc2.right);
78 pDest.bottom = MAX(pSrc1.bottom, pSrc2.bottom);
79
80 return !pDest.isEmpty();
81 }
82
83 /**
84 * Check if the two rectangles are next to each other.
85 * @param pSrc1 a source rectangle
86 * @param pSrc2 a source rectangle
87 */
LooseIntersectRectangle(const Common::Rect & pSrc1,const Common::Rect & pSrc2)88 static bool LooseIntersectRectangle(const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
89 Common::Rect pDest;
90
91 pDest.left = MAX(pSrc1.left, pSrc2.left);
92 pDest.top = MAX(pSrc1.top, pSrc2.top);
93 pDest.right = MIN(pSrc1.right, pSrc2.right);
94 pDest.bottom = MIN(pSrc1.bottom, pSrc2.bottom);
95
96 return pDest.isValidRect();
97 }
98
99 /**
100 * Adds velocities and creates clipping rectangles for all the
101 * objects that have moved on the specified object list.
102 * @param pObjList Playfield display list to draw
103 * @param pWin Playfield window top left position
104 * @param pClip Playfield clipping rectangle
105 * @param bNoVelocity When reset, objects pos is updated with velocity
106 * @param bScrolled) When set, playfield has scrolled
107 */
FindMovingObjects(OBJECT ** pObjList,Common::Point * pWin,Common::Rect * pClip,bool bNoVelocity,bool bScrolled)108 void FindMovingObjects(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip, bool bNoVelocity, bool bScrolled) {
109 OBJECT *pObj; // object list traversal pointer
110
111 for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) {
112 if (!bNoVelocity) {
113 // we want to add velocities to objects position
114
115 if (bScrolled) {
116 // this playfield has scrolled
117
118 // indicate change
119 pObj->flags |= DMA_CHANGED;
120 }
121 }
122
123 if ((pObj->flags & DMA_CHANGED) || // object changed
124 HasPalMoved(pObj->pPal)) { // or palette moved
125 // object has changed in some way
126
127 Common::Rect rcClip; // objects clipped bounding rectangle
128 Common::Rect rcObj; // objects bounding rectangle
129
130 // calc intersection of objects previous bounding rectangle
131 // NOTE: previous position is in screen co-ords
132 if (IntersectRectangle(rcClip, pObj->rcPrev, *pClip)) {
133 // previous position is within clipping rect
134 AddClipRect(rcClip);
135 }
136
137 // calc objects current bounding rectangle
138 if (pObj->flags & DMA_ABS) {
139 // object position is absolute
140 rcObj.left = fracToInt(pObj->xPos);
141 rcObj.top = fracToInt(pObj->yPos);
142 } else {
143 // object position is relative to window
144 rcObj.left = fracToInt(pObj->xPos) - pWin->x;
145 rcObj.top = fracToInt(pObj->yPos) - pWin->y;
146 }
147 rcObj.right = rcObj.left + pObj->width;
148 rcObj.bottom = rcObj.top + pObj->height;
149
150 // calc intersection of object with clipping rect
151 if (IntersectRectangle(rcClip, rcObj, *pClip)) {
152 // current position is within clipping rect
153 AddClipRect(rcClip);
154
155 // update previous position
156 pObj->rcPrev = rcClip;
157 } else {
158 // clear previous position
159 pObj->rcPrev = Common::Rect();
160 }
161
162 // clear changed flag
163 pObj->flags &= ~DMA_CHANGED;
164 }
165 }
166 }
167
168 /**
169 * Merges any clipping rectangles that overlap to try and reduce
170 * the total number of clip rectangles.
171 */
MergeClipRect()172 void MergeClipRect() {
173 RectList &s_rectList = _vm->_clipRects;
174
175 if (s_rectList.size() <= 1)
176 return;
177
178 RectList::iterator rOuter, rInner;
179
180 for (rOuter = s_rectList.begin(); rOuter != s_rectList.end(); ++rOuter) {
181 rInner = rOuter;
182 while (++rInner != s_rectList.end()) {
183
184 if (LooseIntersectRectangle(*rOuter, *rInner)) {
185 // these two rectangles overlap or
186 // are next to each other - merge them
187
188 UnionRectangle(*rOuter, *rOuter, *rInner);
189
190 // remove the inner rect from the list
191 s_rectList.erase(rInner);
192
193 // move back to beginning of list
194 rInner = rOuter;
195 }
196 }
197 }
198 }
199
200 /**
201 * Redraws all objects within this clipping rectangle.
202 * @param pObjList Object list to draw
203 * @param pWin Window top left position
204 * @param pClip Pointer to clip rectangle
205 */
UpdateClipRect(OBJECT ** pObjList,Common::Point * pWin,Common::Rect * pClip)206 void UpdateClipRect(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip) {
207 int x, y, right, bottom; // object corners
208 int hclip, vclip; // total size of object clipping
209 DRAWOBJECT currentObj; // filled in to draw the current object in list
210 OBJECT *pObj; // object list iterator
211
212 // Initialize the fields of the drawing object to empty
213 memset(¤tObj, 0, sizeof(DRAWOBJECT));
214
215 for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) {
216 if (pObj->flags & DMA_ABS) {
217 // object position is absolute
218 x = fracToInt(pObj->xPos);
219 y = fracToInt(pObj->yPos);
220 } else {
221 // object position is relative to window
222 x = fracToInt(pObj->xPos) - pWin->x;
223 y = fracToInt(pObj->yPos) - pWin->y;
224 }
225
226 // calc object right
227 right = x + pObj->width;
228 if (right < 0)
229 // totally clipped if negative
230 continue;
231
232 // calc object bottom
233 bottom = y + pObj->height;
234 if (bottom < 0)
235 // totally clipped if negative
236 continue;
237
238 // bottom clip = low right y - clip low right y
239 currentObj.botClip = bottom - pClip->bottom;
240 if (currentObj.botClip < 0) {
241 // negative - object is not clipped
242 currentObj.botClip = 0;
243 }
244
245 // right clip = low right x - clip low right x
246 currentObj.rightClip = right - pClip->right;
247 if (currentObj.rightClip < 0) {
248 // negative - object is not clipped
249 currentObj.rightClip = 0;
250 }
251
252 // top clip = clip top left y - top left y
253 currentObj.topClip = pClip->top - y;
254 if (currentObj.topClip < 0) {
255 // negative - object is not clipped
256 currentObj.topClip = 0;
257 } else { // clipped - adjust start position to top of clip rect
258 y = pClip->top;
259 }
260
261 // left clip = clip top left x - top left x
262 currentObj.leftClip = pClip->left - x;
263 if (currentObj.leftClip < 0) {
264 // negative - object is not clipped
265 currentObj.leftClip = 0;
266 } else {
267 // NOTE: This else statement is disabled in tinsel v1
268 // clipped - adjust start position to left of clip rect
269 x = pClip->left;
270 }
271
272 // calc object total horizontal clipping
273 hclip = currentObj.leftClip + currentObj.rightClip;
274
275 // calc object total vertical clipping
276 vclip = currentObj.topClip + currentObj.botClip;
277
278 if (hclip + vclip != 0) {
279 // object is clipped in some way
280
281 if (pObj->width <= hclip)
282 // object totally clipped horizontally - ignore
283 continue;
284
285 if (pObj->height <= vclip)
286 // object totally clipped vertically - ignore
287 continue;
288
289 // set clip bit in objects flags
290 currentObj.flags = pObj->flags | DMA_CLIP;
291 } else { // object is not clipped - copy flags
292 currentObj.flags = pObj->flags;
293 }
294
295 // copy objects properties to local object
296 currentObj.width = pObj->width;
297 currentObj.height = pObj->height;
298 currentObj.xPos = (short)x;
299 currentObj.yPos = (short)y;
300 currentObj.pPal = pObj->pPal;
301 currentObj.constant = pObj->constant;
302 currentObj.hBits = pObj->hBits;
303
304 // draw the object
305 DrawObject(¤tObj);
306 }
307 }
308
309 } // End of namespace Tinsel
310