diff --git a/Makefile b/Makefile index a06c9bfc2..f8dc5065d 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ OBJS+=$(addprefix audio/, $(AUDIO_OBJS)) OBJS+=keymaps.o ifdef CONFIG_SDL -OBJS+=sdl.o x_keymap.o +OBJS+=sdl.o sdl_zoom.o x_keymap.o endif ifdef CONFIG_CURSES OBJS+=curses.o @@ -209,7 +209,9 @@ cocoa.o: cocoa.m keymaps.o: keymaps.c keymaps.h -sdl.o: sdl.c keymaps.h sdl_keysym.h +sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h + +sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h sdl.o audio/sdlaudio.o baum.o: CFLAGS += $(SDL_CFLAGS) diff --git a/console.h b/console.h index 417217582..35183399f 100644 --- a/console.h +++ b/console.h @@ -75,6 +75,7 @@ void kbd_put_keysym(int keysym); #define QEMU_BIG_ENDIAN_FLAG 0x01 #define QEMU_ALLOCATED_FLAG 0x02 +#define QEMU_REALPIXELS_FLAG 0x04 struct PixelFormat { uint8_t bits_per_pixel; @@ -172,7 +173,8 @@ static inline int is_surface_bgr(DisplaySurface *surface) static inline int is_buffer_shared(DisplaySurface *surface) { - return (!(surface->flags & QEMU_ALLOCATED_FLAG)); + return (!(surface->flags & QEMU_ALLOCATED_FLAG) && + !(surface->flags & QEMU_REALPIXELS_FLAG)); } static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl) diff --git a/sdl.c b/sdl.c index 178b5532b..d81399e51 100644 --- a/sdl.c +++ b/sdl.c @@ -32,6 +32,7 @@ #include "console.h" #include "sysemu.h" #include "x_keymap.h" +#include "sdl_zoom.h" static DisplayChangeListener *dcl; static SDL_Surface *real_screen; @@ -54,20 +55,29 @@ static int guest_cursor = 0; static int guest_x, guest_y; static SDL_Cursor *guest_sprite = 0; static uint8_t allocator; -static uint8_t hostbpp; +static SDL_PixelFormat host_format; +static int scaling_active = 0; static void sdl_update(DisplayState *ds, int x, int y, int w, int h) { // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); + SDL_Rect rec; + rec.x = x; + rec.y = y; + rec.w = w; + rec.h = h; + if (guest_screen) { - SDL_Rect rec; - rec.x = x; - rec.y = y; - rec.w = w; - rec.h = h; - SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); - } - SDL_UpdateRect(real_screen, x, y, w, h); + if (!scaling_active) { + SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); + } else { + if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) { + fprintf(stderr, "Zoom blit failed\n"); + exit(1); + } + } + } + SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h); } static void sdl_setdata(DisplayState *ds) @@ -92,7 +102,7 @@ static void do_sdl_resize(int new_width, int new_height, int bpp) // printf("resizing to %d %d\n", w, h); - flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL; + flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE; if (gui_fullscreen) flags |= SDL_FULLSCREEN; if (gui_noframe) @@ -110,7 +120,10 @@ static void do_sdl_resize(int new_width, int new_height, int bpp) static void sdl_resize(DisplayState *ds) { if (!allocator) { - do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0); + if (!scaling_active) + do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0); + else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds)) + do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds)); sdl_setdata(ds); } else { if (guest_screen != NULL) { @@ -163,8 +176,26 @@ static DisplaySurface* sdl_create_displaysurface(int width, int height) surface->width = width; surface->height = height; + + if (scaling_active) { + if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) { + surface->linesize = width * 4; + surface->pf = qemu_default_pixelformat(32); + } else { + surface->linesize = width * host_format.BytesPerPixel; + surface->pf = sdl_to_qemu_pixelformat(&host_format); + } +#ifdef WORDS_BIGENDIAN + surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_ALLOCATED_FLAG; +#endif + surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); - if (hostbpp == 16) + return surface; + } + + if (host_format.BitsPerPixel == 16) do_sdl_resize(width, height, 16); else do_sdl_resize(width, height, 32); @@ -174,9 +205,9 @@ static DisplaySurface* sdl_create_displaysurface(int width, int height) surface->data = real_screen->pixels; #ifdef WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; + surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG; #else - surface->flags = QEMU_ALLOCATED_FLAG; + surface->flags = QEMU_REALPIXELS_FLAG; #endif allocator = 1; @@ -188,6 +219,9 @@ static void sdl_free_displaysurface(DisplaySurface *surface) allocator = 0; if (surface == NULL) return; + + if (surface->flags & QEMU_ALLOCATED_FLAG) + qemu_free(surface->data); qemu_free(surface); } @@ -482,8 +516,8 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state static void toggle_full_screen(DisplayState *ds) { gui_fullscreen = !gui_fullscreen; - do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel); if (gui_fullscreen) { + scaling_active = 0; gui_saved_grab = gui_grab; sdl_grab_start(); } else { @@ -675,6 +709,18 @@ static void sdl_refresh(DisplayState *ds) } } break; + case SDL_VIDEORESIZE: + { + SDL_ResizeEvent *rev = &ev->resize; + int bpp = real_screen->format->BitsPerPixel; + if (bpp != 16 && bpp != 32) + bpp = 32; + do_sdl_resize(rev->w, rev->h, bpp); + scaling_active = 1; + vga_hw_invalidate(); + vga_hw_update(); + break; + } default: break; } @@ -783,7 +829,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) exit(1); } vi = SDL_GetVideoInfo(); - hostbpp = vi->vfmt->BitsPerPixel; + host_format = *(vi->vfmt); dcl = qemu_mallocz(sizeof(DisplayChangeListener)); dcl->dpy_update = sdl_update; diff --git a/sdl_zoom.c b/sdl_zoom.c new file mode 100644 index 000000000..56d3604fc --- /dev/null +++ b/sdl_zoom.c @@ -0,0 +1,94 @@ +/* + * SDL_zoom - surface scaling + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. + * Modifications by Stefano Stabellini. + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#include "sdl_zoom.h" +#include "osdep.h" +#include + +static int sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect); +static int sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect); + +#define BPP 32 +#include "sdl_zoom_template.h" +#undef BPP +#define BPP 16 +#include "sdl_zoom_template.h" +#undef BPP + +int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, int smooth, + SDL_Rect *in_rect) +{ + SDL_Rect zoom, src_rect; + int extra; + + /* Grow the size of the modified rectangle to avoid edge artefacts */ + src_rect.x = (in_rect->x > 0) ? (in_rect->x - 1) : 0; + src_rect.y = (in_rect->y > 0) ? (in_rect->y - 1) : 0; + + src_rect.w = in_rect->w + 1; + if (src_rect.x + src_rect.w > src_sfc->w) + src_rect.w = src_sfc->w - src_rect.x; + + src_rect.h = in_rect->h + 1; + if (src_rect.y + src_rect.h > src_sfc->h) + src_rect.h = src_sfc->h - src_rect.y; + + /* (x,y) : round down */ + zoom.x = (int)(((float)(src_rect.x * dst_sfc->w)) / (float)(src_sfc->w)); + zoom.y = (int)(((float)(src_rect.y * dst_sfc->h)) / (float)(src_sfc->h)); + + /* (w,h) : round up */ + zoom.w = (int)( ((double)((src_rect.w * dst_sfc->w) + (src_sfc->w - 1))) / + (double)(src_sfc->w)); + + zoom.h = (int)( ((double)((src_rect.h * dst_sfc->h) + (src_sfc->h - 1))) / + (double)(src_sfc->h)); + + /* Account for any (x,y) rounding by adding one-source-pixel's worth + * of destination pixels and then edge checking. + */ + + extra = ((dst_sfc->w-1) / src_sfc->w) + 1; + + if ((zoom.x + zoom.w) < (dst_sfc->w - extra)) + zoom.w += extra; + else + zoom.w = dst_sfc->w - zoom.x; + + extra = ((dst_sfc->h-1) / src_sfc->h) + 1; + + if ((zoom.y + zoom.h) < (dst_sfc->h - extra)) + zoom.h += extra; + else + zoom.h = dst_sfc->h - zoom.y; + + /* The rectangle (zoom.x, zoom.y, zoom.w, zoom.h) is the area on the + * destination surface that needs to be updated. + */ + if (src_sfc->format->BitsPerPixel == 32) + sdl_zoom_rgb32(src_sfc, dst_sfc, smooth, &zoom); + else if (src_sfc->format->BitsPerPixel == 16) + sdl_zoom_rgb16(src_sfc, dst_sfc, smooth, &zoom); + else { + fprintf(stderr, "pixel format not supported\n"); + return -1; + } + + /* Return the rectangle of the update to the caller */ + *in_rect = zoom; + + return 0; +} + diff --git a/sdl_zoom.h b/sdl_zoom.h new file mode 100644 index 000000000..33dc63408 --- /dev/null +++ b/sdl_zoom.h @@ -0,0 +1,25 @@ +/* + * SDL_zoom - surface scaling + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. + * Modifications by Stefano Stabellini. + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef _SDL_zoom_h +#define _SDL_zoom_h + +#include + +#define SMOOTHING_OFF 0 +#define SMOOTHING_ON 1 + +int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, + int smooth, SDL_Rect *src_rect); + +#endif /* _SDL_zoom_h */ diff --git a/sdl_zoom_template.h b/sdl_zoom_template.h new file mode 100644 index 000000000..64bbca849 --- /dev/null +++ b/sdl_zoom_template.h @@ -0,0 +1,225 @@ +/* + * SDL_zoom_template - surface scaling + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. + * Modifications by Stefano Stabellini. + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#if BPP == 16 +#define SDL_TYPE Uint16 +#elif BPP == 32 +#define SDL_TYPE Uint32 +#else +#error unsupport depth +#endif + +/* + * Simple helper functions to make the code looks nicer + * + * Assume spf = source SDL_PixelFormat + * dpf = dest SDL_PixelFormat + * + */ +#define getRed(color) (((color) & spf->Rmask) >> spf->Rshift) +#define getGreen(color) (((color) & spf->Gmask) >> spf->Gshift) +#define getBlue(color) (((color) & spf->Bmask) >> spf->Bshift) +#define getAlpha(color) (((color) & spf->Amask) >> spf->Ashift) + +#define setRed(r, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Rmask))) + \ + (((r) & (dpf->Rmask >> dpf->Rshift)) << dpf->Rshift); \ +} while (0); + +#define setGreen(g, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Gmask))) + \ + (((g) & (dpf->Gmask >> dpf->Gshift)) << dpf->Gshift); \ +} while (0); + +#define setBlue(b, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Bmask))) + \ + (((b) & (dpf->Bmask >> dpf->Bshift)) << dpf->Bshift); \ +} while (0); + +#define setAlpha(a, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Amask))) + \ + (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \ +} while (0); + +static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect) +{ + int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump; + SDL_TYPE *c00, *c01, *c10, *c11, *sp, *csp, *dp; + int d_gap; + SDL_PixelFormat *spf = src->format; + SDL_PixelFormat *dpf = dst->format; + + if (smooth) { + /* For interpolation: assume source dimension is one pixel. + * Smaller here to avoid overflow on right and bottom edge. + */ + sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); + sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); + } else { + sx = (int) (65536.0 * (float) src->w / (float) dst->w); + sy = (int) (65536.0 * (float) src->h / (float) dst->h); + } + + if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) { + free(sax); + return (-1); + } + + sp = csp = (SDL_TYPE *) src->pixels; + dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch + + dst_rect->x * dst->format->BytesPerPixel); + + csx = 0; + csax = sax; + for (x = 0; x <= dst->w; x++) { + *csax = csx; + csax++; + csx &= 0xffff; + csx += sx; + } + csy = 0; + csay = say; + for (y = 0; y <= dst->h; y++) { + *csay = csy; + csay++; + csy &= 0xffff; + csy += sy; + } + + d_gap = dst->pitch - dst_rect->w * dst->format->BytesPerPixel; + + if (smooth) { + csay = say; + for (y = 0; y < dst_rect->y; y++) { + csay++; + sstep = (*csay >> 16) * src->pitch; + csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); + } + + /* Calculate sstep_jump */ + csax = sax; + sstep_jump = 0; + for (x = 0; x < dst_rect->x; x++) { + csax++; + sstep = (*csax >> 16); + sstep_jump += sstep; + } + + for (y = 0; y < dst_rect->h ; y++) { + /* Setup colour source pointers */ + c00 = csp + sstep_jump; + c01 = c00 + 1; + c10 = (SDL_TYPE *) ((Uint8 *) csp + src->pitch) + sstep_jump; + c11 = c10 + 1; + csax = sax + dst_rect->x; + + for (x = 0; x < dst_rect->w; x++) { + + /* Interpolate colours */ + ex = (*csax & 0xffff); + ey = (*csay & 0xffff); + t1 = ((((getRed(*c01) - getRed(*c00)) * ex) >> 16) + + getRed(*c00)) & (dpf->Rmask >> dpf->Rshift); + t2 = ((((getRed(*c11) - getRed(*c10)) * ex) >> 16) + + getRed(*c10)) & (dpf->Rmask >> dpf->Rshift); + setRed((((t2 - t1) * ey) >> 16) + t1, dp); + t1 = ((((getGreen(*c01) - getGreen(*c00)) * ex) >> 16) + + getGreen(*c00)) & (dpf->Gmask >> dpf->Gshift); + t2 = ((((getGreen(*c11) - getGreen(*c10)) * ex) >> 16) + + getGreen(*c10)) & (dpf->Gmask >> dpf->Gshift); + setGreen((((t2 - t1) * ey) >> 16) + t1, dp); + t1 = ((((getBlue(*c01) - getBlue(*c00)) * ex) >> 16) + + getBlue(*c00)) & (dpf->Bmask >> dpf->Bshift); + t2 = ((((getBlue(*c11) - getBlue(*c10)) * ex) >> 16) + + getBlue(*c10)) & (dpf->Bmask >> dpf->Bshift); + setBlue((((t2 - t1) * ey) >> 16) + t1, dp); + t1 = ((((getAlpha(*c01) - getAlpha(*c00)) * ex) >> 16) + + getAlpha(*c00)) & (dpf->Amask >> dpf->Ashift); + t2 = ((((getAlpha(*c11) - getAlpha(*c10)) * ex) >> 16) + + getAlpha(*c10)) & (dpf->Amask >> dpf->Ashift); + setAlpha((((t2 - t1) * ey) >> 16) + t1, dp); + + /* Advance source pointers */ + csax++; + sstep = (*csax >> 16); + c00 += sstep; + c01 += sstep; + c10 += sstep; + c11 += sstep; + /* Advance destination pointer */ + dp++; + } + /* Advance source pointer */ + csay++; + csp = (SDL_TYPE *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + /* Advance destination pointers */ + dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); + } + + + } else { + csay = say; + + for (y = 0; y < dst_rect->y; y++) { + csay++; + sstep = (*csay >> 16) * src->pitch; + csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); + } + + /* Calculate sstep_jump */ + csax = sax; + sstep_jump = 0; + for (x = 0; x < dst_rect->x; x++) { + csax++; + sstep = (*csax >> 16); + sstep_jump += sstep; + } + + for (y = 0 ; y < dst_rect->h ; y++) { + sp = csp + sstep_jump; + csax = sax + dst_rect->x; + + for (x = 0; x < dst_rect->w; x++) { + + /* Draw */ + *dp = *sp; + + /* Advance source pointers */ + csax++; + sstep = (*csax >> 16); + sp += sstep; + + /* Advance destination pointer */ + dp++; + } + /* Advance source pointers */ + csay++; + sstep = (*csay >> 16) * src->pitch; + csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); + + /* Advance destination pointer */ + dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); + } + } + + free(sax); + free(say); + return (0); +} + +#undef SDL_TYPE +