change SDL 1.2 to SDL 2.0
[platform/upstream/SDL.git] / src / video / SDL_stretch.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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\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)         \
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: */