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