Started implementing Enhanced MetaFile driver.
[wine] / graphics / enhmetafiledrv / graphics.c
1 /*
2  * Enhanced MetaFile driver graphics functions
3  *
4  * Copyright 1999 Huw D M Davies
5  */
6
7 #include <stdlib.h>
8 #include "gdi.h"
9 #include "dc.h"
10 #include "enhmetafiledrv.h"
11 #include "heap.h"
12 #include "debug.h"
13
14 DEFAULT_DEBUG_CHANNEL(enhmetafile)
15
16 /**********************************************************************
17  *           EMFDRV_MoveToEx
18  */
19 BOOL
20 EMFDRV_MoveToEx(DC *dc,INT x,INT y,LPPOINT pt)
21 {
22     EMRMOVETOEX emr;
23
24     emr.emr.iType = EMR_MOVETOEX;
25     emr.emr.nSize = sizeof(emr);
26     emr.ptl.x = x;
27     emr.ptl.y = y;
28
29     if(!EMFDRV_WriteRecord( dc, &emr.emr ))
30         return FALSE;
31
32     if (pt) {
33         pt->x = dc->w.CursPosX;
34         pt->y = dc->w.CursPosY;
35     }
36     dc->w.CursPosX = x;
37     dc->w.CursPosY = y;
38     return TRUE;
39 }
40
41 /***********************************************************************
42  *           EMFDRV_LineTo
43  */
44 BOOL
45 EMFDRV_LineTo( DC *dc, INT x, INT y )
46 {
47     EMRLINETO emr;
48     RECTL bounds;
49
50     emr.emr.iType = EMR_LINETO;
51     emr.emr.nSize = sizeof(emr);
52     emr.ptl.x = x;
53     emr.ptl.y = y;
54
55     if(!EMFDRV_WriteRecord( dc, &emr.emr ))
56         return FALSE;
57
58     bounds.left   = MIN(x, dc->w.CursPosX);
59     bounds.top    = MIN(y, dc->w.CursPosY);
60     bounds.right  = MAX(x, dc->w.CursPosX);
61     bounds.bottom = MAX(y, dc->w.CursPosY);
62
63     EMFDRV_UpdateBBox( dc, &bounds );
64
65     dc->w.CursPosX = x;
66     dc->w.CursPosY = y;
67     return TRUE;
68 }
69
70
71 /***********************************************************************
72  *           EMFDRV_ArcChordPie
73  */
74 static BOOL
75 EMFDRV_ArcChordPie( DC *dc, INT left, INT top, INT right, INT bottom,
76                     INT xstart, INT ystart, INT xend, INT yend, DWORD iType )
77 {
78     INT temp, xCentre, yCentre, i;
79     double angleStart, angleEnd;
80     double xinterStart, yinterStart, xinterEnd, yinterEnd;
81     EMRARC emr;
82     RECTL bounds;
83
84     if(left == right || top == bottom) return FALSE;
85
86     if(left > right) {temp = left; left = right; right = temp;}
87     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
88
89     right--;
90     bottom--;
91
92     emr.emr.iType     = iType;
93     emr.emr.nSize     = sizeof(emr);
94     emr.rclBox.left   = left;
95     emr.rclBox.top    = top;
96     emr.rclBox.right  = right;
97     emr.rclBox.bottom = bottom;
98     emr.ptlStart.x    = xstart;
99     emr.ptlStart.y    = ystart;
100     emr.ptlEnd.x      = xend;
101     emr.ptlEnd.x      = yend;
102
103
104     /* Now calculate the BBox */
105     xCentre = (left + right + 1) / 2;
106     yCentre = (top + bottom + 1) / 2;
107
108     xstart -= xCentre;
109     ystart -= yCentre;
110     xend   -= xCentre;
111     yend   -= yCentre;
112
113     /* invert y co-ords to get angle anti-clockwise from x-axis */
114     angleStart = atan2( -(double)ystart, (double)xstart);
115     angleEnd   = atan2( -(double)yend, (double)xend);
116
117     /* These are the intercepts of the start/end lines with the arc */
118
119     xinterStart = (right - left + 1)/2 * cos(angleStart) + xCentre;
120     yinterStart = -(bottom - top + 1)/2 * sin(angleStart) + yCentre;
121     xinterEnd   = (right - left + 1)/2 * cos(angleEnd) + xCentre;
122     yinterEnd   = -(bottom - top + 1)/2 * sin(angleEnd) + yCentre;
123
124     if(angleStart < 0) angleStart += 2 * M_PI;
125     if(angleEnd < 0) angleEnd += 2 * M_PI;
126     if(angleEnd < angleStart) angleEnd += 2 * M_PI;
127
128     bounds.left   = MIN(xinterStart, xinterEnd);
129     bounds.top    = MIN(yinterStart, yinterEnd);
130     bounds.right  = MAX(xinterStart, xinterEnd);
131     bounds.bottom = MAX(yinterStart, yinterEnd);
132     
133     for(i = 0; i <= 8; i++) {
134         if(i * M_PI / 2 < angleStart) /* loop until we're past start */
135             continue;
136         if(i * M_PI / 2 > angleEnd)   /* if we're past end we're finished */
137             break;
138
139         /* the arc touches the rectangle at the start of quadrant i, so adjust
140            BBox to reflect this. */
141
142         switch(i % 4) { 
143         case 0:
144             bounds.right = right;
145             break;
146         case 1:
147             bounds.top = top;
148             break;
149         case 2:
150             bounds.left = left;
151             break;
152         case 3:
153             bounds.bottom = bottom;
154             break;
155         }
156     }
157
158     /* If we're drawing a pie then make sure we include the centre */
159     if(iType == EMR_PIE) {
160         if(bounds.left > xCentre) bounds.left = xCentre;
161         else if(bounds.right < xCentre) bounds.right = xCentre;
162         if(bounds.top > yCentre) bounds.top = yCentre;
163         else if(bounds.bottom < yCentre) bounds.right = yCentre;
164     }
165     if(!EMFDRV_WriteRecord( dc, &emr.emr ))
166         return FALSE;
167     EMFDRV_UpdateBBox( dc, &bounds );
168     return TRUE;
169 }
170
171
172 /***********************************************************************
173  *           EMFDRV_Arc
174  */
175 BOOL
176 EMFDRV_Arc( DC *dc, INT left, INT top, INT right, INT bottom,
177             INT xstart, INT ystart, INT xend, INT yend )
178 {
179     return EMFDRV_ArcChordPie( dc, left, top, right, bottom, xstart, ystart,
180                                xend, yend, EMR_ARC );
181 }
182
183 /***********************************************************************
184  *           EMFDRV_Pie
185  */
186 BOOL
187 EMFDRV_Pie( DC *dc, INT left, INT top, INT right, INT bottom,
188             INT xstart, INT ystart, INT xend, INT yend )
189 {
190     return EMFDRV_ArcChordPie( dc, left, top, right, bottom, xstart, ystart,
191                                xend, yend, EMR_PIE );
192 }
193
194
195 /***********************************************************************
196  *           EMFDRV_Chord
197  */
198 BOOL
199 EMFDRV_Chord( DC *dc, INT left, INT top, INT right, INT bottom,
200              INT xstart, INT ystart, INT xend, INT yend )
201 {
202     return EMFDRV_ArcChordPie( dc, left, top, right, bottom, xstart, ystart,
203                                xend, yend, EMR_CHORD );
204 }
205
206 /***********************************************************************
207  *           EMFDRV_Ellipse
208  */
209 BOOL
210 EMFDRV_Ellipse( DC *dc, INT left, INT top, INT right, INT bottom )
211 {
212     EMRELLIPSE emr;
213     INT temp;
214
215     TRACE(enhmetafile, "%d,%d - %d,%d\n", left, top, right, bottom);
216
217     if(left == right || top == bottom) return FALSE;
218
219     if(left > right) {temp = left; left = right; right = temp;}
220     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
221
222     right--;
223     bottom--;
224
225     emr.emr.iType     = EMR_ELLIPSE;
226     emr.emr.nSize     = sizeof(emr);
227     emr.rclBox.left   = left;
228     emr.rclBox.top    = top;
229     emr.rclBox.right  = right;
230     emr.rclBox.bottom = bottom;
231
232     EMFDRV_UpdateBBox( dc, &emr.rclBox );
233     return EMFDRV_WriteRecord( dc, &emr.emr );
234 }
235
236 /***********************************************************************
237  *           EMFDRV_Rectangle
238  */
239 BOOL
240 EMFDRV_Rectangle(DC *dc, INT left, INT top, INT right, INT bottom)
241 {
242     EMRRECTANGLE emr;
243     INT temp;
244
245     TRACE(enhmetafile, "%d,%d - %d,%d\n", left, top, right, bottom);
246
247     if(left == right || top == bottom) return FALSE;
248
249     if(left > right) {temp = left; left = right; right = temp;}
250     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
251
252     right--;
253     bottom--;
254
255     emr.emr.iType     = EMR_RECTANGLE;
256     emr.emr.nSize     = sizeof(emr);
257     emr.rclBox.left   = left;
258     emr.rclBox.top    = top;
259     emr.rclBox.right  = right;
260     emr.rclBox.bottom = bottom;
261
262     EMFDRV_UpdateBBox( dc, &emr.rclBox );
263     return EMFDRV_WriteRecord( dc, &emr.emr );
264 }
265
266 /***********************************************************************
267  *           EMFDRV_RoundRect
268  */
269 BOOL 
270 EMFDRV_RoundRect( DC *dc, INT left, INT top, INT right,
271                   INT bottom, INT ell_width, INT ell_height )
272 {
273     EMRROUNDRECT emr;
274     INT temp;
275
276     if(left == right || top == bottom) return FALSE;
277
278     if(left > right) {temp = left; left = right; right = temp;}
279     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
280
281     right--;
282     bottom--;
283
284     emr.emr.iType     = EMR_ROUNDRECT;
285     emr.emr.nSize     = sizeof(emr);
286     emr.rclBox.left   = left;
287     emr.rclBox.top    = top;
288     emr.rclBox.right  = right;
289     emr.rclBox.bottom = bottom;
290     emr.szlCorner.cx  = ell_width;
291     emr.szlCorner.cy  = ell_height;
292
293     EMFDRV_UpdateBBox( dc, &emr.rclBox );
294     return EMFDRV_WriteRecord( dc, &emr.emr );
295 }
296
297 /***********************************************************************
298  *           EMFDRV_SetPixel
299  */
300 COLORREF
301 EMFDRV_SetPixel( DC *dc, INT x, INT y, COLORREF color )
302 {
303   return TRUE;
304 }
305
306
307 /**********************************************************************
308  *          EMFDRV_Polylinegon
309  *
310  * Helper for EMFDRV_Poly{line|gon}
311  */
312 static BOOL
313 EMFDRV_Polylinegon( DC *dc, const POINT* pt, INT count, DWORD iType )
314 {
315     EMRPOLYLINE *emr;
316     DWORD size;
317     INT i;
318     BOOL ret;
319
320     size = sizeof(EMRPOLYLINE) + sizeof(POINTL) * (count - 1);
321
322     emr = HeapAlloc( SystemHeap, 0, size );
323     emr->emr.iType = iType;
324     emr->emr.nSize = size;
325     
326     emr->rclBounds.left = emr->rclBounds.right = pt[0].x;
327     emr->rclBounds.top = emr->rclBounds.bottom = pt[0].y;
328
329     for(i = 1; i < count; i++) {
330         if(pt[i].x < emr->rclBounds.left)
331             emr->rclBounds.left = pt[i].x;
332         else if(pt[i].x > emr->rclBounds.right)
333             emr->rclBounds.right = pt[i].x;
334         if(pt[i].y < emr->rclBounds.top)
335             emr->rclBounds.top = pt[i].y;
336         else if(pt[i].y > emr->rclBounds.bottom)
337             emr->rclBounds.bottom = pt[i].y;
338     }
339
340     emr->cptl = count;
341     memcpy(emr->aptl, pt, size);
342
343     ret = EMFDRV_WriteRecord( dc, &emr->emr );
344     if(ret)
345         EMFDRV_UpdateBBox( dc, &emr->rclBounds );
346     HeapFree( SystemHeap, 0, emr );
347     return ret;
348 }
349
350
351 /**********************************************************************
352  *          EMFDRV_Polyline
353  */
354 BOOL
355 EMFDRV_Polyline( DC *dc, const POINT* pt, INT count )
356 {
357     return EMFDRV_Polylinegon( dc, pt, count, EMR_POLYLINE );
358 }
359
360 /**********************************************************************
361  *          EMFDRV_Polygon
362  */
363 BOOL
364 EMFDRV_Polygon( DC *dc, const POINT* pt, INT count )
365 {
366     if(count < 2) return FALSE;
367     return EMFDRV_Polylinegon( dc, pt, count, EMR_POLYGON );
368 }
369
370
371 /**********************************************************************
372  *          EMFDRV_PolyPolylinegon
373  *
374  * Helper for EMFDRV_PolyPoly{line|gon}
375  */
376 static BOOL 
377 EMFDRV_PolyPolylinegon( DC *dc, const POINT* pt, const INT* counts, UINT polys,
378                         DWORD iType)
379 {
380     EMRPOLYPOLYLINE *emr;
381     DWORD cptl = 0, poly, size, point;
382     RECTL bounds;
383     const POINT *pts;
384     BOOL ret;
385
386     bounds.left = bounds.right = pt[0].x;
387     bounds.top = bounds.bottom = pt[0].y;
388
389     pts = pt;
390     for(poly = 0; poly < polys; poly++) {
391         cptl += counts[poly];
392         for(point = 0; point < counts[poly]; point++) {
393             if(bounds.left > pts->x) bounds.left = pts->x;
394             else if(bounds.right < pts->x) bounds.right = pts->x;
395             if(bounds.top > pts->y) bounds.top = pts->y;
396             else if(bounds.bottom < pts->y) bounds.bottom = pts->y;
397             pts++;
398         }
399     }
400     
401     size = sizeof(EMRPOLYPOLYLINE) + (polys - 1) * sizeof(DWORD) + 
402       (cptl - 1) * sizeof(POINTL);
403
404     emr = HeapAlloc( SystemHeap, 0, size );
405
406     emr->emr.iType = iType;
407     emr->emr.nSize = size;
408     emr->rclBounds = bounds;
409     emr->nPolys = polys;
410     emr->cptl = cptl;
411     memcpy(emr->aPolyCounts, counts, polys * sizeof(DWORD));
412     memcpy(emr->aPolyCounts + polys, pt, cptl * sizeof(POINT));
413     ret = EMFDRV_WriteRecord( dc, &emr->emr );
414     if(ret)
415         EMFDRV_UpdateBBox( dc, &emr->rclBounds );
416     HeapFree( SystemHeap, 0, emr );
417     return ret;
418 }
419
420 /**********************************************************************
421  *          EMFDRV_PolyPolyline
422  */
423 BOOL 
424 EMFDRV_PolyPolyline(DC *dc, const POINT* pt, const DWORD* counts, DWORD polys)
425 {
426     return EMFDRV_PolyPolylinegon( dc, pt, (INT *)counts, polys,
427                                    EMR_POLYPOLYLINE );
428 }
429
430 /**********************************************************************
431  *          EMFDRV_PolyPolygon
432  */
433 BOOL 
434 EMFDRV_PolyPolygon( DC *dc, const POINT* pt, const INT* counts, UINT polys )
435 {
436     return EMFDRV_PolyPolylinegon( dc, pt, counts, polys, EMR_POLYPOLYGON );
437 }
438
439
440 /**********************************************************************
441  *          EMFDRV_ExtFloodFill
442  */
443 BOOL 
444 EMFDRV_ExtFloodFill( DC *dc, INT x, INT y, COLORREF color, UINT fillType )
445 {
446     EMREXTFLOODFILL emr;
447
448     emr.emr.iType = EMR_EXTFLOODFILL;
449     emr.emr.nSize = sizeof(emr);
450     emr.ptlStart.x = x;
451     emr.ptlStart.y = y;
452     emr.crColor = color;
453     emr.iMode = fillType;
454
455     return EMFDRV_WriteRecord( dc, &emr.emr );
456 }
457
458
459 /*********************************************************************
460  *          EMFDRV_FillRgn
461  */
462 BOOL EMFDRV_FillRgn( DC *dc, HRGN hrgn, HBRUSH hbrush )
463 {
464     EMRFILLRGN *emr;
465     DWORD size, rgnsize, index;
466     BOOL ret;
467
468     index = EMFDRV_CreateBrushIndirect( dc, hbrush );
469     if(!index) return FALSE;
470
471     rgnsize = GetRegionData( hrgn, 0, NULL );
472     size = rgnsize + sizeof(EMRFILLRGN) - 1;
473     emr = HeapAlloc( SystemHeap, 0, size );
474
475     GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
476
477     emr->emr.iType = EMR_FILLRGN;
478     emr->emr.nSize = size;
479     emr->rclBounds.left   = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
480     emr->rclBounds.top    = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
481     emr->rclBounds.right  = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
482     emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
483     emr->cbRgnData = rgnsize;
484     emr->ihBrush = index;
485     
486     ret = EMFDRV_WriteRecord( dc, &emr->emr );
487     if(ret)
488         EMFDRV_UpdateBBox( dc, &emr->rclBounds );
489     HeapFree( SystemHeap, 0, emr );
490     return ret;
491 }
492 /*********************************************************************
493  *          EMFDRV_FrameRgn
494  */
495 BOOL EMFDRV_FrameRgn( DC *dc, HRGN hrgn, HBRUSH hbrush, INT width, INT height )
496 {
497     EMRFRAMERGN *emr;
498     DWORD size, rgnsize, index;
499     BOOL ret;
500
501     index = EMFDRV_CreateBrushIndirect( dc, hbrush );
502     if(!index) return FALSE;
503
504     rgnsize = GetRegionData( hrgn, 0, NULL );
505     size = rgnsize + sizeof(EMRFRAMERGN) - 1;
506     emr = HeapAlloc( SystemHeap, 0, size );
507
508     GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
509
510     emr->emr.iType = EMR_FRAMERGN;
511     emr->emr.nSize = size;
512     emr->rclBounds.left   = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
513     emr->rclBounds.top    = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
514     emr->rclBounds.right  = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
515     emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
516     emr->cbRgnData = rgnsize;
517     emr->ihBrush = index;
518     emr->szlStroke.cx = width;
519     emr->szlStroke.cy = height;
520
521     ret = EMFDRV_WriteRecord( dc, &emr->emr );
522     if(ret)
523         EMFDRV_UpdateBBox( dc, &emr->rclBounds );
524     HeapFree( SystemHeap, 0, emr );
525     return ret;
526 }
527
528 /*********************************************************************
529  *          EMFDRV_PaintInvertRgn
530  *
531  * Helper for EMFDRV_{Paint|Invert}Rgn
532  */
533 static BOOL EMFDRV_PaintInvertRgn( DC *dc, HRGN hrgn, DWORD iType )
534 {
535     EMRINVERTRGN *emr;
536     DWORD size, rgnsize;
537     BOOL ret;
538
539
540     rgnsize = GetRegionData( hrgn, 0, NULL );
541     size = rgnsize + sizeof(EMRINVERTRGN) - 1;
542     emr = HeapAlloc( SystemHeap, 0, size );
543
544     GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
545
546     emr->emr.iType = iType;
547     emr->emr.nSize = size;
548     emr->rclBounds.left   = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
549     emr->rclBounds.top    = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
550     emr->rclBounds.right  = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
551     emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
552     emr->cbRgnData = rgnsize;
553     
554     ret = EMFDRV_WriteRecord( dc, &emr->emr );
555     if(ret)
556         EMFDRV_UpdateBBox( dc, &emr->rclBounds );
557     HeapFree( SystemHeap, 0, emr );
558     return ret;
559 }
560
561 /**********************************************************************
562  *          EMFDRV_PaintRgn
563  */
564 BOOL
565 EMFDRV_PaintRgn( DC *dc, HRGN hrgn )
566 {
567     return EMFDRV_PaintInvertRgn( dc, hrgn, EMR_PAINTRGN );
568 }
569
570 /**********************************************************************
571  *          EMFDRV_InvertRgn
572  */
573 BOOL
574 EMFDRV_InvertRgn( DC *dc, HRGN hrgn )
575 {
576     return EMFDRV_PaintInvertRgn( dc, hrgn, EMR_INVERTRGN );
577 }
578
579 /**********************************************************************
580  *          EMFDRV_SetBkColor
581  */
582 COLORREF
583 EMFDRV_SetBkColor( DC *dc, COLORREF color )
584 {
585     EMRSETBKCOLOR emr;
586
587     emr.emr.iType = EMR_SETBKCOLOR;
588     emr.emr.nSize = sizeof(emr);
589     emr.crColor = color;
590
591     return EMFDRV_WriteRecord( dc, &emr.emr );
592 }
593
594
595 /**********************************************************************
596  *          EMFDRV_SetTextColor
597  */
598 COLORREF
599 EMFDRV_SetTextColor( DC *dc, COLORREF color )
600 {
601     EMRSETTEXTCOLOR emr;
602
603     emr.emr.iType = EMR_SETTEXTCOLOR;
604     emr.emr.nSize = sizeof(emr);
605     emr.crColor = color;
606
607     return EMFDRV_WriteRecord( dc, &emr.emr );
608 }