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)
This commit is contained in:
Sam Lantinga 2014-08-16 23:25:02 -07:00
parent 4e7db78ed9
commit 8272ed1819

View File

@ -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,80 +637,137 @@ 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;
}
/* clip the source rectangle to the source surface */
if (srcrect) {
int maxw, maxh;
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 (NULL == srcrect) {
src_w = src->w;
src_h = src->h;
} else {
final_src.x = final_src.y = 0;
final_src.w = src->w;
final_src.h = src->h;
src_w = srcrect->w;
src_h = srcrect->h;
}
/* clip the destination rectangle against the clip rectangle */
if (dstrect) {
int maxw, maxh;
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;
}
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;
}
maxh = dst->h - final_dst.y;
if (maxh < final_dst.h)
final_dst.h = maxh;
if (NULL == dstrect) {
dst_w = dst->w;
dst_h = dst->h;
} else {
final_dst.x = final_dst.y = 0;
final_dst.w = dst->w;
final_dst.h = dst->h;
dst_w = dstrect->w;
dst_h = dstrect->h;
}
if (final_dst.w > 0 && final_dst.h > 0) {
return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst);
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 {
dst_x0 = dstrect->x;
dst_y0 = dstrect->y;
dst_x1 = dst_x0 + dst_w - 1;
dst_y1 = dst_y0 + dst_h - 1;
}
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 */
if (src_x0 < 0) {
dst_x0 -= src_x0 * scaling_w;
src_x0 = 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;
}
}
/* 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;
}
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);
}
/**
* This is a semi-private blit function and it performs low-level surface
* scaled blitting only.
@ -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 );
}
}