1 /* 2 * PROJECT: ReactOS win32 kernel mode subsystem 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: win32ss/gdi/ntgdi/xformobj.c 5 * PURPOSE: XFORMOBJ API 6 * PROGRAMMER: Timo Kreuzer 7 */ 8 9 /** Includes ******************************************************************/ 10 11 #include <win32k.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 #define DOES_VALUE_OVERFLOW_LONG(x) \ 16 (((__int64)((long)(x))) != (x)) 17 18 /** Inline helper functions ***************************************************/ 19 20 /* 21 * Inline helper to calculate pfo1 * pfo2 + pfo3 * pfo4 22 */ 23 FORCEINLINE 24 VOID 25 MulAdd( 26 PFLOATOBJ pfoDest, 27 PFLOATOBJ pfo1, 28 PFLOATOBJ pfo2, 29 PFLOATOBJ pfo3, 30 PFLOATOBJ pfo4) 31 { 32 FLOATOBJ foTmp; 33 34 *pfoDest = *pfo1; 35 FLOATOBJ_Mul(pfoDest, pfo2); 36 foTmp = *pfo3; 37 FLOATOBJ_Mul(&foTmp, pfo4); 38 FLOATOBJ_Add(pfoDest, &foTmp); 39 } 40 41 /* 42 * Inline helper to calculate pfo1 * l2 + pfo3 * l4 43 */ 44 FORCEINLINE 45 VOID 46 MulAddLong( 47 PFLOATOBJ pfoDest, 48 PFLOATOBJ pfo1, 49 LONG l2, 50 PFLOATOBJ pfo3, 51 LONG l4) 52 { 53 FLOATOBJ foTmp; 54 55 *pfoDest = *pfo1; 56 FLOATOBJ_MulLong(pfoDest, l2); 57 foTmp = *pfo3; 58 FLOATOBJ_MulLong(&foTmp, l4); 59 FLOATOBJ_Add(pfoDest, &foTmp); 60 } 61 62 /* 63 * Inline helper to calculate pfo1 * pfo2 - pfo3 * pfo4 64 */ 65 FORCEINLINE 66 VOID 67 MulSub( 68 PFLOATOBJ pfoDest, 69 PFLOATOBJ pfo1, 70 PFLOATOBJ pfo2, 71 PFLOATOBJ pfo3, 72 PFLOATOBJ pfo4) 73 { 74 FLOATOBJ foTmp; 75 76 *pfoDest = *pfo1; 77 FLOATOBJ_Mul(pfoDest, pfo2); 78 foTmp = *pfo3; 79 FLOATOBJ_Mul(&foTmp, pfo4); 80 FLOATOBJ_Sub(pfoDest, &foTmp); 81 } 82 83 /* 84 * Inline helper to get the complexity hint from flAccel 85 */ 86 FORCEINLINE 87 ULONG 88 HintFromAccel(ULONG flAccel) 89 { 90 switch (flAccel & (XFORM_SCALE|XFORM_UNITY|XFORM_NO_TRANSLATION)) 91 { 92 case (XFORM_SCALE|XFORM_UNITY|XFORM_NO_TRANSLATION): 93 return GX_IDENTITY; 94 case (XFORM_SCALE|XFORM_UNITY): 95 return GX_OFFSET; 96 case XFORM_SCALE: 97 return GX_SCALE; 98 default: 99 return GX_GENERAL; 100 } 101 } 102 103 /** Internal functions ********************************************************/ 104 105 ULONG 106 NTAPI 107 XFORMOBJ_UpdateAccel( 108 IN XFORMOBJ *pxo) 109 { 110 PMATRIX pmx = XFORMOBJ_pmx(pxo); 111 112 /* Copy Dx and Dy to FIX format */ 113 pmx->fxDx = FLOATOBJ_GetFix(&pmx->efDx); 114 pmx->fxDy = FLOATOBJ_GetFix(&pmx->efDy); 115 116 pmx->flAccel = 0; 117 118 if (FLOATOBJ_Equal0(&pmx->efDx) && 119 FLOATOBJ_Equal0(&pmx->efDy)) 120 { 121 pmx->flAccel |= XFORM_NO_TRANSLATION; 122 } 123 124 if (FLOATOBJ_Equal0(&pmx->efM12) && 125 FLOATOBJ_Equal0(&pmx->efM21)) 126 { 127 pmx->flAccel |= XFORM_SCALE; 128 } 129 130 if (FLOATOBJ_Equal1(&pmx->efM11) && 131 FLOATOBJ_Equal1(&pmx->efM22)) 132 { 133 pmx->flAccel |= XFORM_UNITY; 134 } 135 136 if (FLOATOBJ_IsLong(&pmx->efM11) && FLOATOBJ_IsLong(&pmx->efM12) && 137 FLOATOBJ_IsLong(&pmx->efM21) && FLOATOBJ_IsLong(&pmx->efM22)) 138 { 139 pmx->flAccel |= XFORM_INTEGER; 140 } 141 142 return HintFromAccel(pmx->flAccel); 143 } 144 145 146 ULONG 147 NTAPI 148 XFORMOBJ_iSetXform( 149 OUT XFORMOBJ *pxo, 150 IN const XFORML *pxform) 151 { 152 PMATRIX pmx = XFORMOBJ_pmx(pxo); 153 FLOATOBJ ef1, ef2, efTemp; 154 155 /* Check parameters */ 156 if (!pxo || !pxform) return DDI_ERROR; 157 158 /* Check if the xform is valid */ 159 /* M11 * M22 - M12 * M21 != 0 */ 160 FLOATOBJ_SetFloat(&ef1, pxform->eM11); 161 FLOATOBJ_SetFloat(&efTemp, pxform->eM22); 162 FLOATOBJ_Mul(&ef1, &efTemp); 163 FLOATOBJ_SetFloat(&ef2, pxform->eM12); 164 FLOATOBJ_SetFloat(&efTemp, pxform->eM21); 165 FLOATOBJ_Mul(&ef2, &efTemp); 166 if (FLOATOBJ_Equal(&ef1, &ef2)) 167 return DDI_ERROR; 168 169 /* Copy members */ 170 FLOATOBJ_SetFloat(&pmx->efM11, pxform->eM11); 171 FLOATOBJ_SetFloat(&pmx->efM12, pxform->eM12); 172 FLOATOBJ_SetFloat(&pmx->efM21, pxform->eM21); 173 FLOATOBJ_SetFloat(&pmx->efM22, pxform->eM22); 174 FLOATOBJ_SetFloat(&pmx->efDx, pxform->eDx); 175 FLOATOBJ_SetFloat(&pmx->efDy, pxform->eDy); 176 177 /* Update accelerators and return complexity */ 178 return XFORMOBJ_UpdateAccel(pxo); 179 } 180 181 182 /* 183 * Multiplies pxo1 with pxo2 and stores the result in pxo. 184 * returns complexity hint 185 * | efM11 efM12 0 | 186 * | efM21 efM22 0 | 187 * | efDx efDy 1 | 188 */ 189 ULONG 190 NTAPI 191 XFORMOBJ_iCombine( 192 IN XFORMOBJ *pxo, 193 IN XFORMOBJ *pxo1, 194 IN XFORMOBJ *pxo2) 195 { 196 MATRIX mx; 197 PMATRIX pmx, pmx1, pmx2; 198 199 /* Get the source matrices */ 200 pmx1 = XFORMOBJ_pmx(pxo1); 201 pmx2 = XFORMOBJ_pmx(pxo2); 202 203 /* Do a 3 x 3 matrix multiplication with mx as destinantion */ 204 MulAdd(&mx.efM11, &pmx1->efM11, &pmx2->efM11, &pmx1->efM12, &pmx2->efM21); 205 MulAdd(&mx.efM12, &pmx1->efM11, &pmx2->efM12, &pmx1->efM12, &pmx2->efM22); 206 MulAdd(&mx.efM21, &pmx1->efM21, &pmx2->efM11, &pmx1->efM22, &pmx2->efM21); 207 MulAdd(&mx.efM22, &pmx1->efM21, &pmx2->efM12, &pmx1->efM22, &pmx2->efM22); 208 MulAdd(&mx.efDx, &pmx1->efDx, &pmx2->efM11, &pmx1->efDy, &pmx2->efM21); 209 FLOATOBJ_Add(&mx.efDx, &pmx2->efDx); 210 MulAdd(&mx.efDy, &pmx1->efDx, &pmx2->efM12, &pmx1->efDy, &pmx2->efM22); 211 FLOATOBJ_Add(&mx.efDy, &pmx2->efDy); 212 213 /* Copy back */ 214 pmx = XFORMOBJ_pmx(pxo); 215 *pmx = mx; 216 217 /* Update accelerators and return complexity */ 218 return XFORMOBJ_UpdateAccel(pxo); 219 } 220 221 222 ULONG 223 NTAPI 224 XFORMOBJ_iCombineXform( 225 IN XFORMOBJ *pxo, 226 IN XFORMOBJ *pxo1, 227 IN XFORML *pxform, 228 IN BOOL bLeftMultiply) 229 { 230 MATRIX mx; 231 XFORMOBJ xo2; 232 233 XFORMOBJ_vInit(&xo2, &mx); 234 XFORMOBJ_iSetXform(&xo2, pxform); 235 236 if (bLeftMultiply) 237 { 238 return XFORMOBJ_iCombine(pxo, &xo2, pxo1); 239 } 240 else 241 { 242 return XFORMOBJ_iCombine(pxo, pxo1, &xo2); 243 } 244 } 245 246 /* 247 * A^-1 = adj(A) / det(AT) 248 * A^-1 = 1/(a*d - b*c) * (a22,-a12,a21,-a11) 249 */ 250 ULONG 251 NTAPI 252 XFORMOBJ_iInverse( 253 OUT XFORMOBJ *pxoDst, 254 IN XFORMOBJ *pxoSrc) 255 { 256 PMATRIX pmxDst, pmxSrc; 257 FLOATOBJ foDet; 258 XFORM xformSrc; 259 260 pmxDst = XFORMOBJ_pmx(pxoDst); 261 pmxSrc = XFORMOBJ_pmx(pxoSrc); 262 263 XFORMOBJ_iGetXform(pxoSrc, (XFORML*)&xformSrc); 264 265 /* det = M11 * M22 - M12 * M21 */ 266 MulSub(&foDet, &pmxSrc->efM11, &pmxSrc->efM22, &pmxSrc->efM12, &pmxSrc->efM21); 267 268 if (FLOATOBJ_Equal0(&foDet)) 269 { 270 /* Determinant is 0! */ 271 return DDI_ERROR; 272 } 273 274 /* Calculate adj(A) / det(A) */ 275 pmxDst->efM11 = pmxSrc->efM22; 276 FLOATOBJ_Div(&pmxDst->efM11, &foDet); 277 pmxDst->efM22 = pmxSrc->efM11; 278 FLOATOBJ_Div(&pmxDst->efM22, &foDet); 279 280 /* The other 2 are negative, negate foDet for that */ 281 FLOATOBJ_Neg(&foDet); 282 pmxDst->efM12 = pmxSrc->efM12; 283 FLOATOBJ_Div(&pmxDst->efM12, &foDet); 284 pmxDst->efM21 = pmxSrc->efM21; 285 FLOATOBJ_Div(&pmxDst->efM21, &foDet); 286 287 /* Calculate the inverted x shift: Dx' = -Dx * M11' - Dy * M21' */ 288 pmxDst->efDx = pmxSrc->efDx; 289 FLOATOBJ_Neg(&pmxDst->efDx); 290 MulSub(&pmxDst->efDx, &pmxDst->efDx, &pmxDst->efM11, &pmxSrc->efDy, &pmxDst->efM21); 291 292 /* Calculate the inverted y shift: Dy' = -Dy * M22' - Dx * M12' */ 293 pmxDst->efDy = pmxSrc->efDy; 294 FLOATOBJ_Neg(&pmxDst->efDy); 295 MulSub(&pmxDst->efDy, &pmxDst->efDy, &pmxDst->efM22, &pmxSrc->efDx, &pmxDst->efM12); 296 297 /* Update accelerators and return complexity */ 298 return XFORMOBJ_UpdateAccel(pxoDst); 299 } 300 301 302 /*! 303 * \brief Transforms fix-point coordinates in an array of POINTL structures using 304 * the transformation matrix from the XFORMOBJ. 305 * 306 * \param pxo - Pointer to the XFORMOBJ 307 * 308 * \param cPoints - Number of coordinates to transform 309 * 310 * \param pptIn - Pointer to an array of POINTL structures containing the 311 * source coordinates. 312 * 313 * \param pptOut - Pointer to an array of POINTL structures, receiving the 314 * transformed coordinates. Can be the same as pptIn. 315 * 316 * \return TRUE if the operation was successful, FALSE if any of the calculations 317 * caused an integer overflow. 318 * 319 * \note If the function returns FALSE, it might still have written to the 320 * output buffer. If pptIn and pptOut are equal, the source coordinates 321 * might have been partly overwritten! 322 */ 323 static 324 BOOL 325 NTAPI 326 XFORMOBJ_bXformFixPoints( 327 _In_ XFORMOBJ *pxo, 328 _In_ ULONG cPoints, 329 _In_reads_(cPoints) PPOINTL pptIn, 330 _Out_writes_(cPoints) PPOINTL pptOut) 331 { 332 PMATRIX pmx; 333 INT i; 334 FLOATOBJ fo1, fo2; 335 FLONG flAccel; 336 LONG lM11, lM12, lM21, lM22, lTemp; 337 register LONGLONG llx, lly; 338 339 pmx = XFORMOBJ_pmx(pxo); 340 flAccel = pmx->flAccel; 341 342 if ((flAccel & (XFORM_SCALE|XFORM_UNITY)) == (XFORM_SCALE|XFORM_UNITY)) 343 { 344 /* Identity transformation */ 345 RtlCopyMemory(pptOut, pptIn, cPoints * sizeof(POINTL)); 346 } 347 else if (flAccel & XFORM_INTEGER) 348 { 349 if (flAccel & XFORM_UNITY) 350 { 351 /* 1-scale integer transform, get the off-diagonal elements */ 352 if (!FLOATOBJ_bConvertToLong(&pmx->efM12, &lM12) || 353 !FLOATOBJ_bConvertToLong(&pmx->efM21, &lM21)) 354 { 355 NT_ASSERT(FALSE); 356 return FALSE; 357 } 358 359 i = cPoints - 1; 360 do 361 { 362 /* Calculate x in 64 bit and check for overflow */ 363 llx = Int32x32To64(pptIn[i].y, lM21) + pptIn[i].x; 364 if (DOES_VALUE_OVERFLOW_LONG(llx)) 365 { 366 return FALSE; 367 } 368 369 /* Calculate y in 64 bit and check for overflow */ 370 lly = Int32x32To64(pptIn[i].x, lM12) + pptIn[i].y; 371 if (DOES_VALUE_OVERFLOW_LONG(lly)) 372 { 373 return FALSE; 374 } 375 376 /* Write back the results */ 377 pptOut[i].x = (LONG)llx; 378 pptOut[i].y = (LONG)lly; 379 } 380 while (--i >= 0); 381 } 382 else if (flAccel & XFORM_SCALE) 383 { 384 /* Diagonal integer transform, get the diagonal elements */ 385 if (!FLOATOBJ_bConvertToLong(&pmx->efM11, &lM11) || 386 !FLOATOBJ_bConvertToLong(&pmx->efM22, &lM22)) 387 { 388 NT_ASSERT(FALSE); 389 return FALSE; 390 } 391 392 i = cPoints - 1; 393 do 394 { 395 /* Calculate x in 64 bit and check for overflow */ 396 llx = Int32x32To64(pptIn[i].x, lM11); 397 if (DOES_VALUE_OVERFLOW_LONG(llx)) 398 { 399 return FALSE; 400 } 401 402 /* Calculate y in 64 bit and check for overflow */ 403 lly = Int32x32To64(pptIn[i].y, lM22); 404 if (DOES_VALUE_OVERFLOW_LONG(lly)) 405 { 406 return FALSE; 407 } 408 409 /* Write back the results */ 410 pptOut[i].x = (LONG)llx; 411 pptOut[i].y = (LONG)lly; 412 } 413 while (--i >= 0); 414 } 415 else 416 { 417 /* Full integer transform */ 418 if (!FLOATOBJ_bConvertToLong(&pmx->efM11, &lM11) || 419 !FLOATOBJ_bConvertToLong(&pmx->efM12, &lM12) || 420 !FLOATOBJ_bConvertToLong(&pmx->efM21, &lM21) || 421 !FLOATOBJ_bConvertToLong(&pmx->efM22, &lM22)) 422 { 423 NT_ASSERT(FALSE); 424 return FALSE; 425 } 426 427 i = cPoints - 1; 428 do 429 { 430 /* Calculate x in 64 bit and check for overflow */ 431 llx = Int32x32To64(pptIn[i].x, lM11); 432 llx += Int32x32To64(pptIn[i].y, lM21); 433 if (DOES_VALUE_OVERFLOW_LONG(llx)) 434 { 435 return FALSE; 436 } 437 438 /* Calculate y in 64 bit and check for overflow */ 439 lly = Int32x32To64(pptIn[i].y, lM22); 440 lly += Int32x32To64(pptIn[i].x, lM12); 441 if (DOES_VALUE_OVERFLOW_LONG(lly)) 442 { 443 return FALSE; 444 } 445 446 /* Write back the results */ 447 pptOut[i].x = (LONG)llx; 448 pptOut[i].y = (LONG)lly; 449 } 450 while (--i >= 0); 451 } 452 } 453 else if (flAccel & XFORM_UNITY) 454 { 455 /* 1-scale transform */ 456 i = cPoints - 1; 457 do 458 { 459 /* Calculate x in 64 bit and check for overflow */ 460 fo1 = pmx->efM21; 461 FLOATOBJ_MulLong(&fo1, pptIn[i].y); 462 if (!FLOATOBJ_bConvertToLong(&fo1, &lTemp)) 463 { 464 return FALSE; 465 } 466 llx = (LONGLONG)pptIn[i].x + lTemp; 467 if (DOES_VALUE_OVERFLOW_LONG(llx)) 468 { 469 return FALSE; 470 } 471 472 /* Calculate y in 64 bit and check for overflow */ 473 fo2 = pmx->efM12; 474 FLOATOBJ_MulLong(&fo2, pptIn[i].x); 475 if (!FLOATOBJ_bConvertToLong(&fo2, &lTemp)) 476 { 477 return FALSE; 478 } 479 lly = (LONGLONG)pptIn[i].y + lTemp; 480 if (DOES_VALUE_OVERFLOW_LONG(lly)) 481 { 482 return FALSE; 483 } 484 485 /* Write back the results */ 486 pptOut[i].x = (LONG)llx; 487 pptOut[i].y = (LONG)lly; 488 } 489 while (--i >= 0); 490 } 491 else if (flAccel & XFORM_SCALE) 492 { 493 /* Diagonal float transform */ 494 i = cPoints - 1; 495 do 496 { 497 fo1 = pmx->efM11; 498 FLOATOBJ_MulLong(&fo1, pptIn[i].x); 499 if (!FLOATOBJ_bConvertToLong(&fo1, &pptOut[i].x)) 500 { 501 return FALSE; 502 } 503 504 fo2 = pmx->efM22; 505 FLOATOBJ_MulLong(&fo2, pptIn[i].y); 506 if (!FLOATOBJ_bConvertToLong(&fo2, &pptOut[i].y)) 507 { 508 return FALSE; 509 } 510 } 511 while (--i >= 0); 512 } 513 else 514 { 515 /* Full float transform */ 516 i = cPoints - 1; 517 do 518 { 519 /* Calculate x as FLOATOBJ */ 520 MulAddLong(&fo1, &pmx->efM11, pptIn[i].x, &pmx->efM21, pptIn[i].y); 521 522 /* Calculate y as FLOATOBJ */ 523 MulAddLong(&fo2, &pmx->efM12, pptIn[i].x, &pmx->efM22, pptIn[i].y); 524 525 if (!FLOATOBJ_bConvertToLong(&fo1, &pptOut[i].x)) 526 { 527 return FALSE; 528 } 529 530 if (!FLOATOBJ_bConvertToLong(&fo2, &pptOut[i].y)) 531 { 532 return FALSE; 533 } 534 } 535 while (--i >= 0); 536 } 537 538 if (!(pmx->flAccel & XFORM_NO_TRANSLATION)) 539 { 540 /* Translate points */ 541 i = cPoints - 1; 542 do 543 { 544 llx = (LONGLONG)pptOut[i].x + pmx->fxDx; 545 if (DOES_VALUE_OVERFLOW_LONG(llx)) 546 { 547 return FALSE; 548 } 549 pptOut[i].x = (LONG)llx; 550 551 lly = (LONGLONG)pptOut[i].y + pmx->fxDy; 552 if (DOES_VALUE_OVERFLOW_LONG(lly)) 553 { 554 return FALSE; 555 } 556 pptOut[i].y = (LONG)lly; 557 } 558 while (--i >= 0); 559 } 560 561 return TRUE; 562 } 563 564 /** Public functions **********************************************************/ 565 566 // www.osr.com/ddk/graphics/gdifncs_0s2v.htm 567 ULONG 568 APIENTRY 569 XFORMOBJ_iGetXform( 570 IN XFORMOBJ *pxo, 571 OUT XFORML *pxform) 572 { 573 PMATRIX pmx = XFORMOBJ_pmx(pxo); 574 575 /* Check parameters */ 576 if (!pxo || !pxform) 577 { 578 return DDI_ERROR; 579 } 580 581 /* Copy members */ 582 pxform->eM11 = FLOATOBJ_GetFloat(&pmx->efM11); 583 pxform->eM12 = FLOATOBJ_GetFloat(&pmx->efM12); 584 pxform->eM21 = FLOATOBJ_GetFloat(&pmx->efM21); 585 pxform->eM22 = FLOATOBJ_GetFloat(&pmx->efM22); 586 pxform->eDx = FLOATOBJ_GetFloat(&pmx->efDx); 587 pxform->eDy = FLOATOBJ_GetFloat(&pmx->efDy); 588 589 /* Return complexity hint */ 590 return HintFromAccel(pmx->flAccel); 591 } 592 593 594 // www.osr.com/ddk/graphics/gdifncs_5ig7.htm 595 ULONG 596 APIENTRY 597 XFORMOBJ_iGetFloatObjXform( 598 IN XFORMOBJ *pxo, 599 OUT FLOATOBJ_XFORM *pxfo) 600 { 601 PMATRIX pmx = XFORMOBJ_pmx(pxo); 602 603 /* Check parameters */ 604 if (!pxo || !pxfo) 605 { 606 return DDI_ERROR; 607 } 608 609 /* Copy members */ 610 pxfo->eM11 = pmx->efM11; 611 pxfo->eM12 = pmx->efM12; 612 pxfo->eM21 = pmx->efM21; 613 pxfo->eM22 = pmx->efM22; 614 pxfo->eDx = pmx->efDx; 615 pxfo->eDy = pmx->efDy; 616 617 /* Return complexity hint */ 618 return HintFromAccel(pmx->flAccel); 619 } 620 621 622 // www.osr.com/ddk/graphics/gdifncs_027b.htm 623 BOOL 624 APIENTRY 625 XFORMOBJ_bApplyXform( 626 IN XFORMOBJ *pxo, 627 IN ULONG iMode, 628 IN ULONG cPoints, 629 IN PVOID pvIn, 630 OUT PVOID pvOut) 631 { 632 MATRIX mx; 633 XFORMOBJ xoInv; 634 PPOINTL pptlIn, pptlOut; 635 INT i; 636 637 /* Check parameters */ 638 if (!pxo || !pvIn || !pvOut || cPoints < 1) 639 { 640 return FALSE; 641 } 642 643 /* Use inverse xform? */ 644 if (iMode == XF_INV_FXTOL || iMode == XF_INV_LTOL) 645 { 646 XFORMOBJ_vInit(&xoInv, &mx); 647 if (XFORMOBJ_iInverse(&xoInv, pxo) == DDI_ERROR) 648 { 649 return FALSE; 650 } 651 pxo = &xoInv; 652 } 653 654 /* Convert POINTL to POINTFIX? */ 655 if (iMode == XF_LTOFX || iMode == XF_LTOL || iMode == XF_INV_LTOL) 656 { 657 pptlIn = pvIn; 658 pptlOut = pvOut; 659 for (i = cPoints - 1; i >= 0; i--) 660 { 661 pptlOut[i].x = LONG2FIX(pptlIn[i].x); 662 pptlOut[i].y = LONG2FIX(pptlIn[i].y); 663 } 664 665 /* The input is in the out buffer now! */ 666 pvIn = pvOut; 667 } 668 669 /* Do the actual fixpoint transformation */ 670 if (!XFORMOBJ_bXformFixPoints(pxo, cPoints, pvIn, pvOut)) 671 { 672 return FALSE; 673 } 674 675 /* Convert POINTFIX to POINTL? */ 676 if (iMode == XF_INV_FXTOL || iMode == XF_INV_LTOL || iMode == XF_LTOL) 677 { 678 pptlOut = pvOut; 679 for (i = cPoints - 1; i >= 0; i--) 680 { 681 pptlOut[i].x = FIX2LONG(pptlOut[i].x); 682 pptlOut[i].y = FIX2LONG(pptlOut[i].y); 683 } 684 } 685 686 return TRUE; 687 } 688 689 /* EOF */ 690