[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / win32ss / gdi / ntgdi / line.c
1 /*
2 * PROJECT: ReactOS Win32k Subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: win32ss/gdi/ntgdi/line.c
5 * PURPOSE: Line functions
6 * PROGRAMMERS: ...
7 */
8
9 #include <win32k.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14 // Some code from the WINE project source (www.winehq.com)
15
16 VOID FASTCALL
17 AddPenLinesBounds(PDC dc, int count, POINT *points)
18 {
19 DWORD join, endcap;
20 RECTL bounds, rect;
21 LONG lWidth;
22 PBRUSH pbrLine;
23
24 /* Get BRUSH from current pen. */
25 pbrLine = dc->dclevel.pbrLine;
26 ASSERT(pbrLine);
27
28 lWidth = 0;
29
30 // Setup bounds
31 bounds.left = bounds.top = INT_MAX;
32 bounds.right = bounds.bottom = INT_MIN;
33
34 if (((pbrLine->ulPenStyle & PS_TYPE_MASK) & PS_GEOMETRIC) || pbrLine->lWidth > 1)
35 {
36 /* Windows uses some heuristics to estimate the distance from the point that will be painted */
37 lWidth = pbrLine->lWidth + 2;
38 endcap = (PS_ENDCAP_MASK & pbrLine->ulPenStyle);
39 join = (PS_JOIN_MASK & pbrLine->ulPenStyle);
40 if (join == PS_JOIN_MITER)
41 {
42 lWidth *= 5;
43 if (endcap == PS_ENDCAP_SQUARE) lWidth = (lWidth * 3 + 1) / 2;
44 }
45 else
46 {
47 if (endcap == PS_ENDCAP_SQUARE) lWidth -= lWidth / 4;
48 else lWidth = (lWidth + 1) / 2;
49 }
50 }
51
52 while (count-- > 0)
53 {
54 rect.left = points->x - lWidth;
55 rect.top = points->y - lWidth;
56 rect.right = points->x + lWidth + 1;
57 rect.bottom = points->y + lWidth + 1;
58 RECTL_bUnionRect(&bounds, &bounds, &rect);
59 points++;
60 }
61
62 DPRINT("APLB dc %p l %d t %d\n",dc,rect.left,rect.top);
63 DPRINT(" r %d b %d\n",rect.right,rect.bottom);
64
65 {
66 RECTL rcRgn = dc->erclClip; // Use the clip box for now.
67
68 if (RECTL_bIntersectRect( &rcRgn, &rcRgn, &bounds ))
69 IntUpdateBoundsRect(dc, &rcRgn);
70 else
71 IntUpdateBoundsRect(dc, &bounds);
72 }
73 }
74
75 // Should use Fx in Point
76 //
77 BOOL FASTCALL
78 IntGdiMoveToEx(DC *dc,
79 int X,
80 int Y,
81 LPPOINT Point)
82 {
83 PDC_ATTR pdcattr = dc->pdcattr;
84 if ( Point )
85 {
86 if ( pdcattr->ulDirty_ & DIRTY_PTLCURRENT ) // Double hit!
87 {
88 Point->x = pdcattr->ptfxCurrent.x; // ret prev before change.
89 Point->y = pdcattr->ptfxCurrent.y;
90 IntDPtoLP ( dc, Point, 1); // Reconvert back.
91 }
92 else
93 {
94 Point->x = pdcattr->ptlCurrent.x;
95 Point->y = pdcattr->ptlCurrent.y;
96 }
97 }
98 pdcattr->ptlCurrent.x = X;
99 pdcattr->ptlCurrent.y = Y;
100 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
101 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
102 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
103
104 return TRUE;
105 }
106
107 BOOL FASTCALL
108 GreMoveTo( HDC hdc,
109 INT x,
110 INT y,
111 LPPOINT pptOut)
112 {
113 BOOL Ret;
114 PDC dc;
115 if (!(dc = DC_LockDc(hdc)))
116 {
117 EngSetLastError(ERROR_INVALID_HANDLE);
118 return FALSE;
119 }
120 Ret = IntGdiMoveToEx(dc, x, y, pptOut);
121 DC_UnlockDc(dc);
122 return Ret;
123 }
124
125 // Should use Fx in pt
126 //
127 VOID FASTCALL
128 IntGetCurrentPositionEx(PDC dc, LPPOINT pt)
129 {
130 PDC_ATTR pdcattr = dc->pdcattr;
131
132 if ( pt )
133 {
134 if (pdcattr->ulDirty_ & DIRTY_PTFXCURRENT)
135 {
136 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
137 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
138 pdcattr->ulDirty_ &= ~(DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
139 }
140 pt->x = pdcattr->ptlCurrent.x;
141 pt->y = pdcattr->ptlCurrent.y;
142 }
143 }
144
145 BOOL FASTCALL
146 IntGdiLineTo(DC *dc,
147 int XEnd,
148 int YEnd)
149 {
150 SURFACE *psurf;
151 BOOL Ret = TRUE;
152 PBRUSH pbrLine;
153 RECTL Bounds;
154 POINT Points[2];
155 PDC_ATTR pdcattr;
156 PPATH pPath;
157
158 ASSERT_DC_PREPARED(dc);
159
160 pdcattr = dc->pdcattr;
161
162 if (PATH_IsPathOpen(dc->dclevel))
163 {
164 Ret = PATH_LineTo(dc, XEnd, YEnd);
165 }
166 else
167 {
168 psurf = dc->dclevel.pSurface;
169 if (NULL == psurf)
170 {
171 EngSetLastError(ERROR_INVALID_HANDLE);
172 return FALSE;
173 }
174
175 Points[0].x = pdcattr->ptlCurrent.x;
176 Points[0].y = pdcattr->ptlCurrent.y;
177 Points[1].x = XEnd;
178 Points[1].y = YEnd;
179
180 IntLPtoDP(dc, Points, 2);
181
182 /* The DCOrg is in device coordinates */
183 Points[0].x += dc->ptlDCOrig.x;
184 Points[0].y += dc->ptlDCOrig.y;
185 Points[1].x += dc->ptlDCOrig.x;
186 Points[1].y += dc->ptlDCOrig.y;
187
188 Bounds.left = min(Points[0].x, Points[1].x);
189 Bounds.top = min(Points[0].y, Points[1].y);
190 Bounds.right = max(Points[0].x, Points[1].x);
191 Bounds.bottom = max(Points[0].y, Points[1].y);
192
193 /* Get BRUSH from current pen. */
194 pbrLine = dc->dclevel.pbrLine;
195 ASSERT(pbrLine);
196
197 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
198 {
199 DPRINT("Bounds dc %p l %d t %d\n",dc,Bounds.left,Bounds.top);
200 DPRINT(" r %d b %d\n",Bounds.right,Bounds.bottom);
201 AddPenLinesBounds(dc, 2, Points);
202 }
203
204 if (!(pbrLine->flAttrs & BR_IS_NULL))
205 {
206 if (IntIsEffectiveWidePen(pbrLine))
207 {
208 /* Clear the path */
209 PATH_Delete(dc->dclevel.hPath);
210 dc->dclevel.hPath = NULL;
211
212 /* Begin a path */
213 pPath = PATH_CreatePath(2);
214 dc->dclevel.flPath |= DCPATH_ACTIVE;
215 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
216 IntGetCurrentPositionEx(dc, &pPath->pos);
217 IntLPtoDP(dc, &pPath->pos, 1);
218
219 PATH_MoveTo(dc, pPath);
220 PATH_LineTo(dc, XEnd, YEnd);
221
222 /* Close the path */
223 pPath->state = PATH_Closed;
224 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
225
226 /* Actually stroke a path */
227 Ret = PATH_StrokePath(dc, pPath);
228
229 /* Clear the path */
230 PATH_UnlockPath(pPath);
231 PATH_Delete(dc->dclevel.hPath);
232 dc->dclevel.hPath = NULL;
233 }
234 else
235 {
236 Ret = IntEngLineTo(&psurf->SurfObj,
237 (CLIPOBJ *)&dc->co,
238 &dc->eboLine.BrushObject,
239 Points[0].x, Points[0].y,
240 Points[1].x, Points[1].y,
241 &Bounds,
242 ROP2_TO_MIX(pdcattr->jROP2));
243 }
244 }
245 }
246
247 if (Ret)
248 {
249 pdcattr->ptlCurrent.x = XEnd;
250 pdcattr->ptlCurrent.y = YEnd;
251 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
252 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
253 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
254 }
255
256 return Ret;
257 }
258
259 BOOL FASTCALL
260 IntGdiPolyBezier(DC *dc,
261 LPPOINT pt,
262 DWORD Count)
263 {
264 BOOL ret = FALSE; // Default to FAILURE
265
266 if ( PATH_IsPathOpen(dc->dclevel) )
267 {
268 return PATH_PolyBezier ( dc, pt, Count );
269 }
270
271 /* We'll convert it into line segments and draw them using Polyline */
272 {
273 POINT *Pts;
274 INT nOut;
275
276 Pts = GDI_Bezier ( pt, Count, &nOut );
277 if ( Pts )
278 {
279 ret = IntGdiPolyline(dc, Pts, nOut);
280 ExFreePoolWithTag(Pts, TAG_BEZIER);
281 }
282 }
283
284 return ret;
285 }
286
287 BOOL FASTCALL
288 IntGdiPolyBezierTo(DC *dc,
289 LPPOINT pt,
290 DWORD Count)
291 {
292 BOOL ret = FALSE; // Default to failure
293 PDC_ATTR pdcattr = dc->pdcattr;
294
295 if ( PATH_IsPathOpen(dc->dclevel) )
296 ret = PATH_PolyBezierTo ( dc, pt, Count );
297 else /* We'll do it using PolyBezier */
298 {
299 POINT *npt;
300 npt = ExAllocatePoolWithTag(PagedPool,
301 sizeof(POINT) * (Count + 1),
302 TAG_BEZIER);
303 if ( npt )
304 {
305 npt[0].x = pdcattr->ptlCurrent.x;
306 npt[0].y = pdcattr->ptlCurrent.y;
307 memcpy(npt + 1, pt, sizeof(POINT) * Count);
308 ret = IntGdiPolyBezier(dc, npt, Count+1);
309 ExFreePoolWithTag(npt, TAG_BEZIER);
310 }
311 }
312 if ( ret )
313 {
314 pdcattr->ptlCurrent.x = pt[Count-1].x;
315 pdcattr->ptlCurrent.y = pt[Count-1].y;
316 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
317 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
318 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
319 }
320
321 return ret;
322 }
323
324 BOOL FASTCALL
325 IntGdiPolyline(DC *dc,
326 LPPOINT pt,
327 int Count)
328 {
329 SURFACE *psurf;
330 BRUSH *pbrLine;
331 LPPOINT Points;
332 BOOL Ret = TRUE;
333 LONG i;
334 PDC_ATTR pdcattr = dc->pdcattr;
335 PPATH pPath;
336
337 if (!dc->dclevel.pSurface)
338 {
339 return TRUE;
340 }
341
342 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
343 psurf = dc->dclevel.pSurface;
344
345 /* Get BRUSHOBJ from current pen. */
346 pbrLine = dc->dclevel.pbrLine;
347 ASSERT(pbrLine);
348
349 if (!(pbrLine->flAttrs & BR_IS_NULL))
350 {
351 Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP);
352 if (Points != NULL)
353 {
354 RtlCopyMemory(Points, pt, Count * sizeof(POINT));
355 IntLPtoDP(dc, Points, Count);
356
357 /* Offset the array of points by the DC origin */
358 for (i = 0; i < Count; i++)
359 {
360 Points[i].x += dc->ptlDCOrig.x;
361 Points[i].y += dc->ptlDCOrig.y;
362 }
363
364 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
365 {
366 AddPenLinesBounds(dc, Count, Points);
367 }
368
369 if (IntIsEffectiveWidePen(pbrLine))
370 {
371 /* Clear the path */
372 PATH_Delete(dc->dclevel.hPath);
373 dc->dclevel.hPath = NULL;
374
375 /* Begin a path */
376 pPath = PATH_CreatePath(Count);
377 dc->dclevel.flPath |= DCPATH_ACTIVE;
378 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
379 pPath->pos = pt[0];
380 IntLPtoDP(dc, &pPath->pos, 1);
381
382 PATH_MoveTo(dc, pPath);
383 for (i = 1; i < Count; ++i)
384 {
385 PATH_LineTo(dc, pt[i].x, pt[i].y);
386 }
387
388 /* Close the path */
389 pPath->state = PATH_Closed;
390 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
391
392 /* Actually stroke a path */
393 Ret = PATH_StrokePath(dc, pPath);
394
395 /* Clear the path */
396 PATH_UnlockPath(pPath);
397 PATH_Delete(dc->dclevel.hPath);
398 dc->dclevel.hPath = NULL;
399 }
400 else
401 {
402 Ret = IntEngPolyline(&psurf->SurfObj,
403 (CLIPOBJ *)&dc->co,
404 &dc->eboLine.BrushObject,
405 Points,
406 Count,
407 ROP2_TO_MIX(pdcattr->jROP2));
408 }
409 EngFreeMem(Points);
410 }
411 else
412 {
413 Ret = FALSE;
414 }
415 }
416
417 DC_vFinishBlit(dc, NULL);
418
419 return Ret;
420 }
421
422 BOOL FASTCALL
423 IntGdiPolylineTo(DC *dc,
424 LPPOINT pt,
425 DWORD Count)
426 {
427 BOOL ret = FALSE; // Default to failure
428 PDC_ATTR pdcattr = dc->pdcattr;
429
430 if (PATH_IsPathOpen(dc->dclevel))
431 {
432 ret = PATH_PolylineTo(dc, pt, Count);
433 }
434 else /* Do it using Polyline */
435 {
436 POINT *pts = ExAllocatePoolWithTag(PagedPool,
437 sizeof(POINT) * (Count + 1),
438 TAG_SHAPE);
439 if ( pts )
440 {
441 pts[0].x = pdcattr->ptlCurrent.x;
442 pts[0].y = pdcattr->ptlCurrent.y;
443 memcpy( pts + 1, pt, sizeof(POINT) * Count);
444 ret = IntGdiPolyline(dc, pts, Count + 1);
445 ExFreePoolWithTag(pts, TAG_SHAPE);
446 }
447 }
448 if ( ret )
449 {
450 pdcattr->ptlCurrent.x = pt[Count-1].x;
451 pdcattr->ptlCurrent.y = pt[Count-1].y;
452 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
453 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
454 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
455 }
456
457 return ret;
458 }
459
460
461 BOOL FASTCALL
462 IntGdiPolyPolyline(DC *dc,
463 LPPOINT pt,
464 PULONG PolyPoints,
465 DWORD Count)
466 {
467 ULONG i;
468 LPPOINT pts;
469 PULONG pc;
470 BOOL ret = FALSE; // Default to failure
471 pts = pt;
472 pc = PolyPoints;
473
474 if (PATH_IsPathOpen(dc->dclevel))
475 {
476 return PATH_PolyPolyline( dc, pt, PolyPoints, Count );
477 }
478 for (i = 0; i < Count; i++)
479 {
480 ret = IntGdiPolyline ( dc, pts, *pc );
481 if (ret == FALSE)
482 {
483 return ret;
484 }
485 pts+=*pc++;
486 }
487
488 return ret;
489 }
490
491 /******************************************************************************/
492
493 BOOL
494 APIENTRY
495 NtGdiLineTo(HDC hDC,
496 int XEnd,
497 int YEnd)
498 {
499 DC *dc;
500 BOOL Ret;
501 RECT rcLockRect ;
502
503 dc = DC_LockDc(hDC);
504 if (!dc)
505 {
506 EngSetLastError(ERROR_INVALID_HANDLE);
507 return FALSE;
508 }
509
510 rcLockRect.left = dc->pdcattr->ptlCurrent.x;
511 rcLockRect.top = dc->pdcattr->ptlCurrent.y;
512 rcLockRect.right = XEnd;
513 rcLockRect.bottom = YEnd;
514
515 IntLPtoDP(dc, (PPOINT)&rcLockRect, 2);
516
517 /* The DCOrg is in device coordinates */
518 rcLockRect.left += dc->ptlDCOrig.x;
519 rcLockRect.top += dc->ptlDCOrig.y;
520 rcLockRect.right += dc->ptlDCOrig.x;
521 rcLockRect.bottom += dc->ptlDCOrig.y;
522
523 DC_vPrepareDCsForBlit(dc, &rcLockRect, NULL, NULL);
524
525 Ret = IntGdiLineTo(dc, XEnd, YEnd);
526
527 DC_vFinishBlit(dc, NULL);
528
529 DC_UnlockDc(dc);
530 return Ret;
531 }
532
533 // FIXME: This function is completely broken
534 BOOL
535 APIENTRY
536 NtGdiPolyDraw(
537 IN HDC hdc,
538 IN LPPOINT lppt,
539 IN LPBYTE lpbTypes,
540 IN ULONG cCount)
541 {
542 PDC dc;
543 PDC_ATTR pdcattr;
544 POINT bzr[4];
545 volatile PPOINT line_pts, line_pts_old, bzr_pts;
546 INT num_pts, num_bzr_pts, space, space_old, size;
547 ULONG i;
548 BOOL result = FALSE;
549
550 dc = DC_LockDc(hdc);
551 if (!dc) return FALSE;
552 pdcattr = dc->pdcattr;
553
554 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
555 DC_vUpdateFillBrush(dc);
556
557 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
558 DC_vUpdateLineBrush(dc);
559
560 if (!cCount)
561 {
562 DC_UnlockDc(dc);
563 return TRUE;
564 }
565
566 line_pts = NULL;
567 line_pts_old = NULL;
568 bzr_pts = NULL;
569
570 _SEH2_TRY
571 {
572 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
573 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
574
575 if (PATH_IsPathOpen(dc->dclevel))
576 {
577 result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount);
578 _SEH2_LEAVE;
579 }
580
581 /* Check for valid point types */
582 for (i = 0; i < cCount; i++)
583 {
584 switch (lpbTypes[i])
585 {
586 case PT_MOVETO:
587 case PT_LINETO | PT_CLOSEFIGURE:
588 case PT_LINETO:
589 break;
590 case PT_BEZIERTO:
591 if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
592 ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO))
593 {
594 i += 2;
595 break;
596 }
597 default:
598 _SEH2_LEAVE;
599 }
600 }
601
602 space = cCount + 300;
603 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
604 if (line_pts == NULL)
605 {
606 result = FALSE;
607 _SEH2_LEAVE;
608 }
609
610 num_pts = 1;
611
612 line_pts[0].x = pdcattr->ptlCurrent.x;
613 line_pts[0].y = pdcattr->ptlCurrent.y;
614
615 for ( i = 0; i < cCount; i++ )
616 {
617 switch (lpbTypes[i])
618 {
619 case PT_MOVETO:
620 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
621 num_pts = 0;
622 line_pts[num_pts++] = lppt[i];
623 break;
624 case PT_LINETO:
625 case (PT_LINETO | PT_CLOSEFIGURE):
626 line_pts[num_pts++] = lppt[i];
627 break;
628 case PT_BEZIERTO:
629 bzr[0].x = line_pts[num_pts - 1].x;
630 bzr[0].y = line_pts[num_pts - 1].y;
631 RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) );
632
633 if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
634 {
635 size = num_pts + (cCount - i) + num_bzr_pts;
636 if (space < size)
637 {
638 space_old = space;
639 space = size * 2;
640 line_pts_old = line_pts;
641 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
642 if (!line_pts) _SEH2_LEAVE;
643 RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT));
644 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
645 line_pts_old = NULL;
646 }
647 RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
648 num_pts += num_bzr_pts - 1;
649 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
650 bzr_pts = NULL;
651 }
652 i += 2;
653 break;
654 }
655 if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
656 }
657
658 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
659 IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL );
660 result = TRUE;
661 }
662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
663 {
664 SetLastNtError(_SEH2_GetExceptionCode());
665 }
666 _SEH2_END;
667
668 if (line_pts != NULL)
669 {
670 ExFreePoolWithTag(line_pts, TAG_SHAPE);
671 }
672
673 if ((line_pts_old != NULL) && (line_pts_old != line_pts))
674 {
675 ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
676 }
677
678 if (bzr_pts != NULL)
679 {
680 ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
681 }
682
683 DC_UnlockDc(dc);
684
685 return result;
686 }
687
688 /*
689 * @implemented
690 */
691 _Success_(return != FALSE)
692 BOOL
693 APIENTRY
694 NtGdiMoveTo(
695 IN HDC hdc,
696 IN INT x,
697 IN INT y,
698 OUT OPTIONAL LPPOINT pptOut)
699 {
700 PDC pdc;
701 BOOL Ret;
702 POINT Point;
703
704 pdc = DC_LockDc(hdc);
705 if (!pdc) return FALSE;
706
707 Ret = IntGdiMoveToEx(pdc, x, y, &Point);
708
709 if (Ret && pptOut)
710 {
711 _SEH2_TRY
712 {
713 ProbeForWrite(pptOut, sizeof(POINT), 1);
714 RtlCopyMemory(pptOut, &Point, sizeof(POINT));
715 }
716 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
717 {
718 SetLastNtError(_SEH2_GetExceptionCode());
719 Ret = FALSE; // CHECKME: is this correct?
720 }
721 _SEH2_END;
722 }
723
724 DC_UnlockDc(pdc);
725
726 return Ret;
727 }
728
729 /* EOF */