diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 54cf13c3ae..9461f8622c 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -3487,3 +3487,4 @@ STR_5150 :Enable debugging tools STR_5151 :, STR_5152 :. STR_5153 :RCT1 colour scheme +STR_5154 :Hardware display diff --git a/src/config.c b/src/config.c index 1ac4757029..664e6a54c0 100644 --- a/src/config.c +++ b/src/config.c @@ -161,6 +161,7 @@ config_property_definition _generalDefinitions[] = { { offsetof(general_configuration, window_height), "window_height", CONFIG_VALUE_TYPE_SINT32, -1, NULL }, { offsetof(general_configuration, window_snap_proximity), "window_snap_proximity", CONFIG_VALUE_TYPE_UINT8, 5, NULL }, { offsetof(general_configuration, window_width), "window_width", CONFIG_VALUE_TYPE_SINT32, -1, NULL }, + { offsetof(general_configuration, hardware_display), "hardware_display", CONFIG_VALUE_TYPE_BOOLEAN, false, NULL }, }; config_property_definition _interfaceDefinitions[] = { diff --git a/src/config.h b/src/config.h index 014aab817e..99c4806f37 100644 --- a/src/config.h +++ b/src/config.h @@ -130,6 +130,7 @@ typedef struct { uint16 language; uint8 window_snap_proximity; uint8 autosave_frequency; + uint8 hardware_display; } general_configuration; typedef struct { diff --git a/src/platform/platform.h b/src/platform/platform.h index e851468819..ee3a146cd1 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -77,6 +77,7 @@ void platform_free(); void platform_update_palette(char* colours, int start_index, int num_colours); void platform_set_fullscreen_mode(int mode); void platform_set_cursor(char cursor); +void platform_refresh_video(); void platform_process_messages(); int platform_scancode_to_rct_keycode(int sdl_key); void platform_start_text_input(char* buffer, int max_length); diff --git a/src/platform/shared.c b/src/platform/shared.c index 0ed2fccaa3..407042dc45 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -45,12 +45,16 @@ int gNumResolutions = 0; resolution *gResolutions = NULL; int gResolutionsAllowAnyAspectRatio = 0; -SDL_Window *gWindow; +SDL_Window *gWindow = NULL; +SDL_Renderer *gRenderer = NULL; +SDL_Texture *gBufferTexture = NULL; +SDL_Color gPalette[256]; static SDL_Surface *_surface; static SDL_Palette *_palette; static int _screenBufferSize; static void *_screenBuffer; +static int _screenBufferPitch; static SDL_Cursor* _cursors[CURSOR_COUNT]; static const int _fullscreen_modes[] = { 0, SDL_WINDOW_FULLSCREEN, SDL_WINDOW_FULLSCREEN_DESKTOP }; static unsigned int _lastGestureTimestamp; @@ -162,28 +166,63 @@ void platform_get_closest_resolution(int inWidth, int inHeight, int *outWidth, i void platform_draw() { - // Lock the surface before setting its pixels - if (SDL_MUSTLOCK(_surface)) - if (SDL_LockSurface(_surface) < 0) { - RCT2_ERROR("locking failed %s", SDL_GetError()); - return; + int width = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16); + int height = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16); + + if (gConfigGeneral.hardware_display) { + void *pixels; + int pitch; + if (SDL_LockTexture(gBufferTexture, NULL, &pixels, &pitch) == 0) { + uint8 *dst = pixels; + uint8 *src = (uint8*)_screenBuffer; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uint8 paletteIndex = *src; + SDL_Color colour = gPalette[paletteIndex]; + + dst[0] = 255; + dst[1] = colour.b; + dst[2] = colour.g; + dst[3] = colour.r; + + src += 1; + dst += 4; + } + + src += _screenBufferPitch - width; + dst += pitch - (width * 4); + } + SDL_UnlockTexture(gBufferTexture); } - // Copy pixels from the virtual screen buffer to the surface - memcpy(_surface->pixels, _screenBuffer, _surface->pitch * _surface->h); + SDL_RenderCopy(gRenderer, gBufferTexture, NULL, NULL); + SDL_RenderPresent(gRenderer); + } else { + // Lock the surface before setting its pixels + if (SDL_MUSTLOCK(_surface)) { + if (SDL_LockSurface(_surface) < 0) { + log_error("locking failed %s", SDL_GetError()); + return; + } + } - // Unlock the surface - if (SDL_MUSTLOCK(_surface)) - SDL_UnlockSurface(_surface); + // Copy pixels from the virtual screen buffer to the surface + memcpy(_surface->pixels, _screenBuffer, _surface->pitch * _surface->h); - // Copy the surface to the window - if (SDL_BlitSurface(_surface, NULL, SDL_GetWindowSurface(gWindow), NULL)) { - RCT2_ERROR("SDL_BlitSurface %s", SDL_GetError()); - exit(1); - } - if (SDL_UpdateWindowSurface(gWindow)) { - RCT2_ERROR("SDL_UpdateWindowSurface %s", SDL_GetError()); - exit(1); + // Unlock the surface + if (SDL_MUSTLOCK(_surface)) + SDL_UnlockSurface(_surface); + + // Copy the surface to the window + if (SDL_BlitSurface(_surface, NULL, SDL_GetWindowSurface(gWindow), NULL)) { + log_fatal("SDL_BlitSurface %s", SDL_GetError()); + exit(1); + } + if (SDL_UpdateWindowSurface(gWindow)) { + log_fatal("SDL_UpdateWindowSurface %s", SDL_GetError()); + exit(1); + } } } @@ -194,25 +233,12 @@ static void platform_resize(int width, int height) void *newScreenBuffer; uint32 flags; - if (_surface != NULL) - SDL_FreeSurface(_surface); - if (_palette != NULL) - SDL_FreePalette(_palette); + RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) = width; + RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16) = height; - _surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0); - _palette = SDL_AllocPalette(256); + platform_refresh_video(); - if (!_surface || !_palette) { - RCT2_ERROR("%p || %p == NULL %s", _surface, _palette, SDL_GetError()); - exit(-1); - } - - if (SDL_SetSurfacePalette(_surface, _palette)) { - RCT2_ERROR("SDL_SetSurfacePalette failed %s", SDL_GetError()); - exit(-1); - } - - newScreenBufferSize = _surface->pitch * _surface->h; + newScreenBufferSize = _screenBufferPitch * height; newScreenBuffer = malloc(newScreenBufferSize); if (_screenBuffer == NULL) { memset(newScreenBuffer, 0, newScreenBufferSize); @@ -226,16 +252,13 @@ static void platform_resize(int width, int height) _screenBuffer = newScreenBuffer; _screenBufferSize = newScreenBufferSize; - RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) = width; - RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16) = height; - screenDPI = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo); screenDPI->bits = _screenBuffer; screenDPI->x = 0; screenDPI->y = 0; screenDPI->width = width; screenDPI->height = height; - screenDPI->pitch = _surface->pitch - _surface->w; + screenDPI->pitch = _screenBufferPitch - width; RCT2_GLOBAL(0x009ABDF0, uint8) = 6; RCT2_GLOBAL(0x009ABDF1, uint8) = 3; @@ -268,27 +291,28 @@ static void platform_resize(int width, int height) void platform_update_palette(char* colours, int start_index, int num_colours) { - SDL_Color base[256]; SDL_Surface *surface; int i; - surface = SDL_GetWindowSurface(gWindow); - if (!surface) { - RCT2_ERROR("SDL_GetWindowSurface failed %s", SDL_GetError()); - exit(1); - } - for (i = 0; i < 256; i++) { - base[i].r = colours[2]; - base[i].g = colours[1]; - base[i].b = colours[0]; - base[i].a = 0; + gPalette[i].r = colours[2]; + gPalette[i].g = colours[1]; + gPalette[i].b = colours[0]; + gPalette[i].a = 0; colours += 4; } - if (SDL_SetPaletteColors(_palette, base, 0, 256)) { - RCT2_ERROR("SDL_SetPaletteColors failed %s", SDL_GetError()); - exit(1); + if (!gConfigGeneral.hardware_display) { + surface = SDL_GetWindowSurface(gWindow); + if (!surface) { + log_fatal("SDL_GetWindowSurface failed %s", SDL_GetError()); + exit(1); + } + + if (_palette != NULL && SDL_SetPaletteColors(_palette, gPalette, 0, 256)) { + log_fatal("SDL_SetPaletteColors failed %s", SDL_GetError()); + exit(1); + } } } @@ -521,10 +545,12 @@ static void platform_create_window() int width, height; if (SDL_Init(SDL_INIT_VIDEO) < 0) { - RCT2_ERROR("SDL_Init %s", SDL_GetError()); + log_fatal("SDL_Init %s", SDL_GetError()); exit(-1); } + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0); + platform_load_cursors(); RCT2_CALLPROC_EBPSAFE(0x0068371D); @@ -711,4 +737,41 @@ int platform_get_cursor_pos(int* x, int* y) GetCursorPos(&point); *x = point.x; *y = point.y; +} + +void platform_refresh_video() +{ + int width = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16); + int height = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, sint16); + + if (gConfigGeneral.hardware_display) { + if (gRenderer == NULL) + gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED); + + if (gBufferTexture != NULL) + SDL_DestroyTexture(gBufferTexture); + + gBufferTexture = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, width, height); + _screenBufferPitch = width; + } else { + if (_surface != NULL) + SDL_FreeSurface(_surface); + if (_palette != NULL) + SDL_FreePalette(_palette); + + _surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0); + _palette = SDL_AllocPalette(256); + + if (!_surface || !_palette) { + log_fatal("%p || %p == NULL %s", _surface, _palette, SDL_GetError()); + exit(-1); + } + + if (SDL_SetSurfacePalette(_surface, _palette)) { + log_fatal("SDL_SetSurfacePalette failed %s", SDL_GetError()); + exit(-1); + } + + _screenBufferPitch = _surface->pitch; + } } \ No newline at end of file diff --git a/src/windows/options.c b/src/windows/options.c index ae7513dc1b..9bf276aae8 100644 --- a/src/windows/options.c +++ b/src/windows/options.c @@ -68,6 +68,7 @@ enum WINDOW_OPTIONS_WIDGET_IDX { WIDX_GRIDLINES_CHECKBOX, WIDX_CONSTRUCTION_MARKER, WIDX_CONSTRUCTION_MARKER_DROPDOWN, + WIDX_HARDWARE_DISPLAY_CHECKBOX, WIDX_LANGUAGE, WIDX_LANGUAGE_DROPDOWN, @@ -126,6 +127,7 @@ static rct_widget window_options_widgets[] = { { WWT_CHECKBOX, 0, 10, 299, 99, 110, STR_GRIDLINES, STR_GRIDLINES_TIP }, { WWT_DROPDOWN, 0, 155, 299, 113, 124, STR_NONE, STR_NONE }, // construction marker { WWT_DROPDOWN_BUTTON, 0, 288, 298, 114, 123, 876, STR_NONE }, + { WWT_CHECKBOX, 0, 10, 290, 129, 140, 5154, STR_NONE }, // Culture / units tab { WWT_DROPDOWN, 0, 155, 299, 53, 64, STR_NONE, STR_NONE }, // language @@ -266,6 +268,7 @@ void window_options_open() (1ULL << WIDX_HEIGHT_LABELS_DROPDOWN) | (1ULL << WIDX_TILE_SMOOTHING_CHECKBOX) | (1ULL << WIDX_GRIDLINES_CHECKBOX) | + (1ULL << WIDX_HARDWARE_DISPLAY_CHECKBOX) | (1ULL << WIDX_SAVE_PLUGIN_DATA_CHECKBOX) | (1ULL << WIDX_AUTOSAVE) | (1ULL << WIDX_AUTOSAVE_DROPDOWN) | @@ -364,6 +367,12 @@ static void window_options_mouseup() w->viewport->flags &= ~VIEWPORT_FLAG_GRIDLINES; } break; + case WIDX_HARDWARE_DISPLAY_CHECKBOX: + gConfigGeneral.hardware_display ^= 1; + platform_refresh_video(); + config_save_default(); + window_invalidate(w); + break; case WIDX_SAVE_PLUGIN_DATA_CHECKBOX: gConfigGeneral.save_plugin_data ^= 1; config_save_default(); @@ -708,6 +717,12 @@ static void window_options_invalidate() else w->pressed_widgets &= ~(1ULL << WIDX_GRIDLINES_CHECKBOX); + // show hardware display + if (gConfigGeneral.hardware_display) + w->pressed_widgets |= (1ULL << WIDX_HARDWARE_DISPLAY_CHECKBOX); + else + w->pressed_widgets &= ~(1ULL << WIDX_HARDWARE_DISPLAY_CHECKBOX); + // construction marker: celsius/fahrenheit window_options_widgets[WIDX_CONSTRUCTION_MARKER].image = STR_WHITE + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_CONSTRUCTION_MARKER, uint8); @@ -719,6 +734,7 @@ static void window_options_invalidate() window_options_widgets[WIDX_GRIDLINES_CHECKBOX].type = WWT_CHECKBOX; window_options_widgets[WIDX_CONSTRUCTION_MARKER].type = WWT_DROPDOWN; window_options_widgets[WIDX_CONSTRUCTION_MARKER_DROPDOWN].type = WWT_DROPDOWN_BUTTON; + window_options_widgets[WIDX_HARDWARE_DISPLAY_CHECKBOX].type = WWT_CHECKBOX; break; case WINDOW_OPTIONS_PAGE_CULTURE: // currency: pounds, dollars, etc. (10 total)