single zeemote support
[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 protected:
377         HAAScalerBase(SDL_Surface* screen, int w, int h, float r_x, float r_y)
378         : m_screen(screen), m_w(w), m_h(h),
379          m_Bpp(m_screen->format->BitsPerPixel / 8),
380          ratio_x(r_x), ratio_y(r_y)
381         {
382                 const bool fullscreen = m_screen->flags & SDL_FULLSCREEN;
383                 centerRectangle(m_area, GUI.Width, GUI.Height, w * r_x, h * r_y);
384
385                 // Clear the SDL screen with black, just in case it gets drawn.
386                 SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
387
388                 HAA_Init(fullscreen ? SDL_FULLSCREEN : 0);
389                 actor = HAA_CreateActor(0, m_w, m_h, m_screen->format->BitsPerPixel);
390                 HAA_SetPosition(actor, m_area.x, m_area.y + (fullscreen ? 0 : 60));
391                 HAA_SetScale(actor, r_x, r_y);
392                 HAA_Show(actor);
393         }
394
395 public:
396         virtual ~HAAScalerBase()
397         {
398                 HAA_FreeActor(actor);
399                 HAA_Quit();
400         };
401
402         uint8* getDrawBuffer() const
403         {
404                 return reinterpret_cast<uint8*>(actor->surface->pixels);
405         };
406
407         unsigned int getDrawBufferPitch() const
408         {
409                 return actor->surface->pitch;
410         };
411
412         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
413                                                         unsigned short & w, unsigned short & h) const
414         {
415                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
416         };
417
418         void getRatio(float & x, float & y) const
419         {
420                 x = ratio_x; y = ratio_y;
421         };
422
423         void prepare()
424         {
425
426         };
427
428         void finish()
429         {
430                 HAA_Flip(actor);
431         };
432
433         void pause() { };
434         void resume() { };
435
436         bool filter(const SDL_Event& event)
437         {
438                 return HAA_FilterEvent(&event) == 0;
439         };
440 };
441
442 class HAAFillScaler : public HAAScalerBase
443 {
444         HAAFillScaler(SDL_Surface* screen, int w, int h)
445         : HAAScalerBase(screen, w, h,
446                 GUI.Width / (float)w, GUI.Height / (float)h)
447         {
448         }
449
450 public:
451         class Factory : public ScalerFactory
452         {
453                 const char * getName() const
454                 {
455                         return "haafill";
456                 }
457
458                 bool canEnable(int bpp, int w, int h) const
459                 {
460                         return true;
461                 }
462
463                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
464                 {
465                         return new HAAFillScaler(screen, w, h-20);
466                 }
467         };
468
469         static const Factory factory;
470
471         const char * getName() const
472         {
473                 return "HAA fill screen scaling";
474         }
475 };
476 const HAAFillScaler::Factory HAAFillScaler::factory;
477
478 class HAASquareScaler : public HAAScalerBase
479 {
480         HAASquareScaler(SDL_Surface* screen, int w, int h, float ratio)
481         : HAAScalerBase(screen, w, h, ratio, ratio)
482         {
483         }
484
485 public:
486         class Factory : public ScalerFactory
487         {
488                 const char * getName() const
489                 {
490                         return "haasq";
491                 }
492
493                 bool canEnable(int bpp, int w, int h) const
494                 {
495                         return true;
496                 }
497
498                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
499                 {
500                         return new HAASquareScaler(screen, w, h,
501                                 fminf(GUI.Width / (float)w, GUI.Height / (float)h));
502                 }
503         };
504
505         static const Factory factory;
506
507         const char * getName() const
508         {
509                 return "HAA square screen scaling";
510         }
511 };
512 const HAASquareScaler::Factory HAASquareScaler::factory;
513
514 #endif /* CONF_HD */
515
516 #if CONF_XSP
517 class XSPScaler : public Scaler
518 {
519         SDL_Surface* m_screen;
520         SDL_Rect m_area;
521         SDL_Rect m_real_area;
522         bool m_should_enable, m_enabled; // Try to avoid flicker.
523
524         static void setDoubling(bool enable)
525         {
526                 SDL_SysWMinfo wminfo;
527                 SDL_VERSION(&wminfo.version);
528                 if ( SDL_GetWMInfo(&wminfo) ) {
529                         Display *dpy = wminfo.info.x11.display;
530                         XSPSetPixelDoubling(dpy, 0, enable ? 1 : 0);
531                         XFlush(dpy);
532                 }
533         }
534
535         XSPScaler(SDL_Surface* screen, int w, int h)
536         : m_screen(screen), m_should_enable(true), m_enabled(false)
537         {
538                 centerRectangle(m_area, GUI.Width, GUI.Height,
539                         w * 2, h * 2);
540
541                 m_real_area.x = m_area.x;
542                 m_real_area.y = m_area.y;
543                 m_real_area.w = m_area.w / 2;
544                 m_real_area.h = m_area.h / 2;
545         };
546 public:
547         ~XSPScaler()
548         {
549                 if (m_enabled) setDoubling(false);
550         };
551
552         class Factory : public ScalerFactory
553         {
554                 const char * getName() const
555                 {
556                         return "xsp";
557                 }
558
559                 bool canEnable(int bpp, int w, int h) const
560                 {
561                         return w * 2 < GUI.Width && h * 2 < GUI.Height;
562                 };
563
564                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
565                 {
566                         return new XSPScaler(screen, w, h);
567                 }
568         };
569
570         static const Factory factory;
571
572         const char * getName() const
573         {
574                 return "XSP pixel doubling";
575         }
576
577         uint8* getDrawBuffer() const
578         {
579                 const int Bpp = screen->format->BitsPerPixel / 8;
580                 const int pitch = screen->pitch;
581                 return ((uint8*) screen->pixels)
582                         + (m_area.x * Bpp)
583                         + (m_area.y * pitch);
584         };
585
586         unsigned int getDrawBufferPitch() const
587         {
588                 return screen->pitch;
589         };
590
591         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
592                                                         unsigned short & w, unsigned short & h) const
593         {
594                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
595         };
596
597         void getRatio(float & x, float & y) const
598         {
599                 x = 2.0f; y = 2.0f;
600         };
601
602         void prepare() 
603         {
604                 if (m_should_enable && !m_enabled) {
605                         setDoubling(true);
606                         m_enabled = true;
607                 }
608         };
609
610         void finish()
611         {
612                 SDL_UpdateRects(m_screen, 1, &m_real_area);
613         };
614
615         void pause()
616         {
617                 m_should_enable = false;
618                 if (m_enabled) {
619                         setDoubling(false);
620                         m_enabled = false;
621                 }
622         };
623
624         void resume()
625         {
626                 m_should_enable = true; // Will enable later
627         };
628
629         bool filter(const SDL_Event& event)
630         {
631                 if (event.type == SDL_ACTIVEEVENT &&
632                   (event.active.state & SDL_APPINPUTFOCUS)) {
633                         if (event.active.gain) {
634                                 resume();
635                         } else {
636                                 pause();
637                         }
638
639                         return true;
640                 }
641
642                 return false;
643         };
644 };
645 const XSPScaler::Factory XSPScaler::factory;
646 #endif
647
648 static const ScalerFactory* scalers[] = {
649 /* More useful scalers come first */
650 #if CONF_XSP
651         &XSPScaler::factory,                    /* n8x0 pixel doubling */
652 #endif
653 #ifdef __arm__
654         &ARMScaler::factory,                    /* arm 2x scaling */
655 #endif
656 #if CONF_HD
657         &HAASquareScaler::factory,              /* n900 animation actor scaling */
658 #endif
659         &SWScaler::factory,                             /* soft 2x scaling */
660         &DummyScaler::factory,                  /* failsafe */
661 /* The following scalers will not be automatically enabled, no matter what */
662 #if CONF_HD
663         &HAAFillScaler::factory,
664 #endif
665 };
666
667 /* Entry point functions */
668
669 const ScalerFactory* searchForScaler(int bpp, int w, int h)
670 {
671         const int n = sizeof(scalers) / sizeof(ScalerFactory*);
672         int i;
673
674         if (Config.scaler && strcasecmp(Config.scaler, "help") == 0 ) {
675                 // List scalers
676                 printf("Scalers list:\n");
677                 for (i = 0; i < n; i++) {
678                         printf(" %s\n", scalers[i]->getName());
679                 }
680                 DIE("End of scalers list");
681         } else if (Config.scaler && strcasecmp(Config.scaler, "auto") != 0 ) {
682                 // We prefer a specific scaler
683                 for (i = 0; i < n; i++) {
684                         if (strcasecmp(scalers[i]->getName(), Config.scaler) == 0) {
685                                 if (scalers[i]->canEnable(bpp, w, h)) {
686                                         // Found the scaler selected by the user, and we can use it.
687                                         return scalers[i];
688                                 } else {
689                                         fprintf(stderr,
690                                                 "Selected scaler '%s' cannot be enabled in this mode\n",
691                                                 Config.scaler);
692                                         break; // Fallback to another scaler.
693                                 }
694                         }
695                 }
696                 if (i == n) {
697                         fprintf(stderr, "Selected scaler '%s' does not exist\n",
698                                 Config.scaler);
699                 }
700         }
701
702         // Just try them all now, in a buildtime set priority.
703         for (i = 0; i < n; i++) {
704                 if (scalers[i]->canEnable(bpp, w, h)) {
705                         return scalers[i];
706                 }
707         }
708
709         DIE("Can't use any scaler; this shouldn't happen.");
710 }
711