1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 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_blit.h"
26#include "SDL_sysvideo.h"
27#include "SDL_endian.h"
28
29/* Functions to blit from 8-bit surfaces to other surfaces */
30
31static void Blit1to1(SDL_BlitInfo *info)
32{
33#ifndef USE_DUFFS_LOOP
34	int c;
35#endif
36	int width, height;
37	Uint8 *src, *map, *dst;
38	int srcskip, dstskip;
39
40	/* Set up some basic variables */
41	width = info->d_width;
42	height = info->d_height;
43	src = info->s_pixels;
44	srcskip = info->s_skip;
45	dst = info->d_pixels;
46	dstskip = info->d_skip;
47	map = info->table;
48
49	while ( height-- ) {
50#ifdef USE_DUFFS_LOOP
51		DUFFS_LOOP(
52			{
53			  *dst = map[*src];
54			}
55			dst++;
56			src++;
57		, width);
58#else
59		for ( c=width; c; --c ) {
60		        *dst = map[*src];
61			dst++;
62			src++;
63		}
64#endif
65		src += srcskip;
66		dst += dstskip;
67	}
68}
69/* This is now endian dependent */
70#if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
71#define HI	1
72#define LO	0
73#else /* ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) */
74#define HI	0
75#define LO	1
76#endif
77static void Blit1to2(SDL_BlitInfo *info)
78{
79#ifndef USE_DUFFS_LOOP
80	int c;
81#endif
82	int width, height;
83	Uint8 *src, *dst;
84	Uint16 *map;
85	int srcskip, dstskip;
86
87	/* Set up some basic variables */
88	width = info->d_width;
89	height = info->d_height;
90	src = info->s_pixels;
91	srcskip = info->s_skip;
92	dst = info->d_pixels;
93	dstskip = info->d_skip;
94	map = (Uint16 *)info->table;
95
96#ifdef USE_DUFFS_LOOP
97	while ( height-- ) {
98		DUFFS_LOOP(
99		{
100			*(Uint16 *)dst = map[*src++];
101			dst += 2;
102		},
103		width);
104		src += srcskip;
105		dst += dstskip;
106	}
107#else
108	/* Memory align at 4-byte boundary, if necessary */
109	if ( (long)dst & 0x03 ) {
110		/* Don't do anything if width is 0 */
111		if ( width == 0 ) {
112			return;
113		}
114		--width;
115
116		while ( height-- ) {
117			/* Perform copy alignment */
118			*(Uint16 *)dst = map[*src++];
119			dst += 2;
120
121			/* Copy in 4 pixel chunks */
122			for ( c=width/4; c; --c ) {
123				*(Uint32 *)dst =
124					(map[src[HI]]<<16)|(map[src[LO]]);
125				src += 2;
126				dst += 4;
127				*(Uint32 *)dst =
128					(map[src[HI]]<<16)|(map[src[LO]]);
129				src += 2;
130				dst += 4;
131			}
132			/* Get any leftovers */
133			switch (width & 3) {
134				case 3:
135					*(Uint16 *)dst = map[*src++];
136					dst += 2;
137				case 2:
138					*(Uint32 *)dst =
139					  (map[src[HI]]<<16)|(map[src[LO]]);
140					src += 2;
141					dst += 4;
142					break;
143				case 1:
144					*(Uint16 *)dst = map[*src++];
145					dst += 2;
146					break;
147			}
148			src += srcskip;
149			dst += dstskip;
150		}
151	} else {
152		while ( height-- ) {
153			/* Copy in 4 pixel chunks */
154			for ( c=width/4; c; --c ) {
155				*(Uint32 *)dst =
156					(map[src[HI]]<<16)|(map[src[LO]]);
157				src += 2;
158				dst += 4;
159				*(Uint32 *)dst =
160					(map[src[HI]]<<16)|(map[src[LO]]);
161				src += 2;
162				dst += 4;
163			}
164			/* Get any leftovers */
165			switch (width & 3) {
166				case 3:
167					*(Uint16 *)dst = map[*src++];
168					dst += 2;
169				case 2:
170					*(Uint32 *)dst =
171					  (map[src[HI]]<<16)|(map[src[LO]]);
172					src += 2;
173					dst += 4;
174					break;
175				case 1:
176					*(Uint16 *)dst = map[*src++];
177					dst += 2;
178					break;
179			}
180			src += srcskip;
181			dst += dstskip;
182		}
183	}
184#endif /* USE_DUFFS_LOOP */
185}
186static void Blit1to3(SDL_BlitInfo *info)
187{
188#ifndef USE_DUFFS_LOOP
189	int c;
190#endif
191	int o;
192	int width, height;
193	Uint8 *src, *map, *dst;
194	int srcskip, dstskip;
195
196	/* Set up some basic variables */
197	width = info->d_width;
198	height = info->d_height;
199	src = info->s_pixels;
200	srcskip = info->s_skip;
201	dst = info->d_pixels;
202	dstskip = info->d_skip;
203	map = info->table;
204
205	while ( height-- ) {
206#ifdef USE_DUFFS_LOOP
207		DUFFS_LOOP(
208			{
209				o = *src * 4;
210				dst[0] = map[o++];
211				dst[1] = map[o++];
212				dst[2] = map[o++];
213			}
214			src++;
215			dst += 3;
216		, width);
217#else
218		for ( c=width; c; --c ) {
219			o = *src * 4;
220			dst[0] = map[o++];
221			dst[1] = map[o++];
222			dst[2] = map[o++];
223			src++;
224			dst += 3;
225		}
226#endif /* USE_DUFFS_LOOP */
227		src += srcskip;
228		dst += dstskip;
229	}
230}
231static void Blit1to4(SDL_BlitInfo *info)
232{
233#ifndef USE_DUFFS_LOOP
234	int c;
235#endif
236	int width, height;
237	Uint8 *src;
238	Uint32 *map, *dst;
239	int srcskip, dstskip;
240
241	/* Set up some basic variables */
242	width = info->d_width;
243	height = info->d_height;
244	src = info->s_pixels;
245	srcskip = info->s_skip;
246	dst = (Uint32 *)info->d_pixels;
247	dstskip = info->d_skip/4;
248	map = (Uint32 *)info->table;
249
250	while ( height-- ) {
251#ifdef USE_DUFFS_LOOP
252		DUFFS_LOOP(
253			*dst++ = map[*src++];
254		, width);
255#else
256		for ( c=width/4; c; --c ) {
257			*dst++ = map[*src++];
258			*dst++ = map[*src++];
259			*dst++ = map[*src++];
260			*dst++ = map[*src++];
261		}
262		switch ( width & 3 ) {
263			case 3:
264				*dst++ = map[*src++];
265			case 2:
266				*dst++ = map[*src++];
267			case 1:
268				*dst++ = map[*src++];
269		}
270#endif /* USE_DUFFS_LOOP */
271		src += srcskip;
272		dst += dstskip;
273	}
274}
275
276static void Blit1to1Key(SDL_BlitInfo *info)
277{
278	int width = info->d_width;
279	int height = info->d_height;
280	Uint8 *src = info->s_pixels;
281	int srcskip = info->s_skip;
282	Uint8 *dst = info->d_pixels;
283	int dstskip = info->d_skip;
284	Uint8 *palmap = info->table;
285	Uint32 ckey = info->src->colorkey;
286
287	if ( palmap ) {
288		while ( height-- ) {
289			DUFFS_LOOP(
290			{
291				if ( *src != ckey ) {
292				  *dst = palmap[*src];
293				}
294				dst++;
295				src++;
296			},
297			width);
298			src += srcskip;
299			dst += dstskip;
300		}
301	} else {
302		while ( height-- ) {
303			DUFFS_LOOP(
304			{
305				if ( *src != ckey ) {
306				  *dst = *src;
307				}
308				dst++;
309				src++;
310			},
311			width);
312			src += srcskip;
313			dst += dstskip;
314		}
315	}
316}
317
318static void Blit1to2Key(SDL_BlitInfo *info)
319{
320	int width = info->d_width;
321	int height = info->d_height;
322	Uint8 *src = info->s_pixels;
323	int srcskip = info->s_skip;
324	Uint16 *dstp = (Uint16 *)info->d_pixels;
325	int dstskip = info->d_skip;
326	Uint16 *palmap = (Uint16 *)info->table;
327	Uint32 ckey = info->src->colorkey;
328
329	/* Set up some basic variables */
330	dstskip /= 2;
331
332	while ( height-- ) {
333		DUFFS_LOOP(
334		{
335			if ( *src != ckey ) {
336				*dstp=palmap[*src];
337			}
338			src++;
339			dstp++;
340		},
341		width);
342		src += srcskip;
343		dstp += dstskip;
344	}
345}
346
347static void Blit1to3Key(SDL_BlitInfo *info)
348{
349	int width = info->d_width;
350	int height = info->d_height;
351	Uint8 *src = info->s_pixels;
352	int srcskip = info->s_skip;
353	Uint8 *dst = info->d_pixels;
354	int dstskip = info->d_skip;
355	Uint8 *palmap = info->table;
356	Uint32 ckey = info->src->colorkey;
357	int o;
358
359	while ( height-- ) {
360		DUFFS_LOOP(
361		{
362			if ( *src != ckey ) {
363				o = *src * 4;
364				dst[0] = palmap[o++];
365				dst[1] = palmap[o++];
366				dst[2] = palmap[o++];
367			}
368			src++;
369			dst += 3;
370		},
371		width);
372		src += srcskip;
373		dst += dstskip;
374	}
375}
376
377static void Blit1to4Key(SDL_BlitInfo *info)
378{
379	int width = info->d_width;
380	int height = info->d_height;
381	Uint8 *src = info->s_pixels;
382	int srcskip = info->s_skip;
383	Uint32 *dstp = (Uint32 *)info->d_pixels;
384	int dstskip = info->d_skip;
385	Uint32 *palmap = (Uint32 *)info->table;
386	Uint32 ckey = info->src->colorkey;
387
388	/* Set up some basic variables */
389	dstskip /= 4;
390
391	while ( height-- ) {
392		DUFFS_LOOP(
393		{
394			if ( *src != ckey ) {
395				*dstp = palmap[*src];
396			}
397			src++;
398			dstp++;
399		},
400		width);
401		src += srcskip;
402		dstp += dstskip;
403	}
404}
405
406static void Blit1toNAlpha(SDL_BlitInfo *info)
407{
408	int width = info->d_width;
409	int height = info->d_height;
410	Uint8 *src = info->s_pixels;
411	int srcskip = info->s_skip;
412	Uint8 *dst = info->d_pixels;
413	int dstskip = info->d_skip;
414	SDL_PixelFormat *dstfmt = info->dst;
415	const SDL_Color *srcpal	= info->src->palette->colors;
416	int dstbpp;
417	const int A = info->src->alpha;
418
419	/* Set up some basic variables */
420	dstbpp = dstfmt->BytesPerPixel;
421
422	while ( height-- ) {
423	        int sR, sG, sB;
424		int dR, dG, dB;
425	    	DUFFS_LOOP4(
426			{
427			        Uint32 pixel;
428				sR = srcpal[*src].r;
429				sG = srcpal[*src].g;
430				sB = srcpal[*src].b;
431				DISEMBLE_RGB(dst, dstbpp, dstfmt,
432					     pixel, dR, dG, dB);
433				ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB);
434			  	ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
435				src++;
436				dst += dstbpp;
437			},
438			width);
439		src += srcskip;
440		dst += dstskip;
441	}
442}
443
444static void Blit1toNAlphaKey(SDL_BlitInfo *info)
445{
446	int width = info->d_width;
447	int height = info->d_height;
448	Uint8 *src = info->s_pixels;
449	int srcskip = info->s_skip;
450	Uint8 *dst = info->d_pixels;
451	int dstskip = info->d_skip;
452	SDL_PixelFormat *srcfmt = info->src;
453	SDL_PixelFormat *dstfmt = info->dst;
454	const SDL_Color *srcpal	= info->src->palette->colors;
455	Uint32 ckey = srcfmt->colorkey;
456	int dstbpp;
457	const int A = srcfmt->alpha;
458
459	/* Set up some basic variables */
460	dstbpp = dstfmt->BytesPerPixel;
461
462	while ( height-- ) {
463	        int sR, sG, sB;
464		int dR, dG, dB;
465		DUFFS_LOOP(
466		{
467			if ( *src != ckey ) {
468			        Uint32 pixel;
469				sR = srcpal[*src].r;
470				sG = srcpal[*src].g;
471				sB = srcpal[*src].b;
472				DISEMBLE_RGB(dst, dstbpp, dstfmt,
473							pixel, dR, dG, dB);
474				ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB);
475			  	ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
476			}
477			src++;
478			dst += dstbpp;
479		},
480		width);
481		src += srcskip;
482		dst += dstskip;
483	}
484}
485
486static SDL_loblit one_blit[] = {
487	NULL, Blit1to1, Blit1to2, Blit1to3, Blit1to4
488};
489
490static SDL_loblit one_blitkey[] = {
491        NULL, Blit1to1Key, Blit1to2Key, Blit1to3Key, Blit1to4Key
492};
493
494SDL_loblit SDL_CalculateBlit1(SDL_Surface *surface, int blit_index)
495{
496	int which;
497	SDL_PixelFormat *dstfmt;
498
499	dstfmt = surface->map->dst->format;
500	if ( dstfmt->BitsPerPixel < 8 ) {
501		which = 0;
502	} else {
503		which = dstfmt->BytesPerPixel;
504	}
505	switch(blit_index) {
506	case 0:			/* copy */
507	    return one_blit[which];
508
509	case 1:			/* colorkey */
510	    return one_blitkey[which];
511
512	case 2:			/* alpha */
513	    /* Supporting 8bpp->8bpp alpha is doable but requires lots of
514	       tables which consume space and takes time to precompute,
515	       so is better left to the user */
516	    return which >= 2 ? Blit1toNAlpha : NULL;
517
518	case 3:			/* alpha + colorkey */
519	    return which >= 2 ? Blit1toNAlphaKey : NULL;
520
521	}
522	return NULL;
523}
524