-
-
Notifications
You must be signed in to change notification settings - Fork 11
feat: use solid fills with rle compressed images #52
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
base: main
Are you sure you want to change the base?
Conversation
|
I did nothing new, just moved code to methods to reuse them from both |
|
I like the performance improvement this brings, but the code is getting a bit repetitive. I think we should try to refactor it to make it easier to maintain in the future. Here is my suggestion: |
9dea866 to
6cc5879
Compare
|
What would you say about this api? |
a021e6f to
169a74d
Compare
169a74d to
7774e7d
Compare
rfuest
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you say about this api?
Much better. I like the way the RLE decoding and conversion into pixels are now more decoupled and I think we can make the RLE state handling even simpler now (see comments below).
| }; | ||
| } | ||
| return Some(RawU8::from(value)); | ||
| // total pixels represented by this run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least for the RLE8 case I don't think we need the RLE state anymore. If you take a look at, for example, the way RleState::Running is handled. The state is set to running in RleState::Starting, just to be immediately reset back to Starting in the next loop iteration. And the number of pixels is first decremented by one to then be incremented by one again.
| // alternating pattern -> only one pixel of this color now | ||
| if remaining == 0 { | ||
| self.rle_state = RleState::Starting; | ||
| } else { | ||
| self.rle_state = RleState::Running { | ||
| remaining: remaining.saturating_sub(1), | ||
| value, | ||
| is_odd, | ||
| }; | ||
| } | ||
| return Some((RawU4::from(nibble_value), 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the alternating case it might be worth looking into returning an enum instead of the tuple from the runs iterators. Something like:
enum RleRun<R> {
Solid {
color: R,
count: usize,
},
Alternating {
colors: [R; 2],
count: usize,
}
}This would make it possible to draw the alternating pattern with DrawTarget::draw_contiguous, instead of drawing individual pixels.
| pub fn runs(&mut self) -> &mut I { | ||
| &mut self.runs | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be called as_runs and a to_runs method could also be useful:
| pub fn runs(&mut self) -> &mut I { | |
| &mut self.runs | |
| } | |
| pub fn as_runs(&mut self) -> &mut I { | |
| &mut self.runs | |
| } | |
| pub fn to_runs(self) -> I { | |
| self.runs | |
| } |
| loop { | ||
| let (raw, count) = match self.run.as_mut() { | ||
| Some(run) => run, | ||
| None => { | ||
| self.run = Some(self.runs.next()?); | ||
| self.run.as_mut().unwrap() | ||
| } | ||
| }; | ||
| if *count > 0 { | ||
| *count -= 1; | ||
| return Some(*raw); | ||
| } else { | ||
| self.run = None; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to use an Option for the current run, because count == 0 can be used to describe the same state as None.
By changing the struct to:
pub struct RleColors<C, I: Iterator<Item = (C, usize)>> {
runs: I,
color: C,
count: usize,
}(color can be initialized to C::default() in the constructor)
the next method can be simplified to:
| loop { | |
| let (raw, count) = match self.run.as_mut() { | |
| Some(run) => run, | |
| None => { | |
| self.run = Some(self.runs.next()?); | |
| self.run.as_mut().unwrap() | |
| } | |
| }; | |
| if *count > 0 { | |
| *count -= 1; | |
| return Some(*raw); | |
| } else { | |
| self.run = None; | |
| } | |
| } | |
| loop { | |
| if self.count == 0 { | |
| (self.color, self.count) = self.runs.next()?; | |
| } | |
| if self.count > 0 { | |
| self.count -= 1; | |
| return Some(self.color); | |
| } | |
| } |
Thank you for helping out with tinybmp development! Please:
CHANGELOG.mdentry in the Unreleased section under the appropriate heading (Added, Fixed, etc) if your changes affect the public APIrustfmton the projectjust build(Linux/macOS only) and make sure it passes. If you use Windows, check that CI passes once you've opened the PR.PR description
Added a
next_solid_chunkfor rle colors, it allows to make use offill_solidthat gave a huge performance boost on my mcu.