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
improve rendering of waveform marks #12203
Conversation
…educed code duplication, fixed a small bug with leave events not being send to the opengl widgets, use antialiasing for play pos
What happens when labels overlap? (e.g in the screenshot, label of hotcue 4 would be long enough to overlap label of 3) |
@acolombier I think you're referring to #11745 I suggest to keep this PR compact, then take care of the other issues (#11745 and dark/bright detection / hover effect). I'll test this asap. |
There is currently no such logic. But the case you describe (the previous marker being so long that it overlaps the next marker) could be very easily solved with very simple logic: draw the markers from right to left. But what annoys me most is when you have two markers at the same position (I actually use that quite a lot, e.g. a normal and a looping hotcue). I want to try placing the second one higher than the first in that case. In any case, all that should be in a separate PR. |
@ronso0 this isn't quite the same now - my question was if instead of @m0dB I also use that behavior and suffering from the same limitation and the change you are describing would be very appreciated :) |
I think #11745 is more about the inconsistency between the scrolling waveform and the waveform overview. |
std::unique_ptr<QOpenGLTexture> m_pTexture; // Used by allshader::WaveformRenderMark | ||
QImage m_image; // Used by WaveformRenderMark |
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.
so IIUC, m_pTexture
is always nullptr
when running with a legacy shader and QImage isNull
the other way around, right? Wouldn't it be better instead then if these concepts lived in the shaders then instead? At least that sounds like thats a better separation of concern.
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.
In the renderer you mean? (WaveformMarkRenderer and allshaders::WaveformMarkRenderer). But that would require keeping track of which image or texture goes with which WaveformMark. No, this really needs to be part of WaveformMark. But maybe it can be done by subclassing WaveformMark... I will have a look, I agree this is not ideal.
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.
But that would require keeping track of which image or texture goes with which WaveformMark.
The WaveformMarks in the renderer are just stored in a container, should just be sufficient to store a std::pair<std::unique_ptr<QOpenGLTexture>, WaveformMark>
and then implement generateMarkImage
in terms of that, right?
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.
No, they are stored in a WaveformMarkSet. But I can derive a WaveformMarkImage and a WaveformMarkTexture from WaveformMark and templatize WaveformMarkSet.
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 started doing this, but it adds way too much complexity. I prefer having these two data members, of which either one is used depending on the context, instead of adding all this complexity.
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.
okay. Have you considered putting the members in a variant to highlight the fact that they are mutual exclusive? If that introduces too much complexity again, then ignore it. I'm just trying to come up with solutions to make the code more self-documenting.
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.
yes, that makes sense! will do!
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.
done. hope this is in line with what you had in mind.
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.
Yes, almost. I was rather thinking of std::variant
but this also works. The only thing I'm slighly concerned about is the blind static_cast from the parent to the derived type but I don't know any better solution either right now without introducing lots of boilerplate.
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.
Thank you for going through the extra effort.
My screenshot is HDPI so that will make a difference for sure, but indeed this looks worse than it should... Can you try changing, in waveformmark.cpp:
to
and see if that improves things? (This will make the intro/outro markers look weird, but that's expected) |
and without this PR ? |
Thanks for testing and following up. Well, that's a relieve! That also looks bad! What if you paste this character ↻ in a text editor with font Open Sans (bold) at different sizes? And does it improve things if in |
My conclusion is that Windows does a pretty bad job at rendering this symbol. Wondering what it looks like on Linux. Maybe we should use a png instead? |
If possible, we should use svg vector graphics, as in the skins. |
+1 to using an SVG instead of a font |
I have added the possibility to specify an svg. This is with an svg for the loop mark. Does this look okay on non-HDPI, @JoergAtGithub ? Maybe the svg needs a bit of tweaking. |
…, depending on the context
Great!
No, I will add it to this PR, it's very little effort. Edit: Done, please confirm @JoergAtGithub |
All skins? 😬 |
That's a good point, @ronso0 . I only added this to Latenight, but the text rendering is still there, so of course other skins can continue using that. |
Now all the markers look fine on Windows11! Thank you! |
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.
LGTM. thanks a lot. before merging, did someone test this on linux as well?
looks good on linux |
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.
looks good! Please clean up stray commented-out lines before merging, and I've made some comments. I am not blocking this PR on them though since I am famously bad at getting back to my reviews :)
res/skins/LateNight/waveform.xml
Outdated
@@ -62,7 +62,8 @@ | |||
</MarkRange> | |||
<Mark> | |||
<Control>loop_start_position</Control> | |||
<Text>↻</Text> | |||
<!--Text>↻</Text--> |
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.
remove comments now that we are using the svg files
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.
not sure about this (here). sometimes it's helpful to have the matching unicode char at hand.
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.
Yes, I agree, the Text should be fallback. I will revert the commenting.
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.
Done
|
||
return m_label.area().contains(point) || lineHovered; | ||
} | ||
|
||
// Helper class to calculate the geometry and fontsize needed by generateImage |
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.
technically this is a struct not a class, so let's call it that
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.
Done
const qreal margin{3.f}; | ||
|
||
qreal capHeight; | ||
{ |
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.
why is this scoping needed?
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.
oh is this copied from the original code? then maybe that's fine 👍🏻
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 removed it, as it allows capHeight to be const.
QString path = m_pixmapPath; | ||
// Use devicePixelRatio to properly scale the image | ||
QImage image = *WImageStore::getImage(path, devicePixelRatio); | ||
// QImage image = QImage(path); |
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.
please remove commented-out lines like this throughout
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.
Done
// If loading the image didn't fail, then we're done. Otherwise fall | ||
// through and render a label. | ||
if (!image.isNull()) { | ||
image = |
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.
unnecessary newline. if possible, please run the code formatter on changed lines
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 am using pre-commit , not sure why this wasn't formatted. Anyway, fixed.
Graphics() | ||
: m_obsolete{false} { | ||
} | ||
bool m_obsolete; |
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 is equivalent and simpler, though we might have a style reason not to use this format?
Graphics() | |
: m_obsolete{false} { | |
} | |
bool m_obsolete; | |
bool m_obsolete = false; |
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.
See my comment below on uniform initialization.
|
||
float m_linePosition; | ||
float m_linePosition{}; |
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'm not sure we use this style of initialization elsewhere, probably clearer to do = 0; instead
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 would like to get developer concensus on this. @Swiftb0y (IIRC) recently told me to initialize in the initializer list...
But there are two issues at hand here: 1) using curly braces aka uniform initialization and 2) in-class initialization vs initializer lists.
As for 1, from what I understand, uniform initialization is the prefered way since c++11. The standard https://isocpp.org/wiki/faq/cpp11-language#uniform-init clearly says: "Prefer initializing using {}."
As for 2, I don't think there is such a clear preference. When you have multiple constructors, in-class initialization certainly makes things easier, otherwise I don't think it matters much.
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 would like to get developer concensus on this. @Swiftb0y (IIRC) recently told me to initialize in the initializer list...
Let me be clear, I didn't mean to tell you to use initializer lists, I just said other people will complain and I thought I'd mention it so it doesn't seem like I didn't review your PR thoroughly. I also prefer this style and if other devs do so too, then lets use it. If others complain we can settle this in a simple vote. :)
I'm not sure we use this style of initialization elsewhere, probably clearer to do = 0; instead
I also prefer = 0
but only when the type is clearly numerical. On the other hand the meaning of = 0
is already overloaded in the context of pure virtual member functions. So my preference is probably wrong and we should prefer using {}
as per the cpp core guidelines.
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.
Ok, let's take this discussion to zulip
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.
removing in-class initialization
All comments have been addressed, so I think this can be merged. |
Just tested this again, and it's still working. LGTM! Thank you! |
This PR improves the rendering of the waveform marks, by auto-scaling and better aligning the symbolic marks (such as the intro/outro triangles and the loop open circle arrow), as discussed here: https://mixxx.zulipchat.com/#narrow/stream/109171-development/topic/look.20of.20loop.20out.20marker
Additionally it:
waveformrendermark.cpp
andallshaders/waveformrendermark.cpp
by moving the mark image rendering towaveformmark.cpp
and doing a general cleanup of that code