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