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