1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 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 /* This a stretch blit implementation based on ideas given to me by
25 Tomasz Cejner - thanks! :)
26
27 April 27, 2000 - Sam Lantinga
28 */
29
30 #include "SDL_video.h"
31 #include "SDL_blit.h"
32
33 /* This isn't ready for general consumption yet - it should be folded
34 into the general blitting mechanism.
35 */
36
37 #if ((defined(_MFC_VER) && defined(_M_IX86)/* && !defined(_WIN32_WCE) still needed? */) || \
38 defined(__WATCOMC__) || \
39 (defined(__GNUC__) && defined(__i386__))) && SDL_ASSEMBLY_ROUTINES
40 #define USE_ASM_STRETCH
41 #endif
42
43 #ifdef USE_ASM_STRETCH
44
45 #if defined(_M_IX86) || defined(i386)
46 #define PREFIX16 0x66
47 #define STORE_BYTE 0xAA
48 #define STORE_WORD 0xAB
49 #define LOAD_BYTE 0xAC
50 #define LOAD_WORD 0xAD
51 #define RETURN 0xC3
52 #else
53 #error Need assembly opcodes for this architecture
54 #endif
55
56 static unsigned char copy_row[4096];
57
generate_rowbytes(int src_w,int dst_w,int bpp)58 static int generate_rowbytes(int src_w, int dst_w, int bpp)
59 {
60 static struct {
61 int bpp;
62 int src_w;
63 int dst_w;
64 } last;
65
66 int i;
67 int pos, inc;
68 unsigned char *eip;
69 unsigned char load, store;
70
71 /* See if we need to regenerate the copy buffer */
72 if ( (src_w == last.src_w) &&
73 (dst_w == last.dst_w) && (bpp == last.bpp) ) {
74 return(0);
75 }
76 last.bpp = bpp;
77 last.src_w = src_w;
78 last.dst_w = dst_w;
79
80 switch (bpp) {
81 case 1:
82 load = LOAD_BYTE;
83 store = STORE_BYTE;
84 break;
85 case 2:
86 case 4:
87 load = LOAD_WORD;
88 store = STORE_WORD;
89 break;
90 default:
91 SDL_SetError("ASM stretch of %d bytes isn't supported\n", bpp);
92 return(-1);
93 }
94 pos = 0x10000;
95 inc = (src_w << 16) / dst_w;
96 eip = copy_row;
97 for ( i=0; i<dst_w; ++i ) {
98 while ( pos >= 0x10000L ) {
99 if ( bpp == 2 ) {
100 *eip++ = PREFIX16;
101 }
102 *eip++ = load;
103 pos -= 0x10000L;
104 }
105 if ( bpp == 2 ) {
106 *eip++ = PREFIX16;
107 }
108 *eip++ = store;
109 pos += inc;
110 }
111 *eip++ = RETURN;
112
113 /* Verify that we didn't overflow (too late) */
114 if ( eip > (copy_row+sizeof(copy_row)) ) {
115 SDL_SetError("Copy buffer overflow");
116 return(-1);
117 }
118 return(0);
119 }
120
121 #else
122
123 #define DEFINE_COPY_ROW(name, type) \
124 void name(type *src, int src_w, type *dst, int dst_w) \
125 { \
126 int i; \
127 int pos, inc; \
128 type pixel = 0; \
129 \
130 pos = 0x10000; \
131 inc = (src_w << 16) / dst_w; \
132 for ( i=dst_w; i>0; --i ) { \
133 while ( pos >= 0x10000L ) { \
134 pixel = *src++; \
135 pos -= 0x10000L; \
136 } \
137 *dst++ = pixel; \
138 pos += inc; \
139 } \
140 }
DEFINE_COPY_ROW(copy_row1,Uint8)141 DEFINE_COPY_ROW(copy_row1, Uint8)
142 DEFINE_COPY_ROW(copy_row2, Uint16)
143 DEFINE_COPY_ROW(copy_row4, Uint32)
144
145 #endif /* USE_ASM_STRETCH */
146
147 /* The ASM code doesn't handle 24-bpp stretch blits */
148 void copy_row3(Uint8 *src, int src_w, Uint8 *dst, int dst_w)
149 {
150 int i;
151 int pos, inc;
152 Uint8 pixel[3] = { 0, 0, 0 };
153
154 pos = 0x10000;
155 inc = (src_w << 16) / dst_w;
156 for ( i=dst_w; i>0; --i ) {
157 while ( pos >= 0x10000L ) {
158 pixel[0] = *src++;
159 pixel[1] = *src++;
160 pixel[2] = *src++;
161 pos -= 0x10000L;
162 }
163 *dst++ = pixel[0];
164 *dst++ = pixel[1];
165 *dst++ = pixel[2];
166 pos += inc;
167 }
168 }
169
170 /* Perform a stretch blit between two surfaces of the same format.
171 NOTE: This function is not safe to call from multiple threads!
172 */
SDL_SoftStretch(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)173 int SDL_SoftStretch(SDL_Surface *src, SDL_Rect *srcrect,
174 SDL_Surface *dst, SDL_Rect *dstrect)
175 {
176 int src_locked;
177 int dst_locked;
178 int pos, inc;
179 int dst_width;
180 int dst_maxrow;
181 int src_row, dst_row;
182 Uint8 *srcp = NULL;
183 Uint8 *dstp;
184 SDL_Rect full_src;
185 SDL_Rect full_dst;
186 #if defined(USE_ASM_STRETCH) && defined(__GNUC__)
187 int u1, u2;
188 #endif
189 const int bpp = dst->format->BytesPerPixel;
190
191 if ( src->format->BitsPerPixel != dst->format->BitsPerPixel ) {
192 SDL_SetError("Only works with same format surfaces");
193 return(-1);
194 }
195
196 /* Verify the blit rectangles */
197 if ( srcrect ) {
198 if ( (srcrect->x < 0) || (srcrect->y < 0) ||
199 ((srcrect->x+srcrect->w) > src->w) ||
200 ((srcrect->y+srcrect->h) > src->h) ) {
201 SDL_SetError("Invalid source blit rectangle");
202 return(-1);
203 }
204 } else {
205 full_src.x = 0;
206 full_src.y = 0;
207 full_src.w = src->w;
208 full_src.h = src->h;
209 srcrect = &full_src;
210 }
211 if ( dstrect ) {
212 if ( (dstrect->x < 0) || (dstrect->y < 0) ||
213 ((dstrect->x+dstrect->w) > dst->w) ||
214 ((dstrect->y+dstrect->h) > dst->h) ) {
215 SDL_SetError("Invalid destination blit rectangle");
216 return(-1);
217 }
218 } else {
219 full_dst.x = 0;
220 full_dst.y = 0;
221 full_dst.w = dst->w;
222 full_dst.h = dst->h;
223 dstrect = &full_dst;
224 }
225
226 /* Lock the destination if it's in hardware */
227 dst_locked = 0;
228 if ( SDL_MUSTLOCK(dst) ) {
229 if ( SDL_LockSurface(dst) < 0 ) {
230 SDL_SetError("Unable to lock destination surface");
231 return(-1);
232 }
233 dst_locked = 1;
234 }
235 /* Lock the source if it's in hardware */
236 src_locked = 0;
237 if ( SDL_MUSTLOCK(src) ) {
238 if ( SDL_LockSurface(src) < 0 ) {
239 if ( dst_locked ) {
240 SDL_UnlockSurface(dst);
241 }
242 SDL_SetError("Unable to lock source surface");
243 return(-1);
244 }
245 src_locked = 1;
246 }
247
248 /* Set up the data... */
249 pos = 0x10000;
250 inc = (srcrect->h << 16) / dstrect->h;
251 src_row = srcrect->y;
252 dst_row = dstrect->y;
253 dst_width = dstrect->w*bpp;
254
255 #ifdef USE_ASM_STRETCH
256 /* Write the opcodes for this stretch */
257 if ( (bpp != 3) &&
258 (generate_rowbytes(srcrect->w, dstrect->w, bpp) < 0) ) {
259 return(-1);
260 }
261 #endif
262
263 /* Perform the stretch blit */
264 for ( dst_maxrow = dst_row+dstrect->h; dst_row<dst_maxrow; ++dst_row ) {
265 dstp = (Uint8 *)dst->pixels + (dst_row*dst->pitch)
266 + (dstrect->x*bpp);
267 while ( pos >= 0x10000L ) {
268 srcp = (Uint8 *)src->pixels + (src_row*src->pitch)
269 + (srcrect->x*bpp);
270 ++src_row;
271 pos -= 0x10000L;
272 }
273 #ifdef USE_ASM_STRETCH
274 switch (bpp) {
275 case 3:
276 copy_row3(srcp, srcrect->w, dstp, dstrect->w);
277 break;
278 default:
279 #ifdef __GNUC__
280 __asm__ __volatile__ (
281 "call *%4"
282 : "=&D" (u1), "=&S" (u2)
283 : "0" (dstp), "1" (srcp), "r" (copy_row)
284 : "memory" );
285 #elif defined(_MSC_VER) || defined(__WATCOMC__)
286 { void *code = copy_row;
287 __asm {
288 push edi
289 push esi
290
291 mov edi, dstp
292 mov esi, srcp
293 call dword ptr code
294
295 pop esi
296 pop edi
297 }
298 }
299 #else
300 #error Need inline assembly for this compiler
301 #endif
302 break;
303 }
304 #else
305 switch (bpp) {
306 case 1:
307 copy_row1(srcp, srcrect->w, dstp, dstrect->w);
308 break;
309 case 2:
310 copy_row2((Uint16 *)srcp, srcrect->w,
311 (Uint16 *)dstp, dstrect->w);
312 break;
313 case 3:
314 copy_row3(srcp, srcrect->w, dstp, dstrect->w);
315 break;
316 case 4:
317 copy_row4((Uint32 *)srcp, srcrect->w,
318 (Uint32 *)dstp, dstrect->w);
319 break;
320 }
321 #endif
322 pos += inc;
323 }
324
325 /* We need to unlock the surfaces if they're locked */
326 if ( dst_locked ) {
327 SDL_UnlockSurface(dst);
328 }
329 if ( src_locked ) {
330 SDL_UnlockSurface(src);
331 }
332 return(0);
333 }
334
335