xref: /reactos/win32ss/gdi/ntgdi/line.c (revision 402bc38b)
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
AddPenLinesBounds(PDC dc,int count,POINT * points)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
IntGdiMoveToEx(DC * dc,int X,int Y,LPPOINT Point)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
GreMoveTo(HDC hdc,INT x,INT y,LPPOINT pptOut)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
IntGetCurrentPositionEx(PDC dc,LPPOINT pt)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
IntGdiLineTo(DC * dc,int XEnd,int YEnd)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
IntGdiPolyBezier(DC * dc,LPPOINT pt,DWORD Count)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
IntGdiPolyBezierTo(DC * dc,LPPOINT pt,DWORD Count)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
IntGdiPolyline(DC * dc,LPPOINT pt,int Count)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
IntGdiPolylineTo(DC * dc,LPPOINT pt,DWORD Count)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
IntGdiPolyPolyline(DC * dc,LPPOINT pt,PULONG PolyPoints,DWORD Count)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
NtGdiLineTo(HDC hDC,int XEnd,int YEnd)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
GdiPolyDraw(IN HDC hdc,IN LPPOINT lppt,IN LPBYTE lpbTypes,IN ULONG cCount)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
NtGdiPolyDraw(_In_ HDC hdc,_In_reads_ (cpt)LPPOINT ppt,_In_reads_ (cpt)LPBYTE pjAttr,_In_ ULONG cpt)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
NtGdiMoveTo(IN HDC hdc,IN INT x,IN INT y,OUT OPTIONAL LPPOINT pptOut)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