| 1 | /* |
| 2 | SDL - Simple DirectMedia Layer |
| 3 | Copyright (C) 1997-2009 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@libsdl.org |
| 21 | */ |
| 22 | #include "SDL_config.h" |
| 23 | |
| 24 | #include "SDL_video.h" |
| 25 | #include "SDL_sysvideo.h" |
| 26 | #include "SDL_blit.h" |
| 27 | #include "SDL_RLEaccel_c.h" |
| 28 | #include "SDL_pixels_c.h" |
| 29 | |
| 30 | #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && SDL_ASSEMBLY_ROUTINES |
| 31 | #define MMX_ASMBLIT |
| 32 | #if (__GNUC__ > 2) /* SSE instructions aren't in GCC 2. */ |
| 33 | #define SSE_ASMBLIT |
| 34 | #endif |
| 35 | #endif |
| 36 | |
| 37 | #if defined(MMX_ASMBLIT) |
| 38 | #include "SDL_cpuinfo.h" |
| 39 | #include "mmx.h" |
| 40 | #endif |
| 41 | |
| 42 | /* The general purpose software blit routine */ |
| 43 | static int SDL_SoftBlit(SDL_Surface *src, SDL_Rect *srcrect, |
| 44 | SDL_Surface *dst, SDL_Rect *dstrect) |
| 45 | { |
| 46 | int okay; |
| 47 | int src_locked; |
| 48 | int dst_locked; |
| 49 | |
| 50 | /* Everything is okay at the beginning... */ |
| 51 | okay = 1; |
| 52 | |
| 53 | /* Lock the destination if it's in hardware */ |
| 54 | dst_locked = 0; |
| 55 | if ( SDL_MUSTLOCK(dst) ) { |
| 56 | if ( SDL_LockSurface(dst) < 0 ) { |
| 57 | okay = 0; |
| 58 | } else { |
| 59 | dst_locked = 1; |
| 60 | } |
| 61 | } |
| 62 | /* Lock the source if it's in hardware */ |
| 63 | src_locked = 0; |
| 64 | if ( SDL_MUSTLOCK(src) ) { |
| 65 | if ( SDL_LockSurface(src) < 0 ) { |
| 66 | okay = 0; |
| 67 | } else { |
| 68 | src_locked = 1; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /* Set up source and destination buffer pointers, and BLIT! */ |
| 73 | if ( okay && srcrect->w && srcrect->h ) { |
| 74 | SDL_BlitInfo info; |
| 75 | SDL_loblit RunBlit; |
| 76 | |
| 77 | /* Set up the blit information */ |
| 78 | info.s_pixels = (Uint8 *)src->pixels + |
| 79 | (Uint16)srcrect->y*src->pitch + |
| 80 | (Uint16)srcrect->x*src->format->BytesPerPixel; |
| 81 | info.s_width = srcrect->w; |
| 82 | info.s_height = srcrect->h; |
| 83 | info.s_skip=src->pitch-info.s_width*src->format->BytesPerPixel; |
| 84 | info.d_pixels = (Uint8 *)dst->pixels + |
| 85 | (Uint16)dstrect->y*dst->pitch + |
| 86 | (Uint16)dstrect->x*dst->format->BytesPerPixel; |
| 87 | info.d_width = dstrect->w; |
| 88 | info.d_height = dstrect->h; |
| 89 | info.d_skip=dst->pitch-info.d_width*dst->format->BytesPerPixel; |
| 90 | info.aux_data = src->map->sw_data->aux_data; |
| 91 | info.src = src->format; |
| 92 | info.table = src->map->table; |
| 93 | info.dst = dst->format; |
| 94 | RunBlit = src->map->sw_data->blit; |
| 95 | |
| 96 | /* Run the actual software blit */ |
| 97 | RunBlit(&info); |
| 98 | } |
| 99 | |
| 100 | /* We need to unlock the surfaces if they're locked */ |
| 101 | if ( dst_locked ) { |
| 102 | SDL_UnlockSurface(dst); |
| 103 | } |
| 104 | if ( src_locked ) { |
| 105 | SDL_UnlockSurface(src); |
| 106 | } |
| 107 | /* Blit is done! */ |
| 108 | return(okay ? 0 : -1); |
| 109 | } |
| 110 | |
| 111 | #ifdef MMX_ASMBLIT |
| 112 | static __inline__ void SDL_memcpyMMX(Uint8 *to, const Uint8 *from, int len) |
| 113 | { |
| 114 | int i; |
| 115 | |
| 116 | for(i=0; i<len/8; i++) { |
| 117 | __asm__ __volatile__ ( |
| 118 | " movq (%0), %%mm0\n" |
| 119 | " movq %%mm0, (%1)\n" |
| 120 | : : "r" (from), "r" (to) : "memory"); |
| 121 | from+=8; |
| 122 | to+=8; |
| 123 | } |
| 124 | if (len&7) |
| 125 | SDL_memcpy(to, from, len&7); |
| 126 | } |
| 127 | |
| 128 | #ifdef SSE_ASMBLIT |
| 129 | static __inline__ void SDL_memcpySSE(Uint8 *to, const Uint8 *from, int len) |
| 130 | { |
| 131 | int i; |
| 132 | |
| 133 | __asm__ __volatile__ ( |
| 134 | " prefetchnta (%0)\n" |
| 135 | " prefetchnta 64(%0)\n" |
| 136 | " prefetchnta 128(%0)\n" |
| 137 | " prefetchnta 192(%0)\n" |
| 138 | : : "r" (from) ); |
| 139 | |
| 140 | for(i=0; i<len/8; i++) { |
| 141 | __asm__ __volatile__ ( |
| 142 | " prefetchnta 256(%0)\n" |
| 143 | " movq (%0), %%mm0\n" |
| 144 | " movntq %%mm0, (%1)\n" |
| 145 | : : "r" (from), "r" (to) : "memory"); |
| 146 | from+=8; |
| 147 | to+=8; |
| 148 | } |
| 149 | if (len&7) |
| 150 | SDL_memcpy(to, from, len&7); |
| 151 | } |
| 152 | #endif |
| 153 | #endif |
| 154 | |
| 155 | static void SDL_BlitCopy(SDL_BlitInfo *info) |
| 156 | { |
| 157 | Uint8 *src, *dst; |
| 158 | int w, h; |
| 159 | int srcskip, dstskip; |
| 160 | |
| 161 | w = info->d_width*info->dst->BytesPerPixel; |
| 162 | h = info->d_height; |
| 163 | src = info->s_pixels; |
| 164 | dst = info->d_pixels; |
| 165 | srcskip = w+info->s_skip; |
| 166 | dstskip = w+info->d_skip; |
| 167 | |
| 168 | #ifdef SSE_ASMBLIT |
| 169 | if(SDL_HasSSE()) |
| 170 | { |
| 171 | while ( h-- ) { |
| 172 | SDL_memcpySSE(dst, src, w); |
| 173 | src += srcskip; |
| 174 | dst += dstskip; |
| 175 | } |
| 176 | __asm__ __volatile__ ( |
| 177 | " emms\n" |
| 178 | ::); |
| 179 | } |
| 180 | else |
| 181 | #endif |
| 182 | #ifdef MMX_ASMBLIT |
| 183 | if(SDL_HasMMX()) |
| 184 | { |
| 185 | while ( h-- ) { |
| 186 | SDL_memcpyMMX(dst, src, w); |
| 187 | src += srcskip; |
| 188 | dst += dstskip; |
| 189 | } |
| 190 | __asm__ __volatile__ ( |
| 191 | " emms\n" |
| 192 | ::); |
| 193 | } |
| 194 | else |
| 195 | #endif |
| 196 | while ( h-- ) { |
| 197 | SDL_memcpy(dst, src, w); |
| 198 | src += srcskip; |
| 199 | dst += dstskip; |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | static void SDL_BlitCopyOverlap(SDL_BlitInfo *info) |
| 204 | { |
| 205 | Uint8 *src, *dst; |
| 206 | int w, h; |
| 207 | int srcskip, dstskip; |
| 208 | |
| 209 | w = info->d_width*info->dst->BytesPerPixel; |
| 210 | h = info->d_height; |
| 211 | src = info->s_pixels; |
| 212 | dst = info->d_pixels; |
| 213 | srcskip = w+info->s_skip; |
| 214 | dstskip = w+info->d_skip; |
| 215 | if ( dst < src ) { |
| 216 | while ( h-- ) { |
| 217 | SDL_memcpy(dst, src, w); |
| 218 | src += srcskip; |
| 219 | dst += dstskip; |
| 220 | } |
| 221 | } else { |
| 222 | src += ((h-1) * srcskip); |
| 223 | dst += ((h-1) * dstskip); |
| 224 | while ( h-- ) { |
| 225 | SDL_revcpy(dst, src, w); |
| 226 | src -= srcskip; |
| 227 | dst -= dstskip; |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | /* Figure out which of many blit routines to set up on a surface */ |
| 233 | int SDL_CalculateBlit(SDL_Surface *surface) |
| 234 | { |
| 235 | int blit_index; |
| 236 | |
| 237 | /* Clean everything out to start */ |
| 238 | if ( (surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL ) { |
| 239 | SDL_UnRLESurface(surface, 1); |
| 240 | } |
| 241 | surface->map->sw_blit = NULL; |
| 242 | |
| 243 | /* Figure out if an accelerated hardware blit is possible */ |
| 244 | surface->flags &= ~SDL_HWACCEL; |
| 245 | if ( surface->map->identity ) { |
| 246 | int hw_blit_ok; |
| 247 | |
| 248 | if ( (surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE ) { |
| 249 | /* We only support accelerated blitting to hardware */ |
| 250 | if ( surface->map->dst->flags & SDL_HWSURFACE ) { |
| 251 | hw_blit_ok = current_video->info.blit_hw; |
| 252 | } else { |
| 253 | hw_blit_ok = 0; |
| 254 | } |
| 255 | if (hw_blit_ok && (surface->flags & SDL_SRCCOLORKEY)) { |
| 256 | hw_blit_ok = current_video->info.blit_hw_CC; |
| 257 | } |
| 258 | if ( hw_blit_ok && (surface->flags & SDL_SRCALPHA) ) { |
| 259 | hw_blit_ok = current_video->info.blit_hw_A; |
| 260 | } |
| 261 | } else { |
| 262 | /* We only support accelerated blitting to hardware */ |
| 263 | if ( surface->map->dst->flags & SDL_HWSURFACE ) { |
| 264 | hw_blit_ok = current_video->info.blit_sw; |
| 265 | } else { |
| 266 | hw_blit_ok = 0; |
| 267 | } |
| 268 | if (hw_blit_ok && (surface->flags & SDL_SRCCOLORKEY)) { |
| 269 | hw_blit_ok = current_video->info.blit_sw_CC; |
| 270 | } |
| 271 | if ( hw_blit_ok && (surface->flags & SDL_SRCALPHA) ) { |
| 272 | hw_blit_ok = current_video->info.blit_sw_A; |
| 273 | } |
| 274 | } |
| 275 | if ( hw_blit_ok ) { |
| 276 | SDL_VideoDevice *video = current_video; |
| 277 | SDL_VideoDevice *this = current_video; |
| 278 | video->CheckHWBlit(this, surface, surface->map->dst); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /* if an alpha pixel format is specified, we can accelerate alpha blits */ |
| 283 | if (((surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE )&&(current_video->displayformatalphapixel)) |
| 284 | { |
| 285 | if ( (surface->flags & SDL_SRCALPHA) ) |
| 286 | if ( current_video->info.blit_hw_A ) { |
| 287 | SDL_VideoDevice *video = current_video; |
| 288 | SDL_VideoDevice *this = current_video; |
| 289 | video->CheckHWBlit(this, surface, surface->map->dst); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* Get the blit function index, based on surface mode */ |
| 294 | /* { 0 = nothing, 1 = colorkey, 2 = alpha, 3 = colorkey+alpha } */ |
| 295 | blit_index = 0; |
| 296 | blit_index |= (!!(surface->flags & SDL_SRCCOLORKEY)) << 0; |
| 297 | if ( surface->flags & SDL_SRCALPHA |
| 298 | && (surface->format->alpha != SDL_ALPHA_OPAQUE |
| 299 | || surface->format->Amask) ) { |
| 300 | blit_index |= 2; |
| 301 | } |
| 302 | |
| 303 | /* Check for special "identity" case -- copy blit */ |
| 304 | if ( surface->map->identity && blit_index == 0 ) { |
| 305 | surface->map->sw_data->blit = SDL_BlitCopy; |
| 306 | |
| 307 | /* Handle overlapping blits on the same surface */ |
| 308 | if ( surface == surface->map->dst ) { |
| 309 | surface->map->sw_data->blit = SDL_BlitCopyOverlap; |
| 310 | } |
| 311 | } else { |
| 312 | if ( surface->format->BitsPerPixel < 8 ) { |
| 313 | surface->map->sw_data->blit = |
| 314 | SDL_CalculateBlit0(surface, blit_index); |
| 315 | } else { |
| 316 | switch ( surface->format->BytesPerPixel ) { |
| 317 | case 1: |
| 318 | surface->map->sw_data->blit = |
| 319 | SDL_CalculateBlit1(surface, blit_index); |
| 320 | break; |
| 321 | case 2: |
| 322 | case 3: |
| 323 | case 4: |
| 324 | surface->map->sw_data->blit = |
| 325 | SDL_CalculateBlitN(surface, blit_index); |
| 326 | break; |
| 327 | default: |
| 328 | surface->map->sw_data->blit = NULL; |
| 329 | break; |
| 330 | } |
| 331 | } |
| 332 | } |
| 333 | /* Make sure we have a blit function */ |
| 334 | if ( surface->map->sw_data->blit == NULL ) { |
| 335 | SDL_InvalidateMap(surface->map); |
| 336 | SDL_SetError("Blit combination not supported"); |
| 337 | return(-1); |
| 338 | } |
| 339 | |
| 340 | /* Choose software blitting function */ |
| 341 | if(surface->flags & SDL_RLEACCELOK |
| 342 | && (surface->flags & SDL_HWACCEL) != SDL_HWACCEL) { |
| 343 | |
| 344 | if(surface->map->identity |
| 345 | && (blit_index == 1 |
| 346 | || (blit_index == 3 && !surface->format->Amask))) { |
| 347 | if ( SDL_RLESurface(surface) == 0 ) |
| 348 | surface->map->sw_blit = SDL_RLEBlit; |
| 349 | } else if(blit_index == 2 && surface->format->Amask) { |
| 350 | if ( SDL_RLESurface(surface) == 0 ) |
| 351 | surface->map->sw_blit = SDL_RLEAlphaBlit; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | if ( surface->map->sw_blit == NULL ) { |
| 356 | surface->map->sw_blit = SDL_SoftBlit; |
| 357 | } |
| 358 | return(0); |
| 359 | } |
| 360 | |