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 #include "twine/menu/interface.h"
24 #include "graphics/managed_surface.h"
25 #include "graphics/primitives.h"
26 #include "twine/twine.h"
27 
28 namespace TwinE {
29 
Interface(TwinEEngine * engine)30 Interface::Interface(TwinEEngine *engine) : _engine(engine) {}
31 
32 const int32 INSIDE = 0; // 0000
33 const int32 LEFT = 1;   // 0001
34 const int32 RIGHT = 2;  // 0010
35 const int32 TOP = 4;    // 0100
36 const int32 BOTTOM = 8; // 1000
37 
checkClipping(int32 x,int32 y) const38 int32 Interface::checkClipping(int32 x, int32 y) const {
39 	int32 code = INSIDE;
40 	if (x < _clip.left) {
41 		code |= LEFT;
42 	} else if (x > _clip.right) {
43 		code |= RIGHT;
44 	}
45 	if (y < _clip.top) {
46 		code |= TOP;
47 	} else if (y > _clip.bottom) {
48 		code |= BOTTOM;
49 	}
50 	return code;
51 }
52 
drawLine(int32 startWidth,int32 startHeight,int32 endWidth,int32 endHeight,uint8 lineColor)53 bool Interface::drawLine(int32 startWidth, int32 startHeight, int32 endWidth, int32 endHeight, uint8 lineColor) {
54 	// draw line from left to right
55 	if (startWidth > endWidth) {
56 		SWAP(endWidth, startWidth);
57 		SWAP(endHeight, startHeight);
58 	}
59 
60 	// Perform proper clipping (CohenSutherland algorithm)
61 	int32 outcode0 = checkClipping(startWidth, startHeight);
62 	int32 outcode1 = checkClipping(endWidth, endHeight);
63 
64 	while ((outcode0 | outcode1) != INSIDE) {
65 		if ((outcode0 & outcode1) != INSIDE && outcode0 != INSIDE) {
66 			return false; // Reject lines which are behind one clipping plane
67 		}
68 
69 		// At least one endpoint is outside the clip rectangle; pick it.
70 		const int32 outcodeOut = outcode0 ? outcode0 : outcode1;
71 
72 		int32 x = 0;
73 		int32 y = 0;
74 		if (outcodeOut & TOP) { // point is above the clip rectangle
75 			x = startWidth + (int)((endWidth - startWidth) * (float)(_clip.top - startHeight) / (float)(endHeight - startHeight));
76 			y = _clip.top;
77 		} else if (outcodeOut & BOTTOM) { // point is below the clip rectangle
78 			x = startWidth + (int)((endWidth - startWidth) * (float)(_clip.bottom - startHeight) / (float)(endHeight - startHeight));
79 			y = _clip.bottom;
80 		} else if (outcodeOut & RIGHT) { // point is to the right of clip rectangle
81 			y = startHeight + (int)((endHeight - startHeight) * (float)(_clip.right - startWidth) / (float)(endWidth - startWidth));
82 			x = _clip.right;
83 		} else if (outcodeOut & LEFT) { // point is to the left of clip rectangle
84 			y = startHeight + (int)((endHeight - startHeight) * (float)(_clip.left - startWidth) / (float)(endWidth - startWidth));
85 			x = _clip.left;
86 		}
87 
88 		// Clip the point
89 		if (outcodeOut == outcode0) {
90 			startWidth = x;
91 			startHeight = y;
92 			outcode0 = checkClipping(startWidth, startHeight);
93 		} else {
94 			endWidth = x;
95 			endHeight = y;
96 			outcode1 = checkClipping(endWidth, endHeight);
97 		}
98 	}
99 
100 	int32 pitch = _engine->width();
101 	endWidth -= startWidth;
102 	endHeight -= startHeight;
103 	if (endHeight < 0) {
104 		pitch = -pitch;
105 		endHeight = -endHeight;
106 	}
107 
108 	uint8 *out = (uint8*)_engine->_frontVideoBuffer.getBasePtr(startWidth, startHeight);
109 	_engine->_frontVideoBuffer.addDirtyRect(Common::Rect(startWidth, startHeight, startWidth + endWidth, startHeight + endHeight));
110 
111 	if (endWidth < endHeight) { // significant slope
112 		SWAP(endWidth, endHeight);
113 		const int16 var2 = endWidth << 1;
114 		startHeight = endWidth;
115 		endHeight <<= 1;
116 		endWidth++;
117 		do {
118 			*out = lineColor;
119 			startHeight -= endHeight;
120 			if (startHeight > 0) {
121 				out += pitch;
122 			} else {
123 				startHeight += var2;
124 				out += pitch + 1;
125 			}
126 		} while (--endWidth);
127 	} else { // reduced slope
128 		const int16 var2 = endWidth << 1;
129 		startHeight = endWidth;
130 		endHeight <<= 1;
131 		endWidth++;
132 		do {
133 			*out++ = lineColor;
134 			startHeight -= endHeight;
135 			if (startHeight < 0) {
136 				startHeight += var2;
137 				out += pitch;
138 			}
139 		} while (--endWidth);
140 	}
141 	return true;
142 }
143 
blitBox(const Common::Rect & rect,const Graphics::ManagedSurface & source,Graphics::ManagedSurface & dest)144 void Interface::blitBox(const Common::Rect &rect, const Graphics::ManagedSurface &source, Graphics::ManagedSurface &dest) {
145 	Common::Rect r(rect);
146 	r.right += 1;
147 	r.bottom += 1;
148 	dest.blitFrom(source, r, Common::Point(rect.left, rect.top));
149 }
150 
drawTransparentBox(const Common::Rect & rect,int32 colorAdj)151 void Interface::drawTransparentBox(const Common::Rect &rect, int32 colorAdj) {
152 	Common::Rect r = rect;
153 	r.clip(_engine->rect());
154 	if (r.isEmpty()) {
155 		return;
156 	}
157 
158 	uint8 *pos = (uint8*)_engine->_frontVideoBuffer.getBasePtr(0, r.top);
159 
160 	for (int32 y = r.top; y <= r.bottom; ++y) {
161 		for (int32 x = r.left; x <= r.right; ++x) {
162 			const int8 color = (pos[x] & 0x0F) - colorAdj;
163 			const int8 color2 = pos[x] & 0xF0;
164 			if (color < 0) {
165 				pos[x] = color2;
166 			} else {
167 				pos[x] = color + color2;
168 			}
169 		}
170 		pos += _engine->_frontVideoBuffer.pitch;
171 	}
172 	_engine->_frontVideoBuffer.addDirtyRect(r);
173 }
174 
drawFilledRect(const Common::Rect & rect,uint8 colorIndex)175 void Interface::drawFilledRect(const Common::Rect &rect, uint8 colorIndex) {
176 	if (!rect.isValidRect()) {
177 		return;
178 	}
179 	_engine->_frontVideoBuffer.fillRect(Common::Rect(rect.left, rect.top, rect.right + 1, rect.bottom + 1), colorIndex);
180 }
181 
setClip(const Common::Rect & rect)182 bool Interface::setClip(const Common::Rect &rect) {
183 	if (!_clip.isValidRect()) {
184 		return false;
185 	}
186 	_clip = rect;
187 	_clip.clip(_engine->rect());
188 	return true;
189 }
190 
saveClip()191 void Interface::saveClip() {
192 	_savedClip = _clip;
193 }
194 
loadClip()195 void Interface::loadClip() {
196 	_clip = _savedClip;
197 }
198 
resetClip()199 void Interface::resetClip() {
200 	_clip = _engine->rect();
201 }
202 
203 } // namespace TwinE
204