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