Fixed creating wxGraphicContext from native DC with Cairo renderer (MSW).
When x- or y-coordinate of DC origin > 0 then surface created with cairo_win32_surface_create() is not fully operational (for some Cairo operations memory access violation errors occur - see Cairo bug 96482) so in this case we would need to apply a workaround and pass non-transformed DC to Cairo and next apply original DC transformation to the Cairo context operations on our own. Closes #17564
This commit is contained in:
parent
a735371b0e
commit
f06bfe37ae
@ -238,6 +238,8 @@
|
||||
(), () , NULL ) \
|
||||
m( cairo_surface_t*, cairo_surface_create_similar_image, \
|
||||
(cairo_surface_t *other, cairo_format_t format, int width, int height), (other, format, width, height), NULL) \
|
||||
m( cairo_status_t, cairo_surface_status, \
|
||||
(cairo_surface_t *surface), (surface), CAIRO_STATUS_SUCCESS) \
|
||||
wxCAIRO_PLATFORM_METHODS(m)
|
||||
|
||||
#define wxCAIRO_DECLARE_TYPE(rettype, name, args, argnames, defret) \
|
||||
|
@ -1869,15 +1869,15 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
||||
|
||||
HDC hdc = (HDC)dc.GetHDC();
|
||||
m_mswStateSavedDC = ::SaveDC(hdc);
|
||||
bool hasBitmap = false;
|
||||
bool hasBitmap = false; // To signal that Cairo context is created
|
||||
// from raw bitmap and not from DC.
|
||||
bool adjustTransformFromDC = false; // To signal that we have to transfer
|
||||
// transformation settings from source
|
||||
// wxDC to Cairo context on our own.
|
||||
|
||||
// cairo_win32_surface_create creates a 24-bit bitmap,
|
||||
// so if we 32bpp bitmap, we need to create a 32-bit surface instead.
|
||||
if (bmp.GetDepth() < 32)
|
||||
{
|
||||
m_mswSurface = cairo_win32_surface_create(hdc);
|
||||
}
|
||||
else
|
||||
if ( bmp.GetDepth() == 32 )
|
||||
{
|
||||
#if wxUSE_WXDIB
|
||||
// We need to convert the currently selected bitmap to a DIB
|
||||
@ -1892,7 +1892,7 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
||||
|
||||
bmp.ConvertToDIB(); // Does nothing if already a DIB.
|
||||
|
||||
if (!bmp.HasAlpha())
|
||||
if ( !bmp.HasAlpha() )
|
||||
{
|
||||
// Initialize alpha channel, even if we don't have any alpha yet,
|
||||
// we should have correct (opaque) alpha values in it for Cairo
|
||||
@ -1940,8 +1940,20 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
||||
info.bmWidth,
|
||||
info.bmHeight,
|
||||
info.bmWidthBytes);
|
||||
|
||||
hasBitmap = true;
|
||||
if ( cairo_surface_status(m_mswSurface) == CAIRO_STATUS_SUCCESS )
|
||||
{
|
||||
hasBitmap = true;
|
||||
// We will have to transfer transformation settings
|
||||
// from source wxDC to Cairo context on our own
|
||||
// because in this case they are not inherited
|
||||
// implicitly from underlying DC.
|
||||
adjustTransformFromDC = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_surface_destroy(m_mswSurface);
|
||||
m_mswSurface = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1952,29 +1964,52 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
||||
{
|
||||
wxLogLastError( wxS("wxCairoContext ctor - GetObject") );
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if we failed to create Cairo surface from 32bpp bitmap.
|
||||
if( !hasBitmap )
|
||||
// Fallback if Cairo surface hasn't been created from 32bpp bitmap.
|
||||
if( !hasBitmap )
|
||||
{
|
||||
// When x- or y-coordinate of DC origin > 0 then surface
|
||||
// created from DC is not fully operational (for some Cairo
|
||||
// operations memory access violation errors occur - see Cairo
|
||||
// bug 96482) so in this case we would need to pass non-transformed
|
||||
// DC to Cairo and to apply original DC transformation to the Cairo
|
||||
// context operations on our own.
|
||||
// We believe this bug will be fixed in the next Cairo version.
|
||||
#if CAIRO_VERSION <= CAIRO_VERSION_ENCODE(1, 15, 2)
|
||||
if ( cairo_version() <= CAIRO_VERSION_ENCODE(1, 15, 2) )
|
||||
{
|
||||
m_mswSurface = cairo_win32_surface_create(hdc);
|
||||
wxCoord orgX, orgY;
|
||||
dc.GetDeviceOrigin(&orgX, &orgY);
|
||||
if ( orgX > 0 || orgY > 0 )
|
||||
{
|
||||
::SetViewportOrgEx(hdc, 0, 0, NULL);
|
||||
::SetViewportExtEx(hdc, 1, 1, NULL);
|
||||
::SetWindowOrgEx(hdc, 0, 0, NULL);
|
||||
::SetWindowExtEx(hdc, 1, 1, NULL);
|
||||
adjustTransformFromDC = true;
|
||||
}
|
||||
}
|
||||
#endif // Cairo <= 1.15.2
|
||||
|
||||
m_mswSurface = cairo_win32_surface_create(hdc);
|
||||
wxASSERT_MSG( cairo_surface_status(m_mswSurface) == CAIRO_STATUS_SUCCESS,
|
||||
wxS("wxCairoContext ctor - Error creating Cairo surface") );
|
||||
}
|
||||
|
||||
Init( cairo_create(m_mswSurface) );
|
||||
// If we've created a image surface, we need:
|
||||
// 1. To flip the Y axis so that all drawing will appear right side up.
|
||||
// 2. To explicitly transfer transformations settings from source DC
|
||||
// to Cairo context because they are not inherited implicitly.
|
||||
// We have to remember these operations as an internal transformation
|
||||
// which is not going to be exposed through e.g. GetTransform().
|
||||
if (hasBitmap)
|
||||
{
|
||||
// Flip the Y axis.
|
||||
// If we've created an image surface we need to flip the Y axis
|
||||
// so that all drawing will appear right side up.
|
||||
// We have to remember these operations as an internal transformation
|
||||
// which is not going to be exposed through e.g. GetTransform().
|
||||
cairo_matrix_init(&m_internalTransform, 1.0, 0.0, 0.0, -1.0, 0.0, height);
|
||||
|
||||
// Transfer transformation settings from source DC to Cairo context on our own.
|
||||
ApplyTransformFromDC(dc);
|
||||
}
|
||||
// Transfer transformation settings from source wxDC
|
||||
// to Cairo context on our own, if required.
|
||||
if ( adjustTransformFromDC )
|
||||
ApplyTransformFromDC(dc);
|
||||
#endif // __WXMSW__
|
||||
|
||||
#ifdef __WXGTK3__
|
||||
@ -2062,8 +2097,58 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, HDC handle )
|
||||
: wxGraphicsContext(renderer)
|
||||
{
|
||||
m_mswStateSavedDC = ::SaveDC(handle);
|
||||
|
||||
bool adjustTransformFromDC = false; // To signal that we have to transfer
|
||||
// transformation settings from source
|
||||
// wxDC to Cairo context on our own.
|
||||
cairo_matrix_t dcTransform;
|
||||
cairo_matrix_init_identity(&dcTransform);
|
||||
// When x- or y-coordinate of DC origin > 0 then surface
|
||||
// created from DC is not fully operational (for some Cairo
|
||||
// operations memory access violation errors occur - see Cairo
|
||||
// bug 96482) so in this case we would need to pass non-transformed
|
||||
// DC to Cairo and to apply original DC transformation to the Cairo
|
||||
// context operations on our own.
|
||||
// We believe this bug will be fixed in the next Cairo version.
|
||||
#if CAIRO_VERSION <= CAIRO_VERSION_ENCODE(1, 15, 2)
|
||||
if ( cairo_version() <= CAIRO_VERSION_ENCODE(1, 15, 2) )
|
||||
{
|
||||
POINT devOrg;
|
||||
::GetViewportOrgEx(handle, &devOrg);
|
||||
if ( devOrg.x > 0 || devOrg.y > 0 )
|
||||
{
|
||||
SIZE devExt;
|
||||
::GetViewportExtEx(handle, &devExt);
|
||||
POINT logOrg;
|
||||
::GetWindowOrgEx(handle, &logOrg);
|
||||
SIZE logExt;
|
||||
::GetWindowExtEx(handle, &logExt);
|
||||
|
||||
double sx = (double)devExt.cx / logExt.cx;
|
||||
double sy = (double)devExt.cy / logExt.cy;
|
||||
|
||||
cairo_matrix_translate(&dcTransform, devOrg.x, devOrg.y);
|
||||
cairo_matrix_scale(&dcTransform, sx, sy);
|
||||
cairo_matrix_translate(&dcTransform, -logOrg.x, -logOrg.y);
|
||||
|
||||
::SetViewportOrgEx(handle, 0, 0, NULL);
|
||||
::SetViewportExtEx(handle, 1, 1, NULL);
|
||||
::SetWindowOrgEx(handle, 0, 0, NULL);
|
||||
::SetWindowExtEx(handle, 1, 1, NULL);
|
||||
|
||||
adjustTransformFromDC = true;
|
||||
}
|
||||
}
|
||||
#endif // Cairo <= 1.15.2
|
||||
m_mswSurface = cairo_win32_surface_create(handle);
|
||||
Init( cairo_create(m_mswSurface) );
|
||||
if ( adjustTransformFromDC )
|
||||
{
|
||||
cairo_matrix_multiply(&m_internalTransform,
|
||||
&dcTransform, &m_internalTransform);
|
||||
cairo_set_matrix(m_context, &m_internalTransform);
|
||||
}
|
||||
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
// Try to determine DC size.
|
||||
@ -2088,6 +2173,7 @@ wxCairoContext::wxCairoContext(wxGraphicsRenderer* renderer, HWND hWnd)
|
||||
|
||||
m_mswStateSavedDC = 0;
|
||||
m_mswSurface = cairo_win32_surface_create((HDC)m_mswWindowHDC);
|
||||
|
||||
Init(cairo_create(m_mswSurface));
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
@ -2178,7 +2264,8 @@ wxCairoContext::~wxCairoContext()
|
||||
#ifdef __WXMSW__
|
||||
if ( m_mswSurface )
|
||||
{
|
||||
HDC hdc = cairo_win32_surface_get_dc(m_mswSurface);
|
||||
// HDC hdc = cairo_win32_surface_get_dc(m_mswSurface);
|
||||
HDC hdc = 0;
|
||||
|
||||
cairo_surface_destroy(m_mswSurface);
|
||||
|
||||
@ -2219,8 +2306,8 @@ void wxCairoContext::Init(cairo_t *context)
|
||||
|
||||
void wxCairoContext::ApplyTransformFromDC(const wxDC& dc, ApplyTransformMode mode)
|
||||
{
|
||||
// Transfer transformation settings from source DC to Cairo context
|
||||
// and store it as an internal transformation
|
||||
// Transfer transformation settings from source wxDC
|
||||
// to Cairo context and store them as an internal transformation
|
||||
// (which is not going to be exposed).
|
||||
double sx, sy;
|
||||
dc.GetUserScale(&sx, &sy);
|
||||
@ -2242,7 +2329,6 @@ void wxCairoContext::ApplyTransformFromDC(const wxDC& dc, ApplyTransformMode mod
|
||||
org = dc.GetLogicalOrigin();
|
||||
cairo_matrix_translate(&m_internalTransform, -org.x, -org.y);
|
||||
|
||||
// Apply all above transformations.
|
||||
cairo_set_matrix(m_context, &m_internalTransform);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user