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#include "SDL_video.h"
25#include "SDL_sysvideo.h"
26#include "SDL_blit.h"
27#include "SDL_RLEaccel_c.h"
28#include "SDL_pixels_c.h"
29
30#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && SDL_ASSEMBLY_ROUTINES
31#define MMX_ASMBLIT
32#if (__GNUC__ > 2)  /* SSE instructions aren't in GCC 2. */
33#define SSE_ASMBLIT
34#endif
35#endif
36
37#if defined(MMX_ASMBLIT)
38#include "SDL_cpuinfo.h"
39#include "mmx.h"
40#endif
41
42/* The general purpose software blit routine */
43static int SDL_SoftBlit(SDL_Surface *src, SDL_Rect *srcrect,
44			SDL_Surface *dst, SDL_Rect *dstrect)
45{
46	int okay;
47	int src_locked;
48	int dst_locked;
49
50	/* Everything is okay at the beginning...  */
51	okay = 1;
52
53	/* Lock the destination if it's in hardware */
54	dst_locked = 0;
55	if ( SDL_MUSTLOCK(dst) ) {
56		if ( SDL_LockSurface(dst) < 0 ) {
57			okay = 0;
58		} else {
59			dst_locked = 1;
60		}
61	}
62	/* Lock the source if it's in hardware */
63	src_locked = 0;
64	if ( SDL_MUSTLOCK(src) ) {
65		if ( SDL_LockSurface(src) < 0 ) {
66			okay = 0;
67		} else {
68			src_locked = 1;
69		}
70	}
71
72	/* Set up source and destination buffer pointers, and BLIT! */
73	if ( okay  && srcrect->w && srcrect->h ) {
74		SDL_BlitInfo info;
75		SDL_loblit RunBlit;
76
77		/* Set up the blit information */
78		info.s_pixels = (Uint8 *)src->pixels +
79				(Uint16)srcrect->y*src->pitch +
80				(Uint16)srcrect->x*src->format->BytesPerPixel;
81		info.s_width = srcrect->w;
82		info.s_height = srcrect->h;
83		info.s_skip=src->pitch-info.s_width*src->format->BytesPerPixel;
84		info.d_pixels = (Uint8 *)dst->pixels +
85				(Uint16)dstrect->y*dst->pitch +
86				(Uint16)dstrect->x*dst->format->BytesPerPixel;
87		info.d_width = dstrect->w;
88		info.d_height = dstrect->h;
89		info.d_skip=dst->pitch-info.d_width*dst->format->BytesPerPixel;
90		info.aux_data = src->map->sw_data->aux_data;
91		info.src = src->format;
92		info.table = src->map->table;
93		info.dst = dst->format;
94		RunBlit = src->map->sw_data->blit;
95
96		/* Run the actual software blit */
97		RunBlit(&info);
98	}
99
100	/* We need to unlock the surfaces if they're locked */
101	if ( dst_locked ) {
102		SDL_UnlockSurface(dst);
103	}
104	if ( src_locked ) {
105		SDL_UnlockSurface(src);
106	}
107	/* Blit is done! */
108	return(okay ? 0 : -1);
109}
110
111#ifdef MMX_ASMBLIT
112static __inline__ void SDL_memcpyMMX(Uint8 *to, const Uint8 *from, int len)
113{
114	int i;
115
116	for(i=0; i<len/8; i++) {
117		__asm__ __volatile__ (
118		"	movq (%0), %%mm0\n"
119		"	movq %%mm0, (%1)\n"
120		: : "r" (from), "r" (to) : "memory");
121		from+=8;
122		to+=8;
123	}
124	if (len&7)
125		SDL_memcpy(to, from, len&7);
126}
127
128#ifdef SSE_ASMBLIT
129static __inline__ void SDL_memcpySSE(Uint8 *to, const Uint8 *from, int len)
130{
131	int i;
132
133	__asm__ __volatile__ (
134	"	prefetchnta (%0)\n"
135	"	prefetchnta 64(%0)\n"
136	"	prefetchnta 128(%0)\n"
137	"	prefetchnta 192(%0)\n"
138	: : "r" (from) );
139
140	for(i=0; i<len/8; i++) {
141		__asm__ __volatile__ (
142		"	prefetchnta 256(%0)\n"
143		"	movq (%0), %%mm0\n"
144		"	movntq %%mm0, (%1)\n"
145		: : "r" (from), "r" (to) : "memory");
146		from+=8;
147		to+=8;
148	}
149	if (len&7)
150		SDL_memcpy(to, from, len&7);
151}
152#endif
153#endif
154
155static void SDL_BlitCopy(SDL_BlitInfo *info)
156{
157	Uint8 *src, *dst;
158	int w, h;
159	int srcskip, dstskip;
160
161	w = info->d_width*info->dst->BytesPerPixel;
162	h = info->d_height;
163	src = info->s_pixels;
164	dst = info->d_pixels;
165	srcskip = w+info->s_skip;
166	dstskip = w+info->d_skip;
167
168#ifdef SSE_ASMBLIT
169	if(SDL_HasSSE())
170	{
171		while ( h-- ) {
172			SDL_memcpySSE(dst, src, w);
173			src += srcskip;
174			dst += dstskip;
175		}
176		__asm__ __volatile__ (
177		"	emms\n"
178		::);
179	}
180	else
181#endif
182#ifdef MMX_ASMBLIT
183	if(SDL_HasMMX())
184	{
185		while ( h-- ) {
186			SDL_memcpyMMX(dst, src, w);
187			src += srcskip;
188			dst += dstskip;
189		}
190		__asm__ __volatile__ (
191		"	emms\n"
192		::);
193	}
194	else
195#endif
196	while ( h-- ) {
197		SDL_memcpy(dst, src, w);
198		src += srcskip;
199		dst += dstskip;
200	}
201}
202
203static void SDL_BlitCopyOverlap(SDL_BlitInfo *info)
204{
205	Uint8 *src, *dst;
206	int w, h;
207	int srcskip, dstskip;
208
209	w = info->d_width*info->dst->BytesPerPixel;
210	h = info->d_height;
211	src = info->s_pixels;
212	dst = info->d_pixels;
213	srcskip = w+info->s_skip;
214	dstskip = w+info->d_skip;
215	if ( dst < src ) {
216		while ( h-- ) {
217			SDL_memcpy(dst, src, w);
218			src += srcskip;
219			dst += dstskip;
220		}
221	} else {
222		src += ((h-1) * srcskip);
223		dst += ((h-1) * dstskip);
224		while ( h-- ) {
225			SDL_revcpy(dst, src, w);
226			src -= srcskip;
227			dst -= dstskip;
228		}
229	}
230}
231
232/* Figure out which of many blit routines to set up on a surface */
233int SDL_CalculateBlit(SDL_Surface *surface)
234{
235	int blit_index;
236
237	/* Clean everything out to start */
238	if ( (surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL ) {
239		SDL_UnRLESurface(surface, 1);
240	}
241	surface->map->sw_blit = NULL;
242
243	/* Figure out if an accelerated hardware blit is possible */
244	surface->flags &= ~SDL_HWACCEL;
245	if ( surface->map->identity ) {
246		int hw_blit_ok;
247
248		if ( (surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE ) {
249			/* We only support accelerated blitting to hardware */
250			if ( surface->map->dst->flags & SDL_HWSURFACE ) {
251				hw_blit_ok = current_video->info.blit_hw;
252			} else {
253				hw_blit_ok = 0;
254			}
255			if (hw_blit_ok && (surface->flags & SDL_SRCCOLORKEY)) {
256				hw_blit_ok = current_video->info.blit_hw_CC;
257			}
258			if ( hw_blit_ok && (surface->flags & SDL_SRCALPHA) ) {
259				hw_blit_ok = current_video->info.blit_hw_A;
260			}
261		} else {
262			/* We only support accelerated blitting to hardware */
263			if ( surface->map->dst->flags & SDL_HWSURFACE ) {
264				hw_blit_ok = current_video->info.blit_sw;
265			} else {
266				hw_blit_ok = 0;
267			}
268			if (hw_blit_ok && (surface->flags & SDL_SRCCOLORKEY)) {
269				hw_blit_ok = current_video->info.blit_sw_CC;
270			}
271			if ( hw_blit_ok && (surface->flags & SDL_SRCALPHA) ) {
272				hw_blit_ok = current_video->info.blit_sw_A;
273			}
274		}
275		if ( hw_blit_ok ) {
276			SDL_VideoDevice *video = current_video;
277			SDL_VideoDevice *this  = current_video;
278			video->CheckHWBlit(this, surface, surface->map->dst);
279		}
280	}
281
282	/* if an alpha pixel format is specified, we can accelerate alpha blits */
283	if (((surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE )&&(current_video->displayformatalphapixel))
284	{
285		if ( (surface->flags & SDL_SRCALPHA) )
286			if ( current_video->info.blit_hw_A ) {
287				SDL_VideoDevice *video = current_video;
288				SDL_VideoDevice *this  = current_video;
289				video->CheckHWBlit(this, surface, surface->map->dst);
290			}
291	}
292
293	/* Get the blit function index, based on surface mode */
294	/* { 0 = nothing, 1 = colorkey, 2 = alpha, 3 = colorkey+alpha } */
295	blit_index = 0;
296	blit_index |= (!!(surface->flags & SDL_SRCCOLORKEY))      << 0;
297	if ( surface->flags & SDL_SRCALPHA
298	     && (surface->format->alpha != SDL_ALPHA_OPAQUE
299		 || surface->format->Amask) ) {
300	        blit_index |= 2;
301	}
302
303	/* Check for special "identity" case -- copy blit */
304	if ( surface->map->identity && blit_index == 0 ) {
305	        surface->map->sw_data->blit = SDL_BlitCopy;
306
307		/* Handle overlapping blits on the same surface */
308		if ( surface == surface->map->dst ) {
309		        surface->map->sw_data->blit = SDL_BlitCopyOverlap;
310		}
311	} else {
312		if ( surface->format->BitsPerPixel < 8 ) {
313			surface->map->sw_data->blit =
314			    SDL_CalculateBlit0(surface, blit_index);
315		} else {
316			switch ( surface->format->BytesPerPixel ) {
317			    case 1:
318				surface->map->sw_data->blit =
319				    SDL_CalculateBlit1(surface, blit_index);
320				break;
321			    case 2:
322			    case 3:
323			    case 4:
324				surface->map->sw_data->blit =
325				    SDL_CalculateBlitN(surface, blit_index);
326				break;
327			    default:
328				surface->map->sw_data->blit = NULL;
329				break;
330			}
331		}
332	}
333	/* Make sure we have a blit function */
334	if ( surface->map->sw_data->blit == NULL ) {
335		SDL_InvalidateMap(surface->map);
336		SDL_SetError("Blit combination not supported");
337		return(-1);
338	}
339
340	/* Choose software blitting function */
341	if(surface->flags & SDL_RLEACCELOK
342	   && (surface->flags & SDL_HWACCEL) != SDL_HWACCEL) {
343
344	        if(surface->map->identity
345		   && (blit_index == 1
346		       || (blit_index == 3 && !surface->format->Amask))) {
347		        if ( SDL_RLESurface(surface) == 0 )
348			        surface->map->sw_blit = SDL_RLEBlit;
349		} else if(blit_index == 2 && surface->format->Amask) {
350		        if ( SDL_RLESurface(surface) == 0 )
351			        surface->map->sw_blit = SDL_RLEAlphaBlit;
352		}
353	}
354
355	if ( surface->map->sw_blit == NULL ) {
356		surface->map->sw_blit = SDL_SoftBlit;
357	}
358	return(0);
359}
360
361