xref: /reactos/win32ss/gdi/ntgdi/line.c (revision 50cf16b3)
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 = pbrLine->lWidth;
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) || lWidth > 1)
35     {
36         /* Windows uses some heuristics to estimate the distance from the point that will be painted */
37         lWidth = 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;
67        if (dc->fs & DC_FLAG_DIRTY_RAO) CLIPPING_UpdateGCRegion(dc);
68        if (REGION_GetRgnBox(dc->prgnRao, &rcRgn))
69        {
70           if (RECTL_bIntersectRect( &rcRgn, &rcRgn, &bounds )) IntUpdateBoundsRect(dc, &rcRgn);
71        }
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 = dc->pdcattr;
158     ASSERT_DC_PREPARED(dc);
159 
160     if (PATH_IsPathOpen(dc->dclevel))
161     {
162         Ret = PATH_LineTo(dc, XEnd, YEnd);
163     }
164     else
165     {
166         psurf = dc->dclevel.pSurface;
167         if (NULL == psurf)
168         {
169             EngSetLastError(ERROR_INVALID_HANDLE);
170             return FALSE;
171         }
172 
173         Points[0].x = pdcattr->ptlCurrent.x;
174         Points[0].y = pdcattr->ptlCurrent.y;
175         Points[1].x = XEnd;
176         Points[1].y = YEnd;
177 
178         IntLPtoDP(dc, Points, 2);
179 
180         /* The DCOrg is in device coordinates */
181         Points[0].x += dc->ptlDCOrig.x;
182         Points[0].y += dc->ptlDCOrig.y;
183         Points[1].x += dc->ptlDCOrig.x;
184         Points[1].y += dc->ptlDCOrig.y;
185 
186         Bounds.left = min(Points[0].x, Points[1].x);
187         Bounds.top = min(Points[0].y, Points[1].y);
188         Bounds.right = max(Points[0].x, Points[1].x);
189         Bounds.bottom = max(Points[0].y, Points[1].y);
190 
191         /* Get BRUSH from current pen. */
192         pbrLine = dc->dclevel.pbrLine;
193         ASSERT(pbrLine);
194 
195         if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
196         {
197            DPRINT("Bounds dc %p l %d t %d\n",dc,Bounds.left,Bounds.top);
198            DPRINT("                   r %d b %d\n",Bounds.right,Bounds.bottom);
199            AddPenLinesBounds(dc, 2, Points);
200         }
201 
202         if (!(pbrLine->flAttrs & BR_IS_NULL))
203         {
204             Ret = IntEngLineTo(&psurf->SurfObj,
205                                (CLIPOBJ *)&dc->co,
206                                &dc->eboLine.BrushObject,
207                                Points[0].x, Points[0].y,
208                                Points[1].x, Points[1].y,
209                                &Bounds,
210                                ROP2_TO_MIX(pdcattr->jROP2));
211         }
212 
213     }
214 
215     if (Ret)
216     {
217         pdcattr->ptlCurrent.x = XEnd;
218         pdcattr->ptlCurrent.y = YEnd;
219         pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
220         CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
221         pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
222     }
223 
224     return Ret;
225 }
226 
227 BOOL FASTCALL
228 IntGdiPolyBezier(DC      *dc,
229                  LPPOINT pt,
230                  DWORD   Count)
231 {
232     BOOL ret = FALSE; // Default to FAILURE
233 
234     if ( PATH_IsPathOpen(dc->dclevel) )
235     {
236         return PATH_PolyBezier ( dc, pt, Count );
237     }
238 
239     /* We'll convert it into line segments and draw them using Polyline */
240     {
241         POINT *Pts;
242         INT nOut;
243 
244         Pts = GDI_Bezier ( pt, Count, &nOut );
245         if ( Pts )
246         {
247             ret = IntGdiPolyline(dc, Pts, nOut);
248             ExFreePoolWithTag(Pts, TAG_BEZIER);
249         }
250     }
251 
252     return ret;
253 }
254 
255 BOOL FASTCALL
256 IntGdiPolyBezierTo(DC      *dc,
257                    LPPOINT pt,
258                    DWORD  Count)
259 {
260     BOOL ret = FALSE; // Default to failure
261     PDC_ATTR pdcattr = dc->pdcattr;
262 
263     if ( PATH_IsPathOpen(dc->dclevel) )
264         ret = PATH_PolyBezierTo ( dc, pt, Count );
265     else /* We'll do it using PolyBezier */
266     {
267         POINT *npt;
268         npt = ExAllocatePoolWithTag(PagedPool,
269                                     sizeof(POINT) * (Count + 1),
270                                     TAG_BEZIER);
271         if ( npt )
272         {
273             npt[0].x = pdcattr->ptlCurrent.x;
274             npt[0].y = pdcattr->ptlCurrent.y;
275             memcpy(npt + 1, pt, sizeof(POINT) * Count);
276             ret = IntGdiPolyBezier(dc, npt, Count+1);
277             ExFreePoolWithTag(npt, TAG_BEZIER);
278         }
279     }
280     if ( ret )
281     {
282         pdcattr->ptlCurrent.x = pt[Count-1].x;
283         pdcattr->ptlCurrent.y = pt[Count-1].y;
284         pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
285         CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
286         pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
287     }
288 
289     return ret;
290 }
291 
292 BOOL FASTCALL
293 IntGdiPolyline(DC      *dc,
294                LPPOINT pt,
295                int     Count)
296 {
297     SURFACE *psurf;
298     BRUSH *pbrLine;
299     LPPOINT Points;
300     BOOL Ret = TRUE;
301     LONG i;
302     PDC_ATTR pdcattr = dc->pdcattr;
303 
304     if (!dc->dclevel.pSurface)
305     {
306         return TRUE;
307     }
308 
309     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
310     psurf = dc->dclevel.pSurface;
311 
312     /* Get BRUSHOBJ from current pen. */
313     pbrLine = dc->dclevel.pbrLine;
314     ASSERT(pbrLine);
315 
316     if (!(pbrLine->flAttrs & BR_IS_NULL))
317     {
318         Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP);
319         if (Points != NULL)
320         {
321             RtlCopyMemory(Points, pt, Count * sizeof(POINT));
322             IntLPtoDP(dc, Points, Count);
323 
324             /* Offset the array of points by the DC origin */
325             for (i = 0; i < Count; i++)
326             {
327                 Points[i].x += dc->ptlDCOrig.x;
328                 Points[i].y += dc->ptlDCOrig.y;
329             }
330 
331             if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
332             {
333                AddPenLinesBounds(dc, Count, Points);
334             }
335 
336             Ret = IntEngPolyline(&psurf->SurfObj,
337                                  (CLIPOBJ *)&dc->co,
338                                  &dc->eboLine.BrushObject,
339                                  Points,
340                                  Count,
341                                  ROP2_TO_MIX(pdcattr->jROP2));
342 
343             EngFreeMem(Points);
344         }
345         else
346         {
347             Ret = FALSE;
348         }
349     }
350 
351     DC_vFinishBlit(dc, NULL);
352 
353     return Ret;
354 }
355 
356 BOOL FASTCALL
357 IntGdiPolylineTo(DC      *dc,
358                  LPPOINT pt,
359                  DWORD   Count)
360 {
361     BOOL ret = FALSE; // Default to failure
362     PDC_ATTR pdcattr = dc->pdcattr;
363 
364     if (PATH_IsPathOpen(dc->dclevel))
365     {
366         ret = PATH_PolylineTo(dc, pt, Count);
367     }
368     else /* Do it using Polyline */
369     {
370         POINT *pts = ExAllocatePoolWithTag(PagedPool,
371                                            sizeof(POINT) * (Count + 1),
372                                            TAG_SHAPE);
373         if ( pts )
374         {
375             pts[0].x = pdcattr->ptlCurrent.x;
376             pts[0].y = pdcattr->ptlCurrent.y;
377             memcpy( pts + 1, pt, sizeof(POINT) * Count);
378             ret = IntGdiPolyline(dc, pts, Count + 1);
379             ExFreePoolWithTag(pts, TAG_SHAPE);
380         }
381     }
382     if ( ret )
383     {
384         pdcattr->ptlCurrent.x = pt[Count-1].x;
385         pdcattr->ptlCurrent.y = pt[Count-1].y;
386         pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
387         CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
388         pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE);
389     }
390 
391     return ret;
392 }
393 
394 
395 BOOL FASTCALL
396 IntGdiPolyPolyline(DC      *dc,
397                    LPPOINT pt,
398                    PULONG  PolyPoints,
399                    DWORD   Count)
400 {
401     ULONG i;
402     LPPOINT pts;
403     PULONG pc;
404     BOOL ret = FALSE; // Default to failure
405     pts = pt;
406     pc = PolyPoints;
407 
408     if (PATH_IsPathOpen(dc->dclevel))
409     {
410         return PATH_PolyPolyline( dc, pt, PolyPoints, Count );
411     }
412     for (i = 0; i < Count; i++)
413     {
414         ret = IntGdiPolyline ( dc, pts, *pc );
415         if (ret == FALSE)
416         {
417             return ret;
418         }
419         pts+=*pc++;
420     }
421 
422     return ret;
423 }
424 
425 /******************************************************************************/
426 
427 BOOL
428 APIENTRY
429 NtGdiLineTo(HDC  hDC,
430             int  XEnd,
431             int  YEnd)
432 {
433     DC *dc;
434     BOOL Ret;
435     RECT rcLockRect ;
436 
437     dc = DC_LockDc(hDC);
438     if (!dc)
439     {
440         EngSetLastError(ERROR_INVALID_HANDLE);
441         return FALSE;
442     }
443 
444     rcLockRect.left = dc->pdcattr->ptlCurrent.x;
445     rcLockRect.top = dc->pdcattr->ptlCurrent.y;
446     rcLockRect.right = XEnd;
447     rcLockRect.bottom = YEnd;
448 
449     IntLPtoDP(dc, &rcLockRect, 2);
450 
451     /* The DCOrg is in device coordinates */
452     rcLockRect.left += dc->ptlDCOrig.x;
453     rcLockRect.top += dc->ptlDCOrig.y;
454     rcLockRect.right += dc->ptlDCOrig.x;
455     rcLockRect.bottom += dc->ptlDCOrig.y;
456 
457     DC_vPrepareDCsForBlit(dc, &rcLockRect, NULL, NULL);
458 
459     Ret = IntGdiLineTo(dc, XEnd, YEnd);
460 
461     DC_vFinishBlit(dc, NULL);
462 
463     DC_UnlockDc(dc);
464     return Ret;
465 }
466 
467 // FIXME: This function is completely broken
468 BOOL
469 APIENTRY
470 NtGdiPolyDraw(
471     IN HDC hdc,
472     IN LPPOINT lppt,
473     IN LPBYTE lpbTypes,
474     IN ULONG cCount)
475 {
476     PDC dc;
477     PDC_ATTR pdcattr;
478     POINT bzr[4];
479     volatile PPOINT line_pts, line_pts_old, bzr_pts;
480     INT num_pts, num_bzr_pts, space, space_old, size;
481     ULONG i;
482     BOOL result = FALSE;
483 
484     dc = DC_LockDc(hdc);
485     if (!dc) return FALSE;
486     pdcattr = dc->pdcattr;
487 
488     if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
489        DC_vUpdateFillBrush(dc);
490 
491     if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
492        DC_vUpdateLineBrush(dc);
493 
494     if (!cCount)
495     {
496        DC_UnlockDc(dc);
497        return TRUE;
498     }
499 
500     line_pts = NULL;
501     line_pts_old = NULL;
502     bzr_pts = NULL;
503 
504     _SEH2_TRY
505     {
506         ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG));
507         ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE));
508 
509         if (PATH_IsPathOpen(dc->dclevel))
510         {
511            result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount);
512            _SEH2_LEAVE;
513         }
514 
515         /* Check for valid point types */
516         for (i = 0; i < cCount; i++)
517         {
518            switch (lpbTypes[i])
519            {
520            case PT_MOVETO:
521            case PT_LINETO | PT_CLOSEFIGURE:
522            case PT_LINETO:
523                break;
524            case PT_BEZIERTO:
525                if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
526                   ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO))
527                {
528                    i += 2;
529                    break;
530                }
531            default:
532                _SEH2_LEAVE;
533            }
534         }
535 
536         space = cCount + 300;
537         line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
538         if (line_pts == NULL)
539         {
540             result = FALSE;
541             _SEH2_LEAVE;
542         }
543 
544         num_pts = 1;
545 
546         line_pts[0].x = pdcattr->ptlCurrent.x;
547         line_pts[0].y = pdcattr->ptlCurrent.y;
548 
549         for ( i = 0; i < cCount; i++ )
550         {
551            switch (lpbTypes[i])
552            {
553            case PT_MOVETO:
554                if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
555                num_pts = 0;
556                line_pts[num_pts++] = lppt[i];
557                break;
558            case PT_LINETO:
559            case (PT_LINETO | PT_CLOSEFIGURE):
560                line_pts[num_pts++] = lppt[i];
561                break;
562            case PT_BEZIERTO:
563                bzr[0].x = line_pts[num_pts - 1].x;
564                bzr[0].y = line_pts[num_pts - 1].y;
565                RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) );
566 
567                if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
568                {
569                    size = num_pts + (cCount - i) + num_bzr_pts;
570                    if (space < size)
571                    {
572                       space_old = space;
573                       space = size * 2;
574                       line_pts_old = line_pts;
575                       line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE);
576                       if (!line_pts) _SEH2_LEAVE;
577                       RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT));
578                       ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
579                       line_pts_old = NULL;
580                    }
581                    RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
582                    num_pts += num_bzr_pts - 1;
583                    ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
584                    bzr_pts = NULL;
585                }
586                i += 2;
587                break;
588            }
589            if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
590         }
591 
592         if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts );
593         IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL );
594         result = TRUE;
595     }
596     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
597     {
598         SetLastNtError(_SEH2_GetExceptionCode());
599     }
600     _SEH2_END;
601 
602     if (line_pts != NULL)
603     {
604         ExFreePoolWithTag(line_pts, TAG_SHAPE);
605     }
606 
607     if ((line_pts_old != NULL) && (line_pts_old != line_pts))
608     {
609         ExFreePoolWithTag(line_pts_old, TAG_SHAPE);
610     }
611 
612     if (bzr_pts != NULL)
613     {
614         ExFreePoolWithTag(bzr_pts, TAG_BEZIER);
615     }
616 
617     DC_UnlockDc(dc);
618 
619     return result;
620 }
621 
622 /*
623  * @implemented
624  */
625 _Success_(return != FALSE)
626 BOOL
627 APIENTRY
628 NtGdiMoveTo(
629     IN HDC hdc,
630     IN INT x,
631     IN INT y,
632     OUT OPTIONAL LPPOINT pptOut)
633 {
634     PDC pdc;
635     BOOL Ret;
636     POINT Point;
637 
638     pdc = DC_LockDc(hdc);
639     if (!pdc) return FALSE;
640 
641     Ret = IntGdiMoveToEx(pdc, x, y, &Point);
642 
643     if (Ret && pptOut)
644     {
645        _SEH2_TRY
646        {
647            ProbeForWrite(pptOut, sizeof(POINT), 1);
648            RtlCopyMemory(pptOut, &Point, sizeof(POINT));
649        }
650        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
651        {
652            SetLastNtError(_SEH2_GetExceptionCode());
653            Ret = FALSE; // CHECKME: is this correct?
654        }
655        _SEH2_END;
656     }
657 
658     DC_UnlockDc(pdc);
659 
660     return Ret;
661 }
662 
663 /* EOF */
664