Fixed bug 2687 - SDL_BlitScaled does not handle clipping correctly
authorSam Lantinga <slouken@libsdl.org>
Sat, 16 Aug 2014 23:25:02 -0700
changeset 90765e713281410c
parent 9075 257a6793aaf5
child 9077 28e6de55eb07
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
     1.1 --- a/src/video/SDL_surface.c	Sat Aug 16 23:23:15 2014 -0700
     1.2 +++ b/src/video/SDL_surface.c	Sat Aug 16 23:25:02 2014 -0700
     1.3 @@ -26,7 +26,6 @@
     1.4  #include "SDL_RLEaccel_c.h"
     1.5  #include "SDL_pixels_c.h"
     1.6  
     1.7 -
     1.8  /* Public routines */
     1.9  /*
    1.10   * Create an empty RGB surface of the appropriate depth
    1.11 @@ -623,7 +622,12 @@
    1.12  SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,
    1.13                SDL_Surface * dst, SDL_Rect * dstrect)
    1.14  {
    1.15 -    SDL_Rect final_src, final_dst, fulldst;
    1.16 +    double src_x0, src_y0, src_x1, src_y1;
    1.17 +    double dst_x0, dst_y0, dst_x1, dst_y1;
    1.18 +    SDL_Rect final_src, final_dst;
    1.19 +    double scaling_w, scaling_h;
    1.20 +    int src_w, src_h;
    1.21 +    int dst_w, dst_h;
    1.22  
    1.23      /* Make sure the surfaces aren't locked */
    1.24      if (!src || !dst) {
    1.25 @@ -633,78 +637,135 @@
    1.26          return SDL_SetError("Surfaces must not be locked during blit");
    1.27      }
    1.28  
    1.29 -    /* If the destination rectangle is NULL, use the entire dest surface */
    1.30 -    if (dstrect == NULL) {
    1.31 -        fulldst.x = fulldst.y = 0;
    1.32 -        fulldst.w = dst->w;
    1.33 -        fulldst.h = dst->h;
    1.34 -        dstrect = &fulldst;
    1.35 +    if (NULL == srcrect) {
    1.36 +        src_w = src->w;
    1.37 +        src_h = src->h;
    1.38 +    } else {
    1.39 +        src_w = srcrect->w;
    1.40 +        src_h = srcrect->h;
    1.41      }
    1.42  
    1.43 -    /* clip the source rectangle to the source surface */
    1.44 -    if (srcrect) {
    1.45 -        int maxw, maxh;
    1.46 -
    1.47 -        final_src.x = srcrect->x;
    1.48 -        final_src.w = srcrect->w;
    1.49 -        if (final_src.x < 0) {
    1.50 -            final_src.w += final_src.x;
    1.51 -            final_src.x = 0;
    1.52 -        }
    1.53 -        maxw = src->w - final_src.x;
    1.54 -        if (maxw < final_src.w)
    1.55 -            final_src.w = maxw;
    1.56 -
    1.57 -        final_src.y = srcrect->y;
    1.58 -        final_src.h = srcrect->h;
    1.59 -        if (final_src.y < 0) {
    1.60 -            final_src.h += final_src.y;
    1.61 -            final_src.y = 0;
    1.62 -        }
    1.63 -        maxh = src->h - final_src.y;
    1.64 -        if (maxh < final_src.h)
    1.65 -            final_src.h = maxh;
    1.66 -
    1.67 +    if (NULL == dstrect) {
    1.68 +        dst_w = dst->w;
    1.69 +        dst_h = dst->h;
    1.70      } else {
    1.71 -        final_src.x = final_src.y = 0;
    1.72 -        final_src.w = src->w;
    1.73 -        final_src.h = src->h;
    1.74 +        dst_w = dstrect->w;
    1.75 +        dst_h = dstrect->h;
    1.76      }
    1.77  
    1.78 -    /* clip the destination rectangle against the clip rectangle */
    1.79 -    if (dstrect) {
    1.80 -        int maxw, maxh;
    1.81 -
    1.82 -        final_dst.x = dstrect->x;
    1.83 -        final_dst.w = dstrect->w;
    1.84 -        if (final_dst.x < 0) {
    1.85 -            final_dst.w += final_dst.x;
    1.86 -            final_dst.x = 0;
    1.87 -        }
    1.88 -        maxw = dst->w - final_dst.x;
    1.89 -        if (maxw < final_dst.w)
    1.90 -            final_dst.w = maxw;
    1.91 -
    1.92 -        final_dst.y = dstrect->y;
    1.93 -        final_dst.h = dstrect->h;
    1.94 -        if (final_dst.y < 0) {
    1.95 -            final_dst.h += final_dst.y;
    1.96 -            final_dst.y = 0;
    1.97 -        }
    1.98 -        maxh = dst->h - final_dst.y;
    1.99 -        if (maxh < final_dst.h)
   1.100 -            final_dst.h = maxh;
   1.101 -    } else {
   1.102 -        final_dst.x = final_dst.y = 0;
   1.103 -        final_dst.w = dst->w;
   1.104 -        final_dst.h = dst->h;
   1.105 +    if (dst_w == src_w && dst_h == src_h) {
   1.106 +        /* No scaling, defer to regular blit */
   1.107 +        return SDL_BlitSurface(src, srcrect, dst, dstrect);
   1.108      }
   1.109  
   1.110 -    if (final_dst.w > 0 && final_dst.h > 0) {
   1.111 -        return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst);
   1.112 +    scaling_w = (double)dst_w / src_w;
   1.113 +    scaling_h = (double)dst_h / src_h;
   1.114 +
   1.115 +    if (NULL == dstrect) {
   1.116 +        dst_x0 = 0;
   1.117 +        dst_y0 = 0;
   1.118 +        dst_x1 = dst_w - 1;
   1.119 +        dst_y1 = dst_h - 1;
   1.120 +    } else {
   1.121 +        dst_x0 = dstrect->x;
   1.122 +        dst_y0 = dstrect->y;
   1.123 +        dst_x1 = dst_x0 + dst_w - 1;
   1.124 +        dst_y1 = dst_y0 + dst_h - 1;
   1.125      }
   1.126  
   1.127 -    return 0;
   1.128 +    if (NULL == srcrect) {
   1.129 +        src_x0 = 0;
   1.130 +        src_y0 = 0;
   1.131 +        src_x1 = src_w - 1;
   1.132 +        src_y1 = src_h - 1;
   1.133 +    } else {
   1.134 +        src_x0 = srcrect->x;
   1.135 +        src_y0 = srcrect->y;
   1.136 +        src_x1 = src_x0 + src_w - 1;
   1.137 +        src_y1 = src_y0 + src_h - 1;
   1.138 +
   1.139 +        /* Clip source rectangle to the source surface */
   1.140 +
   1.141 +        if (src_x0 < 0) {
   1.142 +            dst_x0 -= src_x0 * scaling_w;
   1.143 +            src_x0 = 0;
   1.144 +        }
   1.145 +
   1.146 +        if (src_x1 >= src->w) {
   1.147 +            dst_x1 -= (src_x1 - src->w + 1) * scaling_w;
   1.148 +            src_x1 = src->w - 1;
   1.149 +        }
   1.150 +
   1.151 +        if (src_y0 < 0) {
   1.152 +            dst_y0 -= src_y0 * scaling_h;
   1.153 +            src_y0 = 0;
   1.154 +        }
   1.155 +
   1.156 +        if (src_y1 >= src->h) {
   1.157 +            dst_y1 -= (src_y1 - src->h + 1) * scaling_h;
   1.158 +            src_y1 = src->h - 1;
   1.159 +        }
   1.160 +    }
   1.161 +
   1.162 +    /* Clip destination rectangle to the clip rectangle */
   1.163 +
   1.164 +    /* Translate to clip space for easier calculations */
   1.165 +    dst_x0 -= dst->clip_rect.x;
   1.166 +    dst_x1 -= dst->clip_rect.x;
   1.167 +    dst_y0 -= dst->clip_rect.y;
   1.168 +    dst_y1 -= dst->clip_rect.y;
   1.169 +
   1.170 +    if (dst_x0 < 0) {
   1.171 +        src_x0 -= dst_x0 / scaling_w;
   1.172 +        dst_x0 = 0;
   1.173 +    }
   1.174 +
   1.175 +    if (dst_x1 >= dst->clip_rect.w) {
   1.176 +        src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w;
   1.177 +        dst_x1 = dst->clip_rect.w - 1;
   1.178 +    }
   1.179 +
   1.180 +    if (dst_y0 < 0) {
   1.181 +        src_y0 -= dst_y0 / scaling_h;
   1.182 +        dst_y0 = 0;
   1.183 +    }
   1.184 +
   1.185 +    if (dst_y1 >= dst->clip_rect.h) {
   1.186 +        src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h;
   1.187 +        dst_y1 = dst->clip_rect.h - 1;
   1.188 +    }
   1.189 +
   1.190 +    /* Translate back to surface coordinates */
   1.191 +    dst_x0 += dst->clip_rect.x;
   1.192 +    dst_x1 += dst->clip_rect.x;
   1.193 +    dst_y0 += dst->clip_rect.y;
   1.194 +    dst_y1 += dst->clip_rect.y;
   1.195 +
   1.196 +    final_src.x = SDL_round(src_x0);
   1.197 +    final_src.y = SDL_round(src_y0);
   1.198 +    final_src.w = SDL_round(src_x1 - src_x0 + 1);
   1.199 +    final_src.h = SDL_round(src_y1 - src_y0 + 1);
   1.200 +
   1.201 +    final_dst.x = SDL_round(dst_x0);
   1.202 +    final_dst.y = SDL_round(dst_y0);
   1.203 +    final_dst.w = SDL_round(dst_x1 - dst_x0 + 1);
   1.204 +    final_dst.h = SDL_round(dst_y1 - dst_y0 + 1);
   1.205 +
   1.206 +    if (final_dst.w < 0)
   1.207 +        final_dst.w = 0;
   1.208 +    if (final_dst.h < 0)
   1.209 +        final_dst.h = 0;
   1.210 +
   1.211 +    if (dstrect)
   1.212 +        *dstrect = final_dst;
   1.213 +
   1.214 +    if (final_dst.w == 0 || final_dst.h == 0 ||
   1.215 +        final_src.w <= 0 || final_src.h <= 0) {
   1.216 +        /* No-op. */
   1.217 +        return 0;
   1.218 +    }
   1.219 +
   1.220 +    return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst);
   1.221  }
   1.222  
   1.223  /**
   1.224 @@ -721,43 +782,6 @@
   1.225          SDL_COPY_COLORKEY
   1.226      );
   1.227  
   1.228 -    /* Save off the original dst width, height */
   1.229 -    int dstW = dstrect->w;
   1.230 -    int dstH = dstrect->h;
   1.231 -    SDL_Rect full_rect;
   1.232 -    SDL_Rect final_dst = *dstrect;
   1.233 -    SDL_Rect final_src = *srcrect;
   1.234 -
   1.235 -    /* Clip the dst surface to the dstrect */
   1.236 -    full_rect.x = 0;
   1.237 -    full_rect.y = 0;
   1.238 -    full_rect.w = dst->w;
   1.239 -    full_rect.h = dst->h;
   1.240 -    if (!SDL_IntersectRect(&final_dst, &full_rect, &final_dst)) {
   1.241 -        return 0;
   1.242 -    }
   1.243 -
   1.244 -    /* Did the dst width change? */
   1.245 -    if ( dstW != final_dst.w ) {
   1.246 -        /* scale the src width appropriately */
   1.247 -        final_src.w = final_src.w * dst->clip_rect.w / dstW;
   1.248 -    }
   1.249 -
   1.250 -    /* Did the dst height change? */
   1.251 -    if ( dstH != final_dst.h ) {
   1.252 -        /* scale the src width appropriately */
   1.253 -        final_src.h = final_src.h * dst->clip_rect.h / dstH;
   1.254 -    }
   1.255 -
   1.256 -    /* Clip the src surface to the srcrect */
   1.257 -    full_rect.x = 0;
   1.258 -    full_rect.y = 0;
   1.259 -    full_rect.w = src->w;
   1.260 -    full_rect.h = src->h;
   1.261 -    if (!SDL_IntersectRect(&final_src, &full_rect, &final_src)) {
   1.262 -        return 0;
   1.263 -    }
   1.264 -
   1.265      if (!(src->map->info.flags & SDL_COPY_NEAREST)) {
   1.266          src->map->info.flags |= SDL_COPY_NEAREST;
   1.267          SDL_InvalidateMap(src->map);
   1.268 @@ -766,9 +790,9 @@
   1.269      if ( !(src->map->info.flags & complex_copy_flags) &&
   1.270           src->format->format == dst->format->format &&
   1.271           !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) {
   1.272 -        return SDL_SoftStretch( src, &final_src, dst, &final_dst );
   1.273 +        return SDL_SoftStretch( src, srcrect, dst, dstrect );
   1.274      } else {
   1.275 -        return SDL_LowerBlit( src, &final_src, dst, &final_dst );
   1.276 +        return SDL_LowerBlit( src, srcrect, dst, dstrect );
   1.277      }
   1.278  }
   1.279