Detect DCs without fonts and return an error in wglUseBitmapFonts.
[wine] / graphics / painting.c
1 /*
2  * GDI drawing functions.
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  * Copyright 1997 Bertho A. Stultiens
6  *           1999 Huw D M Davies
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <string.h>
24 #include <stdlib.h>
25
26 #include "windef.h"
27 #include "wingdi.h"
28 #include "winerror.h"
29 #include "gdi.h"
30 #include "bitmap.h"
31 #include "path.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(gdi);
35
36
37 /***********************************************************************
38  *           LineTo    (GDI.19)
39  */
40 BOOL16 WINAPI LineTo16( HDC16 hdc, INT16 x, INT16 y )
41 {
42     return LineTo( hdc, x, y );
43 }
44
45
46 /***********************************************************************
47  *           LineTo    (GDI32.@)
48  */
49 BOOL WINAPI LineTo( HDC hdc, INT x, INT y )
50 {
51     DC * dc = DC_GetDCUpdate( hdc );
52     BOOL ret;
53
54     if(!dc) return FALSE;
55
56     if(PATH_IsPathOpen(dc->path))
57         ret = PATH_LineTo(dc, x, y);
58     else
59         ret = dc->funcs->pLineTo && dc->funcs->pLineTo(dc->physDev,x,y);
60     if(ret) {
61         dc->CursPosX = x;
62         dc->CursPosY = y;
63     }
64     GDI_ReleaseObj( hdc );
65     return ret;
66 }
67
68
69 /***********************************************************************
70  *           MoveTo    (GDI.20)
71  */
72 DWORD WINAPI MoveTo16( HDC16 hdc, INT16 x, INT16 y )
73 {
74     POINT pt;
75
76     if (!MoveToEx( (HDC)hdc, x, y, &pt )) return 0;
77     return MAKELONG(pt.x,pt.y);
78 }
79
80
81 /***********************************************************************
82  *           MoveToEx    (GDI.483)
83  */
84 BOOL16 WINAPI MoveToEx16( HDC16 hdc, INT16 x, INT16 y, LPPOINT16 pt )
85 {
86     POINT pt32;
87
88     if (!MoveToEx( (HDC)hdc, (INT)x, (INT)y, &pt32 )) return FALSE;
89     if (pt) CONV_POINT32TO16( &pt32, pt );
90     return TRUE;
91 }
92
93
94 /***********************************************************************
95  *           MoveToEx    (GDI32.@)
96  */
97 BOOL WINAPI MoveToEx( HDC hdc, INT x, INT y, LPPOINT pt )
98 {
99     BOOL ret = TRUE;
100     DC * dc = DC_GetDCPtr( hdc );
101
102     if(!dc) return FALSE;
103
104     if(pt) {
105         pt->x = dc->CursPosX;
106         pt->y = dc->CursPosY;
107     }
108     dc->CursPosX = x;
109     dc->CursPosY = y;
110
111     if(PATH_IsPathOpen(dc->path)) ret = PATH_MoveTo(dc);
112     else if (dc->funcs->pMoveTo) ret = dc->funcs->pMoveTo(dc->physDev,x,y);
113     GDI_ReleaseObj( hdc );
114     return ret;
115 }
116
117
118 /***********************************************************************
119  *           Arc    (GDI.23)
120  */
121 BOOL16 WINAPI Arc16( HDC16 hdc, INT16 left, INT16 top, INT16 right,
122                      INT16 bottom, INT16 xstart, INT16 ystart,
123                      INT16 xend, INT16 yend )
124 {
125     return Arc( (HDC)hdc, (INT)left, (INT)top, (INT)right,
126                   (INT)bottom, (INT)xstart, (INT)ystart, (INT)xend,
127                   (INT)yend );
128 }
129
130
131 /***********************************************************************
132  *           Arc    (GDI32.@)
133  */
134 BOOL WINAPI Arc( HDC hdc, INT left, INT top, INT right,
135                      INT bottom, INT xstart, INT ystart,
136                      INT xend, INT yend )
137 {
138     BOOL ret = FALSE;
139     DC * dc = DC_GetDCUpdate( hdc );
140     if (dc)
141     {
142     if(PATH_IsPathOpen(dc->path))
143             ret = PATH_Arc(dc, left, top, right, bottom, xstart, ystart, xend, yend,0);
144         else if (dc->funcs->pArc)
145             ret = dc->funcs->pArc(dc->physDev,left,top,right,bottom,xstart,ystart,xend,yend);
146         GDI_ReleaseObj( hdc );
147     }
148     return ret;
149 }
150
151 /***********************************************************************
152  *           ArcTo    (GDI32.@)
153  */
154 BOOL WINAPI ArcTo( HDC hdc,
155                      INT left,   INT top,
156                      INT right,  INT bottom,
157                      INT xstart, INT ystart,
158                      INT xend,   INT yend )
159 {
160     BOOL result;
161     DC * dc = DC_GetDCUpdate( hdc );
162     if(!dc) return FALSE;
163
164     if(dc->funcs->pArcTo)
165     {
166         result = dc->funcs->pArcTo( dc->physDev, left, top, right, bottom,
167                                   xstart, ystart, xend, yend );
168         GDI_ReleaseObj( hdc );
169         return result;
170     }
171     GDI_ReleaseObj( hdc );
172     /*
173      * Else emulate it.
174      * According to the documentation, a line is drawn from the current
175      * position to the starting point of the arc.
176      */
177     LineTo(hdc, xstart, ystart);
178     /*
179      * Then the arc is drawn.
180      */
181     result = Arc(hdc, left, top, right, bottom, xstart, ystart, xend, yend);
182     /*
183      * If no error occurred, the current position is moved to the ending
184      * point of the arc.
185      */
186     if (result) MoveToEx(hdc, xend, yend, NULL);
187     return result;
188 }
189
190 /***********************************************************************
191  *           Pie    (GDI.26)
192  */
193 BOOL16 WINAPI Pie16( HDC16 hdc, INT16 left, INT16 top,
194                      INT16 right, INT16 bottom, INT16 xstart, INT16 ystart,
195                      INT16 xend, INT16 yend )
196 {
197     return Pie( (HDC)hdc, (INT)left, (INT)top, (INT)right,
198                   (INT)bottom, (INT)xstart, (INT)ystart, (INT)xend,
199                   (INT)yend );
200 }
201
202
203 /***********************************************************************
204  *           Pie   (GDI32.@)
205  */
206 BOOL WINAPI Pie( HDC hdc, INT left, INT top,
207                      INT right, INT bottom, INT xstart, INT ystart,
208                      INT xend, INT yend )
209 {
210     BOOL ret = FALSE;
211     DC * dc = DC_GetDCUpdate( hdc );
212     if (!dc) return FALSE;
213
214     if(PATH_IsPathOpen(dc->path))
215         ret = PATH_Arc(dc,left,top,right,bottom,xstart,ystart,xend,yend,2);
216     else if(dc->funcs->pPie)
217         ret = dc->funcs->pPie(dc->physDev,left,top,right,bottom,xstart,ystart,xend,yend);
218
219     GDI_ReleaseObj( hdc );
220     return ret;
221 }
222
223
224 /***********************************************************************
225  *           Chord    (GDI.348)
226  */
227 BOOL16 WINAPI Chord16( HDC16 hdc, INT16 left, INT16 top,
228                        INT16 right, INT16 bottom, INT16 xstart, INT16 ystart,
229                        INT16 xend, INT16 yend )
230 {
231     return Chord( hdc, left, top, right, bottom, xstart, ystart, xend, yend );
232 }
233
234
235 /***********************************************************************
236  *           Chord    (GDI32.@)
237  */
238 BOOL WINAPI Chord( HDC hdc, INT left, INT top,
239                        INT right, INT bottom, INT xstart, INT ystart,
240                        INT xend, INT yend )
241 {
242     BOOL ret = FALSE;
243     DC * dc = DC_GetDCUpdate( hdc );
244     if (!dc) return FALSE;
245
246     if(PATH_IsPathOpen(dc->path))
247         ret = PATH_Arc(dc,left,top,right,bottom,xstart,ystart,xend,yend,1);
248     else if(dc->funcs->pChord)
249         ret = dc->funcs->pChord(dc->physDev,left,top,right,bottom,xstart,ystart,xend,yend);
250
251     GDI_ReleaseObj( hdc );
252     return ret;
253 }
254
255
256 /***********************************************************************
257  *           Ellipse    (GDI.24)
258  */
259 BOOL16 WINAPI Ellipse16( HDC16 hdc, INT16 left, INT16 top,
260                          INT16 right, INT16 bottom )
261 {
262     return Ellipse( hdc, left, top, right, bottom );
263 }
264
265
266 /***********************************************************************
267  *           Ellipse    (GDI32.@)
268  */
269 BOOL WINAPI Ellipse( HDC hdc, INT left, INT top,
270                          INT right, INT bottom )
271 {
272     BOOL ret = FALSE;
273     DC * dc = DC_GetDCUpdate( hdc );
274     if (!dc) return FALSE;
275
276     if(PATH_IsPathOpen(dc->path))
277         ret = PATH_Ellipse(dc,left,top,right,bottom);
278     else if (dc->funcs->pEllipse)
279         ret = dc->funcs->pEllipse(dc->physDev,left,top,right,bottom);
280
281     GDI_ReleaseObj( hdc );
282     return ret;
283 }
284
285
286 /***********************************************************************
287  *           Rectangle    (GDI.27)
288  */
289 BOOL16 WINAPI Rectangle16( HDC16 hdc, INT16 left, INT16 top,
290                            INT16 right, INT16 bottom )
291 {
292     return Rectangle( hdc, left, top, right, bottom );
293 }
294
295
296 /***********************************************************************
297  *           Rectangle    (GDI32.@)
298  */
299 BOOL WINAPI Rectangle( HDC hdc, INT left, INT top,
300                            INT right, INT bottom )
301 {
302     BOOL ret = FALSE;
303     DC * dc = DC_GetDCUpdate( hdc );
304     if (dc)
305     {
306     if(PATH_IsPathOpen(dc->path))
307             ret = PATH_Rectangle(dc, left, top, right, bottom);
308         else if (dc->funcs->pRectangle)
309             ret = dc->funcs->pRectangle(dc->physDev,left,top,right,bottom);
310         GDI_ReleaseObj( hdc );
311     }
312     return ret;
313 }
314
315
316 /***********************************************************************
317  *           RoundRect    (GDI.28)
318  */
319 BOOL16 WINAPI RoundRect16( HDC16 hdc, INT16 left, INT16 top, INT16 right,
320                            INT16 bottom, INT16 ell_width, INT16 ell_height )
321 {
322     return RoundRect( hdc, left, top, right, bottom, ell_width, ell_height );
323 }
324
325
326 /***********************************************************************
327  *           RoundRect    (GDI32.@)
328  */
329 BOOL WINAPI RoundRect( HDC hdc, INT left, INT top, INT right,
330                            INT bottom, INT ell_width, INT ell_height )
331 {
332     BOOL ret = FALSE;
333     DC *dc = DC_GetDCUpdate( hdc );
334
335     if (dc)
336     {
337         if(PATH_IsPathOpen(dc->path))
338             ret = PATH_RoundRect(dc,left,top,right,bottom,ell_width,ell_height);
339         else if (dc->funcs->pRoundRect)
340             ret = dc->funcs->pRoundRect(dc->physDev,left,top,right,bottom,ell_width,ell_height);
341         GDI_ReleaseObj( hdc );
342     }
343     return ret;
344 }
345
346 /***********************************************************************
347  *           SetPixel    (GDI.31)
348  */
349 COLORREF WINAPI SetPixel16( HDC16 hdc, INT16 x, INT16 y, COLORREF color )
350 {
351     return SetPixel( hdc, x, y, color );
352 }
353
354
355 /***********************************************************************
356  *           SetPixel    (GDI32.@)
357  */
358 COLORREF WINAPI SetPixel( HDC hdc, INT x, INT y, COLORREF color )
359 {
360     COLORREF ret = 0;
361     DC * dc = DC_GetDCUpdate( hdc );
362     if (dc)
363     {
364         if (dc->funcs->pSetPixel) ret = dc->funcs->pSetPixel(dc->physDev,x,y,color);
365         GDI_ReleaseObj( hdc );
366     }
367     return ret;
368 }
369
370 /***********************************************************************
371  *           SetPixelV    (GDI32.@)
372  */
373 BOOL WINAPI SetPixelV( HDC hdc, INT x, INT y, COLORREF color )
374 {
375     BOOL ret = FALSE;
376     DC * dc = DC_GetDCUpdate( hdc );
377     if (dc)
378     {
379         if (dc->funcs->pSetPixel)
380         {
381             dc->funcs->pSetPixel(dc->physDev,x,y,color);
382             ret = TRUE;
383         }
384         GDI_ReleaseObj( hdc );
385     }
386     return ret;
387 }
388
389 /***********************************************************************
390  *           GetPixel    (GDI.83)
391  */
392 COLORREF WINAPI GetPixel16( HDC16 hdc, INT16 x, INT16 y )
393 {
394     return GetPixel( hdc, x, y );
395 }
396
397
398 /***********************************************************************
399  *           GetPixel    (GDI32.@)
400  */
401 COLORREF WINAPI GetPixel( HDC hdc, INT x, INT y )
402 {
403     COLORREF ret = CLR_INVALID;
404     DC * dc = DC_GetDCUpdate( hdc );
405
406     if (dc)
407     {
408     /* FIXME: should this be in the graphics driver? */
409         if (PtVisible( hdc, x, y ))
410         {
411             if (dc->funcs->pGetPixel) ret = dc->funcs->pGetPixel(dc->physDev,x,y);
412         }
413         GDI_ReleaseObj( hdc );
414     }
415     return ret;
416 }
417
418
419 /******************************************************************************
420  * ChoosePixelFormat [GDI32.@]
421  * Matches a pixel format to given format
422  *
423  * PARAMS
424  *    hdc  [I] Device context to search for best pixel match
425  *    ppfd [I] Pixel format for which a match is sought
426  *
427  * RETURNS
428  *    Success: Pixel format index closest to given format
429  *    Failure: 0
430  */
431 INT WINAPI ChoosePixelFormat( HDC hdc, const LPPIXELFORMATDESCRIPTOR ppfd )
432 {
433     INT ret = 0;
434     DC * dc = DC_GetDCPtr( hdc );
435
436     TRACE("(%08x,%p)\n",hdc,ppfd);
437
438     if (!dc) return 0;
439
440     if (!dc->funcs->pChoosePixelFormat) FIXME(" :stub\n");
441     else ret = dc->funcs->pChoosePixelFormat(dc->physDev,ppfd);
442
443     GDI_ReleaseObj( hdc );
444     return ret;
445 }
446
447
448 /******************************************************************************
449  * SetPixelFormat [GDI32.@]
450  * Sets pixel format of device context
451  *
452  * PARAMS
453  *    hdc          [I] Device context to search for best pixel match
454  *    iPixelFormat [I] Pixel format index
455  *    ppfd         [I] Pixel format for which a match is sought
456  *
457  * RETURNS STD
458  */
459 BOOL WINAPI SetPixelFormat( HDC hdc, INT iPixelFormat,
460                             const PIXELFORMATDESCRIPTOR *ppfd)
461 {
462     INT bRet = FALSE;
463     DC * dc = DC_GetDCPtr( hdc );
464
465     TRACE("(%d,%d,%p)\n",hdc,iPixelFormat,ppfd);
466
467     if (!dc) return 0;
468
469     if (!dc->funcs->pSetPixelFormat) FIXME(" :stub\n");
470     else bRet = dc->funcs->pSetPixelFormat(dc->physDev,iPixelFormat,ppfd);
471
472     GDI_ReleaseObj( hdc );
473     return bRet;
474 }
475
476
477 /******************************************************************************
478  * GetPixelFormat [GDI32.@]
479  * Gets index of pixel format of DC
480  *
481  * PARAMETERS
482  *    hdc [I] Device context whose pixel format index is sought
483  *
484  * RETURNS
485  *    Success: Currently selected pixel format
486  *    Failure: 0
487  */
488 INT WINAPI GetPixelFormat( HDC hdc )
489 {
490     INT ret = 0;
491     DC * dc = DC_GetDCPtr( hdc );
492
493     TRACE("(%08x)\n",hdc);
494
495     if (!dc) return 0;
496
497     if (!dc->funcs->pGetPixelFormat) FIXME(" :stub\n");
498     else ret = dc->funcs->pGetPixelFormat(dc->physDev);
499
500     GDI_ReleaseObj( hdc );
501     return ret;
502 }
503
504
505 /******************************************************************************
506  * DescribePixelFormat [GDI32.@]
507  * Gets info about pixel format from DC
508  *
509  * PARAMS
510  *    hdc          [I] Device context
511  *    iPixelFormat [I] Pixel format selector
512  *    nBytes       [I] Size of buffer
513  *    ppfd         [O] Pointer to structure to receive pixel format data
514  *
515  * RETURNS
516  *    Success: Maximum pixel format index of the device context
517  *    Failure: 0
518  */
519 INT WINAPI DescribePixelFormat( HDC hdc, INT iPixelFormat, UINT nBytes,
520                                 LPPIXELFORMATDESCRIPTOR ppfd )
521 {
522     INT ret = 0;
523     DC * dc = DC_GetDCPtr( hdc );
524
525     TRACE("(%08x,%d,%d,%p): stub\n",hdc,iPixelFormat,nBytes,ppfd);
526
527     if (!dc) return 0;
528
529     if (!dc->funcs->pDescribePixelFormat)
530     {
531         FIXME(" :stub\n");
532         ppfd->nSize = nBytes;
533         ppfd->nVersion = 1;
534         ret = 3;
535     }
536     else ret = dc->funcs->pDescribePixelFormat(dc->physDev,iPixelFormat,nBytes,ppfd);
537
538     GDI_ReleaseObj( hdc );
539     return ret;
540 }
541
542
543 /******************************************************************************
544  * SwapBuffers [GDI32.@]
545  * Exchanges front and back buffers of window
546  *
547  * PARAMS
548  *    hdc [I] Device context whose buffers get swapped
549  *
550  * RETURNS STD
551  */
552 BOOL WINAPI SwapBuffers( HDC hdc )
553 {
554     INT bRet = FALSE;
555     DC * dc = DC_GetDCPtr( hdc );
556
557     TRACE("(%08x)\n",hdc);
558
559     if (!dc) return TRUE;
560
561     if (!dc->funcs->pSwapBuffers)
562     {
563         FIXME(" :stub\n");
564         bRet = TRUE;
565     }
566     else bRet = dc->funcs->pSwapBuffers(dc->physDev);
567
568     GDI_ReleaseObj( hdc );
569     return bRet;
570 }
571
572
573 /***********************************************************************
574  *           PaintRgn    (GDI.43)
575  */
576 BOOL16 WINAPI PaintRgn16( HDC16 hdc, HRGN16 hrgn )
577 {
578     return PaintRgn( hdc, hrgn );
579 }
580
581
582 /***********************************************************************
583  *           PaintRgn    (GDI32.@)
584  */
585 BOOL WINAPI PaintRgn( HDC hdc, HRGN hrgn )
586 {
587     BOOL ret = FALSE;
588     DC * dc = DC_GetDCUpdate( hdc );
589     if (dc)
590     {
591         if (dc->funcs->pPaintRgn) ret = dc->funcs->pPaintRgn(dc->physDev,hrgn);
592         GDI_ReleaseObj( hdc );
593     }
594     return ret;
595 }
596
597
598 /***********************************************************************
599  *           FillRgn    (GDI.40)
600  */
601 BOOL16 WINAPI FillRgn16( HDC16 hdc, HRGN16 hrgn, HBRUSH16 hbrush )
602 {
603     return FillRgn( hdc, hrgn, hbrush );
604 }
605
606
607 /***********************************************************************
608  *           FillRgn    (GDI32.@)
609  */
610 BOOL WINAPI FillRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush )
611 {
612     BOOL retval = FALSE;
613     HBRUSH prevBrush;
614     DC * dc = DC_GetDCUpdate( hdc );
615
616     if (!dc) return FALSE;
617     if(dc->funcs->pFillRgn)
618         retval = dc->funcs->pFillRgn(dc->physDev, hrgn, hbrush);
619     else if ((prevBrush = SelectObject( hdc, hbrush )))
620     {
621     retval = PaintRgn( hdc, hrgn );
622     SelectObject( hdc, prevBrush );
623     }
624     GDI_ReleaseObj( hdc );
625     return retval;
626 }
627
628
629 /***********************************************************************
630  *           FrameRgn     (GDI.41)
631  */
632 BOOL16 WINAPI FrameRgn16( HDC16 hdc, HRGN16 hrgn, HBRUSH16 hbrush,
633                           INT16 nWidth, INT16 nHeight )
634 {
635     return FrameRgn( hdc, hrgn, hbrush, nWidth, nHeight );
636 }
637
638
639 /***********************************************************************
640  *           FrameRgn     (GDI32.@)
641  */
642 BOOL WINAPI FrameRgn( HDC hdc, HRGN hrgn, HBRUSH hbrush,
643                           INT nWidth, INT nHeight )
644 {
645     BOOL ret = FALSE;
646     DC *dc = DC_GetDCUpdate( hdc );
647
648     if (!dc) return FALSE;
649     if(dc->funcs->pFrameRgn)
650         ret = dc->funcs->pFrameRgn( dc->physDev, hrgn, hbrush, nWidth, nHeight );
651     else
652     {
653         HRGN tmp = CreateRectRgn( 0, 0, 0, 0 );
654         if (tmp)
655         {
656             if (REGION_FrameRgn( tmp, hrgn, nWidth, nHeight ))
657             {
658                 FillRgn( hdc, tmp, hbrush );
659                 ret = TRUE;
660             }
661             DeleteObject( tmp );
662         }
663     }
664     GDI_ReleaseObj( hdc );
665     return ret;
666 }
667
668
669 /***********************************************************************
670  *           InvertRgn    (GDI.42)
671  */
672 BOOL16 WINAPI InvertRgn16( HDC16 hdc, HRGN16 hrgn )
673 {
674     return InvertRgn( hdc, hrgn );
675 }
676
677
678 /***********************************************************************
679  *           InvertRgn    (GDI32.@)
680  */
681 BOOL WINAPI InvertRgn( HDC hdc, HRGN hrgn )
682 {
683     HBRUSH prevBrush;
684     INT prevROP;
685     BOOL retval;
686     DC *dc = DC_GetDCUpdate( hdc );
687     if (!dc) return FALSE;
688
689     if(dc->funcs->pInvertRgn)
690         retval = dc->funcs->pInvertRgn( dc->physDev, hrgn );
691     else
692     {
693     prevBrush = SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
694     prevROP = SetROP2( hdc, R2_NOT );
695     retval = PaintRgn( hdc, hrgn );
696     SelectObject( hdc, prevBrush );
697     SetROP2( hdc, prevROP );
698     }
699     GDI_ReleaseObj( hdc );
700     return retval;
701 }
702
703 /**********************************************************************
704  *          Polyline  (GDI.37)
705  */
706 BOOL16 WINAPI Polyline16( HDC16 hdc, const POINT16* pt, INT16 count )
707 {
708     register int i;
709     BOOL16 ret;
710     LPPOINT pt32 = (LPPOINT)HeapAlloc( GetProcessHeap(), 0,
711                                            count*sizeof(POINT) );
712
713     if (!pt32) return FALSE;
714     for (i=count;i--;) CONV_POINT16TO32(&(pt[i]),&(pt32[i]));
715     ret = Polyline(hdc,pt32,count);
716     HeapFree( GetProcessHeap(), 0, pt32 );
717     return ret;
718 }
719
720
721 /**********************************************************************
722  *          Polyline   (GDI32.@)
723  */
724 BOOL WINAPI Polyline( HDC hdc, const POINT* pt, INT count )
725 {
726     BOOL ret = FALSE;
727     DC * dc = DC_GetDCUpdate( hdc );
728     if (dc)
729     {
730         if (PATH_IsPathOpen(dc->path)) ret = PATH_Polyline(dc, pt, count);
731         else if (dc->funcs->pPolyline) ret = dc->funcs->pPolyline(dc->physDev,pt,count);
732         GDI_ReleaseObj( hdc );
733     }
734     return ret;
735 }
736
737 /**********************************************************************
738  *          PolylineTo   (GDI32.@)
739  */
740 BOOL WINAPI PolylineTo( HDC hdc, const POINT* pt, DWORD cCount )
741 {
742     DC * dc = DC_GetDCUpdate( hdc );
743     BOOL ret = FALSE;
744
745     if(!dc) return FALSE;
746
747     if(PATH_IsPathOpen(dc->path))
748         ret = PATH_PolylineTo(dc, pt, cCount);
749
750     else if(dc->funcs->pPolylineTo)
751         ret = dc->funcs->pPolylineTo(dc->physDev, pt, cCount);
752
753     else { /* do it using Polyline */
754         POINT *pts = HeapAlloc( GetProcessHeap(), 0,
755                                 sizeof(POINT) * (cCount + 1) );
756         if (pts)
757         {
758         pts[0].x = dc->CursPosX;
759         pts[0].y = dc->CursPosY;
760         memcpy( pts + 1, pt, sizeof(POINT) * cCount );
761         ret = Polyline( hdc, pts, cCount + 1 );
762         HeapFree( GetProcessHeap(), 0, pts );
763     }
764     }
765     if(ret) {
766         dc->CursPosX = pt[cCount-1].x;
767         dc->CursPosY = pt[cCount-1].y;
768     }
769     GDI_ReleaseObj( hdc );
770     return ret;
771 }
772
773 /**********************************************************************
774  *          Polygon  (GDI.36)
775  */
776 BOOL16 WINAPI Polygon16( HDC16 hdc, const POINT16* pt, INT16 count )
777 {
778     register int i;
779     BOOL ret;
780     LPPOINT pt32 = (LPPOINT)HeapAlloc( GetProcessHeap(), 0,
781                                            count*sizeof(POINT) );
782
783     if (!pt32) return FALSE;
784     for (i=count;i--;) CONV_POINT16TO32(&(pt[i]),&(pt32[i]));
785     ret = Polygon(hdc,pt32,count);
786     HeapFree( GetProcessHeap(), 0, pt32 );
787     return ret;
788 }
789
790
791 /**********************************************************************
792  *          Polygon  (GDI32.@)
793  */
794 BOOL WINAPI Polygon( HDC hdc, const POINT* pt, INT count )
795 {
796     BOOL ret = FALSE;
797     DC * dc = DC_GetDCUpdate( hdc );
798     if (dc)
799     {
800         if (PATH_IsPathOpen(dc->path)) ret = PATH_Polygon(dc, pt, count);
801         else if (dc->funcs->pPolygon) ret = dc->funcs->pPolygon(dc->physDev,pt,count);
802         GDI_ReleaseObj( hdc );
803     }
804     return ret;
805 }
806
807
808 /**********************************************************************
809  *          PolyPolygon (GDI.450)
810  */
811 BOOL16 WINAPI PolyPolygon16( HDC16 hdc, const POINT16* pt, const INT16* counts,
812                              UINT16 polygons )
813 {
814     int         i,nrpts;
815     LPPOINT     pt32;
816     LPINT       counts32;
817     BOOL16      ret;
818
819     nrpts=0;
820     for (i=polygons;i--;)
821         nrpts+=counts[i];
822     pt32 = (LPPOINT)HeapAlloc( GetProcessHeap(), 0, sizeof(POINT)*nrpts);
823     if(pt32 == NULL) return FALSE;
824     for (i=nrpts;i--;)
825         CONV_POINT16TO32(&(pt[i]),&(pt32[i]));
826     counts32 = (LPINT)HeapAlloc( GetProcessHeap(), 0, polygons*sizeof(INT) );
827     if(counts32 == NULL) {
828         HeapFree( GetProcessHeap(), 0, pt32 );
829         return FALSE;
830     }
831     for (i=polygons;i--;) counts32[i]=counts[i];
832
833     ret = PolyPolygon(hdc,pt32,counts32,polygons);
834     HeapFree( GetProcessHeap(), 0, counts32 );
835     HeapFree( GetProcessHeap(), 0, pt32 );
836     return ret;
837 }
838
839 /**********************************************************************
840  *          PolyPolygon  (GDI32.@)
841  */
842 BOOL WINAPI PolyPolygon( HDC hdc, const POINT* pt, const INT* counts,
843                              UINT polygons )
844 {
845     BOOL ret = FALSE;
846     DC * dc = DC_GetDCUpdate( hdc );
847     if (dc)
848     {
849         if (PATH_IsPathOpen(dc->path)) ret = PATH_PolyPolygon(dc, pt, counts, polygons);
850         else if (dc->funcs->pPolyPolygon) ret = dc->funcs->pPolyPolygon(dc->physDev,pt,counts,polygons);
851         GDI_ReleaseObj( hdc );
852     }
853     return ret;
854 }
855
856 /**********************************************************************
857  *          PolyPolyline  (GDI32.@)
858  */
859 BOOL WINAPI PolyPolyline( HDC hdc, const POINT* pt, const DWORD* counts,
860                             DWORD polylines )
861 {
862     BOOL ret = FALSE;
863     DC * dc = DC_GetDCUpdate( hdc );
864     if (dc)
865     {
866         if (PATH_IsPathOpen(dc->path)) ret = PATH_PolyPolyline(dc, pt, counts, polylines);
867         else if (dc->funcs->pPolyPolyline) ret = dc->funcs->pPolyPolyline(dc->physDev,pt,counts,polylines);
868         GDI_ReleaseObj( hdc );
869     }
870     return ret;
871 }
872
873 /**********************************************************************
874  *          ExtFloodFill   (GDI.372)
875  */
876 BOOL16 WINAPI ExtFloodFill16( HDC16 hdc, INT16 x, INT16 y, COLORREF color,
877                               UINT16 fillType )
878 {
879     return ExtFloodFill( hdc, x, y, color, fillType );
880 }
881
882
883 /**********************************************************************
884  *          ExtFloodFill   (GDI32.@)
885  */
886 BOOL WINAPI ExtFloodFill( HDC hdc, INT x, INT y, COLORREF color,
887                               UINT fillType )
888 {
889     BOOL ret = FALSE;
890     DC * dc = DC_GetDCUpdate( hdc );
891     if (dc)
892     {
893         if (dc->funcs->pExtFloodFill) ret = dc->funcs->pExtFloodFill(dc->physDev,x,y,color,fillType);
894         GDI_ReleaseObj( hdc );
895     }
896     return ret;
897 }
898
899
900 /**********************************************************************
901  *          FloodFill   (GDI.25)
902  */
903 BOOL16 WINAPI FloodFill16( HDC16 hdc, INT16 x, INT16 y, COLORREF color )
904 {
905     return ExtFloodFill( hdc, x, y, color, FLOODFILLBORDER );
906 }
907
908
909 /**********************************************************************
910  *          FloodFill   (GDI32.@)
911  */
912 BOOL WINAPI FloodFill( HDC hdc, INT x, INT y, COLORREF color )
913 {
914     return ExtFloodFill( hdc, x, y, color, FLOODFILLBORDER );
915 }
916
917
918 /******************************************************************************
919  * PolyBezier [GDI.502]
920  */
921 BOOL16 WINAPI PolyBezier16( HDC16 hDc, const POINT16* lppt, INT16 cPoints )
922 {
923     int i;
924     BOOL16 ret;
925     LPPOINT pt32 = (LPPOINT)HeapAlloc( GetProcessHeap(), 0,
926                                            cPoints*sizeof(POINT) );
927     if(!pt32) return FALSE;
928     for (i=cPoints;i--;) CONV_POINT16TO32(&(lppt[i]),&(pt32[i]));
929     ret= PolyBezier(hDc, pt32, cPoints);
930     HeapFree( GetProcessHeap(), 0, pt32 );
931     return ret;
932 }
933
934 /******************************************************************************
935  * PolyBezierTo [GDI.503]
936  */
937 BOOL16 WINAPI PolyBezierTo16( HDC16 hDc, const POINT16* lppt, INT16 cPoints )
938 {
939     int i;
940     BOOL16 ret;
941     LPPOINT pt32 = (LPPOINT)HeapAlloc( GetProcessHeap(), 0,
942                                            cPoints*sizeof(POINT) );
943     if(!pt32) return FALSE;
944     for (i=cPoints;i--;) CONV_POINT16TO32(&(lppt[i]),&(pt32[i]));
945     ret= PolyBezierTo(hDc, pt32, cPoints);
946     HeapFree( GetProcessHeap(), 0, pt32 );
947     return ret;
948 }
949
950 /******************************************************************************
951  * PolyBezier [GDI32.@]
952  * Draws one or more Bezier curves
953  *
954  * PARAMS
955  *    hDc     [I] Handle to device context
956  *    lppt    [I] Pointer to endpoints and control points
957  *    cPoints [I] Count of endpoints and control points
958  *
959  * RETURNS STD
960  */
961 BOOL WINAPI PolyBezier( HDC hdc, const POINT* lppt, DWORD cPoints )
962 {
963     BOOL ret = FALSE;
964     DC * dc = DC_GetDCUpdate( hdc );
965
966     if(!dc) return FALSE;
967
968     if(PATH_IsPathOpen(dc->path))
969         ret = PATH_PolyBezier(dc, lppt, cPoints);
970     else if (dc->funcs->pPolyBezier)
971         ret = dc->funcs->pPolyBezier(dc->physDev, lppt, cPoints);
972     else  /* We'll convert it into line segments and draw them using Polyline */
973     {
974         POINT *Pts;
975         INT nOut;
976
977         if ((Pts = GDI_Bezier( lppt, cPoints, &nOut )))
978         {
979             TRACE("Pts = %p, no = %d\n", Pts, nOut);
980             ret = Polyline( dc->hSelf, Pts, nOut );
981             HeapFree( GetProcessHeap(), 0, Pts );
982         }
983     }
984
985     GDI_ReleaseObj( hdc );
986     return ret;
987 }
988
989 /******************************************************************************
990  * PolyBezierTo [GDI32.@]
991  * Draws one or more Bezier curves
992  *
993  * PARAMS
994  *    hDc     [I] Handle to device context
995  *    lppt    [I] Pointer to endpoints and control points
996  *    cPoints [I] Count of endpoints and control points
997  *
998  * RETURNS STD
999  */
1000 BOOL WINAPI PolyBezierTo( HDC hdc, const POINT* lppt, DWORD cPoints )
1001 {
1002     DC * dc = DC_GetDCUpdate( hdc );
1003     BOOL ret;
1004
1005     if(!dc) return FALSE;
1006
1007     if(PATH_IsPathOpen(dc->path))
1008         ret = PATH_PolyBezierTo(dc, lppt, cPoints);
1009     else if(dc->funcs->pPolyBezierTo)
1010         ret = dc->funcs->pPolyBezierTo(dc->physDev, lppt, cPoints);
1011     else { /* We'll do it using PolyBezier */
1012         POINT *pt;
1013         pt = HeapAlloc( GetProcessHeap(), 0, sizeof(POINT) * (cPoints + 1) );
1014         if(!pt) return FALSE;
1015         pt[0].x = dc->CursPosX;
1016         pt[0].y = dc->CursPosY;
1017         memcpy(pt + 1, lppt, sizeof(POINT) * cPoints);
1018         ret = PolyBezier(dc->hSelf, pt, cPoints+1);
1019         HeapFree( GetProcessHeap(), 0, pt );
1020     }
1021     if(ret) {
1022         dc->CursPosX = lppt[cPoints-1].x;
1023         dc->CursPosY = lppt[cPoints-1].y;
1024     }
1025     GDI_ReleaseObj( hdc );
1026     return ret;
1027 }
1028
1029 /***********************************************************************
1030  *      AngleArc (GDI32.@)
1031  */
1032 BOOL WINAPI AngleArc(HDC hdc, INT x, INT y, DWORD dwRadius, FLOAT eStartAngle, FLOAT eSweepAngle)
1033 {
1034     INT x1,y1,x2,y2, arcdir;
1035     BOOL result;
1036     DC *dc;
1037
1038     if( (signed int)dwRadius < 0 )
1039         return FALSE;
1040
1041     dc = DC_GetDCUpdate( hdc );
1042     if(!dc) return FALSE;
1043
1044     if(dc->funcs->pAngleArc)
1045     {
1046         result = dc->funcs->pAngleArc( dc->physDev, x, y, dwRadius, eStartAngle, eSweepAngle );
1047
1048         GDI_ReleaseObj( hdc );
1049         return result;
1050     }
1051     GDI_ReleaseObj( hdc );
1052
1053     /* AngleArc always works counterclockwise */
1054     arcdir = GetArcDirection( hdc );
1055     SetArcDirection( hdc, AD_COUNTERCLOCKWISE );
1056
1057     x1 = x + cos(eStartAngle*M_PI/180) * dwRadius;
1058     y1 = y - sin(eStartAngle*M_PI/180) * dwRadius;
1059     x2 = x + cos((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius;
1060     y2 = x - sin((eStartAngle+eSweepAngle)*M_PI/180) * dwRadius;
1061
1062     LineTo( hdc, x1, y1 );
1063     if( eSweepAngle >= 0 )
1064         result = Arc( hdc, x-dwRadius, y-dwRadius, x+dwRadius, y+dwRadius,
1065                       x1, y1, x2, y2 );
1066     else
1067         result = Arc( hdc, x-dwRadius, y-dwRadius, x+dwRadius, y+dwRadius,
1068                       x2, y2, x1, y1 );
1069
1070     if( result ) MoveToEx( hdc, x2, y2, NULL );
1071     SetArcDirection( hdc, arcdir );
1072     return result;
1073 }
1074
1075 /***********************************************************************
1076  *      PolyDraw (GDI32.@)
1077  */
1078 BOOL WINAPI PolyDraw(HDC hdc, const POINT *lppt, const BYTE *lpbTypes,
1079                        DWORD cCount)
1080 {
1081     DC *dc;
1082     BOOL result;
1083     POINT lastmove;
1084     int i;
1085
1086     dc = DC_GetDCUpdate( hdc );
1087     if(!dc) return FALSE;
1088
1089     if(dc->funcs->pPolyDraw)
1090     {
1091         result = dc->funcs->pPolyDraw( dc->physDev, lppt, lpbTypes, cCount );
1092         GDI_ReleaseObj( hdc );
1093         return result;
1094     }
1095     GDI_ReleaseObj( hdc );
1096
1097     /* check for each bezierto if there are two more points */
1098     for( i = 0; i < cCount; i++ )
1099         if( lpbTypes[i] != PT_MOVETO &&
1100             lpbTypes[i] & PT_BEZIERTO )
1101         {
1102             if( cCount < i+3 )
1103                 return FALSE;
1104             else
1105                 i += 2;
1106         }
1107
1108     /* if no moveto occurs, we will close the figure here */
1109     lastmove.x = dc->CursPosX;
1110     lastmove.y = dc->CursPosY;
1111
1112     /* now let's draw */
1113     for( i = 0; i < cCount; i++ )
1114     {
1115         if( lpbTypes[i] == PT_MOVETO )
1116         {
1117             MoveToEx( hdc, lppt[i].x, lppt[i].y, NULL );
1118             lastmove.x = dc->CursPosX;
1119             lastmove.y = dc->CursPosY;
1120         }
1121         else if( lpbTypes[i] & PT_LINETO )
1122             LineTo( hdc, lppt[i].x, lppt[i].y );
1123         else if( lpbTypes[i] & PT_BEZIERTO )
1124         {
1125             PolyBezierTo( hdc, &lppt[i], 3 );
1126             i += 2;
1127         }
1128         else
1129             return FALSE;
1130
1131         if( lpbTypes[i] & PT_CLOSEFIGURE )
1132         {
1133             if( PATH_IsPathOpen( dc->path ) )
1134                 CloseFigure( hdc );
1135             else
1136                 LineTo( hdc, lastmove.x, lastmove.y );
1137         }
1138     }
1139
1140     return TRUE;
1141 }
1142
1143 /******************************************************************
1144  *
1145  *   *Very* simple bezier drawing code,
1146  *
1147  *   It uses a recursive algorithm to divide the curve in a series
1148  *   of straight line segements. Not ideal but for me sufficient.
1149  *   If you are in need for something better look for some incremental
1150  *   algorithm.
1151  *
1152  *   7 July 1998 Rein Klazes
1153  */
1154
1155  /*
1156   * some macro definitions for bezier drawing
1157   *
1158   * to avoid trucation errors the coordinates are
1159   * shifted upwards. When used in drawing they are
1160   * shifted down again, including correct rounding
1161   * and avoiding floating point arithmatic
1162   * 4 bits should allow 27 bits coordinates which I saw
1163   * somewere in the win32 doc's
1164   *
1165   */
1166
1167 #define BEZIERSHIFTBITS 4
1168 #define BEZIERSHIFTUP(x)    ((x)<<BEZIERSHIFTBITS)
1169 #define BEZIERPIXEL        BEZIERSHIFTUP(1)
1170 #define BEZIERSHIFTDOWN(x)  (((x)+(1<<(BEZIERSHIFTBITS-1)))>>BEZIERSHIFTBITS)
1171 /* maximum depth of recursion */
1172 #define BEZIERMAXDEPTH  8
1173
1174 /* size of array to store points on */
1175 /* enough for one curve */
1176 #define BEZIER_INITBUFSIZE    (150)
1177
1178 /* calculate Bezier average, in this case the middle
1179  * correctly rounded...
1180  * */
1181
1182 #define BEZIERMIDDLE(Mid, P1, P2) \
1183     (Mid).x=((P1).x+(P2).x + 1)/2;\
1184     (Mid).y=((P1).y+(P2).y + 1)/2;
1185
1186 /**********************************************************
1187 * BezierCheck helper function to check
1188 * that recursion can be terminated
1189 *       Points[0] and Points[3] are begin and endpoint
1190 *       Points[1] and Points[2] are control points
1191 *       level is the recursion depth
1192 *       returns true if the recusion can be terminated
1193 */
1194 static BOOL BezierCheck( int level, POINT *Points)
1195 {
1196     INT dx, dy;
1197     dx=Points[3].x-Points[0].x;
1198     dy=Points[3].y-Points[0].y;
1199     if(abs(dy)<=abs(dx)){/* shallow line */
1200         /* check that control points are between begin and end */
1201         if(Points[1].x < Points[0].x){
1202             if(Points[1].x < Points[3].x)
1203                 return FALSE;
1204         }else
1205             if(Points[1].x > Points[3].x)
1206                 return FALSE;
1207         if(Points[2].x < Points[0].x){
1208             if(Points[2].x < Points[3].x)
1209                 return FALSE;
1210         }else
1211             if(Points[2].x > Points[3].x)
1212                 return FALSE;
1213         dx=BEZIERSHIFTDOWN(dx);
1214         if(!dx) return TRUE;
1215         if(abs(Points[1].y-Points[0].y-(dy/dx)*
1216                 BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL ||
1217            abs(Points[2].y-Points[0].y-(dy/dx)*
1218                    BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL )
1219             return FALSE;
1220         else
1221             return TRUE;
1222     }else{ /* steep line */
1223         /* check that control points are between begin and end */
1224         if(Points[1].y < Points[0].y){
1225             if(Points[1].y < Points[3].y)
1226                 return FALSE;
1227         }else
1228             if(Points[1].y > Points[3].y)
1229                 return FALSE;
1230         if(Points[2].y < Points[0].y){
1231             if(Points[2].y < Points[3].y)
1232                 return FALSE;
1233         }else
1234             if(Points[2].y > Points[3].y)
1235                 return FALSE;
1236         dy=BEZIERSHIFTDOWN(dy);
1237         if(!dy) return TRUE;
1238         if(abs(Points[1].x-Points[0].x-(dx/dy)*
1239                 BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL ||
1240            abs(Points[2].x-Points[0].x-(dx/dy)*
1241                    BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL )
1242             return FALSE;
1243         else
1244             return TRUE;
1245     }
1246 }
1247
1248 /* Helper for GDI_Bezier.
1249  * Just handles one Bezier, so Points should point to four POINTs
1250  */
1251 static void GDI_InternalBezier( POINT *Points, POINT **PtsOut, INT *dwOut,
1252                                 INT *nPtsOut, INT level )
1253 {
1254     if(*nPtsOut == *dwOut) {
1255         *dwOut *= 2;
1256         *PtsOut = HeapReAlloc( GetProcessHeap(), 0, *PtsOut,
1257                                *dwOut * sizeof(POINT) );
1258     }
1259
1260     if(!level || BezierCheck(level, Points)) {
1261         if(*nPtsOut == 0) {
1262             (*PtsOut)[0].x = BEZIERSHIFTDOWN(Points[0].x);
1263             (*PtsOut)[0].y = BEZIERSHIFTDOWN(Points[0].y);
1264             *nPtsOut = 1;
1265         }
1266         (*PtsOut)[*nPtsOut].x = BEZIERSHIFTDOWN(Points[3].x);
1267         (*PtsOut)[*nPtsOut].y = BEZIERSHIFTDOWN(Points[3].y);
1268         (*nPtsOut) ++;
1269     } else {
1270         POINT Points2[4]; /* for the second recursive call */
1271         Points2[3]=Points[3];
1272         BEZIERMIDDLE(Points2[2], Points[2], Points[3]);
1273         BEZIERMIDDLE(Points2[0], Points[1], Points[2]);
1274         BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]);
1275
1276         BEZIERMIDDLE(Points[1], Points[0],  Points[1]);
1277         BEZIERMIDDLE(Points[2], Points[1], Points2[0]);
1278         BEZIERMIDDLE(Points[3], Points[2], Points2[1]);
1279
1280         Points2[0]=Points[3];
1281
1282         /* do the two halves */
1283         GDI_InternalBezier(Points, PtsOut, dwOut, nPtsOut, level-1);
1284         GDI_InternalBezier(Points2, PtsOut, dwOut, nPtsOut, level-1);
1285     }
1286 }
1287
1288
1289
1290 /***********************************************************************
1291  *           GDI_Bezier   [INTERNAL]
1292  *   Calculate line segments that approximate -what microsoft calls- a bezier
1293  *   curve.
1294  *   The routine recursively divides the curve in two parts until a straight
1295  *   line can be drawn
1296  *
1297  *  PARAMS
1298  *
1299  *  Points  [I] Ptr to count POINTs which are the end and control points
1300  *              of the set of Bezier curves to flatten.
1301  *  count   [I] Number of Points.  Must be 3n+1.
1302  *  nPtsOut [O] Will contain no of points that have been produced (i.e. no. of
1303  *              lines+1).
1304  *
1305  *  RETURNS
1306  *
1307  *  Ptr to an array of POINTs that contain the lines that approximinate the
1308  *  Beziers.  The array is allocated on the process heap and it is the caller's
1309  *  responsibility to HeapFree it. [this is not a particularly nice interface
1310  *  but since we can't know in advance how many points will generate, the
1311  *  alternative would be to call the function twice, once to determine the size
1312  *  and a second time to do the work - I decided this was too much of a pain].
1313  */
1314 POINT *GDI_Bezier( const POINT *Points, INT count, INT *nPtsOut )
1315 {
1316     POINT *out;
1317     INT Bezier, dwOut = BEZIER_INITBUFSIZE, i;
1318
1319     if((count - 1) % 3 != 0) {
1320         ERR("Invalid no. of points\n");
1321         return NULL;
1322     }
1323     *nPtsOut = 0;
1324     out = HeapAlloc( GetProcessHeap(), 0, dwOut * sizeof(POINT));
1325     for(Bezier = 0; Bezier < (count-1)/3; Bezier++) {
1326         POINT ptBuf[4];
1327         memcpy(ptBuf, Points + Bezier * 3, sizeof(POINT) * 4);
1328         for(i = 0; i < 4; i++) {
1329             ptBuf[i].x = BEZIERSHIFTUP(ptBuf[i].x);
1330             ptBuf[i].y = BEZIERSHIFTUP(ptBuf[i].y);
1331         }
1332         GDI_InternalBezier( ptBuf, &out, &dwOut, nPtsOut, BEZIERMAXDEPTH );
1333     }
1334     TRACE("Produced %d points\n", *nPtsOut);
1335     return out;
1336 }