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:
Artur Wieczorek 2016-06-11 22:35:33 +02:00
parent a735371b0e
commit f06bfe37ae
2 changed files with 114 additions and 26 deletions

View File

@ -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) \

View File

@ -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);
}