-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
library: display hotcues on overview waveform #15514
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,10 @@ | |
|
|
||
| #include <QPainter> | ||
|
|
||
| #include "util/color/color.h" | ||
| #include "util/colorcomponents.h" | ||
| #include "util/math.h" | ||
| #include "util/painterscope.h" | ||
| #include "util/timer.h" | ||
| #include "waveform/renderers/waveformsignalcolors.h" | ||
|
|
||
|
|
@@ -12,7 +14,9 @@ namespace waveformOverviewRenderer { | |
| QImage render(ConstWaveformPointer pWaveform, | ||
| mixxx::OverviewType type, | ||
| const WaveformSignalColors& signalColors, | ||
| bool mono) { | ||
| bool mono, | ||
| const QList<mixxx::CueInfo>& cueInfos, | ||
| double trackDurationMillis) { | ||
| const int dataSize = pWaveform->getDataSize(); | ||
| if (dataSize <= 0) { | ||
| return QImage(); | ||
|
|
@@ -47,6 +51,8 @@ QImage render(ConstWaveformPointer pWaveform, | |
| mono); | ||
| } | ||
|
|
||
| // markers are drawn after normalization for maximum visibility | ||
|
|
||
| // Evaluate waveform ratio peak | ||
| float peak = 1; | ||
| for (int i = 0; i < dataSize; i += 2) { | ||
|
|
@@ -73,6 +79,90 @@ QImage render(ConstWaveformPointer pWaveform, | |
| Qt::IgnoreAspectRatio, | ||
| Qt::SmoothTransformation); | ||
|
|
||
| // now draw markers on the final normalized image for maximum visibility | ||
| if (trackDurationMillis > 0) { | ||
| // ensure image format supports proper alpha channel handling | ||
| if (normImage.format() != QImage::Format_ARGB32_Premultiplied) { | ||
| normImage = normImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); | ||
| } | ||
|
|
||
| QPainter markerPainter(&normImage); | ||
| markerPainter.setRenderHint(QPainter::Antialiasing, false); | ||
| const int imageWidth = normImage.width(); | ||
| const int imageHeight = normImage.height(); | ||
|
|
||
| // draw minute markers - make them taller and brighter | ||
| const int markerHeight = static_cast<int>(imageHeight * 0.2); | ||
| const int lowerMarkerYPos = static_cast<int>(imageHeight * 0.8); | ||
|
|
||
| // use pure opaque white | ||
| QColor minuteColor(255, 255, 255, 255); | ||
|
|
||
| for (double currentMarkerMillis = 60000; currentMarkerMillis < trackDurationMillis; | ||
| currentMarkerMillis += 60000) { | ||
| const int x = static_cast<int>( | ||
| (currentMarkerMillis / trackDurationMillis) * imageWidth); | ||
|
|
||
| if (x >= 0 && x < imageWidth) { | ||
| // draw 2px wide for visibility | ||
| markerPainter.setCompositionMode(QPainter::CompositionMode_Source); | ||
| markerPainter.fillRect(x, 0, 2, markerHeight, minuteColor); | ||
| markerPainter.fillRect(x, | ||
| lowerMarkerYPos, | ||
| 2, | ||
| imageHeight - lowerMarkerYPos, | ||
| minuteColor); | ||
| markerPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); | ||
| } | ||
| } | ||
|
|
||
| // draw hotcue markers | ||
| if (!cueInfos.isEmpty()) { | ||
| PainterScope painterScope(&markerPainter); | ||
| for (const auto& cueInfo : cueInfos) { | ||
| if (cueInfo.getType() != mixxx::CueType::HotCue) { | ||
| continue; | ||
| } | ||
|
|
||
| auto positionMillis = cueInfo.getStartPositionMillis(); | ||
| if (!positionMillis.has_value()) { | ||
| continue; | ||
| } | ||
|
|
||
| // allow negative positions (preroll) | ||
| if (*positionMillis > trackDurationMillis) { | ||
| continue; | ||
| } | ||
|
|
||
| const int x = static_cast<int>( | ||
| (*positionMillis / trackDurationMillis) * imageWidth); | ||
|
|
||
| if (x < 0 || x >= imageWidth) { | ||
| continue; | ||
| } | ||
|
|
||
| // use same rendering style as deck overview: | ||
| // contrasting border line + bright fill line | ||
| auto color = cueInfo.getColor(); | ||
| if (!color.has_value()) { | ||
| continue; | ||
| } | ||
|
|
||
| QColor fillColor = QColor::fromRgb(*color).lighter(110); | ||
| QColor borderColor = Color::chooseContrastColor( | ||
| QColor::fromRgb(*color), 120); | ||
|
|
||
| // draw border/shadow line (1px wide, offset by -1) | ||
| markerPainter.setPen(borderColor); | ||
| markerPainter.drawLine(x - 1, 0, x - 1, imageHeight); | ||
|
|
||
| // draw main bright marker line (1px wide) | ||
| markerPainter.setPen(fillColor); | ||
| markerPainter.drawLine(x, 0, x, imageHeight); | ||
| } | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a need for the intermediate baseColor?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried a few ways to get the lines to be as distinct as on the deck overview but struggled 10px made it almost bearable I'll see what I can do now and no
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Okay, that's because this paints the full-width waveform and the scaling to desired width happens in OverviewCache (IIRC this has been adopted from CoverArtCache), hence no fixed width here will guarantee that the markers have adequate width in the final image in the library. Options are a) pass the resized image here (to a new function) to paint the hotcue overlay, b) do the scaling here and paint hotcues on top, or c) ... Back'n forth of a) is not ideal (heayv QImage), so it's b) unless you/ai figure another way.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw, please remove those |
||
|
|
||
| return normImage; | ||
| } | ||
|
|
||
|
|
||
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 set/reset the CompositionMode for each marker?
Consider using PainterScope.
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.
✅ now using PainterScope instead of manual setCompositionMode() calls