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 143 144 145 146 147 148 149 150 151 152 153 154
{
    POINT *line_pts = NULL, *bzr_pts = NULL, bzr[4];
    INT i, num_pts, num_bzr_pts, space, size;

    /* 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:
155 156 157 158 159
            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;
160 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
        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;
}

212
BOOL nulldrv_PolylineTo( PHYSDEV dev, const POINT *points, INT count )
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
{
    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
228
/***********************************************************************
229
 *           LineTo    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
230
 */
231
BOOL WINAPI LineTo( HDC hdc, INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
232
{
233
    DC * dc = get_dc_ptr( hdc );
234
    PHYSDEV physdev;
235
    BOOL ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
236

237 238
    if(!dc) return FALSE;

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

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


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

261 262 263
    if(!dc) return FALSE;

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

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


/***********************************************************************
278
 *           Arc    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
279
 */
280 281 282
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
283
{
284 285
    PHYSDEV physdev;
    BOOL ret;
286 287
    DC * dc = get_dc_ptr( hdc );

288 289 290 291 292
    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 );
293
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
294 295
}

296
/***********************************************************************
297
 *           ArcTo    (GDI32.@)
298
 */
299 300
BOOL WINAPI ArcTo( HDC hdc,
                     INT left,   INT top,
301 302 303
                     INT right,  INT bottom,
                     INT xstart, INT ystart,
                     INT xend,   INT yend )
304
{
305 306 307 308 309 310 311
    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;
312
    PHYSDEV physdev;
313
    BOOL result;
314
    DC * dc = get_dc_ptr( hdc );
315 316
    if(!dc) return FALSE;

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

321 322 323 324 325
    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));
326
    }
327
    release_dc_ptr( dc );
328 329
    return result;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
330 331 332


/***********************************************************************
333
 *           Pie   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
334
 */
335 336 337
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
338
{
339 340
    BOOL ret;
    PHYSDEV physdev;
341
    DC * dc = get_dc_ptr( hdc );
342 343
    if (!dc) return FALSE;

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


/***********************************************************************
353
 *           Chord    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
354
 */
355 356 357
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
358
{
359 360
    BOOL ret;
    PHYSDEV physdev;
361
    DC * dc = get_dc_ptr( hdc );
362 363
    if (!dc) return FALSE;

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


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

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


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

401 402 403 404 405
    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 );
406
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
407 408 409 410
}


/***********************************************************************
411
 *           RoundRect    (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
412
 */
413 414
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
415
{
416 417
    PHYSDEV physdev;
    BOOL ret;
418
    DC *dc = get_dc_ptr( hdc );
419

420 421 422 423 424
    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 );
425
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
426 427 428
}

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

437 438 439 440 441
    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 );
442
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
443 444
}

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

453 454 455 456 457 458
    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
459
}
Alexandre Julliard's avatar
Alexandre Julliard committed
460 461

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

470 471 472 473 474
    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 );
475
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
476 477 478
}


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

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

491 492 493 494 495 496
    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
497 498 499 500
}


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


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


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

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


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

551 552 553 554 555
    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
556 557 558 559 560
    return retval;
}


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

570 571 572 573 574
    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 );
575
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
576 577 578 579
}


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

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


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

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

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

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

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

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


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

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


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

667 668 669 670 671
    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 );
672
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
673 674
}

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

685 686 687 688 689
    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 );
690
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
691 692
}

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

703 704 705 706 707
    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 );
708
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
709 710 711 712
}


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


Alexandre Julliard's avatar
Alexandre Julliard committed
721
/******************************************************************************
722
 * PolyBezier [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
723 724 725 726 727 728 729
 * 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
 *
730 731 732
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
733
 */
734
BOOL WINAPI PolyBezier( HDC hdc, const POINT* lppt, DWORD cPoints )
Alexandre Julliard's avatar
Alexandre Julliard committed
735
{
736 737
    PHYSDEV physdev;
    BOOL ret;
738 739 740 741
    DC * dc;

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
753
/******************************************************************************
754
 * PolyBezierTo [GDI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
755 756 757 758 759 760 761
 * 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
 *
762 763 764
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
765
 */
766
BOOL WINAPI PolyBezierTo( HDC hdc, const POINT* lppt, DWORD cPoints )
Alexandre Julliard's avatar
Alexandre Julliard committed
767
{
768
    DC * dc;
769 770
    BOOL ret;
    PHYSDEV physdev;
771

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

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

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

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

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

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

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

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

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

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

827
    if(!dc) return FALSE;
828

829
    update_dc( dc );
830 831
    physdev = GET_DC_PHYSDEV( dc, pPolyDraw );
    result = physdev->funcs->pPolyDraw( physdev, lppt, lpbTypes, cCount );
832
    release_dc_ptr( dc );
833
    return result;
834
}
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

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


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

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

#define BEZIERSHIFTBITS 4
#define BEZIERSHIFTUP(x)    ((x)<<BEZIERSHIFTBITS)
919
#define BEZIERPIXEL        BEZIERSHIFTUP(1)
920 921 922 923 924 925 926 927
#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)

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

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

936 937 938 939 940 941
/**********************************************************
* 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
942
*       returns true if the recursion can be terminated
943 944
*/
static BOOL BezierCheck( int level, POINT *Points)
945
{
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
    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;
    }
}
997

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
/* 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);
    }
}


1039

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
/***********************************************************************
 *           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).
1054
 *
1055 1056
 *  RETURNS
 *
Austin English's avatar
Austin English committed
1057
 *  Ptr to an array of POINTs that contain the lines that approximate the
1058 1059
 *  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
1060
 *  but since we can't know in advance how many points we will generate, the
1061 1062 1063 1064 1065 1066 1067 1068
 *  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;

1069 1070
    if (count == 1 || (count - 1) % 3 != 0) {
        ERR("Invalid no. of points %d\n", count);
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
	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;
}
1087 1088

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

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

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

1109 1110 1111 1112 1113
    if (!(dc = get_dc_ptr( hdc )))
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
1114 1115 1116 1117 1118
    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;
1119
}
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129

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