path.c 40.2 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Graphics paths (BeginPath, EndPath etc.)
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4
 * Copyright 1997, 1998 Martin Boehme
5
 *                 1999 Huw D M Davies
Alexandre Julliard's avatar
Alexandre Julliard committed
6 7 8
 */

#include <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
9
#include <math.h>
10
#include <string.h>
11 12 13 14
#include "config.h"
#if defined(HAVE_FLOAT_H)
#include <float.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
15

16 17
#include "winbase.h"
#include "wingdi.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
18 19 20
#include "winerror.h"

#include "dc.h"
21
#include "debugtools.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
22 23
#include "path.h"

24 25
DEFAULT_DEBUG_CHANNEL(gdi)

Alexandre Julliard's avatar
Alexandre Julliard committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
/* Notes on the implementation
 *
 * The implementation is based on dynamically resizable arrays of points and
 * flags. I dithered for a bit before deciding on this implementation, and
 * I had even done a bit of work on a linked list version before switching
 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
 * implementation of FlattenPath is easier, because you can rip the
 * PT_BEZIERTO entries out of the middle of the list and link the
 * corresponding PT_LINETO entries in. However, when you use arrays,
 * PathToRegion becomes easier, since you can essentially just pass your array
 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
 * have had the extra effort of creating a chunk-based allocation scheme
 * in order to use memory effectively. That's why I finally decided to use
 * arrays. Note by the way that the array based implementation has the same
 * linear time complexity that linked lists would have since the arrays grow
 * exponentially.
 *
 * The points are stored in the path in device coordinates. This is
 * consistent with the way Windows does things (for instance, see the Win32
 * SDK documentation for GetPath).
 *
 * The word "stroke" appears in several places (e.g. in the flag
 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
 * PT_MOVETO. Note that this is not the same as the definition of a figure;
 * a figure can contain several strokes.
 *
 * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
 * the path is open and to call the corresponding function in path.c if this
 * is the case. A more elegant approach would be to modify the function
 * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
 * complex. Also, the performance degradation caused by my approach in the
 * case where no path is open is so small that it cannot be measured.
 *
 * Martin Boehme
 */

/* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */

#define NUM_ENTRIES_INITIAL 16  /* Initial size of points / flags arrays  */
#define GROW_FACTOR_NUMER    2  /* Numerator of grow factor for the array */
#define GROW_FACTOR_DENOM    1  /* Denominator of grow factor             */


70
static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
71
   HRGN *pHrgn);
Alexandre Julliard's avatar
Alexandre Julliard committed
72
static void   PATH_EmptyPath(GdiPath *pPath);
73
static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
Alexandre Julliard's avatar
Alexandre Julliard committed
74
   BYTE flags);
75 76 77
static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
   double angleStart, double angleEnd, BOOL addMoveTo);
Alexandre Julliard's avatar
Alexandre Julliard committed
78
static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
79
   double y, POINT *pPoint);
Alexandre Julliard's avatar
Alexandre Julliard committed
80 81
static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
   *pPoint, double *pX, double *pY);
Alexandre Julliard's avatar
Alexandre Julliard committed
82 83 84 85 86 87 88


/***********************************************************************
 *           BeginPath16    (GDI.512)
 */
BOOL16 WINAPI BeginPath16(HDC16 hdc)
{
89
   return (BOOL16)BeginPath((HDC)hdc);
Alexandre Julliard's avatar
Alexandre Julliard committed
90 91
}

Alexandre Julliard's avatar
Alexandre Julliard committed
92 93

/***********************************************************************
94
 *           BeginPath    (GDI32.9)
Alexandre Julliard's avatar
Alexandre Julliard committed
95
 */
96
BOOL WINAPI BeginPath(HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
97
{
98 99
    BOOL ret = TRUE;
    DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
100
   
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
    if(!dc) return FALSE;

    if(dc->funcs->pBeginPath)
        ret = dc->funcs->pBeginPath(dc);
    else
    {
        /* If path is already open, do nothing */
        if(dc->w.path.state != PATH_Open)
        {
            /* Make sure that path is empty */
            PATH_EmptyPath(&dc->w.path);

            /* Initialize variables for new path */
            dc->w.path.newStroke=TRUE;
            dc->w.path.state=PATH_Open;
        }
    }
    GDI_ReleaseObj( hdc );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
120 121 122
}


Alexandre Julliard's avatar
Alexandre Julliard committed
123 124 125 126 127
/***********************************************************************
 *           EndPath16    (GDI.514)
 */
BOOL16 WINAPI EndPath16(HDC16 hdc)
{
128
   return (BOOL16)EndPath((HDC)hdc);
Alexandre Julliard's avatar
Alexandre Julliard committed
129 130 131
}


Alexandre Julliard's avatar
Alexandre Julliard committed
132
/***********************************************************************
133
 *           EndPath    (GDI32.78)
Alexandre Julliard's avatar
Alexandre Julliard committed
134
 */
135
BOOL WINAPI EndPath(HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
136
{
137 138
    BOOL ret = TRUE;
    DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
139
   
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    if(!dc) return FALSE;

    if(dc->funcs->pEndPath)
        ret = dc->funcs->pEndPath(dc);
    else
    {
        /* Check that path is currently being constructed */
        if(dc->w.path.state!=PATH_Open)
        {
            SetLastError(ERROR_CAN_NOT_COMPLETE);
            ret = FALSE;
        }
        /* Set flag to indicate that path is finished */
        else dc->w.path.state=PATH_Closed;
    }
    GDI_ReleaseObj( hdc );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
157 158 159
}


Alexandre Julliard's avatar
Alexandre Julliard committed
160 161 162 163 164
/***********************************************************************
 *           AbortPath16    (GDI.511)
 */
BOOL16 WINAPI AbortPath16(HDC16 hdc)
{
165
   return (BOOL16)AbortPath((HDC)hdc);
Alexandre Julliard's avatar
Alexandre Julliard committed
166 167 168
}


Alexandre Julliard's avatar
Alexandre Julliard committed
169
/******************************************************************************
170
 * AbortPath [GDI32.1]
Alexandre Julliard's avatar
Alexandre Julliard committed
171 172 173 174 175 176 177 178 179
 * Closes and discards paths from device context
 *
 * NOTES
 *    Check that SetLastError is being called correctly
 *
 * PARAMS
 *    hdc [I] Handle to device context
 *
 * RETURNS STD
Alexandre Julliard's avatar
Alexandre Julliard committed
180
 */
181
BOOL WINAPI AbortPath( HDC hdc )
Alexandre Julliard's avatar
Alexandre Julliard committed
182
{
183 184
    BOOL ret = TRUE;
    DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
185
   
186 187 188 189 190 191 192 193
    if(!dc) return FALSE;

    if(dc->funcs->pAbortPath)
        ret = dc->funcs->pAbortPath(dc);
    else /* Remove all entries from the path */
        PATH_EmptyPath( &dc->w.path );
    GDI_ReleaseObj( hdc );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
194 195 196
}


Alexandre Julliard's avatar
Alexandre Julliard committed
197 198 199 200 201
/***********************************************************************
 *           CloseFigure16    (GDI.513)
 */
BOOL16 WINAPI CloseFigure16(HDC16 hdc)
{
202
   return (BOOL16)CloseFigure((HDC)hdc);
Alexandre Julliard's avatar
Alexandre Julliard committed
203 204 205
}


Alexandre Julliard's avatar
Alexandre Julliard committed
206
/***********************************************************************
207
 *           CloseFigure    (GDI32.16)
Alexandre Julliard's avatar
Alexandre Julliard committed
208 209
 *
 * FIXME: Check that SetLastError is being called correctly 
Alexandre Julliard's avatar
Alexandre Julliard committed
210
 */
211
BOOL WINAPI CloseFigure(HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
212
{
213 214
    BOOL ret = TRUE;
    DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
215
   
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    if(!dc) return FALSE;

    if(dc->funcs->pCloseFigure)
        ret = dc->funcs->pCloseFigure(dc);
    else
    {
        /* Check that path is open */
        if(dc->w.path.state!=PATH_Open)
        {
            SetLastError(ERROR_CAN_NOT_COMPLETE);
            ret = FALSE;
        }
        else
        {
            /* FIXME: Shouldn't we draw a line to the beginning of the
               figure? */
            /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
            if(dc->w.path.numEntriesUsed)
            {
                dc->w.path.pFlags[dc->w.path.numEntriesUsed-1]|=PT_CLOSEFIGURE;
                dc->w.path.newStroke=TRUE;
            }
        }
    }
    GDI_ReleaseObj( hdc );
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
242 243 244
}


Alexandre Julliard's avatar
Alexandre Julliard committed
245 246 247 248 249 250
/***********************************************************************
 *           GetPath16    (GDI.517)
 */
INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
   INT16 nSize)
{
251
   FIXME("(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
Alexandre Julliard's avatar
Alexandre Julliard committed
252 253 254 255 256

   return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
257
/***********************************************************************
258
 *           GetPath    (GDI32.210)
Alexandre Julliard's avatar
Alexandre Julliard committed
259
 */
260 261
INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
   INT nSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
262
{
263
   INT ret = -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
264
   GdiPath *pPath;
265
   DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
266
   
267 268 269
   if(!dc) return -1;
   
   pPath = &dc->w.path;
Alexandre Julliard's avatar
Alexandre Julliard committed
270 271 272 273 274
   
   /* Check that path is closed */
   if(pPath->state!=PATH_Closed)
   {
      SetLastError(ERROR_CAN_NOT_COMPLETE);
275
      goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
276 277 278
   }
   
   if(nSize==0)
279
      ret = pPath->numEntriesUsed;
Alexandre Julliard's avatar
Alexandre Julliard committed
280 281 282
   else if(nSize<pPath->numEntriesUsed)
   {
      SetLastError(ERROR_INVALID_PARAMETER);
283
      goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
284 285 286
   }
   else
   {
287
      memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
Alexandre Julliard's avatar
Alexandre Julliard committed
288 289 290
      memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);

      /* Convert the points to logical coordinates */
291
      if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
Alexandre Julliard's avatar
Alexandre Julliard committed
292 293 294
      {
	 /* FIXME: Is this the correct value? */
         SetLastError(ERROR_CAN_NOT_COMPLETE);
295
	goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
296
      }
297
     else ret = pPath->numEntriesUsed;
Alexandre Julliard's avatar
Alexandre Julliard committed
298
   }
299 300 301
 done:
   GDI_ReleaseObj( hdc );
   return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
302 303
}

304 305 306 307 308 309 310
/***********************************************************************
 *           PathToRegion16    (GDI.518)
 */
HRGN16 WINAPI PathToRegion16(HDC16 hdc)
{
  return (HRGN16) PathToRegion((HDC) hdc);
}
Alexandre Julliard's avatar
Alexandre Julliard committed
311 312

/***********************************************************************
313
 *           PathToRegion    (GDI32.261)
Alexandre Julliard's avatar
Alexandre Julliard committed
314 315 316 317 318
 *
 * FIXME 
 *   Check that SetLastError is being called correctly 
 *
 * The documentation does not state this explicitly, but a test under Windows
Alexandre Julliard's avatar
Alexandre Julliard committed
319 320
 * shows that the region which is returned should be in device coordinates.
 */
321
HRGN WINAPI PathToRegion(HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
322 323
{
   GdiPath *pPath;
324 325
   HRGN  hrgnRval = 0;
   DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
326 327

   /* Get pointer to path */
328 329 330
   if(!dc) return -1;
   
    pPath = &dc->w.path;
Alexandre Julliard's avatar
Alexandre Julliard committed
331 332
   
   /* Check that path is closed */
333 334
   if(pPath->state!=PATH_Closed) SetLastError(ERROR_CAN_NOT_COMPLETE);
   else
Alexandre Julliard's avatar
Alexandre Julliard committed
335
   {
336 337 338 339 340
       /* FIXME: Should we empty the path even if conversion failed? */
       if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
           PATH_EmptyPath(pPath);
       else
           hrgnRval=0;
Alexandre Julliard's avatar
Alexandre Julliard committed
341
   }
342
   GDI_ReleaseObj( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
343 344 345
   return hrgnRval;
}

346
static BOOL PATH_FillPath(DC *dc, GdiPath *pPath)
Alexandre Julliard's avatar
Alexandre Julliard committed
347
{
348 349 350
   INT   mapMode, graphicsMode;
   SIZE  ptViewportExt, ptWindowExt;
   POINT ptViewportOrg, ptWindowOrg;
351
   XFORM xform;
352
   HRGN  hrgn;
353

354 355 356
   if(dc->funcs->pFillPath)
       return dc->funcs->pFillPath(dc);

Alexandre Julliard's avatar
Alexandre Julliard committed
357 358 359 360 361 362 363 364
   /* Check that path is closed */
   if(pPath->state!=PATH_Closed)
   {
      SetLastError(ERROR_CAN_NOT_COMPLETE);
      return FALSE;
   }
   
   /* Construct a region from the path and fill it */
365
   if(PATH_PathToRegion(pPath, dc->w.polyFillMode, &hrgn))
Alexandre Julliard's avatar
Alexandre Julliard committed
366 367 368 369
   {
      /* Since PaintRgn interprets the region as being in logical coordinates
       * but the points we store for the path are already in device
       * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
Alexandre Julliard's avatar
Alexandre Julliard committed
370 371 372
       * Using SaveDC to save information about the mapping mode / world
       * transform would be easier but would require more overhead, especially
       * now that SaveDC saves the current path.
Alexandre Julliard's avatar
Alexandre Julliard committed
373 374 375
       */
       
      /* Save the information about the old mapping mode */
376 377 378 379 380
      mapMode=GetMapMode(dc->hSelf);
      GetViewportExtEx(dc->hSelf, &ptViewportExt);
      GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
      GetWindowExtEx(dc->hSelf, &ptWindowExt);
      GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
Alexandre Julliard's avatar
Alexandre Julliard committed
381
      
Alexandre Julliard's avatar
Alexandre Julliard committed
382 383 384 385 386
      /* Save world transform
       * NB: The Windows documentation on world transforms would lead one to
       * believe that this has to be done only in GM_ADVANCED; however, my
       * tests show that resetting the graphics mode to GM_COMPATIBLE does
       * not reset the world transform.
Alexandre Julliard's avatar
Alexandre Julliard committed
387
       */
388
      GetWorldTransform(dc->hSelf, &xform);
Alexandre Julliard's avatar
Alexandre Julliard committed
389
      
Alexandre Julliard's avatar
Alexandre Julliard committed
390
      /* Set MM_TEXT */
391 392 393
      SetMapMode(dc->hSelf, MM_TEXT);
      SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
      SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
394

Alexandre Julliard's avatar
Alexandre Julliard committed
395
      /* Paint the region */
396
      PaintRgn(dc->hSelf, hrgn);
397
      DeleteObject(hrgn);
Alexandre Julliard's avatar
Alexandre Julliard committed
398
      /* Restore the old mapping mode */
399 400 401 402 403
      SetMapMode(dc->hSelf, mapMode);
      SetViewportExtEx(dc->hSelf, ptViewportExt.cx, ptViewportExt.cy, NULL);
      SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
      SetWindowExtEx(dc->hSelf, ptWindowExt.cx, ptWindowExt.cy, NULL);
      SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
404

Alexandre Julliard's avatar
Alexandre Julliard committed
405
      /* Go to GM_ADVANCED temporarily to restore the world transform */
406 407 408 409
      graphicsMode=GetGraphicsMode(dc->hSelf);
      SetGraphicsMode(dc->hSelf, GM_ADVANCED);
      SetWorldTransform(dc->hSelf, &xform);
      SetGraphicsMode(dc->hSelf, graphicsMode);
Alexandre Julliard's avatar
Alexandre Julliard committed
410 411
      return TRUE;
   }
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
   return FALSE;
}

/***********************************************************************
 *           FillPath16    (GDI.515)
 */
BOOL16 WINAPI FillPath16(HDC16 hdc)
{
  return (BOOL16) FillPath((HDC) hdc); 
}

/***********************************************************************
 *           FillPath    (GDI32.100)
 *
 * FIXME
 *    Check that SetLastError is being called correctly 
 */
BOOL WINAPI FillPath(HDC hdc)
{
431 432
    DC *dc = DC_GetDCPtr( hdc );
    BOOL bRet = FALSE;
433
   
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    if(!dc) return FALSE;

    if(dc->funcs->pFillPath)
        bRet = dc->funcs->pFillPath(dc);
    else
    {
        bRet = PATH_FillPath(dc, &dc->w.path);
        if(bRet)
        {
            /* FIXME: Should the path be emptied even if conversion
               failed? */
            PATH_EmptyPath(&dc->w.path);
        }
    }
    GDI_ReleaseObj( hdc );
    return bRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
450 451
}

452 453 454 455 456 457 458
/***********************************************************************
 *           SelectClipPath16    (GDI.519)
 */
BOOL16 WINAPI SelectClipPath16(HDC16 hdc, INT16 iMode)
{
  return (BOOL16) SelectClipPath((HDC) hdc, iMode);
}
Alexandre Julliard's avatar
Alexandre Julliard committed
459 460

/***********************************************************************
461
 *           SelectClipPath    (GDI32.296)
Alexandre Julliard's avatar
Alexandre Julliard committed
462 463
 * FIXME 
 *  Check that SetLastError is being called correctly 
Alexandre Julliard's avatar
Alexandre Julliard committed
464
 */
465
BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
Alexandre Julliard's avatar
Alexandre Julliard committed
466 467
{
   GdiPath *pPath;
468
   HRGN  hrgnPath;
469
   BOOL  success = FALSE;
470
   DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
471
   
472
   if(!dc) return FALSE;
473 474

   if(dc->funcs->pSelectClipPath)
475 476
     success = dc->funcs->pSelectClipPath(dc, iMode);
   else
Alexandre Julliard's avatar
Alexandre Julliard committed
477
   {
478
       pPath = &dc->w.path;
Alexandre Julliard's avatar
Alexandre Julliard committed
479
   
480 481 482 483 484 485 486 487 488 489 490 491 492 493
       /* Check that path is closed */
       if(pPath->state!=PATH_Closed)
           SetLastError(ERROR_CAN_NOT_COMPLETE);
       /* Construct a region from the path */
       else if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
       {
           success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
           DeleteObject(hrgnPath);

           /* Empty the path */
           if(success)
               PATH_EmptyPath(pPath);
           /* FIXME: Should this function delete the path even if it failed? */
       }
Alexandre Julliard's avatar
Alexandre Julliard committed
494
   }
495 496
   GDI_ReleaseObj( hdc );
   return success;
Alexandre Julliard's avatar
Alexandre Julliard committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
}


/***********************************************************************
 * Exported functions
 */

/* PATH_InitGdiPath
 *
 * Initializes the GdiPath structure.
 */
void PATH_InitGdiPath(GdiPath *pPath)
{
   assert(pPath!=NULL);

   pPath->state=PATH_Null;
   pPath->pPoints=NULL;
   pPath->pFlags=NULL;
   pPath->numEntriesUsed=0;
   pPath->numEntriesAllocated=0;
}

/* PATH_DestroyGdiPath
 *
 * Destroys a GdiPath structure (frees the memory in the arrays).
 */
void PATH_DestroyGdiPath(GdiPath *pPath)
{
   assert(pPath!=NULL);

527 528
   if (pPath->pPoints) HeapFree( GetProcessHeap(), 0, pPath->pPoints );
   if (pPath->pFlags) HeapFree( GetProcessHeap(), 0, pPath->pFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
529 530 531 532 533 534 535 536 537 538 539 540
}

/* PATH_AssignGdiPath
 *
 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
 * not just the pointers. Since this means that the arrays in pPathDest may
 * need to be resized, pPathDest should have been initialized using
 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
 * not a copy constructor).
 * Returns TRUE if successful, else FALSE.
 */
541
BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
Alexandre Julliard's avatar
Alexandre Julliard committed
542 543 544 545 546 547 548 549 550
{
   assert(pPathDest!=NULL && pPathSrc!=NULL);

   /* Make sure destination arrays are big enough */
   if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
      return FALSE;

   /* Perform the copy operation */
   memcpy(pPathDest->pPoints, pPathSrc->pPoints,
551
      sizeof(POINT)*pPathSrc->numEntriesUsed);
Alexandre Julliard's avatar
Alexandre Julliard committed
552
   memcpy(pPathDest->pFlags, pPathSrc->pFlags,
553 554
      sizeof(BYTE)*pPathSrc->numEntriesUsed);

Alexandre Julliard's avatar
Alexandre Julliard committed
555 556 557 558 559 560 561 562 563 564 565 566 567
   pPathDest->state=pPathSrc->state;
   pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
   pPathDest->newStroke=pPathSrc->newStroke;

   return TRUE;
}

/* PATH_MoveTo
 *
 * Should be called when a MoveTo is performed on a DC that has an
 * open path. This starts a new stroke. Returns TRUE if successful, else
 * FALSE.
 */
568
BOOL PATH_MoveTo(DC *dc)
Alexandre Julliard's avatar
Alexandre Julliard committed
569
{
570
   GdiPath *pPath = &dc->w.path;
Alexandre Julliard's avatar
Alexandre Julliard committed
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
   
   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      /* FIXME: Do we have to call SetLastError? */
      return FALSE;

   /* Start a new stroke */
   pPath->newStroke=TRUE;

   return TRUE;
}

/* PATH_LineTo
 *
 * Should be called when a LineTo is performed on a DC that has an
 * open path. This adds a PT_LINETO entry to the path (and possibly
 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
 * Returns TRUE if successful, else FALSE.
 */
590
BOOL PATH_LineTo(DC *dc, INT x, INT y)
Alexandre Julliard's avatar
Alexandre Julliard committed
591
{
592
   GdiPath *pPath = &dc->w.path;
593
   POINT point, pointCurPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
594 595 596 597 598 599 600 601
   
   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   /* Convert point to device coordinates */
   point.x=x;
   point.y=y;
602
   if(!LPtoDP(dc->hSelf, &point, 1))
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604 605 606 607 608
      return FALSE;
   
   /* Add a PT_MOVETO if necessary */
   if(pPath->newStroke)
   {
      pPath->newStroke=FALSE;
609 610 611
      pointCurPos.x = dc->w.CursPosX;
      pointCurPos.y = dc->w.CursPosY;
      if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
Alexandre Julliard's avatar
Alexandre Julliard committed
612
         return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
613
      if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
Alexandre Julliard's avatar
Alexandre Julliard committed
614 615 616 617
         return FALSE;
   }
   
   /* Add a PT_LINETO entry */
Alexandre Julliard's avatar
Alexandre Julliard committed
618 619 620 621 622 623 624 625
   return PATH_AddEntry(pPath, &point, PT_LINETO);
}

/* PATH_Rectangle
 *
 * Should be called when a call to Rectangle is performed on a DC that has
 * an open path. Returns TRUE if successful, else FALSE.
 */
626
BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2)
Alexandre Julliard's avatar
Alexandre Julliard committed
627
{
628
   GdiPath *pPath = &dc->w.path;
629 630
   POINT corners[2], pointTemp;
   INT   temp;
Alexandre Julliard's avatar
Alexandre Julliard committed
631 632 633 634 635 636 637 638 639 640

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   /* Convert points to device coordinates */
   corners[0].x=x1;
   corners[0].y=y1;
   corners[1].x=x2;
   corners[1].y=y2;
641
   if(!LPtoDP(dc->hSelf, corners, 2))
Alexandre Julliard's avatar
Alexandre Julliard committed
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
      return FALSE;
   
   /* Make sure first corner is top left and second corner is bottom right */
   if(corners[0].x>corners[1].x)
   {
      temp=corners[0].x;
      corners[0].x=corners[1].x;
      corners[1].x=temp;
   }
   if(corners[0].y>corners[1].y)
   {
      temp=corners[0].y;
      corners[0].y=corners[1].y;
      corners[1].y=temp;
   }
   
   /* In GM_COMPATIBLE, don't include bottom and right edges */
659
   if(dc->w.GraphicsMode==GM_COMPATIBLE)
Alexandre Julliard's avatar
Alexandre Julliard committed
660 661 662 663 664 665
   {
      corners[1].x--;
      corners[1].y--;
   }

   /* Close any previous figure */
666
   if(!CloseFigure(dc->hSelf))
Alexandre Julliard's avatar
Alexandre Julliard committed
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
   {
      /* The CloseFigure call shouldn't have failed */
      assert(FALSE);
      return FALSE;
   }

   /* Add four points to the path */
   pointTemp.x=corners[1].x;
   pointTemp.y=corners[0].y;
   if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
      return FALSE;
   if(!PATH_AddEntry(pPath, corners, PT_LINETO))
      return FALSE;
   pointTemp.x=corners[0].x;
   pointTemp.y=corners[1].y;
   if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
      return FALSE;
   if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
      return FALSE;

   /* Close the rectangle figure */
688
   if(!CloseFigure(dc->hSelf))
Alexandre Julliard's avatar
Alexandre Julliard committed
689 690 691 692 693 694 695
   {
      /* The CloseFigure call shouldn't have failed */
      assert(FALSE);
      return FALSE;
   }

   return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
696 697
}

Alexandre Julliard's avatar
Alexandre Julliard committed
698 699 700 701 702 703
/* PATH_Ellipse
 * 
 * Should be called when a call to Ellipse is performed on a DC that has
 * an open path. This adds four Bezier splines representing the ellipse
 * to the path. Returns TRUE if successful, else FALSE.
 */
704
BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2)
Alexandre Julliard's avatar
Alexandre Julliard committed
705
{
706 707
   /* TODO: This should probably be revised to call PATH_AngleArc */
   /* (once it exists) */
708
   return PATH_Arc(dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
Alexandre Julliard's avatar
Alexandre Julliard committed
709 710 711 712 713 714 715 716
}

/* PATH_Arc
 *
 * Should be called when a call to Arc is performed on a DC that has
 * an open path. This adds up to five Bezier splines representing the arc
 * to the path. Returns TRUE if successful, else FALSE.
 */
717
BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
718
   INT xStart, INT yStart, INT xEnd, INT yEnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
719
{
720
   GdiPath     *pPath = &dc->w.path;
Alexandre Julliard's avatar
Alexandre Julliard committed
721 722 723 724
   double      angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
               /* Initialize angleEndQuadrant to silence gcc's warning */
   double      x, y;
   FLOAT_POINT corners[2], pointStart, pointEnd;
725 726
   BOOL      start, end;
   INT       temp;
Alexandre Julliard's avatar
Alexandre Julliard committed
727 728

   /* FIXME: This function should check for all possible error returns */
Alexandre Julliard's avatar
Alexandre Julliard committed
729
   /* FIXME: Do we have to respect newStroke? */
Alexandre Julliard's avatar
Alexandre Julliard committed
730 731 732 733 734
   
   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
735 736
   /* FIXME: Do we have to close the current figure? */
   
Alexandre Julliard's avatar
Alexandre Julliard committed
737
   /* Check for zero height / width */
Alexandre Julliard's avatar
Alexandre Julliard committed
738
   /* FIXME: Only in GM_COMPATIBLE? */
Alexandre Julliard's avatar
Alexandre Julliard committed
739 740 741 742
   if(x1==x2 || y1==y2)
      return TRUE;
   
   /* Convert points to device coordinates */
Alexandre Julliard's avatar
Alexandre Julliard committed
743 744 745 746 747 748 749 750
   corners[0].x=(FLOAT)x1;
   corners[0].y=(FLOAT)y1;
   corners[1].x=(FLOAT)x2;
   corners[1].y=(FLOAT)y2;
   pointStart.x=(FLOAT)xStart;
   pointStart.y=(FLOAT)yStart;
   pointEnd.x=(FLOAT)xEnd;
   pointEnd.y=(FLOAT)yEnd;
751 752 753 754
   INTERNAL_LPTODP_FLOAT(dc, corners);
   INTERNAL_LPTODP_FLOAT(dc, corners+1);
   INTERNAL_LPTODP_FLOAT(dc, &pointStart);
   INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
755 756

   /* Make sure first corner is top left and second corner is bottom right */
Alexandre Julliard's avatar
Alexandre Julliard committed
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
   if(corners[0].x>corners[1].x)
   {
      temp=corners[0].x;
      corners[0].x=corners[1].x;
      corners[1].x=temp;
   }
   if(corners[0].y>corners[1].y)
   {
      temp=corners[0].y;
      corners[0].y=corners[1].y;
      corners[1].y=temp;
   }

   /* Compute start and end angle */
   PATH_NormalizePoint(corners, &pointStart, &x, &y);
   angleStart=atan2(y, x);
   PATH_NormalizePoint(corners, &pointEnd, &x, &y);
   angleEnd=atan2(y, x);

   /* Make sure the end angle is "on the right side" of the start angle */
777
   if(dc->w.ArcDirection==AD_CLOCKWISE)
Alexandre Julliard's avatar
Alexandre Julliard committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
   {
      if(angleEnd<=angleStart)
      {
         angleEnd+=2*M_PI;
	 assert(angleEnd>=angleStart);
      }
   }
   else
   {
      if(angleEnd>=angleStart)
      {
         angleEnd-=2*M_PI;
	 assert(angleEnd<=angleStart);
      }
   }

Alexandre Julliard's avatar
Alexandre Julliard committed
794
   /* In GM_COMPATIBLE, don't include bottom and right edges */
795
   if(dc->w.GraphicsMode==GM_COMPATIBLE)
Alexandre Julliard's avatar
Alexandre Julliard committed
796 797 798 799 800
   {
      corners[1].x--;
      corners[1].y--;
   }
   
Alexandre Julliard's avatar
Alexandre Julliard committed
801 802 803 804 805 806 807 808 809 810
   /* Add the arc to the path with one Bezier spline per quadrant that the
    * arc spans */
   start=TRUE;
   end=FALSE;
   do
   {
      /* Determine the start and end angles for this quadrant */
      if(start)
      {
         angleStartQuadrant=angleStart;
811
	 if(dc->w.ArcDirection==AD_CLOCKWISE)
Alexandre Julliard's avatar
Alexandre Julliard committed
812 813 814 815 816 817 818
	    angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
	 else
	    angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
      }
      else
      {
	 angleStartQuadrant=angleEndQuadrant;
819
	 if(dc->w.ArcDirection==AD_CLOCKWISE)
Alexandre Julliard's avatar
Alexandre Julliard committed
820 821 822 823 824 825
	    angleEndQuadrant+=M_PI_2;
	 else
	    angleEndQuadrant-=M_PI_2;
      }

      /* Have we reached the last part of the arc? */
826
      if((dc->w.ArcDirection==AD_CLOCKWISE &&
Alexandre Julliard's avatar
Alexandre Julliard committed
827
         angleEnd<angleEndQuadrant) ||
828
	 (dc->w.ArcDirection==AD_COUNTERCLOCKWISE &&
Alexandre Julliard's avatar
Alexandre Julliard committed
829
	 angleEnd>angleEndQuadrant))
Alexandre Julliard's avatar
Alexandre Julliard committed
830 831 832 833 834 835 836 837 838 839 840 841 842 843
      {
	 /* Adjust the end angle for this quadrant */
         angleEndQuadrant=angleEnd;
	 end=TRUE;
      }

      /* Add the Bezier spline to the path */
      PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
         start);
      start=FALSE;
   }  while(!end);

   return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
844

845
BOOL PATH_PolyBezierTo(DC *dc, const POINT *pts, DWORD cbPoints)
846
{
847
   GdiPath     *pPath = &dc->w.path;
848 849 850 851 852 853 854 855 856 857 858
   POINT       pt;
   INT         i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   /* Add a PT_MOVETO if necessary */
   if(pPath->newStroke)
   {
      pPath->newStroke=FALSE;
859 860 861
      pt.x = dc->w.CursPosX;
      pt.y = dc->w.CursPosY;
      if(!LPtoDP(dc->hSelf, &pt, 1))
862 863 864 865
         return FALSE;
      if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
         return FALSE;
   }
866

867 868
   for(i = 0; i < cbPoints; i++) {
       pt = pts[i];
869
       if(!LPtoDP(dc->hSelf, &pt, 1))
870 871 872 873 874 875
	   return FALSE;
       PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
   }
   return TRUE;
}
   
876
BOOL PATH_PolyBezier(DC *dc, const POINT *pts, DWORD cbPoints)
877
{
878
   GdiPath     *pPath = &dc->w.path;
879 880 881 882 883 884 885 886 887
   POINT       pt;
   INT         i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   for(i = 0; i < cbPoints; i++) {
       pt = pts[i];
888
       if(!LPtoDP(dc->hSelf, &pt, 1))
889 890 891 892 893 894
	   return FALSE;
       PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
   }
   return TRUE;
}

895
BOOL PATH_Polyline(DC *dc, const POINT *pts, DWORD cbPoints)
896
{
897
   GdiPath     *pPath = &dc->w.path;
898 899 900 901 902 903 904 905 906
   POINT       pt;
   INT         i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   for(i = 0; i < cbPoints; i++) {
       pt = pts[i];
907
       if(!LPtoDP(dc->hSelf, &pt, 1))
908 909 910 911 912 913
	   return FALSE;
       PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
   }
   return TRUE;
}
   
914
BOOL PATH_PolylineTo(DC *dc, const POINT *pts, DWORD cbPoints)
915
{
916
   GdiPath     *pPath = &dc->w.path;
917 918 919 920 921 922 923 924 925 926 927
   POINT       pt;
   INT         i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   /* Add a PT_MOVETO if necessary */
   if(pPath->newStroke)
   {
      pPath->newStroke=FALSE;
928 929 930
      pt.x = dc->w.CursPosX;
      pt.y = dc->w.CursPosY;
      if(!LPtoDP(dc->hSelf, &pt, 1))
931 932 933 934 935 936 937
         return FALSE;
      if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
         return FALSE;
   }

   for(i = 0; i < cbPoints; i++) {
       pt = pts[i];
938
       if(!LPtoDP(dc->hSelf, &pt, 1))
939 940 941 942 943 944 945 946
	   return FALSE;
       PATH_AddEntry(pPath, &pt, PT_LINETO);
   }

   return TRUE;
}


947
BOOL PATH_Polygon(DC *dc, const POINT *pts, DWORD cbPoints)
948
{
949
   GdiPath     *pPath = &dc->w.path;
950 951 952 953 954 955 956 957 958
   POINT       pt;
   INT         i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   for(i = 0; i < cbPoints; i++) {
       pt = pts[i];
959
       if(!LPtoDP(dc->hSelf, &pt, 1))
960 961 962 963 964 965 966 967
	   return FALSE;
       PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
		     ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
		      PT_LINETO));
   }
   return TRUE;
}

968
BOOL PATH_PolyPolygon( DC *dc, const POINT* pts, const INT* counts,
969 970
		       UINT polygons )
{
971
   GdiPath     *pPath = &dc->w.path;
972 973 974 975 976 977 978 979 980 981
   POINT       pt, startpt;
   INT         poly, point, i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   for(i = 0, poly = 0; poly < polygons; poly++) {
       for(point = 0; point < counts[poly]; point++, i++) {
	   pt = pts[i];
982
	   if(!LPtoDP(dc->hSelf, &pt, 1))
983 984 985 986 987 988 989 990 991 992
	       return FALSE;
	   if(point == 0) startpt = pt;
	   PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
       }
       /* win98 adds an extra line to close the figure for some reason */
       PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
   }
   return TRUE;
}

993
BOOL PATH_PolyPolyline( DC *dc, const POINT* pts, const DWORD* counts,
994 995
			DWORD polylines )
{
996
   GdiPath     *pPath = &dc->w.path;
997 998 999 1000 1001 1002 1003 1004 1005 1006
   POINT       pt;
   INT         poly, point, i;

   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;

   for(i = 0, poly = 0; poly < polylines; poly++) {
       for(point = 0; point < counts[poly]; point++, i++) {
	   pt = pts[i];
1007
	   if(!LPtoDP(dc->hSelf, &pt, 1))
1008 1009 1010 1011 1012 1013 1014
	       return FALSE;
	   PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
       }
   }
   return TRUE;
}
   
Alexandre Julliard's avatar
Alexandre Julliard committed
1015 1016 1017 1018
/***********************************************************************
 * Internal functions
 */

1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069

/* PATH_AddFlatBezier
 *
 */
static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
{
    POINT *pts;
    INT no, i;

    pts = GDI_Bezier( pt, 4, &no );
    if(!pts) return FALSE;

    for(i = 1; i < no; i++)
        PATH_AddEntry(pPath, &pts[i], 
	    (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
    HeapFree( GetProcessHeap(), 0, pts );
    return TRUE;
}

/* PATH_FlattenPath
 *
 * Replaces Beziers with line segments
 *
 */
static BOOL PATH_FlattenPath(GdiPath *pPath)
{
    GdiPath newPath;
    INT srcpt;

    memset(&newPath, 0, sizeof(newPath));
    newPath.state = PATH_Open;
    for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
        switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
	case PT_MOVETO:
	case PT_LINETO:
	    PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
			  pPath->pFlags[srcpt]);
	    break;
	case PT_BEZIERTO:
	  PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
			     pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
	    srcpt += 2;
	    break;
	}
    }
    newPath.state = PATH_Closed;
    PATH_AssignGdiPath(pPath, &newPath);
    PATH_EmptyPath(&newPath);
    return TRUE;
}
	  
Alexandre Julliard's avatar
Alexandre Julliard committed
1070 1071 1072 1073 1074 1075 1076 1077
/* PATH_PathToRegion
 *
 * Creates a region from the specified path using the specified polygon
 * filling mode. The path is left unchanged. A handle to the region that
 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
 * error occurs, SetLastError is called with the appropriate value and
 * FALSE is returned.
 */
1078
static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
1079
   HRGN *pHrgn)
Alexandre Julliard's avatar
Alexandre Julliard committed
1080 1081
{
   int    numStrokes, iStroke, i;
1082 1083
   INT  *pNumPointsInStroke;
   HRGN hrgn;
Alexandre Julliard's avatar
Alexandre Julliard committed
1084 1085 1086

   assert(pPath!=NULL);
   assert(pHrgn!=NULL);
1087

1088
   PATH_FlattenPath(pPath);
1089

Alexandre Julliard's avatar
Alexandre Julliard committed
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
   /* FIXME: What happens when number of points is zero? */
   
   /* First pass: Find out how many strokes there are in the path */
   /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
   numStrokes=0;
   for(i=0; i<pPath->numEntriesUsed; i++)
      if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
         numStrokes++;

   /* Allocate memory for number-of-points-in-stroke array */
1100 1101
   pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0, 
					sizeof(int) * numStrokes );
Alexandre Julliard's avatar
Alexandre Julliard committed
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
   if(!pNumPointsInStroke)
   {
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }
   
   /* Second pass: remember number of points in each polygon */
   iStroke=-1;  /* Will get incremented to 0 at beginning of first stroke */
   for(i=0; i<pPath->numEntriesUsed; i++)
   {
      /* Is this the beginning of a new stroke? */
      if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
      {
         iStroke++;
	 pNumPointsInStroke[iStroke]=0;
      }

      pNumPointsInStroke[iStroke]++;
   }

   /* Create a region from the strokes */
1123
   hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
Alexandre Julliard's avatar
Alexandre Julliard committed
1124
      numStrokes, nPolyFillMode);
1125
   if(hrgn==(HRGN)0)
Alexandre Julliard's avatar
Alexandre Julliard committed
1126 1127 1128 1129 1130 1131
   {
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }

   /* Free memory for number-of-points-in-stroke array */
1132
   HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
Alexandre Julliard's avatar
Alexandre Julliard committed
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156

   /* Success! */
   *pHrgn=hrgn;
   return TRUE;
}

/* PATH_EmptyPath
 *
 * Removes all entries from the path and sets the path state to PATH_Null.
 */
static void PATH_EmptyPath(GdiPath *pPath)
{
   assert(pPath!=NULL);

   pPath->state=PATH_Null;
   pPath->numEntriesUsed=0;
}

/* PATH_AddEntry
 *
 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
 * successful, FALSE otherwise (e.g. if not enough memory was available).
 */
1157
BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
Alexandre Julliard's avatar
Alexandre Julliard committed
1158 1159 1160
{
   assert(pPath!=NULL);
   
Alexandre Julliard's avatar
Alexandre Julliard committed
1161 1162 1163
   /* FIXME: If newStroke is true, perhaps we want to check that we're
    * getting a PT_MOVETO
    */
1164
   TRACE("(%ld,%ld) - %d\n", pPoint->x, pPoint->y, flags);
Alexandre Julliard's avatar
Alexandre Julliard committed
1165

Alexandre Julliard's avatar
Alexandre Julliard committed
1166 1167 1168 1169 1170 1171 1172 1173 1174
   /* Check that path is open */
   if(pPath->state!=PATH_Open)
      return FALSE;
   
   /* Reserve enough memory for an extra path entry */
   if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
      return FALSE;

   /* Store information in path entry */
Alexandre Julliard's avatar
Alexandre Julliard committed
1175
   pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
Alexandre Julliard's avatar
Alexandre Julliard committed
1176 1177
   pPath->pFlags[pPath->numEntriesUsed]=flags;

Alexandre Julliard's avatar
Alexandre Julliard committed
1178 1179 1180 1181
   /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
   if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
      pPath->newStroke=TRUE;

Alexandre Julliard's avatar
Alexandre Julliard committed
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
   /* Increment entry count */
   pPath->numEntriesUsed++;

   return TRUE;
}

/* PATH_ReserveEntries
 *
 * Ensures that at least "numEntries" entries (for points and flags) have
 * been allocated; allocates larger arrays and copies the existing entries
 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
 */
1194
static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
Alexandre Julliard's avatar
Alexandre Julliard committed
1195
{
1196 1197
   INT   numEntriesToAllocate;
   POINT *pPointsNew;
Alexandre Julliard's avatar
Alexandre Julliard committed
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
   BYTE    *pFlagsNew;
   
   assert(pPath!=NULL);
   assert(numEntries>=0);

   /* Do we have to allocate more memory? */
   if(numEntries > pPath->numEntriesAllocated)
   {
      /* Find number of entries to allocate. We let the size of the array
       * grow exponentially, since that will guarantee linear time
       * complexity. */
      if(pPath->numEntriesAllocated)
      {
	 numEntriesToAllocate=pPath->numEntriesAllocated;
	 while(numEntriesToAllocate<numEntries)
	    numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
	       GROW_FACTOR_DENOM;
      }
      else
1217
         numEntriesToAllocate=numEntries;
Alexandre Julliard's avatar
Alexandre Julliard committed
1218 1219

      /* Allocate new arrays */
1220 1221
      pPointsNew=(POINT *)HeapAlloc( GetProcessHeap(), 0, 
				     numEntriesToAllocate * sizeof(POINT) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1222 1223
      if(!pPointsNew)
         return FALSE;
1224 1225
      pFlagsNew=(BYTE *)HeapAlloc( GetProcessHeap(), 0, 
				   numEntriesToAllocate * sizeof(BYTE) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1226 1227
      if(!pFlagsNew)
      {
1228
         HeapFree( GetProcessHeap(), 0, pPointsNew );
Alexandre Julliard's avatar
Alexandre Julliard committed
1229 1230 1231 1232 1233 1234 1235 1236 1237
	 return FALSE;
      }

      /* Copy old arrays to new arrays and discard old arrays */
      if(pPath->pPoints)
      {
         assert(pPath->pFlags);

	 memcpy(pPointsNew, pPath->pPoints,
1238
	     sizeof(POINT)*pPath->numEntriesUsed);
Alexandre Julliard's avatar
Alexandre Julliard committed
1239 1240 1241
	 memcpy(pFlagsNew, pPath->pFlags,
	     sizeof(BYTE)*pPath->numEntriesUsed);

1242 1243
	 HeapFree( GetProcessHeap(), 0, pPath->pPoints );
	 HeapFree( GetProcessHeap(), 0, pPath->pFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
1244 1245 1246 1247 1248 1249 1250 1251 1252
      }
      pPath->pPoints=pPointsNew;
      pPath->pFlags=pFlagsNew;
      pPath->numEntriesAllocated=numEntriesToAllocate;
   }

   return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1253 1254 1255 1256 1257 1258 1259 1260 1261
/* PATH_DoArcPart
 *
 * Creates a Bezier spline that corresponds to part of an arc and appends the
 * corresponding points to the path. The start and end angles are passed in
 * "angleStart" and "angleEnd"; these angles should span a quarter circle
 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
 * point is added to the path; otherwise, it is assumed that the current
 * position is equal to the first control point.
 */
1262 1263
static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
   double angleStart, double angleEnd, BOOL addMoveTo)
Alexandre Julliard's avatar
Alexandre Julliard committed
1264 1265 1266
{
   double  halfAngle, a;
   double  xNorm[4], yNorm[4];
1267
   POINT point;
Alexandre Julliard's avatar
Alexandre Julliard committed
1268 1269 1270 1271 1272 1273 1274 1275
   int     i;

   assert(fabs(angleEnd-angleStart)<=M_PI_2);

   /* FIXME: Is there an easier way of computing this? */

   /* Compute control points */
   halfAngle=(angleEnd-angleStart)/2.0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
   if(fabs(halfAngle)>1e-8)
   {
      a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
      xNorm[0]=cos(angleStart);
      yNorm[0]=sin(angleStart);
      xNorm[1]=xNorm[0] - a*yNorm[0];
      yNorm[1]=yNorm[0] + a*xNorm[0];
      xNorm[3]=cos(angleEnd);
      yNorm[3]=sin(angleEnd);
      xNorm[2]=xNorm[3] + a*yNorm[3];
      yNorm[2]=yNorm[3] - a*xNorm[3];
   }
   else
      for(i=0; i<4; i++)
      {
	 xNorm[i]=cos(angleStart);
	 yNorm[i]=sin(angleStart);
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1294 1295 1296 1297 1298
   
   /* Add starting point to path if desired */
   if(addMoveTo)
   {
      PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
Alexandre Julliard's avatar
Alexandre Julliard committed
1299
      if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
Alexandre Julliard's avatar
Alexandre Julliard committed
1300 1301 1302 1303 1304 1305 1306
         return FALSE;
   }

   /* Add remaining control points */
   for(i=1; i<4; i++)
   {
      PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
Alexandre Julliard's avatar
Alexandre Julliard committed
1307
      if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
Alexandre Julliard's avatar
Alexandre Julliard committed
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
         return FALSE;
   }

   return TRUE;
}

/* PATH_ScaleNormalizedPoint
 *
 * Scales a normalized point (x, y) with respect to the box whose corners are
 * passed in "corners". The point is stored in "*pPoint". The normalized
 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
 * (1.0, 1.0) correspond to corners[1].
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1321
static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1322
   double y, POINT *pPoint)
Alexandre Julliard's avatar
Alexandre Julliard committed
1323
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1324
   pPoint->x=GDI_ROUND( (double)corners[0].x +
Alexandre Julliard's avatar
Alexandre Julliard committed
1325
      (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1326
   pPoint->y=GDI_ROUND( (double)corners[0].y +
Alexandre Julliard's avatar
Alexandre Julliard committed
1327 1328 1329 1330 1331 1332 1333 1334
      (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
}

/* PATH_NormalizePoint
 *
 * Normalizes a point with respect to the box whose corners are passed in
 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1335 1336
static void PATH_NormalizePoint(FLOAT_POINT corners[],
   const FLOAT_POINT *pPoint,
Alexandre Julliard's avatar
Alexandre Julliard committed
1337 1338 1339 1340 1341 1342 1343
   double *pX, double *pY)
{
   *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
      2.0 - 1.0;
   *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
      2.0 - 1.0;
}
1344

1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
/*******************************************************************
 *      FlattenPath16 [GDI.516]
 *
 *
 */
BOOL16 WINAPI FlattenPath16(HDC16 hdc)
{
  return (BOOL16) FlattenPath((HDC) hdc);
}

1355
/*******************************************************************
1356
 *      FlattenPath [GDI32.103]
1357 1358 1359
 *
 *
 */
1360
BOOL WINAPI FlattenPath(HDC hdc)
1361
{
1362 1363
    BOOL ret = FALSE;
    DC *dc = DC_GetDCPtr( hdc );
1364

1365
    if(!dc) return FALSE;
1366

1367 1368 1369 1370 1371 1372 1373 1374 1375
    if(dc->funcs->pFlattenPath) ret = dc->funcs->pFlattenPath(dc);
    else 
    {
	GdiPath *pPath = &dc->w.path;
        if(pPath->state != PATH_Closed)
	    ret = PATH_FlattenPath(pPath);
    }
    GDI_ReleaseObj( hdc );
    return ret;
1376 1377
}

1378

1379
static BOOL PATH_StrokePath(DC *dc, GdiPath *pPath)
1380 1381 1382 1383
{
    INT i;
    POINT ptLastMove = {0,0};

1384 1385 1386
    if(dc->funcs->pStrokePath)
        return dc->funcs->pStrokePath(dc);

1387 1388 1389
    if(pPath->state != PATH_Closed)
        return FALSE;

1390 1391 1392 1393
    SaveDC(dc->hSelf);
    SetMapMode(dc->hSelf, MM_TEXT);
    SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
    SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
1394 1395 1396 1397 1398
    for(i = 0; i < pPath->numEntriesUsed; i++) {
        switch(pPath->pFlags[i]) {
	case PT_MOVETO:
	    TRACE("Got PT_MOVETO (%ld, %ld)\n",
		  pPath->pPoints[i].x, pPath->pPoints[i].y);
1399
	    MoveToEx(dc->hSelf, pPath->pPoints[i].x, pPath->pPoints[i].y, NULL);
1400 1401 1402 1403 1404 1405
	    ptLastMove = pPath->pPoints[i];
	    break;
	case PT_LINETO:
	case (PT_LINETO | PT_CLOSEFIGURE):
	    TRACE("Got PT_LINETO (%ld, %ld)\n",
		  pPath->pPoints[i].x, pPath->pPoints[i].y);
1406
	    LineTo(dc->hSelf, pPath->pPoints[i].x, pPath->pPoints[i].y);
1407 1408 1409 1410 1411 1412 1413 1414
	    break;
	case PT_BEZIERTO:
	    TRACE("Got PT_BEZIERTO\n");
	    if(pPath->pFlags[i+1] != PT_BEZIERTO || 
	       (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
	        ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
		return FALSE;
	    }
1415
	    PolyBezierTo(dc->hSelf, &pPath->pPoints[i], 3);
1416 1417 1418 1419 1420 1421 1422
	    i += 2;
	    break;
	default:
	    ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
	    return FALSE;
	}
	if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1423
	    LineTo(dc->hSelf, ptLastMove.x, ptLastMove.y);
1424
    }
1425
    RestoreDC(dc->hSelf , -1);
1426 1427 1428 1429
    return TRUE;
}


1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
/*******************************************************************
 *      StrokeAndFillPath16 [GDI.520]
 *
 *
 */
BOOL16 WINAPI StrokeAndFillPath16(HDC16 hdc)
{
  return (BOOL16) StrokeAndFillPath((HDC) hdc);
}

1440 1441 1442 1443 1444
/*******************************************************************
 *      StrokeAndFillPath [GDI32.352]
 *
 *
 */
1445
BOOL WINAPI StrokeAndFillPath(HDC hdc)
1446
{
1447
   DC *dc = DC_GetDCPtr( hdc );
1448
   BOOL bRet = FALSE;
1449

1450
   if(!dc) return FALSE;
1451 1452

   if(dc->funcs->pStrokeAndFillPath)
1453 1454 1455 1456 1457 1458 1459 1460
       bRet = dc->funcs->pStrokeAndFillPath(dc);
   else
   {
       bRet = PATH_FillPath(dc, &dc->w.path);
       if(bRet) bRet = PATH_StrokePath(dc, &dc->w.path);
       if(bRet) PATH_EmptyPath(&dc->w.path);
   }
   GDI_ReleaseObj( hdc );
1461
   return bRet;
1462 1463
}

1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
/*******************************************************************
 *      StrokePath16 [GDI.521]
 *
 *
 */
BOOL16 WINAPI StrokePath16(HDC16 hdc)
{
  return (BOOL16) StrokePath((HDC) hdc);
}

1474 1475 1476 1477 1478
/*******************************************************************
 *      StrokePath [GDI32.353]
 *
 *
 */
1479
BOOL WINAPI StrokePath(HDC hdc)
1480
{
1481 1482
    DC *dc = DC_GetDCPtr( hdc );
    GdiPath *pPath;
1483
    BOOL bRet = FALSE;
1484 1485

    TRACE("(%08x)\n", hdc);
1486
    if(!dc) return FALSE;
1487 1488

    if(dc->funcs->pStrokePath)
1489 1490 1491 1492 1493 1494 1495 1496 1497
        bRet = dc->funcs->pStrokePath(dc);
    else
    {
        pPath = &dc->w.path;
        bRet = PATH_StrokePath(dc, pPath);
        PATH_EmptyPath(pPath);
    }
    GDI_ReleaseObj( hdc );
    return bRet;
1498 1499
}

1500 1501 1502 1503 1504 1505 1506 1507 1508 1509
/*******************************************************************
 *      WidenPath16 [GDI.522]
 *
 *
 */
BOOL16 WINAPI WidenPath16(HDC16 hdc)
{
  return (BOOL16) WidenPath((HDC) hdc);
}

1510 1511 1512 1513 1514
/*******************************************************************
 *      WidenPath [GDI32.360]
 *
 *
 */
1515
BOOL WINAPI WidenPath(HDC hdc)
1516
{
1517
   DC *dc = DC_GetDCPtr( hdc );
1518
   BOOL ret = FALSE;
1519
   
1520
   if(!dc) return FALSE;
1521 1522

   if(dc->funcs->pWidenPath)
1523
     ret = dc->funcs->pWidenPath(dc);
1524 1525

   FIXME("stub\n");
1526 1527
   GDI_ReleaseObj( hdc );
   return ret;
1528
}