1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003-2006 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 #include "SDLVideo.h"
22 
23 #include "Interface.h"
24 #include "Palette.h"
25 #include "SDLPixelIterator.h"
26 
27 #if defined(__sgi)
28 #  include <math.h>
29 #  ifdef __cplusplus
30 extern "C" double round(double);
31 #  endif
32 #else
33 #  include <cmath>
34 #endif
35 
36 using namespace GemRB;
37 
SDLVideoDriver(void)38 SDLVideoDriver::SDLVideoDriver(void)
39 {
40 	lastTime = 0;
41 }
42 
~SDLVideoDriver(void)43 SDLVideoDriver::~SDLVideoDriver(void)
44 {
45 	SDL_Quit();
46 }
47 
Init(void)48 int SDLVideoDriver::Init(void)
49 {
50 	if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) {
51 		Log(ERROR, "SDLVideo", "InitSubSystem failed: %s", SDL_GetError());
52 		return GEM_ERROR;
53 	}
54 	SDL_ShowCursor(SDL_DISABLE);
55 	return GEM_OK;
56 }
57 
CreateDriverDisplay(const char * title)58 int SDLVideoDriver::CreateDriverDisplay(const char* title)
59 {
60 	int ret = CreateSDLDisplay(title);
61 	scratchBuffer = CreateBuffer(Region(Point(), screenSize), BufferFormat::DISPLAY_ALPHA);
62 	scratchBuffer->Clear();
63 	return ret;
64 }
65 
PollEvents()66 int SDLVideoDriver::PollEvents()
67 {
68 	int ret = GEM_OK;
69 	SDL_Event currentEvent;
70 
71 	while (ret != GEM_ERROR && SDL_PollEvent(&currentEvent)) {
72 		ret = ProcessEvent(currentEvent);
73 	}
74 
75 	return ret;
76 }
77 
TranslateKeycode(SDLKey sym)78 static SDL_Keycode TranslateKeycode(SDLKey sym)
79 {
80 	switch (sym) {
81 		case SDLK_ESCAPE:
82 			return GEM_ESCAPE;
83 		case SDLK_END:
84 		case SDLK_KP1:
85 			return GEM_END;
86 		case SDLK_HOME:
87 		case SDLK_KP7:
88 			return GEM_HOME;
89 		case SDLK_UP:
90 		case SDLK_KP8:
91 			return GEM_UP;
92 		case SDLK_DOWN:
93 		case SDLK_KP2:
94 			return GEM_DOWN;
95 		case SDLK_LEFT:
96 		case SDLK_KP4:
97 			return GEM_LEFT;
98 		case SDLK_RIGHT:
99 		case SDLK_KP6:
100 			return GEM_RIGHT;
101 		case SDLK_DELETE:
102 #if TARGET_OS_IPHONE < 1
103 			//iOS currently doesnt have a backspace so we use delete.
104 			//This change should be future proof in the event apple changes the delete key to a backspace.
105 			return GEM_DELETE;
106 #endif
107 		case SDLK_BACKSPACE:
108 			return GEM_BACKSP;
109 		case SDLK_RETURN:
110 		case SDLK_KP_ENTER:
111 			return GEM_RETURN;
112 		case SDLK_LALT:
113 		case SDLK_RALT:
114 			return GEM_ALT;
115 		case SDLK_TAB:
116 			return GEM_TAB;
117 		case SDLK_PAGEUP:
118 		case SDLK_KP9:
119 			return GEM_PGUP;
120 		case SDLK_PAGEDOWN:
121 		case SDLK_KP3:
122 			return GEM_PGDOWN;
123 		case SDLK_SCROLLOCK:
124 			return GEM_GRAB;
125 		case SDLK_F1:
126 		case SDLK_F2:
127 		case SDLK_F3:
128 		case SDLK_F4:
129 		case SDLK_F5:
130 		case SDLK_F6:
131 		case SDLK_F7:
132 		case SDLK_F8:
133 		case SDLK_F9:
134 		case SDLK_F10:
135 		case SDLK_F11:
136 		case SDLK_F12:
137 			//assuming they come sequentially,
138 			//also, there is no need to ever produce more than 12
139 			return GEM_FUNCTIONX(1) + sym-SDLK_F1;
140 		default:
141 			break;
142 	}
143 	return sym;
144 }
145 
ProcessEvent(const SDL_Event & event)146 int SDLVideoDriver::ProcessEvent(const SDL_Event & event)
147 {
148 	if (!EvntManager)
149 		return GEM_ERROR;
150 
151 	// FIXME: technically event.key.keysym.mod should be the mod,
152 	// but for the mod keys themselves this is 0 and therefore not what GemRB expects
153 	// int modstate = GetModState(event.key.keysym.mod);
154 	int modstate = GetModState(SDL_GetModState());
155 	SDLKey sym = event.key.keysym.sym;
156 	SDL_Keycode key = sym;
157 	Event e;
158 
159 	/* Loop until there are no events left on the queue */
160 	switch (event.type) {
161 			/* Process the appropriate event type */
162 		case SDL_QUIT:
163 			/* Quit event originated from outside GemRB so ask the user if we should exit */
164 			core->AskAndExit();
165 			return GEM_OK;
166 		case SDL_KEYUP:
167 			key = TranslateKeycode(sym);
168 			if (key != 0) {
169 				Event e = EvntManager->CreateKeyEvent(key, false, modstate);
170 				EvntManager->DispatchEvent(std::move(e));
171 			}
172 			break;
173 		case SDL_KEYDOWN:
174 			// reenable special numpad keys unless numlock is off
175 			if (SDL_GetModState() & KMOD_NUM) {
176 				switch (sym) {
177 					case SDLK_KP1: sym = SDLK_1; break;
178 					case SDLK_KP2: sym = SDLK_2; break;
179 					case SDLK_KP3: sym = SDLK_3; break;
180 					case SDLK_KP4: sym = SDLK_4; break;
181 					// 5 is not special
182 					case SDLK_KP6: sym = SDLK_6; break;
183 					case SDLK_KP7: sym = SDLK_7; break;
184 					case SDLK_KP8: sym = SDLK_8; break;
185 					case SDLK_KP9: sym = SDLK_9; break;
186 					default: break;
187 				}
188 			}
189 			key = TranslateKeycode(sym);
190 
191 			e = EvntManager->CreateKeyEvent(key, true, modstate);
192 			if (e.keyboard.character) {
193 				if (InTextInput() && modstate == 0) {
194 					return GEM_OK;
195 				}
196 #if SDL_VERSION_ATLEAST(1,3,0)
197 				e.keyboard.character = SDL_GetKeyFromScancode(event.key.keysym.scancode);
198 #else
199 				e.keyboard.character = event.key.keysym.unicode;
200 #endif
201 			}
202 
203 			EvntManager->DispatchEvent(std::move(e));
204 			break;
205 		case SDL_MOUSEMOTION:
206 			e = EvntManager->CreateMouseMotionEvent(Point(event.motion.x, event.motion.y), modstate);
207 			EvntManager->DispatchEvent(std::move(e));
208 			break;
209 		case SDL_MOUSEBUTTONDOWN:
210 		case SDL_MOUSEBUTTONUP:
211 			{
212 				EventButton btn = SDL_BUTTON(event.button.button);
213 				if (btn) {
214 					// it has been observed that multibutton mice can
215 					// result in 0 for some of their extra buttons
216 					// on at least some platforms
217 					bool down = (event.type == SDL_MOUSEBUTTONDOWN) ? true : false;
218 					Point p(event.button.x, event.button.y);
219 					e = EvntManager->CreateMouseBtnEvent(p, btn, down, modstate);
220 					EvntManager->DispatchEvent(std::move(e));
221 				}
222 			}
223 			break;
224 		case SDL_JOYAXISMOTION:
225 			{
226 				float pct = event.jaxis.value / float(sizeof(Sint16));
227 				bool xaxis = event.jaxis.axis % 2;
228 				// FIXME: I'm sure this delta needs to be scaled
229 				int delta = (xaxis) ? pct * screenSize.w : pct * screenSize.h;
230 				InputAxis axis = InputAxis(event.jaxis.axis);
231 				e = EvntManager->CreateControllerAxisEvent(axis, delta, pct);
232 				EvntManager->DispatchEvent(std::move(e));
233 			}
234 			break;
235 		case SDL_JOYBUTTONDOWN:
236 		case SDL_JOYBUTTONUP:
237 			{
238 				bool down = (event.type == SDL_JOYBUTTONDOWN) ? true : false;
239 				EventButton btn = EventButton(event.jbutton.button);
240 				e = EvntManager->CreateControllerButtonEvent(btn, down);
241 				EvntManager->DispatchEvent(std::move(e));
242 			}
243 			break;
244 	}
245 	return GEM_OK;
246 }
247 
CreateSprite(const Region & rgn,int bpp,ieDword rMask,ieDword gMask,ieDword bMask,ieDword aMask,void * pixels,bool cK,int index)248 Holder<Sprite2D> SDLVideoDriver::CreateSprite(const Region& rgn, int bpp, ieDword rMask,
249 	ieDword gMask, ieDword bMask, ieDword aMask, void* pixels, bool cK, int index)
250 {
251 	sprite_t* spr = new sprite_t(rgn, bpp, pixels, rMask, gMask, bMask, aMask);
252 
253 	if (cK) {
254 		spr->SetColorKey(index);
255 	}
256 	/*
257 	 there is at least one place (BlitGameSprite) that requires 8 or 32bpp sprites
258 	 untill we support 16bpp fully we cannot do this
259 
260 	// make sure colorkey is set prior to conversion
261 	SDL_PixelFormat* fmt = backBuf->format;
262 	spr->ConvertFormatTo(fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
263 	*/
264 	return spr;
265 }
266 
CreateSprite8(const Region & rgn,void * pixels,PaletteHolder palette,bool cK,int index)267 Holder<Sprite2D> SDLVideoDriver::CreateSprite8(const Region& rgn, void* pixels,
268 										PaletteHolder palette, bool cK, int index)
269 {
270 	return CreatePalettedSprite(rgn, 8, pixels, palette->col, cK, index);
271 }
272 
CreatePalettedSprite(const Region & rgn,int bpp,void * pixels,Color * palette,bool cK,int index)273 Holder<Sprite2D> SDLVideoDriver::CreatePalettedSprite(const Region& rgn, int bpp, void* pixels,
274 											   Color* palette, bool cK, int index)
275 {
276 	sprite_t* spr = new sprite_t(rgn, bpp, pixels, 0, 0, 0, 0);
277 
278 	spr->SetPalette(palette);
279 	if (cK) {
280 		spr->SetColorKey(index);
281 	}
282 	return spr;
283 }
284 
BlitSprite(const Holder<Sprite2D> spr,const Region & src,Region dst,BlitFlags flags,Color tint)285 void SDLVideoDriver::BlitSprite(const Holder<Sprite2D> spr, const Region& src, Region dst,
286 								BlitFlags flags, Color tint)
287 {
288 	dst.x -= spr->Frame.x;
289 	dst.y -= spr->Frame.y;
290 	BlitSpriteClipped(spr, src, dst, flags, &tint);
291 }
292 
BlitGameSprite(const Holder<Sprite2D> spr,const Point & p,BlitFlags flags,Color tint)293 void SDLVideoDriver::BlitGameSprite(const Holder<Sprite2D> spr, const Point& p,
294 									BlitFlags flags, Color tint)
295 {
296 	Region srect(Point(0, 0), spr->Frame.Dimensions());
297 	Region drect = Region(p - spr->Frame.Origin(), spr->Frame.Dimensions());
298 	BlitSpriteClipped(spr, srect, drect, flags, &tint);
299 }
300 
CurrentRenderClip() const301 Region SDLVideoDriver::CurrentRenderClip() const
302 {
303 	Region bufferRegion(Point(), drawingBuffer->Size());
304 	return screenClip.Intersect(bufferRegion);
305 }
306 
307 // SetPixel is in screen coordinates
308 #if SDL_VERSION_ATLEAST(1,3,0)
309 #define SetPixel(buffer, _x, _y) { \
310 Region _r = Region(Point(0,0), buffer->Size()); \
311 Point _p(_x, _y); \
312 if (_r.PointInside(_p)) { SDL_Point _p2 = {_p.x,_p.y}; points.push_back(_p2); } }
313 #else
314 #define SetPixel(buffer, _x, _y) { \
315 Region _r = Region(Point(0,0), buffer->Size()); \
316 Point _p(_x, _y); \
317 if (_r.PointInside(_p)) { points.push_back(_p); } }
318 #endif
319 
320 /** This functions Draws a Circle */
DrawCircleImp(const Point & c,unsigned short r,const Color & color,BlitFlags flags)321 void SDLVideoDriver::DrawCircleImp(const Point& c, unsigned short r, const Color& color, BlitFlags flags)
322 {
323 	//Uses the Breshenham's Circle Algorithm
324 	long x, y, xc, yc, re;
325 
326 	x = r;
327 	y = 0;
328 	xc = 1 - ( 2 * r );
329 	yc = 1;
330 	re = 0;
331 
332 	std::vector<SDL_Point> points;
333 
334 	while (x >= y) {
335 		SetPixel( drawingBuffer, c.x + ( short ) x, c.y + ( short ) y );
336 		SetPixel( drawingBuffer, c.x - ( short ) x, c.y + ( short ) y );
337 		SetPixel( drawingBuffer, c.x - ( short ) x, c.y - ( short ) y );
338 		SetPixel( drawingBuffer, c.x + ( short ) x, c.y - ( short ) y );
339 		SetPixel( drawingBuffer, c.x + ( short ) y, c.y + ( short ) x );
340 		SetPixel( drawingBuffer, c.x - ( short ) y, c.y + ( short ) x );
341 		SetPixel( drawingBuffer, c.x - ( short ) y, c.y - ( short ) x );
342 		SetPixel( drawingBuffer, c.x + ( short ) y, c.y - ( short ) x );
343 
344 		y++;
345 		re += yc;
346 		yc += 2;
347 
348 		if (( ( 2 * re ) + xc ) > 0) {
349 			x--;
350 			re += xc;
351 			xc += 2;
352 		}
353 	}
354 
355 	DrawSDLPoints(points, reinterpret_cast<const SDL_Color&>(color), flags);
356 }
357 
ellipseradius(unsigned short xr,unsigned short yr,double angle)358 static double ellipseradius(unsigned short xr, unsigned short yr, double angle) {
359 	double one = (xr * sin(angle));
360 	double two = (yr * cos(angle));
361 	return sqrt(xr*xr*yr*yr / (one*one + two*two));
362 }
363 
364 /** This functions Draws an Ellipse Segment */
DrawEllipseSegmentImp(const Point & c,unsigned short xr,unsigned short yr,const Color & color,double anglefrom,double angleto,bool drawlines,BlitFlags flags)365 void SDLVideoDriver::DrawEllipseSegmentImp(const Point& c, unsigned short xr,
366 	unsigned short yr, const Color& color, double anglefrom, double angleto, bool drawlines, BlitFlags flags)
367 {
368 	/* beware, dragons and clockwise angles be here! */
369 	double radiusfrom = ellipseradius(xr, yr, anglefrom);
370 	double radiusto = ellipseradius(xr, yr, angleto);
371 	long xfrom = (long)round(radiusfrom * cos(anglefrom));
372 	long yfrom = (long)round(radiusfrom * sin(anglefrom));
373 	long xto = (long)round(radiusto * cos(angleto));
374 	long yto = (long)round(radiusto * sin(angleto));
375 	long xrl = (long) xr;
376 	long yrl = (long) yr;
377 
378 	if (drawlines) {
379 		DrawLine(c, Point(c.x + xfrom, c.y + yfrom), color, flags);
380 		DrawLine(c, Point(c.x + xto, c.y + yto), color, flags);
381 	}
382 
383 	// *Attempt* to calculate the correct x/y boundaries.
384 	// TODO: this doesn't work very well - you can't actually bound many
385 	// arcs this way (imagine a segment with a small piece cut out).
386 	if (xfrom > xto) {
387 		long tmp = xfrom; xfrom = xto; xto = tmp;
388 	}
389 	if (yfrom > yto) {
390 		long tmp = yfrom; yfrom = yto; yto = tmp;
391 	}
392 	if (xfrom >= 0 && yto >= 0) xto = xr;
393 	if (xto <= 0 && yto >= 0) xfrom = -xr;
394 	if (yfrom >= 0 && xto >= 0) yto = yr;
395 	if (yto <= 0 && xto >= 0) yfrom = -yr;
396 
397 	//Uses Bresenham's Ellipse Algorithm
398 	long x, y, xc, yc, ee, tas, tbs, sx, sy;
399 
400 	tas = 2 * xrl * xrl;
401 	tbs = 2 * yrl * yrl;
402 	x = xrl;
403 	y = 0;
404 	xc = yrl * yrl * (1 - (2 * xrl));
405 	yc = xrl * xrl;
406 	ee = 0;
407 	sx = tbs * xrl;
408 	sy = 0;
409 
410 	std::vector<SDL_Point> points;
411 
412 	while (sx >= sy) {
413 		if (x >= xfrom && x <= xto && y >= yfrom && y <= yto)
414 			SetPixel( drawingBuffer, c.x + ( short ) x, c.y + ( short ) y );
415 		if (-x >= xfrom && -x <= xto && y >= yfrom && y <= yto)
416 			SetPixel( drawingBuffer, c.x - ( short ) x, c.y + ( short ) y );
417 		if (-x >= xfrom && -x <= xto && -y >= yfrom && -y <= yto)
418 			SetPixel( drawingBuffer, c.x - ( short ) x, c.y - ( short ) y );
419 		if (x >= xfrom && x <= xto && -y >= yfrom && -y <= yto)
420 			SetPixel( drawingBuffer, c.x + ( short ) x, c.y - ( short ) y );
421 		y++;
422 		sy += tas;
423 		ee += yc;
424 		yc += tas;
425 		if (( 2 * ee + xc ) > 0) {
426 			x--;
427 			sx -= tbs;
428 			ee += xc;
429 			xc += tbs;
430 		}
431 	}
432 
433 	x = 0;
434 	y = yrl;
435 	xc = yrl * yrl;
436 	yc = xrl * xrl * (1 - (2 * yrl));
437 	ee = 0;
438 	sx = 0;
439 	sy = tas * yrl;
440 
441 	while (sx <= sy) {
442 		if (x >= xfrom && x <= xto && y >= yfrom && y <= yto)
443 			SetPixel( drawingBuffer, c.x + ( short ) x, c.y + ( short ) y );
444 		if (-x >= xfrom && -x <= xto && y >= yfrom && y <= yto)
445 			SetPixel( drawingBuffer, c.x - ( short ) x, c.y + ( short ) y );
446 		if (-x >= xfrom && -x <= xto && -y >= yfrom && -y <= yto)
447 			SetPixel( drawingBuffer, c.x - ( short ) x, c.y - ( short ) y );
448 		if (x >= xfrom && x <= xto && -y >= yfrom && -y <= yto)
449 			SetPixel( drawingBuffer, c.x + ( short ) x, c.y - ( short ) y );
450 		x++;
451 		sx += tbs;
452 		ee += xc;
453 		xc += tbs;
454 		if (( 2 * ee + yc ) > 0) {
455 			y--;
456 			sy -= tas;
457 			ee += yc;
458 			yc += tas;
459 		}
460 	}
461 
462 	DrawSDLPoints(points, reinterpret_cast<const SDL_Color&>(color), flags);
463 }
464 
465 
466 /** This functions Draws an Ellipse */
DrawEllipseImp(const Point & c,unsigned short xr,unsigned short yr,const Color & color,BlitFlags flags)467 void SDLVideoDriver::DrawEllipseImp(const Point& c, unsigned short xr,
468 									unsigned short yr, const Color& color, BlitFlags flags)
469 {
470 	//Uses Bresenham's Ellipse Algorithm
471 	long x, y, xc, yc, ee, tas, tbs, sx, sy;
472 	long xrl = (long) xr;
473 	long yrl = (long) yr;
474 
475 	tas = 2 * xrl * xrl;
476 	tbs = 2 * yrl * yrl;
477 	x = xrl;
478 	y = 0;
479 	xc = yrl * yrl * (1 - (2 * xrl));
480 	yc = xrl * xrl;
481 	ee = 0;
482 	sx = tbs * xrl;
483 	sy = 0;
484 
485 	std::vector<SDL_Point> points;
486 
487 	while (sx >= sy) {
488 		SetPixel( drawingBuffer, c.x + ( short ) x, c.y + ( short ) y );
489 		SetPixel( drawingBuffer, c.x - ( short ) x, c.y + ( short ) y );
490 		SetPixel( drawingBuffer, c.x - ( short ) x, c.y - ( short ) y );
491 		SetPixel( drawingBuffer, c.x + ( short ) x, c.y - ( short ) y );
492 		y++;
493 		sy += tas;
494 		ee += yc;
495 		yc += tas;
496 		if (( 2 * ee + xc ) > 0) {
497 			x--;
498 			sx -= tbs;
499 			ee += xc;
500 			xc += tbs;
501 		}
502 	}
503 
504 	x = 0;
505 	y = yrl;
506 	xc = yrl * yrl;
507 	yc = xrl * xrl * (1 - (2 * yrl));
508 	ee = 0;
509 	sx = 0;
510 	sy = tas * yrl;
511 
512 	while (sx <= sy) {
513 		SetPixel( drawingBuffer, c.x + ( short ) x, c.y + ( short ) y );
514 		SetPixel( drawingBuffer, c.x - ( short ) x, c.y + ( short ) y );
515 		SetPixel( drawingBuffer, c.x - ( short ) x, c.y - ( short ) y );
516 		SetPixel( drawingBuffer, c.x + ( short ) x, c.y - ( short ) y );
517 		x++;
518 		sx += tbs;
519 		ee += xc;
520 		xc += tbs;
521 		if (( 2 * ee + yc ) > 0) {
522 			y--;
523 			sy -= tas;
524 			ee += yc;
525 			yc += tas;
526 		}
527 	}
528 
529 	DrawSDLPoints(points, reinterpret_cast<const SDL_Color&>(color), flags);
530 }
531 
532 #undef SetPixel
533 
BlitSpriteClipped(const Holder<Sprite2D> spr,Region src,const Region & dst,BlitFlags flags,const Color * tint)534 void SDLVideoDriver::BlitSpriteClipped(const Holder<Sprite2D> spr, Region src, const Region& dst, BlitFlags flags, const Color* tint)
535 {
536 #if SDL_VERSION_ATLEAST(1,3,0)
537 	// in SDL2 SDL_RenderCopyEx will flip the src rect internally if BlitFlags::MIRRORX or BlitFlags::MIRRORY is set
538 	// instead of doing this and then reversing it in that case only for SDL to reverse it yet again
539 	// lets just not worry about clipping on SDL2. the backends handle all of that for us unlike with SDL 1 where we
540 	// might walk off a memory buffer; we have no danger of that in SDL 2.
541 	// This fixes bizzare clipping issues when a "flipped" sprite is partially offscreen
542 	// we still will clip with screenClip later so no worries there
543 	// we still want to do the clipping for the purposes of avoiding calls to BlitSpriteNativeClipped where
544 	// expensive calls to RenderSpriteVersion may take place
545 	Region originalSrc = src;
546 #endif
547 	// FIXME?: srect isn't verified
548 	Region dclipped = ClippedDrawingRect(dst);
549 	int trim = dst.h - dclipped.h;
550 	if (trim) {
551 		src.h -= trim;
552 		if (dclipped.y > dst.y) { // top clipped
553 			src.y += trim;
554 		} // already have appropriate y for bottom clip
555 	}
556 	trim = dst.w - dclipped.w;
557 	if (trim) {
558 		src.w -= trim;
559 		if (dclipped.x > dst.x) { // left clipped
560 			src.x += trim;
561 		}
562 	} // already have appropriate y for right clip
563 
564 	if (dclipped.Dimensions().IsEmpty() || src.Dimensions().IsEmpty()) {
565 		return;
566 	}
567 
568 	assert(dclipped.w == src.w && dclipped.h == src.h);
569 
570 #if SDL_VERSION_ATLEAST(1,3,0)
571 	dclipped = dst;
572 	src = originalSrc;
573 #endif
574 
575 	if (spr->renderFlags&BlitFlags::MIRRORX) {
576 		flags ^= BlitFlags::MIRRORX;
577 	}
578 
579 	if (spr->renderFlags&BlitFlags::MIRRORY) {
580 		flags ^= BlitFlags::MIRRORY;
581 	}
582 
583 	if (!spr->HasTransparency()) {
584 		flags &= ~BlitFlags::BLENDED;
585 	}
586 
587 	if (spr->BAM) {
588 		BlitSpriteBAMClipped(spr, src, dclipped, flags, tint);
589 	} else {
590 		SDL_Rect srect = RectFromRegion(src);
591 		SDL_Rect drect = RectFromRegion(dclipped);
592 		const sprite_t* native = static_cast<const sprite_t*>(spr.get ());
593 		BlitSpriteNativeClipped(native, srect, drect, flags, reinterpret_cast<const SDL_Color*>(tint));
594 	}
595 }
596 
RenderSpriteVersion(const SDLSurfaceSprite2D * spr,BlitFlags renderflags,const Color * tint)597 BlitFlags SDLVideoDriver::RenderSpriteVersion(const SDLSurfaceSprite2D* spr, BlitFlags renderflags, const Color* tint)
598 {
599 	SDLSurfaceSprite2D::version_t oldVersion = spr->GetVersion();
600 	SDLSurfaceSprite2D::version_t newVersion = renderflags;
601 	auto ret = (BlitFlags::GREY | BlitFlags::SEPIA) & newVersion;
602 
603 	if (spr->Bpp == 8) {
604 		if (tint) {
605 			assert(renderflags & (BlitFlags::COLOR_MOD | BlitFlags::ALPHA_MOD));
606 			uint64_t tintv = *reinterpret_cast<const uint32_t*>(tint);
607 			newVersion |= tintv << 32;
608 		}
609 
610 		if (spr->IsPaletteStale()) {
611 			spr->Restore();
612 		}
613 
614 		if (oldVersion != newVersion) {
615 			SDL_Palette* pal = static_cast<SDL_Palette*>(spr->NewVersion(newVersion));
616 
617 			for (size_t i = 0; i < 256; ++i) {
618 				Color& dstc = reinterpret_cast<Color&>(pal->colors[i]);
619 
620 				if (renderflags&BlitFlags::COLOR_MOD) {
621 					assert(tint);
622 					ShaderTint(*tint, dstc);
623 					ret |= BlitFlags::COLOR_MOD;
624 				}
625 
626 				if (renderflags & BlitFlags::ALPHA_MOD) {
627 					assert(tint);
628 					dstc.a = tint->a;
629 					ret |= BlitFlags::ALPHA_MOD;
630 				}
631 
632 				if (renderflags&BlitFlags::GREY) {
633 					ShaderGreyscale(dstc);
634 				} else if (renderflags&BlitFlags::SEPIA) {
635 					ShaderSepia(dstc);
636 				}
637 			}
638 		} else {
639 			ret |= (BlitFlags::COLOR_MOD | BlitFlags::ALPHA_MOD) & newVersion;
640 		}
641 	} else if (oldVersion != newVersion) {
642 		SDL_Surface* newV = (SDL_Surface*)spr->NewVersion(newVersion);
643 		SDL_LockSurface(newV);
644 
645 		SDL_Rect r = {0, 0, (unsigned short)newV->w, (unsigned short)newV->h};
646 		SDLPixelIterator beg(newV, r);
647 		SDLPixelIterator end = SDLPixelIterator::end(beg);
648 		StaticAlphaIterator alpha(0xff);
649 
650 		if (renderflags & BlitFlags::GREY) {
651 			RGBBlendingPipeline<SHADER::GREYSCALE, true> blender;
652 			Blit(beg, beg, end, alpha, blender);
653 		} else if (renderflags & BlitFlags::SEPIA) {
654 			RGBBlendingPipeline<SHADER::SEPIA, true> blender;
655 			Blit(beg, beg, end, alpha, blender);
656 		}
657 		SDL_UnlockSurface(newV);
658 	}
659 	return static_cast<BlitFlags>(ret);
660 }
661 
662 // static class methods
663 
SetSurfacePalette(SDL_Surface * surf,const SDL_Color * pal,int numcolors)664 int SDLVideoDriver::SetSurfacePalette(SDL_Surface* surf, const SDL_Color* pal, int numcolors)
665 {
666 	if (pal) {
667 #if SDL_VERSION_ATLEAST(1,3,0)
668 		return SDL_SetPaletteColors( surf->format->palette, pal, 0, numcolors );
669 #else
670 		// const_cast because SDL doesnt alter this and we want our interface to be const correct
671 		return SDL_SetPalette( surf, SDL_LOGPAL, const_cast<SDL_Color*>(pal), 0, numcolors );
672 #endif
673 	}
674 	return -1;
675 }
676