Fix wxMemoryDC::Blit() with itself as source in wxGTK3
Drawing on the Cairo surface itself can give invalid results when source and destination regions overlap. To avoid this problem we have to copy actual source surface to the temporary one and use this copy in drawing operations. Closes #17666.
This commit is contained in:
parent
f171d48be4
commit
0ac0f4b259
@ -142,15 +142,79 @@ bool wxGTKCairoDCImpl::DoStretchBlit(int xdest, int ydest, int dstWidth, int dst
|
||||
const int xsrc_dev = source->LogicalToDeviceX(xsrc);
|
||||
const int ysrc_dev = source->LogicalToDeviceY(ysrc);
|
||||
|
||||
cairo_surface_t* surface = cairo_get_target(cr_src);
|
||||
cairo_surface_flush(surface);
|
||||
cairo_surface_t* surfaceSrc = cairo_get_target(cr_src);
|
||||
cairo_surface_flush(surfaceSrc);
|
||||
|
||||
cairo_surface_t* surfaceTmp = NULL;
|
||||
// If destination (this) and source wxDC refer to the same Cairo context
|
||||
// it means that we operate on one surface and results of drawing
|
||||
// can be invalid if destination and source regions overlap.
|
||||
// In such situation we have to copy source surface to the temporary
|
||||
// surface and use this copy in the drawing operations.
|
||||
if ( cr == cr_src )
|
||||
{
|
||||
// Check if destination and source regions overlap.
|
||||
bool regOverlap;
|
||||
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0)
|
||||
if ( cairo_version() >= CAIRO_VERSION_ENCODE(1, 10, 0) )
|
||||
{
|
||||
cairo_rectangle_int_t rdst;
|
||||
rdst.x = xdest;
|
||||
rdst.y = ydest;
|
||||
rdst.width = dstWidth;
|
||||
rdst.height = dstHeight;
|
||||
cairo_region_t* regdst = cairo_region_create_rectangle(&rdst);
|
||||
|
||||
cairo_rectangle_int_t rsrc;
|
||||
rsrc.x = xsrc;
|
||||
rsrc.y = ysrc;
|
||||
rsrc.width = srcWidth;
|
||||
rsrc.height = srcHeight;
|
||||
cairo_region_overlap_t ov = cairo_region_contains_rectangle(regdst, &rsrc);
|
||||
cairo_region_destroy(regdst);
|
||||
regOverlap = (ov != CAIRO_REGION_OVERLAP_OUT);
|
||||
}
|
||||
else
|
||||
#endif // Cairo 1.10
|
||||
{
|
||||
wxRect rdst(xdest, ydest, dstWidth, dstHeight);
|
||||
wxRect rsrc(xsrc, ysrc, srcWidth, srcHeight);
|
||||
regOverlap = rdst.Intersects(rsrc);
|
||||
}
|
||||
// If necessary, copy source surface to the temporary one.
|
||||
if ( regOverlap )
|
||||
{
|
||||
const int w = cairo_image_surface_get_width(surfaceSrc);
|
||||
const int h = cairo_image_surface_get_height(surfaceSrc);
|
||||
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
|
||||
if ( cairo_version() >= CAIRO_VERSION_ENCODE(1, 12, 0) )
|
||||
{
|
||||
surfaceTmp = cairo_surface_create_similar_image(surfaceSrc,
|
||||
cairo_image_surface_get_format(surfaceSrc),
|
||||
w, h);
|
||||
}
|
||||
else
|
||||
#endif // Cairo 1.12
|
||||
{
|
||||
surfaceTmp = cairo_surface_create_similar(surfaceSrc,
|
||||
CAIRO_CONTENT_COLOR_ALPHA,
|
||||
w, h);
|
||||
}
|
||||
cairo_t* crTmp = cairo_create(surfaceTmp);
|
||||
cairo_set_source_surface(crTmp, surfaceSrc, 0, 0);
|
||||
cairo_rectangle(crTmp, 0.0, 0.0, w, h);
|
||||
cairo_set_operator(crTmp, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_fill(crTmp);
|
||||
cairo_destroy(crTmp);
|
||||
}
|
||||
}
|
||||
cairo_save(cr);
|
||||
cairo_translate(cr, xdest, ydest);
|
||||
cairo_rectangle(cr, 0, 0, dstWidth, dstHeight);
|
||||
double sx, sy;
|
||||
source->GetUserScale(&sx, &sy);
|
||||
cairo_scale(cr, dstWidth / (sx * srcWidth), dstHeight / (sy * srcHeight));
|
||||
cairo_set_source_surface(cr, surface, -xsrc_dev, -ysrc_dev);
|
||||
cairo_set_source_surface(cr, surfaceTmp ? surfaceTmp : surfaceSrc, -xsrc_dev, -ysrc_dev);
|
||||
const wxRasterOperationMode rop_save = m_logicalFunction;
|
||||
SetLogicalFunction(rop);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
@ -181,6 +245,10 @@ bool wxGTKCairoDCImpl::DoStretchBlit(int xdest, int ydest, int dstWidth, int dst
|
||||
cairo_fill(cr);
|
||||
}
|
||||
cairo_restore(cr);
|
||||
if ( surfaceTmp )
|
||||
{
|
||||
cairo_surface_destroy(surfaceTmp);
|
||||
}
|
||||
m_logicalFunction = rop_save;
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user