xref: /reactos/win32ss/gdi/ntgdi/coord.c (revision ba3f0743)
1 /*
2  * COPYRIGHT:        GNU GPL, See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Coordinate systems
5  * FILE:             win32ss/gdi/ntgdi/coord.c
6  * PROGRAMER:        Timo Kreuzer (timo.kreuzer@rectos.org)
7  */
8 
9 /* Coordinate translation overview
10  * -------------------------------
11  *
12  * Windows uses 3 different coordinate systems, referred to as world space,
13  * page space and device space.
14  *
15  * Device space:
16  * This is the coordinate system of the physical device that displays the
17  * graphics. One unit matches one pixel of the surface. The coordinate system
18  * is always orthogonal.
19  *
20  * Page space:
21  * This is the coordinate system on the screen or on the paper layout for
22  * printer devices. The coordinate system is also orthogonal but one unit
23  * does not necessarily match one pixel. Instead there are different mapping
24  * modes that can be set using SetMapMode() that specify how page space units
25  * are transformed into device space units. These mapping modes are:
26  * - MM_TEXT: One unit matches one unit in device space (one pixel)
27  * - MM_TWIPS One unit matches 1/20 point (1/1440 inch)
28  * - MM_LOMETRIC: One unit matches 0.1 millimeter
29  * - MM_HIMETRIC: One unit matches 0.01 millimeter
30  * - MM_LOENGLISH: One unit matches 0.01 inch
31  * - MM_HIENGLISH: One unit matches 0.001 inch
32  * - MM_ISOTROPIC:
33  * - MM_ANISOTROPIC:
34  * If the mapping mode is either MM_ISOTROPIC or MM_ANISOTROPIC, the actual
35  * transformation is calculated from the window and viewport extension.
36  * The window extension can be set using SetWindowExtEx() and describes the
37  * extents of an arbitrary window (not to confuse with the gui element!) in
38  * page space coordinates.
39  * The viewport extension can be set using SetViewportExtEx() and describes
40  * the extent of the same window in device space coordinates. If the mapping
41  * mode is MM_ISOTROPIC one of the viewport extensions can be adjusted by GDI
42  * to make sure the mapping stays isotropic, i.e. that it has the same x/y
43  * ratio as the window extension.
44  *
45  * World space:
46  * World space is the coordinate system that is used for all GDI drawing
47  * operations. The metrics of this coordinate system depend on the DCs
48  * graphics mode, which can be set using SetGraphicsMode().
49  * If the graphics mode is GM_COMPATIBLE, world space is identical to page
50  * space and no additional transformation is applied.
51  * If the graphics mode is GM_ADVANCED, an arbitrary coordinate transformation
52  * can be set using SetWorldTransform(), which is applied to transform world
53  * space coordinates into page space coordinates.
54  *
55  * User mode data:
56  * All coordinate translation data is stored in the DC attribute, so the values
57  * might be invalid. This has to be taken into account. Values might also be
58  * zero, so when a division is made, the value has to be read first and then
59  * checked! This is true for both integer and floating point values, even if
60  * we cannot get floating point exceptions on x86, we can get them on all other
61  * architectures that use the FPU directly instead of emulation.
62  * The result of all operations might be completely random and invalid, if it was
63  * messed with in an illegal way in user mode. This is not a problem, since the
64  * result of coordinate transformations are never expected to be "valid" values.
65  * In the worst case, the drawing operation draws rubbish into the DC.
66  */
67 
68 /* INCLUDES ******************************************************************/
69 
70 #include <win32k.h>
71 
72 #define NDEBUG
73 #include <debug.h>
74 C_ASSERT(sizeof(XFORML) == sizeof(XFORM));
75 
76 
77 /* GLOBALS *******************************************************************/
78 
79 const MATRIX gmxIdentity =
80 {
81     FLOATOBJ_1, FLOATOBJ_0,
82     FLOATOBJ_0, FLOATOBJ_1,
83     FLOATOBJ_0, FLOATOBJ_0,
84     0, 0, XFORM_NO_TRANSLATION|XFORM_FORMAT_LTOL|XFORM_UNITY|XFORM_SCALE
85 };
86 
87 
88 /* FUNCTIONS *****************************************************************/
89 
90 VOID
91 FASTCALL
92 DC_vFixIsotropicMapping(PDC pdc)
93 {
94     PDC_ATTR pdcattr;
95     LONG64 fx, fy;
96     LONG s;
97     SIZEL szlWindowExt, szlViewportExt;
98     ASSERT(pdc->pdcattr->iMapMode == MM_ISOTROPIC);
99 
100     /* Get a pointer to the DC_ATTR */
101     pdcattr = pdc->pdcattr;
102 
103     /* Read the extents, we rely on non-null values */
104     szlWindowExt = pdcattr->szlWindowExt;
105     szlViewportExt = pdcattr->szlViewportExt;
106 
107     /* Check if all values are valid */
108     if ((szlWindowExt.cx == 0) || (szlWindowExt.cy == 0) ||
109         (szlViewportExt.cx == 0) || (szlViewportExt.cy == 0))
110     {
111         /* Someone put rubbish into the fields, just ignore it. */
112         return;
113     }
114 
115     fx = abs((LONG64)szlWindowExt.cx * szlViewportExt.cy);
116     fy = abs((LONG64)szlWindowExt.cy * szlViewportExt.cx);
117 
118     if (fx < fy)
119     {
120         s = (szlWindowExt.cy ^ szlViewportExt.cx) > 0 ? 1 : -1;
121         pdcattr->szlViewportExt.cx = (LONG)(fx * s / szlWindowExt.cy);
122     }
123     else if (fx > fy)
124     {
125         s = (szlWindowExt.cx ^ szlViewportExt.cy) > 0 ? 1 : -1;
126         pdcattr->szlViewportExt.cy = (LONG)(fy * s / szlWindowExt.cx);
127     }
128 
129     /* Reset the flag */
130     pdc->pdcattr->flXform &= ~PAGE_EXTENTS_CHANGED;
131 }
132 
133 VOID
134 FASTCALL
135 DC_vGetPageToDevice(PDC pdc, MATRIX *pmx)
136 {
137     PDC_ATTR pdcattr = pdc->pdcattr;
138     PSIZEL pszlViewPortExt;
139     SIZEL szlWindowExt;
140 
141     /* Get the viewport extension */
142     pszlViewPortExt = DC_pszlViewportExt(pdc);
143 
144     /* Copy the window extension, so no one can mess with it */
145     szlWindowExt = pdcattr->szlWindowExt;
146 
147     /* No shearing / rotation */
148     FLOATOBJ_SetLong(&pmx->efM12, 0);
149     FLOATOBJ_SetLong(&pmx->efM21, 0);
150 
151     /* Calculate scaling */
152     if (szlWindowExt.cx != 0)
153     {
154         FLOATOBJ_SetLong(&pmx->efM11, pszlViewPortExt->cx);
155         FLOATOBJ_DivLong(&pmx->efM11, szlWindowExt.cx);
156     }
157     else
158         FLOATOBJ_SetLong(&pmx->efM11, 1);
159 
160     if (szlWindowExt.cy != 0)
161     {
162         FLOATOBJ_SetLong(&pmx->efM22, pszlViewPortExt->cy);
163         FLOATOBJ_DivLong(&pmx->efM22, szlWindowExt.cy);
164     }
165     else
166         FLOATOBJ_SetLong(&pmx->efM22, 1);
167 
168     /* Calculate x offset */
169     FLOATOBJ_SetLong(&pmx->efDx, -pdcattr->ptlWindowOrg.x);
170     FLOATOBJ_Mul(&pmx->efDx, &pmx->efM11);
171     FLOATOBJ_AddLong(&pmx->efDx, pdcattr->ptlViewportOrg.x);
172 
173     /* Calculate y offset */
174     FLOATOBJ_SetLong(&pmx->efDy, -pdcattr->ptlWindowOrg.y);
175     FLOATOBJ_Mul(&pmx->efDy, &pmx->efM22);
176     FLOATOBJ_AddLong(&pmx->efDy, pdcattr->ptlViewportOrg.y);
177 }
178 
179 VOID
180 FASTCALL
181 DC_vUpdateWorldToDevice(PDC pdc)
182 {
183     XFORMOBJ xoPageToDevice, xoWorldToPage, xoWorldToDevice;
184     MATRIX mxPageToDevice;
185 
186     // FIXME: make sure world-to-page is valid!
187 
188     /* Construct a transformation to do the page-to-device conversion */
189     DC_vGetPageToDevice(pdc, &mxPageToDevice);
190     XFORMOBJ_vInit(&xoPageToDevice, &mxPageToDevice);
191 
192     /* Recalculate the world-to-device xform */
193     XFORMOBJ_vInit(&xoWorldToPage, &pdc->pdcattr->mxWorldToPage);
194     XFORMOBJ_vInit(&xoWorldToDevice, &pdc->pdcattr->mxWorldToDevice);
195     XFORMOBJ_iCombine(&xoWorldToDevice, &xoWorldToPage, &xoPageToDevice);
196 
197     /* Reset the flags */
198     pdc->pdcattr->flXform &= ~(PAGE_XLATE_CHANGED|PAGE_EXTENTS_CHANGED|WORLD_XFORM_CHANGED);
199 }
200 
201 VOID
202 FASTCALL
203 DC_vUpdateDeviceToWorld(PDC pdc)
204 {
205     XFORMOBJ xoWorldToDevice, xoDeviceToWorld;
206     PMATRIX pmxWorldToDevice;
207 
208     /* Get the world-to-device translation */
209     pmxWorldToDevice = DC_pmxWorldToDevice(pdc);
210     XFORMOBJ_vInit(&xoWorldToDevice, pmxWorldToDevice);
211 
212     /* Create inverse of world-to-device transformation */
213     XFORMOBJ_vInit(&xoDeviceToWorld, &pdc->pdcattr->mxDeviceToWorld);
214     if (XFORMOBJ_iInverse(&xoDeviceToWorld, &xoWorldToDevice) == DDI_ERROR)
215     {
216         // FIXME: do we need to reset anything?
217         return;
218     }
219 
220     /* Reset the flag */
221     pdc->pdcattr->flXform &= ~DEVICE_TO_WORLD_INVALID;
222 }
223 
224 BOOL
225 NTAPI
226 GreCombineTransform(
227     XFORML *pxformDest,
228     XFORML *pxform1,
229     XFORML *pxform2)
230 {
231     MATRIX mxDest, mx1, mx2;
232     XFORMOBJ xoDest, xo1, xo2;
233 
234     /* Check for illegal parameters */
235     if (!pxformDest || !pxform1 || !pxform2) return FALSE;
236 
237     /* Initialize XFORMOBJs */
238     XFORMOBJ_vInit(&xoDest, &mxDest);
239     XFORMOBJ_vInit(&xo1, &mx1);
240     XFORMOBJ_vInit(&xo2, &mx2);
241 
242     /* Convert the XFORMLs into XFORMOBJs */
243     XFORMOBJ_iSetXform(&xo1, pxform1);
244     XFORMOBJ_iSetXform(&xo2, pxform2);
245 
246     /* Combine them */
247     XFORMOBJ_iCombine(&xoDest, &xo1, &xo2);
248 
249     /* Translate back into XFORML */
250     XFORMOBJ_iGetXform(&xoDest, pxformDest);
251 
252     return TRUE;
253 }
254 
255 BOOL
256 APIENTRY
257 NtGdiCombineTransform(
258     LPXFORM UnsafeXFormResult,
259     LPXFORM Unsafexform1,
260     LPXFORM Unsafexform2)
261 {
262     BOOL Ret;
263 
264     _SEH2_TRY
265     {
266         ProbeForWrite(UnsafeXFormResult, sizeof(XFORM), 1);
267         ProbeForRead(Unsafexform1, sizeof(XFORM), 1);
268         ProbeForRead(Unsafexform2, sizeof(XFORM), 1);
269         Ret = GreCombineTransform((XFORML*)UnsafeXFormResult,
270                                   (XFORML*)Unsafexform1,
271                                   (XFORML*)Unsafexform2);
272     }
273     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274     {
275         Ret = FALSE;
276     }
277     _SEH2_END;
278 
279     return Ret;
280 }
281 
282 // FIXME: Should be XFORML and use XFORMOBJ functions directly
283 BOOL
284 APIENTRY
285 NtGdiGetTransform(
286     HDC hdc,
287     DWORD iXform,
288     LPXFORM pXForm)
289 {
290     PDC pdc;
291     BOOL ret = TRUE;
292     MATRIX mxPageToDevice;
293     XFORMOBJ xo;
294     PMATRIX pmx;
295 
296     if (!pXForm)
297     {
298         EngSetLastError(ERROR_INVALID_PARAMETER);
299         return FALSE;
300     }
301 
302     pdc = DC_LockDc(hdc);
303     if (!pdc)
304     {
305         EngSetLastError(ERROR_INVALID_HANDLE);
306         return FALSE;
307     }
308 
309     switch (iXform)
310     {
311         case GdiWorldSpaceToPageSpace:
312             pmx = DC_pmxWorldToPage(pdc);
313             break;
314 
315         case GdiWorldSpaceToDeviceSpace:
316             pmx = DC_pmxWorldToDevice(pdc);
317             break;
318 
319         case GdiDeviceSpaceToWorldSpace:
320             pmx = DC_pmxDeviceToWorld(pdc);
321             break;
322 
323         case GdiPageSpaceToDeviceSpace:
324             DC_vGetPageToDevice(pdc, &mxPageToDevice);
325             pmx = &mxPageToDevice;
326             break;
327 
328         default:
329             DPRINT1("Unknown transform %lu\n", iXform);
330             ret = FALSE;
331             goto leave;
332     }
333 
334     /* Initialize an XFORMOBJ */
335     XFORMOBJ_vInit(&xo, pmx);
336 
337     _SEH2_TRY
338     {
339         ProbeForWrite(pXForm, sizeof(XFORML), 1);
340         XFORMOBJ_iGetXform(&xo, (XFORML*)pXForm);
341     }
342     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
343     {
344         ret = FALSE;
345     }
346     _SEH2_END;
347 
348 leave:
349     DC_UnlockDc(pdc);
350     return ret;
351 }
352 
353 
354 /*!
355  * Converts points from logical coordinates into device coordinates.
356  * Conversion depends on the mapping mode,
357  * world transfrom, viewport origin settings for the given device context.
358  * \param	hDC		device context.
359  * \param	Points	an array of POINT structures (in/out).
360  * \param	Count	number of elements in the array of POINT structures.
361  * \return  TRUE if success, FALSE otherwise.
362 */
363 BOOL
364 APIENTRY
365 NtGdiTransformPoints(
366     HDC hDC,
367     PPOINT UnsafePtsIn,
368     PPOINT UnsafePtOut,
369     INT Count,
370     INT iMode)
371 {
372     PDC pdc;
373     LPPOINT Points;
374     ULONG Size;
375     BOOL ret = TRUE;
376 
377     if (Count <= 0)
378         return TRUE;
379 
380     pdc = DC_LockDc(hDC);
381     if (!pdc)
382     {
383         EngSetLastError(ERROR_INVALID_PARAMETER);
384         return FALSE;
385     }
386 
387     Size = Count * sizeof(POINT);
388 
389     // FIXME: It would be wise to have a small stack buffer as optimization
390     Points = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEMP);
391     if (!Points)
392     {
393         DC_UnlockDc(pdc);
394         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
395         return FALSE;
396     }
397 
398     _SEH2_TRY
399     {
400         ProbeForWrite(UnsafePtOut, Size, 1);
401         ProbeForRead(UnsafePtsIn, Size, 1);
402         RtlCopyMemory(Points, UnsafePtsIn, Size);
403     }
404     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
405     {
406         /* Do not set last error */
407         _SEH2_YIELD(goto leave;)
408     }
409     _SEH2_END;
410 
411     switch (iMode)
412     {
413         case GdiDpToLp:
414             DC_vXformDeviceToWorld(pdc, Count, Points, Points);
415             break;
416 
417         case GdiLpToDp:
418             DC_vXformWorldToDevice(pdc, Count, Points, Points);
419             break;
420 
421         case 2: // Not supported yet. Need testing.
422         default:
423         {
424             EngSetLastError(ERROR_INVALID_PARAMETER);
425             ret = FALSE;
426             goto leave;
427         }
428     }
429 
430     _SEH2_TRY
431     {
432         /* Pointer was already probed! */
433         RtlCopyMemory(UnsafePtOut, Points, Size);
434     }
435     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
436     {
437         /* Do not set last error */
438         ret = 0;
439     }
440     _SEH2_END;
441 
442 //
443 // If we are getting called that means User XForms is a mess!
444 //
445 leave:
446     DC_UnlockDc(pdc);
447     ExFreePoolWithTag(Points, GDITAG_TEMP);
448     return ret;
449 }
450 
451 BOOL
452 NTAPI
453 GreModifyWorldTransform(
454     PDC pdc,
455     const XFORML *pxform,
456     DWORD dwMode)
457 {
458     MATRIX mxSrc;
459     XFORMOBJ xoSrc, xoDC;
460 
461     switch (dwMode)
462     {
463         case MWT_IDENTITY:
464             pdc->pdcattr->mxWorldToPage = gmxIdentity;
465             break;
466 
467         case MWT_LEFTMULTIPLY:
468             XFORMOBJ_vInit(&xoDC, &pdc->pdcattr->mxWorldToPage);
469             XFORMOBJ_vInit(&xoSrc, &mxSrc);
470             if (XFORMOBJ_iSetXform(&xoSrc, pxform) == DDI_ERROR)
471                 return FALSE;
472             XFORMOBJ_iCombine(&xoDC, &xoSrc, &xoDC);
473             break;
474 
475         case MWT_RIGHTMULTIPLY:
476             XFORMOBJ_vInit(&xoDC, &pdc->pdcattr->mxWorldToPage);
477             XFORMOBJ_vInit(&xoSrc, &mxSrc);
478             if (XFORMOBJ_iSetXform(&xoSrc, pxform) == DDI_ERROR)
479                 return FALSE;
480             XFORMOBJ_iCombine(&xoDC, &xoDC, &xoSrc);
481             break;
482 
483         case MWT_SET:
484             XFORMOBJ_vInit(&xoDC, &pdc->pdcattr->mxWorldToPage);
485             if (XFORMOBJ_iSetXform(&xoDC, pxform) == DDI_ERROR)
486                 return FALSE;
487             break;
488 
489         default:
490             return FALSE;
491     }
492 
493     /*Set invalidation flags */
494     pdc->pdcattr->flXform |= WORLD_XFORM_CHANGED|DEVICE_TO_WORLD_INVALID;
495 
496     return TRUE;
497 }
498 
499 BOOL
500 APIENTRY
501 NtGdiModifyWorldTransform(
502     HDC hdc,
503     LPXFORM pxformUnsafe,
504     DWORD dwMode)
505 {
506     PDC pdc;
507     XFORML xformSafe;
508     BOOL Ret = TRUE;
509 
510     pdc = DC_LockDc(hdc);
511     if (!pdc)
512     {
513         EngSetLastError(ERROR_INVALID_HANDLE);
514         return FALSE;
515     }
516 
517     /* The xform is permitted to be NULL for MWT_IDENTITY.
518      * However, if it is not NULL, then it must be valid even
519      * though it is not used. */
520     if ((dwMode != MWT_IDENTITY) && (pxformUnsafe == NULL))
521     {
522         DC_UnlockDc(pdc);
523         return FALSE;
524     }
525 
526     if (pxformUnsafe != NULL)
527     {
528         _SEH2_TRY
529         {
530             ProbeForRead(pxformUnsafe, sizeof(XFORML), 1);
531             RtlCopyMemory(&xformSafe, pxformUnsafe, sizeof(XFORML));
532         }
533         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
534         {
535             Ret = FALSE;
536         }
537         _SEH2_END;
538     }
539 
540     /* Safe to handle kernel mode data. */
541     if (Ret) Ret = GreModifyWorldTransform(pdc, &xformSafe, dwMode);
542     DC_UnlockDc(pdc);
543     return Ret;
544 }
545 
546 BOOL
547 APIENTRY
548 NtGdiOffsetViewportOrgEx(
549     HDC hDC,
550     int XOffset,
551     int YOffset,
552     LPPOINT UnsafePoint)
553 {
554     PDC      dc;
555     PDC_ATTR pdcattr;
556     NTSTATUS Status = STATUS_SUCCESS;
557 
558     dc = DC_LockDc(hDC);
559     if (!dc)
560     {
561         EngSetLastError(ERROR_INVALID_HANDLE);
562         return FALSE;
563     }
564     pdcattr = dc->pdcattr;
565 
566     if (UnsafePoint)
567     {
568         _SEH2_TRY
569         {
570             ProbeForWrite(UnsafePoint, sizeof(POINT), 1);
571             UnsafePoint->x = pdcattr->ptlViewportOrg.x;
572             UnsafePoint->y = pdcattr->ptlViewportOrg.y;
573             if (pdcattr->dwLayout & LAYOUT_RTL)
574             {
575                 UnsafePoint->x = -UnsafePoint->x;
576             }
577         }
578         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
579         {
580             Status = _SEH2_GetExceptionCode();
581         }
582         _SEH2_END;
583 
584         if (!NT_SUCCESS(Status))
585         {
586             SetLastNtError(Status);
587             DC_UnlockDc(dc);
588             return FALSE;
589         }
590     }
591 
592     if (pdcattr->dwLayout & LAYOUT_RTL)
593     {
594         XOffset = -XOffset;
595     }
596     pdcattr->ptlViewportOrg.x += XOffset;
597     pdcattr->ptlViewportOrg.y += YOffset;
598     pdcattr->flXform |= PAGE_XLATE_CHANGED;
599 
600     DC_UnlockDc(dc);
601 
602     return TRUE;
603 }
604 
605 BOOL
606 APIENTRY
607 NtGdiOffsetWindowOrgEx(
608     HDC hDC,
609     int XOffset,
610     int YOffset,
611     LPPOINT Point)
612 {
613     PDC dc;
614     PDC_ATTR pdcattr;
615 
616     dc = DC_LockDc(hDC);
617     if (!dc)
618     {
619         EngSetLastError(ERROR_INVALID_HANDLE);
620         return FALSE;
621     }
622     pdcattr = dc->pdcattr;
623 
624     if (Point)
625     {
626         NTSTATUS Status = STATUS_SUCCESS;
627 
628         _SEH2_TRY
629         {
630             ProbeForWrite(Point, sizeof(POINT), 1);
631             Point->x = pdcattr->ptlWindowOrg.x;
632             Point->y = pdcattr->ptlWindowOrg.y;
633         }
634         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
635         {
636             Status = _SEH2_GetExceptionCode();
637         }
638         _SEH2_END;
639 
640         if (!NT_SUCCESS(Status))
641         {
642             SetLastNtError(Status);
643             DC_UnlockDc(dc);
644             return FALSE;
645         }
646     }
647 
648     pdcattr->ptlWindowOrg.x += XOffset;
649     pdcattr->ptlWindowOrg.y += YOffset;
650     pdcattr->flXform |= PAGE_XLATE_CHANGED|DEVICE_TO_WORLD_INVALID;
651 
652     DC_UnlockDc(dc);
653 
654     return TRUE;
655 }
656 
657 BOOL
658 APIENTRY
659 NtGdiScaleViewportExtEx(
660     HDC hDC,
661     int Xnum,
662     int Xdenom,
663     int Ynum,
664     int Ydenom,
665     LPSIZE pSize)
666 {
667     PDC pDC;
668     PDC_ATTR pdcattr;
669     BOOL Ret = FALSE;
670     LONG X, Y;
671 
672     pDC = DC_LockDc(hDC);
673     if (!pDC)
674     {
675         EngSetLastError(ERROR_INVALID_HANDLE);
676         return FALSE;
677     }
678     pdcattr = pDC->pdcattr;
679 
680     if (pdcattr->iMapMode > MM_TWIPS)
681     {
682         if (Xdenom && Ydenom)
683         {
684             DC_pszlViewportExt(pDC);
685             X = Xnum * pdcattr->szlViewportExt.cx / Xdenom;
686             if (X)
687             {
688                 Y = Ynum * pdcattr->szlViewportExt.cy / Ydenom;
689                 if (Y)
690                 {
691                     pdcattr->szlViewportExt.cx = X;
692                     pdcattr->szlViewportExt.cy = Y;
693                     pdcattr->flXform |= PAGE_XLATE_CHANGED;
694 
695                     IntMirrorWindowOrg(pDC);
696 
697                     pdcattr->flXform |= (PAGE_EXTENTS_CHANGED |
698                                          INVALIDATE_ATTRIBUTES |
699                                          DEVICE_TO_WORLD_INVALID);
700 
701                     if (pdcattr->iMapMode == MM_ISOTROPIC)
702                     {
703                         DC_vFixIsotropicMapping(pDC);
704                     }
705 
706                     Ret = TRUE;
707                 }
708             }
709         }
710     }
711     else
712         Ret = TRUE;
713 
714     if (pSize)
715     {
716         _SEH2_TRY
717         {
718             ProbeForWrite(pSize, sizeof(SIZE), 1);
719 
720             pSize->cx = pdcattr->szlViewportExt.cx;
721             pSize->cy = pdcattr->szlViewportExt.cy;
722         }
723         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
724         {
725             SetLastNtError(_SEH2_GetExceptionCode());
726             Ret = FALSE;
727         }
728         _SEH2_END;
729     }
730 
731     DC_UnlockDc(pDC);
732     return Ret;
733 }
734 
735 BOOL
736 APIENTRY
737 NtGdiScaleWindowExtEx(
738     HDC hDC,
739     int Xnum,
740     int Xdenom,
741     int Ynum,
742     int Ydenom,
743     LPSIZE pSize)
744 {
745     PDC pDC;
746     PDC_ATTR pdcattr;
747     BOOL Ret = FALSE;
748     LONG X, Y;
749 
750     pDC = DC_LockDc(hDC);
751     if (!pDC)
752     {
753         EngSetLastError(ERROR_INVALID_HANDLE);
754         return FALSE;
755     }
756     pdcattr = pDC->pdcattr;
757 
758     if (pSize)
759     {
760         NTSTATUS Status = STATUS_SUCCESS;
761 
762         _SEH2_TRY
763         {
764             ProbeForWrite(pSize, sizeof(SIZE), 1);
765 
766             X = pdcattr->szlWindowExt.cx;
767             if (pdcattr->dwLayout & LAYOUT_RTL) X = -X;
768             pSize->cx = X;
769             pSize->cy = pdcattr->szlWindowExt.cy;
770         }
771         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
772         {
773             Status = _SEH2_GetExceptionCode();
774         }
775         _SEH2_END;
776 
777         if (!NT_SUCCESS(Status))
778         {
779             SetLastNtError(Status);
780             DC_UnlockDc(pDC);
781             return FALSE;
782         }
783     }
784 
785     if (pdcattr->iMapMode > MM_TWIPS)
786     {
787         if (Xdenom && Ydenom)
788         {
789             X = Xnum * pdcattr->szlWindowExt.cx / Xdenom;
790             if (X)
791             {
792                 Y = Ynum * pdcattr->szlWindowExt.cy / Ydenom;
793                 if (Y)
794                 {
795                     pdcattr->szlWindowExt.cx = X;
796                     pdcattr->szlWindowExt.cy = Y;
797 
798                     IntMirrorWindowOrg(pDC);
799 
800                     pdcattr->flXform |= (PAGE_EXTENTS_CHANGED|INVALIDATE_ATTRIBUTES|DEVICE_TO_WORLD_INVALID);
801 
802                     Ret = TRUE;
803                 }
804             }
805         }
806     }
807     else
808         Ret = TRUE;
809 
810     DC_UnlockDc(pDC);
811     return Ret;
812 }
813 
814 int
815 APIENTRY
816 IntGdiSetMapMode(
817     PDC dc,
818     int MapMode)
819 {
820     INT iPrevMapMode;
821     FLONG flXform;
822     PDC_ATTR pdcattr = dc->pdcattr;
823 
824     flXform = pdcattr->flXform & ~(ISO_OR_ANISO_MAP_MODE|PTOD_EFM22_NEGATIVE|
825         PTOD_EFM11_NEGATIVE|POSITIVE_Y_IS_UP|PAGE_TO_DEVICE_SCALE_IDENTITY|
826         PAGE_TO_DEVICE_IDENTITY);
827 
828     switch (MapMode)
829     {
830         case MM_TEXT:
831             pdcattr->szlWindowExt.cx = 1;
832             pdcattr->szlWindowExt.cy = 1;
833             pdcattr->szlViewportExt.cx = 1;
834             pdcattr->szlViewportExt.cy = 1;
835             flXform |= PAGE_TO_DEVICE_SCALE_IDENTITY;
836             break;
837 
838         case MM_ISOTROPIC:
839             flXform |= ISO_OR_ANISO_MAP_MODE;
840             /* Fall through */
841 
842         case MM_LOMETRIC:
843             pdcattr->szlWindowExt.cx = pdcattr->szlVirtualDeviceMm.cx * 10;
844             pdcattr->szlWindowExt.cy = pdcattr->szlVirtualDeviceMm.cy * 10;
845             pdcattr->szlViewportExt.cx =  pdcattr->szlVirtualDevicePixel.cx;
846             pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
847             break;
848 
849         case MM_HIMETRIC:
850             pdcattr->szlWindowExt.cx = pdcattr->szlVirtualDeviceMm.cx * 100;
851             pdcattr->szlWindowExt.cy = pdcattr->szlVirtualDeviceMm.cy * 100;
852             pdcattr->szlViewportExt.cx =  pdcattr->szlVirtualDevicePixel.cx;
853             pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
854             break;
855 
856         case MM_LOENGLISH:
857             pdcattr->szlWindowExt.cx = MulDiv(1000, pdcattr->szlVirtualDeviceMm.cx, 254);
858             pdcattr->szlWindowExt.cy = MulDiv(1000, pdcattr->szlVirtualDeviceMm.cy, 254);
859             pdcattr->szlViewportExt.cx =  pdcattr->szlVirtualDevicePixel.cx;
860             pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
861             break;
862 
863         case MM_HIENGLISH:
864             pdcattr->szlWindowExt.cx = MulDiv(10000, pdcattr->szlVirtualDeviceMm.cx, 254);
865             pdcattr->szlWindowExt.cy = MulDiv(10000, pdcattr->szlVirtualDeviceMm.cy, 254);
866             pdcattr->szlViewportExt.cx =  pdcattr->szlVirtualDevicePixel.cx;
867             pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
868             break;
869 
870         case MM_TWIPS:
871             pdcattr->szlWindowExt.cx = MulDiv(14400, pdcattr->szlVirtualDeviceMm.cx, 254);
872             pdcattr->szlWindowExt.cy = MulDiv(14400, pdcattr->szlVirtualDeviceMm.cy, 254);
873             pdcattr->szlViewportExt.cx =  pdcattr->szlVirtualDevicePixel.cx;
874             pdcattr->szlViewportExt.cy = -pdcattr->szlVirtualDevicePixel.cy;
875             break;
876 
877         case MM_ANISOTROPIC:
878             flXform &= ~(PAGE_TO_DEVICE_IDENTITY|POSITIVE_Y_IS_UP);
879             flXform |= ISO_OR_ANISO_MAP_MODE;
880             break;
881 
882         default:
883             return 0;
884     }
885 
886     /* Save the old map mode and set the new one */
887     iPrevMapMode = pdcattr->iMapMode;
888     pdcattr->iMapMode = MapMode;
889 
890     /* Update xform flags */
891     pdcattr->flXform = flXform | (PAGE_XLATE_CHANGED|PAGE_EXTENTS_CHANGED|
892         INVALIDATE_ATTRIBUTES|DEVICE_TO_PAGE_INVALID|DEVICE_TO_WORLD_INVALID);
893 
894     return iPrevMapMode;
895 }
896 
897 BOOL
898 FASTCALL
899 GreSetViewportOrgEx(
900     HDC hDC,
901     int X,
902     int Y,
903     LPPOINT Point)
904 {
905     PDC dc;
906     PDC_ATTR pdcattr;
907 
908     dc = DC_LockDc(hDC);
909     if (!dc)
910     {
911         EngSetLastError(ERROR_INVALID_HANDLE);
912         return FALSE;
913     }
914     pdcattr = dc->pdcattr;
915 
916     if (Point)
917     {
918        Point->x = pdcattr->ptlViewportOrg.x;
919        Point->y = pdcattr->ptlViewportOrg.y;
920     }
921 
922     pdcattr->ptlViewportOrg.x = X;
923     pdcattr->ptlViewportOrg.y = Y;
924     pdcattr->flXform |= PAGE_XLATE_CHANGED;
925 
926     DC_UnlockDc(dc);
927     return TRUE;
928 }
929 
930 BOOL
931 APIENTRY
932 NtGdiSetViewportOrgEx(
933     HDC hDC,
934     int X,
935     int Y,
936     LPPOINT Point)
937 {
938     PDC dc;
939     PDC_ATTR pdcattr;
940 
941     dc = DC_LockDc(hDC);
942     if (!dc)
943     {
944         EngSetLastError(ERROR_INVALID_HANDLE);
945         return FALSE;
946     }
947     pdcattr = dc->pdcattr;
948 
949     if (Point)
950     {
951         NTSTATUS Status = STATUS_SUCCESS;
952 
953         _SEH2_TRY
954         {
955             ProbeForWrite(Point, sizeof(POINT), 1);
956             Point->x = pdcattr->ptlViewportOrg.x;
957             Point->y = pdcattr->ptlViewportOrg.y;
958         }
959         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
960         {
961             Status = _SEH2_GetExceptionCode();
962         }
963         _SEH2_END;
964 
965         if (!NT_SUCCESS(Status))
966         {
967             SetLastNtError(Status);
968             DC_UnlockDc(dc);
969             return FALSE;
970         }
971     }
972 
973     pdcattr->ptlViewportOrg.x = X;
974     pdcattr->ptlViewportOrg.y = Y;
975     pdcattr->flXform |= PAGE_XLATE_CHANGED;
976 
977     DC_UnlockDc(dc);
978 
979     return TRUE;
980 }
981 
982 BOOL
983 APIENTRY
984 NtGdiSetWindowOrgEx(
985     HDC hDC,
986     int X,
987     int Y,
988     LPPOINT Point)
989 {
990     PDC dc;
991     PDC_ATTR pdcattr;
992 
993     dc = DC_LockDc(hDC);
994     if (!dc)
995     {
996         EngSetLastError(ERROR_INVALID_HANDLE);
997         return FALSE;
998     }
999     pdcattr = dc->pdcattr;
1000 
1001     if (Point)
1002     {
1003         NTSTATUS Status = STATUS_SUCCESS;
1004 
1005         _SEH2_TRY
1006         {
1007             ProbeForWrite(Point, sizeof(POINT), 1);
1008             Point->x = pdcattr->ptlWindowOrg.x;
1009             Point->y = pdcattr->ptlWindowOrg.y;
1010         }
1011         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1012         {
1013             Status = _SEH2_GetExceptionCode();
1014         }
1015         _SEH2_END;
1016 
1017         if (!NT_SUCCESS(Status))
1018         {
1019             SetLastNtError(Status);
1020             DC_UnlockDc(dc);
1021             return FALSE;
1022         }
1023     }
1024 
1025     pdcattr->ptlWindowOrg.x = X;
1026     pdcattr->ptlWindowOrg.y = Y;
1027     pdcattr->flXform |= PAGE_XLATE_CHANGED;
1028 
1029     DC_UnlockDc(dc);
1030 
1031     return TRUE;
1032 }
1033 
1034 //
1035 // Mirror Window function.
1036 //
1037 VOID
1038 FASTCALL
1039 IntMirrorWindowOrg(PDC dc)
1040 {
1041     PDC_ATTR pdcattr;
1042     LONG X, cx;
1043 
1044     pdcattr = dc->pdcattr;
1045 
1046     if (!(pdcattr->dwLayout & LAYOUT_RTL))
1047     {
1048         pdcattr->ptlWindowOrg.x = pdcattr->lWindowOrgx; // Flip it back.
1049         return;
1050     }
1051 
1052     /* Copy the window extension, so no one can mess with it */
1053     cx = pdcattr->szlViewportExt.cx;
1054     if (cx == 0) return;
1055     //
1056     // WOrgx = wox - (Width - 1) * WExtx / VExtx
1057     //
1058     X = (dc->erclWindow.right - dc->erclWindow.left) - 1; // Get device width - 1
1059 
1060     X = (X * pdcattr->szlWindowExt.cx) / cx;
1061 
1062     pdcattr->ptlWindowOrg.x = pdcattr->lWindowOrgx - X; // Now set the inverted win origion.
1063     pdcattr->flXform |= PAGE_XLATE_CHANGED;
1064 
1065     return;
1066 }
1067 
1068 VOID
1069 NTAPI
1070 DC_vSetLayout(
1071     IN PDC pdc,
1072     IN LONG wox,
1073     IN DWORD dwLayout)
1074 {
1075     PDC_ATTR pdcattr = pdc->pdcattr;
1076 
1077     pdcattr->dwLayout = dwLayout;
1078 
1079     if (!(dwLayout & LAYOUT_ORIENTATIONMASK)) return;
1080 
1081     if (dwLayout & LAYOUT_RTL)
1082     {
1083         pdcattr->iMapMode = MM_ANISOTROPIC;
1084     }
1085 
1086     //pdcattr->szlWindowExt.cy = -pdcattr->szlWindowExt.cy;
1087     //pdcattr->ptlWindowOrg.x  = -pdcattr->ptlWindowOrg.x;
1088 
1089     //if (wox == -1)
1090     //    IntMirrorWindowOrg(pdc);
1091     //else
1092     //    pdcattr->ptlWindowOrg.x = wox - pdcattr->ptlWindowOrg.x;
1093 
1094     if (!(pdcattr->flTextAlign & TA_CENTER)) pdcattr->flTextAlign |= TA_RIGHT;
1095 
1096     if (pdc->dclevel.flPath & DCPATH_CLOCKWISE)
1097         pdc->dclevel.flPath &= ~DCPATH_CLOCKWISE;
1098     else
1099         pdc->dclevel.flPath |= DCPATH_CLOCKWISE;
1100 
1101     pdcattr->flXform |= (PAGE_EXTENTS_CHANGED |
1102                          INVALIDATE_ATTRIBUTES |
1103                          DEVICE_TO_WORLD_INVALID);
1104 }
1105 
1106 // NtGdiSetLayout
1107 //
1108 // The default is left to right. This function changes it to right to left, which
1109 // is the standard in Arabic and Hebrew cultures.
1110 //
1111 /*
1112  * @implemented
1113  */
1114 DWORD
1115 APIENTRY
1116 NtGdiSetLayout(
1117     IN HDC hdc,
1118     IN LONG wox,
1119     IN DWORD dwLayout)
1120 {
1121     PDC pdc;
1122     DWORD dwOldLayout;
1123 
1124     pdc = DC_LockDc(hdc);
1125     if (!pdc)
1126     {
1127         EngSetLastError(ERROR_INVALID_HANDLE);
1128         return GDI_ERROR;
1129     }
1130 
1131     dwOldLayout = pdc->pdcattr->dwLayout;
1132     DC_vSetLayout(pdc, wox, dwLayout);
1133 
1134     DC_UnlockDc(pdc);
1135     return dwOldLayout;
1136 }
1137 
1138 /*
1139  * @implemented
1140  */
1141 LONG
1142 APIENTRY
1143 NtGdiGetDeviceWidth(
1144     IN HDC hdc)
1145 {
1146     PDC dc;
1147     LONG Ret;
1148     dc = DC_LockDc(hdc);
1149     if (!dc)
1150     {
1151         EngSetLastError(ERROR_INVALID_HANDLE);
1152         return 0;
1153     }
1154     Ret = dc->erclWindow.right - dc->erclWindow.left;
1155     DC_UnlockDc(dc);
1156     return Ret;
1157 }
1158 
1159 /*
1160  * @implemented
1161  */
1162 BOOL
1163 APIENTRY
1164 NtGdiMirrorWindowOrg(
1165     IN HDC hdc)
1166 {
1167     PDC dc;
1168     dc = DC_LockDc(hdc);
1169     if (!dc)
1170     {
1171         EngSetLastError(ERROR_INVALID_HANDLE);
1172         return FALSE;
1173     }
1174     IntMirrorWindowOrg(dc);
1175     DC_UnlockDc(dc);
1176     return TRUE;
1177 }
1178 
1179 /*
1180  * @implemented
1181  */
1182 BOOL
1183 APIENTRY
1184 NtGdiSetSizeDevice(
1185     IN HDC hdc,
1186     IN INT cxVirtualDevice,
1187     IN INT cyVirtualDevice)
1188 {
1189     PDC dc;
1190     PDC_ATTR pdcattr;
1191 
1192     if (!cxVirtualDevice || !cyVirtualDevice)
1193     {
1194         return FALSE;
1195     }
1196 
1197     dc = DC_LockDc(hdc);
1198     if (!dc) return FALSE;
1199 
1200     pdcattr = dc->pdcattr;
1201 
1202     pdcattr->szlVirtualDeviceSize.cx = cxVirtualDevice;
1203     pdcattr->szlVirtualDeviceSize.cy = cyVirtualDevice;
1204 
1205     DC_UnlockDc(dc);
1206 
1207     return TRUE;
1208 }
1209 
1210 /*
1211  * @implemented
1212  */
1213 BOOL
1214 APIENTRY
1215 NtGdiSetVirtualResolution(
1216     IN HDC hdc,
1217     IN INT cxVirtualDevicePixel,
1218     IN INT cyVirtualDevicePixel,
1219     IN INT cxVirtualDeviceMm,
1220     IN INT cyVirtualDeviceMm)
1221 {
1222     PDC dc;
1223     PDC_ATTR pdcattr;
1224 
1225     /* Check parameters (all zeroes resets to real resolution) */
1226     if (cxVirtualDevicePixel == 0 && cyVirtualDevicePixel == 0 &&
1227         cxVirtualDeviceMm == 0 && cyVirtualDeviceMm == 0)
1228     {
1229         cxVirtualDevicePixel = NtGdiGetDeviceCaps(hdc, HORZRES);
1230         cyVirtualDevicePixel = NtGdiGetDeviceCaps(hdc, VERTRES);
1231         cxVirtualDeviceMm = NtGdiGetDeviceCaps(hdc, HORZSIZE);
1232         cyVirtualDeviceMm = NtGdiGetDeviceCaps(hdc, VERTSIZE);
1233     }
1234     else if (cxVirtualDevicePixel == 0 || cyVirtualDevicePixel == 0 ||
1235              cxVirtualDeviceMm == 0 || cyVirtualDeviceMm == 0)
1236     {
1237         return FALSE;
1238     }
1239 
1240     dc = DC_LockDc(hdc);
1241     if (!dc) return FALSE;
1242 
1243     pdcattr = dc->pdcattr;
1244 
1245     pdcattr->szlVirtualDevicePixel.cx = cxVirtualDevicePixel;
1246     pdcattr->szlVirtualDevicePixel.cy = cyVirtualDevicePixel;
1247     pdcattr->szlVirtualDeviceMm.cx = cxVirtualDeviceMm;
1248     pdcattr->szlVirtualDeviceMm.cy = cyVirtualDeviceMm;
1249 
1250 //    DC_vUpdateXforms(dc);
1251     DC_UnlockDc(dc);
1252     return TRUE;
1253 }
1254 
1255 static
1256 VOID FASTCALL
1257 DC_vGetAspectRatioFilter(PDC pDC, LPSIZE AspectRatio)
1258 {
1259     if (pDC->pdcattr->flFontMapper & 1) // TRUE assume 1.
1260     {
1261         // "This specifies that Windows should only match fonts that have the
1262         // same aspect ratio as the display.", Programming Windows, Fifth Ed.
1263         AspectRatio->cx = pDC->ppdev->gdiinfo.ulLogPixelsX;
1264         AspectRatio->cy = pDC->ppdev->gdiinfo.ulLogPixelsY;
1265     }
1266     else
1267     {
1268         AspectRatio->cx = 0;
1269         AspectRatio->cy = 0;
1270     }
1271 }
1272 
1273 BOOL APIENTRY
1274 GreGetDCPoint(
1275     HDC hDC,
1276     UINT iPoint,
1277     PPOINTL Point)
1278 {
1279     BOOL Ret = TRUE;
1280     DC *pdc;
1281     SIZE Size;
1282     PSIZEL pszlViewportExt;
1283 
1284     if (!Point)
1285     {
1286         EngSetLastError(ERROR_INVALID_PARAMETER);
1287         return FALSE;
1288     }
1289 
1290     pdc = DC_LockDc(hDC);
1291     if (!pdc)
1292     {
1293         EngSetLastError(ERROR_INVALID_HANDLE);
1294         return FALSE;
1295     }
1296 
1297     switch (iPoint)
1298     {
1299         case GdiGetViewPortExt:
1300             pszlViewportExt = DC_pszlViewportExt(pdc);
1301             Point->x = pszlViewportExt->cx;
1302             Point->y = pszlViewportExt->cy;
1303             break;
1304 
1305         case GdiGetWindowExt:
1306             Point->x = pdc->pdcattr->szlWindowExt.cx;
1307             Point->y = pdc->pdcattr->szlWindowExt.cy;
1308             break;
1309 
1310         case GdiGetViewPortOrg:
1311             *Point = pdc->pdcattr->ptlViewportOrg;
1312             break;
1313 
1314         case GdiGetWindowOrg:
1315             *Point = pdc->pdcattr->ptlWindowOrg;
1316             break;
1317 
1318         case GdiGetDCOrg:
1319             *Point = pdc->ptlDCOrig;
1320             break;
1321 
1322         case GdiGetAspectRatioFilter:
1323             DC_vGetAspectRatioFilter(pdc, &Size);
1324             Point->x = Size.cx;
1325             Point->y = Size.cy;
1326             break;
1327 
1328         default:
1329             EngSetLastError(ERROR_INVALID_PARAMETER);
1330             Ret = FALSE;
1331             break;
1332     }
1333 
1334     DC_UnlockDc(pdc);
1335     return Ret;
1336 }
1337 
1338 BOOL
1339 WINAPI
1340 GreSetDCOrg(
1341     _In_ HDC hdc,
1342     _In_ LONG x,
1343     _In_ LONG y,
1344     _In_opt_ PRECTL Rect)
1345 {
1346     PDC dc;
1347 
1348     dc = DC_LockDc(hdc);
1349     if (!dc) return FALSE;
1350 
1351     /* Set DC Origin */
1352     dc->ptlDCOrig.x = x;
1353     dc->ptlDCOrig.y = y;
1354 
1355     /* Recalculate Fill Origin */
1356     dc->ptlFillOrigin.x = dc->dclevel.ptlBrushOrigin.x + x;
1357     dc->ptlFillOrigin.y = dc->dclevel.ptlBrushOrigin.y + y;
1358 
1359     /* Set DC Window Rectangle */
1360     if (Rect)
1361         dc->erclWindow = *Rect;
1362 
1363     DC_UnlockDc(dc);
1364     return TRUE;
1365 }
1366 
1367 BOOL
1368 WINAPI
1369 GreGetDCOrgEx(
1370     _In_ HDC hdc,
1371     _Out_ PPOINTL Point,
1372     _Out_ PRECTL Rect)
1373 {
1374     PDC dc;
1375 
1376     dc = DC_LockDc(hdc);
1377     if (!dc) return FALSE;
1378 
1379     /* Retrieve DC Window Rectangle without a check */
1380     *Rect = dc->erclWindow;
1381 
1382     DC_UnlockDc(dc);
1383 
1384     /* Use default call for DC Origin and parameter checking */
1385     return GreGetDCPoint( hdc, GdiGetDCOrg, Point);
1386 }
1387 
1388 BOOL
1389 WINAPI
1390 GreGetWindowExtEx(
1391     _In_ HDC hdc,
1392     _Out_ LPSIZE lpSize)
1393 {
1394     return GreGetDCPoint(hdc, GdiGetWindowExt, (PPOINTL)lpSize);
1395 }
1396 
1397 BOOL
1398 WINAPI
1399 GreGetViewportExtEx(
1400     _In_ HDC hdc,
1401     _Out_ LPSIZE lpSize)
1402 {
1403     return GreGetDCPoint(hdc, GdiGetViewPortExt, (PPOINTL)lpSize);
1404 }
1405 
1406 BOOL APIENTRY
1407 NtGdiGetDCPoint(
1408     HDC hDC,
1409     UINT iPoint,
1410     PPOINTL Point)
1411 {
1412     BOOL Ret;
1413     POINTL SafePoint;
1414 
1415     if (!Point)
1416     {
1417         EngSetLastError(ERROR_INVALID_PARAMETER);
1418         return FALSE;
1419     }
1420 
1421     Ret = GreGetDCPoint(hDC, iPoint, &SafePoint);
1422     if (Ret)
1423     {
1424         _SEH2_TRY
1425         {
1426             ProbeForWrite(Point, sizeof(POINT), 1);
1427             *Point = SafePoint;
1428         }
1429         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1430         {
1431             Ret = FALSE;
1432         }
1433         _SEH2_END;
1434     }
1435 
1436     return Ret;
1437 }
1438 
1439 /* EOF */
1440