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