[WIN32SS][FONT] Fix font metrics (#713)
[reactos.git] / win32ss / gdi / ntgdi / coord.c
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 */