src/video/SDL_stretch.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sun, 26 Mar 2017 21:00:19 +0200
changeset 10945 1300a3135d61
parent 10737 3406a0f8b041
child 11478 3a5aa02aee80
permissions -rw-r--r--
Removed newlines from error messages.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 
    67 static unsigned char copy_row[4096] PAGE_ALIGNED;
    68 
    69 static int
    70 generate_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", 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)         \
   152 static 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* */
   170 DEFINE_COPY_ROW(copy_row1, Uint8)
   171 DEFINE_COPY_ROW(copy_row2, Uint16)
   172 DEFINE_COPY_ROW(copy_row4, Uint32)
   173 /* *INDENT-ON* */
   174 
   175 /* The ASM code doesn't handle 24-bpp stretch blits */
   176 static void
   177 copy_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 */
   202 int
   203 SDL_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: */