painting.c 31.9 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
2
 * GDI drawing functions.
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 *
 * Copyright 1993, 1994 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 * Copyright 1997 Bertho A. Stultiens
6
 *           1999 Huw D M Davies
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23 24 25
#include "config.h"
#include "wine/port.h"

26
#include <stdarg.h>
27
#include <string.h>
28 29
#include <stdlib.h>

30
#include "windef.h"
31
#include "winbase.h"
32 33
#include "wingdi.h"
#include "winerror.h"
34
#include "gdi_private.h"
35
#include "wine/debug.h"
36

37
WINE_DEFAULT_DEBUG_CHANNEL(gdi);
38

Alexandre Julliard's avatar
Alexandre Julliard committed
39

40 41 42 43
/***********************************************************************
 *           null driver fallback implementations
 */

44
BOOL nulldrv_AngleArc( PHYSDEV dev, INT x, INT y, DWORD radius, FLOAT start, FLOAT sweep )
45 46 47 48 49 50 51 52 53 54 55
{
    INT x1 = GDI_ROUND( x + cos( start * M_PI / 180 ) * radius );
    INT y1 = GDI_ROUND( y - sin( start * M_PI / 180 ) * radius );
    INT x2 = GDI_ROUND( x + cos( (start + sweep) * M_PI / 180) * radius );
    INT y2 = GDI_ROUND( y - sin( (start + sweep) * M_PI / 180) * radius );
    INT arcdir = SetArcDirection( dev->hdc, sweep >= 0 ? AD_COUNTERCLOCKWISE : AD_CLOCKWISE );
    BOOL ret = ArcTo( dev->hdc, x - radius, y - radius, x + radius, y + radius, x1, y1, x2, y2 );
    SetArcDirection( dev->hdc, arcdir );
    return ret;
}

56 57
BOOL nulldrv_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
                    INT xstart, INT ystart, INT xend, INT yend )
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
{
    INT width = abs( right - left );
    INT height = abs( bottom - top );
    double xradius = width / 2.0;
    double yradius = height / 2.0;
    double xcenter = right > left ? left + xradius : right + xradius;
    double ycenter = bottom > top ? top + yradius : bottom + yradius;
    double angle;

    if (!height || !width) return FALSE;
    /* draw a line from the current position to the starting point of the arc, then draw the arc */
    angle = atan2( (ystart - ycenter) / height, (xstart - xcenter) / width );
    LineTo( dev->hdc, GDI_ROUND( xcenter + cos(angle) * xradius ),
            GDI_ROUND( ycenter + sin(angle) * yradius ));
    return Arc( dev->hdc, left, top, right, bottom, xstart, ystart, xend, yend );
}

75
BOOL nulldrv_FillRgn( PHYSDEV dev, HRGN rgn, HBRUSH brush )
76 77 78 79 80 81 82 83 84 85 86 87
{
    BOOL ret = FALSE;
    HBRUSH prev;

    if ((prev = SelectObject( dev->hdc, brush )))
    {
        ret = PaintRgn( dev->hdc, rgn );
        SelectObject( dev->hdc, prev );
    }
    return ret;
}

88
BOOL nulldrv_FrameRgn( PHYSDEV dev, HRGN rgn, HBRUSH brush, INT width, INT height )
89 90 91 92 93 94 95 96 97 98 99 100
{
    BOOL ret = FALSE;
    HRGN tmp = CreateRectRgn( 0, 0, 0, 0 );

    if (tmp)
    {
        if (REGION_FrameRgn( tmp, rgn, width, height )) ret = FillRgn( dev->hdc, tmp, brush );
        DeleteObject( tmp );
    }
    return ret;
}

101
BOOL nulldrv_InvertRgn( PHYSDEV dev, HRGN rgn )
102 103 104 105 106 107 108 109 110
{
    HBRUSH prev_brush = SelectObject( dev->hdc, GetStockObject(BLACK_BRUSH) );
    INT prev_rop = SetROP2( dev->hdc, R2_NOT );
    BOOL ret = PaintRgn( dev->hdc, rgn );
    SelectObject( dev->hdc, prev_brush );
    SetROP2( dev->hdc, prev_rop );
    return ret;
}

111
BOOL nulldrv_PolyBezier( PHYSDEV dev, const POINT *points, DWORD count )
112 113 114 115 116 117 118 119 120 121 122 123 124
{
    BOOL ret = FALSE;
    POINT *pts;
    INT n;

    if ((pts = GDI_Bezier( points, count, &n )))
    {
        ret = Polyline( dev->hdc, pts, n );
        HeapFree( GetProcessHeap(), 0, pts );
    }
    return ret;
}

125
BOOL nulldrv_PolyBezierTo( PHYSDEV dev, const POINT *points, DWORD count )
126 127 128 129 130 131 132 133 134 135 136 137 138 139
{
    BOOL ret = FALSE;
    POINT *pts = HeapAlloc( GetProcessHeap(), 0, sizeof(POINT) * (count + 1) );

    if (pts)
    {
        GetCurrentPositionEx( dev->hdc, &pts[0] );
        memcpy( pts + 1, points, sizeof(POINT) * count );
        ret = PolyBezier( dev->hdc, pts, count + 1 );
        HeapFree( GetProcessHeap(), 0, pts );
    }
    return ret;
}

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 144
    DWORD i;
    INT num_pts, num_bzr_pts, space, size;
145 146 147 148 149 150 151 152 153 154 155

    /* check for valid point types */
    for (i = 0; i < count; i++)
    {
        switch (types[i])
        {
        case PT_MOVETO:
        case PT_LINETO | PT_CLOSEFIGURE:
        case PT_LINETO:
            break;
        case PT_BEZIERTO:
156 157 158 159 160
            if (i + 2 >= count) return FALSE;
            if (types[i + 1] != PT_BEZIERTO) return FALSE;
            if ((types[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) return FALSE;
            i += 2;
            break;
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
        default:
            return FALSE;
        }
    }

    space = count + 300;
    line_pts = HeapAlloc( GetProcessHeap(), 0, space * sizeof(POINT) );
    num_pts = 1;

    GetCurrentPositionEx( dev->hdc, &line_pts[0] );
    for (i = 0; i < count; i++)
    {
        switch (types[i])
        {
        case PT_MOVETO:
            if (num_pts >= 2) Polyline( dev->hdc, line_pts, num_pts );
            num_pts = 0;
            line_pts[num_pts++] = points[i];
            break;
        case PT_LINETO:
        case (PT_LINETO | PT_CLOSEFIGURE):
            line_pts[num_pts++] = points[i];
            break;
        case PT_BEZIERTO:
            bzr[0].x = line_pts[num_pts - 1].x;
            bzr[0].y = line_pts[num_pts - 1].y;
            memcpy( &bzr[1], &points[i], 3 * sizeof(POINT) );

            if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts )))
            {
                size = num_pts + (count - i) + num_bzr_pts;
                if (space < size)
                {
                    space = size * 2;
                    line_pts = HeapReAlloc( GetProcessHeap(), 0, line_pts, space * sizeof(POINT) );
                }
                memcpy( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) );
                num_pts += num_bzr_pts - 1;
                HeapFree( GetProcessHeap(), 0, bzr_pts );
            }
            i += 2;
            break;
        }
        if (types[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0];
    }

    if (num_pts >= 2) Polyline( dev->hdc, line_pts, num_pts );
    MoveToEx( dev->hdc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL );
    HeapFree( GetProcessHeap(), 0, line_pts );
    return TRUE;
}

213
BOOL nulldrv_PolylineTo( PHYSDEV dev, const POINT *points, INT count )
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
{
    BOOL ret = FALSE;
    POINT *pts;

    if (!count) return FALSE;
    if ((pts = HeapAlloc( GetProcessHeap(), 0, sizeof(POINT) * (count + 1) )))
    {
        GetCurrentPositionEx( dev->hdc, &pts[0] );
        memcpy( pts + 1, points, sizeof(POINT) * count );
        ret = Polyline( dev->hdc, pts, count + 1 );
        HeapFree( GetProcessHeap(), 0, pts );
    }
    return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
229
/***********************************************************************
230
 *           LineTo    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
231
 */
232
BOOL WINAPI LineTo( HDC hdc, INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
233
{
234
    DC * dc = get_dc_ptr( hdc );
235
    PHYSDEV physdev;
236
    BOOL ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
237

238 239
    if(!dc) return FALSE;

240
    update_dc( dc );
241 242 243
    physdev = GET_DC_PHYSDEV( dc, pLineTo );
    ret = physdev->funcs->pLineTo( physdev, x, y );

244
    if(ret) {
245 246
        dc->CursPosX = x;
        dc->CursPosY = y;
247
    }
248
    release_dc_ptr( dc );
249
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251 252 253
}


/***********************************************************************
254
 *           MoveToEx    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
255
 */
256
BOOL WINAPI MoveToEx( HDC hdc, INT x, INT y, LPPOINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
257
{
258 259
    BOOL ret;
    PHYSDEV physdev;
260
    DC * dc = get_dc_ptr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
261

262 263 264
    if(!dc) return FALSE;

    if(pt) {
265 266
        pt->x = dc->CursPosX;
        pt->y = dc->CursPosY;
267
    }
268 269
    dc->CursPosX = x;
    dc->CursPosY = y;
270

271 272
    physdev = GET_DC_PHYSDEV( dc, pMoveTo );
    ret = physdev->funcs->pMoveTo( physdev, x, y );
273
    release_dc_ptr( dc );
274
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
275 276 277 278
}


/***********************************************************************
279
 *           Arc    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
280
 */
281 282 283
BOOL WINAPI Arc( HDC hdc, INT left, INT top, INT right,
                     INT bottom, INT xstart, INT ystart,
                     INT xend, INT yend )
Alexandre Julliard's avatar
Alexandre Julliard committed
284
{
285 286
    PHYSDEV physdev;
    BOOL ret;
287 288
    DC * dc = get_dc_ptr( hdc );

289 290 291 292 293
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pArc );
    ret = physdev->funcs->pArc( physdev, left, top, right, bottom, xstart, ystart, xend, yend );
    release_dc_ptr( dc );
294
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
295 296
}

297
/***********************************************************************
298
 *           ArcTo    (GDI32.@)
299
 */
300 301
BOOL WINAPI ArcTo( HDC hdc,
                     INT left,   INT top,
302 303 304
                     INT right,  INT bottom,
                     INT xstart, INT ystart,
                     INT xend,   INT yend )
305
{
306 307 308 309 310 311 312
    double width = fabs(right-left),
        height = fabs(bottom-top),
        xradius = width/2,
        yradius = height/2,
        xcenter = right > left ? left+xradius : right+xradius,
        ycenter = bottom > top ? top+yradius : bottom+yradius,
        angle;
313
    PHYSDEV physdev;
314
    BOOL result;
315
    DC * dc = get_dc_ptr( hdc );
316 317
    if(!dc) return FALSE;

318
    update_dc( dc );
319 320 321
    physdev = GET_DC_PHYSDEV( dc, pArcTo );
    result = physdev->funcs->pArcTo( physdev, left, top, right, bottom, xstart, ystart, xend, yend );

322 323 324 325 326
    if (result) {
        angle = atan2(((yend-ycenter)/height),
                      ((xend-xcenter)/width));
        dc->CursPosX = GDI_ROUND(xcenter+(cos(angle)*xradius));
        dc->CursPosY = GDI_ROUND(ycenter+(sin(angle)*yradius));
327
    }
328
    release_dc_ptr( dc );
329 330
    return result;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
331 332 333


/***********************************************************************
334
 *           Pie   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
335
 */
336 337 338
BOOL WINAPI Pie( HDC hdc, INT left, INT top,
                     INT right, INT bottom, INT xstart, INT ystart,
                     INT xend, INT yend )
Alexandre Julliard's avatar
Alexandre Julliard committed
339
{
340 341
    BOOL ret;
    PHYSDEV physdev;
342
    DC * dc = get_dc_ptr( hdc );
343 344
    if (!dc) return FALSE;

345
    update_dc( dc );
346 347
    physdev = GET_DC_PHYSDEV( dc, pPie );
    ret = physdev->funcs->pPie( physdev, left, top, right, bottom, xstart, ystart, xend, yend );
348
    release_dc_ptr( dc );
349
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
350 351 352 353
}


/***********************************************************************
354
 *           Chord    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
355
 */
356 357 358
BOOL WINAPI Chord( HDC hdc, INT left, INT top,
                       INT right, INT bottom, INT xstart, INT ystart,
                       INT xend, INT yend )
Alexandre Julliard's avatar
Alexandre Julliard committed
359
{
360 361
    BOOL ret;
    PHYSDEV physdev;
362
    DC * dc = get_dc_ptr( hdc );
363 364
    if (!dc) return FALSE;

365
    update_dc( dc );
366 367
    physdev = GET_DC_PHYSDEV( dc, pChord );
    ret = physdev->funcs->pChord( physdev, left, top, right, bottom, xstart, ystart, xend, yend );
368
    release_dc_ptr( dc );
369
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
370 371 372 373
}


/***********************************************************************
374
 *           Ellipse    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
375
 */
376 377
BOOL WINAPI Ellipse( HDC hdc, INT left, INT top,
                         INT right, INT bottom )
Alexandre Julliard's avatar
Alexandre Julliard committed
378
{
379 380
    BOOL ret;
    PHYSDEV physdev;
381
    DC * dc = get_dc_ptr( hdc );
382 383
    if (!dc) return FALSE;

384
    update_dc( dc );
385 386
    physdev = GET_DC_PHYSDEV( dc, pEllipse );
    ret = physdev->funcs->pEllipse( physdev, left, top, right, bottom );
387
    release_dc_ptr( dc );
388
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
389 390 391 392
}


/***********************************************************************
393
 *           Rectangle    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
394
 */
395 396
BOOL WINAPI Rectangle( HDC hdc, INT left, INT top,
                           INT right, INT bottom )
Alexandre Julliard's avatar
Alexandre Julliard committed
397
{
398 399
    PHYSDEV physdev;
    BOOL ret;
400 401
    DC * dc = get_dc_ptr( hdc );

402 403 404 405 406
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pRectangle );
    ret = physdev->funcs->pRectangle( physdev, left, top, right, bottom );
    release_dc_ptr( dc );
407
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
408 409 410 411
}


/***********************************************************************
412
 *           RoundRect    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
413
 */
414 415
BOOL WINAPI RoundRect( HDC hdc, INT left, INT top, INT right,
                           INT bottom, INT ell_width, INT ell_height )
Alexandre Julliard's avatar
Alexandre Julliard committed
416
{
417 418
    PHYSDEV physdev;
    BOOL ret;
419
    DC *dc = get_dc_ptr( hdc );
420

421 422 423 424 425
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pRoundRect );
    ret = physdev->funcs->pRoundRect( physdev, left, top, right, bottom, ell_width, ell_height );
    release_dc_ptr( dc );
426
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
427 428 429
}

/***********************************************************************
430
 *           SetPixel    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
431
 */
432
COLORREF WINAPI SetPixel( HDC hdc, INT x, INT y, COLORREF color )
Alexandre Julliard's avatar
Alexandre Julliard committed
433
{
434 435
    PHYSDEV physdev;
    COLORREF ret;
436 437
    DC * dc = get_dc_ptr( hdc );

438 439 440 441 442
    if (!dc) return 0;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pSetPixel );
    ret = physdev->funcs->pSetPixel( physdev, x, y, color );
    release_dc_ptr( dc );
443
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
444 445
}

Alexandre Julliard's avatar
Alexandre Julliard committed
446
/***********************************************************************
447
 *           SetPixelV    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
448
 */
449
BOOL WINAPI SetPixelV( HDC hdc, INT x, INT y, COLORREF color )
Alexandre Julliard's avatar
Alexandre Julliard committed
450
{
451
    PHYSDEV physdev;
452 453
    DC * dc = get_dc_ptr( hdc );

454 455 456 457 458 459
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pSetPixel );
    physdev->funcs->pSetPixel( physdev, x, y, color );
    release_dc_ptr( dc );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
460
}
Alexandre Julliard's avatar
Alexandre Julliard committed
461 462

/***********************************************************************
463
 *           GetPixel    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
464
 */
465
COLORREF WINAPI GetPixel( HDC hdc, INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
466
{
467 468
    PHYSDEV physdev;
    COLORREF ret;
469
    DC * dc = get_dc_ptr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
470

471 472 473 474 475
    if (!dc) return CLR_INVALID;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pGetPixel );
    ret = physdev->funcs->pGetPixel( physdev, x, y );
    release_dc_ptr( dc );
476
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
477 478 479
}


Alexandre Julliard's avatar
Alexandre Julliard committed
480
/******************************************************************************
481
 * GdiSetPixelFormat [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
482
 *
483
 * Probably not the correct semantics, it's supposed to be an internal backend for SetPixelFormat.
Alexandre Julliard's avatar
Alexandre Julliard committed
484
 */
485
BOOL WINAPI GdiSetPixelFormat( HDC hdc, INT format, const PIXELFORMATDESCRIPTOR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
486
{
487 488
    DC *dc;
    BOOL ret = TRUE;
489

490
    TRACE("(%p,%d,%p)\n", hdc, format, descr);
491

492 493 494 495 496 497
    if (!(dc = get_dc_ptr( hdc ))) return FALSE;

    if (!dc->pixel_format) dc->pixel_format = format;
    else ret = (dc->pixel_format == format);
    release_dc_ptr( dc );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
498 499 500 501
}


/******************************************************************************
502
 * GdiDescribePixelFormat [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
503
 *
504
 * Probably not the correct semantics, it's supposed to be an internal backend for DescribePixelFormat.
Alexandre Julliard's avatar
Alexandre Julliard committed
505
 */
506
INT WINAPI GdiDescribePixelFormat( HDC hdc, INT format, UINT size, PIXELFORMATDESCRIPTOR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
507
{
508 509
    FIXME( "(%p,%d,%d,%p): stub\n", hdc, format, size, descr );
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
510 511 512 513
}


/******************************************************************************
514
 * GdiSwapBuffers [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
515
 *
516
 * Probably not the correct semantics, it's supposed to be an internal backend for SwapBuffers.
Alexandre Julliard's avatar
Alexandre Julliard committed
517
 */
518
BOOL WINAPI GdiSwapBuffers( HDC hdc )
Alexandre Julliard's avatar
Alexandre Julliard committed
519
{
520 521
    FIXME( "(%p): stub\n", hdc );
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
522 523 524
}


Alexandre Julliard's avatar
Alexandre Julliard committed
525
/***********************************************************************
526
 *           PaintRgn    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
527
 */
528
BOOL WINAPI PaintRgn( HDC hdc, HRGN hrgn )
Alexandre Julliard's avatar
Alexandre Julliard committed
529
{
530 531
    PHYSDEV physdev;
    BOOL ret;
532 533
    DC * dc = get_dc_ptr( hdc );

534 535 536 537 538
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pPaintRgn );
    ret = physdev->funcs->pPaintRgn( physdev, hrgn );
    release_dc_ptr( dc );
539
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
540 541 542 543
}


/***********************************************************************
544
 *           FillRgn    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
545
 */
546
BOOL WINAPI FillRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush )
Alexandre Julliard's avatar
Alexandre Julliard committed
547
{
548 549
    PHYSDEV physdev;
    BOOL retval;
550
    DC * dc = get_dc_ptr( hdc );
551

552 553 554 555 556
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pFillRgn );
    retval = physdev->funcs->pFillRgn( physdev, hrgn, hbrush );
    release_dc_ptr( dc );
Alexandre Julliard's avatar
Alexandre Julliard committed
557 558 559 560 561
    return retval;
}


/***********************************************************************
562
 *           FrameRgn     (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
563
 */
564 565
BOOL WINAPI FrameRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush,
                          INT nWidth, INT nHeight )
Alexandre Julliard's avatar
Alexandre Julliard committed
566
{
567 568
    PHYSDEV physdev;
    BOOL ret;
569
    DC *dc = get_dc_ptr( hdc );
570

571 572 573 574 575
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pFrameRgn );
    ret = physdev->funcs->pFrameRgn( physdev, hrgn, hbrush, nWidth, nHeight );
    release_dc_ptr( dc );
576
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
577 578 579 580
}


/***********************************************************************
581
 *           InvertRgn    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
582
 */
583
BOOL WINAPI InvertRgn( HDC hdc, HRGN hrgn )
Alexandre Julliard's avatar
Alexandre Julliard committed
584
{
585 586
    PHYSDEV physdev;
    BOOL ret;
587
    DC *dc = get_dc_ptr( hdc );
588

589 590 591 592 593
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pInvertRgn );
    ret = physdev->funcs->pInvertRgn( physdev, hrgn );
    release_dc_ptr( dc );
594
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
595 596 597 598
}


/**********************************************************************
599
 *          Polyline   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
600
 */
601
BOOL WINAPI Polyline( HDC hdc, const POINT* pt, INT count )
Alexandre Julliard's avatar
Alexandre Julliard committed
602
{
603 604
    PHYSDEV physdev;
    BOOL ret;
605 606
    DC * dc = get_dc_ptr( hdc );

607 608 609 610 611
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pPolyline );
    ret = physdev->funcs->pPolyline( physdev, pt, count );
    release_dc_ptr( dc );
612
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
613 614
}

615
/**********************************************************************
616
 *          PolylineTo   (GDI32.@)
617
 */
618
BOOL WINAPI PolylineTo( HDC hdc, const POINT* pt, DWORD cCount )
619
{
620
    DC * dc = get_dc_ptr( hdc );
621 622
    PHYSDEV physdev;
    BOOL ret;
Huw D M Davies's avatar
Huw D M Davies committed
623

624
    if(!dc) return FALSE;
Huw D M Davies's avatar
Huw D M Davies committed
625

626
    update_dc( dc );
627 628 629
    physdev = GET_DC_PHYSDEV( dc, pPolylineTo );
    ret = physdev->funcs->pPolylineTo( physdev, pt, cCount );

630
    if (ret && cCount)
631
    {
632 633
        dc->CursPosX = pt[cCount-1].x;
	dc->CursPosY = pt[cCount-1].y;
634
    }
635
    release_dc_ptr( dc );
636
    return ret;
637
}
Alexandre Julliard's avatar
Alexandre Julliard committed
638 639 640


/**********************************************************************
641
 *          Polygon  (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
642
 */
643
BOOL WINAPI Polygon( HDC hdc, const POINT* pt, INT count )
Alexandre Julliard's avatar
Alexandre Julliard committed
644
{
645 646
    PHYSDEV physdev;
    BOOL ret;
647 648
    DC * dc = get_dc_ptr( hdc );

649 650 651 652 653
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pPolygon );
    ret = physdev->funcs->pPolygon( physdev, pt, count );
    release_dc_ptr( dc );
654
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
655 656 657 658
}


/**********************************************************************
659
 *          PolyPolygon  (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
660
 */
661 662
BOOL WINAPI PolyPolygon( HDC hdc, const POINT* pt, const INT* counts,
                             UINT polygons )
Alexandre Julliard's avatar
Alexandre Julliard committed
663
{
664 665
    PHYSDEV physdev;
    BOOL ret;
666 667
    DC * dc = get_dc_ptr( hdc );

668 669 670 671 672
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pPolyPolygon );
    ret = physdev->funcs->pPolyPolygon( physdev, pt, counts, polygons );
    release_dc_ptr( dc );
673
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
674 675
}

Alexandre Julliard's avatar
Alexandre Julliard committed
676
/**********************************************************************
677
 *          PolyPolyline  (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
678
 */
679
BOOL WINAPI PolyPolyline( HDC hdc, const POINT* pt, const DWORD* counts,
680
                            DWORD polylines )
Alexandre Julliard's avatar
Alexandre Julliard committed
681
{
682 683
    PHYSDEV physdev;
    BOOL ret;
684 685
    DC * dc = get_dc_ptr( hdc );

686 687 688 689 690
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pPolyPolyline );
    ret = physdev->funcs->pPolyPolyline( physdev, pt, counts, polylines );
    release_dc_ptr( dc );
691
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
692 693
}

Alexandre Julliard's avatar
Alexandre Julliard committed
694
/**********************************************************************
695
 *          ExtFloodFill   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
696
 */
697 698
BOOL WINAPI ExtFloodFill( HDC hdc, INT x, INT y, COLORREF color,
                              UINT fillType )
Alexandre Julliard's avatar
Alexandre Julliard committed
699
{
700 701
    PHYSDEV physdev;
    BOOL ret;
702 703
    DC * dc = get_dc_ptr( hdc );

704 705 706 707 708
    if (!dc) return FALSE;
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pExtFloodFill );
    ret = physdev->funcs->pExtFloodFill( physdev, x, y, color, fillType );
    release_dc_ptr( dc );
709
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
710 711 712 713
}


/**********************************************************************
714
 *          FloodFill   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
715
 */
716
BOOL WINAPI FloodFill( HDC hdc, INT x, INT y, COLORREF color )
Alexandre Julliard's avatar
Alexandre Julliard committed
717
{
718
    return ExtFloodFill( hdc, x, y, color, FLOODFILLBORDER );
Alexandre Julliard's avatar
Alexandre Julliard committed
719 720 721
}


Alexandre Julliard's avatar
Alexandre Julliard committed
722
/******************************************************************************
723
 * PolyBezier [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
724 725 726 727 728 729 730
 * Draws one or more Bezier curves
 *
 * PARAMS
 *    hDc     [I] Handle to device context
 *    lppt    [I] Pointer to endpoints and control points
 *    cPoints [I] Count of endpoints and control points
 *
731 732 733
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
734
 */
735
BOOL WINAPI PolyBezier( HDC hdc, const POINT* lppt, DWORD cPoints )
Alexandre Julliard's avatar
Alexandre Julliard committed
736
{
737 738
    PHYSDEV physdev;
    BOOL ret;
739 740 741 742
    DC * dc;

    /* cPoints must be 3 * n + 1 (where n>=1) */
    if (cPoints == 1 || (cPoints % 3) != 1) return FALSE;
743

744
    dc = get_dc_ptr( hdc );
745 746
    if(!dc) return FALSE;

747
    update_dc( dc );
748 749
    physdev = GET_DC_PHYSDEV( dc, pPolyBezier );
    ret = physdev->funcs->pPolyBezier( physdev, lppt, cPoints );
750
    release_dc_ptr( dc );
751 752 753
    return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
754
/******************************************************************************
755
 * PolyBezierTo [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
756 757 758 759 760 761 762
 * Draws one or more Bezier curves
 *
 * PARAMS
 *    hDc     [I] Handle to device context
 *    lppt    [I] Pointer to endpoints and control points
 *    cPoints [I] Count of endpoints and control points
 *
763 764 765
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
766
 */
767
BOOL WINAPI PolyBezierTo( HDC hdc, const POINT* lppt, DWORD cPoints )
Alexandre Julliard's avatar
Alexandre Julliard committed
768
{
769
    DC * dc;
770 771
    BOOL ret;
    PHYSDEV physdev;
772

773 774 775
    /* cbPoints must be 3 * n (where n>=1) */
    if (!cPoints || (cPoints % 3) != 0) return FALSE;

776
    dc = get_dc_ptr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
777
    if(!dc) return FALSE;
778

779
    update_dc( dc );
780 781 782
    physdev = GET_DC_PHYSDEV( dc, pPolyBezierTo );
    ret = physdev->funcs->pPolyBezierTo( physdev, lppt, cPoints );

783
    if(ret) {
784 785
        dc->CursPosX = lppt[cPoints-1].x;
        dc->CursPosY = lppt[cPoints-1].y;
786
    }
787
    release_dc_ptr( dc );
Alexandre Julliard's avatar
Alexandre Julliard committed
788 789
    return ret;
}
790

791
/***********************************************************************
792
 *      AngleArc (GDI32.@)
793
 */
794
BOOL WINAPI AngleArc(HDC hdc, INT x, INT y, DWORD dwRadius, FLOAT eStartAngle, FLOAT eSweepAngle)
795
{
796
    PHYSDEV physdev;
797 798 799
    BOOL result;
    DC *dc;

800
    if( (signed int)dwRadius < 0 )
801 802
	return FALSE;

803
    dc = get_dc_ptr( hdc );
804 805
    if(!dc) return FALSE;

806
    update_dc( dc );
807 808 809
    physdev = GET_DC_PHYSDEV( dc, pAngleArc );
    result = physdev->funcs->pAngleArc( physdev, x, y, dwRadius, eStartAngle, eSweepAngle );

810
    if (result) {
811 812
        dc->CursPosX = GDI_ROUND( x + cos((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius );
        dc->CursPosY = GDI_ROUND( y - sin((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius );
813
    }
814
    release_dc_ptr( dc );
815
    return result;
816
}
817

818
/***********************************************************************
819
 *      PolyDraw (GDI32.@)
820
 */
821
BOOL WINAPI PolyDraw(HDC hdc, const POINT *lppt, const BYTE *lpbTypes,
822
                       DWORD cCount)
823
{
824
    DC *dc = get_dc_ptr( hdc );
825 826
    PHYSDEV physdev;
    BOOL result;
827

828
    if(!dc) return FALSE;
829

830
    update_dc( dc );
831 832
    physdev = GET_DC_PHYSDEV( dc, pPolyDraw );
    result = physdev->funcs->pPolyDraw( physdev, lppt, lpbTypes, cCount );
833
    release_dc_ptr( dc );
834
    return result;
835
}
836

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862

/**********************************************************************
 *           LineDDA   (GDI32.@)
 */
BOOL WINAPI LineDDA(INT nXStart, INT nYStart, INT nXEnd, INT nYEnd,
                    LINEDDAPROC callback, LPARAM lParam )
{
    INT xadd = 1, yadd = 1;
    INT err,erradd;
    INT cnt;
    INT dx = nXEnd - nXStart;
    INT dy = nYEnd - nYStart;

    if (dx < 0)
    {
        dx = -dx;
        xadd = -1;
    }
    if (dy < 0)
    {
        dy = -dy;
        yadd = -1;
    }
    if (dx > dy)  /* line is "more horizontal" */
    {
        err = 2*dy - dx; erradd = 2*dy - 2*dx;
863
        for(cnt = 0;cnt < dx; cnt++)
864 865 866 867 868 869 870 871 872 873 874 875 876 877
        {
            callback(nXStart,nYStart,lParam);
            if (err > 0)
            {
                nYStart += yadd;
                err += erradd;
            }
            else err += 2*dy;
            nXStart += xadd;
        }
    }
    else   /* line is "more vertical" */
    {
        err = 2*dx - dy; erradd = 2*dx - 2*dy;
878
        for(cnt = 0;cnt < dy; cnt++)
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
        {
            callback(nXStart,nYStart,lParam);
            if (err > 0)
            {
                nXStart += xadd;
                err += erradd;
            }
            else err += 2*dx;
            nYStart += yadd;
        }
    }
    return TRUE;
}


894
/******************************************************************
895 896
 *
 *   *Very* simple bezier drawing code,
897 898
 *
 *   It uses a recursive algorithm to divide the curve in a series
899
 *   of straight line segments. Not ideal but sufficient for me.
900 901 902 903 904 905
 *   If you are in need for something better look for some incremental
 *   algorithm.
 *
 *   7 July 1998 Rein Klazes
 */

906
 /*
907 908
  * some macro definitions for bezier drawing
  *
909
  * to avoid truncation errors the coordinates are
910 911
  * shifted upwards. When used in drawing they are
  * shifted down again, including correct rounding
912
  * and avoiding floating point arithmetic
913
  * 4 bits should allow 27 bits coordinates which I saw
914
  * somewhere in the win32 doc's
915
  *
916 917 918 919
  */

#define BEZIERSHIFTBITS 4
#define BEZIERSHIFTUP(x)    ((x)<<BEZIERSHIFTBITS)
920
#define BEZIERPIXEL        BEZIERSHIFTUP(1)
921 922 923 924 925 926 927 928
#define BEZIERSHIFTDOWN(x)  (((x)+(1<<(BEZIERSHIFTBITS-1)))>>BEZIERSHIFTBITS)
/* maximum depth of recursion */
#define BEZIERMAXDEPTH  8

/* size of array to store points on */
/* enough for one curve */
#define BEZIER_INITBUFSIZE    (150)

929
/* calculate Bezier average, in this case the middle
930 931 932 933 934 935
 * correctly rounded...
 * */

#define BEZIERMIDDLE(Mid, P1, P2) \
    (Mid).x=((P1).x+(P2).x + 1)/2;\
    (Mid).y=((P1).y+(P2).y + 1)/2;
936

937 938 939 940 941 942
/**********************************************************
* BezierCheck helper function to check
* that recursion can be terminated
*       Points[0] and Points[3] are begin and endpoint
*       Points[1] and Points[2] are control points
*       level is the recursion depth
Austin English's avatar
Austin English committed
943
*       returns true if the recursion can be terminated
944 945
*/
static BOOL BezierCheck( int level, POINT *Points)
946
{
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
    INT dx, dy;
    dx=Points[3].x-Points[0].x;
    dy=Points[3].y-Points[0].y;
    if(abs(dy)<=abs(dx)){/* shallow line */
        /* check that control points are between begin and end */
        if(Points[1].x < Points[0].x){
            if(Points[1].x < Points[3].x)
                return FALSE;
        }else
            if(Points[1].x > Points[3].x)
                return FALSE;
        if(Points[2].x < Points[0].x){
            if(Points[2].x < Points[3].x)
                return FALSE;
        }else
            if(Points[2].x > Points[3].x)
                return FALSE;
        dx=BEZIERSHIFTDOWN(dx);
        if(!dx) return TRUE;
        if(abs(Points[1].y-Points[0].y-(dy/dx)*
                BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL ||
           abs(Points[2].y-Points[0].y-(dy/dx)*
                   BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL )
            return FALSE;
        else
            return TRUE;
    }else{ /* steep line */
        /* check that control points are between begin and end */
        if(Points[1].y < Points[0].y){
            if(Points[1].y < Points[3].y)
                return FALSE;
        }else
            if(Points[1].y > Points[3].y)
                return FALSE;
        if(Points[2].y < Points[0].y){
            if(Points[2].y < Points[3].y)
                return FALSE;
        }else
            if(Points[2].y > Points[3].y)
                return FALSE;
        dy=BEZIERSHIFTDOWN(dy);
        if(!dy) return TRUE;
        if(abs(Points[1].x-Points[0].x-(dx/dy)*
                BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL ||
           abs(Points[2].x-Points[0].x-(dx/dy)*
                   BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL )
            return FALSE;
        else
            return TRUE;
    }
}
998

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
/* Helper for GDI_Bezier.
 * Just handles one Bezier, so Points should point to four POINTs
 */
static void GDI_InternalBezier( POINT *Points, POINT **PtsOut, INT *dwOut,
				INT *nPtsOut, INT level )
{
    if(*nPtsOut == *dwOut) {
        *dwOut *= 2;
	*PtsOut = HeapReAlloc( GetProcessHeap(), 0, *PtsOut,
			       *dwOut * sizeof(POINT) );
    }

    if(!level || BezierCheck(level, Points)) {
        if(*nPtsOut == 0) {
            (*PtsOut)[0].x = BEZIERSHIFTDOWN(Points[0].x);
            (*PtsOut)[0].y = BEZIERSHIFTDOWN(Points[0].y);
            *nPtsOut = 1;
        }
	(*PtsOut)[*nPtsOut].x = BEZIERSHIFTDOWN(Points[3].x);
        (*PtsOut)[*nPtsOut].y = BEZIERSHIFTDOWN(Points[3].y);
        (*nPtsOut) ++;
    } else {
        POINT Points2[4]; /* for the second recursive call */
        Points2[3]=Points[3];
        BEZIERMIDDLE(Points2[2], Points[2], Points[3]);
        BEZIERMIDDLE(Points2[0], Points[1], Points[2]);
        BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]);

        BEZIERMIDDLE(Points[1], Points[0],  Points[1]);
        BEZIERMIDDLE(Points[2], Points[1], Points2[0]);
        BEZIERMIDDLE(Points[3], Points[2], Points2[1]);

        Points2[0]=Points[3];

        /* do the two halves */
        GDI_InternalBezier(Points, PtsOut, dwOut, nPtsOut, level-1);
        GDI_InternalBezier(Points2, PtsOut, dwOut, nPtsOut, level-1);
    }
}


1040

1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
/***********************************************************************
 *           GDI_Bezier   [INTERNAL]
 *   Calculate line segments that approximate -what microsoft calls- a bezier
 *   curve.
 *   The routine recursively divides the curve in two parts until a straight
 *   line can be drawn
 *
 *  PARAMS
 *
 *  Points  [I] Ptr to count POINTs which are the end and control points
 *              of the set of Bezier curves to flatten.
 *  count   [I] Number of Points.  Must be 3n+1.
 *  nPtsOut [O] Will contain no of points that have been produced (i.e. no. of
 *              lines+1).
1055
 *
1056 1057
 *  RETURNS
 *
Austin English's avatar
Austin English committed
1058
 *  Ptr to an array of POINTs that contain the lines that approximate the
1059 1060
 *  Beziers.  The array is allocated on the process heap and it is the caller's
 *  responsibility to HeapFree it. [this is not a particularly nice interface
1061
 *  but since we can't know in advance how many points we will generate, the
1062 1063 1064 1065 1066 1067 1068 1069
 *  alternative would be to call the function twice, once to determine the size
 *  and a second time to do the work - I decided this was too much of a pain].
 */
POINT *GDI_Bezier( const POINT *Points, INT count, INT *nPtsOut )
{
    POINT *out;
    INT Bezier, dwOut = BEZIER_INITBUFSIZE, i;

1070 1071
    if (count == 1 || (count - 1) % 3 != 0) {
        ERR("Invalid no. of points %d\n", count);
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
	return NULL;
    }
    *nPtsOut = 0;
    out = HeapAlloc( GetProcessHeap(), 0, dwOut * sizeof(POINT));
    for(Bezier = 0; Bezier < (count-1)/3; Bezier++) {
	POINT ptBuf[4];
	memcpy(ptBuf, Points + Bezier * 3, sizeof(POINT) * 4);
	for(i = 0; i < 4; i++) {
	    ptBuf[i].x = BEZIERSHIFTUP(ptBuf[i].x);
	    ptBuf[i].y = BEZIERSHIFTUP(ptBuf[i].y);
	}
        GDI_InternalBezier( ptBuf, &out, &dwOut, nPtsOut, BEZIERMAXDEPTH );
    }
    TRACE("Produced %d points\n", *nPtsOut);
    return out;
}
1088 1089

/******************************************************************************
1090
 *           GdiGradientFill   (GDI32.@)
1091 1092
 */
BOOL WINAPI GdiGradientFill( HDC hdc, TRIVERTEX *vert_array, ULONG nvert,
1093
                             void *grad_array, ULONG ngrad, ULONG mode )
1094
{
1095
    DC *dc;
1096 1097
    PHYSDEV physdev;
    BOOL ret;
1098
    ULONG i;
1099

1100
    TRACE("%p vert_array:%p nvert:%d grad_array:%p ngrad:%d\n", hdc, vert_array, nvert, grad_array, ngrad);
1101

1102 1103 1104 1105 1106
    if (!vert_array || !nvert || !grad_array || !ngrad || mode > GRADIENT_FILL_TRIANGLE)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
1107 1108 1109
    for (i = 0; i < ngrad * (mode == GRADIENT_FILL_TRIANGLE ? 3 : 2); i++)
        if (((ULONG *)grad_array)[i] >= nvert) return FALSE;

1110 1111 1112 1113 1114
    if (!(dc = get_dc_ptr( hdc )))
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
1115 1116 1117 1118 1119
    update_dc( dc );
    physdev = GET_DC_PHYSDEV( dc, pGradientFill );
    ret = physdev->funcs->pGradientFill( physdev, vert_array, nvert, grad_array, ngrad, mode );
    release_dc_ptr( dc );
    return ret;
1120
}
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130

/******************************************************************************
 *           GdiDrawStream   (GDI32.@)
 *
 */
BOOL WINAPI GdiDrawStream( HDC hdc, ULONG in, void * pvin )
{
    FIXME("stub: %p, %d, %p\n", hdc, in, pvin);
    return FALSE;
}