1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2 
3 /*
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include "port.h"
20 #include "sdl_common.h"
21 #include "primitives.h"
22 
23 
24 // Pixel drawing routines
25 
26 static Uint32
getpixel_8(SDL_Surface * surface,int x,int y)27 getpixel_8(SDL_Surface *surface, int x, int y)
28 {
29     /* Here p is the address to the pixel we want to retrieve */
30     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x;
31 	return *p;
32 }
33 
34 static void
putpixel_8(SDL_Surface * surface,int x,int y,Uint32 pixel)35 putpixel_8(SDL_Surface *surface, int x, int y, Uint32 pixel)
36 {
37     /* Here p is the address to the pixel we want to set */
38     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 1;
39 	*p = pixel;
40 }
41 
42 static Uint32
getpixel_16(SDL_Surface * surface,int x,int y)43 getpixel_16(SDL_Surface *surface, int x, int y)
44 {
45     /* Here p is the address to the pixel we want to retrieve */
46     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2;
47 	return *(Uint16 *)p;
48 }
49 
50 static void
putpixel_16(SDL_Surface * surface,int x,int y,Uint32 pixel)51 putpixel_16(SDL_Surface *surface, int x, int y, Uint32 pixel)
52 {
53     /* Here p is the address to the pixel we want to set */
54     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2;
55 	*(Uint16 *)p = pixel;
56 }
57 
58 static Uint32
getpixel_24_be(SDL_Surface * surface,int x,int y)59 getpixel_24_be(SDL_Surface *surface, int x, int y)
60 {
61     /* Here p is the address to the pixel we want to retrieve */
62 	Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
63 	return p[0] << 16 | p[1] << 8 | p[2];
64 }
65 
66 static void
putpixel_24_be(SDL_Surface * surface,int x,int y,Uint32 pixel)67 putpixel_24_be(SDL_Surface *surface, int x, int y, Uint32 pixel)
68 {
69     /* Here p is the address to the pixel we want to set */
70     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
71 	p[0] = (pixel >> 16) & 0xff;
72 	p[1] = (pixel >> 8) & 0xff;
73 	p[2] = pixel & 0xff;
74 }
75 
76 static Uint32
getpixel_24_le(SDL_Surface * surface,int x,int y)77 getpixel_24_le(SDL_Surface *surface, int x, int y)
78 {
79     /* Here p is the address to the pixel we want to retrieve */
80     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
81 	return p[0] | p[1] << 8 | p[2] << 16;
82 }
83 
84 static void
putpixel_24_le(SDL_Surface * surface,int x,int y,Uint32 pixel)85 putpixel_24_le(SDL_Surface *surface, int x, int y, Uint32 pixel)
86 {
87     /* Here p is the address to the pixel we want to set */
88     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
89 	p[0] = pixel & 0xff;
90 	p[1] = (pixel >> 8) & 0xff;
91 	p[2] = (pixel >> 16) & 0xff;
92 }
93 
94 static Uint32
getpixel_32(SDL_Surface * surface,int x,int y)95 getpixel_32(SDL_Surface *surface, int x, int y)
96 {
97     /* Here p is the address to the pixel we want to retrieve */
98     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
99 	return *(Uint32 *)p;
100 }
101 
102 static void
putpixel_32(SDL_Surface * surface,int x,int y,Uint32 pixel)103 putpixel_32(SDL_Surface *surface, int x, int y, Uint32 pixel)
104 {
105     /* Here p is the address to the pixel we want to set */
106     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
107 	*(Uint32 *)p = pixel;
108 }
109 
110 GetPixelFn
getpixel_for(SDL_Surface * surface)111 getpixel_for(SDL_Surface *surface)
112 {
113 	int bpp = surface->format->BytesPerPixel;
114 	switch (bpp) {
115 	case 1:
116 		return &getpixel_8;
117 	case 2:
118 		return &getpixel_16;
119 	case 3:
120 		if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
121 			return &getpixel_24_be;
122 		} else {
123 			return &getpixel_24_le;
124 		}
125 	case 4:
126 		return &getpixel_32;
127 	}
128 	return NULL;
129 }
130 
131 PutPixelFn
putpixel_for(SDL_Surface * surface)132 putpixel_for(SDL_Surface *surface)
133 {
134 	int bpp = surface->format->BytesPerPixel;
135 	switch (bpp) {
136 	case 1:
137 		return &putpixel_8;
138 	case 2:
139 		return &putpixel_16;
140 	case 3:
141 		if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
142 			return &putpixel_24_be;
143 		} else {
144 			return &putpixel_24_le;
145 		}
146 	case 4:
147 		return &putpixel_32;
148 	}
149 	return NULL;
150 }
151 
152 static void
renderpixel_replace(SDL_Surface * surface,int x,int y,Uint32 pixel,int factor)153 renderpixel_replace(SDL_Surface *surface, int x, int y, Uint32 pixel,
154 		int factor)
155 {
156 	(void) factor; // ignored
157 	putpixel_32(surface, x, y, pixel);
158 }
159 
160 static inline Uint8
clip_channel(int c)161 clip_channel(int c)
162 {
163 	if (c < 0)
164 		c = 0;
165 	else if (c > 255)
166 		c = 255;
167 	return c;
168 }
169 
170 static inline Uint8
modulated_sum(Uint8 dc,Uint8 sc,int factor)171 modulated_sum(Uint8 dc, Uint8 sc, int factor)
172 {
173 	// We use >> 8 instead of / 255 because it is faster, but it does
174 	// not work 100% correctly. It should be safe because this should
175 	// not be called for factor==255
176 	int b = dc + ((sc * factor) >> 8);
177 	return clip_channel(b);
178 }
179 
180 static inline Uint8
alpha_blend(Uint8 dc,Uint8 sc,int alpha)181 alpha_blend(Uint8 dc, Uint8 sc, int alpha)
182 {
183 	// We use >> 8 instead of / 255 because it is faster, but it does
184 	// not work 100% correctly. It should be safe because this should
185 	// not be called for alpha==255
186 	// No need to clip since we should never get values outside of 0..255
187 	// range, unless alpha is over 255, which is not supported.
188 	return (((sc - dc) * alpha) >> 8) + dc;
189 }
190 
191 // Assumes 8 bits/channel, a safe assumption for 32bpp surfaces
192 #define UNPACK_PIXEL_32(p, fmt, r, g, b) \
193 	do { \
194 		(r) = ((p) >> (fmt)->Rshift) & 0xff; \
195 		(g) = ((p) >> (fmt)->Gshift) & 0xff; \
196 		(b) = ((p) >> (fmt)->Bshift) & 0xff; \
197 	} while (0)
198 
199 // Assumes the channels already clipped to 8 bits
200 static inline Uint32
PACK_PIXEL_32(const SDL_PixelFormat * fmt,Uint8 r,Uint8 g,Uint8 b)201 PACK_PIXEL_32(const SDL_PixelFormat *fmt,
202 		Uint8 r, Uint8 g, Uint8 b)
203 {
204 	return ((Uint32)r << fmt->Rshift) | ((Uint32)g << fmt->Gshift)
205 			| ((Uint32)b << fmt->Bshift);
206 }
207 
208 static void
renderpixel_additive(SDL_Surface * surface,int x,int y,Uint32 pixel,int factor)209 renderpixel_additive(SDL_Surface *surface, int x, int y, Uint32 pixel,
210 		int factor)
211 {
212 	const SDL_PixelFormat *fmt = surface->format;
213 	Uint32 *p;
214 	Uint32 sp;
215 	Uint8 sr, sg, sb;
216 	int r, g, b;
217 
218 	p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4);
219 	sp = *p;
220 	UNPACK_PIXEL_32(sp, fmt, sr, sg, sb);
221 	UNPACK_PIXEL_32(pixel, fmt, r, g, b);
222 
223 	// TODO: We may need a special case for factor == -ADDITIVE_FACTOR_1 too,
224 	// but it is not important enough right now to care ;)
225 	if (factor == ADDITIVE_FACTOR_1)
226 	{	// no need to modulate the 'pixel', and modulation does not
227 		// work correctly with factor==255 anyway
228 		sr = clip_channel(sr + r);
229 		sg = clip_channel(sg + g);
230 		sb = clip_channel(sb + b);
231 	}
232 	else
233 	{
234 		sr = modulated_sum(sr, r, factor);
235 		sg = modulated_sum(sg, g, factor);
236 		sb = modulated_sum(sb, b, factor);
237 	}
238 
239 	*p = PACK_PIXEL_32(fmt, sr, sg, sb);
240 }
241 
242 static void
renderpixel_alpha(SDL_Surface * surface,int x,int y,Uint32 pixel,int factor)243 renderpixel_alpha(SDL_Surface *surface, int x, int y, Uint32 pixel,
244 		int factor)
245 {
246 	const SDL_PixelFormat *fmt = surface->format;
247 	Uint32 *p;
248 	Uint32 sp;
249 	Uint8 sr, sg, sb;
250 	int r, g, b;
251 
252 	if (factor == FULLY_OPAQUE_ALPHA)
253 	{	// alpha == 255 is equivalent to 'replace' and blending does not
254 		// work correctly anyway because we use >> 8 instead of / 255
255 		putpixel_32(surface, x, y, pixel);
256 		return;
257 	}
258 
259 	p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4);
260 	sp = *p;
261 	UNPACK_PIXEL_32(sp, fmt, sr, sg, sb);
262 	UNPACK_PIXEL_32(pixel, fmt, r, g, b);
263 	sr = alpha_blend(sr, r, factor);
264 	sg = alpha_blend(sg, g, factor);
265 	sb = alpha_blend(sb, b, factor);
266 	*p = PACK_PIXEL_32(fmt, sr, sg, sb);
267 }
268 
269 RenderPixelFn
renderpixel_for(SDL_Surface * surface,RenderKind kind)270 renderpixel_for(SDL_Surface *surface, RenderKind kind)
271 {
272 	const SDL_PixelFormat *fmt = surface->format;
273 
274 	// The only supported rendering is to 32bpp surfaces
275 	if (fmt->BytesPerPixel != 4)
276 		return NULL;
277 
278 	// Rendering other than REPLACE is not supported on RGBA surfaces
279 	if (fmt->Amask != 0 && kind != renderReplace)
280 		return NULL;
281 
282 	switch (kind)
283 	{
284 	case renderReplace:
285 		return &renderpixel_replace;
286 	case renderAdditive:
287 		return &renderpixel_additive;
288 	case renderAlpha:
289 		return &renderpixel_alpha;
290 	}
291 	// should not ever get here
292 	return NULL;
293 }
294 
295 /* Line drawing routine
296  * Adapted from Paul Heckbert's implementation of Bresenham's algorithm,
297  * 3 Sep 85; taken from Graphics Gems I */
298 
299 void
line_prim(int x1,int y1,int x2,int y2,Uint32 color,RenderPixelFn plot,int factor,SDL_Surface * dst)300 line_prim(int x1, int y1, int x2, int y2, Uint32 color, RenderPixelFn plot,
301 		int factor, SDL_Surface *dst)
302 {
303 	int d, x, y, ax, ay, sx, sy, dx, dy;
304 	SDL_Rect clip_r;
305 
306 	SDL_GetClipRect (dst, &clip_r);
307 	if (!clip_line (&x1, &y1, &x2, &y2, &clip_r))
308 		return; // line is completely outside clipping rectangle
309 
310 	dx = x2-x1;
311 	ax = ((dx < 0) ? -dx : dx) << 1;
312 	sx = (dx < 0) ? -1 : 1;
313 	dy = y2-y1;
314 	ay = ((dy < 0) ? -dy : dy) << 1;
315 	sy = (dy < 0) ? -1 : 1;
316 
317 	x = x1;
318 	y = y1;
319 	if (ax > ay) {
320 		d = ay - (ax >> 1);
321 		for (;;) {
322 			(*plot)(dst, x, y, color, factor);
323 			if (x == x2)
324 				return;
325 			if (d >= 0) {
326 				y += sy;
327 				d -= ax;
328 			}
329 			x += sx;
330 			d += ay;
331 		}
332 	} else {
333 		d = ax - (ay >> 1);
334 		for (;;) {
335 			(*plot)(dst, x, y, color, factor);
336 			if (y == y2)
337 				return;
338 			if (d >= 0) {
339 				x += sx;
340 				d -= ay;
341 			}
342 			y += sy;
343 			d += ax;
344 		}
345 	}
346 }
347 
348 
349 // Clips line against rectangle using Cohen-Sutherland algorithm
350 
351 enum {C_TOP = 0x1, C_BOTTOM = 0x2, C_RIGHT = 0x4, C_LEFT = 0x8};
352 
353 static int
compute_code(float x,float y,float xmin,float ymin,float xmax,float ymax)354 compute_code (float x, float y, float xmin, float ymin, float xmax, float ymax)
355 {
356 	int c = 0;
357 	if (y > ymax)
358 		c |= C_TOP;
359 	else if (y < ymin)
360 		c |= C_BOTTOM;
361 	if (x > xmax)
362 		c |= C_RIGHT;
363 	else if (x < xmin)
364 		c |= C_LEFT;
365 	return c;
366 }
367 
368 int
clip_line(int * lx1,int * ly1,int * lx2,int * ly2,const SDL_Rect * r)369 clip_line (int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *r)
370 {
371 	int C0, C1, C;
372 	float x, y, x0, y0, x1, y1, xmin, ymin, xmax, ymax;
373 
374 	x0 = (float)*lx1;
375 	y0 = (float)*ly1;
376 	x1 = (float)*lx2;
377 	y1 = (float)*ly2;
378 
379 	xmin = (float)r->x;
380 	ymin = (float)r->y;
381 	xmax = (float)r->x + r->w - 1;
382 	ymax = (float)r->y + r->h - 1;
383 
384 	C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax);
385 	C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax);
386 
387 	for (;;) {
388 		/* trivial accept: both ends in rectangle */
389 		if ((C0 | C1) == 0)
390 		{
391 			*lx1 = (int)x0;
392 			*ly1 = (int)y0;
393 			*lx2 = (int)x1;
394 			*ly2 = (int)y1;
395 			return 1;
396 		}
397 
398 		/* trivial reject: both ends on the external side of the rectangle */
399 		if ((C0 & C1) != 0)
400 			return 0;
401 
402 		/* normal case: clip end outside rectangle */
403 		C = C0 ? C0 : C1;
404 		if (C & C_TOP)
405 		{
406 			x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
407 			y = ymax;
408 		}
409 		else if (C & C_BOTTOM)
410 		{
411 			x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
412 			y = ymin;
413 		}
414 		else if (C & C_RIGHT)
415 		{
416 			x = xmax;
417 			y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
418 		}
419 		else
420 		{
421 			x = xmin;
422 			y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
423 		}
424 
425 		/* set new end point and iterate */
426 		if (C == C0)
427 		{
428 			x0 = x; y0 = y;
429 			C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax);
430 		}
431 		else
432 		{
433 			x1 = x; y1 = y;
434 			C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax);
435 		}
436 	}
437 }
438 
439 void
fillrect_prim(SDL_Rect r,Uint32 color,RenderPixelFn plot,int factor,SDL_Surface * dst)440 fillrect_prim(SDL_Rect r, Uint32 color, RenderPixelFn plot, int factor,
441 		SDL_Surface *dst)
442 {
443 	int x, y;
444 	int x1, y1;
445 	SDL_Rect clip_r;
446 
447 	SDL_GetClipRect (dst, &clip_r);
448 	if (!clip_rect (&r, &clip_r))
449 		return; // rect is completely outside clipping rectangle
450 
451 	// TODO: calculate destination pointer directly instead of
452 	//   using the plot(x,y) version
453 	x1 = r.x + r.w;
454 	y1 = r.y + r.h;
455 	for (y = r.y; y < y1; ++y)
456 	{
457 		for (x = r.x; x < x1; ++x)
458 			plot(dst, x, y, color, factor);
459 	}
460 }
461 
462 // clip the rectangle against the clip rectangle
463 int
clip_rect(SDL_Rect * r,const SDL_Rect * clip_r)464 clip_rect(SDL_Rect *r, const SDL_Rect *clip_r)
465 {
466 	// NOTE: the following clipping code is copied in part
467 	//   from SDL-1.2.4 sources
468 	int dx, dy;
469 	int w = r->w;
470 	int h = r->h;
471 			// SDL_Rect.w and .h are unsigned, we need signed
472 
473 	dx = clip_r->x - r->x;
474 	if (dx > 0)
475 	{
476 		w -= dx;
477 		r->x += dx;
478 	}
479 	dx = r->x + w - clip_r->x - clip_r->w;
480 	if (dx > 0)
481 		w -= dx;
482 
483 	dy = clip_r->y - r->y;
484 	if (dy > 0)
485 	{
486 		h -= dy;
487 		r->y += dy;
488 	}
489 	dy = r->y + h - clip_r->y - clip_r->h;
490 	if (dy > 0)
491 		h -= dy;
492 
493 	if (w <= 0 || h <= 0)
494 	{
495 		r->w = 0;
496 		r->h = 0;
497 		return 0;
498 	}
499 
500 	r->w = w;
501 	r->h = h;
502 	return 1;
503 }
504 
505 void
blt_prim(SDL_Surface * src,SDL_Rect src_r,RenderPixelFn plot,int factor,SDL_Surface * dst,SDL_Rect dst_r)506 blt_prim(SDL_Surface *src, SDL_Rect src_r, RenderPixelFn plot, int factor,
507 		SDL_Surface *dst, SDL_Rect dst_r)
508 {
509 	SDL_PixelFormat *srcfmt = src->format;
510 	SDL_Palette *srcpal = srcfmt->palette;
511 	SDL_PixelFormat *dstfmt = dst->format;
512 	Uint32 mask = 0;
513 	Uint32 key = ~0;
514 	GetPixelFn getpix = getpixel_for(src);
515 	SDL_Rect clip_r;
516 	int x, y;
517 
518 	SDL_GetClipRect (dst, &clip_r);
519 	if (!clip_blt_rects (&src_r, &dst_r, &clip_r))
520 		return; // rect is completely outside clipping rectangle
521 
522 	if (src_r.x >= src->w || src_r.y >= src->h)
523 		return; // rect is completely outside source bounds
524 
525 	if (src_r.x + src_r.w > src->w)
526 		src_r.w = src->w - src_r.x;
527 	if (src_r.y + src_r.h > src->h)
528 		src_r.h = src->h - src_r.y;
529 
530 	// use colorkeys where appropriate
531 	if (srcfmt->Amask)
532 	{	// alpha transparency
533 		mask = srcfmt->Amask;
534 		key = 0;
535 	}
536 	else if (TFB_GetColorKey (src, &key) == 0)
537 	{
538 		mask = ~0;
539 	}
540 	// TODO: calculate the source and destination pointers directly
541 	//   instead of using the plot(x,y) version
542 	for (y = 0; y < src_r.h; ++y)
543 	{
544 		for (x = 0; x < src_r.w; ++x)
545 		{
546 			Uint8 r, g, b, a;
547 			Uint32 p;
548 
549 			p = getpix(src, src_r.x + x, src_r.y + y);
550 			if (srcpal)
551 			{	// source is paletted, colorkey does not use mask
552 				if (p == key)
553 					continue; // transparent pixel
554 			}
555 			else
556 			{	// source is RGB(A), colorkey uses mask
557 				if ((p & mask) == key)
558 					continue; // transparent pixel
559 			}
560 
561 			// convert pixel format to destination
562 			SDL_GetRGBA(p, srcfmt, &r, &g, &b, &a);
563 			// TODO: handle source pixel alpha; plot() should probably
564 			//   get a source alpha parameter
565 			p = SDL_MapRGBA(dstfmt, r, g, b, a);
566 
567 			plot(dst, dst_r.x + x, dst_r.y + y, p, factor);
568 		}
569 	}
570 }
571 
572 // clip the source and destination rectangles against the clip rectangle
573 int
clip_blt_rects(SDL_Rect * src_r,SDL_Rect * dst_r,const SDL_Rect * clip_r)574 clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r)
575 {
576 	// NOTE: the following clipping code is copied in part
577 	//   from SDL-1.2.4 sources
578 	int w, h;
579 	int dx, dy;
580 
581 	// clip the source rectangle to the source surface
582 	w = src_r->w;
583 	if (src_r->x < 0)
584 	{
585 		w += src_r->x;
586 		dst_r->x -= src_r->x;
587 		src_r->x = 0;
588 	}
589 
590 	h = src_r->h;
591 	if (src_r->y < 0)
592 	{
593 		h += src_r->y;
594 		dst_r->y -= src_r->y;
595 		src_r->y = 0;
596 	}
597 
598 	// clip the destination rectangle against the clip rectangle,
599 	// minding the source rectangle in the process
600 	dx = clip_r->x - dst_r->x;
601 	if (dx > 0)
602 	{
603 		w -= dx;
604 		dst_r->x += dx;
605 		src_r->x += dx;
606 	}
607 	dx = dst_r->x + w - clip_r->x - clip_r->w;
608 	if (dx > 0)
609 		w -= dx;
610 
611 	dy = clip_r->y - dst_r->y;
612 	if (dy > 0)
613 	{
614 		h -= dy;
615 		dst_r->y += dy;
616 		src_r->y += dy;
617 	}
618 	dy = dst_r->y + h - clip_r->y - clip_r->h;
619 	if (dy > 0)
620 		h -= dy;
621 
622 	if (w <= 0 || h <= 0)
623 	{
624 		src_r->w = 0;
625 		src_r->h = 0;
626 		return 0;
627 	}
628 
629 	src_r->w = w;
630 	src_r->h = h;
631 	return 1;
632 }
633 
634