[PATCH] oleaut32: Implement VarDecRound
Andrew Eikum
aeikum at codeweavers.com
Tue Sep 17 13:01:45 CDT 2013
This fixes a crash in PowerPoint when a shape is made
semi-transparent.
---
dlls/oleaut32/tests/vartype.c | 35 ++++++++++++++++++
dlls/oleaut32/vartype.c | 86 +++++++++++++++++++++++++++++++------------
2 files changed, 98 insertions(+), 23 deletions(-)
diff --git a/dlls/oleaut32/tests/vartype.c b/dlls/oleaut32/tests/vartype.c
index 4c823c6..e296e77 100644
--- a/dlls/oleaut32/tests/vartype.c
+++ b/dlls/oleaut32/tests/vartype.c
@@ -455,6 +455,7 @@ static HRESULT (WINAPI *pVarDecDiv)(const DECIMAL*,const DECIMAL*,DECIMAL*);
static HRESULT (WINAPI *pVarDecCmp)(const DECIMAL*,const DECIMAL*);
static HRESULT (WINAPI *pVarDecCmpR8)(const DECIMAL*,double);
static HRESULT (WINAPI *pVarDecNeg)(const DECIMAL*,DECIMAL*);
+static HRESULT (WINAPI *pVarDecRound)(const DECIMAL*,int,DECIMAL*);
static HRESULT (WINAPI *pVarBoolFromUI1)(BYTE,VARIANT_BOOL*);
static HRESULT (WINAPI *pVarBoolFromI2)(SHORT,VARIANT_BOOL*);
@@ -4545,6 +4546,39 @@ static void test_VarDecCmpR8(void)
SETDEC(l,0,DECIMAL_NEG,-1,-1); r = DECIMAL_NEG; MATH3(VarDecCmpR8); EXPECT_LT;
}
+#define CLEAR(x) memset(&(x), 0xBB, sizeof(x))
+
+static void test_VarDecRound(void)
+{
+ HRESULT hres;
+ DECIMAL l, out;
+
+ CHECKPTR(VarDecRound);
+
+ CLEAR(out); SETDEC(l, 0, 0, 0, 1); hres = pVarDecRound(&l, 3, &out); EXPECTDEC(0, 0, 0, 1);
+
+ CLEAR(out); SETDEC(l, 0, 0, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, 0, 0, 1);
+ CLEAR(out); SETDEC(l, 1, 0, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, 0, 0, 0);
+ CLEAR(out); SETDEC(l, 1, 0, 0, 1); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 1);
+ CLEAR(out); SETDEC(l, 2, 0, 0, 11); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 1);
+ CLEAR(out); SETDEC(l, 2, 0, 0, 15); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 2);
+ CLEAR(out); SETDEC(l, 6, 0, 0, 550001); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 6);
+
+ CLEAR(out); SETDEC(l, 0, DECIMAL_NEG, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, DECIMAL_NEG, 0, 1);
+ CLEAR(out); SETDEC(l, 1, DECIMAL_NEG, 0, 1); hres = pVarDecRound(&l, 0, &out); EXPECTDEC(0, DECIMAL_NEG, 0, 0);
+ CLEAR(out); SETDEC(l, 1, DECIMAL_NEG, 0, 1); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 1);
+ CLEAR(out); SETDEC(l, 2, DECIMAL_NEG, 0, 11); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 1);
+ CLEAR(out); SETDEC(l, 2, DECIMAL_NEG, 0, 15); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 2);
+ CLEAR(out); SETDEC(l, 6, DECIMAL_NEG, 0, 550001); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, DECIMAL_NEG, 0, 6);
+
+ CLEAR(out); SETDEC64(l, 0, 0, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, 0, 0xffffffff, 0xffffffff, 0xffffffff);
+ CLEAR(out); SETDEC64(l, 28, 0, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, 0, 0, 0, 8);
+ CLEAR(out); SETDEC64(l, 0, DECIMAL_NEG, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, DECIMAL_NEG, 0xffffffff, 0xffffffff, 0xffffffff);
+ CLEAR(out); SETDEC64(l, 28, DECIMAL_NEG, 0xffffffff, 0xffffffff, 0xffffffff); hres = pVarDecRound(&l, 0, &out); EXPECTDEC64(0, DECIMAL_NEG, 0, 0, 8);
+
+ CLEAR(out); SETDEC(l, 2, 0, 0, 0); hres = pVarDecRound(&l, 1, &out); EXPECTDEC(1, 0, 0, 0);
+}
+
/*
* VT_BOOL
*/
@@ -6334,6 +6368,7 @@ START_TEST(vartype)
test_VarDecCmpR8();
test_VarDecMul();
test_VarDecDiv();
+ test_VarDecRound();
test_VarBoolFromI1();
test_VarBoolFromUI1();
diff --git a/dlls/oleaut32/vartype.c b/dlls/oleaut32/vartype.c
index 74f66de..e1672a1 100644
--- a/dlls/oleaut32/vartype.c
+++ b/dlls/oleaut32/vartype.c
@@ -5137,7 +5137,7 @@ static int VARIANT_int_addlossy(
in case of quotient overflow.
*/
static HRESULT VARIANT_DI_div(const VARIANT_DI * dividend, const VARIANT_DI * divisor,
- VARIANT_DI * quotient)
+ VARIANT_DI * quotient, BOOL round_remainder)
{
HRESULT r_overflow = S_OK;
@@ -5180,8 +5180,21 @@ static HRESULT VARIANT_DI_div(const VARIANT_DI * dividend, const VARIANT_DI * di
underflow = VARIANT_int_addlossy(
quotient->bitsnum, "ientscale, sizeof(quotient->bitsnum) / sizeof(DWORD),
remainderplusquotient, &tempquotientscale, 4);
- VARIANT_int_mulbychar(remainderplusquotient + 4, 4, 10);
- memcpy(remainderplusquotient, remainderplusquotient + 4, 4 * sizeof(DWORD));
+ if (round_remainder) {
+ if(remainderplusquotient[4] >= 5){
+ unsigned int i;
+ unsigned char remainder = 1;
+ for (i = 0; i < sizeof(quotient->bitsnum) / sizeof(DWORD) && remainder; i++) {
+ ULONGLONG digit = quotient->bitsnum[i] + 1;
+ remainder = (digit > 0xFFFFFFFF) ? 1 : 0;
+ quotient->bitsnum[i] = digit & 0xFFFFFFFF;
+ }
+ }
+ memset(remainderplusquotient, 0, sizeof(remainderplusquotient));
+ } else {
+ VARIANT_int_mulbychar(remainderplusquotient + 4, 4, 10);
+ memcpy(remainderplusquotient, remainderplusquotient + 4, 4 * sizeof(DWORD));
+ }
tempquotientscale = ++remainderscale;
} while (!underflow && !VARIANT_int_iszero(remainderplusquotient + 4, 4));
@@ -5485,31 +5498,16 @@ static HRESULT VARIANT_DI_FromR8(double source, VARIANT_DI * dest)
return hres;
}
-/************************************************************************
- * VarDecDiv (OLEAUT32.178)
- *
- * Divide one DECIMAL by another.
- *
- * PARAMS
- * pDecLeft [I] Source
- * pDecRight [I] Value to divide by
- * pDecOut [O] Destination
- *
- * RETURNS
- * Success: S_OK.
- * Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
- */
-HRESULT WINAPI VarDecDiv(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
+static HRESULT VARIANT_do_division(const DECIMAL *pDecLeft, const DECIMAL *pDecRight, DECIMAL *pDecOut,
+ BOOL round)
{
HRESULT hRet = S_OK;
VARIANT_DI di_left, di_right, di_result;
HRESULT divresult;
- if (!pDecLeft || !pDecRight || !pDecOut) return E_INVALIDARG;
-
VARIANT_DIFromDec(pDecLeft, &di_left);
VARIANT_DIFromDec(pDecRight, &di_right);
- divresult = VARIANT_DI_div(&di_left, &di_right, &di_result);
+ divresult = VARIANT_DI_div(&di_left, &di_right, &di_result, round);
if (divresult != S_OK)
{
/* division actually overflowed */
@@ -5557,6 +5555,27 @@ HRESULT WINAPI VarDecDiv(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECI
}
/************************************************************************
+ * VarDecDiv (OLEAUT32.178)
+ *
+ * Divide one DECIMAL by another.
+ *
+ * PARAMS
+ * pDecLeft [I] Source
+ * pDecRight [I] Value to divide by
+ * pDecOut [O] Destination
+ *
+ * RETURNS
+ * Success: S_OK.
+ * Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
+ */
+HRESULT WINAPI VarDecDiv(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
+{
+ if (!pDecLeft || !pDecRight || !pDecOut) return E_INVALIDARG;
+
+ return VARIANT_do_division(pDecLeft, pDecRight, pDecOut, FALSE);
+}
+
+/************************************************************************
* VarDecMul (OLEAUT32.179)
*
* Multiply one DECIMAL by another.
@@ -5765,6 +5784,10 @@ HRESULT WINAPI VarDecNeg(const DECIMAL* pDecIn, DECIMAL* pDecOut)
*/
HRESULT WINAPI VarDecRound(const DECIMAL* pDecIn, int cDecimals, DECIMAL* pDecOut)
{
+ DECIMAL divisor, tmp;
+ HRESULT hr;
+ unsigned int i;
+
if (cDecimals < 0 || (DEC_SIGN(pDecIn) & ~DECIMAL_NEG) || DEC_SCALE(pDecIn) > DEC_MAX_SCALE)
return E_INVALIDARG;
@@ -5774,9 +5797,26 @@ HRESULT WINAPI VarDecRound(const DECIMAL* pDecIn, int cDecimals, DECIMAL* pDecOu
return S_OK;
}
- FIXME("semi-stub!\n");
+ /* truncate significant digits and rescale */
+ memset(&divisor, 0, sizeof(divisor));
+ DEC_LO64(&divisor) = 1;
- return DISP_E_OVERFLOW;
+ memset(&tmp, 0, sizeof(tmp));
+ DEC_LO64(&tmp) = 10;
+ for (i = 0; i < DEC_SCALE(pDecIn) - cDecimals; ++i)
+ {
+ hr = VarDecMul(&divisor, &tmp, &divisor);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ hr = VARIANT_do_division(pDecIn, &divisor, pDecOut, TRUE);
+ if (FAILED(hr))
+ return hr;
+
+ DEC_SCALE(pDecOut) = cDecimals;
+
+ return S_OK;
}
/************************************************************************
--
1.8.4
More information about the wine-patches
mailing list