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:
@@ -238,6 +238,8 @@
|
|||||||
(), () , NULL ) \
|
(), () , NULL ) \
|
||||||
m( cairo_surface_t*, cairo_surface_create_similar_image, \
|
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) \
|
(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)
|
wxCAIRO_PLATFORM_METHODS(m)
|
||||||
|
|
||||||
#define wxCAIRO_DECLARE_TYPE(rettype, name, args, argnames, defret) \
|
#define wxCAIRO_DECLARE_TYPE(rettype, name, args, argnames, defret) \
|
||||||
|
@@ -1869,15 +1869,15 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
|||||||
|
|
||||||
HDC hdc = (HDC)dc.GetHDC();
|
HDC hdc = (HDC)dc.GetHDC();
|
||||||
m_mswStateSavedDC = ::SaveDC(hdc);
|
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,
|
// cairo_win32_surface_create creates a 24-bit bitmap,
|
||||||
// so if we 32bpp bitmap, we need to create a 32-bit surface instead.
|
// so if we 32bpp bitmap, we need to create a 32-bit surface instead.
|
||||||
if (bmp.GetDepth() < 32)
|
if ( bmp.GetDepth() == 32 )
|
||||||
{
|
|
||||||
m_mswSurface = cairo_win32_surface_create(hdc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
#if wxUSE_WXDIB
|
#if wxUSE_WXDIB
|
||||||
// We need to convert the currently selected bitmap to a DIB
|
// 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.
|
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,
|
// Initialize alpha channel, even if we don't have any alpha yet,
|
||||||
// we should have correct (opaque) alpha values in it for Cairo
|
// we should have correct (opaque) alpha values in it for Cairo
|
||||||
@@ -1940,8 +1940,20 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
|||||||
info.bmWidth,
|
info.bmWidth,
|
||||||
info.bmHeight,
|
info.bmHeight,
|
||||||
info.bmWidthBytes);
|
info.bmWidthBytes);
|
||||||
|
if ( cairo_surface_status(m_mswSurface) == CAIRO_STATUS_SUCCESS )
|
||||||
hasBitmap = true;
|
{
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -1952,29 +1964,52 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC&
|
|||||||
{
|
{
|
||||||
wxLogLastError( wxS("wxCairoContext ctor - GetObject") );
|
wxLogLastError( wxS("wxCairoContext ctor - GetObject") );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback if we failed to create Cairo surface from 32bpp bitmap.
|
// Fallback if Cairo surface hasn't been created from 32bpp bitmap.
|
||||||
if( !hasBitmap )
|
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) );
|
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)
|
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);
|
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__
|
#endif // __WXMSW__
|
||||||
|
|
||||||
#ifdef __WXGTK3__
|
#ifdef __WXGTK3__
|
||||||
@@ -2062,8 +2097,58 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, HDC handle )
|
|||||||
: wxGraphicsContext(renderer)
|
: wxGraphicsContext(renderer)
|
||||||
{
|
{
|
||||||
m_mswStateSavedDC = ::SaveDC(handle);
|
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);
|
m_mswSurface = cairo_win32_surface_create(handle);
|
||||||
Init( cairo_create(m_mswSurface) );
|
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_width = 0;
|
||||||
m_height = 0;
|
m_height = 0;
|
||||||
// Try to determine DC size.
|
// Try to determine DC size.
|
||||||
@@ -2088,6 +2173,7 @@ wxCairoContext::wxCairoContext(wxGraphicsRenderer* renderer, HWND hWnd)
|
|||||||
|
|
||||||
m_mswStateSavedDC = 0;
|
m_mswStateSavedDC = 0;
|
||||||
m_mswSurface = cairo_win32_surface_create((HDC)m_mswWindowHDC);
|
m_mswSurface = cairo_win32_surface_create((HDC)m_mswWindowHDC);
|
||||||
|
|
||||||
Init(cairo_create(m_mswSurface));
|
Init(cairo_create(m_mswSurface));
|
||||||
m_width = 0;
|
m_width = 0;
|
||||||
m_height = 0;
|
m_height = 0;
|
||||||
@@ -2178,7 +2264,8 @@ wxCairoContext::~wxCairoContext()
|
|||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
if ( m_mswSurface )
|
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);
|
cairo_surface_destroy(m_mswSurface);
|
||||||
|
|
||||||
@@ -2219,8 +2306,8 @@ void wxCairoContext::Init(cairo_t *context)
|
|||||||
|
|
||||||
void wxCairoContext::ApplyTransformFromDC(const wxDC& dc, ApplyTransformMode mode)
|
void wxCairoContext::ApplyTransformFromDC(const wxDC& dc, ApplyTransformMode mode)
|
||||||
{
|
{
|
||||||
// Transfer transformation settings from source DC to Cairo context
|
// Transfer transformation settings from source wxDC
|
||||||
// and store it as an internal transformation
|
// to Cairo context and store them as an internal transformation
|
||||||
// (which is not going to be exposed).
|
// (which is not going to be exposed).
|
||||||
double sx, sy;
|
double sx, sy;
|
||||||
dc.GetUserScale(&sx, &sy);
|
dc.GetUserScale(&sx, &sy);
|
||||||
@@ -2242,7 +2329,6 @@ void wxCairoContext::ApplyTransformFromDC(const wxDC& dc, ApplyTransformMode mod
|
|||||||
org = dc.GetLogicalOrigin();
|
org = dc.GetLogicalOrigin();
|
||||||
cairo_matrix_translate(&m_internalTransform, -org.x, -org.y);
|
cairo_matrix_translate(&m_internalTransform, -org.x, -org.y);
|
||||||
|
|
||||||
// Apply all above transformations.
|
|
||||||
cairo_set_matrix(m_context, &m_internalTransform);
|
cairo_set_matrix(m_context, &m_internalTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user