-
-
Notifications
You must be signed in to change notification settings - Fork 88
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
Orientation #647
Comments
Most display driver crates can also be used without embedded-graphics support by disabling a cargo feature. This means that they need to implement the orientation support without using any e-g types. But providing a more consistent driver interface would is a good idea IMO. Adding a
Yes, the display size should always reflect the drawable area.
This is even lower level than the orientation support and doesn't belong in e-g IMO. But it might be a candidate for inclusion in the lower level crate I mentioned above. |
I always found the orientation confusing myself. I sort of inherited the concept from the original ST7789 driver writer when I took over and kept it for backwards compat. Having some sort of standard might make sense but we need to be careful, there might be some crazy edge cases we could lock out. Perhaps including a Custom enum variant to cover anything like that. |
In this PR I attempted to come up with a better, more general,
Where Orientation is an enum can either be Landscape or Portrait. This fixes the problem with the previous implementation where the image can be reversed on the screen but the API didn't allow inverting it. I would like to see a general flush method added to the DrawTarget interface. Dirvers that don't need/support it can implement it is a NOP. While I am on the topic, another one I would like to see is a trait with a method to turn on/off the display. Where this is possible. As I like to be able to turn off my screens at night based on common code that should be able to work with any disable with this functionality. |
I guess you are talking about this PR? almindor/mipidsi#9 |
Yes, sorry, I fail at copy and paste. |
I also think that having a common Orientation enum in e-g would be useful. |
Does anyone have a good idea for the names of an generic We could define one orientation as the base orientation, which would need to defined in the display driver docs, and use degrees to describe the other orientations: enum Orientation {
Deg0,
Deg90,
Deg180,
Deg270,
}
// or
enum Orientation {
Deg0,
Deg90Cw,
Deg90Ccw,
Deg180,
} |
Have a look at https://github.com/almindor/mipidsi/blob/9b06028fe5169c7068b65b2d886e9456d360f85f/src/lib.rs#L61-L74 Here we decided that we also needed a boolean mirror flag, because some devices are wired up in such a way that the image is displayed mirrored, and some devices are not. Not sure how applicable this is for other chipsets. |
If you define your screen with orientation I had some issues implementing the rotation, if the user can swap the X, Y dimensions when creating an instance. |
To be honest I don't like that at all, especially with a |
Yes, if one display is |
A previous approach of mine was to pass another parameter to the An important question I think: Is mirroring in scope for a common API to set orientation? I tend to think it might be a good idea. But we might need to have some allowances in case the hardware does not support it (e.g. by ignoring the invalid request or producing a defined error for this situation). |
The reason for this was that decoupling the mirror info from the I'm not happy with the current state either tho but don't know of how to make it "elegant yet complete". |
I agree that an extra bool argument for Or do you mean that there would be multiple representations of the same orientation? Like |
What I mean is that if the mirrored information is not part of Regardless of how we want the |
|
That'd work. I don't like it but it'd work :) I'm not going to block either solution as long as we can unify the |
I'm always open to better suggestions 😉. We could also use an pub enum Rotation { Deg0, Deg90, Deg180, Deg270 }
pub struct Orientation {
pub rotation: Rotation,
pub mirrored: bool,
}
impl Orientation {
/// Creates a non mirrored display orientation.
pub const fn new(rotation: Rotation) -> Self {
Self { rotation, mirrored: false }
}
/// Creates a display orientation with mirrored X axis.
pub const fn new_mirrored(rotation: Rotation) -> Self {
Self { rotation, mirrored: true }
}
}
// Usage:
display.set_orientation(Orientation::new(Rotation::Deg0));
display.set_orientation(Orientation::new_mirrored(Rotation::Deg0));
// Or if you prefer struct literals:
display.set_orientation(Orientation { rotation: Rotation::Deg0, mirrored: false })); |
That seems pretty nice. Do we put it into e-g or e-g core ? |
I'm not sure yet, maybe neither. A But I'm not sure if we need the The main trait in that crate would contain common functions for displays, which aren't part of the trait HalDisplay {
// For drivers that draw directly to the framebuffer inside the display controller `flush` is a noop.
// By using another error type for `flush` it can be set to `Infallible` in that case.
type FlushError;
// Error type for all other operations that communicate with the display.
type Error;
// Updates the display.
// The default noop impl can be overridden if the display needs to be flushed.
fn flush(&mut self) -> Result<(), Self::FlushError> {
Ok(())
}
// Sets the orientation.
// The error is wrapped in a `NotSupportedError` enum to notify the user if setting the orientation isn't supported.
fn set_orientation(&mut self, orientation: Orientation) -> Result<(), NotSupportedError<Self::Error>> {
Err(NotSupportedError::NotSupported);
}
// Returns the current orientation.
// The method does directly return an `Orientation` instead of a `Result` because if the orientation can't
// be changed it will always stay in the default orientation. But this also makes the assumption that
// the driver knows the current orientation without communication with the display.
fn orientation(&self) -> Orientations {
Orientation::default()
}
// TODO: other methods?
}
enum NotSupportedError<E> {
NotSupported,
Display(E)
} Some questions remain:
|
Supporting back-lighting would definitely be a good thing for me. Although not 100% certain I can visualize exactly how the wrapper would work. Would D need to require Taking a step back, checking my understanding, the
|
Yes, in my example the impl requires let display = Display::st7789(di, rst);
// The returned `display` would implement `HalDisplay`,
// but `set_backlight` isn't supported and returns `NotSupportedError::NotSupported`
display.init(...)?;
// Add active high GPIO backlight.
let display_with_backlight = GpioBacklight::new(display, gpio_pin, PinState::High);
// `display_with_backlight` supports `set_backlight` by setting the GPIO pin.
// All other method calls are passed though to `display`.
// There would need to be some way of accessing the original `display` for methods which aren't part of `HalDisplay`.
// For example:
display_with_backlight.as_inner_mut().set_tearing_effect(TearingEffect::Vertical)?;
IMO the trait would either have |
Hi, I got here because I was confused that the Now I am happy to see these efforts here to change that but I am not sure if the trait suggested above is the right approach: In our projects, we try to keep hardware-specific details in the board-support layer and add a hardware-independent application on top. From my understanding, setting orientation or a mirrored flag are hardware-specific details but flushing or controlling the backlight are controlled by the hardware-independent application logic. I consider flushing to be part of the hardware-independent part because the application logic knows when a new frame has been completely written which is when One could argue that rotation and mirroring could be controlled by the application logic, too, but this only holds true if the application wants to display something upside-down or mirrored. In the case you have been discussed above (e.g. display is assembled upside-down), this is something hardware-specific which should be compensated without the application logic knowing about that detail. |
That's a valid point. I'm still not totally convinced that |
I am not a fan of having fn draw<D>(&self, display: &mut D) -> Result<...>
where
D: DrawTarget<Color = C> + FlushTarget,
{
...
} having the |
Also, another place where Related comment: oxidecomputer/hubris#484 (reply in thread) |
I'm working on some risc-v based board with a st7789 display.
The display driver provides the possibility to change the orientation in one of 4 modes: Landscape, LandscapeSwapped, Portrait, PortraitSwapped.
It has the possibility to change it on the fly. I have seen some other libraries do this as well.
Now my questions are: why each library provides this on his own? why there is no common Trait for handling it in the core library?
Should then the display size dimension be swapped?
In addition, the st7789 requires some offset between RAM memory and the display size (if display size is not 320x240). I implemented it on my own for this specific driver as the interface for the user to provide his own values or get the numbers calculated automatically. Is there any way to get such interfaces in the embedded-graphics-core as well?
The text was updated successfully, but these errors were encountered: