xref: /reactos/win32ss/gdi/ntgdi/line.c (revision 9d3c3a75)
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 */
730