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