From 8272ed18196e21bf24714e6b89ffc3ae73c36d80 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 16 Aug 2014 23:25:02 -0700 Subject: [PATCH] Fixed bug 2687 - SDL_BlitScaled does not handle clipping correctly Patch from Benoit Pierre: video: fix clipping handling in SDL_UpperBlitScaled - honor destination clipping rectangle - update both destination and source rectangles when clipping source rectangle to source surface and destination rectangle to destination clip rectangle - don't change scaling factors when clipping N.B.: - when no scaling is involved (source and destination width/height are the same), SDL_UpperBlit is used (so SDL_BlitScaled behaves like SDL_BlitSurface) - the final destination rectangle after all clipping is performed is saved back to dstrect (like for SDL_UpperBlit) --- src/video/SDL_surface.c | 222 ++++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 99 deletions(-) diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 421e2b806a001..3f4244b76be3e 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -26,7 +26,6 @@ #include "SDL_RLEaccel_c.h" #include "SDL_pixels_c.h" - /* Public routines */ /* * Create an empty RGB surface of the appropriate depth @@ -623,7 +622,12 @@ int SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect, SDL_Surface * dst, SDL_Rect * dstrect) { - SDL_Rect final_src, final_dst, fulldst; + double src_x0, src_y0, src_x1, src_y1; + double dst_x0, dst_y0, dst_x1, dst_y1; + SDL_Rect final_src, final_dst; + double scaling_w, scaling_h; + int src_w, src_h; + int dst_w, dst_h; /* Make sure the surfaces aren't locked */ if (!src || !dst) { @@ -633,78 +637,135 @@ SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect, return SDL_SetError("Surfaces must not be locked during blit"); } - /* If the destination rectangle is NULL, use the entire dest surface */ - if (dstrect == NULL) { - fulldst.x = fulldst.y = 0; - fulldst.w = dst->w; - fulldst.h = dst->h; - dstrect = &fulldst; + if (NULL == srcrect) { + src_w = src->w; + src_h = src->h; + } else { + src_w = srcrect->w; + src_h = srcrect->h; } - /* clip the source rectangle to the source surface */ - if (srcrect) { - int maxw, maxh; + if (NULL == dstrect) { + dst_w = dst->w; + dst_h = dst->h; + } else { + dst_w = dstrect->w; + dst_h = dstrect->h; + } - final_src.x = srcrect->x; - final_src.w = srcrect->w; - if (final_src.x < 0) { - final_src.w += final_src.x; - final_src.x = 0; - } - maxw = src->w - final_src.x; - if (maxw < final_src.w) - final_src.w = maxw; - - final_src.y = srcrect->y; - final_src.h = srcrect->h; - if (final_src.y < 0) { - final_src.h += final_src.y; - final_src.y = 0; - } - maxh = src->h - final_src.y; - if (maxh < final_src.h) - final_src.h = maxh; + if (dst_w == src_w && dst_h == src_h) { + /* No scaling, defer to regular blit */ + return SDL_BlitSurface(src, srcrect, dst, dstrect); + } + + scaling_w = (double)dst_w / src_w; + scaling_h = (double)dst_h / src_h; + if (NULL == dstrect) { + dst_x0 = 0; + dst_y0 = 0; + dst_x1 = dst_w - 1; + dst_y1 = dst_h - 1; } else { - final_src.x = final_src.y = 0; - final_src.w = src->w; - final_src.h = src->h; + dst_x0 = dstrect->x; + dst_y0 = dstrect->y; + dst_x1 = dst_x0 + dst_w - 1; + dst_y1 = dst_y0 + dst_h - 1; } - /* clip the destination rectangle against the clip rectangle */ - if (dstrect) { - int maxw, maxh; + if (NULL == srcrect) { + src_x0 = 0; + src_y0 = 0; + src_x1 = src_w - 1; + src_y1 = src_h - 1; + } else { + src_x0 = srcrect->x; + src_y0 = srcrect->y; + src_x1 = src_x0 + src_w - 1; + src_y1 = src_y0 + src_h - 1; + + /* Clip source rectangle to the source surface */ - final_dst.x = dstrect->x; - final_dst.w = dstrect->w; - if (final_dst.x < 0) { - final_dst.w += final_dst.x; - final_dst.x = 0; + if (src_x0 < 0) { + dst_x0 -= src_x0 * scaling_w; + src_x0 = 0; } - maxw = dst->w - final_dst.x; - if (maxw < final_dst.w) - final_dst.w = maxw; - - final_dst.y = dstrect->y; - final_dst.h = dstrect->h; - if (final_dst.y < 0) { - final_dst.h += final_dst.y; - final_dst.y = 0; + + if (src_x1 >= src->w) { + dst_x1 -= (src_x1 - src->w + 1) * scaling_w; + src_x1 = src->w - 1; + } + + if (src_y0 < 0) { + dst_y0 -= src_y0 * scaling_h; + src_y0 = 0; + } + + if (src_y1 >= src->h) { + dst_y1 -= (src_y1 - src->h + 1) * scaling_h; + src_y1 = src->h - 1; } - maxh = dst->h - final_dst.y; - if (maxh < final_dst.h) - final_dst.h = maxh; - } else { - final_dst.x = final_dst.y = 0; - final_dst.w = dst->w; - final_dst.h = dst->h; } - if (final_dst.w > 0 && final_dst.h > 0) { - return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst); + /* Clip destination rectangle to the clip rectangle */ + + /* Translate to clip space for easier calculations */ + dst_x0 -= dst->clip_rect.x; + dst_x1 -= dst->clip_rect.x; + dst_y0 -= dst->clip_rect.y; + dst_y1 -= dst->clip_rect.y; + + if (dst_x0 < 0) { + src_x0 -= dst_x0 / scaling_w; + dst_x0 = 0; } - return 0; + if (dst_x1 >= dst->clip_rect.w) { + src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w; + dst_x1 = dst->clip_rect.w - 1; + } + + if (dst_y0 < 0) { + src_y0 -= dst_y0 / scaling_h; + dst_y0 = 0; + } + + if (dst_y1 >= dst->clip_rect.h) { + src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h; + dst_y1 = dst->clip_rect.h - 1; + } + + /* Translate back to surface coordinates */ + dst_x0 += dst->clip_rect.x; + dst_x1 += dst->clip_rect.x; + dst_y0 += dst->clip_rect.y; + dst_y1 += dst->clip_rect.y; + + final_src.x = SDL_round(src_x0); + final_src.y = SDL_round(src_y0); + final_src.w = SDL_round(src_x1 - src_x0 + 1); + final_src.h = SDL_round(src_y1 - src_y0 + 1); + + final_dst.x = SDL_round(dst_x0); + final_dst.y = SDL_round(dst_y0); + final_dst.w = SDL_round(dst_x1 - dst_x0 + 1); + final_dst.h = SDL_round(dst_y1 - dst_y0 + 1); + + if (final_dst.w < 0) + final_dst.w = 0; + if (final_dst.h < 0) + final_dst.h = 0; + + if (dstrect) + *dstrect = final_dst; + + if (final_dst.w == 0 || final_dst.h == 0 || + final_src.w <= 0 || final_src.h <= 0) { + /* No-op. */ + return 0; + } + + return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst); } /** @@ -721,43 +782,6 @@ SDL_LowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect, SDL_COPY_COLORKEY ); - /* Save off the original dst width, height */ - int dstW = dstrect->w; - int dstH = dstrect->h; - SDL_Rect full_rect; - SDL_Rect final_dst = *dstrect; - SDL_Rect final_src = *srcrect; - - /* Clip the dst surface to the dstrect */ - full_rect.x = 0; - full_rect.y = 0; - full_rect.w = dst->w; - full_rect.h = dst->h; - if (!SDL_IntersectRect(&final_dst, &full_rect, &final_dst)) { - return 0; - } - - /* Did the dst width change? */ - if ( dstW != final_dst.w ) { - /* scale the src width appropriately */ - final_src.w = final_src.w * dst->clip_rect.w / dstW; - } - - /* Did the dst height change? */ - if ( dstH != final_dst.h ) { - /* scale the src width appropriately */ - final_src.h = final_src.h * dst->clip_rect.h / dstH; - } - - /* Clip the src surface to the srcrect */ - full_rect.x = 0; - full_rect.y = 0; - full_rect.w = src->w; - full_rect.h = src->h; - if (!SDL_IntersectRect(&final_src, &full_rect, &final_src)) { - return 0; - } - if (!(src->map->info.flags & SDL_COPY_NEAREST)) { src->map->info.flags |= SDL_COPY_NEAREST; SDL_InvalidateMap(src->map); @@ -766,9 +790,9 @@ SDL_LowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect, if ( !(src->map->info.flags & complex_copy_flags) && src->format->format == dst->format->format && !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) { - return SDL_SoftStretch( src, &final_src, dst, &final_dst ); + return SDL_SoftStretch( src, srcrect, dst, dstrect ); } else { - return SDL_LowerBlit( src, &final_src, dst, &final_dst ); + return SDL_LowerBlit( src, srcrect, dst, dstrect ); } }