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