1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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(_MSC_VER) && defined(_M_IX86)) || \
37 (defined(__WATCOMC__) && defined(__386__)) || \
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__) || defined(__386__)
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
generate_rowbytes(int src_w,int dst_w,int bpp)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* */
DEFINE_COPY_ROW(copy_row1,Uint8)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
SDL_SoftStretch(SDL_Surface * src,const SDL_Rect * srcrect,SDL_Surface * dst,const SDL_Rect * dstrect)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: */
354