Option for keeping window aspect ratio
Ape opened this issue ยท 24 comments
Window resizing works nicely with the SDL2 renderer. However, I would like to use letterboxing instead of the current window scaling in order to keep the correct aspect ratio. Is that possible?
Not right now, but this is something that was planned with the newer renderers. I'll try to make it a priority.
Also, I would like to have a scaling mode that does not upscale the tiles at all, but instead centers the picture and adds letterboxing in every direction. This could be used for example with grid size 120x67 with a 16x16 font resulting in 1920x1072 usable area (8 px vertical letterbox on a 1920x1080 monitor). Integer scaling could be allowed because it doesn't negatively affect the looks.
I've already setup a libtcod function that accepts a "viewport" so any rectangle you can make yourself is an option.
It bypasses console_flush
so you need to clear and present SDL's renderer manually using python-tcod's FFI functions for SDL2. I'll have an example that shows how this works.
There's now an example that works with the newest release of python-tcod: https://github.com/libtcod/python-tcod/blob/master/examples/experimental/custrender.py
Nothing was added to python-tcod itself yet, but this shows how to have a fixed aspect ratio or integer scaling.
Thanks for implementing this. I tried to run the example and it works just like I wanted. However, I needed to add something like this, or it would just segfault:
tcod.console_set_custom_font(
"data/fonts/consolas10x10_gs_tc.png",
tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_TCOD
)
Do you have plans for adding a high level option for integer scaling (or other scaling modes)? It could be for example a parameter for tcod.console_init_root
.
However, I needed to add something like this, or it would just segfault.
What's your platform? I'll look into this.
Do you have plans for adding a high level option for integer scaling (or other scaling modes)? It could be for example a parameter for tcod.console_init_root.
In general I'm trying to move away from hidden global values. These kind of options won't be baked into tcod.console_init_root
, if they were it'd be less useful than what's already in the example which allows for many things beyond aspect correction.
tcod.console_init_root
and the concept of a root console was also the reason it's so hard to change the size of the console in reaction to the window being resized, something you can do easily with this new code.
However, I needed to add something like this, or it would just segfault.
What's your platform? I'll look into this.
I'm on Arch Linux with tcod 10.0.4 installed from pip. I have Python 3.7.3.
EDIT: I tried to debug this thing, but gdb doesn't want to work with Python apparently. Strace tells me the last thing the program did before segfault was
openat(AT_FDCWD, "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
So it's probably related to that. I have that font in path /usr/share/fonts/TTF/DejaVuSansMono.ttf
instead of the one it tried to open.
Since this bypasses tcod.console_flush
it also disables any vsync or frame rate limits. While it's nice to see that my game runs at 2271 FPS, I would prefer to run it at 60 FPS. Is there a way to do this with this new rendering?
Vsync is something I should add to tcod.console_init_root
since there doesn't seem to be a way to toggle it once SDL2's window is opened.
Until then you might be able to implement frame limiting in Python.
I installed another version of gdb and managed to get the full stack trace:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff71c893d in stbtt__find_table (tag=<optimized out>, fontstart=<optimized out>, data=<optimized out>) at libtcod/src/vendor/stb_truetype.h:1291
1291 libtcod/src/vendor/stb_truetype.h: No such file or directory.
(gdb) bt
#0 0x00007ffff71c893d in stbtt__find_table (tag=<optimized out>, fontstart=<optimized out>, data=<optimized out>) at libtcod/src/vendor/stb_truetype.h:1291
#1 stbtt.find_table.lto_priv () at libtcod/src/vendor/stb_truetype.h:2583
#2 0x00007ffff71c3c50 in stbtt_InitFont_internal (fontstart=0, data=0x7fffffffd860 "", info=0x7fffffffd878) at libtcod/src/vendor/stb_truetype.h:1354
#3 stbtt_InitFont (info=0x7fffffffd878, data=0x7fffffffd860 "", offset=0) at libtcod/src/vendor/stb_truetype.h:4777
#4 0x00007ffff718062d in _ZN4tcod7tileset12TTFontLoaderC2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEii (tile_height=<optimized out>,
tile_width=<optimized out>, path=..., this=<optimized out>, this=<optimized out>, path=..., tile_width=<optimized out>, tile_height=<optimized out>)
at /usr/include/c++/8.3.0/bits/basic_string.h:2302
#5 _ZN4tcod7tileset13load_truetypeERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt5arrayIiLm2EE (path=..., tile_size=...)
at libtcod/src/libtcod/tileset/truetype.cpp:203
#6 0x00007ffff7182616 in _ZN4tcod7tileset20new_fallback_tilesetERKSt5arrayIiLm2EE (tile_size=...) at /usr/include/c++/8.3.0/bits/char_traits.h:352
#7 0x00007ffff718bb92 in init_display<tcod::sdl2::SDL2Display> (fullscreen=0, title="custrender.py", h=4, w=20) at /usr/include/c++/8.3.0/ext/atomicity.h:69
#8 _ZN4tcod7console9init_rootEiiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEb15TCOD_renderer_t (w=<optimized out>, h=<optimized out>, title=...,
fullscreen=<optimized out>, renderer=<optimized out>) at libtcod/src/libtcod/engine/display.cpp:90
#9 0x00007ffff718c315 in TCOD_console_init_root (w=20, h=4, title=<optimized out>, fullscreen=false, renderer=TCOD_RENDERER_SDL2)
at /usr/include/c++/8.3.0/bits/basic_string.tcc:206
#10 0x00007ffff71f9f3e in _cffi_f_TCOD_console_init_root (self=<optimized out>, args=<optimized out>) at build/temp.linux-x86_64-3.7/tcod._libtcod.c:33106
#11 0x00007ffff7b83e68 in _PyMethodDef_RawFastCallKeywords () from /usr/lib/libpython3.7m.so.1.0
#12 0x00007ffff7b84101 in _PyCFunction_FastCallKeywords () from /usr/lib/libpython3.7m.so.1.0
#13 0x00007ffff7bf4d19 in _PyEval_EvalFrameDefault () from /usr/lib/libpython3.7m.so.1.0
#14 0x00007ffff7b3cd09 in _PyEval_EvalCodeWithName () from /usr/lib/libpython3.7m.so.1.0
#15 0x00007ffff7b83882 in _PyFunction_FastCallKeywords () from /usr/lib/libpython3.7m.so.1.0
#16 0x00007ffff7bf0f9c in _PyEval_EvalFrameDefault () from /usr/lib/libpython3.7m.so.1.0
#17 0x00007ffff7b836db in _PyFunction_FastCallKeywords () from /usr/lib/libpython3.7m.so.1.0
#18 0x00007ffff7bf022d in _PyEval_EvalFrameDefault () from /usr/lib/libpython3.7m.so.1.0
#19 0x00007ffff7b3cd09 in _PyEval_EvalCodeWithName () from /usr/lib/libpython3.7m.so.1.0
#20 0x00007ffff7b3dc64 in PyEval_EvalCodeEx () from /usr/lib/libpython3.7m.so.1.0
#21 0x00007ffff7b3dc8c in PyEval_EvalCode () from /usr/lib/libpython3.7m.so.1.0
#22 0x00007ffff7c66694 in ?? () from /usr/lib/libpython3.7m.so.1.0
#23 0x00007ffff7c67b6e in PyRun_FileExFlags () from /usr/lib/libpython3.7m.so.1.0
#24 0x00007ffff7c6b035 in PyRun_SimpleFileExFlags () from /usr/lib/libpython3.7m.so.1.0
#25 0x00007ffff7c6d2a7 in ?? () from /usr/lib/libpython3.7m.so.1.0
#26 0x00007ffff7c6d4ec in _Py_UnixMain () from /usr/lib/libpython3.7m.so.1.0
#27 0x00007ffff7dcace3 in __libc_start_main () from /usr/lib/libc.so.6
#28 0x000055555555505e in _start ()
Okay, you just cannot assume font paths like this:
https://github.com/libtcod/libtcod/blob/b2e1a7f1b496e9eeca16a51f74a4aea3b68441ec/src/libtcod/tileset/fallback.cpp#L52
The fallback fonts will be for prototype projects and not for releases, they should work fine on Windows and maybe even MacOS, but there's no standard for Linux. So if you have a suggestion for that then I'm listening.
This is clearly my C++ inexperience. I suspect the problem is actually this line: https://github.com/libtcod/libtcod/blob/b2e1a7f1b496e9eeca16a51f74a4aea3b68441ec/src/libtcod/tileset/truetype.cpp#L53 Which should really be throwing something instead of returning. The zero width data gets sent to the font loader which segfaults.
Looks like you are correct about the cause of the segfault. Throwing with a proper message would certainly be more helpful than segfaulting.
I think the standard thing to do on Linux is something like fc-match --format=%{file} monospace
which returns a path to the default monospace font. There is also the equivalent C function FcFontMatch
.
There's one more issue with this rendering example. If you add any console drawing functions that modify the background color it will leak to the background area around the viewport. For example:
...
while True:
color = [random.randrange(255) for x in range(3)]
console.print(0, 0, "X", bg=color)
# Clear background with white.
clear((255, 255, 255))
...
I think the standard thing to do on Linux is something like
fc-match --format=%{file} monospace
Okay, I've added this and it should be tested.
If you add any console drawing functions that modify the background color it will leak to the background area around the viewport.
Turns out the accumulation function was clearing the screen and it was doing so after rendering the console so it had the text background color set when it did. This is now fixed.
I think the standard thing to do on Linux is something like
fc-match --format=%{file} monospace
Okay, I've added this and it should be tested.
The fallback font now works on my system.
In the example it says:
You can change to a console of a different size in response to a WINDOWRESIZED event if you want.
But how can this be done? It looks like the console size paramers must be given in console_init_root
and are read-only after that. Exiting the console and calling console_init_root
doesn't work very well either because it recreates the window.
But how can this be done?
The accumulate function will take any console of any size. You just swap out the root console with a new one make by calling tcod.console.Console
with the size you want.
Thanks. console_init_root
could be replaced with something like init_window
that takes the parameters title
, fullscreen
, and renderer
, but not the console related parameters. Then I could just create the console(s) manually and use this new rendering method to draw them to the screen.
init_window
could work for now. The hard part is figuring out where to put it in the API.
console_init_root
now has a vsync
parameter in the latest release.
I've also added another example that shows how to make a resizable console: https://github.com/libtcod/python-tcod/blob/master/examples/experimental/resizable_console.py
custrender.py
has a new initialization function: init_sdl2
, this one takes width and height in pixel resolution and you can give it SDL2 window flags.
This should address the features you wanted from an init_window
function.
Thanks. What width and height values should I pass to init_sdl2
if I want to use SDL_WINDOW_FULLSCREEN
or SDL_WINDOW_MAXIMIZED
where the resolution is determined automatically?
Also, can you add the functions in custrender.py
to the tcod python library so I can just use them without having to copy-paste them to my project. I can still use modified functions when needed, but some of the functions are quite generic or are at least good for the most common cases.
You might want to use SDL_WINDOW_FULLSCREEN_DESKTOP
instead of SDL_WINDOW_FULLSCREEN
.
SDL_WINDOW_FULLSCREEN
is exclusive mode, you'd need to get video modes from SDL_GetDisplayMode
and use that to set the resolution, and few people like having their resolution changed.
For SDL_WINDOW_FULLSCREEN_DESKTOP
or SDL_WINDOW_MAXIMIZED
the resolution given should be a decent fallback for whenever the window flags are not applied for whatever reason. It would be the resolution for the window if the window was unmaximized or taken off of fullscreen mode.
I was thinking of adding something like a tcod.future
package that could hold functions until they're more stable.
I've been able to improve the renderers. Now everything you'd want is available in the tcod.console_flush function since version 11.8. You no longer need custrender.py
.
Be sure to mention anything else this should have.