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