add some fixes for hwsurfaces
[drnoksnes] / platform / sdlvscalers.cpp
1 #include <stdio.h> 
2 #include <math.h>
3
4 #include <X11/Xlib.h>
5 #include <X11/Xutil.h>
6 #include <SDL.h>
7
8 #if CONF_XSP
9 #       include <SDL_syswm.h>
10 #       include <X11/extensions/Xsp.h>
11 #endif
12 #if CONF_HD
13 #       include <SDL_haa.h>
14 #endif
15
16 #include "snes9x.h"
17 #include "platform.h"
18 #include "sdlv.h"
19
20 #define DIE(format, ...) do { \
21                 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
22                 fprintf(stderr, format "\n", ## __VA_ARGS__); \
23                 abort(); \
24         } while (0);
25
26 /* Helper functions */
27
28 static void centerRectangle(SDL_Rect& result, int areaW, int areaH, int w, int h)
29 {
30         result.x = areaW / 2 - w / 2;
31         result.w = w;
32         result.y = areaH / 2 - h / 2;
33         result.h = h;
34         /* We need to keep this 4-byte aligned (each pixel is 2-byte) */
35         result.x &= ~1;
36 }
37
38 /* Base scaler for stupid scalers */
39 /** Does nothing but center the image */
40 class DummyScaler : public Scaler
41 {
42         SDL_Surface * m_screen;
43         SDL_Rect m_area;
44
45 protected:
46         DummyScaler(SDL_Surface* screen, int w, int h)
47         : m_screen(screen)
48         {
49                 centerRectangle(m_area, GUI.Width, GUI.Height, w, h);
50         }
51
52 public:
53
54         ~DummyScaler()
55         {
56         };
57
58         class Factory : public ScalerFactory
59         {
60                 const char * getName() const
61                 {
62                         return "none";
63                 }
64
65                 bool canEnable(int bpp, int w, int h) const
66                 {
67                         return true;
68                 }
69
70                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
71                 {
72                         return new DummyScaler(screen, w, h);
73                 }
74         };
75
76         static const Factory factory;
77
78         virtual const char * getName() const
79         {
80                 return "no scaling";
81         }
82
83         virtual uint8* getDrawBuffer() const
84         {
85                 const int Bpp = screen->format->BitsPerPixel / 8;
86                 const int pitch = screen->pitch;
87                 return ((uint8*) screen->pixels)
88                         + (m_area.x * Bpp)
89                         + (m_area.y * pitch);
90         };
91
92         virtual unsigned int getDrawBufferPitch() const
93         {
94                 return screen->pitch;
95         };
96
97         virtual void getRenderedGUIArea(unsigned short & x, unsigned short & y,
98                                                         unsigned short & w, unsigned short & h) const
99         {
100                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
101         };
102
103         virtual void getRatio(float & x, float & y) const
104         {
105                 x = 1.0f; y = 1.0f;
106         };
107
108         virtual void prepare()
109         {
110                 if (SDL_MUSTLOCK(m_screen)) SDL_LockSurface(m_screen);
111         };
112
113         virtual void finish()
114         {
115                 if (SDL_MUSTLOCK(m_screen)) SDL_UnlockSurface(m_screen);
116                 SDL_UpdateRects(m_screen, 1, &m_area);
117         };
118
119         virtual void pause() { };
120         virtual void resume() { };
121
122         virtual bool filter(const SDL_Event& event) { return false; };
123 };
124 const DummyScaler::Factory DummyScaler::factory;
125
126 /* Basic and slow software scaler */
127
128 class SWScaler : public Scaler
129 {
130         SDL_Surface * m_screen;
131         SDL_Rect m_area;
132         uint8 * m_surface;
133         const int m_w, m_h, m_Bpp;
134
135 protected:
136         SWScaler(SDL_Surface* screen, int w, int h)
137         : m_screen(screen), m_w(w), m_h(h),
138          m_Bpp(m_screen->format->BitsPerPixel / 8)
139         {
140                 centerRectangle(m_area, GUI.Width, GUI.Height, w * 2, h * 2);
141                 m_surface = reinterpret_cast<uint8*>(malloc(w * h * m_Bpp));
142         }
143 public:
144         virtual ~SWScaler()
145         {
146                 free(m_surface);
147         };
148
149         class Factory : public ScalerFactory
150         {
151                 const char * getName() const
152                 {
153                         return "soft2x";
154                 }
155
156                 bool canEnable(int bpp, int w, int h) const
157                 {
158                         return w * 2 < GUI.Width && h * 2 < GUI.Height;
159                 }
160
161                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
162                 {
163                         return new SWScaler(screen, w, h);
164                 }
165         };
166
167         static const Factory factory;
168
169         virtual const char * getName() const
170         {
171                 return "software 2x scaling";
172         }
173
174         uint8* getDrawBuffer() const
175         {
176                 return m_surface;
177         };
178
179         unsigned int getDrawBufferPitch() const
180         {
181                 return m_w * m_Bpp;
182         };
183
184         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
185                                                         unsigned short & w, unsigned short & h) const
186         {
187                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
188         };
189
190         void getRatio(float & x, float & y) const
191         {
192                 x = 2.0f; y = 2.0f;
193         };
194
195         void prepare() { };
196
197         void finish()
198         {
199                 uint16 * src = reinterpret_cast<uint16*>(m_surface);
200                 uint16 * dst = reinterpret_cast<uint16*>(
201                         ((uint8*) m_screen->pixels)
202                         + (m_area.x * m_Bpp)
203                         + (m_area.y * m_screen->pitch));
204                 const int src_pitch = m_w;
205                 const int dst_pitch = m_screen->pitch / m_Bpp;
206                 int x, y;
207
208                 for (y = 0; y < m_h*2; y++) {
209                         for (x = 0; x < m_w*2; x+=2) {
210                                 dst[x] = src[x/2];
211                                 dst[x + 1] = src[x/2];
212                         }
213                         dst += dst_pitch;
214                         if (y&1) src += src_pitch;
215                 }
216
217                 SDL_UpdateRects(m_screen, 1, &m_area);
218         };
219
220         void pause() { };
221         void resume() { };
222
223         bool filter(const SDL_Event& event) { return false; };
224 };
225 const SWScaler::Factory SWScaler::factory;
226
227 /* Platform specific scalers */
228
229 #ifdef __arm__
230 class ARMScaler : public Scaler
231 {
232         SDL_Surface * m_screen;
233         SDL_Rect m_area;
234         uint8 * m_surface;
235         const int m_w, m_h, m_Bpp;
236
237 protected:
238         ARMScaler(SDL_Surface* screen, int w, int h)
239         : m_screen(screen), m_w(w), m_h(h),
240          m_Bpp(m_screen->format->BitsPerPixel / 8)
241         {
242                 centerRectangle(m_area, GUI.Width, GUI.Height, w * 2, h * 2);
243                 m_surface = reinterpret_cast<uint8*>(malloc(w * h * m_Bpp));
244         }
245 public:
246         virtual ~ARMScaler()
247         {
248                 free(m_surface);
249         };
250
251         class Factory : public ScalerFactory
252         {
253                 const char * getName() const
254                 {
255                         return "arm2x";
256                 }
257
258                 bool canEnable(int bpp, int w, int h) const
259                 {
260                         return bpp == 16 && w * 2 < GUI.Width && h * 2 < GUI.Height &&
261                                 w % 16 == 0 /* asm assumes w div by 16 */;
262                 }
263
264                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
265                 {
266                         return new ARMScaler(screen, w, h);
267                 }
268         };
269
270         static const Factory factory;
271
272         virtual const char * getName() const
273         {
274                 return "software ARM 2x scaling";
275         }
276
277         uint8* getDrawBuffer() const
278         {
279                 return m_surface;
280         };
281
282         unsigned int getDrawBufferPitch() const
283         {
284                 return m_w * m_Bpp;
285         };
286
287         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
288                                                         unsigned short & w, unsigned short & h) const
289         {
290                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
291         };
292
293         void getRatio(float & x, float & y) const
294         {
295                 x = 2.0f; y = 2.0f;
296         };
297
298         void prepare() {
299                 SDL_FillRect(m_screen, NULL, 0);
300         };
301
302         void finish()
303         {
304                 if (SDL_MUSTLOCK(m_screen)) SDL_LockSurface(m_screen);
305                 uint16 * src = reinterpret_cast<uint16*>(m_surface);
306                 uint16 * dst = reinterpret_cast<uint16*>(
307                         ((uint8*) m_screen->pixels)
308                         + (m_area.x * m_Bpp)
309                         + (m_area.y * m_screen->pitch));
310                 const int src_pitch = m_w;
311                 const int dst_pitch = m_screen->pitch / m_Bpp;
312                 int y;
313
314                 for (y = 0; y < m_h*2; y++) {
315                         asm volatile
316                         (
317                                 "mov r0, %0; mov r1, %1; mov r2, %2;"
318                                 "stmdb r13!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,r14};"
319                                 "1:     ldmia r1!,{r3,r4,r5,r6,r7,r8,r9,r10};"
320                                 "mov r14,r5,lsr #16;"
321                                 "mov r12,r5,lsl #16;"
322                                 "orr r14,r14,r14,lsl #16;"
323                                 "orr r12,r12,r12,lsr #16;"
324                                 "mov r11,r4,lsr #16;"
325                                 "mov r5,r4,lsl #16;"
326                                 "orr r11,r11,r11,lsl #16;"
327                                 "orr r5,r5,r5,lsr #16;"
328                                 "mov r4,r3,lsr #16;"
329                                 "mov r3,r3,lsl #16;"
330                                 "orr r4,r4,r4,lsl #16;"
331                                 "orr r3,r3,r3,lsr #16;"
332                                 "stmia r0!,{r3,r4,r5,r11,r12,r14};"
333                                 "mov r3,r6,lsl #16;"
334                                 "mov r4,r6,lsr #16;"
335                                 "orr r3,r3,r3,lsr #16;"
336                                 "orr r4,r4,r4,lsl #16;"
337                                 "mov r5,r7,lsl #16;"
338                                 "mov r6,r7,lsr #16;"
339                                 "orr r5,r5,r5,lsr #16;"
340                                 "orr r6,r6,r6,lsl #16;"
341                                 "mov r7,r8,lsl #16;"
342                                 "mov r8,r8,lsr #16;"
343                                 "orr r7,r7,r7,lsr #16;"
344                                 "orr r8,r8,r8,lsl #16;"
345                                 "mov r12,r10,lsr #16;"
346                                 "mov r11,r10,lsl #16;"
347                                 "orr r12,r12,r12,lsl #16;"
348                                 "orr r11,r11,r11,lsr #16;"
349                                 "mov r10,r9,lsr #16;"
350                                 "mov r9,r9,lsl #16;"
351                                 "orr r10,r10,r10,lsl #16;"
352                                 "orr r9,r9,r9,lsr #16;"
353                                 "stmia r0!,{r3,r4,r5,r6,r7,r8,r9,r10,r11,r12};"
354                                 "subs r2,r2,#16;"
355                                 "bhi 1b;"
356                                 "ldmia r13!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,r14};"
357                         :
358                         : "r" (dst), "r" (src), "r" (m_w)
359                         : "r0", "r1", "r2", "r3"
360                         );
361                         dst += dst_pitch;
362                         if (y&1) src += src_pitch;
363                 }
364
365                 if (SDL_MUSTLOCK(m_screen)) SDL_UnlockSurface(m_screen);
366
367                 if (m_screen->flags & SDL_DOUBLEBUF) {
368                         SDL_Flip(m_screen);
369                 } else {
370                         SDL_UpdateRects(m_screen, 1, &m_area);
371                 }
372         };
373
374         void pause() { };
375         void resume() { };
376         bool filter(const SDL_Event& event) { return false; };
377 };
378 const ARMScaler::Factory ARMScaler::factory;
379 #endif
380
381 #if CONF_HD
382 class HAAScalerBase : public Scaler
383 {
384         SDL_Surface *m_screen;
385         SDL_Rect m_area;
386         HAA_Actor *actor;
387         const int m_w, m_h, m_Bpp;
388         const float ratio_x, ratio_y;
389
390         static bool initialized;
391
392 protected:
393         HAAScalerBase(SDL_Surface* screen, int w, int h, float r_x, float r_y)
394         : m_screen(screen), m_w(w), m_h(h),
395          m_Bpp(m_screen->format->BitsPerPixel / 8),
396          ratio_x(r_x), ratio_y(r_y)
397         {
398                 const bool fullscreen = m_screen->flags & SDL_FULLSCREEN;
399                 centerRectangle(m_area, GUI.Width, GUI.Height, w * r_x, h * r_y);
400
401                 // Clear the SDL screen with black, just in case it gets drawn.
402                 SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
403
404                 if (!initialized) {
405                         HAA_Init(0);
406                         initialized = true;
407                 } else {
408                         HAA_SetVideoMode(); // Tell HAA we might have changed video mode
409                 }
410
411                 actor = HAA_CreateActor(0, m_w, m_h, m_screen->format->BitsPerPixel);
412                 HAA_SetPosition(actor, m_area.x, m_area.y + (fullscreen ? 0 : 60));
413                         // In windowed mode, take care of the title bar (xoffset = 60)
414                 HAA_SetScale(actor, r_x, r_y);
415                 HAA_Show(actor);
416         }
417
418 public:
419         virtual ~HAAScalerBase()
420         {
421                 HAA_FreeActor(actor);
422         };
423
424         uint8* getDrawBuffer() const
425         {
426                 return reinterpret_cast<uint8*>(actor->surface->pixels);
427         };
428
429         unsigned int getDrawBufferPitch() const
430         {
431                 return actor->surface->pitch;
432         };
433
434         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
435                                                         unsigned short & w, unsigned short & h) const
436         {
437                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
438         };
439
440         void getRatio(float & x, float & y) const
441         {
442                 x = ratio_x; y = ratio_y;
443         };
444
445         void prepare()
446         {
447
448         };
449
450         void finish()
451         {
452                 HAA_Flip(actor);
453         };
454
455         void pause() { };
456         void resume() { };
457
458         bool filter(const SDL_Event& event)
459         {
460                 return HAA_FilterEvent(&event) == 0;
461         };
462 };
463 bool HAAScalerBase::initialized = false;
464
465 class HAAFillScaler : public HAAScalerBase
466 {
467         HAAFillScaler(SDL_Surface* screen, int w, int h)
468         : HAAScalerBase(screen, w, h,
469                 GUI.Width / (float)w, GUI.Height / (float)h)
470         {
471         }
472
473 public:
474         class Factory : public ScalerFactory
475         {
476                 const char * getName() const
477                 {
478                         return "haafill";
479                 }
480
481                 bool canEnable(int bpp, int w, int h) const
482                 {
483                         return true;
484                 }
485
486                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
487                 {
488                         return new HAAFillScaler(screen, w, h-20);
489                 }
490         };
491
492         static const Factory factory;
493
494         const char * getName() const
495         {
496                 return "HAA fill screen scaling";
497         }
498 };
499 const HAAFillScaler::Factory HAAFillScaler::factory;
500
501 class HAASquareScaler : public HAAScalerBase
502 {
503         HAASquareScaler(SDL_Surface* screen, int w, int h, float ratio)
504         : HAAScalerBase(screen, w, h, ratio, ratio)
505         {
506         }
507
508 public:
509         class Factory : public ScalerFactory
510         {
511                 const char * getName() const
512                 {
513                         return "haasq";
514                 }
515
516                 bool canEnable(int bpp, int w, int h) const
517                 {
518                         return true;
519                 }
520
521                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
522                 {
523                         return new HAASquareScaler(screen, w, h,
524                                 fminf(GUI.Width / (float)w, GUI.Height / (float)h));
525                 }
526         };
527
528         static const Factory factory;
529
530         const char * getName() const
531         {
532                 return "HAA square screen scaling";
533         }
534 };
535 const HAASquareScaler::Factory HAASquareScaler::factory;
536
537 #endif /* CONF_HD */
538
539 #if CONF_XSP
540 class XSPScaler : public Scaler
541 {
542         SDL_Surface* m_screen;
543         SDL_Rect m_area;
544         SDL_Rect m_real_area;
545         bool m_should_enable, m_enabled; // Try to avoid flicker.
546
547         static void setDoubling(bool enable)
548         {
549                 SDL_SysWMinfo wminfo;
550                 SDL_VERSION(&wminfo.version);
551                 if ( SDL_GetWMInfo(&wminfo) ) {
552                         Display *dpy = wminfo.info.x11.display;
553                         XSPSetPixelDoubling(dpy, 0, enable ? 1 : 0);
554                         XFlush(dpy);
555                 }
556         }
557
558         XSPScaler(SDL_Surface* screen, int w, int h)
559         : m_screen(screen), m_should_enable(true), m_enabled(false)
560         {
561                 centerRectangle(m_area, GUI.Width, GUI.Height,
562                         w * 2, h * 2);
563
564                 m_real_area.x = m_area.x;
565                 m_real_area.y = m_area.y;
566                 m_real_area.w = m_area.w / 2;
567                 m_real_area.h = m_area.h / 2;
568         };
569 public:
570         ~XSPScaler()
571         {
572                 if (m_enabled) setDoubling(false);
573         };
574
575         class Factory : public ScalerFactory
576         {
577                 const char * getName() const
578                 {
579                         return "xsp";
580                 }
581
582                 bool canEnable(int bpp, int w, int h) const
583                 {
584                         return w * 2 < GUI.Width && h * 2 < GUI.Height;
585                 };
586
587                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
588                 {
589                         return new XSPScaler(screen, w, h);
590                 }
591         };
592
593         static const Factory factory;
594
595         const char * getName() const
596         {
597                 return "XSP pixel doubling";
598         }
599
600         uint8* getDrawBuffer() const
601         {
602                 const int Bpp = screen->format->BitsPerPixel / 8;
603                 const int pitch = screen->pitch;
604                 return ((uint8*) screen->pixels)
605                         + (m_area.x * Bpp)
606                         + (m_area.y * pitch);
607         };
608
609         unsigned int getDrawBufferPitch() const
610         {
611                 return screen->pitch;
612         };
613
614         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
615                                                         unsigned short & w, unsigned short & h) const
616         {
617                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
618         };
619
620         void getRatio(float & x, float & y) const
621         {
622                 x = 2.0f; y = 2.0f;
623         };
624
625         void prepare() 
626         {
627                 if (m_should_enable && !m_enabled) {
628                         setDoubling(true);
629                         m_enabled = true;
630                 }
631         };
632
633         void finish()
634         {
635                 SDL_UpdateRects(m_screen, 1, &m_real_area);
636         };
637
638         void pause()
639         {
640                 m_should_enable = false;
641                 if (m_enabled) {
642                         setDoubling(false);
643                         m_enabled = false;
644                 }
645         };
646
647         void resume()
648         {
649                 m_should_enable = true; // Will enable later
650         };
651
652         bool filter(const SDL_Event& event)
653         {
654                 if (event.type == SDL_ACTIVEEVENT &&
655                   (event.active.state & SDL_APPINPUTFOCUS)) {
656                         if (event.active.gain) {
657                                 resume();
658                         } else {
659                                 pause();
660                         }
661
662                         return true;
663                 }
664
665                 return false;
666         };
667 };
668 const XSPScaler::Factory XSPScaler::factory;
669 #endif
670
671 static const ScalerFactory* scalers[] = {
672 /* More useful scalers come first */
673 #if CONF_XSP
674         &XSPScaler::factory,                    /* n8x0 pixel doubling */
675 #endif
676 #ifdef __arm__
677         &ARMScaler::factory,                    /* arm 2x scaling */
678 #endif
679 #if CONF_HD
680         &HAASquareScaler::factory,              /* n900 animation actor scaling */
681 #endif
682         &SWScaler::factory,                             /* soft 2x scaling */
683         &DummyScaler::factory,                  /* failsafe */
684 /* The following scalers will not be automatically enabled, no matter what */
685 #if CONF_HD
686         &HAAFillScaler::factory,
687 #endif
688 };
689
690 /* Entry point functions */
691
692 const ScalerFactory* searchForScaler(int bpp, int w, int h)
693 {
694         const int n = sizeof(scalers) / sizeof(ScalerFactory*);
695         int i;
696
697         if (Config.scaler && strcasecmp(Config.scaler, "help") == 0 ) {
698                 // List scalers
699                 printf("Scalers list:\n");
700                 for (i = 0; i < n; i++) {
701                         printf(" %s\n", scalers[i]->getName());
702                 }
703                 DIE("End of scalers list");
704         } else if (Config.scaler && strcasecmp(Config.scaler, "auto") != 0 ) {
705                 // We prefer a specific scaler
706                 for (i = 0; i < n; i++) {
707                         if (strcasecmp(scalers[i]->getName(), Config.scaler) == 0) {
708                                 if (scalers[i]->canEnable(bpp, w, h)) {
709                                         // Found the scaler selected by the user, and we can use it.
710                                         return scalers[i];
711                                 } else {
712                                         fprintf(stderr,
713                                                 "Selected scaler '%s' cannot be enabled in this mode\n",
714                                                 Config.scaler);
715                                         break; // Fallback to another scaler.
716                                 }
717                         }
718                 }
719                 if (i == n) {
720                         fprintf(stderr, "Selected scaler '%s' does not exist\n",
721                                 Config.scaler);
722                 }
723         }
724
725         // Just try them all now, in a buildtime set priority.
726         for (i = 0; i < n; i++) {
727                 if (scalers[i]->canEnable(bpp, w, h)) {
728                         return scalers[i];
729                 }
730         }
731
732         DIE("Can't use any scaler; this shouldn't happen.");
733 }
734