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