/* * PROJECT: ReactOS win32 kernel mode subsystem * LICENSE: GPL - See COPYING in the top level directory * FILE: win32ss/gdi/ntgdi/xformobj.c * PURPOSE: XFORMOBJ API * PROGRAMMERS: Timo Kreuzer * Katayama Hirofumi MZ */ /** Includes ******************************************************************/ #include #define NDEBUG #include #define DOES_VALUE_OVERFLOW_LONG(x) \ (((__int64)((long)(x))) != (x)) /** Inline helper functions ***************************************************/ /* * Inline helper to calculate pfo1 * pfo2 + pfo3 * pfo4 */ FORCEINLINE VOID MulAdd( PFLOATOBJ pfoDest, PFLOATOBJ pfo1, PFLOATOBJ pfo2, PFLOATOBJ pfo3, PFLOATOBJ pfo4) { FLOATOBJ foTmp; *pfoDest = *pfo1; FLOATOBJ_Mul(pfoDest, pfo2); foTmp = *pfo3; FLOATOBJ_Mul(&foTmp, pfo4); FLOATOBJ_Add(pfoDest, &foTmp); } /* * Inline helper to calculate pfo1 * l2 + pfo3 * l4 */ FORCEINLINE VOID MulAddLong( PFLOATOBJ pfoDest, PFLOATOBJ pfo1, LONG l2, PFLOATOBJ pfo3, LONG l4) { FLOATOBJ foTmp; *pfoDest = *pfo1; FLOATOBJ_MulLong(pfoDest, l2); foTmp = *pfo3; FLOATOBJ_MulLong(&foTmp, l4); FLOATOBJ_Add(pfoDest, &foTmp); } /* * Inline helper to calculate pfo1 * pfo2 - pfo3 * pfo4 */ FORCEINLINE VOID MulSub( PFLOATOBJ pfoDest, PFLOATOBJ pfo1, PFLOATOBJ pfo2, PFLOATOBJ pfo3, PFLOATOBJ pfo4) { FLOATOBJ foTmp; *pfoDest = *pfo1; FLOATOBJ_Mul(pfoDest, pfo2); foTmp = *pfo3; FLOATOBJ_Mul(&foTmp, pfo4); FLOATOBJ_Sub(pfoDest, &foTmp); } /* * Inline helper to get the complexity hint from flAccel */ FORCEINLINE ULONG HintFromAccel(ULONG flAccel) { switch (flAccel & (XFORM_SCALE|XFORM_UNITY|XFORM_NO_TRANSLATION)) { case (XFORM_SCALE|XFORM_UNITY|XFORM_NO_TRANSLATION): return GX_IDENTITY; case (XFORM_SCALE|XFORM_UNITY): return GX_OFFSET; case XFORM_SCALE: return GX_SCALE; default: return GX_GENERAL; } } /** Internal functions ********************************************************/ ULONG FASTCALL MX_UpdateAccel(IN OUT PMATRIX pmx) { /* Copy Dx and Dy to FIX format */ pmx->fxDx = FLOATOBJ_GetFix(&pmx->efDx); pmx->fxDy = FLOATOBJ_GetFix(&pmx->efDy); pmx->flAccel = 0; if (FLOATOBJ_Equal0(&pmx->efDx) && FLOATOBJ_Equal0(&pmx->efDy)) { pmx->flAccel |= XFORM_NO_TRANSLATION; } if (FLOATOBJ_Equal0(&pmx->efM12) && FLOATOBJ_Equal0(&pmx->efM21)) { pmx->flAccel |= XFORM_SCALE; } if (FLOATOBJ_Equal1(&pmx->efM11) && FLOATOBJ_Equal1(&pmx->efM22)) { pmx->flAccel |= XFORM_UNITY; } if (FLOATOBJ_IsLong(&pmx->efM11) && FLOATOBJ_IsLong(&pmx->efM12) && FLOATOBJ_IsLong(&pmx->efM21) && FLOATOBJ_IsLong(&pmx->efM22)) { pmx->flAccel |= XFORM_INTEGER; } return HintFromAccel(pmx->flAccel); } ULONG NTAPI XFORMOBJ_UpdateAccel( IN OUT XFORMOBJ *pxo) { PMATRIX pmx = XFORMOBJ_pmx(pxo); return MX_UpdateAccel(pmx); } ULONG NTAPI XFORMOBJ_iSetXform( IN OUT XFORMOBJ *pxo, IN const XFORML *pxform) { PMATRIX pmx; MATRIX mxTemp; ULONG Hint; /* Check parameters */ if (!pxo || !pxform) return DDI_ERROR; /* Copy members */ FLOATOBJ_SetFloat(&mxTemp.efM11, pxform->eM11); FLOATOBJ_SetFloat(&mxTemp.efM12, pxform->eM12); FLOATOBJ_SetFloat(&mxTemp.efM21, pxform->eM21); FLOATOBJ_SetFloat(&mxTemp.efM22, pxform->eM22); FLOATOBJ_SetFloat(&mxTemp.efDx, pxform->eDx); FLOATOBJ_SetFloat(&mxTemp.efDy, pxform->eDy); /* Update accelerators and return complexity */ Hint = MX_UpdateAccel(&mxTemp); /* Check whether det = (M11 * M22 - M12 * M21) is non-zero */ if (Hint == GX_SCALE) { if (FLOATOBJ_Equal0(&mxTemp.efM11) || FLOATOBJ_Equal0(&mxTemp.efM22)) { return DDI_ERROR; } } else if (Hint == GX_GENERAL) { if (!MX_IsInvertible(&mxTemp)) { return DDI_ERROR; } } /* Store */ pmx = XFORMOBJ_pmx(pxo); *pmx = mxTemp; return Hint; } /* * Multiplies pxo1 with pxo2 and stores the result in pxo. * returns complexity hint * | efM11 efM12 0 | * | efM21 efM22 0 | * | efDx efDy 1 | */ ULONG NTAPI XFORMOBJ_iCombine( IN OUT XFORMOBJ *pxo, IN XFORMOBJ *pxo1, IN XFORMOBJ *pxo2) { MATRIX mx; PMATRIX pmx, pmx1, pmx2; /* Get the source matrices */ pmx1 = XFORMOBJ_pmx(pxo1); pmx2 = XFORMOBJ_pmx(pxo2); /* Do a 3 x 3 matrix multiplication with mx as destinantion */ MulAdd(&mx.efM11, &pmx1->efM11, &pmx2->efM11, &pmx1->efM12, &pmx2->efM21); MulAdd(&mx.efM12, &pmx1->efM11, &pmx2->efM12, &pmx1->efM12, &pmx2->efM22); MulAdd(&mx.efM21, &pmx1->efM21, &pmx2->efM11, &pmx1->efM22, &pmx2->efM21); MulAdd(&mx.efM22, &pmx1->efM21, &pmx2->efM12, &pmx1->efM22, &pmx2->efM22); MulAdd(&mx.efDx, &pmx1->efDx, &pmx2->efM11, &pmx1->efDy, &pmx2->efM21); FLOATOBJ_Add(&mx.efDx, &pmx2->efDx); MulAdd(&mx.efDy, &pmx1->efDx, &pmx2->efM12, &pmx1->efDy, &pmx2->efM22); FLOATOBJ_Add(&mx.efDy, &pmx2->efDy); /* Copy back */ pmx = XFORMOBJ_pmx(pxo); *pmx = mx; /* Update accelerators and return complexity */ return XFORMOBJ_UpdateAccel(pxo); } ULONG NTAPI XFORMOBJ_iCombineXform( IN OUT XFORMOBJ *pxo, IN XFORMOBJ *pxo1, IN XFORML *pxform, IN BOOL bLeftMultiply) { MATRIX mx; XFORMOBJ xo2; XFORMOBJ_vInit(&xo2, &mx); XFORMOBJ_iSetXform(&xo2, pxform); if (bLeftMultiply) { return XFORMOBJ_iCombine(pxo, &xo2, pxo1); } else { return XFORMOBJ_iCombine(pxo, pxo1, &xo2); } } BOOL FASTCALL MX_IsInvertible(IN PMATRIX pmx) { FLOATOBJ foDet; MulSub(&foDet, &pmx->efM11, &pmx->efM22, &pmx->efM12, &pmx->efM21); return !FLOATOBJ_Equal0(&foDet); } VOID FASTCALL MX_Set0(OUT PMATRIX pmx) { FLOATOBJ_Set0(&pmx->efM11); FLOATOBJ_Set0(&pmx->efM12); FLOATOBJ_Set0(&pmx->efM21); FLOATOBJ_Set0(&pmx->efM22); FLOATOBJ_Set0(&pmx->efDx); FLOATOBJ_Set0(&pmx->efDy); } /* * A^-1 = adj(A) / det(AT) * A^-1 = 1/(a*d - b*c) * (a22,-a12,a21,-a11) */ ULONG NTAPI XFORMOBJ_iInverse( OUT XFORMOBJ *pxoDst, IN XFORMOBJ *pxoSrc) { PMATRIX pmxDst, pmxSrc; FLOATOBJ foDet; XFORM xformSrc; pmxDst = XFORMOBJ_pmx(pxoDst); pmxSrc = XFORMOBJ_pmx(pxoSrc); XFORMOBJ_iGetXform(pxoSrc, (XFORML*)&xformSrc); /* det = M11 * M22 - M12 * M21 */ MulSub(&foDet, &pmxSrc->efM11, &pmxSrc->efM22, &pmxSrc->efM12, &pmxSrc->efM21); if (FLOATOBJ_Equal0(&foDet)) { /* Determinant is 0! */ return DDI_ERROR; } /* Calculate adj(A) / det(A) */ pmxDst->efM11 = pmxSrc->efM22; FLOATOBJ_Div(&pmxDst->efM11, &foDet); pmxDst->efM22 = pmxSrc->efM11; FLOATOBJ_Div(&pmxDst->efM22, &foDet); /* The other 2 are negative, negate foDet for that */ FLOATOBJ_Neg(&foDet); pmxDst->efM12 = pmxSrc->efM12; FLOATOBJ_Div(&pmxDst->efM12, &foDet); pmxDst->efM21 = pmxSrc->efM21; FLOATOBJ_Div(&pmxDst->efM21, &foDet); /* Calculate the inverted x shift: Dx' = -Dx * M11' - Dy * M21' */ pmxDst->efDx = pmxSrc->efDx; FLOATOBJ_Neg(&pmxDst->efDx); MulSub(&pmxDst->efDx, &pmxDst->efDx, &pmxDst->efM11, &pmxSrc->efDy, &pmxDst->efM21); /* Calculate the inverted y shift: Dy' = -Dy * M22' - Dx * M12' */ pmxDst->efDy = pmxSrc->efDy; FLOATOBJ_Neg(&pmxDst->efDy); MulSub(&pmxDst->efDy, &pmxDst->efDy, &pmxDst->efM22, &pmxSrc->efDx, &pmxDst->efM12); /* Update accelerators and return complexity */ return XFORMOBJ_UpdateAccel(pxoDst); } /*! * \brief Transforms fix-point coordinates in an array of POINTL structures using * the transformation matrix from the XFORMOBJ. * * \param pxo - Pointer to the XFORMOBJ * * \param cPoints - Number of coordinates to transform * * \param pptIn - Pointer to an array of POINTL structures containing the * source coordinates. * * \param pptOut - Pointer to an array of POINTL structures, receiving the * transformed coordinates. Can be the same as pptIn. * * \return TRUE if the operation was successful, FALSE if any of the calculations * caused an integer overflow. * * \note If the function returns FALSE, it might still have written to the * output buffer. If pptIn and pptOut are equal, the source coordinates * might have been partly overwritten! */ static BOOL NTAPI XFORMOBJ_bXformFixPoints( _In_ XFORMOBJ *pxo, _In_ ULONG cPoints, _In_reads_(cPoints) PPOINTL pptIn, _Out_writes_(cPoints) PPOINTL pptOut) { PMATRIX pmx; INT i; FLOATOBJ fo1, fo2; FLONG flAccel; LONG lM11, lM12, lM21, lM22, lTemp; register LONGLONG llx, lly; pmx = XFORMOBJ_pmx(pxo); flAccel = pmx->flAccel; if ((flAccel & (XFORM_SCALE|XFORM_UNITY)) == (XFORM_SCALE|XFORM_UNITY)) { /* Identity transformation */ RtlCopyMemory(pptOut, pptIn, cPoints * sizeof(POINTL)); } else if (flAccel & XFORM_INTEGER) { if (flAccel & XFORM_UNITY) { /* 1-scale integer transform, get the off-diagonal elements */ if (!FLOATOBJ_bConvertToLong(&pmx->efM12, &lM12) || !FLOATOBJ_bConvertToLong(&pmx->efM21, &lM21)) { NT_ASSERT(FALSE); return FALSE; } i = cPoints - 1; do { /* Calculate x in 64 bit and check for overflow */ llx = Int32x32To64(pptIn[i].y, lM21) + pptIn[i].x; if (DOES_VALUE_OVERFLOW_LONG(llx)) { return FALSE; } /* Calculate y in 64 bit and check for overflow */ lly = Int32x32To64(pptIn[i].x, lM12) + pptIn[i].y; if (DOES_VALUE_OVERFLOW_LONG(lly)) { return FALSE; } /* Write back the results */ pptOut[i].x = (LONG)llx; pptOut[i].y = (LONG)lly; } while (--i >= 0); } else if (flAccel & XFORM_SCALE) { /* Diagonal integer transform, get the diagonal elements */ if (!FLOATOBJ_bConvertToLong(&pmx->efM11, &lM11) || !FLOATOBJ_bConvertToLong(&pmx->efM22, &lM22)) { NT_ASSERT(FALSE); return FALSE; } i = cPoints - 1; do { /* Calculate x in 64 bit and check for overflow */ llx = Int32x32To64(pptIn[i].x, lM11); if (DOES_VALUE_OVERFLOW_LONG(llx)) { return FALSE; } /* Calculate y in 64 bit and check for overflow */ lly = Int32x32To64(pptIn[i].y, lM22); if (DOES_VALUE_OVERFLOW_LONG(lly)) { return FALSE; } /* Write back the results */ pptOut[i].x = (LONG)llx; pptOut[i].y = (LONG)lly; } while (--i >= 0); } else { /* Full integer transform */ if (!FLOATOBJ_bConvertToLong(&pmx->efM11, &lM11) || !FLOATOBJ_bConvertToLong(&pmx->efM12, &lM12) || !FLOATOBJ_bConvertToLong(&pmx->efM21, &lM21) || !FLOATOBJ_bConvertToLong(&pmx->efM22, &lM22)) { NT_ASSERT(FALSE); return FALSE; } i = cPoints - 1; do { /* Calculate x in 64 bit and check for overflow */ llx = Int32x32To64(pptIn[i].x, lM11); llx += Int32x32To64(pptIn[i].y, lM21); if (DOES_VALUE_OVERFLOW_LONG(llx)) { return FALSE; } /* Calculate y in 64 bit and check for overflow */ lly = Int32x32To64(pptIn[i].y, lM22); lly += Int32x32To64(pptIn[i].x, lM12); if (DOES_VALUE_OVERFLOW_LONG(lly)) { return FALSE; } /* Write back the results */ pptOut[i].x = (LONG)llx; pptOut[i].y = (LONG)lly; } while (--i >= 0); } } else if (flAccel & XFORM_UNITY) { /* 1-scale transform */ i = cPoints - 1; do { /* Calculate x in 64 bit and check for overflow */ fo1 = pmx->efM21; FLOATOBJ_MulLong(&fo1, pptIn[i].y); if (!FLOATOBJ_bConvertToLong(&fo1, &lTemp)) { return FALSE; } llx = (LONGLONG)pptIn[i].x + lTemp; if (DOES_VALUE_OVERFLOW_LONG(llx)) { return FALSE; } /* Calculate y in 64 bit and check for overflow */ fo2 = pmx->efM12; FLOATOBJ_MulLong(&fo2, pptIn[i].x); if (!FLOATOBJ_bConvertToLong(&fo2, &lTemp)) { return FALSE; } lly = (LONGLONG)pptIn[i].y + lTemp; if (DOES_VALUE_OVERFLOW_LONG(lly)) { return FALSE; } /* Write back the results */ pptOut[i].x = (LONG)llx; pptOut[i].y = (LONG)lly; } while (--i >= 0); } else if (flAccel & XFORM_SCALE) { /* Diagonal float transform */ i = cPoints - 1; do { fo1 = pmx->efM11; FLOATOBJ_MulLong(&fo1, pptIn[i].x); if (!FLOATOBJ_bConvertToLong(&fo1, &pptOut[i].x)) { return FALSE; } fo2 = pmx->efM22; FLOATOBJ_MulLong(&fo2, pptIn[i].y); if (!FLOATOBJ_bConvertToLong(&fo2, &pptOut[i].y)) { return FALSE; } } while (--i >= 0); } else { /* Full float transform */ i = cPoints - 1; do { /* Calculate x as FLOATOBJ */ MulAddLong(&fo1, &pmx->efM11, pptIn[i].x, &pmx->efM21, pptIn[i].y); /* Calculate y as FLOATOBJ */ MulAddLong(&fo2, &pmx->efM12, pptIn[i].x, &pmx->efM22, pptIn[i].y); if (!FLOATOBJ_bConvertToLong(&fo1, &pptOut[i].x)) { return FALSE; } if (!FLOATOBJ_bConvertToLong(&fo2, &pptOut[i].y)) { return FALSE; } } while (--i >= 0); } if (!(pmx->flAccel & XFORM_NO_TRANSLATION)) { /* Translate points */ i = cPoints - 1; do { llx = (LONGLONG)pptOut[i].x + pmx->fxDx; if (DOES_VALUE_OVERFLOW_LONG(llx)) { return FALSE; } pptOut[i].x = (LONG)llx; lly = (LONGLONG)pptOut[i].y + pmx->fxDy; if (DOES_VALUE_OVERFLOW_LONG(lly)) { return FALSE; } pptOut[i].y = (LONG)lly; } while (--i >= 0); } return TRUE; } /** Public functions **********************************************************/ // www.osr.com/ddk/graphics/gdifncs_0s2v.htm ULONG APIENTRY XFORMOBJ_iGetXform( IN XFORMOBJ *pxo, OUT XFORML *pxform) { PMATRIX pmx = XFORMOBJ_pmx(pxo); /* Check parameters */ if (!pxo || !pxform) { return DDI_ERROR; } /* Copy members */ pxform->eM11 = FLOATOBJ_GetFloat(&pmx->efM11); pxform->eM12 = FLOATOBJ_GetFloat(&pmx->efM12); pxform->eM21 = FLOATOBJ_GetFloat(&pmx->efM21); pxform->eM22 = FLOATOBJ_GetFloat(&pmx->efM22); pxform->eDx = FLOATOBJ_GetFloat(&pmx->efDx); pxform->eDy = FLOATOBJ_GetFloat(&pmx->efDy); /* Return complexity hint */ return HintFromAccel(pmx->flAccel); } // www.osr.com/ddk/graphics/gdifncs_5ig7.htm ULONG APIENTRY XFORMOBJ_iGetFloatObjXform( IN XFORMOBJ *pxo, OUT FLOATOBJ_XFORM *pxfo) { PMATRIX pmx = XFORMOBJ_pmx(pxo); /* Check parameters */ if (!pxo || !pxfo) { return DDI_ERROR; } /* Copy members */ pxfo->eM11 = pmx->efM11; pxfo->eM12 = pmx->efM12; pxfo->eM21 = pmx->efM21; pxfo->eM22 = pmx->efM22; pxfo->eDx = pmx->efDx; pxfo->eDy = pmx->efDy; /* Return complexity hint */ return HintFromAccel(pmx->flAccel); } // www.osr.com/ddk/graphics/gdifncs_027b.htm BOOL APIENTRY XFORMOBJ_bApplyXform( IN XFORMOBJ *pxo, IN ULONG iMode, IN ULONG cPoints, IN PVOID pvIn, OUT PVOID pvOut) { MATRIX mx; XFORMOBJ xoInv; PPOINTL pptlIn, pptlOut; INT i; /* Check parameters */ if (!pxo || !pvIn || !pvOut || cPoints < 1) { return FALSE; } /* Use inverse xform? */ if (iMode == XF_INV_FXTOL || iMode == XF_INV_LTOL) { XFORMOBJ_vInit(&xoInv, &mx); if (XFORMOBJ_iInverse(&xoInv, pxo) == DDI_ERROR) { return FALSE; } pxo = &xoInv; } /* Convert POINTL to POINTFIX? */ if (iMode == XF_LTOFX || iMode == XF_LTOL || iMode == XF_INV_LTOL) { pptlIn = pvIn; pptlOut = pvOut; for (i = cPoints - 1; i >= 0; i--) { pptlOut[i].x = LONG2FIX(pptlIn[i].x); pptlOut[i].y = LONG2FIX(pptlIn[i].y); } /* The input is in the out buffer now! */ pvIn = pvOut; } /* Do the actual fixpoint transformation */ if (!XFORMOBJ_bXformFixPoints(pxo, cPoints, pvIn, pvOut)) { return FALSE; } /* Convert POINTFIX to POINTL? */ if (iMode == XF_INV_FXTOL || iMode == XF_INV_LTOL || iMode == XF_LTOL) { pptlOut = pvOut; for (i = cPoints - 1; i >= 0; i--) { pptlOut[i].x = FIX2LONG(pptlOut[i].x); pptlOut[i].y = FIX2LONG(pptlOut[i].y); } } return TRUE; } /* EOF */