1 /*
2 * Enhanced MetaFile driver graphics functions
3 *
4 * Copyright 1999 Huw D M Davies
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "enhmfdrv/enhmetafiledrv.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(enhmetafile);
35
36 static const RECTL empty_bounds = { 0, 0, -1, -1 };
37
38 /* determine if we can use 16-bit points to store all the input points */
can_use_short_points(const POINT * pts,UINT count)39 static BOOL can_use_short_points( const POINT *pts, UINT count )
40 {
41 UINT i;
42
43 for (i = 0; i < count; i++)
44 if (((pts[i].x + 0x8000) & ~0xffff) || ((pts[i].y + 0x8000) & ~0xffff))
45 return FALSE;
46 return TRUE;
47 }
48
49 /* store points in either long or short format; return a pointer to the end of the stored data */
store_points(POINTL * dest,const POINT * pts,UINT count,BOOL short_points)50 static void *store_points( POINTL *dest, const POINT *pts, UINT count, BOOL short_points )
51 {
52 if (short_points)
53 {
54 UINT i;
55 POINTS *dest_short = (POINTS *)dest;
56
57 for (i = 0; i < count; i++)
58 {
59 dest_short[i].x = pts[i].x;
60 dest_short[i].y = pts[i].y;
61 }
62 return dest_short + count;
63 }
64 else
65 {
66 memcpy( dest, pts, count * sizeof(*dest) );
67 return dest + count;
68 }
69 }
70
71 /* compute the bounds of an array of points, optionally including the current position */
72 #ifdef __REACTOS__
get_points_bounds(RECTL * bounds,const POINT * pts,UINT count,HDC hdc)73 static void get_points_bounds( RECTL *bounds, const POINT *pts, UINT count, HDC hdc )
74 #else
75 static void get_points_bounds( RECTL *bounds, const POINT *pts, UINT count, DC *dc )
76 #endif
77 {
78 UINT i;
79 #ifdef __REACTOS__
80 if (hdc)
81 {
82 POINT cur_pt;
83 GetCurrentPositionEx( hdc, &cur_pt );
84 bounds->left = bounds->right = cur_pt.x;
85 bounds->top = bounds->bottom = cur_pt.y;
86 }
87 #else
88 if (dc)
89 {
90 bounds->left = bounds->right = dc->cur_pos.x;
91 bounds->top = bounds->bottom = dc->cur_pos.y;
92 }
93 #endif
94 else if (count)
95 {
96 bounds->left = bounds->right = pts[0].x;
97 bounds->top = bounds->bottom = pts[0].y;
98 }
99 else *bounds = empty_bounds;
100
101 for (i = 0; i < count; i++)
102 {
103 bounds->left = min( bounds->left, pts[i].x );
104 bounds->right = max( bounds->right, pts[i].x );
105 bounds->top = min( bounds->top, pts[i].y );
106 bounds->bottom = max( bounds->bottom, pts[i].y );
107 }
108 }
109
110 /* helper for path stroke and fill functions */
111 #ifdef __REACTOS__
emfdrv_stroke_and_fill_path(PHYSDEV dev,INT type)112 static BOOL emfdrv_stroke_and_fill_path( PHYSDEV dev, INT type )
113 {
114 EMRSTROKEANDFILLPATH emr;
115 LPPOINT Points;
116 LPBYTE Types;
117 INT nSize;
118
119 emr.emr.iType = type;
120 emr.emr.nSize = sizeof(emr);
121
122 nSize = GetPath(dev->hdc, NULL, NULL, 0);
123 if (nSize != -1)
124 {
125 Points = HeapAlloc( GetProcessHeap(), 0, nSize*sizeof(POINT) );
126 Types = HeapAlloc( GetProcessHeap(), 0, nSize*sizeof(BYTE) );
127
128 GetPath(dev->hdc, Points, Types, nSize);
129 get_points_bounds( &emr.rclBounds, Points, nSize, 0 );
130
131 HeapFree( GetProcessHeap(), 0, Points );
132 HeapFree( GetProcessHeap(), 0, Types );
133
134 TRACE("GetBounds l %d t %d r %d b %d\n",emr.rclBounds.left, emr.rclBounds.top, emr.rclBounds.right, emr.rclBounds.bottom);
135 }
136 else emr.rclBounds = empty_bounds;
137
138 if (!EMFDRV_WriteRecord( dev, &emr.emr )) return FALSE;
139 if (nSize == -1 ) return FALSE;
140 EMFDRV_UpdateBBox( dev, &emr.rclBounds );
141 return TRUE;
142 }
143 #else
emfdrv_stroke_and_fill_path(PHYSDEV dev,INT type)144 static BOOL emfdrv_stroke_and_fill_path( PHYSDEV dev, INT type )
145 {
146 DC *dc = get_physdev_dc( dev );
147 EMRSTROKEANDFILLPATH emr;
148 struct gdi_path *path;
149 POINT *points;
150 BYTE *flags;
151
152 emr.emr.iType = type;
153 emr.emr.nSize = sizeof(emr);
154
155 if ((path = get_gdi_flat_path( dc, NULL )))
156 {
157 int count = get_gdi_path_data( path, &points, &flags );
158 get_points_bounds( &emr.rclBounds, points, count, 0 );
159 free_gdi_path( path );
160 }
161 else emr.rclBounds = empty_bounds;
162
163 if (!EMFDRV_WriteRecord( dev, &emr.emr )) return FALSE;
164 if (!path) return FALSE;
165 EMFDRV_UpdateBBox( dev, &emr.rclBounds );
166 return TRUE;
167 }
168 #endif
169
170 /**********************************************************************
171 * EMFDRV_MoveTo
172 */
EMFDRV_MoveTo(PHYSDEV dev,INT x,INT y)173 BOOL EMFDRV_MoveTo(PHYSDEV dev, INT x, INT y)
174 {
175 EMRMOVETOEX emr;
176
177 emr.emr.iType = EMR_MOVETOEX;
178 emr.emr.nSize = sizeof(emr);
179 emr.ptl.x = x;
180 emr.ptl.y = y;
181
182 return EMFDRV_WriteRecord( dev, &emr.emr );
183 }
184
185 /***********************************************************************
186 * EMFDRV_LineTo
187 */
EMFDRV_LineTo(PHYSDEV dev,INT x,INT y)188 BOOL EMFDRV_LineTo( PHYSDEV dev, INT x, INT y )
189 {
190 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
191 #ifndef __REACTOS__
192 DC *dc = get_physdev_dc( dev );
193 #endif
194 POINT pt;
195 EMRLINETO emr;
196 RECTL bounds;
197
198 emr.emr.iType = EMR_LINETO;
199 emr.emr.nSize = sizeof(emr);
200 emr.ptl.x = x;
201 emr.ptl.y = y;
202
203 if(!EMFDRV_WriteRecord( dev, &emr.emr ))
204 return FALSE;
205 #ifdef __REACTOS__
206 GetCurrentPositionEx( dev->hdc, &pt );
207 #else
208 pt = dc->cur_pos;
209 #endif
210 bounds.left = min(x, pt.x);
211 bounds.top = min(y, pt.y);
212 bounds.right = max(x, pt.x);
213 bounds.bottom = max(y, pt.y);
214
215 if(!physDev->path)
216 EMFDRV_UpdateBBox( dev, &bounds );
217
218 return TRUE;
219 }
220
221
222 /***********************************************************************
223 * EMFDRV_ArcChordPie
224 */
225 static BOOL
EMFDRV_ArcChordPie(PHYSDEV dev,INT left,INT top,INT right,INT bottom,INT xstart,INT ystart,INT xend,INT yend,DWORD iType)226 EMFDRV_ArcChordPie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
227 INT xstart, INT ystart, INT xend, INT yend, DWORD iType )
228 {
229 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
230 #ifndef __REACTOS__
231 DC *dc = get_physdev_dc( dev );
232 #endif
233 INT temp, xCentre, yCentre, i;
234 double angleStart, angleEnd;
235 double xinterStart, yinterStart, xinterEnd, yinterEnd;
236 EMRARC emr;
237 RECTL bounds;
238
239 if(left == right || top == bottom) return FALSE;
240
241 if(left > right) {temp = left; left = right; right = temp;}
242 if(top > bottom) {temp = top; top = bottom; bottom = temp;}
243 #ifdef __REACTOS__
244 if(GetGraphicsMode(dev->hdc) == GM_COMPATIBLE) {
245 #else
246 if(dc->GraphicsMode == GM_COMPATIBLE) {
247 #endif
248 right--;
249 bottom--;
250 }
251
252 emr.emr.iType = iType;
253 emr.emr.nSize = sizeof(emr);
254 emr.rclBox.left = left;
255 emr.rclBox.top = top;
256 emr.rclBox.right = right;
257 emr.rclBox.bottom = bottom;
258 emr.ptlStart.x = xstart;
259 emr.ptlStart.y = ystart;
260 emr.ptlEnd.x = xend;
261 emr.ptlEnd.y = yend;
262
263
264 /* Now calculate the BBox */
265 xCentre = (left + right + 1) / 2;
266 yCentre = (top + bottom + 1) / 2;
267
268 xstart -= xCentre;
269 ystart -= yCentre;
270 xend -= xCentre;
271 yend -= yCentre;
272
273 /* invert y co-ords to get angle anti-clockwise from x-axis */
274 angleStart = atan2( -(double)ystart, (double)xstart);
275 angleEnd = atan2( -(double)yend, (double)xend);
276
277 /* These are the intercepts of the start/end lines with the arc */
278
279 xinterStart = (right - left + 1)/2 * cos(angleStart) + xCentre;
280 yinterStart = -(bottom - top + 1)/2 * sin(angleStart) + yCentre;
281 xinterEnd = (right - left + 1)/2 * cos(angleEnd) + xCentre;
282 yinterEnd = -(bottom - top + 1)/2 * sin(angleEnd) + yCentre;
283
284 if(angleStart < 0) angleStart += 2 * M_PI;
285 if(angleEnd < 0) angleEnd += 2 * M_PI;
286 if(angleEnd < angleStart) angleEnd += 2 * M_PI;
287
288 bounds.left = min(xinterStart, xinterEnd);
289 bounds.top = min(yinterStart, yinterEnd);
290 bounds.right = max(xinterStart, xinterEnd);
291 bounds.bottom = max(yinterStart, yinterEnd);
292
293 for(i = 0; i <= 8; i++) {
294 if(i * M_PI / 2 < angleStart) /* loop until we're past start */
295 continue;
296 if(i * M_PI / 2 > angleEnd) /* if we're past end we're finished */
297 break;
298
299 /* the arc touches the rectangle at the start of quadrant i, so adjust
300 BBox to reflect this. */
301
302 switch(i % 4) {
303 case 0:
304 bounds.right = right;
305 break;
306 case 1:
307 bounds.top = top;
308 break;
309 case 2:
310 bounds.left = left;
311 break;
312 case 3:
313 bounds.bottom = bottom;
314 break;
315 }
316 }
317
318 /* If we're drawing a pie then make sure we include the centre */
319 if(iType == EMR_PIE) {
320 if(bounds.left > xCentre) bounds.left = xCentre;
321 else if(bounds.right < xCentre) bounds.right = xCentre;
322 if(bounds.top > yCentre) bounds.top = yCentre;
323 else if(bounds.bottom < yCentre) bounds.bottom = yCentre;
324 }
325 if (iType == EMR_ARCTO)
326 {
327 POINT pt;
328 #ifdef __REACTOS__
329 GetCurrentPositionEx( dev->hdc, &pt );
330 #else
331 pt = dc->cur_pos;
332 #endif
333 bounds.left = min( bounds.left, pt.x );
334 bounds.top = min( bounds.top, pt.y );
335 bounds.right = max( bounds.right, pt.x );
336 bounds.bottom = max( bounds.bottom, pt.y );
337 }
338 if(!EMFDRV_WriteRecord( dev, &emr.emr ))
339 return FALSE;
340 if(!physDev->path)
341 EMFDRV_UpdateBBox( dev, &bounds );
342 return TRUE;
343 }
344
345
346 /***********************************************************************
347 * EMFDRV_Arc
348 */
349 BOOL EMFDRV_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
350 INT xstart, INT ystart, INT xend, INT yend )
351 {
352 return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
353 xend, yend, EMR_ARC );
354 }
355
356 /***********************************************************************
357 * EMFDRV_ArcTo
358 */
359 BOOL EMFDRV_ArcTo( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
360 INT xstart, INT ystart, INT xend, INT yend )
361 {
362 return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
363 xend, yend, EMR_ARCTO );
364 }
365
366 /***********************************************************************
367 * EMFDRV_Pie
368 */
369 BOOL EMFDRV_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
370 INT xstart, INT ystart, INT xend, INT yend )
371 {
372 return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
373 xend, yend, EMR_PIE );
374 }
375
376
377 /***********************************************************************
378 * EMFDRV_Chord
379 */
380 BOOL EMFDRV_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
381 INT xstart, INT ystart, INT xend, INT yend )
382 {
383 return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
384 xend, yend, EMR_CHORD );
385 }
386
387 /***********************************************************************
388 * EMFDRV_AngleArc
389 */
390 BOOL EMFDRV_AngleArc( PHYSDEV dev, INT x, INT y, DWORD radius, FLOAT start, FLOAT sweep )
391 {
392 EMRANGLEARC emr;
393
394 emr.emr.iType = EMR_ANGLEARC;
395 emr.emr.nSize = sizeof( emr );
396 emr.ptlCenter.x = x;
397 emr.ptlCenter.y = y;
398 emr.nRadius = radius;
399 emr.eStartAngle = start;
400 emr.eSweepAngle = sweep;
401
402 return EMFDRV_WriteRecord( dev, &emr.emr );
403 }
404
405 /***********************************************************************
406 * EMFDRV_Ellipse
407 */
408 BOOL EMFDRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
409 {
410 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
411 #ifndef __REACTOS__
412 DC *dc = get_physdev_dc( dev );
413 #endif
414 EMRELLIPSE emr;
415 INT temp;
416
417 TRACE("%d,%d - %d,%d\n", left, top, right, bottom);
418
419 if(left == right || top == bottom) return FALSE;
420
421 if(left > right) {temp = left; left = right; right = temp;}
422 if(top > bottom) {temp = top; top = bottom; bottom = temp;}
423 #ifdef __REACTOS__
424 if(GetGraphicsMode(dev->hdc) == GM_COMPATIBLE) {
425 #else
426 if(dc->GraphicsMode == GM_COMPATIBLE) {
427 #endif
428 right--;
429 bottom--;
430 }
431
432 emr.emr.iType = EMR_ELLIPSE;
433 emr.emr.nSize = sizeof(emr);
434 emr.rclBox.left = left;
435 emr.rclBox.top = top;
436 emr.rclBox.right = right;
437 emr.rclBox.bottom = bottom;
438
439 if(!physDev->path)
440 EMFDRV_UpdateBBox( dev, &emr.rclBox );
441 return EMFDRV_WriteRecord( dev, &emr.emr );
442 }
443
444 /***********************************************************************
445 * EMFDRV_Rectangle
446 */
447 BOOL EMFDRV_Rectangle(PHYSDEV dev, INT left, INT top, INT right, INT bottom)
448 {
449 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
450 #ifndef __REACTOS__
451 DC *dc = get_physdev_dc( dev );
452 #endif
453 EMRRECTANGLE emr;
454 INT temp;
455
456 TRACE("%d,%d - %d,%d\n", left, top, right, bottom);
457
458 if(left == right || top == bottom) return FALSE;
459
460 if(left > right) {temp = left; left = right; right = temp;}
461 if(top > bottom) {temp = top; top = bottom; bottom = temp;}
462 #ifdef __REACTOS__
463 if(GetGraphicsMode(dev->hdc) == GM_COMPATIBLE) {
464 #else
465 if(dc->GraphicsMode == GM_COMPATIBLE) {
466 #endif
467 right--;
468 bottom--;
469 }
470
471 emr.emr.iType = EMR_RECTANGLE;
472 emr.emr.nSize = sizeof(emr);
473 emr.rclBox.left = left;
474 emr.rclBox.top = top;
475 emr.rclBox.right = right;
476 emr.rclBox.bottom = bottom;
477
478 if(!physDev->path)
479 EMFDRV_UpdateBBox( dev, &emr.rclBox );
480 return EMFDRV_WriteRecord( dev, &emr.emr );
481 }
482
483 /***********************************************************************
484 * EMFDRV_RoundRect
485 */
486 BOOL EMFDRV_RoundRect( PHYSDEV dev, INT left, INT top, INT right,
487 INT bottom, INT ell_width, INT ell_height )
488 {
489 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
490 #ifndef __REACTOS__
491 DC *dc = get_physdev_dc( dev );
492 #endif
493 EMRROUNDRECT emr;
494 INT temp;
495
496 if(left == right || top == bottom) return FALSE;
497
498 if(left > right) {temp = left; left = right; right = temp;}
499 if(top > bottom) {temp = top; top = bottom; bottom = temp;}
500 #ifdef __REACTOS__
501 if(GetGraphicsMode(dev->hdc) == GM_COMPATIBLE) {
502 #else
503 if(dc->GraphicsMode == GM_COMPATIBLE) {
504 #endif
505 right--;
506 bottom--;
507 }
508
509 emr.emr.iType = EMR_ROUNDRECT;
510 emr.emr.nSize = sizeof(emr);
511 emr.rclBox.left = left;
512 emr.rclBox.top = top;
513 emr.rclBox.right = right;
514 emr.rclBox.bottom = bottom;
515 emr.szlCorner.cx = ell_width;
516 emr.szlCorner.cy = ell_height;
517
518 if(!physDev->path)
519 EMFDRV_UpdateBBox( dev, &emr.rclBox );
520 return EMFDRV_WriteRecord( dev, &emr.emr );
521 }
522
523 /***********************************************************************
524 * EMFDRV_SetPixel
525 */
526 COLORREF EMFDRV_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
527 {
528 EMRSETPIXELV emr;
529
530 emr.emr.iType = EMR_SETPIXELV;
531 emr.emr.nSize = sizeof(emr);
532 emr.ptlPixel.x = x;
533 emr.ptlPixel.y = y;
534 emr.crColor = color;
535
536 if (EMFDRV_WriteRecord( dev, &emr.emr )) {
537 RECTL bounds;
538 bounds.left = bounds.right = x;
539 bounds.top = bounds.bottom = y;
540 EMFDRV_UpdateBBox( dev, &bounds );
541 return color;
542 }
543 return -1;
544 }
545
546 /**********************************************************************
547 * EMFDRV_Polylinegon
548 *
549 * Helper for EMFDRV_Poly{line|gon}
550 */
551 static BOOL
552 EMFDRV_Polylinegon( PHYSDEV dev, const POINT* pt, INT count, DWORD iType )
553 {
554 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
555 #ifndef __REACTOS__
556 DC *dc = get_physdev_dc( dev );
557 #endif
558 EMRPOLYLINE *emr;
559 DWORD size;
560 BOOL ret, use_small_emr = can_use_short_points( pt, count );
561
562 size = use_small_emr ? offsetof( EMRPOLYLINE16, apts[count] ) : offsetof( EMRPOLYLINE, aptl[count] );
563
564 emr = HeapAlloc( GetProcessHeap(), 0, size );
565 emr->emr.iType = use_small_emr ? iType + EMR_POLYLINE16 - EMR_POLYLINE : iType;
566 emr->emr.nSize = size;
567 emr->cptl = count;
568
569 store_points( emr->aptl, pt, count, use_small_emr );
570
571 if (!physDev->path)
572 get_points_bounds( &emr->rclBounds, pt, count,
573 #ifdef __REACTOS__
574 (iType == EMR_POLYBEZIERTO || iType == EMR_POLYLINETO) ? dev->hdc : 0 );
575 #else
576 (iType == EMR_POLYBEZIERTO || iType == EMR_POLYLINETO) ? dc : 0 );
577 #endif
578 else
579 emr->rclBounds = empty_bounds;
580
581 ret = EMFDRV_WriteRecord( dev, &emr->emr );
582 if (ret && !physDev->path)
583 EMFDRV_UpdateBBox( dev, &emr->rclBounds );
584 HeapFree( GetProcessHeap(), 0, emr );
585 return ret;
586 }
587
588
589 /**********************************************************************
590 * EMFDRV_Polyline
591 */
592 BOOL EMFDRV_Polyline( PHYSDEV dev, const POINT* pt, INT count )
593 {
594 return EMFDRV_Polylinegon( dev, pt, count, EMR_POLYLINE );
595 }
596
597 /**********************************************************************
598 * EMFDRV_PolylineTo
599 */
600 BOOL EMFDRV_PolylineTo( PHYSDEV dev, const POINT* pt, INT count )
601 {
602 return EMFDRV_Polylinegon( dev, pt, count, EMR_POLYLINETO );
603 }
604
605 /**********************************************************************
606 * EMFDRV_Polygon
607 */
608 BOOL EMFDRV_Polygon( PHYSDEV dev, const POINT* pt, INT count )
609 {
610 if(count < 2) return FALSE;
611 return EMFDRV_Polylinegon( dev, pt, count, EMR_POLYGON );
612 }
613
614 /**********************************************************************
615 * EMFDRV_PolyBezier
616 */
617 BOOL EMFDRV_PolyBezier( PHYSDEV dev, const POINT *pts, DWORD count )
618 {
619 return EMFDRV_Polylinegon( dev, pts, count, EMR_POLYBEZIER );
620 }
621
622 /**********************************************************************
623 * EMFDRV_PolyBezierTo
624 */
625 BOOL EMFDRV_PolyBezierTo( PHYSDEV dev, const POINT *pts, DWORD count )
626 {
627 return EMFDRV_Polylinegon( dev, pts, count, EMR_POLYBEZIERTO );
628 }
629
630
631 /**********************************************************************
632 * EMFDRV_PolyPolylinegon
633 *
634 * Helper for EMFDRV_PolyPoly{line|gon}
635 */
636 static BOOL
637 EMFDRV_PolyPolylinegon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polys,
638 DWORD iType)
639 {
640 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
641 EMRPOLYPOLYLINE *emr;
642 DWORD cptl = 0, poly, size;
643 BOOL ret, use_small_emr, bounds_valid = TRUE;
644
645 for(poly = 0; poly < polys; poly++) {
646 cptl += counts[poly];
647 if(counts[poly] < 2) bounds_valid = FALSE;
648 }
649 if(!cptl) bounds_valid = FALSE;
650 use_small_emr = can_use_short_points( pt, cptl );
651
652 size = FIELD_OFFSET(EMRPOLYPOLYLINE, aPolyCounts[polys]);
653 if(use_small_emr)
654 size += cptl * sizeof(POINTS);
655 else
656 size += cptl * sizeof(POINTL);
657
658 emr = HeapAlloc( GetProcessHeap(), 0, size );
659
660 emr->emr.iType = iType;
661 if(use_small_emr) emr->emr.iType += EMR_POLYPOLYLINE16 - EMR_POLYPOLYLINE;
662
663 emr->emr.nSize = size;
664 if(bounds_valid && !physDev->path)
665 get_points_bounds( &emr->rclBounds, pt, cptl, 0 );
666 else
667 emr->rclBounds = empty_bounds;
668 emr->nPolys = polys;
669 emr->cptl = cptl;
670
671 if(polys)
672 {
673 memcpy( emr->aPolyCounts, counts, polys * sizeof(DWORD) );
674 store_points( (POINTL *)(emr->aPolyCounts + polys), pt, cptl, use_small_emr );
675 }
676
677 ret = EMFDRV_WriteRecord( dev, &emr->emr );
678 if(ret && !bounds_valid)
679 {
680 ret = FALSE;
681 SetLastError( ERROR_INVALID_PARAMETER );
682 }
683 if(ret && !physDev->path)
684 EMFDRV_UpdateBBox( dev, &emr->rclBounds );
685 HeapFree( GetProcessHeap(), 0, emr );
686 return ret;
687 }
688
689 /**********************************************************************
690 * EMFDRV_PolyPolyline
691 */
692 BOOL EMFDRV_PolyPolyline(PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polys)
693 {
694 return EMFDRV_PolyPolylinegon( dev, pt, (const INT *)counts, polys,
695 EMR_POLYPOLYLINE );
696 }
697
698 /**********************************************************************
699 * EMFDRV_PolyPolygon
700 */
701 BOOL EMFDRV_PolyPolygon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polys )
702 {
703 return EMFDRV_PolyPolylinegon( dev, pt, counts, polys, EMR_POLYPOLYGON );
704 }
705
706
707 /**********************************************************************
708 * EMFDRV_PolyDraw
709 */
710 BOOL EMFDRV_PolyDraw( PHYSDEV dev, const POINT *pts, const BYTE *types, DWORD count )
711 {
712 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
713 EMRPOLYDRAW *emr;
714 BOOL ret;
715 BYTE *types_dest;
716 BOOL use_small_emr = can_use_short_points( pts, count );
717 DWORD size;
718
719 size = use_small_emr ? offsetof( EMRPOLYDRAW16, apts[count] ) : offsetof( EMRPOLYDRAW, aptl[count] );
720 size += (count + 3) & ~3;
721
722 if (!(emr = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
723
724 emr->emr.iType = use_small_emr ? EMR_POLYDRAW16 : EMR_POLYDRAW;
725 emr->emr.nSize = size;
726 emr->cptl = count;
727
728 types_dest = store_points( emr->aptl, pts, count, use_small_emr );
729 memcpy( types_dest, types, count );
730 if (count & 3) memset( types_dest + count, 0, 4 - (count & 3) );
731
732 if (!physDev->path)
733 get_points_bounds( &emr->rclBounds, pts, count, 0 );
734 else
735 emr->rclBounds = empty_bounds;
736
737 ret = EMFDRV_WriteRecord( dev, &emr->emr );
738 if (ret && !physDev->path) EMFDRV_UpdateBBox( dev, &emr->rclBounds );
739 HeapFree( GetProcessHeap(), 0, emr );
740 return ret;
741 }
742
743
744 /**********************************************************************
745 * EMFDRV_ExtFloodFill
746 */
747 BOOL EMFDRV_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT fillType )
748 {
749 EMREXTFLOODFILL emr;
750
751 emr.emr.iType = EMR_EXTFLOODFILL;
752 emr.emr.nSize = sizeof(emr);
753 emr.ptlStart.x = x;
754 emr.ptlStart.y = y;
755 emr.crColor = color;
756 emr.iMode = fillType;
757
758 return EMFDRV_WriteRecord( dev, &emr.emr );
759 }
760
761
762 /*********************************************************************
763 * EMFDRV_FillRgn
764 */
765 BOOL EMFDRV_FillRgn( PHYSDEV dev, HRGN hrgn, HBRUSH hbrush )
766 {
767 EMRFILLRGN *emr;
768 DWORD size, rgnsize, index;
769 BOOL ret;
770
771 index = EMFDRV_CreateBrushIndirect( dev, hbrush );
772 if(!index) return FALSE;
773
774 rgnsize = GetRegionData( hrgn, 0, NULL );
775 size = rgnsize + offsetof(EMRFILLRGN,RgnData);
776 emr = HeapAlloc( GetProcessHeap(), 0, size );
777
778 GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
779
780 emr->emr.iType = EMR_FILLRGN;
781 emr->emr.nSize = size;
782 emr->rclBounds.left = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
783 emr->rclBounds.top = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
784 emr->rclBounds.right = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
785 emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
786 emr->cbRgnData = rgnsize;
787 emr->ihBrush = index;
788
789 ret = EMFDRV_WriteRecord( dev, &emr->emr );
790 if(ret)
791 EMFDRV_UpdateBBox( dev, &emr->rclBounds );
792 HeapFree( GetProcessHeap(), 0, emr );
793 return ret;
794 }
795 /*********************************************************************
796 * EMFDRV_FrameRgn
797 */
798 BOOL EMFDRV_FrameRgn( PHYSDEV dev, HRGN hrgn, HBRUSH hbrush, INT width, INT height )
799 {
800 EMRFRAMERGN *emr;
801 DWORD size, rgnsize, index;
802 BOOL ret;
803
804 index = EMFDRV_CreateBrushIndirect( dev, hbrush );
805 if(!index) return FALSE;
806
807 rgnsize = GetRegionData( hrgn, 0, NULL );
808 size = rgnsize + offsetof(EMRFRAMERGN,RgnData);
809 emr = HeapAlloc( GetProcessHeap(), 0, size );
810
811 GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
812
813 emr->emr.iType = EMR_FRAMERGN;
814 emr->emr.nSize = size;
815 emr->rclBounds.left = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
816 emr->rclBounds.top = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
817 emr->rclBounds.right = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
818 emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
819 emr->cbRgnData = rgnsize;
820 emr->ihBrush = index;
821 emr->szlStroke.cx = width;
822 emr->szlStroke.cy = height;
823
824 ret = EMFDRV_WriteRecord( dev, &emr->emr );
825 if(ret)
826 EMFDRV_UpdateBBox( dev, &emr->rclBounds );
827 HeapFree( GetProcessHeap(), 0, emr );
828 return ret;
829 }
830
831 /*********************************************************************
832 * EMFDRV_PaintInvertRgn
833 *
834 * Helper for EMFDRV_{Paint|Invert}Rgn
835 */
836 static BOOL EMFDRV_PaintInvertRgn( PHYSDEV dev, HRGN hrgn, DWORD iType )
837 {
838 EMRINVERTRGN *emr;
839 DWORD size, rgnsize;
840 BOOL ret;
841
842
843 rgnsize = GetRegionData( hrgn, 0, NULL );
844 size = rgnsize + offsetof(EMRINVERTRGN,RgnData);
845 emr = HeapAlloc( GetProcessHeap(), 0, size );
846
847 GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
848
849 emr->emr.iType = iType;
850 emr->emr.nSize = size;
851 emr->rclBounds.left = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
852 emr->rclBounds.top = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
853 emr->rclBounds.right = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
854 emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
855 emr->cbRgnData = rgnsize;
856
857 ret = EMFDRV_WriteRecord( dev, &emr->emr );
858 if(ret)
859 EMFDRV_UpdateBBox( dev, &emr->rclBounds );
860 HeapFree( GetProcessHeap(), 0, emr );
861 return ret;
862 }
863
864 /**********************************************************************
865 * EMFDRV_PaintRgn
866 */
867 BOOL EMFDRV_PaintRgn( PHYSDEV dev, HRGN hrgn )
868 {
869 return EMFDRV_PaintInvertRgn( dev, hrgn, EMR_PAINTRGN );
870 }
871
872 /**********************************************************************
873 * EMFDRV_InvertRgn
874 */
875 BOOL EMFDRV_InvertRgn( PHYSDEV dev, HRGN hrgn )
876 {
877 return EMFDRV_PaintInvertRgn( dev, hrgn, EMR_INVERTRGN );
878 }
879
880 /**********************************************************************
881 * EMFDRV_ExtTextOut
882 */
883 BOOL EMFDRV_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *lprect,
884 LPCWSTR str, UINT count, const INT *lpDx )
885 {
886 EMFDRV_PDEVICE *physDev = get_emf_physdev( dev );
887 #ifndef __REACTOS__
888 DC *dc = get_physdev_dc( dev );
889 #endif
890 EMREXTTEXTOUTW *pemr;
891 DWORD nSize;
892 BOOL ret;
893 int textHeight = 0;
894 int textWidth = 0;
895 #ifdef __REACTOS__
896 const UINT textAlign = GetTextAlign( dev->hdc );
897 const INT graphicsMode = GetGraphicsMode( dev->hdc );
898 #else
899 const UINT textAlign = dc->textAlign;
900 const INT graphicsMode = dc->GraphicsMode;
901 #endif
902 FLOAT exScale, eyScale;
903
904 nSize = sizeof(*pemr) + ((count+1) & ~1) * sizeof(WCHAR) + count * sizeof(INT);
905
906 TRACE("%s %s count %d nSize = %d\n", debugstr_wn(str, count),
907 wine_dbgstr_rect(lprect), count, nSize);
908 pemr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nSize);
909
910 if (graphicsMode == GM_COMPATIBLE)
911 {
912 const INT horzSize = GetDeviceCaps( dev->hdc, HORZSIZE );
913 const INT horzRes = GetDeviceCaps( dev->hdc, HORZRES );
914 const INT vertSize = GetDeviceCaps( dev->hdc, VERTSIZE );
915 const INT vertRes = GetDeviceCaps( dev->hdc, VERTRES );
916 SIZE wndext, vportext;
917
918 GetViewportExtEx( dev->hdc, &vportext );
919 GetWindowExtEx( dev->hdc, &wndext );
920 exScale = 100.0 * ((FLOAT)horzSize / (FLOAT)horzRes) /
921 ((FLOAT)wndext.cx / (FLOAT)vportext.cx);
922 eyScale = 100.0 * ((FLOAT)vertSize / (FLOAT)vertRes) /
923 ((FLOAT)wndext.cy / (FLOAT)vportext.cy);
924 }
925 else
926 {
927 exScale = 0.0;
928 eyScale = 0.0;
929 }
930
931 pemr->emr.iType = EMR_EXTTEXTOUTW;
932 pemr->emr.nSize = nSize;
933 pemr->iGraphicsMode = graphicsMode;
934 pemr->exScale = exScale;
935 pemr->eyScale = eyScale;
936 pemr->emrtext.ptlReference.x = x;
937 pemr->emrtext.ptlReference.y = y;
938 pemr->emrtext.nChars = count;
939 pemr->emrtext.offString = sizeof(*pemr);
940 memcpy((char*)pemr + pemr->emrtext.offString, str, count * sizeof(WCHAR));
941 pemr->emrtext.fOptions = flags;
942 if(!lprect) {
943 pemr->emrtext.rcl.left = pemr->emrtext.rcl.top = 0;
944 pemr->emrtext.rcl.right = pemr->emrtext.rcl.bottom = -1;
945 } else {
946 pemr->emrtext.rcl.left = lprect->left;
947 pemr->emrtext.rcl.top = lprect->top;
948 pemr->emrtext.rcl.right = lprect->right;
949 pemr->emrtext.rcl.bottom = lprect->bottom;
950 }
951
952 pemr->emrtext.offDx = pemr->emrtext.offString + ((count+1) & ~1) * sizeof(WCHAR);
953 if(lpDx) {
954 UINT i;
955 SIZE strSize;
956 memcpy((char*)pemr + pemr->emrtext.offDx, lpDx, count * sizeof(INT));
957 for (i = 0; i < count; i++) {
958 textWidth += lpDx[i];
959 }
960 if (GetTextExtentPoint32W( dev->hdc, str, count, &strSize ))
961 textHeight = strSize.cy;
962 }
963 else {
964 UINT i;
965 INT *dx = (INT *)((char*)pemr + pemr->emrtext.offDx);
966 SIZE charSize;
967 for (i = 0; i < count; i++) {
968 if (GetTextExtentPoint32W( dev->hdc, str + i, 1, &charSize )) {
969 dx[i] = charSize.cx;
970 textWidth += charSize.cx;
971 textHeight = max(textHeight, charSize.cy);
972 }
973 }
974 }
975
976 if (physDev->path)
977 {
978 pemr->rclBounds.left = pemr->rclBounds.top = 0;
979 pemr->rclBounds.right = pemr->rclBounds.bottom = -1;
980 goto no_bounds;
981 }
982
983 /* FIXME: handle font escapement */
984 switch (textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER)) {
985 case TA_CENTER: {
986 pemr->rclBounds.left = x - (textWidth / 2) - 1;
987 pemr->rclBounds.right = x + (textWidth / 2) + 1;
988 break;
989 }
990 case TA_RIGHT: {
991 pemr->rclBounds.left = x - textWidth - 1;
992 pemr->rclBounds.right = x;
993 break;
994 }
995 default: { /* TA_LEFT */
996 pemr->rclBounds.left = x;
997 pemr->rclBounds.right = x + textWidth + 1;
998 }
999 }
1000
1001 switch (textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE)) {
1002 case TA_BASELINE: {
1003 TEXTMETRICW tm;
1004 if (!GetTextMetricsW( dev->hdc, &tm ))
1005 tm.tmDescent = 0;
1006 /* Play safe here... it's better to have a bounding box */
1007 /* that is too big than too small. */
1008 pemr->rclBounds.top = y - textHeight - 1;
1009 pemr->rclBounds.bottom = y + tm.tmDescent + 1;
1010 break;
1011 }
1012 case TA_BOTTOM: {
1013 pemr->rclBounds.top = y - textHeight - 1;
1014 pemr->rclBounds.bottom = y;
1015 break;
1016 }
1017 default: { /* TA_TOP */
1018 pemr->rclBounds.top = y;
1019 pemr->rclBounds.bottom = y + textHeight + 1;
1020 }
1021 }
1022 EMFDRV_UpdateBBox( dev, &pemr->rclBounds );
1023
1024 no_bounds:
1025 ret = EMFDRV_WriteRecord( dev, &pemr->emr );
1026 HeapFree( GetProcessHeap(), 0, pemr );
1027 return ret;
1028 }
1029
1030 /**********************************************************************
1031 * EMFDRV_GradientFill
1032 */
1033 BOOL EMFDRV_GradientFill( PHYSDEV dev, TRIVERTEX *vert_array, ULONG nvert,
1034 void *grad_array, ULONG ngrad, ULONG mode )
1035 {
1036 EMRGRADIENTFILL *emr;
1037 ULONG i, pt, size, num_pts = ngrad * (mode == GRADIENT_FILL_TRIANGLE ? 3 : 2);
1038 const ULONG *pts = (const ULONG *)grad_array;
1039 BOOL ret;
1040
1041 size = FIELD_OFFSET(EMRGRADIENTFILL, Ver[nvert]) + num_pts * sizeof(pts[0]);
1042
1043 emr = HeapAlloc( GetProcessHeap(), 0, size );
1044 if (!emr) return FALSE;
1045
1046 for (i = 0; i < num_pts; i++)
1047 {
1048 pt = pts[i];
1049
1050 if (i == 0)
1051 {
1052 emr->rclBounds.left = emr->rclBounds.right = vert_array[pt].x;
1053 emr->rclBounds.top = emr->rclBounds.bottom = vert_array[pt].y;
1054 }
1055 else
1056 {
1057 if (vert_array[pt].x < emr->rclBounds.left)
1058 emr->rclBounds.left = vert_array[pt].x;
1059 else if (vert_array[pt].x > emr->rclBounds.right)
1060 emr->rclBounds.right = vert_array[pt].x;
1061 if (vert_array[pt].y < emr->rclBounds.top)
1062 emr->rclBounds.top = vert_array[pt].y;
1063 else if (vert_array[pt].y > emr->rclBounds.bottom)
1064 emr->rclBounds.bottom = vert_array[pt].y;
1065 }
1066 }
1067 emr->rclBounds.right--;
1068 emr->rclBounds.bottom--;
1069
1070 emr->emr.iType = EMR_GRADIENTFILL;
1071 emr->emr.nSize = size;
1072 emr->nVer = nvert;
1073 emr->nTri = ngrad;
1074 emr->ulMode = mode;
1075 memcpy( emr->Ver, vert_array, nvert * sizeof(vert_array[0]) );
1076 memcpy( emr->Ver + nvert, pts, num_pts * sizeof(pts[0]) );
1077
1078 EMFDRV_UpdateBBox( dev, &emr->rclBounds );
1079 ret = EMFDRV_WriteRecord( dev, &emr->emr );
1080 HeapFree( GetProcessHeap(), 0, emr );
1081 return ret;
1082 }
1083
1084 /**********************************************************************
1085 * EMFDRV_FillPath
1086 */
1087 BOOL EMFDRV_FillPath( PHYSDEV dev )
1088 {
1089 return emfdrv_stroke_and_fill_path( dev, EMR_FILLPATH );
1090 }
1091
1092 /**********************************************************************
1093 * EMFDRV_StrokeAndFillPath
1094 */
1095 BOOL EMFDRV_StrokeAndFillPath( PHYSDEV dev )
1096 {
1097 return emfdrv_stroke_and_fill_path( dev, EMR_STROKEANDFILLPATH );
1098 }
1099
1100 /**********************************************************************
1101 * EMFDRV_StrokePath
1102 */
1103 BOOL EMFDRV_StrokePath( PHYSDEV dev )
1104 {
1105 return emfdrv_stroke_and_fill_path( dev, EMR_STROKEPATH );
1106 }
1107