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