src/video/SDL_stretch.c
changeset 0 74212992fb08
child 252 e8157fcb3114
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/video/SDL_stretch.c	Thu Apr 26 16:45:43 2001 +0000
     1.3 @@ -0,0 +1,312 @@
     1.4 +/*
     1.5 +    SDL - Simple DirectMedia Layer
     1.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
     1.7 +
     1.8 +    This library is free software; you can redistribute it and/or
     1.9 +    modify it under the terms of the GNU Library General Public
    1.10 +    License as published by the Free Software Foundation; either
    1.11 +    version 2 of the License, or (at your option) any later version.
    1.12 +
    1.13 +    This library is distributed in the hope that it will be useful,
    1.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.16 +    Library General Public License for more details.
    1.17 +
    1.18 +    You should have received a copy of the GNU Library General Public
    1.19 +    License along with this library; if not, write to the Free
    1.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.21 +
    1.22 +    Sam Lantinga
    1.23 +    slouken@devolution.com
    1.24 +*/
    1.25 +
    1.26 +#ifdef SAVE_RCSID
    1.27 +static char rcsid =
    1.28 + "@(#) $Id$";
    1.29 +#endif
    1.30 +
    1.31 +/* This a stretch blit implementation based on ideas given to me by
    1.32 +   Tomasz Cejner - thanks! :)
    1.33 +
    1.34 +   April 27, 2000 - Sam Lantinga
    1.35 +*/
    1.36 +
    1.37 +#include "SDL_error.h"
    1.38 +#include "SDL_video.h"
    1.39 +#include "SDL_blit.h"
    1.40 +
    1.41 +/* This isn't ready for general consumption yet - it should be folded
    1.42 +   into the general blitting mechanism.
    1.43 +*/
    1.44 +
    1.45 +#if (defined(WIN32) && !defined(_M_ALPHA) && !defined(_WIN32_WCE)) || \
    1.46 +    defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT)
    1.47 +#define USE_ASM_STRETCH
    1.48 +#endif
    1.49 +
    1.50 +#ifdef USE_ASM_STRETCH
    1.51 +
    1.52 +#if defined(WIN32) || defined(i386)
    1.53 +#define PREFIX16	0x66
    1.54 +#define STORE_BYTE	0xAA
    1.55 +#define STORE_WORD	0xAB
    1.56 +#define LOAD_BYTE	0xAC
    1.57 +#define LOAD_WORD	0xAD
    1.58 +#define RETURN		0xC3
    1.59 +#else
    1.60 +#error Need assembly opcodes for this architecture
    1.61 +#endif
    1.62 +
    1.63 +#if defined(__ELF__) && defined(__GNUC__)
    1.64 +extern unsigned char _copy_row[4096] __attribute__ ((alias ("copy_row")));
    1.65 +#endif
    1.66 +static unsigned char copy_row[4096];
    1.67 +
    1.68 +static int generate_rowbytes(int src_w, int dst_w, int bpp)
    1.69 +{
    1.70 +	static struct {
    1.71 +		int bpp;
    1.72 +		int src_w;
    1.73 +		int dst_w;
    1.74 +	} last;
    1.75 +
    1.76 +	int i;
    1.77 +	int pos, inc;
    1.78 +	unsigned char *eip;
    1.79 +	unsigned char load, store;
    1.80 +
    1.81 +	/* See if we need to regenerate the copy buffer */
    1.82 +	if ( (src_w == last.src_w) &&
    1.83 +	     (dst_w == last.src_w) && (bpp == last.bpp) ) {
    1.84 +		return(0);
    1.85 +	}
    1.86 +	last.bpp = bpp;
    1.87 +	last.src_w = src_w;
    1.88 +	last.dst_w = dst_w;
    1.89 +
    1.90 +	switch (bpp) {
    1.91 +	    case 1:
    1.92 +		load = LOAD_BYTE;
    1.93 +		store = STORE_BYTE;
    1.94 +		break;
    1.95 +	    case 2:
    1.96 +	    case 4:
    1.97 +		load = LOAD_WORD;
    1.98 +		store = STORE_WORD;
    1.99 +		break;
   1.100 +	    default:
   1.101 +		SDL_SetError("ASM stretch of %d bytes isn't supported\n", bpp);
   1.102 +		return(-1);
   1.103 +	}
   1.104 +	pos = 0x10000;
   1.105 +	inc = (src_w << 16) / dst_w;
   1.106 +	eip = copy_row;
   1.107 +	for ( i=0; i<dst_w; ++i ) {
   1.108 +		while ( pos >= 0x10000L ) {
   1.109 +			if ( bpp == 2 ) {
   1.110 +				*eip++ = PREFIX16;
   1.111 +			}
   1.112 +			*eip++ = load;
   1.113 +			pos -= 0x10000L;
   1.114 +		}
   1.115 +		if ( bpp == 2 ) {
   1.116 +			*eip++ = PREFIX16;
   1.117 +		}
   1.118 +		*eip++ = store;
   1.119 +		pos += inc;
   1.120 +	}
   1.121 +	*eip++ = RETURN;
   1.122 +
   1.123 +	/* Verify that we didn't overflow (too late) */
   1.124 +	if ( eip > (copy_row+sizeof(copy_row)) ) {
   1.125 +		SDL_SetError("Copy buffer overflow");
   1.126 +		return(-1);
   1.127 +	}
   1.128 +	return(0);
   1.129 +}
   1.130 +
   1.131 +#else
   1.132 +
   1.133 +#define DEFINE_COPY_ROW(name, type)			\
   1.134 +void name(type *src, int src_w, type *dst, int dst_w)	\
   1.135 +{							\
   1.136 +	int i;						\
   1.137 +	int pos, inc;					\
   1.138 +	type pixel = 0;					\
   1.139 +							\
   1.140 +	pos = 0x10000;					\
   1.141 +	inc = (src_w << 16) / dst_w;			\
   1.142 +	for ( i=dst_w; i>0; --i ) {			\
   1.143 +		while ( pos >= 0x10000L ) {		\
   1.144 +			pixel = *src++;			\
   1.145 +			pos -= 0x10000L;		\
   1.146 +		}					\
   1.147 +		*dst++ = pixel;				\
   1.148 +		pos += inc;				\
   1.149 +	}						\
   1.150 +}
   1.151 +DEFINE_COPY_ROW(copy_row1, Uint8)
   1.152 +DEFINE_COPY_ROW(copy_row2, Uint16)
   1.153 +DEFINE_COPY_ROW(copy_row4, Uint32)
   1.154 +
   1.155 +#endif /* USE_ASM_STRETCH */
   1.156 +
   1.157 +/* The ASM code doesn't handle 24-bpp stretch blits */
   1.158 +void copy_row3(Uint8 *src, int src_w, Uint8 *dst, int dst_w)
   1.159 +{
   1.160 +	int i;
   1.161 +	int pos, inc;
   1.162 +	Uint8 pixel[3];
   1.163 +
   1.164 +	pos = 0x10000;
   1.165 +	inc = (src_w << 16) / dst_w;
   1.166 +	for ( i=dst_w; i>0; --i ) {
   1.167 +		while ( pos >= 0x10000L ) {
   1.168 +			pixel[0] = *src++;
   1.169 +			pixel[1] = *src++;
   1.170 +			pixel[2] = *src++;
   1.171 +			pos -= 0x10000L;
   1.172 +		}
   1.173 +		*dst++ = pixel[0];
   1.174 +		*dst++ = pixel[1];
   1.175 +		*dst++ = pixel[2];
   1.176 +		pos += inc;
   1.177 +	}
   1.178 +}
   1.179 +
   1.180 +/* Perform a stretch blit between two surfaces of the same format.
   1.181 +   NOTE:  This function is not safe to call from multiple threads!
   1.182 +*/
   1.183 +int SDL_SoftStretch(SDL_Surface *src, SDL_Rect *srcrect,
   1.184 +                    SDL_Surface *dst, SDL_Rect *dstrect)
   1.185 +{
   1.186 +	int pos, inc;
   1.187 +	int dst_width;
   1.188 +	int dst_maxrow;
   1.189 +	int src_row, dst_row;
   1.190 +	Uint8 *srcp = NULL;
   1.191 +	Uint8 *dstp;
   1.192 +	SDL_Rect full_src;
   1.193 +	SDL_Rect full_dst;
   1.194 +#if defined(USE_ASM_STRETCH) && defined(__GNUC__)
   1.195 +	int u1, u2;
   1.196 +#endif
   1.197 +	const int bpp = dst->format->BytesPerPixel;
   1.198 +
   1.199 +	if ( src->format->BitsPerPixel != dst->format->BitsPerPixel ) {
   1.200 +		SDL_SetError("Only works with same format surfaces");
   1.201 +		return(-1);
   1.202 +	}
   1.203 +
   1.204 +	/* Verify the blit rectangles */
   1.205 +	if ( srcrect ) {
   1.206 +		if ( (srcrect->x < 0) || (srcrect->y < 0) ||
   1.207 +		     ((srcrect->x+srcrect->w) > src->w) ||
   1.208 +		     ((srcrect->y+srcrect->h) > src->h) ) {
   1.209 +			SDL_SetError("Invalid source blit rectangle");
   1.210 +			return(-1);
   1.211 +		}
   1.212 +	} else {
   1.213 +		full_src.x = 0;
   1.214 +		full_src.y = 0;
   1.215 +		full_src.w = src->w;
   1.216 +		full_src.h = src->h;
   1.217 +		srcrect = &full_src;
   1.218 +	}
   1.219 +	if ( dstrect ) {
   1.220 +		if ( (dstrect->x < 0) || (dstrect->y < 0) ||
   1.221 +		     ((dstrect->x+dstrect->w) > dst->w) ||
   1.222 +		     ((dstrect->y+dstrect->h) > dst->h) ) {
   1.223 +			SDL_SetError("Invalid destination blit rectangle");
   1.224 +			return(-1);
   1.225 +		}
   1.226 +	} else {
   1.227 +		full_dst.x = 0;
   1.228 +		full_dst.y = 0;
   1.229 +		full_dst.w = dst->w;
   1.230 +		full_dst.h = dst->h;
   1.231 +		dstrect = &full_dst;
   1.232 +	}
   1.233 +
   1.234 +	/* Set up the data... */
   1.235 +	pos = 0x10000;
   1.236 +	inc = (srcrect->h << 16) / dstrect->h;
   1.237 +	src_row = srcrect->y;
   1.238 +	dst_row = dstrect->y;
   1.239 +	dst_width = dstrect->w*bpp;
   1.240 +
   1.241 +#ifdef USE_ASM_STRETCH
   1.242 +	/* Write the opcodes for this stretch */
   1.243 +	if ( (bpp != 3) &&
   1.244 +	     (generate_rowbytes(srcrect->w, dstrect->w, bpp) < 0) ) {
   1.245 +		return(-1);
   1.246 +	}
   1.247 +#endif
   1.248 +
   1.249 +	/* Perform the stretch blit */
   1.250 +	for ( dst_maxrow = dst_row+dstrect->h; dst_row<dst_maxrow; ++dst_row ) {
   1.251 +		dstp = (Uint8 *)dst->pixels + (dst_row*dst->pitch)
   1.252 +		                            + (dstrect->x*bpp);
   1.253 +		while ( pos >= 0x10000L ) {
   1.254 +			srcp = (Uint8 *)src->pixels + (src_row*src->pitch)
   1.255 +			                            + (srcrect->x*bpp);
   1.256 +			++src_row;
   1.257 +			pos -= 0x10000L;
   1.258 +		}
   1.259 +#ifdef USE_ASM_STRETCH
   1.260 +		switch (bpp) {
   1.261 +		    case 3:
   1.262 +			copy_row3(srcp, srcrect->w, dstp, dstrect->w);
   1.263 +			break;
   1.264 +		    default:
   1.265 +#ifdef __GNUC__
   1.266 +			__asm__ __volatile__ ("
   1.267 +				call _copy_row
   1.268 +			"
   1.269 +			: "=&D" (u1), "=&S" (u2)
   1.270 +			: "0" (dstp), "1" (srcp)
   1.271 +			: "memory" );
   1.272 +#else
   1.273 +#ifdef WIN32
   1.274 +		{ void *code = &copy_row;
   1.275 +			__asm {
   1.276 +				push edi
   1.277 +				push esi
   1.278 +	
   1.279 +				mov edi, dstp
   1.280 +				mov esi, srcp
   1.281 +				call dword ptr code
   1.282 +
   1.283 +				pop esi
   1.284 +				pop edi
   1.285 +			}
   1.286 +		}
   1.287 +#else
   1.288 +#error Need inline assembly for this compiler
   1.289 +#endif
   1.290 +#endif /* __GNUC__ */
   1.291 +			break;
   1.292 +		}
   1.293 +#else
   1.294 +		switch (bpp) {
   1.295 +		    case 1:
   1.296 +			copy_row1(srcp, srcrect->w, dstp, dstrect->w);
   1.297 +			break;
   1.298 +		    case 2:
   1.299 +			copy_row2((Uint16 *)srcp, srcrect->w,
   1.300 +			          (Uint16 *)dstp, dstrect->w);
   1.301 +			break;
   1.302 +		    case 3:
   1.303 +			copy_row3(srcp, srcrect->w, dstp, dstrect->w);
   1.304 +			break;
   1.305 +		    case 4:
   1.306 +			copy_row4((Uint32 *)srcp, srcrect->w,
   1.307 +			          (Uint32 *)dstp, dstrect->w);
   1.308 +			break;
   1.309 +		}
   1.310 +#endif
   1.311 +		pos += inc;
   1.312 +	}
   1.313 +	return(0);
   1.314 +}
   1.315 +