2 * Enhanced MetaFile driver
4 * Copyright 1999 Huw D M Davies
5 * Copyright 2021 Jacek Caban for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "ntgdi_private.h"
31 struct gdi_physdev dev
;
32 INT dev_caps
[COLORMGMTCAPS
+ 1];
35 static inline EMFDRV_PDEVICE
*get_emf_physdev( PHYSDEV dev
)
37 return CONTAINING_RECORD( dev
, EMFDRV_PDEVICE
, dev
);
40 static void emfdrv_update_bounds( DC
*dc
, RECTL
*rect
)
42 RECTL
*bounds
= &dc
->attr
->emf_bounds
;
43 RECTL vport_rect
= *rect
;
45 lp_to_dp( dc
, (POINT
*)&vport_rect
, 2 );
47 /* The coordinate systems may be mirrored
48 (LPtoDP handles points, not rectangles) */
49 if (vport_rect
.left
> vport_rect
.right
)
51 LONG temp
= vport_rect
.right
;
52 vport_rect
.right
= vport_rect
.left
;
53 vport_rect
.left
= temp
;
55 if (vport_rect
.top
> vport_rect
.bottom
)
57 LONG temp
= vport_rect
.bottom
;
58 vport_rect
.bottom
= vport_rect
.top
;
59 vport_rect
.top
= temp
;
62 if (bounds
->left
> bounds
->right
)
64 /* first bounding rectangle */
69 bounds
->left
= min( bounds
->left
, vport_rect
.left
);
70 bounds
->top
= min( bounds
->top
, vport_rect
.top
);
71 bounds
->right
= max( bounds
->right
, vport_rect
.right
);
72 bounds
->bottom
= max( bounds
->bottom
, vport_rect
.bottom
);
76 static BOOL CDECL
EMFDRV_LineTo( PHYSDEV dev
, INT x
, INT y
)
78 DC
*dc
= get_physdev_dc( dev
);
82 pt
= dc
->attr
->cur_pos
;
84 bounds
.left
= min( x
, pt
.x
);
85 bounds
.top
= min( y
, pt
.y
);
86 bounds
.right
= max( x
, pt
.x
);
87 bounds
.bottom
= max( y
, pt
.y
);
88 emfdrv_update_bounds( dc
, &bounds
);
92 static BOOL CDECL
EMFDRV_RoundRect( PHYSDEV dev
, INT left
, INT top
, INT right
,
93 INT bottom
, INT ell_width
, INT ell_height
)
95 DC
*dc
= get_physdev_dc( dev
);
98 if (left
== right
|| top
== bottom
) return FALSE
;
100 bounds
.left
= min( left
, right
);
101 bounds
.top
= min( top
, bottom
);
102 bounds
.right
= max( left
, right
);
103 bounds
.bottom
= max( top
, bottom
);
104 if (dc
->attr
->graphics_mode
== GM_COMPATIBLE
)
110 emfdrv_update_bounds( dc
, &bounds
);
114 static BOOL
EMFDRV_ArcChordPie( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
115 INT xstart
, INT ystart
, INT xend
, INT yend
, DWORD type
)
117 DC
*dc
= get_physdev_dc( dev
);
118 INT temp
, x_centre
, y_centre
, i
;
119 double angle_start
, angle_end
;
120 double xinter_start
, yinter_start
, xinter_end
, yinter_end
;
124 if (left
== right
|| top
== bottom
) return FALSE
;
126 if (left
> right
) { temp
= left
; left
= right
; right
= temp
; }
127 if (top
> bottom
) { temp
= top
; top
= bottom
; bottom
= temp
; }
129 if (dc
->attr
->graphics_mode
== GM_COMPATIBLE
)
135 emr
.emr
.iType
= type
;
136 emr
.emr
.nSize
= sizeof(emr
);
137 emr
.rclBox
.left
= left
;
138 emr
.rclBox
.top
= top
;
139 emr
.rclBox
.right
= right
;
140 emr
.rclBox
.bottom
= bottom
;
141 emr
.ptlStart
.x
= xstart
;
142 emr
.ptlStart
.y
= ystart
;
146 /* Now calculate the BBox */
147 x_centre
= (left
+ right
+ 1) / 2;
148 y_centre
= (top
+ bottom
+ 1) / 2;
155 /* invert y co-ords to get angle anti-clockwise from x-axis */
156 angle_start
= atan2( -(double)ystart
, (double)xstart
);
157 angle_end
= atan2( -(double)yend
, (double)xend
);
159 /* These are the intercepts of the start/end lines with the arc */
160 xinter_start
= (right
- left
+ 1)/2 * cos(angle_start
) + x_centre
;
161 yinter_start
= -(bottom
- top
+ 1)/2 * sin(angle_start
) + y_centre
;
162 xinter_end
= (right
- left
+ 1)/2 * cos(angle_end
) + x_centre
;
163 yinter_end
= -(bottom
- top
+ 1)/2 * sin(angle_end
) + y_centre
;
165 if (angle_start
< 0) angle_start
+= 2 * M_PI
;
166 if (angle_end
< 0) angle_end
+= 2 * M_PI
;
167 if (angle_end
< angle_start
) angle_end
+= 2 * M_PI
;
169 bounds
.left
= min( xinter_start
, xinter_end
);
170 bounds
.top
= min( yinter_start
, yinter_end
);
171 bounds
.right
= max( xinter_start
, xinter_end
);
172 bounds
.bottom
= max( yinter_start
, yinter_end
);
174 for (i
= 0; i
<= 8; i
++)
176 if(i
* M_PI
/ 2 < angle_start
) /* loop until we're past start */
178 if(i
* M_PI
/ 2 > angle_end
) /* if we're past end we're finished */
181 /* the arc touches the rectangle at the start of quadrant i, so adjust
182 BBox to reflect this. */
186 bounds
.right
= right
;
195 bounds
.bottom
= bottom
;
200 /* If we're drawing a pie then make sure we include the centre */
203 if (bounds
.left
> x_centre
) bounds
.left
= x_centre
;
204 else if (bounds
.right
< x_centre
) bounds
.right
= x_centre
;
205 if (bounds
.top
> y_centre
) bounds
.top
= y_centre
;
206 else if (bounds
.bottom
< y_centre
) bounds
.bottom
= y_centre
;
208 else if (type
== EMR_ARCTO
)
211 pt
= dc
->attr
->cur_pos
;
212 bounds
.left
= min( bounds
.left
, pt
.x
);
213 bounds
.top
= min( bounds
.top
, pt
.y
);
214 bounds
.right
= max( bounds
.right
, pt
.x
);
215 bounds
.bottom
= max( bounds
.bottom
, pt
.y
);
217 emfdrv_update_bounds( dc
, &bounds
);
221 static BOOL CDECL
EMFDRV_Arc( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
222 INT xstart
, INT ystart
, INT xend
, INT yend
)
224 return EMFDRV_ArcChordPie( dev
, left
, top
, right
, bottom
, xstart
, ystart
,
225 xend
, yend
, EMR_ARC
);
228 static BOOL CDECL
EMFDRV_ArcTo( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
229 INT xstart
, INT ystart
, INT xend
, INT yend
)
231 return EMFDRV_ArcChordPie( dev
, left
, top
, right
, bottom
, xstart
, ystart
,
232 xend
, yend
, EMR_ARCTO
);
235 static BOOL CDECL
EMFDRV_Pie( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
236 INT xstart
, INT ystart
, INT xend
, INT yend
)
238 return EMFDRV_ArcChordPie( dev
, left
, top
, right
, bottom
, xstart
, ystart
,
239 xend
, yend
, EMR_PIE
);
242 static BOOL CDECL
EMFDRV_Chord( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
,
243 INT xstart
, INT ystart
, INT xend
, INT yend
)
245 return EMFDRV_ArcChordPie( dev
, left
, top
, right
, bottom
, xstart
, ystart
,
246 xend
, yend
, EMR_CHORD
);
249 static BOOL CDECL
EMFDRV_Ellipse( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
251 DC
*dc
= get_physdev_dc( dev
);
254 if (left
== right
|| top
== bottom
) return FALSE
;
256 bounds
.left
= min( left
, right
);
257 bounds
.top
= min( top
, bottom
);
258 bounds
.right
= max( left
, right
);
259 bounds
.bottom
= max( top
, bottom
);
260 if (dc
->attr
->graphics_mode
== GM_COMPATIBLE
)
266 emfdrv_update_bounds( dc
, &bounds
);
270 static BOOL CDECL
EMFDRV_Rectangle( PHYSDEV dev
, INT left
, INT top
, INT right
, INT bottom
)
272 DC
*dc
= get_physdev_dc( dev
);
275 if (left
== right
|| top
== bottom
) return FALSE
;
277 bounds
.left
= min( left
, right
);
278 bounds
.top
= min( top
, bottom
);
279 bounds
.right
= max( left
, right
);
280 bounds
.bottom
= max( top
, bottom
);
281 if (dc
->attr
->graphics_mode
== GM_COMPATIBLE
)
287 emfdrv_update_bounds( dc
, &bounds
);
291 static COLORREF CDECL
EMFDRV_SetPixel( PHYSDEV dev
, INT x
, INT y
, COLORREF color
)
293 DC
*dc
= get_physdev_dc( dev
);
296 bounds
.left
= bounds
.right
= x
;
297 bounds
.top
= bounds
.bottom
= y
;
298 emfdrv_update_bounds( dc
, &bounds
);
302 static BOOL CDECL
EMFDRV_PolylineTo( PHYSDEV dev
, const POINT
*pt
, INT count
)
304 /* FIXME: update bounding rect */
308 static BOOL CDECL
EMFDRV_PolyBezier( PHYSDEV dev
, const POINT
*pts
, DWORD count
)
310 /* FIXME: update bounding rect */
314 static BOOL CDECL
EMFDRV_PolyBezierTo( PHYSDEV dev
, const POINT
*pts
, DWORD count
)
316 /* FIXME: update bounding rect */
320 static BOOL CDECL
EMFDRV_PolyPolyline( PHYSDEV dev
, const POINT
*pt
,
321 const DWORD
*counts
, DWORD polys
)
323 /* FIXME: update bounding rect */
327 static BOOL CDECL
EMFDRV_PolyPolygon( PHYSDEV dev
, const POINT
*pt
,
328 const INT
*counts
, UINT polys
)
330 /* FIXME: update bounding rect */
334 static BOOL CDECL
EMFDRV_PolyDraw( PHYSDEV dev
, const POINT
*pts
,
335 const BYTE
*types
, DWORD count
)
337 /* FIXME: update bounding rect */
341 static BOOL CDECL
EMFDRV_FillRgn( PHYSDEV dev
, HRGN hrgn
, HBRUSH hbrush
)
343 /* FIXME: update bounding rect */
347 static BOOL CDECL
EMFDRV_FrameRgn( PHYSDEV dev
, HRGN hrgn
, HBRUSH hbrush
, INT width
, INT height
)
349 /* FIXME: update bounding rect */
353 static BOOL CDECL
EMFDRV_InvertRgn( PHYSDEV dev
, HRGN hrgn
)
355 /* FIXME: update bounding rect */
359 static BOOL CDECL
EMFDRV_ExtTextOut( PHYSDEV dev
, INT x
, INT y
, UINT flags
, const RECT
*lprect
,
360 LPCWSTR str
, UINT count
, const INT
*lpDx
)
362 /* FIXME: update bounding rect */
366 static BOOL CDECL
EMFDRV_GradientFill( PHYSDEV dev
, TRIVERTEX
*vert_array
, ULONG nvert
,
367 void *grad_array
, ULONG ngrad
, ULONG mode
)
369 /* FIXME: update bounding rect */
373 static BOOL CDECL
EMFDRV_FillPath( PHYSDEV dev
)
375 /* FIXME: update bound rect */
379 static BOOL CDECL
EMFDRV_StrokeAndFillPath( PHYSDEV dev
)
381 /* FIXME: update bound rect */
385 static BOOL CDECL
EMFDRV_StrokePath( PHYSDEV dev
)
387 /* FIXME: update bound rect */
391 static BOOL CDECL
EMFDRV_AlphaBlend( PHYSDEV dev_dst
, struct bitblt_coords
*dst
,
392 PHYSDEV dev_src
, struct bitblt_coords
*src
,
395 /* FIXME: update bound rect */
399 static BOOL CDECL
EMFDRV_PatBlt( PHYSDEV dev
, struct bitblt_coords
*dst
, DWORD rop
)
401 /* FIXME: update bound rect */
405 static HBITMAP CDECL
EMFDRV_SelectBitmap( PHYSDEV dev
, HBITMAP hbitmap
)
410 static HFONT CDECL
EMFDRV_SelectFont( PHYSDEV dev
, HFONT font
, UINT
*aa_flags
)
412 *aa_flags
= GGO_BITMAP
; /* no point in anti-aliasing on metafiles */
414 dev
= GET_NEXT_PHYSDEV( dev
, pSelectFont
);
415 return dev
->funcs
->pSelectFont( dev
, font
, aa_flags
);
418 static INT CDECL
EMFDRV_GetDeviceCaps( PHYSDEV dev
, INT cap
)
420 EMFDRV_PDEVICE
*physDev
= get_emf_physdev( dev
);
422 if (cap
>= 0 && cap
< ARRAY_SIZE( physDev
->dev_caps
))
423 return physDev
->dev_caps
[cap
];
427 static BOOL CDECL
EMFDRV_DeleteDC( PHYSDEV dev
)
429 EMFDRV_PDEVICE
*physDev
= get_emf_physdev( dev
);
434 static const struct gdi_dc_funcs emfdrv_driver
=
436 NULL
, /* pAbortDoc */
437 NULL
, /* pAbortPath */
438 EMFDRV_AlphaBlend
, /* pAlphaBlend */
439 NULL
, /* pAngleArc */
440 EMFDRV_Arc
, /* pArc */
441 EMFDRV_ArcTo
, /* pArcTo */
442 NULL
, /* pBeginPath */
443 NULL
, /* pBlendImage */
444 EMFDRV_Chord
, /* pChord */
445 NULL
, /* pCloseFigure */
446 NULL
, /* pCreateCompatibleDC */
447 NULL
, /* pCreateDC */
448 EMFDRV_DeleteDC
, /* pDeleteDC */
449 NULL
, /* pDeleteObject */
450 EMFDRV_Ellipse
, /* pEllipse */
454 NULL
, /* pEnumFonts */
455 NULL
, /* pExtEscape */
456 NULL
, /* pExtFloodFill */
457 EMFDRV_ExtTextOut
, /* pExtTextOut */
458 EMFDRV_FillPath
, /* pFillPath */
459 EMFDRV_FillRgn
, /* pFillRgn */
460 NULL
, /* pFontIsLinked */
461 EMFDRV_FrameRgn
, /* pFrameRgn */
462 NULL
, /* pGetBoundsRect */
463 NULL
, /* pGetCharABCWidths */
464 NULL
, /* pGetCharABCWidthsI */
465 NULL
, /* pGetCharWidth */
466 NULL
, /* pGetCharWidthInfo */
467 EMFDRV_GetDeviceCaps
, /* pGetDeviceCaps */
468 NULL
, /* pGetDeviceGammaRamp */
469 NULL
, /* pGetFontData */
470 NULL
, /* pGetFontRealizationInfo */
471 NULL
, /* pGetFontUnicodeRanges */
472 NULL
, /* pGetGlyphIndices */
473 NULL
, /* pGetGlyphOutline */
474 NULL
, /* pGetICMProfile */
475 NULL
, /* pGetImage */
476 NULL
, /* pGetKerningPairs */
477 NULL
, /* pGetNearestColor */
478 NULL
, /* pGetOutlineTextMetrics */
479 NULL
, /* pGetPixel */
480 NULL
, /* pGetSystemPaletteEntries */
481 NULL
, /* pGetTextCharsetInfo */
482 NULL
, /* pGetTextExtentExPoint */
483 NULL
, /* pGetTextExtentExPointI */
484 NULL
, /* pGetTextFace */
485 NULL
, /* pGetTextMetrics */
486 EMFDRV_GradientFill
, /* pGradientFill */
487 EMFDRV_InvertRgn
, /* pInvertRgn */
488 EMFDRV_LineTo
, /* pLineTo */
490 NULL
, /* pPaintRgn */
491 EMFDRV_PatBlt
, /* pPatBlt */
492 EMFDRV_Pie
, /* pPie */
493 EMFDRV_PolyBezier
, /* pPolyBezier */
494 EMFDRV_PolyBezierTo
, /* pPolyBezierTo */
495 EMFDRV_PolyDraw
, /* pPolyDraw */
496 EMFDRV_PolyPolygon
, /* pPolyPolygon */
497 EMFDRV_PolyPolyline
, /* pPolyPolyline */
498 EMFDRV_PolylineTo
, /* pPolylineTo */
499 NULL
, /* pPutImage */
500 NULL
, /* pRealizeDefaultPalette */
501 NULL
, /* pRealizePalette */
502 EMFDRV_Rectangle
, /* pRectangle */
504 EMFDRV_RoundRect
, /* pRoundRect */
505 EMFDRV_SelectBitmap
, /* pSelectBitmap */
506 NULL
, /* pSelectBrush */
507 EMFDRV_SelectFont
, /* pSelectFont */
508 NULL
, /* pSelectPen */
509 NULL
, /* pSetBkColor */
510 NULL
, /* pSetBoundsRect */
511 NULL
, /* pSetDCBrushColor*/
512 NULL
, /* pSetDCPenColor*/
513 NULL
, /* pSetDIBitsToDevice */
514 NULL
, /* pSetDeviceClipping */
515 NULL
, /* pSetDeviceGammaRamp */
516 EMFDRV_SetPixel
, /* pSetPixel */
517 NULL
, /* pSetTextColor */
518 NULL
, /* pStartDoc */
519 NULL
, /* pStartPage */
520 NULL
, /* pStretchBlt */
521 NULL
, /* pStretchDIBits */
522 EMFDRV_StrokeAndFillPath
, /* pStrokeAndFillPath */
523 EMFDRV_StrokePath
, /* pStrokePath */
524 NULL
, /* pUnrealizePalette */
525 NULL
, /* pD3DKMTCheckVidPnExclusiveOwnership */
526 NULL
, /* pD3DKMTCloseAdapter */
527 NULL
, /* pD3DKMTOpenAdapterFromLuid */
528 NULL
, /* pD3DKMTQueryVideoMemoryInfo */
529 NULL
, /* pD3DKMTSetVidPnSourceOwner */
530 GDI_PRIORITY_GRAPHICS_DRV
/* priority */
534 static BOOL
devcap_is_valid( int cap
)
536 if (cap
>= 0 && cap
<= ASPECTXY
) return !(cap
& 1);
537 if (cap
>= PHYSICALWIDTH
&& cap
<= COLORMGMTCAPS
) return TRUE
;
551 /**********************************************************************
552 * NtGdiCreateMetafileDC (win32u.@)
554 HDC WINAPI
NtGdiCreateMetafileDC( HDC hdc
)
556 EMFDRV_PDEVICE
*physDev
;
561 if (!(dc
= alloc_dc_ptr( NTGDI_OBJ_ENHMETADC
))) return 0;
563 physDev
= malloc( sizeof(*physDev
) );
570 push_dc_driver( &dc
->physDev
, &physDev
->dev
, &emfdrv_driver
);
572 if (hdc
) /* if no ref, use current display */
575 ref_dc
= NtGdiOpenDCW( NULL
, NULL
, NULL
, 0, TRUE
, NULL
, NULL
, NULL
);
577 memset( physDev
->dev_caps
, 0, sizeof(physDev
->dev_caps
) );
578 for (cap
= 0; cap
< ARRAY_SIZE( physDev
->dev_caps
); cap
++)
579 if (devcap_is_valid( cap
))
580 physDev
->dev_caps
[cap
] = NtGdiGetDeviceCaps( ref_dc
, cap
);
582 if (!hdc
) NtGdiDeleteObjectApp( ref_dc
);
584 NtGdiSetVirtualResolution( dc
->hSelf
, 0, 0, 0, 0 );
587 release_dc_ptr( dc
);