comctl32: We can now store binary files in the repository.
[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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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         DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     DC_ReleaseDCPtr( dc );
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     /* Calculate the end point */
797     x2 = x + cos((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius;
798     y2 = y - sin((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius;
799
800     if(!PATH_IsPathOpen(dc->path) && dc->funcs->pAngleArc)
801         result = dc->funcs->pAngleArc( dc->physDev, x, y, dwRadius, eStartAngle, eSweepAngle );
802     else { /* do it using ArcTo */
803         x1 = x + cos(eStartAngle*M_PI/180) * dwRadius;
804         y1 = y - sin(eStartAngle*M_PI/180) * dwRadius;
805
806         arcdir = SetArcDirection( hdc, eSweepAngle >= 0 ? AD_COUNTERCLOCKWISE : AD_CLOCKWISE);
807         result = ArcTo( hdc, x-dwRadius, y-dwRadius, x+dwRadius, y+dwRadius,
808                         x1, y1, x2, y2 );
809         SetArcDirection( hdc, arcdir );
810     }
811     if (result) {
812         dc->CursPosX = x2;
813         dc->CursPosY = y2;
814     }
815     DC_ReleaseDCPtr( dc );
816     return result;
817 }
818
819 /***********************************************************************
820  *      PolyDraw (GDI32.@)
821  */
822 BOOL WINAPI PolyDraw(HDC hdc, const POINT *lppt, const BYTE *lpbTypes,
823                        DWORD cCount)
824 {
825     DC *dc;
826     BOOL result = FALSE;
827     POINT * line_pts = NULL, * bzr_pts = NULL, bzr[4];
828     INT i, num_pts, num_bzr_pts, space, size;
829
830     dc = DC_GetDCUpdate( hdc );
831     if(!dc) return FALSE;
832
833     if( PATH_IsPathOpen( dc->path ) )
834         result = PATH_PolyDraw(dc, lppt, lpbTypes, cCount);
835     else if(dc->funcs->pPolyDraw)
836         result = dc->funcs->pPolyDraw( dc->physDev, lppt, lpbTypes, cCount );
837     else {
838         /* check for valid point types */
839         for(i = 0; i < cCount; i++) {
840             switch(lpbTypes[i]) {
841                 case PT_MOVETO:
842                 case PT_LINETO | PT_CLOSEFIGURE:
843                 case PT_LINETO:
844                     break;
845                 case PT_BEZIERTO:
846                     if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) &&
847                         ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)){
848                         i += 2;
849                         break;
850                     }
851                 default:
852                     goto end;
853             }
854         }
855
856         space = cCount + 300;
857         line_pts = HeapAlloc(GetProcessHeap(), 0, space * sizeof(POINT));
858         num_pts = 1;
859
860         line_pts[0].x = dc->CursPosX;
861         line_pts[0].y = dc->CursPosY;
862
863         for(i = 0; i < cCount; i++) {
864             switch(lpbTypes[i]) {
865                 case PT_MOVETO:
866                     if(num_pts >= 2)
867                         Polyline(dc->hSelf, line_pts, num_pts);
868                     num_pts = 0;
869                     line_pts[num_pts++] = lppt[i];
870                     break;
871                 case PT_LINETO:
872                 case (PT_LINETO | PT_CLOSEFIGURE):
873                     line_pts[num_pts++] = lppt[i];
874                     break;
875                 case PT_BEZIERTO:
876                     bzr[0].x = line_pts[num_pts - 1].x;
877                     bzr[0].y = line_pts[num_pts - 1].y;
878                     memcpy(&bzr[1], &lppt[i], 3 * sizeof(POINT));
879
880                     bzr_pts = GDI_Bezier(bzr, 4, &num_bzr_pts);
881
882                     size = num_pts + (cCount - i) + num_bzr_pts;
883                     if(space < size){
884                         space = size * 2;
885                         line_pts = HeapReAlloc(GetProcessHeap(), 0, line_pts,
886                             space * sizeof(POINT));
887                     }
888                     memcpy(&line_pts[num_pts], &bzr_pts[1],
889                         (num_bzr_pts - 1) * sizeof(POINT));
890                     num_pts += num_bzr_pts - 1;
891                     HeapFree(GetProcessHeap(), 0, bzr_pts);
892                     i += 2;
893                     break;
894                 default:
895                     goto end;
896             }
897
898             if(lpbTypes[i] & PT_CLOSEFIGURE)
899                 line_pts[num_pts++] = line_pts[0];
900         }
901
902         if(num_pts >= 2)
903             Polyline(dc->hSelf, line_pts, num_pts);
904
905         MoveToEx(dc->hSelf, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL);
906
907         result = TRUE;
908     }
909
910 end:
911     DC_ReleaseDCPtr( dc );
912     return result;
913 }
914
915
916 /**********************************************************************
917  *           LineDDA   (GDI32.@)
918  */
919 BOOL WINAPI LineDDA(INT nXStart, INT nYStart, INT nXEnd, INT nYEnd,
920                     LINEDDAPROC callback, LPARAM lParam )
921 {
922     INT xadd = 1, yadd = 1;
923     INT err,erradd;
924     INT cnt;
925     INT dx = nXEnd - nXStart;
926     INT dy = nYEnd - nYStart;
927
928     if (dx < 0)
929     {
930         dx = -dx;
931         xadd = -1;
932     }
933     if (dy < 0)
934     {
935         dy = -dy;
936         yadd = -1;
937     }
938     if (dx > dy)  /* line is "more horizontal" */
939     {
940         err = 2*dy - dx; erradd = 2*dy - 2*dx;
941         for(cnt = 0;cnt <= dx; cnt++)
942         {
943             callback(nXStart,nYStart,lParam);
944             if (err > 0)
945             {
946                 nYStart += yadd;
947                 err += erradd;
948             }
949             else err += 2*dy;
950             nXStart += xadd;
951         }
952     }
953     else   /* line is "more vertical" */
954     {
955         err = 2*dx - dy; erradd = 2*dx - 2*dy;
956         for(cnt = 0;cnt <= dy; cnt++)
957         {
958             callback(nXStart,nYStart,lParam);
959             if (err > 0)
960             {
961                 nXStart += xadd;
962                 err += erradd;
963             }
964             else err += 2*dx;
965             nYStart += yadd;
966         }
967     }
968     return TRUE;
969 }
970
971
972 /******************************************************************
973  *
974  *   *Very* simple bezier drawing code,
975  *
976  *   It uses a recursive algorithm to divide the curve in a series
977  *   of straight line segements. Not ideal but for me sufficient.
978  *   If you are in need for something better look for some incremental
979  *   algorithm.
980  *
981  *   7 July 1998 Rein Klazes
982  */
983
984  /*
985   * some macro definitions for bezier drawing
986   *
987   * to avoid truncation errors the coordinates are
988   * shifted upwards. When used in drawing they are
989   * shifted down again, including correct rounding
990   * and avoiding floating point arithmetic
991   * 4 bits should allow 27 bits coordinates which I saw
992   * somewhere in the win32 doc's
993   *
994   */
995
996 #define BEZIERSHIFTBITS 4
997 #define BEZIERSHIFTUP(x)    ((x)<<BEZIERSHIFTBITS)
998 #define BEZIERPIXEL        BEZIERSHIFTUP(1)
999 #define BEZIERSHIFTDOWN(x)  (((x)+(1<<(BEZIERSHIFTBITS-1)))>>BEZIERSHIFTBITS)
1000 /* maximum depth of recursion */
1001 #define BEZIERMAXDEPTH  8
1002
1003 /* size of array to store points on */
1004 /* enough for one curve */
1005 #define BEZIER_INITBUFSIZE    (150)
1006
1007 /* calculate Bezier average, in this case the middle
1008  * correctly rounded...
1009  * */
1010
1011 #define BEZIERMIDDLE(Mid, P1, P2) \
1012     (Mid).x=((P1).x+(P2).x + 1)/2;\
1013     (Mid).y=((P1).y+(P2).y + 1)/2;
1014
1015 /**********************************************************
1016 * BezierCheck helper function to check
1017 * that recursion can be terminated
1018 *       Points[0] and Points[3] are begin and endpoint
1019 *       Points[1] and Points[2] are control points
1020 *       level is the recursion depth
1021 *       returns true if the recusion can be terminated
1022 */
1023 static BOOL BezierCheck( int level, POINT *Points)
1024 {
1025     INT dx, dy;
1026     dx=Points[3].x-Points[0].x;
1027     dy=Points[3].y-Points[0].y;
1028     if(abs(dy)<=abs(dx)){/* shallow line */
1029         /* check that control points are between begin and end */
1030         if(Points[1].x < Points[0].x){
1031             if(Points[1].x < Points[3].x)
1032                 return FALSE;
1033         }else
1034             if(Points[1].x > Points[3].x)
1035                 return FALSE;
1036         if(Points[2].x < Points[0].x){
1037             if(Points[2].x < Points[3].x)
1038                 return FALSE;
1039         }else
1040             if(Points[2].x > Points[3].x)
1041                 return FALSE;
1042         dx=BEZIERSHIFTDOWN(dx);
1043         if(!dx) return TRUE;
1044         if(abs(Points[1].y-Points[0].y-(dy/dx)*
1045                 BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL ||
1046            abs(Points[2].y-Points[0].y-(dy/dx)*
1047                    BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL )
1048             return FALSE;
1049         else
1050             return TRUE;
1051     }else{ /* steep line */
1052         /* check that control points are between begin and end */
1053         if(Points[1].y < Points[0].y){
1054             if(Points[1].y < Points[3].y)
1055                 return FALSE;
1056         }else
1057             if(Points[1].y > Points[3].y)
1058                 return FALSE;
1059         if(Points[2].y < Points[0].y){
1060             if(Points[2].y < Points[3].y)
1061                 return FALSE;
1062         }else
1063             if(Points[2].y > Points[3].y)
1064                 return FALSE;
1065         dy=BEZIERSHIFTDOWN(dy);
1066         if(!dy) return TRUE;
1067         if(abs(Points[1].x-Points[0].x-(dx/dy)*
1068                 BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL ||
1069            abs(Points[2].x-Points[0].x-(dx/dy)*
1070                    BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL )
1071             return FALSE;
1072         else
1073             return TRUE;
1074     }
1075 }
1076
1077 /* Helper for GDI_Bezier.
1078  * Just handles one Bezier, so Points should point to four POINTs
1079  */
1080 static void GDI_InternalBezier( POINT *Points, POINT **PtsOut, INT *dwOut,
1081                                 INT *nPtsOut, INT level )
1082 {
1083     if(*nPtsOut == *dwOut) {
1084         *dwOut *= 2;
1085         *PtsOut = HeapReAlloc( GetProcessHeap(), 0, *PtsOut,
1086                                *dwOut * sizeof(POINT) );
1087     }
1088
1089     if(!level || BezierCheck(level, Points)) {
1090         if(*nPtsOut == 0) {
1091             (*PtsOut)[0].x = BEZIERSHIFTDOWN(Points[0].x);
1092             (*PtsOut)[0].y = BEZIERSHIFTDOWN(Points[0].y);
1093             *nPtsOut = 1;
1094         }
1095         (*PtsOut)[*nPtsOut].x = BEZIERSHIFTDOWN(Points[3].x);
1096         (*PtsOut)[*nPtsOut].y = BEZIERSHIFTDOWN(Points[3].y);
1097         (*nPtsOut) ++;
1098     } else {
1099         POINT Points2[4]; /* for the second recursive call */
1100         Points2[3]=Points[3];
1101         BEZIERMIDDLE(Points2[2], Points[2], Points[3]);
1102         BEZIERMIDDLE(Points2[0], Points[1], Points[2]);
1103         BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]);
1104
1105         BEZIERMIDDLE(Points[1], Points[0],  Points[1]);
1106         BEZIERMIDDLE(Points[2], Points[1], Points2[0]);
1107         BEZIERMIDDLE(Points[3], Points[2], Points2[1]);
1108
1109         Points2[0]=Points[3];
1110
1111         /* do the two halves */
1112         GDI_InternalBezier(Points, PtsOut, dwOut, nPtsOut, level-1);
1113         GDI_InternalBezier(Points2, PtsOut, dwOut, nPtsOut, level-1);
1114     }
1115 }
1116
1117
1118
1119 /***********************************************************************
1120  *           GDI_Bezier   [INTERNAL]
1121  *   Calculate line segments that approximate -what microsoft calls- a bezier
1122  *   curve.
1123  *   The routine recursively divides the curve in two parts until a straight
1124  *   line can be drawn
1125  *
1126  *  PARAMS
1127  *
1128  *  Points  [I] Ptr to count POINTs which are the end and control points
1129  *              of the set of Bezier curves to flatten.
1130  *  count   [I] Number of Points.  Must be 3n+1.
1131  *  nPtsOut [O] Will contain no of points that have been produced (i.e. no. of
1132  *              lines+1).
1133  *
1134  *  RETURNS
1135  *
1136  *  Ptr to an array of POINTs that contain the lines that approximinate the
1137  *  Beziers.  The array is allocated on the process heap and it is the caller's
1138  *  responsibility to HeapFree it. [this is not a particularly nice interface
1139  *  but since we can't know in advance how many points will generate, the
1140  *  alternative would be to call the function twice, once to determine the size
1141  *  and a second time to do the work - I decided this was too much of a pain].
1142  */
1143 POINT *GDI_Bezier( const POINT *Points, INT count, INT *nPtsOut )
1144 {
1145     POINT *out;
1146     INT Bezier, dwOut = BEZIER_INITBUFSIZE, i;
1147
1148     if (count == 1 || (count - 1) % 3 != 0) {
1149         ERR("Invalid no. of points %d\n", count);
1150         return NULL;
1151     }
1152     *nPtsOut = 0;
1153     out = HeapAlloc( GetProcessHeap(), 0, dwOut * sizeof(POINT));
1154     for(Bezier = 0; Bezier < (count-1)/3; Bezier++) {
1155         POINT ptBuf[4];
1156         memcpy(ptBuf, Points + Bezier * 3, sizeof(POINT) * 4);
1157         for(i = 0; i < 4; i++) {
1158             ptBuf[i].x = BEZIERSHIFTUP(ptBuf[i].x);
1159             ptBuf[i].y = BEZIERSHIFTUP(ptBuf[i].y);
1160         }
1161         GDI_InternalBezier( ptBuf, &out, &dwOut, nPtsOut, BEZIERMAXDEPTH );
1162     }
1163     TRACE("Produced %d points\n", *nPtsOut);
1164     return out;
1165 }
1166
1167 /******************************************************************************
1168  *           GdiGradientFill   (GDI32.@)
1169  *
1170  *  FIXME: we don't support the Alpha channel properly
1171  */
1172 BOOL WINAPI GdiGradientFill( HDC hdc, TRIVERTEX *vert_array, ULONG nvert,
1173                           void * grad_array, ULONG ngrad, ULONG mode )
1174 {
1175   unsigned int i;
1176
1177   TRACE("vert_array:0x%08lx nvert:%d grad_array:0x%08lx ngrad:%d\n",
1178         (long)vert_array, nvert, (long)grad_array, ngrad);
1179
1180   switch(mode) 
1181     {
1182     case GRADIENT_FILL_RECT_H:
1183       for(i = 0; i < ngrad; i++) 
1184         {
1185           GRADIENT_RECT *rect = ((GRADIENT_RECT *)grad_array) + i;
1186           TRIVERTEX *v1 = vert_array + rect->UpperLeft;
1187           TRIVERTEX *v2 = vert_array + rect->LowerRight;
1188           int y1 = v1->y < v2->y ? v1->y : v2->y;
1189           int y2 = v2->y > v1->y ? v2->y : v1->y;
1190           int x, dx;
1191           if (v1->x > v2->x)
1192             {
1193               TRIVERTEX *t = v2;
1194               v2 = v1;
1195               v1 = t;
1196             }
1197           dx = v2->x - v1->x;
1198           for (x = 0; x < dx; x++)
1199             {
1200               POINT pts[2];
1201               HPEN hPen, hOldPen;
1202               
1203               hPen = CreatePen( PS_SOLID, 1, RGB(
1204                   (v1->Red   * (dx - x) + v2->Red   * x) / dx >> 8,
1205                   (v1->Green * (dx - x) + v2->Green * x) / dx >> 8,
1206                   (v1->Blue  * (dx - x) + v2->Blue  * x) / dx >> 8));
1207               hOldPen = SelectObject( hdc, hPen );
1208               pts[0].x = v1->x + x;
1209               pts[0].y = y1;
1210               pts[1].x = v1->x + x;
1211               pts[1].y = y2;
1212               Polyline( hdc, &pts[0], 2 );
1213               DeleteObject( SelectObject(hdc, hOldPen ) );
1214             }
1215         }
1216       break;
1217     case GRADIENT_FILL_RECT_V:
1218       for(i = 0; i < ngrad; i++) 
1219         {
1220           GRADIENT_RECT *rect = ((GRADIENT_RECT *)grad_array) + i;
1221           TRIVERTEX *v1 = vert_array + rect->UpperLeft;
1222           TRIVERTEX *v2 = vert_array + rect->LowerRight;
1223           int x1 = v1->x < v2->x ? v1->x : v2->x;
1224           int x2 = v2->x > v1->x ? v2->x : v1->x;
1225           int y, dy;
1226           if (v1->y > v2->y)
1227             {
1228               TRIVERTEX *t = v2;
1229               v2 = v1;
1230               v1 = t;
1231             }
1232           dy = v2->y - v1->y;
1233           for (y = 0; y < dy; y++)
1234             {
1235               POINT pts[2];
1236               HPEN hPen, hOldPen;
1237               
1238               hPen = CreatePen( PS_SOLID, 1, RGB(
1239                   (v1->Red   * (dy - y) + v2->Red   * y) / dy >> 8,
1240                   (v1->Green * (dy - y) + v2->Green * y) / dy >> 8,
1241                   (v1->Blue  * (dy - y) + v2->Blue  * y) / dy >> 8));
1242               hOldPen = SelectObject( hdc, hPen );
1243               pts[0].x = x1;
1244               pts[0].y = v1->y + y;
1245               pts[1].x = x2;
1246               pts[1].y = v1->y + y;
1247               Polyline( hdc, &pts[0], 2 );
1248               DeleteObject( SelectObject(hdc, hOldPen ) );
1249             }
1250         }
1251       break;
1252     case GRADIENT_FILL_TRIANGLE:
1253       for (i = 0; i < ngrad; i++)  
1254         {
1255           GRADIENT_TRIANGLE *tri = ((GRADIENT_TRIANGLE *)grad_array) + i;
1256           TRIVERTEX *v1 = vert_array + tri->Vertex1;
1257           TRIVERTEX *v2 = vert_array + tri->Vertex2;
1258           TRIVERTEX *v3 = vert_array + tri->Vertex3;
1259           int y, dy;
1260           
1261           if (v1->y > v2->y)
1262             { TRIVERTEX *t = v1; v1 = v2; v2 = t; }
1263           if (v2->y > v3->y)
1264             {
1265               TRIVERTEX *t = v2; v2 = v3; v3 = t;
1266               if (v1->y > v2->y)
1267                 { t = v1; v1 = v2; v2 = t; }
1268             }
1269           /* v1->y <= v2->y <= v3->y */
1270
1271           dy = v3->y - v1->y;
1272           for (y = 0; y < dy; y++)
1273             {
1274               /* v1->y <= y < v3->y */
1275               TRIVERTEX *v = y < (v2->y - v1->y) ? v1 : v3;
1276               /* (v->y <= y < v2->y) || (v2->y <= y < v->y) */
1277               int dy2 = v2->y - v->y;
1278               int y2 = y + v1->y - v->y;
1279
1280               int x1 = (v3->x     * y  + v1->x     * (dy  - y )) / dy;
1281               int x2 = (v2->x     * y2 + v-> x     * (dy2 - y2)) / dy2;
1282               int r1 = (v3->Red   * y  + v1->Red   * (dy  - y )) / dy;
1283               int r2 = (v2->Red   * y2 + v-> Red   * (dy2 - y2)) / dy2;
1284               int g1 = (v3->Green * y  + v1->Green * (dy  - y )) / dy;
1285               int g2 = (v2->Green * y2 + v-> Green * (dy2 - y2)) / dy2;
1286               int b1 = (v3->Blue  * y  + v1->Blue  * (dy  - y )) / dy;
1287               int b2 = (v2->Blue  * y2 + v-> Blue  * (dy2 - y2)) / dy2;
1288                
1289               int x;
1290               if (x1 < x2)
1291                 {
1292                   int dx = x2 - x1;
1293                   for (x = 0; x < dx; x++)
1294                     SetPixel (hdc, x + x1, y + v1->y, RGB(
1295                       (r1 * (dx - x) + r2 * x) / dx >> 8,
1296                       (g1 * (dx - x) + g2 * x) / dx >> 8,
1297                       (b1 * (dx - x) + b2 * x) / dx >> 8));
1298                 }
1299               else
1300                 {
1301                   int dx = x1 - x2;
1302                   for (x = 0; x < dx; x++)
1303                     SetPixel (hdc, x + x2, y + v1->y, RGB(
1304                       (r2 * (dx - x) + r1 * x) / dx >> 8,
1305                       (g2 * (dx - x) + g1 * x) / dx >> 8,
1306                       (b2 * (dx - x) + b1 * x) / dx >> 8));
1307                 }
1308             }
1309         }
1310       break;
1311     default:
1312       return FALSE;
1313   }
1314
1315   return TRUE;
1316 }