src/video/SDL_stretch.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 15 Sep 2012 10:59:39 -0400
changeset 6430 48d519500f7e
parent 6389 43a190ad60a7
child 6885 700f1b25f77f
permissions -rwxr-xr-x
Removed Windows CE support from SDL 2.0.

It's a long-dead platform, and we don't have any way to build for, test, or
maintain it, so there's no sense in doing acrobatics to support it.

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