From 3ace50119075d51455e6081a48459f1cf09a8c77 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 22 Sep 2011 10:11:27 +0200 Subject: [PATCH] gdi32: Copy DIB rectangles in the correct order when source and destination overlap. --- dlls/gdi32/dibdrv/bitblt.c | 102 ++++++++++++++++++++++++++++++++++--- dlls/gdi32/dibdrv/dibdrv.h | 5 ++ 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/dlls/gdi32/dibdrv/bitblt.c b/dlls/gdi32/dibdrv/bitblt.c index 1d41e11336d..94ae266a0c7 100644 --- a/dlls/gdi32/dibdrv/bitblt.c +++ b/dlls/gdi32/dibdrv/bitblt.c @@ -431,28 +431,118 @@ static const unsigned char BITBLT_Opcodes[256][MAX_OP_LEN] = { OP(PAT,DST,R2_WHITE) } /* 0xff 1 */ }; +static int get_overlap( const dib_info *dst, const RECT *dst_rect, + const dib_info *src, const RECT *src_rect ) +{ + const char *src_top, *dst_top; + int height, ret = 0; + + if (dst->stride != src->stride) return 0; /* can't be the same dib */ + if (dst_rect->right <= src_rect->left) return 0; + if (dst_rect->left >= src_rect->right) return 0; + + src_top = (const char *)src->bits.ptr + src_rect->top * src->stride; + dst_top = (const char *)dst->bits.ptr + dst_rect->top * dst->stride; + height = (dst_rect->bottom - dst_rect->top) * dst->stride; + + if (dst->stride > 0) + { + if (src_top >= dst_top + height) return 0; + if (src_top + height <= dst_top) return 0; + if (dst_top < src_top) ret |= OVERLAP_ABOVE; + else if (dst_top > src_top) ret |= OVERLAP_BELOW; + } + else + { + if (src_top <= dst_top + height) return 0; + if (src_top + height >= dst_top) return 0; + if (dst_top > src_top) ret |= OVERLAP_ABOVE; + else if (dst_top < src_top) ret |= OVERLAP_BELOW; + } + + if (dst_rect->left < src_rect->left) ret |= OVERLAP_LEFT; + else if (dst_rect->left > src_rect->left) ret |= OVERLAP_RIGHT; + + return ret; +} + static DWORD copy_rect( dib_info *dst, const RECT *dst_rect, const dib_info *src, const RECT *src_rect, HRGN clip, INT rop2 ) { POINT origin; RECT clipped_rect; const WINEREGION *clip_data; - int i; + int i, start, end, overlap; origin.x = src_rect->left; origin.y = src_rect->top; + overlap = get_overlap( dst, dst_rect, src, src_rect ); if (clip == NULL) dst->funcs->copy_rect( dst, dst_rect, src, &origin, rop2 ); else { clip_data = get_wine_region( clip ); - for (i = 0; i < clip_data->numRects; i++) + if (overlap & OVERLAP_BELOW) { - if (intersect_rect( &clipped_rect, dst_rect, clip_data->rects + i )) + if (overlap & OVERLAP_RIGHT) /* right to left, bottom to top */ { - origin.x = src_rect->left + clipped_rect.left - dst_rect->left; - origin.y = src_rect->top + clipped_rect.top - dst_rect->top; - dst->funcs->copy_rect( dst, &clipped_rect, src, &origin, rop2 ); + for (i = clip_data->numRects - 1; i >= 0; i--) + { + if (intersect_rect( &clipped_rect, dst_rect, clip_data->rects + i )) + { + origin.x = src_rect->left + clipped_rect.left - dst_rect->left; + origin.y = src_rect->top + clipped_rect.top - dst_rect->top; + dst->funcs->copy_rect( dst, &clipped_rect, src, &origin, rop2 ); + } + } + } + else /* left to right, bottom to top */ + { + for (start = clip_data->numRects - 1; start >= 0; start = end) + { + for (end = start - 1; end >= 0; end--) + if (clip_data->rects[start].top != clip_data->rects[end].top) break; + + for (i = end + 1; i <= start; i++) + { + if (intersect_rect( &clipped_rect, dst_rect, clip_data->rects + i )) + { + origin.x = src_rect->left + clipped_rect.left - dst_rect->left; + origin.y = src_rect->top + clipped_rect.top - dst_rect->top; + dst->funcs->copy_rect( dst, &clipped_rect, src, &origin, rop2 ); + } + } + } + } + } + else if (overlap & OVERLAP_RIGHT) /* right to left, top to bottom */ + { + for (start = 0; start < clip_data->numRects; start = end) + { + for (end = start + 1; end < clip_data->numRects; end++) + if (clip_data->rects[start].top != clip_data->rects[end].top) break; + + for (i = end - 1; i >= start; i--) + { + if (intersect_rect( &clipped_rect, dst_rect, clip_data->rects + i )) + { + origin.x = src_rect->left + clipped_rect.left - dst_rect->left; + origin.y = src_rect->top + clipped_rect.top - dst_rect->top; + dst->funcs->copy_rect( dst, &clipped_rect, src, &origin, rop2 ); + } + } + } + } + else /* left to right, top to bottom */ + { + for (i = 0; i < clip_data->numRects; i++) + { + if (intersect_rect( &clipped_rect, dst_rect, clip_data->rects + i )) + { + origin.x = src_rect->left + clipped_rect.left - dst_rect->left; + origin.y = src_rect->top + clipped_rect.top - dst_rect->top; + dst->funcs->copy_rect( dst, &clipped_rect, src, &origin, rop2 ); + } } } release_wine_region( clip ); diff --git a/dlls/gdi32/dibdrv/dibdrv.h b/dlls/gdi32/dibdrv/dibdrv.h index 7f0d6972aed..81309008fbb 100644 --- a/dlls/gdi32/dibdrv/dibdrv.h +++ b/dlls/gdi32/dibdrv/dibdrv.h @@ -147,6 +147,11 @@ struct rop_codes DWORD a1, a2, x1, x2; }; +#define OVERLAP_LEFT 0x01 /* dest starts left of source */ +#define OVERLAP_RIGHT 0x02 /* dest starts right of source */ +#define OVERLAP_ABOVE 0x04 /* dest starts above source */ +#define OVERLAP_BELOW 0x08 /* dest starts below source */ + extern void get_rop_codes(INT rop, struct rop_codes *codes); extern void calc_and_xor_masks(INT rop, DWORD color, DWORD *and, DWORD *xor) DECLSPEC_HIDDEN; extern void update_brush_rop( dibdrv_physdev *pdev, INT rop ) DECLSPEC_HIDDEN;