1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include <stdio.h> // for FILE
11 #include <stdint.h> // for int32_t, int64_t
12 #include <algorithm> // for min/max
13 #include "mozilla/Likely.h" // for MOZ_UNLIKELY
14 #include "mozilla/gfx/Rect.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/gfx/Logging.h"
17 #include "nsCoord.h" // for nscoord, etc
18 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
19 #include "nsPoint.h" // for nsIntPoint, nsPoint
20 #include "nsMargin.h" // for nsIntMargin, nsMargin
21 #include "nsSize.h" // for IntSize, nsSize
22 #include "nscore.h" // for NS_BUILD_REFCNT_LOGGING
23 #if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || \
24 (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
25 # if defined(_MSC_VER) && !defined(__clang__)
26 # include "smmintrin.h"
28 # include "emmintrin.h"
32 typedef mozilla::gfx::IntRect nsIntRect
;
34 struct nsRect
: public mozilla::gfx::BaseRect
<nscoord
, nsRect
, nsPoint
, nsSize
,
36 typedef mozilla::gfx::BaseRect
<nscoord
, nsRect
, nsPoint
, nsSize
, nsMargin
>
39 static void VERIFY_COORD(nscoord aValue
) { ::VERIFY_COORD(aValue
); }
42 nsRect() : Super() { MOZ_COUNT_CTOR(nsRect
); }
43 nsRect(const nsRect
& aRect
) : Super(aRect
) { MOZ_COUNT_CTOR(nsRect
); }
44 nsRect(const nsPoint
& aOrigin
, const nsSize
& aSize
) : Super(aOrigin
, aSize
) {
45 MOZ_COUNT_CTOR(nsRect
);
47 nsRect(nscoord aX
, nscoord aY
, nscoord aWidth
, nscoord aHeight
)
48 : Super(aX
, aY
, aWidth
, aHeight
) {
49 MOZ_COUNT_CTOR(nsRect
);
52 #ifdef NS_BUILD_REFCNT_LOGGING
53 ~nsRect() { MOZ_COUNT_DTOR(nsRect
); }
56 // We have saturating versions of all the Union methods. These avoid
57 // overflowing nscoord values in the 'width' and 'height' fields by
58 // clamping the width and height values to nscoord_MAX if necessary.
60 MOZ_MUST_USE nsRect
SaturatingUnion(const nsRect
& aRect
) const {
63 } else if (aRect
.IsEmpty()) {
64 return *static_cast<const nsRect
*>(this);
66 return SaturatingUnionEdges(aRect
);
70 MOZ_MUST_USE nsRect
SaturatingUnionEdges(const nsRect
& aRect
) const {
71 #ifdef NS_COORD_IS_FLOAT
72 return UnionEdges(aRect
);
74 nscoord resultX
= std::min(aRect
.X(), x
);
76 std::max(int64_t(aRect
.X()) + aRect
.Width(), int64_t(x
) + width
) -
78 if (MOZ_UNLIKELY(w
> nscoord_MAX
)) {
79 // Clamp huge negative x to nscoord_MIN / 2 and try again.
80 resultX
= std::max(resultX
, nscoord_MIN
/ 2);
81 w
= std::max(int64_t(aRect
.X()) + aRect
.Width(), int64_t(x
) + width
) -
83 if (MOZ_UNLIKELY(w
> nscoord_MAX
)) {
88 nscoord resultY
= std::min(aRect
.y
, y
);
90 std::max(int64_t(aRect
.Y()) + aRect
.Height(), int64_t(y
) + height
) -
92 if (MOZ_UNLIKELY(h
> nscoord_MAX
)) {
93 // Clamp huge negative y to nscoord_MIN / 2 and try again.
94 resultY
= std::max(resultY
, nscoord_MIN
/ 2);
95 h
= std::max(int64_t(aRect
.Y()) + aRect
.Height(), int64_t(y
) + height
) -
97 if (MOZ_UNLIKELY(h
> nscoord_MAX
)) {
101 return nsRect(resultX
, resultY
, nscoord(w
), nscoord(h
));
105 #ifndef NS_COORD_IS_FLOAT
106 // Make all nsRect Union methods be saturating.
107 MOZ_MUST_USE nsRect
UnionEdges(const nsRect
& aRect
) const {
108 return SaturatingUnionEdges(aRect
);
110 void UnionRectEdges(const nsRect
& aRect1
, const nsRect
& aRect2
) {
111 *this = aRect1
.UnionEdges(aRect2
);
113 MOZ_MUST_USE nsRect
Union(const nsRect
& aRect
) const {
114 return SaturatingUnion(aRect
);
116 MOZ_MUST_USE nsRect
UnsafeUnion(const nsRect
& aRect
) const {
117 return Super::Union(aRect
);
119 void UnionRect(const nsRect
& aRect1
, const nsRect
& aRect2
) {
120 *this = aRect1
.Union(aRect2
);
123 # if defined(_MSC_VER) && !defined(__clang__) && \
124 (defined(_M_X64) || defined(_M_IX86))
125 // Only MSVC supports inlining intrinsics for archs you're not compiling for.
126 MOZ_MUST_USE nsRect
Intersect(const nsRect
& aRect
) const {
128 if (mozilla::gfx::Factory::HasSSE4()) {
129 __m128i rect1
= _mm_loadu_si128((__m128i
*)&aRect
); // x1, y1, w1, h1
130 __m128i rect2
= _mm_loadu_si128((__m128i
*)this); // x2, y2, w2, h2
132 __m128i resultRect
= _mm_max_epi32(rect1
, rect2
); // xr, yr, zz, zz
134 // result.width = std::min<int32_t>(x - result.x + width,
135 // aRect.x - result.x + aRect.width);
136 // result.height = std::min<int32_t>(y - result.y + height,
137 // aRect.y - result.y + aRect.height);
138 __m128i widthheight
= _mm_min_epi32(
139 _mm_add_epi32(_mm_sub_epi32(rect1
, resultRect
),
140 _mm_srli_si128(rect1
, 8)),
141 _mm_add_epi32(_mm_sub_epi32(rect2
, resultRect
),
142 _mm_srli_si128(rect2
, 8))); // w, h, zz, zz
143 widthheight
= _mm_slli_si128(widthheight
, 8); // 00, 00, wr, hr
146 _mm_blend_epi16(resultRect
, widthheight
, 0xF0); // xr, yr, wr, hr
148 if ((_mm_movemask_ps(_mm_castsi128_ps(
149 _mm_cmplt_epi32(resultRect
, _mm_setzero_si128()))) &
151 // It's potentially more efficient to store all 0s. But the non SSE4
152 // code leaves x/y intact so let's do the same here.
153 resultRect
= _mm_and_si128(resultRect
,
154 _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
157 _mm_storeu_si128((__m128i
*)&result
, resultRect
);
162 result
.x
= std::max
<int32_t>(x
, aRect
.x
);
163 result
.y
= std::max
<int32_t>(y
, aRect
.y
);
164 result
.width
= std::min
<int32_t>(x
- result
.x
+ width
,
165 aRect
.x
- result
.x
+ aRect
.width
);
166 result
.height
= std::min
<int32_t>(y
- result
.y
+ height
,
167 aRect
.y
- result
.y
+ aRect
.height
);
168 if (result
.width
< 0 || result
.height
< 0) {
174 bool IntersectRect(const nsRect
& aRect1
, const nsRect
& aRect2
) {
175 if (mozilla::gfx::Factory::HasSSE4()) {
176 __m128i rect1
= _mm_loadu_si128((__m128i
*)&aRect1
); // x1, y1, w1, h1
177 __m128i rect2
= _mm_loadu_si128((__m128i
*)&aRect2
); // x2, y2, w2, h2
179 __m128i resultRect
= _mm_max_epi32(rect1
, rect2
); // xr, yr, zz, zz
180 // result.width = std::min<int32_t>(x - result.x + width,
181 // aRect.x - result.x + aRect.width);
182 // result.height = std::min<int32_t>(y - result.y + height,
183 // aRect.y - result.y + aRect.height);
184 __m128i widthheight
= _mm_min_epi32(
185 _mm_add_epi32(_mm_sub_epi32(rect1
, resultRect
),
186 _mm_srli_si128(rect1
, 8)),
187 _mm_add_epi32(_mm_sub_epi32(rect2
, resultRect
),
188 _mm_srli_si128(rect2
, 8))); // w, h, zz, zz
189 widthheight
= _mm_slli_si128(widthheight
, 8); // 00, 00, wr, hr
192 _mm_blend_epi16(resultRect
, widthheight
, 0xF0); // xr, yr, wr, hr
194 if ((_mm_movemask_ps(_mm_castsi128_ps(
195 _mm_cmpgt_epi32(resultRect
, _mm_setzero_si128()))) &
197 // It's potentially more efficient to store all 0s. But the non SSE4
198 // code leaves x/y intact so let's do the same here.
199 resultRect
= _mm_and_si128(resultRect
,
200 _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
201 _mm_storeu_si128((__m128i
*)this, resultRect
);
205 _mm_storeu_si128((__m128i
*)this, resultRect
);
210 int32_t newX
= std::max
<int32_t>(aRect1
.x
, aRect2
.x
);
211 int32_t newY
= std::max
<int32_t>(aRect1
.y
, aRect2
.y
);
212 width
= std::min
<int32_t>(aRect1
.x
- newX
+ aRect1
.width
,
213 aRect2
.x
- newX
+ aRect2
.width
);
214 height
= std::min
<int32_t>(aRect1
.y
- newY
+ aRect1
.height
,
215 aRect2
.y
- newY
+ aRect2
.height
);
218 if (width
<= 0 || height
<= 0) {
227 void SaturatingUnionRect(const nsRect
& aRect1
, const nsRect
& aRect2
) {
228 *this = aRect1
.SaturatingUnion(aRect2
);
230 void SaturatingUnionRectEdges(const nsRect
& aRect1
, const nsRect
& aRect2
) {
231 *this = aRect1
.SaturatingUnionEdges(aRect2
);
234 // Return whether this rect's right or bottom edge overflow int32.
235 bool Overflows() const;
238 * Return this rect scaled to a different appunits per pixel (APP) ratio.
239 * In the RoundOut version we make the rect the smallest rect containing the
240 * unrounded result. In the RoundIn version we make the rect the largest rect
241 * contained in the unrounded result.
242 * @param aFromAPP the APP to scale from
243 * @param aToAPP the APP to scale to
244 * @note this can turn an empty rectangle into a non-empty rectangle
246 MOZ_MUST_USE
inline nsRect
ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP
,
247 int32_t aToAPP
) const;
248 MOZ_MUST_USE
inline nsRect
ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP
,
249 int32_t aToAPP
) const;
251 MOZ_MUST_USE
inline mozilla::gfx::IntRect
ScaleToNearestPixels(
252 float aXScale
, float aYScale
, nscoord aAppUnitsPerPixel
) const;
254 MOZ_MUST_USE
inline mozilla::gfx::IntRect
ToNearestPixels(
255 nscoord aAppUnitsPerPixel
) const;
257 // Note: this can turn an empty rectangle into a non-empty rectangle
258 MOZ_MUST_USE
inline mozilla::gfx::IntRect
ScaleToOutsidePixels(
259 float aXScale
, float aYScale
, nscoord aAppUnitsPerPixel
) const;
261 // Note: this can turn an empty rectangle into a non-empty rectangle
262 MOZ_MUST_USE
inline mozilla::gfx::IntRect
ToOutsidePixels(
263 nscoord aAppUnitsPerPixel
) const;
265 MOZ_MUST_USE
inline mozilla::gfx::IntRect
ScaleToInsidePixels(
266 float aXScale
, float aYScale
, nscoord aAppUnitsPerPixel
) const;
268 MOZ_MUST_USE
inline mozilla::gfx::IntRect
ToInsidePixels(
269 nscoord aAppUnitsPerPixel
) const;
271 // This is here only to keep IPDL-generated code happy. DO NOT USE.
272 bool operator==(const nsRect
& aRect
) const { return IsEqualEdges(aRect
); }
274 MOZ_MUST_USE
inline nsRect
RemoveResolution(const float aResolution
) const;
278 * App Unit/Pixel conversions
281 inline nsRect
nsRect::ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP
,
282 int32_t aToAPP
) const {
283 if (aFromAPP
== aToAPP
) {
288 rect
.SetBox(NSToCoordFloor(NSCoordScale(x
, aFromAPP
, aToAPP
)),
289 NSToCoordFloor(NSCoordScale(y
, aFromAPP
, aToAPP
)),
290 NSToCoordCeil(NSCoordScale(XMost(), aFromAPP
, aToAPP
)),
291 NSToCoordCeil(NSCoordScale(YMost(), aFromAPP
, aToAPP
)));
295 inline nsRect
nsRect::ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP
,
296 int32_t aToAPP
) const {
297 if (aFromAPP
== aToAPP
) {
302 rect
.SetBox(NSToCoordCeil(NSCoordScale(x
, aFromAPP
, aToAPP
)),
303 NSToCoordCeil(NSCoordScale(y
, aFromAPP
, aToAPP
)),
304 NSToCoordFloor(NSCoordScale(XMost(), aFromAPP
, aToAPP
)),
305 NSToCoordFloor(NSCoordScale(YMost(), aFromAPP
, aToAPP
)));
309 #if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || \
310 (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
311 // Life would be so much better if we had SSE4 here.
312 static MOZ_ALWAYS_INLINE __m128i
floor_ps2epi32(__m128 x
) {
313 __m128 one
= _mm_set_ps(1.0f
, 1.0f
, 1.0f
, 1.0f
);
315 __m128 t
= _mm_cvtepi32_ps(_mm_cvttps_epi32(x
));
316 __m128 r
= _mm_sub_ps(t
, _mm_and_ps(_mm_cmplt_ps(x
, t
), one
));
318 return _mm_cvttps_epi32(r
);
321 static MOZ_ALWAYS_INLINE __m128i
ceil_ps2epi32(__m128 x
) {
322 __m128 t
= _mm_sub_ps(_mm_setzero_ps(), x
);
323 __m128i r
= _mm_sub_epi32(_mm_setzero_si128(), floor_ps2epi32(t
));
329 // scale the rect but round to preserve centers
330 inline mozilla::gfx::IntRect
nsRect::ScaleToNearestPixels(
331 float aXScale
, float aYScale
, nscoord aAppUnitsPerPixel
) const {
332 mozilla::gfx::IntRect rect
;
333 // Android x86 builds have bindgen issues.
334 #if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || \
335 (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
336 __m128 appUnitsPacked
= _mm_set_ps(aAppUnitsPerPixel
, aAppUnitsPerPixel
,
337 aAppUnitsPerPixel
, aAppUnitsPerPixel
);
338 __m128 scalesPacked
= _mm_set_ps(aYScale
, aXScale
, aYScale
, aXScale
);
339 __m128 biasesPacked
= _mm_set_ps(0.5f
, 0.5f
, 0.5f
, 0.5f
);
341 __m128i rectPacked
= _mm_loadu_si128((__m128i
*)this);
342 __m128i topLeft
= _mm_slli_si128(rectPacked
, 8);
344 rectPacked
= _mm_add_epi32(rectPacked
, topLeft
); // X, Y, XMost(), YMost()
346 __m128 rectFloat
= _mm_cvtepi32_ps(rectPacked
);
348 // Scale, i.e. ([ x y xmost ymost ] / aAppUnitsPerPixel) * [ aXScale aYScale
350 rectFloat
= _mm_mul_ps(_mm_div_ps(rectFloat
, appUnitsPacked
), scalesPacked
);
353 // Executed with bias and roundmode down, since round-nearest rounds 0.5
354 // downward half the time.
355 rectFloat
= _mm_add_ps(rectFloat
, biasesPacked
);
356 rectPacked
= floor_ps2epi32(rectFloat
);
358 topLeft
= _mm_slli_si128(rectPacked
, 8);
359 rectPacked
= _mm_sub_epi32(rectPacked
, topLeft
); // X, Y, Width, Height
361 // Avoid negative width/height due to overflow.
362 __m128i mask
= _mm_or_si128(_mm_cmpgt_epi32(rectPacked
, _mm_setzero_si128()),
363 _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
364 // Mask will now contain [ 0xFFFFFFFF 0xFFFFFFFF (width <= 0 ? 0 : 0xFFFFFFFF)
365 // (height <= 0 ? 0 : 0xFFFFFFFF) ]
366 rectPacked
= _mm_and_si128(rectPacked
, mask
);
368 _mm_storeu_si128((__m128i
*)&rect
, rectPacked
);
371 NSToIntRoundUp(NSAppUnitsToFloatPixels(x
, aAppUnitsPerPixel
) * aXScale
),
372 NSToIntRoundUp(NSAppUnitsToFloatPixels(y
, aAppUnitsPerPixel
) * aYScale
),
373 NSToIntRoundUp(NSAppUnitsToFloatPixels(XMost(), aAppUnitsPerPixel
) *
375 NSToIntRoundUp(NSAppUnitsToFloatPixels(YMost(), aAppUnitsPerPixel
) *
381 // scale the rect but round to smallest containing rect
382 inline mozilla::gfx::IntRect
nsRect::ScaleToOutsidePixels(
383 float aXScale
, float aYScale
, nscoord aAppUnitsPerPixel
) const {
384 mozilla::gfx::IntRect rect
;
385 // Android x86 builds have bindgen issues.
386 #if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || \
387 (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
388 __m128 appUnitsPacked
= _mm_set_ps(aAppUnitsPerPixel
, aAppUnitsPerPixel
,
389 aAppUnitsPerPixel
, aAppUnitsPerPixel
);
390 __m128 scalesPacked
= _mm_set_ps(aYScale
, aXScale
, aYScale
, aXScale
);
392 __m128i rectPacked
= _mm_loadu_si128((__m128i
*)this); // x, y, w, h
393 __m128i topLeft
= _mm_slli_si128(rectPacked
, 8); // 0, 0, x, y
395 rectPacked
= _mm_add_epi32(rectPacked
, topLeft
); // X, Y, XMost(), YMost()
397 __m128 rectFloat
= _mm_cvtepi32_ps(rectPacked
);
399 // Scale i.e. ([ x y xmost ymost ] / aAppUnitsPerPixel) *
400 // [ aXScale aYScale aXScale aYScale ]
401 rectFloat
= _mm_mul_ps(_mm_div_ps(rectFloat
, appUnitsPacked
), scalesPacked
);
402 rectPacked
= ceil_ps2epi32(rectFloat
); // xx, xx, XMost(), YMost()
403 __m128i tmp
= floor_ps2epi32(rectFloat
); // x, y, xx, xx
405 // _mm_move_sd is 1 cycle method of getting the blending we want.
406 rectPacked
= _mm_castpd_si128(
407 _mm_move_sd(_mm_castsi128_pd(rectPacked
),
408 _mm_castsi128_pd(tmp
))); // x, y, XMost(), YMost()
410 topLeft
= _mm_slli_si128(rectPacked
, 8); // 0, 0, r.x, r.y
411 rectPacked
= _mm_sub_epi32(rectPacked
, topLeft
); // r.x, r.y, r.w, r.h
413 // Avoid negative width/height due to overflow.
414 __m128i mask
= _mm_or_si128(_mm_cmpgt_epi32(rectPacked
, _mm_setzero_si128()),
415 _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
417 // Mask will now contain [ 0xFFFFFFFF 0xFFFFFFFF (width <= 0 ? 0 : 0xFFFFFFFF) (height <= 0 ? 0 : 0xFFFFFFFF) ]
419 rectPacked
= _mm_and_si128(rectPacked
, mask
);
421 _mm_storeu_si128((__m128i
*)&rect
, rectPacked
);
424 NSToIntFloor(NSAppUnitsToFloatPixels(x
, float(aAppUnitsPerPixel
)) *
426 NSToIntFloor(NSAppUnitsToFloatPixels(y
, float(aAppUnitsPerPixel
)) *
428 NSToIntCeil(NSAppUnitsToFloatPixels(XMost(), float(aAppUnitsPerPixel
)) *
430 NSToIntCeil(NSAppUnitsToFloatPixels(YMost(), float(aAppUnitsPerPixel
)) *
436 // scale the rect but round to largest contained rect
437 inline mozilla::gfx::IntRect
nsRect::ScaleToInsidePixels(
438 float aXScale
, float aYScale
, nscoord aAppUnitsPerPixel
) const {
439 mozilla::gfx::IntRect rect
;
441 NSToIntCeil(NSAppUnitsToFloatPixels(x
, float(aAppUnitsPerPixel
)) *
443 NSToIntCeil(NSAppUnitsToFloatPixels(y
, float(aAppUnitsPerPixel
)) *
445 NSToIntFloor(NSAppUnitsToFloatPixels(XMost(), float(aAppUnitsPerPixel
)) *
447 NSToIntFloor(NSAppUnitsToFloatPixels(YMost(), float(aAppUnitsPerPixel
)) *
452 inline mozilla::gfx::IntRect
nsRect::ToNearestPixels(
453 nscoord aAppUnitsPerPixel
) const {
454 return ScaleToNearestPixels(1.0f
, 1.0f
, aAppUnitsPerPixel
);
457 inline mozilla::gfx::IntRect
nsRect::ToOutsidePixels(
458 nscoord aAppUnitsPerPixel
) const {
459 return ScaleToOutsidePixels(1.0f
, 1.0f
, aAppUnitsPerPixel
);
462 inline mozilla::gfx::IntRect
nsRect::ToInsidePixels(
463 nscoord aAppUnitsPerPixel
) const {
464 return ScaleToInsidePixels(1.0f
, 1.0f
, aAppUnitsPerPixel
);
467 inline nsRect
nsRect::RemoveResolution(const float aResolution
) const {
468 MOZ_ASSERT(aResolution
> 0.0f
);
470 rect
.MoveTo(NSToCoordRound(NSCoordToFloat(x
) / aResolution
),
471 NSToCoordRound(NSCoordToFloat(y
) / aResolution
));
472 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
473 // rect as well instead of possibly rounding the width or height to zero.
474 if (width
== 1 && height
== 1) {
477 rect
.SizeTo(NSToCoordCeil(NSCoordToFloat(width
) / aResolution
),
478 NSToCoordCeil(NSCoordToFloat(height
) / aResolution
));
484 const mozilla::gfx::IntRect
& GetMaxSizedIntRect();
486 // app units are integer multiples of pixels, so no rounding needed
487 template <class units
>
488 nsRect
ToAppUnits(const mozilla::gfx::IntRectTyped
<units
>& aRect
,
489 nscoord aAppUnitsPerPixel
) {
490 return nsRect(NSIntPixelsToAppUnits(aRect
.X(), aAppUnitsPerPixel
),
491 NSIntPixelsToAppUnits(aRect
.Y(), aAppUnitsPerPixel
),
492 NSIntPixelsToAppUnits(aRect
.Width(), aAppUnitsPerPixel
),
493 NSIntPixelsToAppUnits(aRect
.Height(), aAppUnitsPerPixel
));
498 extern FILE* operator<<(FILE* out
, const nsRect
& rect
);
501 #endif /* NSRECT_H */