748 lines
24 KiB
C++
748 lines
24 KiB
C++
|
/**************************************************************************\
|
||
|
*
|
||
|
* Copyright (c) 1998 Microsoft Corporation
|
||
|
*
|
||
|
* Abstract:
|
||
|
*
|
||
|
* Contains macros for helping with floating point arithmetic.
|
||
|
*
|
||
|
* History:
|
||
|
*
|
||
|
* 07/08/1999 agodfrey
|
||
|
* Remove MSVCRT dependency.
|
||
|
* 12/06/1998 andrewgo
|
||
|
* Created it.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#ifndef _REAL_HPP_
|
||
|
#define _REAL_HPP_
|
||
|
|
||
|
// The following stuff is taken from Office code
|
||
|
|
||
|
/*****************************************************************************
|
||
|
Intrinsic functions - it is essential that these be used because there is
|
||
|
no library implementation. Note that the intrinsic forms often have
|
||
|
restricted behavior, e.g. the argument to a trignometric function must be
|
||
|
less than 2^63 radians.
|
||
|
******************************************************************* JohnBo **/
|
||
|
#pragma intrinsic(sin, cos, tan)
|
||
|
#pragma intrinsic(atan, atan2)
|
||
|
#pragma intrinsic(sqrt)
|
||
|
#pragma intrinsic(log, log10, exp)
|
||
|
#pragma intrinsic(fabs)
|
||
|
#pragma intrinsic(fmod)
|
||
|
|
||
|
namespace GpRuntime
|
||
|
{
|
||
|
/*
|
||
|
[JohnBo]
|
||
|
These in-line functions are required to force direct use of the
|
||
|
instructions - without this the CI versions are used unless g optimization
|
||
|
is switched on, with g optimization on the in-line function calls will be
|
||
|
removed completely.
|
||
|
*/
|
||
|
#pragma optimize("g", on)
|
||
|
inline double InlineSin(double x) { return sin(x); }
|
||
|
inline double InlineCos(double x) { return cos(x); }
|
||
|
inline double InlineTan(double x) { return tan(x); }
|
||
|
inline double InlineATan(double x) { return atan(x); }
|
||
|
inline double InlineATan2(double y, double x) { return atan2(y, x); }
|
||
|
inline double InlineSqrt(double x) { return sqrt(x); }
|
||
|
inline double InlineLog(double x) { return log(x); }
|
||
|
inline double InlineLog10(double x) { return log10(x); }
|
||
|
inline double InlineExp(double x) { return exp(x); }
|
||
|
/* Restore default optimization. */
|
||
|
#pragma optimize("", on)
|
||
|
|
||
|
// Out-of-line math functions
|
||
|
// pow: We implemented it ourselves
|
||
|
// exp: Because the inline version is so long, the compiler won't
|
||
|
// inline it unless the original caller has 'generate fast code'
|
||
|
// set. Instead, we use an out-of-line version.
|
||
|
double Pow(double, double);
|
||
|
double Exp(double);
|
||
|
|
||
|
/*// Trying something:
|
||
|
inline double FPX86InlineFmod(double x, double y) { return fmod(x,y); }
|
||
|
#define fmod(x,y) FPX86InlineFmod(x,y)
|
||
|
*/
|
||
|
};
|
||
|
|
||
|
/* Force use of the in-line functions. */
|
||
|
|
||
|
#define sin(x) GpRuntime::InlineSin(x)
|
||
|
#define cos(x) GpRuntime::InlineCos(x)
|
||
|
#define tan(x) GpRuntime::InlineTan(x)
|
||
|
#define atan(x) GpRuntime::InlineATan(x)
|
||
|
#define atan2(y,x) GpRuntime::InlineATan2(y,x)
|
||
|
#define sqrt(x) GpRuntime::InlineSqrt(x)
|
||
|
#define log(x) GpRuntime::InlineLog(x)
|
||
|
#define log10(x) GpRuntime::InlineLog10(x)
|
||
|
#define exp(x) GpRuntime::Exp(x)
|
||
|
|
||
|
#define pow(x,y) GpRuntime::Pow(x,y)
|
||
|
|
||
|
/* Integer interfaces */
|
||
|
#pragma intrinsic(abs, labs)
|
||
|
|
||
|
// End of Office code
|
||
|
|
||
|
// Our pixel positioning uses 28.4 fixed point arithmetic and therefore
|
||
|
// anything below the threshold of 1/32 should be irrelevant.
|
||
|
// Our choice of PIXEL_EPSILON is 1/64 which should give us correct pixel
|
||
|
// comparisons even in the event of accumulated floating point error.
|
||
|
#define PIXEL_EPSILON 0.015625f // 1/64
|
||
|
|
||
|
#ifndef REAL_EPSILON
|
||
|
#define REAL_EPSILON FLT_EPSILON
|
||
|
#endif
|
||
|
|
||
|
// This is for computing the complexity of matrices. When you compose matrices
|
||
|
// or scale them up by large factors, it's really easy to hit the REAL_EPSILON
|
||
|
// limits without actually affecting the transform in any noticable way.
|
||
|
// e.g. a matrix with a rotation of 1e-5 degrees is, for all practical purposes,
|
||
|
// not a rotation.
|
||
|
#define CPLX_EPSILON (REAL_EPSILON*5000.0f)
|
||
|
|
||
|
// This is a tolerance error for the difference of each real coordinates.
|
||
|
// This leaves some acurracy to calculate tangent or normal for the two points.
|
||
|
#define POINTF_EPSILON (REAL_EPSILON*5000.0f)
|
||
|
|
||
|
|
||
|
#define REALFMOD fmodf
|
||
|
#define REALSQRT sqrtf
|
||
|
#ifndef REALABS
|
||
|
#define REALABS fabsf
|
||
|
#endif
|
||
|
#define REALSIN sinf
|
||
|
#define REALCOS cosf
|
||
|
#define REALATAN2 atan2f
|
||
|
|
||
|
// #define REAL_EPSILON DBL_EPSILON
|
||
|
// #define REALFMOD fmod
|
||
|
// #define REALSQRT sqrt
|
||
|
// #define REALABS fabs
|
||
|
// #define REALSIN sin
|
||
|
// #define REALCOS cos
|
||
|
// #define REALATAN2 atan2
|
||
|
|
||
|
// convert from unknown FLOAT type => REAL
|
||
|
#define TOREAL(x) (static_cast<REAL>(x))
|
||
|
|
||
|
// defined separately for possibly future optimization LONG to REAL
|
||
|
#define LTOF(x) (static_cast<REAL>(x))
|
||
|
|
||
|
// Return the positive integer remainder of a/b. b should not be zero.
|
||
|
// note that a % b will return a negative number
|
||
|
// for a<0 xor b<0 which is not suitable for texture mapping
|
||
|
// or brush tiling.
|
||
|
// This macro computes the remainder of a/b
|
||
|
// correctly adjusted for tiling negative coordinates.
|
||
|
|
||
|
#define RemainderI(a, b)\
|
||
|
((a) >= 0 ? (a) % (b) : \
|
||
|
(b) - 1 - ((-(a) - 1) % (b)))
|
||
|
|
||
|
|
||
|
// This definition assumes y > 0
|
||
|
inline REAL GpModF(REAL x, REAL y)
|
||
|
{
|
||
|
if(x > 0)
|
||
|
{
|
||
|
return static_cast<REAL> (x - ((INT) (x/y))*y);
|
||
|
}
|
||
|
else if(x < 0)
|
||
|
{
|
||
|
REAL z;
|
||
|
x = - x;
|
||
|
z = static_cast<REAL> (x - ((INT) (x/y))*y);
|
||
|
if(z > 0)
|
||
|
z = y - z;
|
||
|
|
||
|
return z;
|
||
|
}
|
||
|
else // x == 0
|
||
|
return 0.0;
|
||
|
|
||
|
/*
|
||
|
// This assumes fmod(x, y) = - fmod(-x, y) when x < 0.
|
||
|
|
||
|
REAL z = REALFMOD(x, y);
|
||
|
|
||
|
if(z >= 0)
|
||
|
return z;
|
||
|
else
|
||
|
return y + z;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
#if defined(_X86_) && !defined(_COMPLUS_GDI)
|
||
|
|
||
|
#define _USE_X86_ASSEMBLY
|
||
|
|
||
|
#else
|
||
|
|
||
|
#undef _USE_X86_ASSEMBLY
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
|
||
|
#define FPU_STATE() \
|
||
|
UINT32 cwSave; \
|
||
|
UINT32 cwTemp;
|
||
|
|
||
|
#define FPU_GET_STATE() \
|
||
|
UINT32 cwSave = this->cwSave; \
|
||
|
UINT32 cwTemp = this->cwTemp;
|
||
|
|
||
|
#define FPU_SAVE_STATE() \
|
||
|
this->cwSave = cwSave; \
|
||
|
this->cwTemp = cwTemp;
|
||
|
|
||
|
#define FPU_SAVE_MODE() \
|
||
|
UINT32 cwSave; \
|
||
|
UINT32 cwTemp; \
|
||
|
\
|
||
|
__asm { \
|
||
|
_asm fnstcw WORD PTR cwSave \
|
||
|
_asm mov eax, cwSave \
|
||
|
_asm mov cwTemp, eax \
|
||
|
} \
|
||
|
this->cwSave =
|
||
|
|
||
|
#define FPU_RESTORE_MODE() \
|
||
|
__asm { \
|
||
|
_asm fldcw WORD PTR cwSave \
|
||
|
}
|
||
|
|
||
|
#define FPU_RESTORE_MODE_NO_EXCEPTIONS()\
|
||
|
__asm { \
|
||
|
_asm fnclex \
|
||
|
_asm fldcw WORD PTR cwSave \
|
||
|
}
|
||
|
|
||
|
#define FPU_CHOP_ON() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm or eax, 0x0c00 \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define FPU_ROUND_ON() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm and eax,0xf3ff \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define FPU_ROUND_ON_PREC_HI() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm and eax,0xf0ff \
|
||
|
_asm or eax,0x0200 \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define FPU_PREC_LOW() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm and eax, 0xfcff \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define FPU_PREC_LOW_MASK_EXCEPTIONS() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm and eax, 0xfcff \
|
||
|
_asm or eax, 0x3f \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define FPU_CHOP_ON_PREC_LOW() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm or eax, 0x0c00 \
|
||
|
_asm and eax, 0xfcff \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define FPU_CHOP_OFF_PREC_HI() \
|
||
|
__asm { \
|
||
|
_asm mov eax, cwTemp \
|
||
|
_asm mov ah, 2 \
|
||
|
_asm mov cwTemp, eax \
|
||
|
_asm fldcw WORD PTR cwTemp \
|
||
|
}
|
||
|
|
||
|
#define CHOP_ROUND_ON()
|
||
|
#define CHOP_ROUND_OFF()
|
||
|
|
||
|
#if DBG
|
||
|
#define ASSERT_CHOP_ROUND() \
|
||
|
{ \
|
||
|
WORD cw; \
|
||
|
__asm { \
|
||
|
__asm fnstcw cw \
|
||
|
} \
|
||
|
ASSERT((cw & 0xc00) == 0xc00, "Chop round must be on"); \
|
||
|
}
|
||
|
#else
|
||
|
#define ASSERT_CHOP_ROUND()
|
||
|
#endif
|
||
|
|
||
|
#else // _USE_X86_ASSEMBLY
|
||
|
|
||
|
#define FLT_TO_FIX_SCALE(value_in, scale) \
|
||
|
((INT)((REAL)(value_in) * scale))
|
||
|
#define FLT_TO_UCHAR_SCALE(value_in, scale) \
|
||
|
((UCHAR)((INT)((REAL)(value_in) * scale)))
|
||
|
#define UNSAFE_FLT_TO_FIX(value_in) \
|
||
|
FLT_TO_FIX(value_in)
|
||
|
|
||
|
#define FPU_SAVE_MODE()
|
||
|
#define FPU_RESTORE_MODE()
|
||
|
#define FPU_RESTORE_MODE_NO_EXCEPTIONS()
|
||
|
#define FPU_CHOP_ON()
|
||
|
#define FPU_ROUND_ON()
|
||
|
#define FPU_ROUND_ON_PREC_HI()
|
||
|
#define FPU_PREC_LOW()
|
||
|
#define FPU_PREC_LOW_MASK_EXCEPTIONS()
|
||
|
#define FPU_CHOP_ON_PREC_LOW()
|
||
|
#define FPU_CHOP_OFF_PREC_HI()
|
||
|
#define CHOP_ROUND_ON()
|
||
|
#define CHOP_ROUND_OFF()
|
||
|
#define ASSERT_CHOP_ROUND()
|
||
|
|
||
|
#endif //_USE_X86_ASSEMBLY
|
||
|
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
|
||
|
__inline INT __fastcall FLOOR(
|
||
|
REAL a)
|
||
|
{
|
||
|
INT i;
|
||
|
|
||
|
_asm {
|
||
|
fld a
|
||
|
fistp i
|
||
|
}
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
// Can cause overflow exceptions
|
||
|
__inline INT __fastcall UNSAFE_FLOOR(
|
||
|
REAL a)
|
||
|
{
|
||
|
INT l;
|
||
|
|
||
|
_asm {
|
||
|
fld a
|
||
|
fistp l
|
||
|
}
|
||
|
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define FLOOR(a) ((INT) floor(a))
|
||
|
#define UNSAFE_FLOOR(a) ((INT) floor(a))
|
||
|
|
||
|
#endif
|
||
|
|
||
|
//--------------------------------------------------------------------------
|
||
|
// Class for invoking 'floor' mode, and restoring it afterwards
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
// These define the bits in the FPU control word that we care about.
|
||
|
// The high byte 0x0c is the rounding control and the low byte
|
||
|
// 0x3F is the exception mask flags.
|
||
|
|
||
|
#define FP_CTRL_MASK 0x0c3F
|
||
|
|
||
|
// Set the rounding control and mask PE. DE is no longer masked because
|
||
|
// taking the exception can be helpful in finding bugs. However, we should
|
||
|
// probably turn on DE masking when we ship.
|
||
|
|
||
|
// Round Down
|
||
|
#define FP_CTRL_ROUNDDOWN 0x0400
|
||
|
|
||
|
// Mask PE only.
|
||
|
// Use this define to track down nasty FP bugs.
|
||
|
// #define FP_CTRL_MASKEDEXCEPTIONS 0x0020
|
||
|
|
||
|
// Mask all FP exceptions.
|
||
|
|
||
|
#define FP_CTRL_MASKEDEXCEPTIONS 0x003F
|
||
|
|
||
|
#define FP_CTRL_STATE (FP_CTRL_ROUNDDOWN | FP_CTRL_MASKEDEXCEPTIONS)
|
||
|
|
||
|
class FPUStateSaver
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
UINT32 SavedState;
|
||
|
|
||
|
#if DBG
|
||
|
// For AssertMode, we use SaveLevel to keep track
|
||
|
// of the nesting level of FPUState. This is not thread safe.
|
||
|
// At worst, that could cause spurious asserts; more likely it could
|
||
|
// fail to catch some errors. To prevent the spurious asserts,
|
||
|
// used interlocked instructions to modify it. To fix it properly,
|
||
|
// we'd need to use per-thread storage.
|
||
|
|
||
|
static LONG SaveLevel;
|
||
|
#endif
|
||
|
|
||
|
public:
|
||
|
|
||
|
FPUStateSaver()
|
||
|
{
|
||
|
// Here we set our thread's round mode to 'round down', and
|
||
|
// save the old mode. 'Round to nearest' is actually the
|
||
|
// default state for a process, but applications can set it
|
||
|
// to whatever they like, and we should also respect and save
|
||
|
// their preferred mode.
|
||
|
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
|
||
|
UINT32 tempState;
|
||
|
UINT32 savedState;
|
||
|
|
||
|
// clear the current exception state.
|
||
|
// fnclex is a non-wait version of fclex - which means it clears
|
||
|
// the exceptions without taking any pending unmasked exceptions.
|
||
|
// We issue a fclex so that any unmasked exceptions are
|
||
|
// triggered immediately. This indicates a bug in the caller
|
||
|
// of the API.
|
||
|
|
||
|
_asm fclex
|
||
|
|
||
|
// Save control word:
|
||
|
|
||
|
_asm fnstcw WORD PTR savedState
|
||
|
|
||
|
this->SavedState = savedState;
|
||
|
|
||
|
// Floor mode on & set up our prefered exception masks
|
||
|
|
||
|
_asm mov eax, savedState
|
||
|
_asm and eax, ~FP_CTRL_MASK
|
||
|
_asm or eax, FP_CTRL_STATE
|
||
|
_asm mov tempState, eax
|
||
|
_asm fldcw WORD PTR tempState
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#if DBG
|
||
|
InterlockedIncrement(&SaveLevel);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
~FPUStateSaver()
|
||
|
{
|
||
|
AssertMode();
|
||
|
|
||
|
#if DBG
|
||
|
InterlockedDecrement(&SaveLevel);
|
||
|
#endif
|
||
|
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
|
||
|
UINT32 savedState = this->SavedState;
|
||
|
|
||
|
// Clear the exception state.
|
||
|
// Note: We issue the fwait and then an fnclex - which is equivalent
|
||
|
// to the fclex instruction (9B DB E2) which causes us to
|
||
|
// immediately take any unmasked pending exceptions.
|
||
|
// Because we clear the exception state on the way in, hitting
|
||
|
// an exception on this line means we generated an exception
|
||
|
// in our code (or a call out to other code between the
|
||
|
// FPUStateSaver constructor and destructor nesting) which was
|
||
|
// pending and not handled.
|
||
|
|
||
|
_asm fclex
|
||
|
|
||
|
// Restore control word (rounding mode and exception masks):
|
||
|
|
||
|
_asm fldcw WORD PTR savedState
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// AssertMode.
|
||
|
//
|
||
|
// AssertMode does nothing in Free builds, unless the FREE_BUILD_FP_BARRIER
|
||
|
// define is set to 1. Debug builds always have FP barriers.
|
||
|
// If exceptions are unmasked and you're getting delayed FP exceptions,
|
||
|
// turn on the FP barriers and add FPUStateSaver::AssertMode() calls
|
||
|
// bracketing all your calls. This will allow you to isolate the FP
|
||
|
// exception generator.
|
||
|
|
||
|
#define FREE_BUILD_FP_BARRIER 0
|
||
|
|
||
|
#if DBG
|
||
|
static VOID AssertMode();
|
||
|
#else
|
||
|
static VOID AssertMode()
|
||
|
{
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
#if FREE_BUILD_FP_BARRIER
|
||
|
_asm fwait
|
||
|
#endif
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//--------------------------------------------------------------------------
|
||
|
// The following conversion routines are MUCH faster than the standard
|
||
|
// C routines, because they assume that the FPU floating point state is
|
||
|
// properly set.
|
||
|
//
|
||
|
// However, because they assume that the FPU floating point state has been
|
||
|
// properly set, they can only be used if an instance of the
|
||
|
// 'FPUStateSaver' class is in scope.
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
static INT Floor(REAL x)
|
||
|
{
|
||
|
AssertMode();
|
||
|
return((INT) FLOOR(x));
|
||
|
}
|
||
|
static INT Trunc(REAL x)
|
||
|
{
|
||
|
AssertMode();
|
||
|
return (x>=0) ? FLOOR(x) : -FLOOR(-x);
|
||
|
}
|
||
|
static INT Ceiling(REAL x)
|
||
|
{
|
||
|
AssertMode();
|
||
|
return((INT) -FLOOR(-x));
|
||
|
}
|
||
|
static INT Round(REAL x)
|
||
|
{
|
||
|
AssertMode();
|
||
|
return((INT) FLOOR((x) + TOREAL(0.5)));
|
||
|
}
|
||
|
|
||
|
// Saturation versions of the above conversion routines. Don't test for
|
||
|
// equality to INT_MAX because, when converted to floating-point for the
|
||
|
// comparison, the value is (INT_MAX + 1):
|
||
|
|
||
|
#define SATURATE(op) \
|
||
|
static INT op##Sat(REAL x) \
|
||
|
{ \
|
||
|
return (x >= INT_MIN) ? ((x < INT_MAX) ? op(x) \
|
||
|
: INT_MAX) \
|
||
|
: INT_MIN; \
|
||
|
}
|
||
|
|
||
|
SATURATE(Floor);
|
||
|
SATURATE(Trunc);
|
||
|
SATURATE(Ceiling);
|
||
|
SATURATE(Round);
|
||
|
|
||
|
#undef SATURATE
|
||
|
};
|
||
|
|
||
|
|
||
|
// FPUStateSandbox
|
||
|
//
|
||
|
// This object is designed to sandbox FPU unsafe code.
|
||
|
// For example, many badly written printer drivers on win9x codebases
|
||
|
// manipulate the FPU state without restoring it on exit. In order to
|
||
|
// prevent code like that from hosing us, we wrap calls to potentially
|
||
|
// unsafe code (like driver escapes) in an FPUStateSandbox.
|
||
|
//
|
||
|
// This will guarantee that after calling the unsafe code, the FPU state
|
||
|
// (rounding mode and exceptions) are reset to our preferred state.
|
||
|
// Because we assume that we're restoring to our preferred state, we
|
||
|
// ASSERT on our preferred state being set on entry. This means that
|
||
|
// the sandbox must be declared inside some top level FPUStateSaver block.
|
||
|
// This condition is not strictly necessary and if there is a requirement
|
||
|
// for an FPUStateSandbox not contained inside an FPUStateSaver, this
|
||
|
// ASSERT can be removed. The sandbox saves the current state and restores
|
||
|
// it on exit, so it can operate outside of our preferred state if required.
|
||
|
//
|
||
|
// So far we've found a number of printer drivers on win9x codebase that
|
||
|
// require sandboxing - e.g. HP4500c pcl.
|
||
|
//
|
||
|
// Couple of caveats: This code is designed to wrap simple calls out of
|
||
|
// GDI+ to the printer driver, such as Escape. It's not intended to be
|
||
|
// nested or for use with GDI+ code. However, nesting will work. In
|
||
|
// particular you should not call FPUStateSaver functions inside of
|
||
|
// an FPUStateSandbox unless you've acquired another nested FPUStateSaver.
|
||
|
// The only anticipated need for this is for sandboxing a callback function
|
||
|
// that calls into our API again. In this case it's ok, because all the
|
||
|
// GDI+ calls will be wrapped by a nested FPUStateSaver acquired at the
|
||
|
// API.
|
||
|
//
|
||
|
// NOTE: The ASSERTs in GpRound will not catch the scenario when they're
|
||
|
// called inside of a sandbox and not properly surrounded by a FPUStateSaver.
|
||
|
// GpRound may work incorrectly inside of a sandbox because the unsafe code
|
||
|
// could change the rounding mode. It could also generate exceptions.
|
||
|
|
||
|
class FPUStateSandbox
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
UINT32 SavedState;
|
||
|
|
||
|
public:
|
||
|
|
||
|
FPUStateSandbox()
|
||
|
{
|
||
|
// it is assumed that this call is issued inside of an
|
||
|
// FPUStateSaver block, so that the CTRL word is set to
|
||
|
// our preferred state.
|
||
|
|
||
|
// Lets not do this ASSERT - turns out that it gets called on the device
|
||
|
// destructor durning InternalGdiplusShutdown and we don't want to wrap
|
||
|
// that with an FPUStateSaver.
|
||
|
|
||
|
// FPUStateSaver::AssertMode();
|
||
|
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
|
||
|
UINT32 savedState;
|
||
|
|
||
|
// We must protect the sandboxed code from clearing the exception
|
||
|
// masks and taking an exception generated by GDI+.
|
||
|
// We do this by issuing fclex - which takes any unmasked exceptions
|
||
|
// and clears all of the exceptions after that (masked and unmasked)
|
||
|
// giving the sandboxed code a clean slate.
|
||
|
|
||
|
_asm fclex
|
||
|
|
||
|
// Save control word:
|
||
|
|
||
|
_asm fnstcw WORD PTR savedState
|
||
|
this->SavedState = savedState;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
~FPUStateSandbox()
|
||
|
{
|
||
|
#if defined(_USE_X86_ASSEMBLY)
|
||
|
|
||
|
UINT32 savedState = this->SavedState;
|
||
|
|
||
|
// clear the current exception state.
|
||
|
// fnclex is a non-wait version of fclex - which means it clears
|
||
|
// the exceptions without taking any pending unmasked exceptions.
|
||
|
// We issue an fnclex so that any unmasked exceptions are ignored.
|
||
|
// This is designed to prevent the sandboxed code from blowing
|
||
|
// up in code outside of the sandbox.
|
||
|
|
||
|
_asm fnclex
|
||
|
|
||
|
// Restore control word (rounding mode and exception masks):
|
||
|
|
||
|
_asm fldcw WORD PTR savedState
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------
|
||
|
// The following are simply handy versions that require less typing to
|
||
|
// use (i.e., 'GpFloor(x)' instead of 'FPUStateSaver::Floor(x)').
|
||
|
//
|
||
|
// These functions require that a version of 'FPUStateSaver' has been
|
||
|
// instantiated already for the current thread.
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
inline INT GpFloor(REAL x) { return(FPUStateSaver::Floor(x)); }
|
||
|
|
||
|
inline INT GpTrunc(REAL x) { return(FPUStateSaver::Trunc(x)); }
|
||
|
|
||
|
inline INT GpCeiling(REAL x) { return(FPUStateSaver::Ceiling(x)); }
|
||
|
|
||
|
inline INT GpRound(REAL x) { return(FPUStateSaver::Round(x)); }
|
||
|
|
||
|
inline INT GpFloorSat(REAL x) { return(FPUStateSaver::FloorSat(x)); }
|
||
|
|
||
|
inline INT GpTruncSat(REAL x) { return(FPUStateSaver::TruncSat(x)); }
|
||
|
|
||
|
inline INT GpCeilingSat(REAL x) { return(FPUStateSaver::CeilingSat(x)); }
|
||
|
|
||
|
inline INT GpRoundSat(REAL x) { return(FPUStateSaver::RoundSat(x)); }
|
||
|
|
||
|
|
||
|
/**************************************************************************\
|
||
|
*
|
||
|
* Function Description:
|
||
|
*
|
||
|
* Return TRUE if two points are close. Close is defined as near enough
|
||
|
* that the rounding to 32bit float precision could have resulted in the
|
||
|
* difference. We define an arbitrary number of allowed rounding errors (10).
|
||
|
* We divide by b to normalize the difference. It doesn't matter which point
|
||
|
* we divide by - if they're significantly different, we'll return true, and
|
||
|
* if they're really close, then a==b (almost).
|
||
|
*
|
||
|
* Arguments:
|
||
|
*
|
||
|
* a, b - input numbers to compare.
|
||
|
*
|
||
|
* Return Value:
|
||
|
*
|
||
|
* TRUE if the numbers are close enough.
|
||
|
*
|
||
|
* Created:
|
||
|
*
|
||
|
* 12/11/2000 asecchia
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
inline BOOL IsCloseReal(const REAL a, const REAL b)
|
||
|
{
|
||
|
// if b == 0.0f we don't want to divide by zero. If this happens
|
||
|
// it's sufficient to use 1.0 as the divisor because REAL_EPSILON
|
||
|
// should be good enough to test if a number is close enough to zero.
|
||
|
|
||
|
// NOTE: if b << a, this could cause an FP overflow. Currently we mask
|
||
|
// these exceptions, but if we unmask them, we should probably check
|
||
|
// the divide.
|
||
|
|
||
|
// We assume we can generate an overflow exception without taking down
|
||
|
// the system. We will still get the right results based on the FPU
|
||
|
// default handling of the overflow.
|
||
|
|
||
|
#if !(FP_CTRL_STATE & 0x8)
|
||
|
|
||
|
// Ensure that anyone clearing the overflow mask comes and revisits this
|
||
|
// assumption.
|
||
|
|
||
|
#error #O exception cleared. Go check FP_CTRL_MASKEDEXCEPTIONS.
|
||
|
|
||
|
#endif
|
||
|
|
||
|
FPUStateSaver::AssertMode();
|
||
|
|
||
|
return( REALABS( (a-b) / ((b==0.0f)?1.0f:b) ) < 10.0f*REAL_EPSILON );
|
||
|
}
|
||
|
|
||
|
inline BOOL IsClosePointF(const PointF &pt1, const PointF &pt2)
|
||
|
{
|
||
|
return (
|
||
|
IsCloseReal(pt1.X, pt2.X) &&
|
||
|
IsCloseReal(pt1.Y, pt2.Y)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#endif // !_REAL_HPP_
|