Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switching font #100

Closed
jice-nospam opened this issue Mar 8, 2020 · 45 comments
Closed

switching font #100

jice-nospam opened this issue Mar 8, 2020 · 45 comments

Comments

@jice-nospam
Copy link
Contributor

I'm trying to port this doryen-rs example to bracket-lib. A few versions ago I was able to do it with this code :

self.cur_font = ctx.register_font(font).unwrap();
ctx.consoles[ctx.active_console].font_index = self.cur_font;

but the consoles field is now private (which is a good thing). Is there a way to change the font index of a console or do I have to register a new console, but in that case how can I get rid of the previous console ?

@thebracket
Copy link
Collaborator

Currently, deleting a layer isn't a great idea (not all of the back-ends handle that very well at all). I hope to fix that soon. I've typically gone with a layer per font, but I can see why run-time font switches would be attractive. I'll include an interface for doing that in the fonts branch I'm currently working on (which just moved from u8 to u16 to let you have bigger fonts, added character translation options ready for some unicode, and is actively pushing towards supporting different font layouts).

@jice-nospam
Copy link
Contributor Author

Ok, great ! I've got a similar issue with another doryen-rs example about dynamically updating a console. The idea is that the console adds columns/lines when the window is resized (instead of scaling the characters, there are more of them when the window is bigger).

bracket-lib consoles size, as returned by get_char_size() cannot be changed once the console is created (only the size in pixels).

That would require something like BTerm::set_char_size(w: u32, h: u32) which updates current console by reallocating it and copying existing content .
I can make a dedicated issue if you want to track this independently of dynamic font switching.

thebracket added a commit that referenced this issue Mar 9, 2020
…cefully handle choosing a font with a different pixel size, yet.
@jice-nospam
Copy link
Contributor Author

Another issue I'm foreseeing with window resizing : the current BEvent::Resized event sends the new physical size in pixels. To be able to use that to compute the new console size in chars, I'll also need the HiDPI factor. Maybe it should be included in the Resized event, or available through another API like BTerm::get_scale_factor

@thebracket
Copy link
Collaborator

Resizing is a whole other bundle of pain. It's actually a really complicated one (I've looked at it before and held off):

  • It would have to be an opt-in behavior; the design right now is "window scales up" and there are projects that expect that.
  • It's going to be really tricky with Amethyst, which also assumes that you want to scale up. I'm not sure if you can dynamically resize the Amethyst context at all (I can find out).
  • Resizing the actual console is quite an operation on some platforms. Crossterm handles it ok (since it's just telling the OS-provided terminal to handle it). The GL path would have to rebuild both the backing and the GL buffers. The WASM path would have to regenerate some textures. Fiddly, but do-able.
  • Copying the content's backing buffer also requires a bit of nastiness: growing is easy, but as soon as you shrink you are going to have to throw away user content. That's fine for "redraw each frame", but is a problem for progressive rendering. So now the user's program has to check the terminal size on each frame, in case the target changed. Doable, but also potentially annoying for the user!

Last time I looked at it, I said "this is going to be painful" and backed it out. I could do another push on it, when fonts work.

@thebracket
Copy link
Collaborator

thebracket commented Mar 9, 2020

Also, allowing set_char_size runs into some fun backing issues. The GL window would have to request a new size (fine, but it may not work). WASM would have to resize the canvas, change the coordinate space, and redraw - again fine, but there's quite a bit of room for error in there.

There's also the fun case: say you have a bunch of layers. Resize one, and they all have to resize.

@jice-nospam
Copy link
Contributor Author

yeah, it's definitely an opt-in option. I'm supporting it only on native target in doryen-rs because you're not supposed to resize the canvas in web mode (and resizing the browser window doesn't affect the canvas size). I guess a native-only feature would be enough to provide backward compatibility for doryen-rs users.

@jice-nospam
Copy link
Contributor Author

as for issues with multi-layers and preservation of the console content, I think it should be up to the user to deal with that. Some applications might want to change the layout depending on the window size anyway so I think it's not up to bracket-lib to decide what to do. But the basic building block for all of this is being able to resize the char_size of a specific console.

thebracket added a commit that referenced this issue Mar 9, 2020
…there, so try the squishy example to see a simple console resize and squish itself down. It's a start. Never going to be super performant, since it is doing a bunch of copy/reallocate. I still get 400 FPS on release (debug = don't ask).
thebracket added a commit that referenced this issue Mar 9, 2020
@jice-nospam
Copy link
Contributor Author

Great! the resize example works with this branch. I've still have an issue with font switching (screen turns black when trying to change the font. Might be an issue on my side. Investigating)

@jice-nospam
Copy link
Contributor Author

jice-nospam commented Mar 9, 2020

I'm not pre-registering the font in the terminal builder. Instead, I register it live like this :

let cur_font = ctx.register_font(font).unwrap();
ctx.set_active_font(cur_font);

Is that supported? All I get is a black screen

@thebracket
Copy link
Collaborator

The backend builds the font in GL at initialization time, so that will get you a blank screen.

@jice-nospam
Copy link
Contributor Author

Ok I think the register_font function should be private then. Having to know the font at initialization time is a reasonable requirement. I'll change the doryen-rs API to reflect that.

thebracket added a commit that referenced this issue Mar 9, 2020
@thebracket
Copy link
Collaborator

Forgot to tag the commit, but I've made register_font pub(crate).

@thebracket
Copy link
Collaborator

Run into a funny one. winit panics hard when you call set_inner_size once it has started. Others are having similar problems, hopefully it'll be fixed at some point in the future. winit seriously causes me more problems than anything else.

@jice-nospam
Copy link
Contributor Author

I'm also getting regressions (black screen) in some examples. Might be on my side.

@jice-nospam
Copy link
Contributor Author

I can't get the default scaling behavior to work. I'm using with_resize_scaling(false) in the term builder, but it still resizes the console

@thebracket
Copy link
Collaborator

I'm not seeing any black screens with the current build (just ran through examples). Definitely need to dampen key input once more (it's hard to press a key just once, messing up some examples).

@jice-nospam
Copy link
Contributor Author

More info on regression : it seems background colors are not rendered anymore, only the font on black background. Still don't know if it's in my wrapper or in bracket

@jice-nospam
Copy link
Contributor Author

Oh it works in bracket bench_scalable example so it's definitely on my side

@thebracket
Copy link
Collaborator

Definitely got background colors here, on both the alpha and textblock examples.
image
image

@jice-nospam
Copy link
Contributor Author

I haven't a clue why, but scaling and background rendering is broken on my side. I'll come back to it tomorrow.

@thebracket
Copy link
Collaborator

I just had a thought (untested): the font files are RGBA, with alpha backgrounds. I haven't tested to see what happens if you load one with a regular black background and no alpha content.

@jice-nospam
Copy link
Contributor Author

Ok nice catch ! It happens to be the opposite : using a font with transparent background result in no background color while using a font with a black background works !
This font result in a black background (not visible here because white characters on transparent background) :
terminal_8x8
This font works fine :
terminal_colored_8x8

@jice-nospam
Copy link
Contributor Author

The alpha fonts have been working so far but I guess the support of alpha transparency in the engine broke something

@jice-nospam
Copy link
Contributor Author

I confirm bracketlib's textblock example doesn't show background color when using the terminal_8x8.png font from doryen-rs (the all white font in the comment above).
Same result if I take bracketlib's default terminal8x8.png font and replace the black background with transparent and turning it into a 32bits png :

terminal8x8

thebracket added a commit that referenced this issue Mar 10, 2020
@jice-nospam
Copy link
Contributor Author

ok the HiDPI issue is due to doryen-rs not having the scale factor value. See my comment above : #100 (comment)
To resize properly the doryen-rs console, I need some ctx.get_scale_factor() function. This might be also useful for projects using graphics overlay like the native_gl example.

Still trying to figure out why all my examples are resized instead of scaled when I change the window size

@jice-nospam
Copy link
Contributor Author

The font loader now reformats images into RGBA on load. The shader is smart enough to handle either black or transparent as a being a background component.

I think fonts should be pre-processed during loading so that the shader only handles one format. It would make it simpler and faster. This is how I'm doing it in doryen-rs :
https://github.com/jice-nospam/doryen-rs/blob/master/src/font.rs#L59

This works with the popular "pink fonts" format (see DF tileset for example), but also a font with any opaque background color.
If the background color is black, greyscale font format is assumed and the color is converted to alpha value (white = opaque, black = transparent, gray = semi-transparent).

thebracket added a commit that referenced this issue Mar 10, 2020
@thebracket
Copy link
Collaborator

I'd previously looked at how you were doing the file scan. I deliberately didn't do that: if you're using a complicated tileset, you may well have a color in your (0,0) pixel. It's generally a bad idea to require that the user embed meta-data in their actual data files for that reason: they may need that pixel! (I've debugged people's projects with this very problem in the past)

Ok, I'm going through the bracket branch of doryen_rs now to see what I can find.

Based on what I'm finding so far:

  • I changed bracket-terminal to not send "resize" events when scaling is off. That gets rid of some odd behavior (doryen is resizing even when scaling is disabled).
  • So this is interesting. My terminal8x8.png and your terminal_8x8.png are very similar. Both have alpha on the background. The doryen basic example shows background colors with my font file and not yours. Digging into the file showed that your background colors had color data (not black) as well as alpha. So I changed the shader for consoles with backgrounds to look for EITHER color data OR alpha, rather than both. That restored background color to the basic example.
  • Resize and scale-factor changed messages now include the scale factor in the message.
  • I can't for the life of me figure out what pot_height and pot_width are for. Not sure there's a benefit to having your backing vector be power-of-two sized.
  • Input scaling on basic is kinda working. It draws the mouse in the extra background area when the console is larger - so the extra console space is there (it doesn't refresh it, so you can draw squiggly lines down there).

If you change the render function in examples/basic.rs to:

fn render(&mut self, api: &mut dyn DoryenApi) {
    let con = api.con();
    let console_width = con.get_width();
    let console_height = con.get_height();
    con.rectangle(
        0,
        0,
        console_width,
        console_height,
        Some((128, 128, 128, 255)),
        Some((0, 0, 0, 255)),
        Some('.' as u16),
    );
    con.area(
        10,
        10,
        5,
        5,
        Some((255, 64, 64, 255)),
        Some((128, 32, 32, 255)),
        Some('&' as u16),
    );
    con.ascii(self.player_pos.0, self.player_pos.1, '@' as u16);
    con.fore(self.player_pos.0, self.player_pos.1, (255, 255, 255, 255));
    con.print_color(
        (console_width / 2) as i32,
        (console_height - 1) as i32,
        "#[red]arrows#[white] : move - #[red]CTRL-S#[white] : save screenshot",
        TextAlign::Center,
        None,
    );
    con.print_color(
        (console_width / 2) as i32,
        (console_height - 3) as i32,
        &format!(
            "#[white]Mouse coordinates: #[red]{}, {}",
            self.mouse_pos.0, self.mouse_pos.1
        ),
        TextAlign::Center,
        None,
    );
    con.print_color(
        5,
        5,
        "#[blue]This blue text contains a #[red]red#[] word",
        TextAlign::Left,
        None,
    );
    con.back(
        self.mouse_pos.0 as i32,
        self.mouse_pos.1 as i32,
        (255, 255, 255, 255),
    );
}

And the resize event in app.rs to:

BEvent::Resized { new_size, dpi_scale_factor: _ } => {
    self.new_size = (new_size.x as u32, new_size.y as u32)
}

The basic example now works well and resizes on my machine. (I tried with 100% and 125% scale factors).

doryen-basic-resize

thebracket added a commit that referenced this issue Mar 10, 2020
@jice-nospam
Copy link
Contributor Author

Concerning pot_width and pot_height, some (old) opengl drivers only support power-of-two texture sizes. doryen-rs console data is stored in textures and accessed directly by the shader so I have to know the actual console size versus the logical size. But all of this should disappear with bracketlib.

thebracket added a commit that referenced this issue Mar 10, 2020
…ion in the initializer to specify an explicit background color to have converted to black/transparent.
@thebracket
Copy link
Collaborator

With the last commit, you can specify with_font_bg as part of the initializer if you want an explicit background replace. The fontswitch example is updated to include a magenta background DF font.

@jice-nospam
Copy link
Contributor Author

Well I think everything is looking quite good right now.
Just a minor issue, visible in the resize gif in your comment above : while resizing, the console texture is flipped upside down. This disappears as soon as you release the mouse button.

Also there's a different behavior in font switching between doryen and bracket. doryen was scaling the console to keep the same window/character size :

doryen

while bracketlib doesn't change scale nor window size :

bracket

I don't know what default behavior is the best or what should be controllable by the user. There are three ways to handle a change of font character size :

  • clip console to fit the window (current bracketlib behavior)

  • change scale to keep console and window size (old doryen-rs behavior)

  • change window size to keep console size and pixel perfect characters

thebracket added a commit that referenced this issue Mar 10, 2020
…. WASM honors font changes, correctly rebuilding the back-ends (it has issues with tiny fonts still; investigating). It's a lot more careful about scaling, so you can change font in fontswitch example and resize the window and it still scales.
thebracket added a commit that referenced this issue Mar 10, 2020
@thebracket
Copy link
Collaborator

That flipping upside down is trippy. I'll see what I can find.

The "change window size" option above isn't going to work until winit fix their code. Currently any call to set_inner_size (after the message loop starts) on recent winit results in a panic!

Changing scale to keep the original size is default bracket-lib behavior, unless you called with_resize_scaling(true). I think the name of that option needs to change; it doesn't really describe what it does. If true, the consoles adjust their size. If false, it uses GL to simply scale to fill the window.

I'll rename that option to: with_automatic_console_resize to describe what it actually does. I also just found a bug that was not resetting "needs resize" on consoles after a scale change, causing a HUGE performance drop after you resized your window.

@jice-nospam
Copy link
Contributor Author

Ok I still have an issue with automatic console resizing.
When opening a 80x50 terminal with with_resize_scaling(true) on a 1.5 hidpi screen, I get a 120x75 console instead with the exact same look as in this comment.

I reproduce the issue on the latest fonts branch version by running the benchmark_scalable example. This is what I get, a much bigger console than the requested 80x45 :

bench_hidpi

@jice-nospam
Copy link
Contributor Author

What happens is that there is a Resized event in the window initialization so bracketlib consider the window was resized from 1.0 scale factor to 1.5 and it increases the console size accordingly. I don't know if this initial Resize event happens on every platform

@jice-nospam
Copy link
Contributor Author

Changing scale to keep the original size is default bracket-lib behavior, unless you called with_resize_scaling(true).

This is not the behavior I observe with the fontswitch example. When the font characters are bigger, there are less columns/lines visible in the console.

thebracket added a commit that referenced this issue Mar 10, 2020
… ignores the first 'resize' that makes Linux so happy.
@thebracket
Copy link
Collaborator

The last couple of commits should help:

  • You can now have fonts other than 16x16; it figures out the dimensions based on the tile size you specify. The fontswitch example loads the unicode image (I recommend an optimization level other than 0; it takes a while to load in pure debug mode) and uses it.
  • The console resizing function now takes into account scale factor. That one always trips me up; I don't have any devices > 1080p, and working on scaled up text leaves very little room. I tend to forget the option exists. It also ignores the first resize call (needed because Wayland gives the wrong dimensions every time otherwise).

I have to admit, I'm now thoroughly confused by what you're wanting regarding scale.

  1. Running doryen_rs on the master branch, if I run the basic example and resize the window, it isn't resizing the console at all - it's resizing the rendered graphics in the window. Bracket-lib does that by default (if you don't enable with_automatic_console_resize), and has since day 1.
  2. Adjust basic on master to resizable: false in the initialization, and you simply can't resize the window at all. I deliberately don't support that, so people who want a bigger view can make the window larger.
  3. So I switch over to the bracket branch on Doryen. With resizable set to false in the config - it does exactly the same as (1): you resize the window, the console retains the same dimensions and the rendered window expands to exactly fill whatever size window you dragged out.
    4, Switch resizable to true and run basic, and you enable the console resizing. Resize the window, and the actual console resizes - adding/removing columns and rows. It sizes the console to fit the window. So add 24 pixels of width, the window is now 83 characters wide. Adjust the basic example to use con.get_width() (and height) instead of the constant, and the display fills out the completed console: you have dynamically resizing consoles that scale to fit, with a tiny gutter if you didn't drop on an exact font dimension.
  4. resizable does invert the display during the drag. Just pushed a fix for that.

thebracket added a commit that referenced this issue Mar 10, 2020
…e the huge font to obtain unicode characters. Still needs work.
@thebracket
Copy link
Collaborator

Looking at the doryen fonts example, I think I see what you're asking. You want the option of changing font and retaining console dimensions (which can result in squished text).

thebracket added a commit that referenced this issue Mar 10, 2020
…want to resize the console to the natural size or retain the size and scale the font. #100
@thebracket
Copy link
Collaborator

Alright, with that commit if you set the resize_to_natural_dimensions parameter to true you get my preferred behavior: resize the console to match the font (natural sizes). If you set it to false, it retains the console dimensions and scales the font. I tested it with a local copy of your fonts example from the bracket branch, and it works - although it looks better when the window is scaled up a bit, due to NEAREST scaling in GL (to avoid aliasing in pixels from neighboring glyphs when scaled).

@jice-nospam
Copy link
Contributor Author

1. Running `doryen_rs` on the master branch, if I run the `basic` example and resize the window, it isn't resizing the _console_ at all - it's resizing the rendered graphics in the window. Bracket-lib does that by default (if you don't enable `with_automatic_console_resize`), and has since day 1.

Yes, the only example concerning console resizing in doryen-rs is.. resize :) the basic example is not supposed to resize but simply scale (the default bracket behavior).

2. Adjust `basic` on master to `resizable: false` in the initialization, and you simply can't resize the window at all. I deliberately don't support that, so people who want a bigger view can make the window larger.

That's what the resizable flag in master branch was supposed to do, simply lock or not the window size. Whether you scale or resize the console depends on what you do in the resize callback. See the resize example. On the bracket branch though, the resizable flag is similar to bracket's with_automatic_console_resize. I should probably rename it but it's still wip for now. I also just removed the resize callback as it's now handled automatically by bracket.

you have dynamically resizing consoles that scale to fit, with a tiny gutter if you didn't drop on an exact font dimension.

Actually I don't see the gutter, once the mouse button is released the console is always scaled to fit exactly the window size, resulting most of the time in pixel artifacts as seen below :
dots
When the window size is not a multiple of the font character size, it might be better to add margins around the console to keep pixel perfection. Is this what bracket-lib does? It might be an issue on my side, once again ! :)

The unicode example is working fine now ! I still have an issue with non ascii characters in text_input. I'm investigating this.

Finally, is there a way to start a terminal with a specified size independently of the console/font size ? That's what the fonts example used to do :

        console_width: CONSOLE_WIDTH,
        console_height: CONSOLE_HEIGHT,
        screen_width: CONSOLE_WIDTH * 24,
        screen_height: CONSOLE_HEIGHT * 24,

I was starting with 24x24 console cells even though the first font has 8x8 character to have a big enough window for bigger fonts.
That might be useful for someone wanting a global x2 or x3 scale to get an old school pixelated look.
I'm still a bit confused about how the term builder parameters with_dimensions and with_tile_dimensions interact with consoles and fonts that have their own dimensions and tile size.

@jice-nospam
Copy link
Contributor Author

Ok I have my answer, the solution was with_tile_dimensions. The font example starts now with a the same screen size as on the master branch.

@thebracket
Copy link
Collaborator

The split between those needs to be better documented. It overrides the library's guess as to what the pixel size should be, originally a workaround for layers with multiple tile sets in play. I'll make a note to clean that up.

@jice-nospam
Copy link
Contributor Author

Well this is looking pretty good. The only remaining features/issues are really minor I'm tracking them here on doryen side.
What stays in doryen so far is the Image and subcell api, I think I'll move them to a dedicated console agnostic doryen-subcell library as it's quite specific.

@thebracket
Copy link
Collaborator

There's a full sprite system planned for a future release, if it's any help!

@thebracket
Copy link
Collaborator

The fonts branch is merged, so I'm going to go ahead and close this. Feel free to open issues if you find breakage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants