Line data Source code
1 :
2 : /* Copyright (c) 2006-2017, Stefan Eilemann <eile@equalizergraphics.com>
3 : *
4 : * This library is free software; you can redistribute it and/or modify it under
5 : * the terms of the GNU Lesser General Public License version 2.1 as published
6 : * by the Free Software Foundation.
7 : *
8 : * This library is distributed in the hope that it will be useful, but WITHOUT
9 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 : * details.
12 : *
13 : * You should have received a copy of the GNU Lesser General Public License
14 : * along with this library; if not, write to the Free Software Foundation, Inc.,
15 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 : */
17 :
18 : #ifndef EQFABRIC_PIXELVIEWPORT_H
19 : #define EQFABRIC_PIXELVIEWPORT_H
20 :
21 : #include <eq/fabric/pixel.h> // used in inline method
22 : #include <eq/fabric/viewport.h> // used in inline method
23 : #include <eq/fabric/zoom.h> // used in inline method
24 :
25 : #include <lunchbox/debug.h>
26 :
27 : #include <limits>
28 :
29 : namespace eq
30 : {
31 : namespace fabric
32 : {
33 : /** Holds a 2D pixel viewport with methods for manipulation. */
34 : class PixelViewport
35 : {
36 : public:
37 : /** @name Constructors */
38 : //@{
39 : /** Construct a new, invalid pixel viewport. @version 1.0 */
40 30084 : PixelViewport()
41 30084 : : x(0)
42 : , y(0)
43 : , w(-1)
44 30084 : , h(-1)
45 : {
46 30084 : }
47 :
48 : /** Construct a new pixel viewport with default values. @version 1.0 */
49 628 : PixelViewport(const int32_t x_, const int32_t y_, const int32_t w_,
50 : const int32_t h_)
51 628 : : x(x_)
52 : , y(y_)
53 : , w(w_)
54 628 : , h(h_)
55 : {
56 628 : }
57 :
58 : /** Construct a new pixel viewport with default values. @version 1.1.6*/
59 4 : explicit PixelViewport(const int32_t pvp[4])
60 4 : : x(pvp[0])
61 4 : , y(pvp[1])
62 4 : , w(pvp[2])
63 12 : , h(pvp[3])
64 : {
65 4 : }
66 : //@}
67 :
68 : /** @name Data Access */
69 : //@{
70 : /** Invalidate the pixel viewport. @version 1.0 */
71 1758 : void invalidate()
72 : {
73 1758 : x = 0;
74 1758 : y = 0;
75 1758 : w = -1;
76 1758 : h = -1;
77 1758 : }
78 :
79 : /**
80 : * @return true if the pixel viewport has a non-negative, but
81 : * potentially empty, size.
82 : * @version 1.0
83 : */
84 7608 : bool isValid() const { return (w >= 0 && h >= 0); }
85 : /**
86 : * @return true if the pixel viewport has a non-zero area, i.e, it is
87 : * not empty.
88 : * @version 1.0
89 : */
90 5807 : bool hasArea() const { return (w > 0 && h > 0); }
91 : /** @return the area in pixels. @version 1.0 */
92 84 : uint32_t getArea() const { return w * h; }
93 : /**
94 : * @return true if the given point is inside the pixel viewport.
95 : * @version 1.0
96 : */
97 0 : bool isInside(const int32_t pX, const int32_t pY) const
98 : {
99 0 : if (pX < x || pY < y || pX > (x + w) || pY > (y + h))
100 0 : return false;
101 0 : return true;
102 : }
103 : //@}
104 :
105 : /** @name Operations */
106 : //@{
107 : /** Apply a fractional viewport to this pixel viewport. @internal */
108 5776 : PixelViewport& apply(const Viewport& vp)
109 : {
110 : // honor position over size to avoid rounding artifacts
111 5776 : const int32_t xEnd = x + int32_t((vp.x + vp.w) * w);
112 5776 : const int32_t yEnd = y + int32_t((vp.y + vp.h) * h);
113 :
114 5776 : x += int32_t(w * vp.x);
115 5776 : y += int32_t(h * vp.y);
116 5776 : w = xEnd - x;
117 5776 : h = yEnd - y;
118 5776 : return *this;
119 : }
120 :
121 : /** Apply a pixel decomposition to this pixel viewport. @internal */
122 590 : void apply(const Pixel& pixel)
123 : {
124 590 : if (pixel.w > 1)
125 : {
126 0 : int32_t newWidth = w / pixel.w;
127 : // This would be the correct thing to do, but it would
128 : // require frustum adaptations in CUV::_computeFrustum:
129 : // if( w - ( newWidth * pixel.w ) > pixel.x )
130 0 : if (w - (newWidth * pixel.w) != 0)
131 0 : ++newWidth;
132 :
133 0 : w = newWidth;
134 : }
135 590 : if (pixel.h > 1)
136 : {
137 0 : int32_t newHeight = h / pixel.h;
138 : // This would be the correct thing to do, but it would
139 : // require frustum adaptations in CUV::_computeFrustum:
140 : // if( w - ( newWidth * pixel.h ) > pixel.y )
141 0 : if (h - (newHeight * pixel.h) != 0)
142 0 : ++newHeight;
143 :
144 0 : h = newHeight;
145 : }
146 590 : }
147 :
148 : /** Apply a zoom to this pixel viewport. @internal */
149 296 : void apply(const Zoom& zoom)
150 : {
151 296 : if (zoom == Zoom::NONE)
152 296 : return;
153 :
154 0 : x = static_cast<int32_t>(x * zoom.x() + .5f);
155 0 : y = static_cast<int32_t>(y * zoom.y() + .5f);
156 0 : w = static_cast<int32_t>(w * zoom.x() + .5f);
157 0 : h = static_cast<int32_t>(h * zoom.y() + .5f);
158 : }
159 :
160 : /**
161 : * @return the zoom which would result in the given rhs pixel
162 : * viewport when applied to this pixel viewport.
163 : * @internal
164 : */
165 590 : const Zoom getZoom(const PixelViewport& rhs) const
166 : {
167 590 : if (*this == rhs)
168 590 : return Zoom::NONE;
169 :
170 0 : if (!rhs.hasArea())
171 0 : return Zoom(std::numeric_limits<float>::max(),
172 0 : std::numeric_limits<float>::max());
173 :
174 0 : return Zoom(w / static_cast<float>(rhs.w),
175 0 : h / static_cast<float>(rhs.h));
176 : }
177 :
178 : /** @return the X end position. @version 1.0 */
179 522 : int32_t getXEnd() const { return x + w; }
180 : /** @return the Y end position. @version 1.0 */
181 522 : int32_t getYEnd() const { return y + h; }
182 : /** Convert into a lunchbox::Plugin usable format. @version 1.5.2 */
183 4 : void convertToPlugin(uint64_t dims[4]) const
184 : {
185 4 : dims[0] = x;
186 4 : dims[1] = w;
187 4 : dims[2] = y;
188 4 : dims[3] = h;
189 4 : }
190 :
191 : /** Convert from a lunchbox::Plugin format. @version 1.5.2 */
192 1 : void convertFromPlugin(const uint64_t dims[4])
193 : {
194 1 : x = int32_t(dims[0]);
195 1 : w = int32_t(dims[1]);
196 1 : y = int32_t(dims[2]);
197 1 : h = int32_t(dims[3]);
198 1 : }
199 :
200 : /** @return the addition of this pvp with an offset. @version 1.0 */
201 66 : const PixelViewport operator+(const Vector2i& offset) const
202 : {
203 66 : return PixelViewport(x + offset.x(), y + offset.y(), w, h);
204 : }
205 :
206 : /** @internal
207 : * @return the viewport which would provide this pixel viewport when
208 : * applied to the rhs pixel viewport.
209 : */
210 1279 : Viewport operator/(const PixelViewport& rhs) const
211 : {
212 1279 : if (*this == rhs)
213 488 : return Viewport::FULL;
214 :
215 791 : if (!rhs.hasArea())
216 1384 : return Viewport(static_cast<float>(x), static_cast<float>(y), 0.f,
217 692 : 0.f);
218 :
219 693 : return Viewport((x - rhs.x) / static_cast<float>(rhs.w),
220 198 : (y - rhs.y) / static_cast<float>(rhs.h),
221 198 : (w) / static_cast<float>(rhs.w),
222 297 : (h) / static_cast<float>(rhs.h));
223 : }
224 :
225 : /** @return this pvp minus an offset. @version 1.3.0 */
226 1 : const PixelViewport& operator-=(const Vector2i& offset)
227 : {
228 1 : x -= offset.x();
229 1 : y -= offset.y();
230 1 : return *this;
231 : }
232 :
233 : /**
234 : * Perform the inverse operation of applying a pixel decomposition to
235 : * this pixel viewport.
236 : * @version 1.0
237 : */
238 0 : void unapply(const Pixel& pixel)
239 : {
240 0 : w *= pixel.w;
241 0 : h *= pixel.h;
242 0 : x += pixel.x;
243 0 : y += pixel.y;
244 0 : }
245 :
246 : /**
247 : * @return true if the two pixel viewports are identical.
248 : * @version 1.0
249 : */
250 3161 : bool operator==(const PixelViewport& rhs) const
251 : {
252 3161 : return (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h);
253 : }
254 :
255 : /**
256 : * @return true if the two pixel viewports are not identical.
257 : * @version 1.0
258 : */
259 5352 : bool operator!=(const PixelViewport& rhs) const
260 : {
261 5352 : return (x != rhs.x || y != rhs.y || w != rhs.w || h != rhs.h);
262 : }
263 :
264 : /**
265 : * Create a pixel viewport that includes both viewports (union).
266 : * @version 1.0
267 : */
268 75 : PixelViewport& merge(const PixelViewport& rhs)
269 : {
270 75 : if (*this == rhs || !rhs.isValid())
271 54 : return *this;
272 :
273 21 : if (!hasArea())
274 : {
275 19 : *this = rhs;
276 19 : return *this;
277 : }
278 2 : if (!rhs.hasArea())
279 0 : return *this;
280 :
281 2 : const int32_t sEx = x + w;
282 2 : const int32_t sEy = y + h;
283 2 : const int32_t dEx = rhs.x + rhs.w;
284 2 : const int32_t dEy = rhs.y + rhs.h;
285 :
286 2 : x = LB_MIN(x, rhs.x);
287 2 : y = LB_MIN(y, rhs.y);
288 2 : w = LB_MAX(sEx, dEx) - x;
289 2 : h = LB_MAX(sEy, dEy) - y;
290 2 : return *this;
291 : }
292 :
293 : /** Create the intersection of the two pixel viewports. @version 1.0 */
294 6 : void intersect(const PixelViewport& rhs)
295 : {
296 6 : if (*this == rhs)
297 3 : return;
298 :
299 3 : if (!rhs.isValid() || !isValid())
300 : {
301 0 : invalidate();
302 0 : return;
303 : }
304 :
305 3 : if (!rhs.hasArea() || !hasArea())
306 : {
307 0 : x = 0;
308 0 : y = 0;
309 0 : w = 0;
310 0 : h = 0;
311 0 : return;
312 : }
313 :
314 3 : const int32_t sEx = x + w;
315 3 : const int32_t sEy = y + h;
316 3 : const int32_t dEx = rhs.x + rhs.w;
317 3 : const int32_t dEy = rhs.y + rhs.h;
318 :
319 3 : x = LB_MAX(x, rhs.x);
320 3 : y = LB_MAX(y, rhs.y);
321 3 : w = LB_MIN(sEx, dEx) - x;
322 3 : h = LB_MIN(sEy, dEy) - y;
323 : }
324 :
325 : //@}
326 :
327 : int32_t x;
328 : int32_t y;
329 : int32_t w;
330 : int32_t h;
331 : };
332 :
333 732 : inline std::ostream& operator<<(std::ostream& os, const PixelViewport& pvp)
334 : {
335 732 : os << "[ " << pvp.x << " " << pvp.y << " " << pvp.w << " " << pvp.h << " ]";
336 732 : return os;
337 : }
338 : }
339 : }
340 :
341 : #endif // EQFABRIC_PIXELVIEWPORT_H
|