xref: /reactos/win32ss/gdi/ntgdi/xformobj.c (revision 02e84521)
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