platform, add resize handling and integer scaling for sdl window mode
authorkub <derkub@gmail.com>
Tue, 10 Sep 2024 21:07:20 +0000 (23:07 +0200)
committerkub <derkub@gmail.com>
Thu, 12 Sep 2024 19:40:44 +0000 (21:40 +0200)
platform/common/menu_pico.c
platform/common/plat_sdl.c

index 5bf67b7..0900bf8 100644 (file)
@@ -88,7 +88,6 @@ static void make_bg(int no_scale, int from_screen)
        int h = g_menubg_src_h ? g_menubg_src_h : g_screen_height;
        int pp = g_menubg_src_pp ? g_menubg_src_pp : g_screen_ppitch;
        short *dst;
-       int x, y;
 
        if (from_screen) {
                src = g_screen_ptr;
@@ -104,24 +103,24 @@ static void make_bg(int no_scale, int from_screen)
        if (!no_scale && g_menuscreen_w / w >= 2 && g_menuscreen_h / h >= 2)
        {
                int xf = g_menuscreen_w / w, yf = g_menuscreen_h / h;
-               int f = no_scale ? 1 : xf < yf ? xf : yf;
-               int xs = f * w, ys = f * h;
-               unsigned short t;
-               int i, j, k, l;
+               int f = no_scale ? 1 : xf < yf ? xf : yf, xs = f * w, ys = f * h;
+               int x = (g_menuscreen_w - xs)/2, y = (g_menuscreen_h - ys)/2;
+               uint16_t *p = (uint16_t *)g_menubg_ptr;
+               uint16_t *q = (uint16_t *)src;
 
-               x = (g_menuscreen_w - xs)/2, y = (g_menuscreen_h - ys)/2;
-               dst = (short *)g_menubg_ptr + y * g_menuscreen_w + x;
+               int i, j, k, l;
+               p += y * g_menuscreen_pp + x;
                for (i = 0; i < h; i++) {
-                       for (j = 0; j < w; j++, src++) {
-                               t = (PXMASKH(*src,1)>>1) - (PXMASKH(*src,3)>>3);
+                       for (j = 0; j < w; j++, q++) {
+                               uint16_t t = (PXMASKH(*q,1)>>1) - (PXMASKH(*q,3)>>3);
                                for (l = 0; l < f; l++)
-                                       *dst++ = t;
+                                       *p++ = t;
                        }
-                       src += pp - w;
-                       dst += g_menuscreen_w - xs;
+                       p += g_menuscreen_pp - xs;
+                       q += pp - w;
                        for (k = 1; k < f; k++) {
-                               memcpy(dst, dst-g_menuscreen_w, g_menuscreen_w*2);
-                               dst += g_menuscreen_w;
+                               memcpy(p, p-g_menuscreen_pp, g_menuscreen_w*2);
+                               p += g_menuscreen_pp;
                        }
                }
                return;
index cdcaee7..8ba3a8c 100644 (file)
@@ -174,6 +174,30 @@ void rgb565_to_uyvy(void *d, const void *s, int w, int h, int pitch, int dpitch,
   }
 }
 
+void copy_intscale(void *dst, int w, int h, int pp, void *src, int sw, int sh, int spp)
+{
+       int xf = w / sw, yf = h / sh;
+       int f = xf < yf ? xf : yf, xs = f * sw, ys = f * sh;
+       int x = (w - xs)/2, y = (h - ys)/2;
+       uint16_t *p = (uint16_t *)dst;
+       uint16_t *q = (uint16_t *)src;
+
+       // copy 16bit image with scaling by an integer factor
+       int i, j, k, l;
+       p += y * pp + x;
+       for (i = 0; i < sh; i++) {
+               for (j = 0; j < sw; j++, q++)
+                       for (l = 0; l < f; l++)
+                               *p++ = *q;
+               p += pp - xs;
+               q += spp - sw;
+               for (k = 1; k < f; k++) {
+                       memcpy(p, p-pp, w*2);
+                       p += pp;
+               }
+       }
+}
+
 static int clear_buf_cnt, clear_stat_cnt;
 
 static void resize_buffers(void)
@@ -188,22 +212,39 @@ static void resize_buffers(void)
 
 void plat_video_set_size(int w, int h)
 {
+       if ((plat_sdl_overlay || plat_sdl_gl_active) &&
+            (w != g_screen_width || h != g_screen_height)) {
+               // scale to the window, but mind aspect ratio (scaled to 4:3)
+               if (g_menuscreen_w * /*h*/w*3/4 >= g_menuscreen_h * w)
+                       w = (w * 3 * g_menuscreen_w/g_menuscreen_h)/4 & ~1;
+               else
+                       h = (h * 4 * g_menuscreen_h/g_menuscreen_w)/3 & ~1;
+       }
+
        if (area.w != w || area.h != h) {
                area = (struct area) { w, h };
-               if (plat_sdl_change_video_mode(w, h, 0) < 0) {
-                       // failed, revert to original resolution
-                       area = (struct area) { g_screen_width,g_screen_height };
-                       plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
+
+               if (plat_sdl_overlay || plat_sdl_gl_active || !plat_sdl_is_windowed()) {
+                       // create surface for overlays, or try using a hw scaler
+                       if (plat_sdl_change_video_mode(w, h, 0) < 0) {
+                               // failed, revert to original resolution
+                               area = (struct area) { g_screen_width,g_screen_height };
+                               plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
+                       }
                }
-               if (!plat_sdl_overlay && !plat_sdl_gl_active) {
+               if (plat_sdl_overlay || plat_sdl_gl_active ||
+                   plat_sdl_screen->w >= 320*2 || plat_sdl_screen->h >= 240*2) {
+                       // use shadow buffer for overlays and sw integer scaling
+                       g_screen_width = area.w;
+                       g_screen_height = area.h;
+                       g_screen_ppitch = area.w;
+                       g_screen_ptr = shadow_fb;
+               } else {
+                       // unscaled SDL window buffer can be used directly
                        g_screen_width = plat_sdl_screen->w;
                        g_screen_height = plat_sdl_screen->h;
                        g_screen_ppitch = plat_sdl_screen->pitch/2;
                        g_screen_ptr = plat_sdl_screen->pixels;
-               } else {
-                       g_screen_width = w;
-                       g_screen_height = h;
-                       g_screen_ppitch = w;
                }
        }
 }
@@ -236,15 +277,24 @@ void plat_video_flip(void)
                gl_flip(shadow_fb, g_screen_ppitch, g_screen_height);
        }
        else {
-               if (SDL_MUSTLOCK(plat_sdl_screen)) {
+               int copy = g_screen_ptr != plat_sdl_screen->pixels;
+               if (copy)
+                       copy_intscale(plat_sdl_screen->pixels, plat_sdl_screen->w,
+                               plat_sdl_screen->h, plat_sdl_screen->pitch/2,
+                               shadow_fb, area.w, area.h, area.w);
+
+               if (SDL_MUSTLOCK(plat_sdl_screen))
                        SDL_UnlockSurface(plat_sdl_screen);
-                       SDL_Flip(plat_sdl_screen);
+               SDL_Flip(plat_sdl_screen);
+               if (SDL_MUSTLOCK(plat_sdl_screen))
                        SDL_LockSurface(plat_sdl_screen);
-               } else
-                       SDL_Flip(plat_sdl_screen);
-               g_screen_ppitch = plat_sdl_screen->pitch/2;
-               g_screen_ptr = plat_sdl_screen->pixels;
-               plat_video_set_buffer(g_screen_ptr);
+
+               if (!copy) {
+                       g_screen_ppitch = plat_sdl_screen->pitch/2;
+                       g_screen_ptr = plat_sdl_screen->pixels;
+                       plat_video_set_buffer(g_screen_ptr);
+               }
+
                if (clear_buf_cnt) {
                        memset(g_screen_ptr, 0, plat_sdl_screen->pitch*plat_sdl_screen->h);
                        clear_buf_cnt--;
@@ -277,7 +327,8 @@ void plat_video_clear_status(void)
 
 void plat_video_clear_buffers(void)
 {
-       if (plat_sdl_overlay || plat_sdl_gl_active)
+       if (plat_sdl_overlay || plat_sdl_gl_active ||
+           plat_sdl_screen->w >= 320*2 || plat_sdl_screen->h >= 240*2)
                memset(shadow_fb, 0, g_menuscreen_w * g_menuscreen_h * 2);
        else {
                memset(g_screen_ptr, 0, plat_sdl_screen->pitch*plat_sdl_screen->h);
@@ -366,40 +417,48 @@ void plat_video_loop_prepare(void)
                }
                g_screen_ppitch = g_screen_width;
                g_screen_ptr = shadow_fb;
+               plat_video_set_size(g_screen_width, g_screen_height);
        }
        else {
-               g_screen_width = plat_sdl_screen->w;
-               g_screen_height = plat_sdl_screen->h;
-               g_screen_ppitch = plat_sdl_screen->pitch/2;
+               if (plat_sdl_is_windowed() &&
+                    (plat_sdl_screen->w >= 320*2 || plat_sdl_screen->h >= 240*2)) {
+                       // shadow buffer for integer scaling
+                       g_screen_width = 320;
+                       g_screen_height = 240;
+                       g_screen_ppitch = 320;
+                       g_screen_ptr = shadow_fb;
+               } else {
+                       // no scaling needed, use screen buffer directly
+                       g_screen_width = plat_sdl_screen->w;
+                       g_screen_height = plat_sdl_screen->h;
+                       g_screen_ppitch = plat_sdl_screen->pitch/2;
+                       g_screen_ptr = plat_sdl_screen->pixels;
+               }
+               plat_video_set_size(g_screen_width, g_screen_height);
+
                if (SDL_MUSTLOCK(plat_sdl_screen))
                        SDL_LockSurface(plat_sdl_screen);
-               g_screen_ptr = plat_sdl_screen->pixels;
        }
 
-       plat_video_set_size(g_screen_width, g_screen_height);
        plat_video_set_buffer(g_screen_ptr);
 }
 
 static void plat_sdl_resize(int w, int h)
 {
        // take over new settings
-       if (plat_sdl_screen->w != area.w || plat_sdl_screen->h != area.h) {
 #if defined(__OPENDINGUX__)
-        if (currentConfig.vscaling != EOPT_SCALE_HW &&
-                plat_sdl_screen->w == 320 &&
-                plat_sdl_screen->h == 480) {
-                   g_menuscreen_h = 240;
-                   g_menuscreen_w = 320;
-
-        } else
+       if (currentConfig.vscaling != EOPT_SCALE_HW &&
+           plat_sdl_screen->w == 320 && plat_sdl_screen->h == 480) {
+               g_menuscreen_h = 240;
+               g_menuscreen_w = 320;
+       } else
 #endif
-        {
-                   g_menuscreen_h = plat_sdl_screen->h;
-                   g_menuscreen_w = plat_sdl_screen->w;
-        }
-        resize_buffers();
-               rendstatus_old = -1;
+       {
+               g_menuscreen_h = plat_sdl_screen->h;
+               g_menuscreen_w = plat_sdl_screen->w;
        }
+       resize_buffers();
+       rendstatus_old = -1;
 }
 
 static void plat_sdl_quit(void)