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