src/video/SDL_stretch.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 18 Oct 2009 17:31:37 +0000
branchSDL-1.2
changeset 4355 9b464226e541
parent 4159 a1b03ba2fcd0
child 4356 ab2dfac9d5c1
permissions -rw-r--r--
Fixed bug #855

Ludwig Nussel 2009-10-18 06:31:52 PDT

an mprotect call was added to fix bug 528. However that results in a buffer
that allows writing and code execution. Ie the no-execute security features of
modern operating systems are defeated this way. Two mprotect calls are needed.
One to make the buffer executable but not writeable when done and another one
to make the buffer writeable again if the content needs to be changed.
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@4159
     3
    Copyright (C) 1997-2009 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@0
    23
slouken@0
    24
/* This a stretch blit implementation based on ideas given to me by
slouken@0
    25
   Tomasz Cejner - thanks! :)
slouken@0
    26
slouken@0
    27
   April 27, 2000 - Sam Lantinga
slouken@0
    28
*/
slouken@0
    29
slouken@0
    30
#include "SDL_video.h"
slouken@0
    31
#include "SDL_blit.h"
slouken@0
    32
slouken@0
    33
/* This isn't ready for general consumption yet - it should be folded
slouken@0
    34
   into the general blitting mechanism.
slouken@0
    35
*/
slouken@0
    36
slouken@1361
    37
#if ((defined(_MFC_VER) && defined(_M_IX86)/* && !defined(_WIN32_WCE) still needed? */) || \
slouken@1442
    38
     defined(__WATCOMC__) || \
slouken@1402
    39
     (defined(__GNUC__) && defined(__i386__))) && SDL_ASSEMBLY_ROUTINES
slouken@0
    40
#define USE_ASM_STRETCH
slouken@0
    41
#endif
slouken@0
    42
slouken@0
    43
#ifdef USE_ASM_STRETCH
slouken@0
    44
slouken@4109
    45
#ifdef HAVE_MPROTECT
slouken@4108
    46
#include <sys/types.h>
slouken@4108
    47
#include <sys/mman.h>
slouken@4108
    48
#endif
slouken@4109
    49
#ifdef __GNUC__
slouken@4109
    50
#define PAGE_ALIGNED __attribute__((__aligned__(4096)))
slouken@4109
    51
#else
slouken@4109
    52
#define PAGE_ALIGNED
slouken@4109
    53
#endif
slouken@4108
    54
slouken@1361
    55
#if defined(_M_IX86) || defined(i386)
slouken@0
    56
#define PREFIX16	0x66
slouken@0
    57
#define STORE_BYTE	0xAA
slouken@0
    58
#define STORE_WORD	0xAB
slouken@0
    59
#define LOAD_BYTE	0xAC
slouken@0
    60
#define LOAD_WORD	0xAD
slouken@0
    61
#define RETURN		0xC3
slouken@0
    62
#else
slouken@0
    63
#error Need assembly opcodes for this architecture
slouken@0
    64
#endif
slouken@0
    65
slouken@4109
    66
static unsigned char copy_row[4096] PAGE_ALIGNED;
slouken@0
    67
slouken@0
    68
static int generate_rowbytes(int src_w, int dst_w, int bpp)
slouken@0
    69
{
slouken@0
    70
	static struct {
slouken@0
    71
		int bpp;
slouken@0
    72
		int src_w;
slouken@0
    73
		int dst_w;
slouken@4109
    74
		int status;
slouken@0
    75
	} last;
slouken@0
    76
slouken@0
    77
	int i;
slouken@0
    78
	int pos, inc;
slouken@0
    79
	unsigned char *eip;
slouken@0
    80
	unsigned char load, store;
slouken@0
    81
slouken@0
    82
	/* See if we need to regenerate the copy buffer */
slouken@0
    83
	if ( (src_w == last.src_w) &&
icculus@1164
    84
	     (dst_w == last.dst_w) && (bpp == last.bpp) ) {
slouken@4109
    85
		return(last.status);
slouken@0
    86
	}
slouken@0
    87
	last.bpp = bpp;
slouken@0
    88
	last.src_w = src_w;
slouken@0
    89
	last.dst_w = dst_w;
slouken@4109
    90
	last.status = -1;
slouken@0
    91
slouken@0
    92
	switch (bpp) {
slouken@0
    93
	    case 1:
slouken@0
    94
		load = LOAD_BYTE;
slouken@0
    95
		store = STORE_BYTE;
slouken@0
    96
		break;
slouken@0
    97
	    case 2:
slouken@0
    98
	    case 4:
slouken@0
    99
		load = LOAD_WORD;
slouken@0
   100
		store = STORE_WORD;
slouken@0
   101
		break;
slouken@0
   102
	    default:
slouken@0
   103
		SDL_SetError("ASM stretch of %d bytes isn't supported\n", bpp);
slouken@0
   104
		return(-1);
slouken@0
   105
	}
slouken@4355
   106
#ifdef HAVE_MPROTECT
slouken@4355
   107
	/* Make the code writeable */
slouken@4355
   108
	if ( mprotect(copy_row, sizeof(copy_row), PROT_READ|PROT_WRITE) < 0 ) {
slouken@4355
   109
		SDL_SetError("Couldn't make copy buffer writeable");
slouken@4355
   110
		return(-1);
slouken@4355
   111
	}
slouken@4355
   112
#endif
slouken@0
   113
	pos = 0x10000;
slouken@0
   114
	inc = (src_w << 16) / dst_w;
slouken@0
   115
	eip = copy_row;
slouken@0
   116
	for ( i=0; i<dst_w; ++i ) {
slouken@0
   117
		while ( pos >= 0x10000L ) {
slouken@0
   118
			if ( bpp == 2 ) {
slouken@0
   119
				*eip++ = PREFIX16;
slouken@0
   120
			}
slouken@0
   121
			*eip++ = load;
slouken@0
   122
			pos -= 0x10000L;
slouken@0
   123
		}
slouken@0
   124
		if ( bpp == 2 ) {
slouken@0
   125
			*eip++ = PREFIX16;
slouken@0
   126
		}
slouken@0
   127
		*eip++ = store;
slouken@0
   128
		pos += inc;
slouken@0
   129
	}
slouken@0
   130
	*eip++ = RETURN;
slouken@0
   131
slouken@4109
   132
	/* Verify that we didn't overflow (too late!!!) */
slouken@0
   133
	if ( eip > (copy_row+sizeof(copy_row)) ) {
slouken@0
   134
		SDL_SetError("Copy buffer overflow");
slouken@0
   135
		return(-1);
slouken@0
   136
	}
slouken@4109
   137
#ifdef HAVE_MPROTECT
slouken@4355
   138
	/* Make the code executable but not writeable */
slouken@4355
   139
	if ( mprotect(copy_row, sizeof(copy_row), PROT_READ|PROT_EXEC) < 0 ) {
slouken@4109
   140
		SDL_SetError("Couldn't make copy buffer executable");
slouken@4109
   141
		return(-1);
slouken@4109
   142
	}
slouken@4109
   143
#endif
slouken@4109
   144
	last.status = 0;
slouken@0
   145
	return(0);
slouken@0
   146
}
slouken@0
   147
slouken@4109
   148
#endif /* USE_ASM_STRETCH */
slouken@0
   149
slouken@0
   150
#define DEFINE_COPY_ROW(name, type)			\
slouken@0
   151
void name(type *src, int src_w, type *dst, int dst_w)	\
slouken@0
   152
{							\
slouken@0
   153
	int i;						\
slouken@0
   154
	int pos, inc;					\
slouken@0
   155
	type pixel = 0;					\
slouken@0
   156
							\
slouken@0
   157
	pos = 0x10000;					\
slouken@0
   158
	inc = (src_w << 16) / dst_w;			\
slouken@0
   159
	for ( i=dst_w; i>0; --i ) {			\
slouken@0
   160
		while ( pos >= 0x10000L ) {		\
slouken@0
   161
			pixel = *src++;			\
slouken@0
   162
			pos -= 0x10000L;		\
slouken@0
   163
		}					\
slouken@0
   164
		*dst++ = pixel;				\
slouken@0
   165
		pos += inc;				\
slouken@0
   166
	}						\
slouken@0
   167
}
slouken@0
   168
DEFINE_COPY_ROW(copy_row1, Uint8)
slouken@0
   169
DEFINE_COPY_ROW(copy_row2, Uint16)
slouken@0
   170
DEFINE_COPY_ROW(copy_row4, Uint32)
slouken@0
   171
slouken@0
   172
/* The ASM code doesn't handle 24-bpp stretch blits */
slouken@0
   173
void copy_row3(Uint8 *src, int src_w, Uint8 *dst, int dst_w)
slouken@0
   174
{
slouken@0
   175
	int i;
slouken@0
   176
	int pos, inc;
slouken@1849
   177
	Uint8 pixel[3] = { 0, 0, 0 };
slouken@0
   178
slouken@0
   179
	pos = 0x10000;
slouken@0
   180
	inc = (src_w << 16) / dst_w;
slouken@0
   181
	for ( i=dst_w; i>0; --i ) {
slouken@0
   182
		while ( pos >= 0x10000L ) {
slouken@0
   183
			pixel[0] = *src++;
slouken@0
   184
			pixel[1] = *src++;
slouken@0
   185
			pixel[2] = *src++;
slouken@0
   186
			pos -= 0x10000L;
slouken@0
   187
		}
slouken@0
   188
		*dst++ = pixel[0];
slouken@0
   189
		*dst++ = pixel[1];
slouken@0
   190
		*dst++ = pixel[2];
slouken@0
   191
		pos += inc;
slouken@0
   192
	}
slouken@0
   193
}
slouken@0
   194
slouken@0
   195
/* Perform a stretch blit between two surfaces of the same format.
slouken@0
   196
   NOTE:  This function is not safe to call from multiple threads!
slouken@0
   197
*/
slouken@0
   198
int SDL_SoftStretch(SDL_Surface *src, SDL_Rect *srcrect,
slouken@0
   199
                    SDL_Surface *dst, SDL_Rect *dstrect)
slouken@0
   200
{
slouken@894
   201
	int src_locked;
slouken@894
   202
	int dst_locked;
slouken@0
   203
	int pos, inc;
slouken@0
   204
	int dst_width;
slouken@0
   205
	int dst_maxrow;
slouken@0
   206
	int src_row, dst_row;
slouken@0
   207
	Uint8 *srcp = NULL;
slouken@0
   208
	Uint8 *dstp;
slouken@0
   209
	SDL_Rect full_src;
slouken@0
   210
	SDL_Rect full_dst;
slouken@4109
   211
#ifdef USE_ASM_STRETCH
slouken@4109
   212
	SDL_bool use_asm = SDL_TRUE;
slouken@4109
   213
#ifdef __GNUC__
slouken@0
   214
	int u1, u2;
slouken@0
   215
#endif
slouken@4109
   216
#endif /* USE_ASM_STRETCH */
slouken@0
   217
	const int bpp = dst->format->BytesPerPixel;
slouken@0
   218
slouken@0
   219
	if ( src->format->BitsPerPixel != dst->format->BitsPerPixel ) {
slouken@0
   220
		SDL_SetError("Only works with same format surfaces");
slouken@0
   221
		return(-1);
slouken@0
   222
	}
slouken@0
   223
slouken@0
   224
	/* Verify the blit rectangles */
slouken@0
   225
	if ( srcrect ) {
slouken@0
   226
		if ( (srcrect->x < 0) || (srcrect->y < 0) ||
slouken@0
   227
		     ((srcrect->x+srcrect->w) > src->w) ||
slouken@0
   228
		     ((srcrect->y+srcrect->h) > src->h) ) {
slouken@0
   229
			SDL_SetError("Invalid source blit rectangle");
slouken@0
   230
			return(-1);
slouken@0
   231
		}
slouken@0
   232
	} else {
slouken@0
   233
		full_src.x = 0;
slouken@0
   234
		full_src.y = 0;
slouken@0
   235
		full_src.w = src->w;
slouken@0
   236
		full_src.h = src->h;
slouken@0
   237
		srcrect = &full_src;
slouken@0
   238
	}
slouken@0
   239
	if ( dstrect ) {
slouken@0
   240
		if ( (dstrect->x < 0) || (dstrect->y < 0) ||
slouken@0
   241
		     ((dstrect->x+dstrect->w) > dst->w) ||
slouken@0
   242
		     ((dstrect->y+dstrect->h) > dst->h) ) {
slouken@0
   243
			SDL_SetError("Invalid destination blit rectangle");
slouken@0
   244
			return(-1);
slouken@0
   245
		}
slouken@0
   246
	} else {
slouken@0
   247
		full_dst.x = 0;
slouken@0
   248
		full_dst.y = 0;
slouken@0
   249
		full_dst.w = dst->w;
slouken@0
   250
		full_dst.h = dst->h;
slouken@0
   251
		dstrect = &full_dst;
slouken@0
   252
	}
slouken@0
   253
slouken@894
   254
	/* Lock the destination if it's in hardware */
slouken@894
   255
	dst_locked = 0;
slouken@894
   256
	if ( SDL_MUSTLOCK(dst) ) {
slouken@894
   257
		if ( SDL_LockSurface(dst) < 0 ) {
slouken@894
   258
			SDL_SetError("Unable to lock destination surface");
slouken@894
   259
			return(-1);
slouken@894
   260
		}
slouken@894
   261
		dst_locked = 1;
slouken@894
   262
	}
slouken@894
   263
	/* Lock the source if it's in hardware */
slouken@894
   264
	src_locked = 0;
slouken@894
   265
	if ( SDL_MUSTLOCK(src) ) {
slouken@894
   266
		if ( SDL_LockSurface(src) < 0 ) {
slouken@894
   267
			if ( dst_locked ) {
slouken@894
   268
				SDL_UnlockSurface(dst);
slouken@894
   269
			}
slouken@894
   270
			SDL_SetError("Unable to lock source surface");
slouken@894
   271
			return(-1);
slouken@894
   272
		}
slouken@894
   273
		src_locked = 1;
slouken@894
   274
	}
slouken@894
   275
slouken@0
   276
	/* Set up the data... */
slouken@0
   277
	pos = 0x10000;
slouken@0
   278
	inc = (srcrect->h << 16) / dstrect->h;
slouken@0
   279
	src_row = srcrect->y;
slouken@0
   280
	dst_row = dstrect->y;
slouken@0
   281
	dst_width = dstrect->w*bpp;
slouken@0
   282
slouken@0
   283
#ifdef USE_ASM_STRETCH
slouken@0
   284
	/* Write the opcodes for this stretch */
slouken@4109
   285
	if ( (bpp == 3) ||
slouken@0
   286
	     (generate_rowbytes(srcrect->w, dstrect->w, bpp) < 0) ) {
slouken@4109
   287
		use_asm = SDL_FALSE;
slouken@0
   288
	}
slouken@0
   289
#endif
slouken@0
   290
slouken@0
   291
	/* Perform the stretch blit */
slouken@0
   292
	for ( dst_maxrow = dst_row+dstrect->h; dst_row<dst_maxrow; ++dst_row ) {
slouken@0
   293
		dstp = (Uint8 *)dst->pixels + (dst_row*dst->pitch)
slouken@0
   294
		                            + (dstrect->x*bpp);
slouken@0
   295
		while ( pos >= 0x10000L ) {
slouken@0
   296
			srcp = (Uint8 *)src->pixels + (src_row*src->pitch)
slouken@0
   297
			                            + (srcrect->x*bpp);
slouken@0
   298
			++src_row;
slouken@0
   299
			pos -= 0x10000L;
slouken@0
   300
		}
slouken@0
   301
#ifdef USE_ASM_STRETCH
slouken@4109
   302
		if (use_asm) {
slouken@0
   303
#ifdef __GNUC__
slouken@627
   304
			__asm__ __volatile__ (
icculus@1228
   305
			"call *%4"
slouken@0
   306
			: "=&D" (u1), "=&S" (u2)
icculus@1234
   307
			: "0" (dstp), "1" (srcp), "r" (copy_row)
slouken@0
   308
			: "memory" );
slouken@1442
   309
#elif defined(_MSC_VER) || defined(__WATCOMC__)
icculus@1234
   310
		{ void *code = copy_row;
slouken@0
   311
			__asm {
slouken@0
   312
				push edi
slouken@0
   313
				push esi
slouken@0
   314
	
slouken@0
   315
				mov edi, dstp
slouken@0
   316
				mov esi, srcp
slouken@0
   317
				call dword ptr code
slouken@0
   318
slouken@0
   319
				pop esi
slouken@0
   320
				pop edi
slouken@0
   321
			}
slouken@0
   322
		}
slouken@0
   323
#else
slouken@0
   324
#error Need inline assembly for this compiler
slouken@0
   325
#endif
slouken@4109
   326
		} else
slouken@4109
   327
#endif
slouken@0
   328
		switch (bpp) {
slouken@0
   329
		    case 1:
slouken@0
   330
			copy_row1(srcp, srcrect->w, dstp, dstrect->w);
slouken@0
   331
			break;
slouken@0
   332
		    case 2:
slouken@0
   333
			copy_row2((Uint16 *)srcp, srcrect->w,
slouken@0
   334
			          (Uint16 *)dstp, dstrect->w);
slouken@0
   335
			break;
slouken@0
   336
		    case 3:
slouken@0
   337
			copy_row3(srcp, srcrect->w, dstp, dstrect->w);
slouken@0
   338
			break;
slouken@0
   339
		    case 4:
slouken@0
   340
			copy_row4((Uint32 *)srcp, srcrect->w,
slouken@0
   341
			          (Uint32 *)dstp, dstrect->w);
slouken@0
   342
			break;
slouken@0
   343
		}
slouken@0
   344
		pos += inc;
slouken@0
   345
	}
slouken@894
   346
slouken@894
   347
	/* We need to unlock the surfaces if they're locked */
slouken@894
   348
	if ( dst_locked ) {
slouken@894
   349
		SDL_UnlockSurface(dst);
slouken@894
   350
	}
slouken@894
   351
	if ( src_locked ) {
slouken@894
   352
		SDL_UnlockSurface(src);
slouken@894
   353
	}
slouken@0
   354
	return(0);
slouken@0
   355
}
slouken@0
   356