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(¤tEvent)) {
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