gdi32/tests: Print GetLastError() in decimal with '%u'.
[wine] / dlls / gdi32 / painting.c
1 /*
2  * GDI drawing functions.
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  * Copyright 1997 Bertho A. Stultiens
6  *           1999 Huw D M Davies
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdarg.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winerror.h"
34 #include "gdi_private.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(gdi);
38
39
40 /***********************************************************************
41  *           LineTo    (GDI32.@)
42  */
43 BOOL WINAPI LineTo( HDC hdc, INT x, INT y )
44 {
45     DC * dc = DC_GetDCUpdate( hdc );
46     BOOL ret;
47
48     if(!dc) return FALSE;
49
50     if(PATH_IsPathOpen(dc->path))
51         ret = PATH_LineTo(dc, x, y);
52     else
53         ret = dc->funcs->pLineTo && dc->funcs->pLineTo(dc->physDev,x,y);
54     if(ret) {
55         dc->CursPosX = x;
56         dc->CursPosY = y;
57     }
58     GDI_ReleaseObj( hdc );
59     return ret;
60 }
61
62
63 /***********************************************************************
64  *           MoveToEx    (GDI32.@)
65  */
66 BOOL WINAPI MoveToEx( HDC hdc, INT x, INT y, LPPOINT pt )
67 {
68     BOOL ret = TRUE;
69     DC * dc = DC_GetDCPtr( hdc );
70
71     if(!dc) return FALSE;
72
73     if(pt) {
74         pt->x = dc->CursPosX;
75         pt->y = dc->CursPosY;
76     }
77     dc->CursPosX = x;
78     dc->CursPosY = y;
79
80     if(PATH_IsPathOpen(dc->path)) ret = PATH_MoveTo(dc);
81     else if (dc->funcs->pMoveTo) ret = dc->funcs->pMoveTo(dc->physDev,x,y);
82     GDI_ReleaseObj( hdc );
83     return ret;
84 }
85
86
87 /***********************************************************************
88  *           Arc    (GDI32.@)
89  */
90 BOOL WINAPI Arc( HDC hdc, INT left, INT top, INT right,
91                      INT bottom, INT xstart, INT ystart,
92                      INT xend, INT yend )
93 {
94     BOOL ret = FALSE;
95     DC * dc = DC_GetDCUpdate( hdc );
96     if (dc)
97     {
98     if(PATH_IsPathOpen(dc->path))
99             ret = PATH_Arc(dc, left, top, right, bottom, xstart, ystart, xend, yend,0);
100         else if (dc->funcs->pArc)
101             ret = dc->funcs->pArc(dc->physDev,left,top,right,bottom,xstart,ystart,xend,yend);
102         GDI_ReleaseObj( hdc );
103     }
104     return ret;
105 }
106
107 /***********************************************************************
108  *           ArcTo    (GDI32.@)
109  */
110 BOOL WINAPI ArcTo( HDC hdc,
111                      INT left,   INT top,
112                      INT right,  INT bottom,
113                      INT xstart, INT ystart,
114                      INT xend,   INT yend )
115 {
116     BOOL result;
117     DC * dc = DC_GetDCUpdate( hdc );
118     if(!dc) return FALSE;
119
120     if(dc->funcs->pArcTo)
121     {
122         result = dc->funcs->pArcTo( dc->physDev, left, top, right, bottom,
123                                   xstart, ystart, xend, yend );
124         GDI_ReleaseObj( hdc );
125         return result;
126     }
127     GDI_ReleaseObj( hdc );
128     /*
129      * Else emulate it.
130      * According to the documentation, a line is drawn from the current
131      * position to the starting point of the arc.
132      */
133     LineTo(hdc, xstart, ystart);
134     /*
135      * Then the arc is drawn.
136      */
137     result = Arc(hdc, left, top, right, bottom, xstart, ystart, xend, yend);
138     /*
139      * If no error occurred, the current position is moved to the ending
140      * point of the arc.
141      */
142     if (result) MoveToEx(hdc, xend, yend, NULL);
143     return result;
144 }
145
146
147 /***********************************************************************
148  *           Pie   (GDI32.@)
149  */
150 BOOL WINAPI Pie( HDC hdc, INT left, INT top,
151                      INT right, INT bottom, INT xstart, INT ystart,
152                      INT xend, INT yend )
153 {
154     BOOL ret = FALSE;
155     DC * dc = DC_GetDCUpdate( hdc );
156     if (!dc) return FALSE;
157
158     if(PATH_IsPathOpen(dc->path))
159         ret = PATH_Arc(dc,left,top,right,bottom,xstart,ystart,xend,yend,2);
160     else if(dc->funcs->pPie)
161         ret = dc->funcs->pPie(dc->physDev,left,top,right,bottom,xstart,ystart,xend,yend);
162
163     GDI_ReleaseObj( hdc );
164     return ret;
165 }
166
167
168 /***********************************************************************
169  *           Chord    (GDI32.@)
170  */
171 BOOL WINAPI Chord( HDC hdc, INT left, INT top,
172                        INT right, INT bottom, INT xstart, INT ystart,
173                        INT xend, INT yend )
174 {
175     BOOL ret = FALSE;
176     DC * dc = DC_GetDCUpdate( hdc );
177     if (!dc) return FALSE;
178
179     if(PATH_IsPathOpen(dc->path))
180         ret = PATH_Arc(dc,left,top,right,bottom,xstart,ystart,xend,yend,1);
181     else if(dc->funcs->pChord)
182         ret = dc->funcs->pChord(dc->physDev,left,top,right,bottom,xstart,ystart,xend,yend);
183
184     GDI_ReleaseObj( hdc );
185     return ret;
186 }
187
188
189 /***********************************************************************
190  *           Ellipse    (GDI32.@)
191  */
192 BOOL WINAPI Ellipse( HDC hdc, INT left, INT top,
193                          INT right, INT bottom )
194 {
195     BOOL ret = FALSE;
196     DC * dc = DC_GetDCUpdate( hdc );
197     if (!dc) return FALSE;
198
199     if(PATH_IsPathOpen(dc->path))
200         ret = PATH_Ellipse(dc,left,top,right,bottom);
201     else if (dc->funcs->pEllipse)
202         ret = dc->funcs->pEllipse(dc->physDev,left,top,right,bottom);
203
204     GDI_ReleaseObj( hdc );
205     return ret;
206 }
207
208
209 /***********************************************************************
210  *           Rectangle    (GDI32.@)
211  */
212 BOOL WINAPI Rectangle( HDC hdc, INT left, INT top,
213                            INT right, INT bottom )
214 {
215     BOOL ret = FALSE;
216     DC * dc = DC_GetDCUpdate( hdc );
217     if (dc)
218     {
219     if(PATH_IsPathOpen(dc->path))
220             ret = PATH_Rectangle(dc, left, top, right, bottom);
221         else if (dc->funcs->pRectangle)
222             ret = dc->funcs->pRectangle(dc->physDev,left,top,right,bottom);
223         GDI_ReleaseObj( hdc );
224     }
225     return ret;
226 }
227
228
229 /***********************************************************************
230  *           RoundRect    (GDI32.@)
231  */
232 BOOL WINAPI RoundRect( HDC hdc, INT left, INT top, INT right,
233                            INT bottom, INT ell_width, INT ell_height )
234 {
235     BOOL ret = FALSE;
236     DC *dc = DC_GetDCUpdate( hdc );
237
238     if (dc)
239     {
240         if(PATH_IsPathOpen(dc->path))
241             ret = PATH_RoundRect(dc,left,top,right,bottom,ell_width,ell_height);
242         else if (dc->funcs->pRoundRect)
243             ret = dc->funcs->pRoundRect(dc->physDev,left,top,right,bottom,ell_width,ell_height);
244         GDI_ReleaseObj( hdc );
245     }
246     return ret;
247 }
248
249 /***********************************************************************
250  *           SetPixel    (GDI32.@)
251  */
252 COLORREF WINAPI SetPixel( HDC hdc, INT x, INT y, COLORREF color )
253 {
254     COLORREF ret = 0;
255     DC * dc = DC_GetDCUpdate( hdc );
256     if (dc)
257     {
258         if (dc->funcs->pSetPixel) ret = dc->funcs->pSetPixel(dc->physDev,x,y,color);
259         GDI_ReleaseObj( hdc );
260     }
261     return ret;
262 }
263
264 /***********************************************************************
265  *           SetPixelV    (GDI32.@)
266  */
267 BOOL WINAPI SetPixelV( HDC hdc, INT x, INT y, COLORREF color )
268 {
269     BOOL ret = FALSE;
270     DC * dc = DC_GetDCUpdate( hdc );
271     if (dc)
272     {
273         if (dc->funcs->pSetPixel)
274         {
275             dc->funcs->pSetPixel(dc->physDev,x,y,color);
276             ret = TRUE;
277         }
278         GDI_ReleaseObj( hdc );
279     }
280     return ret;
281 }
282
283 /***********************************************************************
284  *           GetPixel    (GDI32.@)
285  */
286 COLORREF WINAPI GetPixel( HDC hdc, INT x, INT y )
287 {
288     COLORREF ret = CLR_INVALID;
289     DC * dc = DC_GetDCUpdate( hdc );
290
291     if (dc)
292     {
293     /* FIXME: should this be in the graphics driver? */
294         if (PtVisible( hdc, x, y ))
295         {
296             if (dc->funcs->pGetPixel) ret = dc->funcs->pGetPixel(dc->physDev,x,y);
297         }
298         GDI_ReleaseObj( hdc );
299     }
300     return ret;
301 }
302
303
304 /******************************************************************************
305  * ChoosePixelFormat [GDI32.@]
306  * Matches a pixel format to given format
307  *
308  * PARAMS
309  *    hdc  [I] Device context to search for best pixel match
310  *    ppfd [I] Pixel format for which a match is sought
311  *
312  * RETURNS
313  *    Success: Pixel format index closest to given format
314  *    Failure: 0
315  */
316 INT WINAPI ChoosePixelFormat( HDC hdc, const PIXELFORMATDESCRIPTOR* ppfd )
317 {
318     INT ret = 0;
319     DC * dc = DC_GetDCPtr( hdc );
320
321     TRACE("(%p,%p)\n",hdc,ppfd);
322
323     if (!dc) return 0;
324
325     if (!dc->funcs->pChoosePixelFormat) FIXME(" :stub\n");
326     else ret = dc->funcs->pChoosePixelFormat(dc->physDev,ppfd);
327
328     GDI_ReleaseObj( hdc );
329     return ret;
330 }
331
332
333 /******************************************************************************
334  * SetPixelFormat [GDI32.@]
335  * Sets pixel format of device context
336  *
337  * PARAMS
338  *    hdc          [I] Device context to search for best pixel match
339  *    iPixelFormat [I] Pixel format index
340  *    ppfd         [I] Pixel format for which a match is sought
341  *
342  * RETURNS
343  *    Success: TRUE
344  *    Failure: FALSE
345  */
346 BOOL WINAPI SetPixelFormat( HDC hdc, INT iPixelFormat,
347                             const PIXELFORMATDESCRIPTOR *ppfd)
348 {
349     INT bRet = FALSE;
350     DC * dc = DC_GetDCPtr( hdc );
351
352     TRACE("(%p,%d,%p)\n",hdc,iPixelFormat,ppfd);
353
354     if (!dc) return 0;
355
356     if (!dc->funcs->pSetPixelFormat) FIXME(" :stub\n");
357     else bRet = dc->funcs->pSetPixelFormat(dc->physDev,iPixelFormat,ppfd);
358
359     GDI_ReleaseObj( hdc );
360     return bRet;
361 }
362
363
364 /******************************************************************************
365  * GetPixelFormat [GDI32.@]
366  * Gets index of pixel format of DC
367  *
368  * PARAMETERS
369  *    hdc [I] Device context whose pixel format index is sought
370  *
371  * RETURNS
372  *    Success: Currently selected pixel format
373  *    Failure: 0
374  */
375 INT WINAPI GetPixelFormat( HDC hdc )
376 {
377     INT ret = 0;
378     DC * dc = DC_GetDCPtr( hdc );
379
380     TRACE("(%p)\n",hdc);
381
382     if (!dc) return 0;
383
384     if (!dc->funcs->pGetPixelFormat) FIXME(" :stub\n");
385     else ret = dc->funcs->pGetPixelFormat(dc->physDev);
386
387     GDI_ReleaseObj( hdc );
388     return ret;
389 }
390
391
392 /******************************************************************************
393  * DescribePixelFormat [GDI32.@]
394  * Gets info about pixel format from DC
395  *
396  * PARAMS
397  *    hdc          [I] Device context
398  *    iPixelFormat [I] Pixel format selector
399  *    nBytes       [I] Size of buffer
400  *    ppfd         [O] Pointer to structure to receive pixel format data
401  *
402  * RETURNS
403  *    Success: Maximum pixel format index of the device context
404  *    Failure: 0
405  */
406 INT WINAPI DescribePixelFormat( HDC hdc, INT iPixelFormat, UINT nBytes,
407                                 LPPIXELFORMATDESCRIPTOR ppfd )
408 {
409     INT ret = 0;
410     DC * dc = DC_GetDCPtr( hdc );
411
412     TRACE("(%p,%d,%d,%p): stub\n",hdc,iPixelFormat,nBytes,ppfd);
413
414     if (!dc) return 0;
415
416     if (!dc->funcs->pDescribePixelFormat)
417     {
418         FIXME(" :stub\n");
419         ppfd->nSize = nBytes;
420         ppfd->nVersion = 1;
421         ret = 3;
422     }
423     else ret = dc->funcs->pDescribePixelFormat(dc->physDev,iPixelFormat,nBytes,ppfd);
424
425     GDI_ReleaseObj( hdc );
426     return ret;
427 }
428
429
430 /******************************************************************************
431  * SwapBuffers [GDI32.@]
432  * Exchanges front and back buffers of window
433  *
434  * PARAMS
435  *    hdc [I] Device context whose buffers get swapped
436  *
437  * RETURNS
438  *    Success: TRUE
439  *    Failure: FALSE
440  */
441 BOOL WINAPI SwapBuffers( HDC hdc )
442 {
443     INT bRet = FALSE;
444     DC * dc = DC_GetDCPtr( hdc );
445
446     TRACE("(%p)\n",hdc);
447
448     if (!dc) return TRUE;
449
450     if (!dc->funcs->pSwapBuffers)
451     {
452         FIXME(" :stub\n");
453         bRet = TRUE;
454     }
455     else bRet = dc->funcs->pSwapBuffers(dc->physDev);
456
457     GDI_ReleaseObj( hdc );
458     return bRet;
459 }
460
461
462 /***********************************************************************
463  *           PaintRgn    (GDI32.@)
464  */
465 BOOL WINAPI PaintRgn( HDC hdc, HRGN hrgn )
466 {
467     BOOL ret = FALSE;
468     DC * dc = DC_GetDCUpdate( hdc );
469     if (dc)
470     {
471         if (dc->funcs->pPaintRgn) ret = dc->funcs->pPaintRgn(dc->physDev,hrgn);
472         GDI_ReleaseObj( hdc );
473     }
474     return ret;
475 }
476
477
478 /***********************************************************************
479  *           FillRgn    (GDI32.@)
480  */
481 BOOL WINAPI FillRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush )
482 {
483     BOOL retval = FALSE;
484     HBRUSH prevBrush;
485     DC * dc = DC_GetDCUpdate( hdc );
486
487     if (!dc) return FALSE;
488     if(dc->funcs->pFillRgn)
489         retval = dc->funcs->pFillRgn(dc->physDev, hrgn, hbrush);
490     else if ((prevBrush = SelectObject( hdc, hbrush )))
491     {
492     retval = PaintRgn( hdc, hrgn );
493     SelectObject( hdc, prevBrush );
494     }
495     GDI_ReleaseObj( hdc );
496     return retval;
497 }
498
499
500 /***********************************************************************
501  *           FrameRgn     (GDI32.@)
502  */
503 BOOL WINAPI FrameRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush,
504                           INT nWidth, INT nHeight )
505 {
506     BOOL ret = FALSE;
507     DC *dc = DC_GetDCUpdate( hdc );
508
509     if (!dc) return FALSE;
510     if(dc->funcs->pFrameRgn)
511         ret = dc->funcs->pFrameRgn( dc->physDev, hrgn, hbrush, nWidth, nHeight );
512     else
513     {
514         HRGN tmp = CreateRectRgn( 0, 0, 0, 0 );
515         if (tmp)
516         {
517             if (REGION_FrameRgn( tmp, hrgn, nWidth, nHeight ))
518             {
519                 FillRgn( hdc, tmp, hbrush );
520                 ret = TRUE;
521             }
522             DeleteObject( tmp );
523         }
524     }
525     GDI_ReleaseObj( hdc );
526     return ret;
527 }
528
529
530 /***********************************************************************
531  *           InvertRgn    (GDI32.@)
532  */
533 BOOL WINAPI InvertRgn( HDC hdc, HRGN hrgn )
534 {
535     HBRUSH prevBrush;
536     INT prevROP;
537     BOOL retval;
538     DC *dc = DC_GetDCUpdate( hdc );
539     if (!dc) return FALSE;
540
541     if(dc->funcs->pInvertRgn)
542         retval = dc->funcs->pInvertRgn( dc->physDev, hrgn );
543     else
544     {
545     prevBrush = SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
546     prevROP = SetROP2( hdc, R2_NOT );
547     retval = PaintRgn( hdc, hrgn );
548     SelectObject( hdc, prevBrush );
549     SetROP2( hdc, prevROP );
550     }
551     GDI_ReleaseObj( hdc );
552     return retval;
553 }
554
555
556 /**********************************************************************
557  *          Polyline   (GDI32.@)
558  */
559 BOOL WINAPI Polyline( HDC hdc, const POINT* pt, INT count )
560 {
561     BOOL ret = FALSE;
562     DC * dc = DC_GetDCUpdate( hdc );
563     if (dc)
564     {
565         if (PATH_IsPathOpen(dc->path)) ret = PATH_Polyline(dc, pt, count);
566         else if (dc->funcs->pPolyline) ret = dc->funcs->pPolyline(dc->physDev,pt,count);
567         GDI_ReleaseObj( hdc );
568     }
569     return ret;
570 }
571
572 /**********************************************************************
573  *          PolylineTo   (GDI32.@)
574  */
575 BOOL WINAPI PolylineTo( HDC hdc, const POINT* pt, DWORD cCount )
576 {
577     DC * dc = DC_GetDCUpdate( hdc );
578     BOOL ret = FALSE;
579
580     if(!dc) return FALSE;
581
582     if(PATH_IsPathOpen(dc->path))
583         ret = PATH_PolylineTo(dc, pt, cCount);
584
585     else if(dc->funcs->pPolylineTo)
586         ret = dc->funcs->pPolylineTo(dc->physDev, pt, cCount);
587
588     else { /* do it using Polyline */
589         POINT *pts = HeapAlloc( GetProcessHeap(), 0,
590                                 sizeof(POINT) * (cCount + 1) );
591         if (pts)
592         {
593         pts[0].x = dc->CursPosX;
594         pts[0].y = dc->CursPosY;
595         memcpy( pts + 1, pt, sizeof(POINT) * cCount );
596         ret = Polyline( hdc, pts, cCount + 1 );
597         HeapFree( GetProcessHeap(), 0, pts );
598     }
599     }
600     if(ret) {
601         dc->CursPosX = pt[cCount-1].x;
602         dc->CursPosY = pt[cCount-1].y;
603     }
604     GDI_ReleaseObj( hdc );
605     return ret;
606 }
607
608
609 /**********************************************************************
610  *          Polygon  (GDI32.@)
611  */
612 BOOL WINAPI Polygon( HDC hdc, const POINT* pt, INT count )
613 {
614     BOOL ret = FALSE;
615     DC * dc = DC_GetDCUpdate( hdc );
616     if (dc)
617     {
618         if (PATH_IsPathOpen(dc->path)) ret = PATH_Polygon(dc, pt, count);
619         else if (dc->funcs->pPolygon) ret = dc->funcs->pPolygon(dc->physDev,pt,count);
620         GDI_ReleaseObj( hdc );
621     }
622     return ret;
623 }
624
625
626 /**********************************************************************
627  *          PolyPolygon  (GDI32.@)
628  */
629 BOOL WINAPI PolyPolygon( HDC hdc, const POINT* pt, const INT* counts,
630                              UINT polygons )
631 {
632     BOOL ret = FALSE;
633     DC * dc = DC_GetDCUpdate( hdc );
634     if (dc)
635     {
636         if (PATH_IsPathOpen(dc->path)) ret = PATH_PolyPolygon(dc, pt, counts, polygons);
637         else if (dc->funcs->pPolyPolygon) ret = dc->funcs->pPolyPolygon(dc->physDev,pt,counts,polygons);
638         GDI_ReleaseObj( hdc );
639     }
640     return ret;
641 }
642
643 /**********************************************************************
644  *          PolyPolyline  (GDI32.@)
645  */
646 BOOL WINAPI PolyPolyline( HDC hdc, const POINT* pt, const DWORD* counts,
647                             DWORD polylines )
648 {
649     BOOL ret = FALSE;
650     DC * dc = DC_GetDCUpdate( hdc );
651     if (dc)
652     {
653         if (PATH_IsPathOpen(dc->path)) ret = PATH_PolyPolyline(dc, pt, counts, polylines);
654         else if (dc->funcs->pPolyPolyline) ret = dc->funcs->pPolyPolyline(dc->physDev,pt,counts,polylines);
655         GDI_ReleaseObj( hdc );
656     }
657     return ret;
658 }
659
660 /**********************************************************************
661  *          ExtFloodFill   (GDI32.@)
662  */
663 BOOL WINAPI ExtFloodFill( HDC hdc, INT x, INT y, COLORREF color,
664                               UINT fillType )
665 {
666     BOOL ret = FALSE;
667     DC * dc = DC_GetDCUpdate( hdc );
668     if (dc)
669     {
670         if (dc->funcs->pExtFloodFill) ret = dc->funcs->pExtFloodFill(dc->physDev,x,y,color,fillType);
671         GDI_ReleaseObj( hdc );
672     }
673     return ret;
674 }
675
676
677 /**********************************************************************
678  *          FloodFill   (GDI32.@)
679  */
680 BOOL WINAPI FloodFill( HDC hdc, INT x, INT y, COLORREF color )
681 {
682     return ExtFloodFill( hdc, x, y, color, FLOODFILLBORDER );
683 }
684
685
686 /******************************************************************************
687  * PolyBezier [GDI32.@]
688  * Draws one or more Bezier curves
689  *
690  * PARAMS
691  *    hDc     [I] Handle to device context
692  *    lppt    [I] Pointer to endpoints and control points
693  *    cPoints [I] Count of endpoints and control points
694  *
695  * RETURNS
696  *    Success: TRUE
697  *    Failure: FALSE
698  */
699 BOOL WINAPI PolyBezier( HDC hdc, const POINT* lppt, DWORD cPoints )
700 {
701     BOOL ret = FALSE;
702     DC * dc;
703
704     /* cPoints must be 3 * n + 1 (where n>=1) */
705     if (cPoints == 1 || (cPoints % 3) != 1) return FALSE;
706
707     dc = DC_GetDCUpdate( hdc );
708     if(!dc) return FALSE;
709
710     if(PATH_IsPathOpen(dc->path))
711         ret = PATH_PolyBezier(dc, lppt, cPoints);
712     else if (dc->funcs->pPolyBezier)
713         ret = dc->funcs->pPolyBezier(dc->physDev, lppt, cPoints);
714     else  /* We'll convert it into line segments and draw them using Polyline */
715     {
716         POINT *Pts;
717         INT nOut;
718
719         if ((Pts = GDI_Bezier( lppt, cPoints, &nOut )))
720         {
721             TRACE("Pts = %p, no = %d\n", Pts, nOut);
722             ret = Polyline( dc->hSelf, Pts, nOut );
723             HeapFree( GetProcessHeap(), 0, Pts );
724         }
725     }
726
727     GDI_ReleaseObj( hdc );
728     return ret;
729 }
730
731 /******************************************************************************
732  * PolyBezierTo [GDI32.@]
733  * Draws one or more Bezier curves
734  *
735  * PARAMS
736  *    hDc     [I] Handle to device context
737  *    lppt    [I] Pointer to endpoints and control points
738  *    cPoints [I] Count of endpoints and control points
739  *
740  * RETURNS
741  *    Success: TRUE
742  *    Failure: FALSE
743  */
744 BOOL WINAPI PolyBezierTo( HDC hdc, const POINT* lppt, DWORD cPoints )
745 {
746     DC * dc;
747     BOOL ret;
748
749     /* cbPoints must be 3 * n (where n>=1) */
750     if (!cPoints || (cPoints % 3) != 0) return FALSE;
751
752     dc = DC_GetDCUpdate( hdc );
753     if(!dc) return FALSE;
754
755     if(PATH_IsPathOpen(dc->path))
756         ret = PATH_PolyBezierTo(dc, lppt, cPoints);
757     else if(dc->funcs->pPolyBezierTo)
758         ret = dc->funcs->pPolyBezierTo(dc->physDev, lppt, cPoints);
759     else { /* We'll do it using PolyBezier */
760         POINT *pt;
761         pt = HeapAlloc( GetProcessHeap(), 0, sizeof(POINT) * (cPoints + 1) );
762         if(!pt) return FALSE;
763         pt[0].x = dc->CursPosX;
764         pt[0].y = dc->CursPosY;
765         memcpy(pt + 1, lppt, sizeof(POINT) * cPoints);
766         ret = PolyBezier(dc->hSelf, pt, cPoints+1);
767         HeapFree( GetProcessHeap(), 0, pt );
768     }
769     if(ret) {
770         dc->CursPosX = lppt[cPoints-1].x;
771         dc->CursPosY = lppt[cPoints-1].y;
772     }
773     GDI_ReleaseObj( hdc );
774     return ret;
775 }
776
777 /***********************************************************************
778  *      AngleArc (GDI32.@)
779  */
780 BOOL WINAPI AngleArc(HDC hdc, INT x, INT y, DWORD dwRadius, FLOAT eStartAngle, FLOAT eSweepAngle)
781 {
782     INT x1,y1,x2,y2, arcdir;
783     BOOL result;
784     DC *dc;
785
786     if( (signed int)dwRadius < 0 )
787         return FALSE;
788
789     dc = DC_GetDCUpdate( hdc );
790     if(!dc) return FALSE;
791
792     if(dc->funcs->pAngleArc)
793     {
794         result = dc->funcs->pAngleArc( dc->physDev, x, y, dwRadius, eStartAngle, eSweepAngle );
795
796         GDI_ReleaseObj( hdc );
797         return result;
798     }
799     GDI_ReleaseObj( hdc );
800
801     /* AngleArc always works counterclockwise */
802     arcdir = GetArcDirection( hdc );
803     SetArcDirection( hdc, AD_COUNTERCLOCKWISE );
804
805     x1 = x + cos(eStartAngle*M_PI/180) * dwRadius;
806     y1 = y - sin(eStartAngle*M_PI/180) * dwRadius;
807     x2 = x + cos((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius;
808     y2 = x - sin((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius;
809
810     LineTo( hdc, x1, y1 );
811     if( eSweepAngle >= 0 )
812         result = Arc( hdc, x-dwRadius, y-dwRadius, x+dwRadius, y+dwRadius,
813                       x1, y1, x2, y2 );
814     else
815         result = Arc( hdc, x-dwRadius, y-dwRadius, x+dwRadius, y+dwRadius,
816                       x2, y2, x1, y1 );
817
818     if( result ) MoveToEx( hdc, x2, y2, NULL );
819     SetArcDirection( hdc, arcdir );
820     return result;
821 }
822
823 /***********************************************************************
824  *      PolyDraw (GDI32.@)
825  */
826 BOOL WINAPI PolyDraw(HDC hdc, const POINT *lppt, const BYTE *lpbTypes,
827                        DWORD cCount)
828 {
829     DC *dc;
830     BOOL result;
831     POINT lastmove;
832     unsigned int i;
833
834     dc = DC_GetDCUpdate( hdc );
835     if(!dc) return FALSE;
836
837     if(dc->funcs->pPolyDraw)
838     {
839         result = dc->funcs->pPolyDraw( dc->physDev, lppt, lpbTypes, cCount );
840         GDI_ReleaseObj( hdc );
841         return result;
842     }
843     GDI_ReleaseObj( hdc );
844
845     /* check for each bezierto if there are two more points */
846     for( i = 0; i < cCount; i++ )
847         if( lpbTypes[i] != PT_MOVETO &&
848             lpbTypes[i] & PT_BEZIERTO )
849         {
850             if( cCount < i+3 )
851                 return FALSE;
852             else
853                 i += 2;
854         }
855
856     /* if no moveto occurs, we will close the figure here */
857     lastmove.x = dc->CursPosX;
858     lastmove.y = dc->CursPosY;
859
860     /* now let's draw */
861     for( i = 0; i < cCount; i++ )
862     {
863         if( lpbTypes[i] == PT_MOVETO )
864         {
865             MoveToEx( hdc, lppt[i].x, lppt[i].y, NULL );
866             lastmove.x = dc->CursPosX;
867             lastmove.y = dc->CursPosY;
868         }
869         else if( lpbTypes[i] & PT_LINETO )
870             LineTo( hdc, lppt[i].x, lppt[i].y );
871         else if( lpbTypes[i] & PT_BEZIERTO )
872         {
873             PolyBezierTo( hdc, &lppt[i], 3 );
874             i += 2;
875         }
876         else
877             return FALSE;
878
879         if( lpbTypes[i] & PT_CLOSEFIGURE )
880         {
881             if( PATH_IsPathOpen( dc->path ) )
882                 CloseFigure( hdc );
883             else
884                 LineTo( hdc, lastmove.x, lastmove.y );
885         }
886     }
887
888     return TRUE;
889 }
890
891
892 /**********************************************************************
893  *           LineDDA   (GDI32.@)
894  */
895 BOOL WINAPI LineDDA(INT nXStart, INT nYStart, INT nXEnd, INT nYEnd,
896                     LINEDDAPROC callback, LPARAM lParam )
897 {
898     INT xadd = 1, yadd = 1;
899     INT err,erradd;
900     INT cnt;
901     INT dx = nXEnd - nXStart;
902     INT dy = nYEnd - nYStart;
903
904     if (dx < 0)
905     {
906         dx = -dx;
907         xadd = -1;
908     }
909     if (dy < 0)
910     {
911         dy = -dy;
912         yadd = -1;
913     }
914     if (dx > dy)  /* line is "more horizontal" */
915     {
916         err = 2*dy - dx; erradd = 2*dy - 2*dx;
917         for(cnt = 0;cnt <= dx; cnt++)
918         {
919             callback(nXStart,nYStart,lParam);
920             if (err > 0)
921             {
922                 nYStart += yadd;
923                 err += erradd;
924             }
925             else err += 2*dy;
926             nXStart += xadd;
927         }
928     }
929     else   /* line is "more vertical" */
930     {
931         err = 2*dx - dy; erradd = 2*dx - 2*dy;
932         for(cnt = 0;cnt <= dy; cnt++)
933         {
934             callback(nXStart,nYStart,lParam);
935             if (err > 0)
936             {
937                 nXStart += xadd;
938                 err += erradd;
939             }
940             else err += 2*dx;
941             nYStart += yadd;
942         }
943     }
944     return TRUE;
945 }
946
947
948 /******************************************************************
949  *
950  *   *Very* simple bezier drawing code,
951  *
952  *   It uses a recursive algorithm to divide the curve in a series
953  *   of straight line segements. Not ideal but for me sufficient.
954  *   If you are in need for something better look for some incremental
955  *   algorithm.
956  *
957  *   7 July 1998 Rein Klazes
958  */
959
960  /*
961   * some macro definitions for bezier drawing
962   *
963   * to avoid truncation errors the coordinates are
964   * shifted upwards. When used in drawing they are
965   * shifted down again, including correct rounding
966   * and avoiding floating point arithmetic
967   * 4 bits should allow 27 bits coordinates which I saw
968   * somewhere in the win32 doc's
969   *
970   */
971
972 #define BEZIERSHIFTBITS 4
973 #define BEZIERSHIFTUP(x)    ((x)<<BEZIERSHIFTBITS)
974 #define BEZIERPIXEL        BEZIERSHIFTUP(1)
975 #define BEZIERSHIFTDOWN(x)  (((x)+(1<<(BEZIERSHIFTBITS-1)))>>BEZIERSHIFTBITS)
976 /* maximum depth of recursion */
977 #define BEZIERMAXDEPTH  8
978
979 /* size of array to store points on */
980 /* enough for one curve */
981 #define BEZIER_INITBUFSIZE    (150)
982
983 /* calculate Bezier average, in this case the middle
984  * correctly rounded...
985  * */
986
987 #define BEZIERMIDDLE(Mid, P1, P2) \
988     (Mid).x=((P1).x+(P2).x + 1)/2;\
989     (Mid).y=((P1).y+(P2).y + 1)/2;
990
991 /**********************************************************
992 * BezierCheck helper function to check
993 * that recursion can be terminated
994 *       Points[0] and Points[3] are begin and endpoint
995 *       Points[1] and Points[2] are control points
996 *       level is the recursion depth
997 *       returns true if the recusion can be terminated
998 */
999 static BOOL BezierCheck( int level, POINT *Points)
1000 {
1001     INT dx, dy;
1002     dx=Points[3].x-Points[0].x;
1003     dy=Points[3].y-Points[0].y;
1004     if(abs(dy)<=abs(dx)){/* shallow line */
1005         /* check that control points are between begin and end */
1006         if(Points[1].x < Points[0].x){
1007             if(Points[1].x < Points[3].x)
1008                 return FALSE;
1009         }else
1010             if(Points[1].x > Points[3].x)
1011                 return FALSE;
1012         if(Points[2].x < Points[0].x){
1013             if(Points[2].x < Points[3].x)
1014                 return FALSE;
1015         }else
1016             if(Points[2].x > Points[3].x)
1017                 return FALSE;
1018         dx=BEZIERSHIFTDOWN(dx);
1019         if(!dx) return TRUE;
1020         if(abs(Points[1].y-Points[0].y-(dy/dx)*
1021                 BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL ||
1022            abs(Points[2].y-Points[0].y-(dy/dx)*
1023                    BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL )
1024             return FALSE;
1025         else
1026             return TRUE;
1027     }else{ /* steep line */
1028         /* check that control points are between begin and end */
1029         if(Points[1].y < Points[0].y){
1030             if(Points[1].y < Points[3].y)
1031                 return FALSE;
1032         }else
1033             if(Points[1].y > Points[3].y)
1034                 return FALSE;
1035         if(Points[2].y < Points[0].y){
1036             if(Points[2].y < Points[3].y)
1037                 return FALSE;
1038         }else
1039             if(Points[2].y > Points[3].y)
1040                 return FALSE;
1041         dy=BEZIERSHIFTDOWN(dy);
1042         if(!dy) return TRUE;
1043         if(abs(Points[1].x-Points[0].x-(dx/dy)*
1044                 BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL ||
1045            abs(Points[2].x-Points[0].x-(dx/dy)*
1046                    BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL )
1047             return FALSE;
1048         else
1049             return TRUE;
1050     }
1051 }
1052
1053 /* Helper for GDI_Bezier.
1054  * Just handles one Bezier, so Points should point to four POINTs
1055  */
1056 static void GDI_InternalBezier( POINT *Points, POINT **PtsOut, INT *dwOut,
1057                                 INT *nPtsOut, INT level )
1058 {
1059     if(*nPtsOut == *dwOut) {
1060         *dwOut *= 2;
1061         *PtsOut = HeapReAlloc( GetProcessHeap(), 0, *PtsOut,
1062                                *dwOut * sizeof(POINT) );
1063     }
1064
1065     if(!level || BezierCheck(level, Points)) {
1066         if(*nPtsOut == 0) {
1067             (*PtsOut)[0].x = BEZIERSHIFTDOWN(Points[0].x);
1068             (*PtsOut)[0].y = BEZIERSHIFTDOWN(Points[0].y);
1069             *nPtsOut = 1;
1070         }
1071         (*PtsOut)[*nPtsOut].x = BEZIERSHIFTDOWN(Points[3].x);
1072         (*PtsOut)[*nPtsOut].y = BEZIERSHIFTDOWN(Points[3].y);
1073         (*nPtsOut) ++;
1074     } else {
1075         POINT Points2[4]; /* for the second recursive call */
1076         Points2[3]=Points[3];
1077         BEZIERMIDDLE(Points2[2], Points[2], Points[3]);
1078         BEZIERMIDDLE(Points2[0], Points[1], Points[2]);
1079         BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]);
1080
1081         BEZIERMIDDLE(Points[1], Points[0],  Points[1]);
1082         BEZIERMIDDLE(Points[2], Points[1], Points2[0]);
1083         BEZIERMIDDLE(Points[3], Points[2], Points2[1]);
1084
1085         Points2[0]=Points[3];
1086
1087         /* do the two halves */
1088         GDI_InternalBezier(Points, PtsOut, dwOut, nPtsOut, level-1);
1089         GDI_InternalBezier(Points2, PtsOut, dwOut, nPtsOut, level-1);
1090     }
1091 }
1092
1093
1094
1095 /***********************************************************************
1096  *           GDI_Bezier   [INTERNAL]
1097  *   Calculate line segments that approximate -what microsoft calls- a bezier
1098  *   curve.
1099  *   The routine recursively divides the curve in two parts until a straight
1100  *   line can be drawn
1101  *
1102  *  PARAMS
1103  *
1104  *  Points  [I] Ptr to count POINTs which are the end and control points
1105  *              of the set of Bezier curves to flatten.
1106  *  count   [I] Number of Points.  Must be 3n+1.
1107  *  nPtsOut [O] Will contain no of points that have been produced (i.e. no. of
1108  *              lines+1).
1109  *
1110  *  RETURNS
1111  *
1112  *  Ptr to an array of POINTs that contain the lines that approximinate the
1113  *  Beziers.  The array is allocated on the process heap and it is the caller's
1114  *  responsibility to HeapFree it. [this is not a particularly nice interface
1115  *  but since we can't know in advance how many points will generate, the
1116  *  alternative would be to call the function twice, once to determine the size
1117  *  and a second time to do the work - I decided this was too much of a pain].
1118  */
1119 POINT *GDI_Bezier( const POINT *Points, INT count, INT *nPtsOut )
1120 {
1121     POINT *out;
1122     INT Bezier, dwOut = BEZIER_INITBUFSIZE, i;
1123
1124     if (count == 1 || (count - 1) % 3 != 0) {
1125         ERR("Invalid no. of points %d\n", count);
1126         return NULL;
1127     }
1128     *nPtsOut = 0;
1129     out = HeapAlloc( GetProcessHeap(), 0, dwOut * sizeof(POINT));
1130     for(Bezier = 0; Bezier < (count-1)/3; Bezier++) {
1131         POINT ptBuf[4];
1132         memcpy(ptBuf, Points + Bezier * 3, sizeof(POINT) * 4);
1133         for(i = 0; i < 4; i++) {
1134             ptBuf[i].x = BEZIERSHIFTUP(ptBuf[i].x);
1135             ptBuf[i].y = BEZIERSHIFTUP(ptBuf[i].y);
1136         }
1137         GDI_InternalBezier( ptBuf, &out, &dwOut, nPtsOut, BEZIERMAXDEPTH );
1138     }
1139     TRACE("Produced %d points\n", *nPtsOut);
1140     return out;
1141 }
1142
1143 /******************************************************************************
1144  *           GdiGradientFill   (GDI32.@)
1145  *
1146  *  FIXME: we don't support the Alpha channel properly
1147  */
1148 BOOL WINAPI GdiGradientFill( HDC hdc, TRIVERTEX *vert_array, ULONG nvert,
1149                           void * grad_array, ULONG ngrad, ULONG mode )
1150 {
1151   unsigned int i;
1152
1153   TRACE("vert_array:0x%08lx nvert:%d grad_array:0x%08lx ngrad:%d\n",
1154         (long)vert_array, nvert, (long)grad_array, ngrad);
1155
1156   switch(mode) 
1157     {
1158     case GRADIENT_FILL_RECT_H:
1159       for(i = 0; i < ngrad; i++) 
1160         {
1161           GRADIENT_RECT *rect = ((GRADIENT_RECT *)grad_array) + i;
1162           TRIVERTEX *v1 = vert_array + rect->UpperLeft;
1163           TRIVERTEX *v2 = vert_array + rect->LowerRight;
1164           int y1 = v1->y < v2->y ? v1->y : v2->y;
1165           int y2 = v2->y > v1->y ? v2->y : v1->y;
1166           int x, dx;
1167           if (v1->x > v2->x)
1168             {
1169               TRIVERTEX *t = v2;
1170               v2 = v1;
1171               v1 = t;
1172             }
1173           dx = v2->x - v1->x;
1174           for (x = 0; x < dx; x++)
1175             {
1176               POINT pts[2];
1177               HPEN hPen, hOldPen;
1178               
1179               hPen = CreatePen( PS_SOLID, 1, RGB(
1180                   (v1->Red   * (dx - x) + v2->Red   * x) / dx >> 8,
1181                   (v1->Green * (dx - x) + v2->Green * x) / dx >> 8,
1182                   (v1->Blue  * (dx - x) + v2->Blue  * x) / dx >> 8));
1183               hOldPen = SelectObject( hdc, hPen );
1184               pts[0].x = v1->x + x;
1185               pts[0].y = y1;
1186               pts[1].x = v1->x + x;
1187               pts[1].y = y2;
1188               Polyline( hdc, &pts[0], 2 );
1189               DeleteObject( SelectObject(hdc, hOldPen ) );
1190             }
1191         }
1192       break;
1193     case GRADIENT_FILL_RECT_V:
1194       for(i = 0; i < ngrad; i++) 
1195         {
1196           GRADIENT_RECT *rect = ((GRADIENT_RECT *)grad_array) + i;
1197           TRIVERTEX *v1 = vert_array + rect->UpperLeft;
1198           TRIVERTEX *v2 = vert_array + rect->LowerRight;
1199           int x1 = v1->x < v2->x ? v1->x : v2->x;
1200           int x2 = v2->x > v1->x ? v2->x : v1->x;
1201           int y, dy;
1202           if (v1->y > v2->y)
1203             {
1204               TRIVERTEX *t = v2;
1205               v2 = v1;
1206               v1 = t;
1207             }
1208           dy = v2->y - v1->y;
1209           for (y = 0; y < dy; y++)
1210             {
1211               POINT pts[2];
1212               HPEN hPen, hOldPen;
1213               
1214               hPen = CreatePen( PS_SOLID, 1, RGB(
1215                   (v1->Red   * (dy - y) + v2->Red   * y) / dy >> 8,
1216                   (v1->Green * (dy - y) + v2->Green * y) / dy >> 8,
1217                   (v1->Blue  * (dy - y) + v2->Blue  * y) / dy >> 8));
1218               hOldPen = SelectObject( hdc, hPen );
1219               pts[0].x = x1;
1220               pts[0].y = v1->y + y;
1221               pts[1].x = x2;
1222               pts[1].y = v1->y + y;
1223               Polyline( hdc, &pts[0], 2 );
1224               DeleteObject( SelectObject(hdc, hOldPen ) );
1225             }
1226         }
1227       break;
1228     case GRADIENT_FILL_TRIANGLE:
1229       for (i = 0; i < ngrad; i++)  
1230         {
1231           GRADIENT_TRIANGLE *tri = ((GRADIENT_TRIANGLE *)grad_array) + i;
1232           TRIVERTEX *v1 = vert_array + tri->Vertex1;
1233           TRIVERTEX *v2 = vert_array + tri->Vertex2;
1234           TRIVERTEX *v3 = vert_array + tri->Vertex3;
1235           int y, dy;
1236           
1237           if (v1->y > v2->y)
1238             { TRIVERTEX *t = v1; v1 = v2; v2 = t; }
1239           if (v2->y > v3->y)
1240             {
1241               TRIVERTEX *t = v2; v2 = v3; v3 = t;
1242               if (v1->y > v2->y)
1243                 { t = v1; v1 = v2; v2 = t; }
1244             }
1245           /* v1->y <= v2->y <= v3->y */
1246
1247           dy = v3->y - v1->y;
1248           for (y = 0; y < dy; y++)
1249             {
1250               /* v1->y <= y < v3->y */
1251               TRIVERTEX *v = y < (v2->y - v1->y) ? v1 : v3;
1252               /* (v->y <= y < v2->y) || (v2->y <= y < v->y) */
1253               int dy2 = v2->y - v->y;
1254               int y2 = y + v1->y - v->y;
1255
1256               int x1 = (v3->x     * y  + v1->x     * (dy  - y )) / dy;
1257               int x2 = (v2->x     * y2 + v-> x     * (dy2 - y2)) / dy2;
1258               int r1 = (v3->Red   * y  + v1->Red   * (dy  - y )) / dy;
1259               int r2 = (v2->Red   * y2 + v-> Red   * (dy2 - y2)) / dy2;
1260               int g1 = (v3->Green * y  + v1->Green * (dy  - y )) / dy;
1261               int g2 = (v2->Green * y2 + v-> Green * (dy2 - y2)) / dy2;
1262               int b1 = (v3->Blue  * y  + v1->Blue  * (dy  - y )) / dy;
1263               int b2 = (v2->Blue  * y2 + v-> Blue  * (dy2 - y2)) / dy2;
1264                
1265               int x;
1266               if (x1 < x2)
1267                 {
1268                   int dx = x2 - x1;
1269                   for (x = 0; x < dx; x++)
1270                     SetPixel (hdc, x + x1, y + v1->y, RGB(
1271                       (r1 * (dx - x) + r2 * x) / dx >> 8,
1272                       (g1 * (dx - x) + g2 * x) / dx >> 8,
1273                       (b1 * (dx - x) + b2 * x) / dx >> 8));
1274                 }
1275               else
1276                 {
1277                   int dx = x1 - x2;
1278                   for (x = 0; x < dx; x++)
1279                     SetPixel (hdc, x + x2, y + v1->y, RGB(
1280                       (r2 * (dx - x) + r1 * x) / dx >> 8,
1281                       (g2 * (dx - x) + g1 * x) / dx >> 8,
1282                       (b2 * (dx - x) + b1 * x) / dx >> 8));
1283                 }
1284             }
1285         }
1286       break;
1287     default:
1288       return FALSE;
1289   }
1290
1291   return TRUE;
1292 }