SDL_stretch.c (10029B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../SDL_internal.h" 22 23/* This a stretch blit implementation based on ideas given to me by 24 Tomasz Cejner - thanks! :) 25 26 April 27, 2000 - Sam Lantinga 27*/ 28 29#include "SDL_video.h" 30#include "SDL_blit.h" 31 32/* This isn't ready for general consumption yet - it should be folded 33 into the general blitting mechanism. 34*/ 35 36#if ((defined(_MFC_VER) && defined(_M_IX86)) || \ 37 defined(__WATCOMC__) || \ 38 (defined(__GNUC__) && defined(__i386__))) && SDL_ASSEMBLY_ROUTINES 39/* There's a bug with gcc 4.4.1 and -O2 where srcp doesn't get the correct 40 * value after the first scanline. FIXME? */ 41/* #define USE_ASM_STRETCH */ 42#endif 43 44#ifdef USE_ASM_STRETCH 45 46#ifdef HAVE_MPROTECT 47#include <sys/types.h> 48#include <sys/mman.h> 49#endif 50#ifdef __GNUC__ 51#define PAGE_ALIGNED __attribute__((__aligned__(4096))) 52#else 53#define PAGE_ALIGNED 54#endif 55 56#if defined(_M_IX86) || defined(i386) 57#define PREFIX16 0x66 58#define STORE_BYTE 0xAA 59#define STORE_WORD 0xAB 60#define LOAD_BYTE 0xAC 61#define LOAD_WORD 0xAD 62#define RETURN 0xC3 63#else 64#error Need assembly opcodes for this architecture 65#endif 66 67static unsigned char copy_row[4096] PAGE_ALIGNED; 68 69static int 70generate_rowbytes(int src_w, int dst_w, int bpp) 71{ 72 static struct 73 { 74 int bpp; 75 int src_w; 76 int dst_w; 77 int status; 78 } last; 79 80 int i; 81 int pos, inc; 82 unsigned char *eip, *fence; 83 unsigned char load, store; 84 85 /* See if we need to regenerate the copy buffer */ 86 if ((src_w == last.src_w) && (dst_w == last.dst_w) && (bpp == last.bpp)) { 87 return (last.status); 88 } 89 last.bpp = bpp; 90 last.src_w = src_w; 91 last.dst_w = dst_w; 92 last.status = -1; 93 94 switch (bpp) { 95 case 1: 96 load = LOAD_BYTE; 97 store = STORE_BYTE; 98 break; 99 case 2: 100 case 4: 101 load = LOAD_WORD; 102 store = STORE_WORD; 103 break; 104 default: 105 return SDL_SetError("ASM stretch of %d bytes isn't supported\n", bpp); 106 } 107#ifdef HAVE_MPROTECT 108 /* Make the code writeable */ 109 if (mprotect(copy_row, sizeof(copy_row), PROT_READ | PROT_WRITE) < 0) { 110 return SDL_SetError("Couldn't make copy buffer writeable"); 111 } 112#endif 113 pos = 0x10000; 114 inc = (src_w << 16) / dst_w; 115 eip = copy_row; 116 fence = copy_row + sizeof(copy_row)-2; 117 for (i = 0; i < dst_w; ++i) { 118 while (pos >= 0x10000L) { 119 if (eip == fence) { 120 return -1; 121 } 122 if (bpp == 2) { 123 *eip++ = PREFIX16; 124 } 125 *eip++ = load; 126 pos -= 0x10000L; 127 } 128 if (eip == fence) { 129 return -1; 130 } 131 if (bpp == 2) { 132 *eip++ = PREFIX16; 133 } 134 *eip++ = store; 135 pos += inc; 136 } 137 *eip++ = RETURN; 138 139#ifdef HAVE_MPROTECT 140 /* Make the code executable but not writeable */ 141 if (mprotect(copy_row, sizeof(copy_row), PROT_READ | PROT_EXEC) < 0) { 142 return SDL_SetError("Couldn't make copy buffer executable"); 143 } 144#endif 145 last.status = 0; 146 return (0); 147} 148 149#endif /* USE_ASM_STRETCH */ 150 151#define DEFINE_COPY_ROW(name, type) \ 152static void name(type *src, int src_w, type *dst, int dst_w) \ 153{ \ 154 int i; \ 155 int pos, inc; \ 156 type pixel = 0; \ 157 \ 158 pos = 0x10000; \ 159 inc = (src_w << 16) / dst_w; \ 160 for ( i=dst_w; i>0; --i ) { \ 161 while ( pos >= 0x10000L ) { \ 162 pixel = *src++; \ 163 pos -= 0x10000L; \ 164 } \ 165 *dst++ = pixel; \ 166 pos += inc; \ 167 } \ 168} 169/* *INDENT-OFF* */ 170DEFINE_COPY_ROW(copy_row1, Uint8) 171DEFINE_COPY_ROW(copy_row2, Uint16) 172DEFINE_COPY_ROW(copy_row4, Uint32) 173/* *INDENT-ON* */ 174 175/* The ASM code doesn't handle 24-bpp stretch blits */ 176static void 177copy_row3(Uint8 * src, int src_w, Uint8 * dst, int dst_w) 178{ 179 int i; 180 int pos, inc; 181 Uint8 pixel[3] = { 0, 0, 0 }; 182 183 pos = 0x10000; 184 inc = (src_w << 16) / dst_w; 185 for (i = dst_w; i > 0; --i) { 186 while (pos >= 0x10000L) { 187 pixel[0] = *src++; 188 pixel[1] = *src++; 189 pixel[2] = *src++; 190 pos -= 0x10000L; 191 } 192 *dst++ = pixel[0]; 193 *dst++ = pixel[1]; 194 *dst++ = pixel[2]; 195 pos += inc; 196 } 197} 198 199/* Perform a stretch blit between two surfaces of the same format. 200 NOTE: This function is not safe to call from multiple threads! 201*/ 202int 203SDL_SoftStretch(SDL_Surface * src, const SDL_Rect * srcrect, 204 SDL_Surface * dst, const SDL_Rect * dstrect) 205{ 206 int src_locked; 207 int dst_locked; 208 int pos, inc; 209 int dst_maxrow; 210 int src_row, dst_row; 211 Uint8 *srcp = NULL; 212 Uint8 *dstp; 213 SDL_Rect full_src; 214 SDL_Rect full_dst; 215#ifdef USE_ASM_STRETCH 216 SDL_bool use_asm = SDL_TRUE; 217#ifdef __GNUC__ 218 int u1, u2; 219#endif 220#endif /* USE_ASM_STRETCH */ 221 const int bpp = dst->format->BytesPerPixel; 222 223 if (src->format->format != dst->format->format) { 224 return SDL_SetError("Only works with same format surfaces"); 225 } 226 227 /* Verify the blit rectangles */ 228 if (srcrect) { 229 if ((srcrect->x < 0) || (srcrect->y < 0) || 230 ((srcrect->x + srcrect->w) > src->w) || 231 ((srcrect->y + srcrect->h) > src->h)) { 232 return SDL_SetError("Invalid source blit rectangle"); 233 } 234 } else { 235 full_src.x = 0; 236 full_src.y = 0; 237 full_src.w = src->w; 238 full_src.h = src->h; 239 srcrect = &full_src; 240 } 241 if (dstrect) { 242 if ((dstrect->x < 0) || (dstrect->y < 0) || 243 ((dstrect->x + dstrect->w) > dst->w) || 244 ((dstrect->y + dstrect->h) > dst->h)) { 245 return SDL_SetError("Invalid destination blit rectangle"); 246 } 247 } else { 248 full_dst.x = 0; 249 full_dst.y = 0; 250 full_dst.w = dst->w; 251 full_dst.h = dst->h; 252 dstrect = &full_dst; 253 } 254 255 /* Lock the destination if it's in hardware */ 256 dst_locked = 0; 257 if (SDL_MUSTLOCK(dst)) { 258 if (SDL_LockSurface(dst) < 0) { 259 return SDL_SetError("Unable to lock destination surface"); 260 } 261 dst_locked = 1; 262 } 263 /* Lock the source if it's in hardware */ 264 src_locked = 0; 265 if (SDL_MUSTLOCK(src)) { 266 if (SDL_LockSurface(src) < 0) { 267 if (dst_locked) { 268 SDL_UnlockSurface(dst); 269 } 270 return SDL_SetError("Unable to lock source surface"); 271 } 272 src_locked = 1; 273 } 274 275 /* Set up the data... */ 276 pos = 0x10000; 277 inc = (srcrect->h << 16) / dstrect->h; 278 src_row = srcrect->y; 279 dst_row = dstrect->y; 280 281#ifdef USE_ASM_STRETCH 282 /* Write the opcodes for this stretch */ 283 if ((bpp == 3) || (generate_rowbytes(srcrect->w, dstrect->w, bpp) < 0)) { 284 use_asm = SDL_FALSE; 285 } 286#endif 287 288 /* Perform the stretch blit */ 289 for (dst_maxrow = dst_row + dstrect->h; dst_row < dst_maxrow; ++dst_row) { 290 dstp = (Uint8 *) dst->pixels + (dst_row * dst->pitch) 291 + (dstrect->x * bpp); 292 while (pos >= 0x10000L) { 293 srcp = (Uint8 *) src->pixels + (src_row * src->pitch) 294 + (srcrect->x * bpp); 295 ++src_row; 296 pos -= 0x10000L; 297 } 298#ifdef USE_ASM_STRETCH 299 if (use_asm) { 300#ifdef __GNUC__ 301 __asm__ __volatile__("call *%4":"=&D"(u1), "=&S"(u2) 302 :"0"(dstp), "1"(srcp), "r"(copy_row) 303 :"memory"); 304#elif defined(_MSC_VER) || defined(__WATCOMC__) 305 /* *INDENT-OFF* */ 306 { 307 void *code = copy_row; 308 __asm { 309 push edi 310 push esi 311 mov edi, dstp 312 mov esi, srcp 313 call dword ptr code 314 pop esi 315 pop edi 316 } 317 } 318 /* *INDENT-ON* */ 319#else 320#error Need inline assembly for this compiler 321#endif 322 } else 323#endif 324 switch (bpp) { 325 case 1: 326 copy_row1(srcp, srcrect->w, dstp, dstrect->w); 327 break; 328 case 2: 329 copy_row2((Uint16 *) srcp, srcrect->w, 330 (Uint16 *) dstp, dstrect->w); 331 break; 332 case 3: 333 copy_row3(srcp, srcrect->w, dstp, dstrect->w); 334 break; 335 case 4: 336 copy_row4((Uint32 *) srcp, srcrect->w, 337 (Uint32 *) dstp, dstrect->w); 338 break; 339 } 340 pos += inc; 341 } 342 343 /* We need to unlock the surfaces if they're locked */ 344 if (dst_locked) { 345 SDL_UnlockSurface(dst); 346 } 347 if (src_locked) { 348 SDL_UnlockSurface(src); 349 } 350 return (0); 351} 352 353/* vi: set ts=4 sw=4 expandtab: */