diff --git a/libaegisub/common/color.cpp b/libaegisub/common/color.cpp index e46a6cbdf1..d6484db97b 100644 --- a/libaegisub/common/color.cpp +++ b/libaegisub/common/color.cpp @@ -47,7 +47,10 @@ std::string Color::GetHexFormatted(bool rgba) const { } std::string Color::GetRgbFormatted() const { - return agi::format("rgb(%d, %d, %d)", r, g, b); + if (a) + return agi::format("rgba(%d, %d, %d, %d)", r, g, b, a); + else + return agi::format("rgb(%d, %d, %d)", r, g, b); } } diff --git a/libaegisub/common/option.cpp b/libaegisub/common/option.cpp index c75aad91f3..ad2fd897dc 100644 --- a/libaegisub/common/option.cpp +++ b/libaegisub/common/option.cpp @@ -113,6 +113,7 @@ class ConfigVisitor final : public json::ConstVisitor { if ((size == 4 && string[0] == '#') || (size == 7 && string[0] == '#') || (size >= 10 && string.starts_with("rgb(")) || + (size >= 13 && string.starts_with("rgba(")) || ((size == 9 || size == 10) && string.starts_with("&H"))) { values.push_back(std::make_unique(name, agi::Color(string))); diff --git a/libaegisub/common/parser.cpp b/libaegisub/common/parser.cpp index 9301c1510c..ad81d346e5 100644 --- a/libaegisub/common/parser.cpp +++ b/libaegisub/common/parser.cpp @@ -88,6 +88,7 @@ struct color_grammar : qi::grammar { css_color = "rgb(" >> blank >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> blank >> ')' + | "rgba(" >> blank >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> blank >> ')' | '#' >> hex_byte >> hex_byte >> hex_byte | '#' >> hex_char >> hex_char >> hex_char ; diff --git a/src/audio_display.cpp b/src/audio_display.cpp index 0011a846a8..944cfcee28 100644 --- a/src/audio_display.cpp +++ b/src/audio_display.cpp @@ -898,8 +898,17 @@ void AudioDisplay::PaintMarkers(wxDC &dc, TimeRange updtime) { int marker_x = RelativeXFromTime(marker->GetPosition()); - dc.SetPen(marker->GetStyle()); - dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height); + if (marker->GetWidth() == 0) { + dc.SetPen(marker->GetStyle()); + dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height); + } else { + int w = RelativeXFromTime((marker->GetPosition() + marker->GetWidth() - 1) + (ms_per_pixel - 1)) - marker_x - 1; // Add (ms_per_pixel - 1) to get "rounding up" behavior + + dc.SetBrush(wxBrush(marker->GetStyle().GetColour())); + dc.SetPen(*wxTRANSPARENT_PEN); + + dc.DrawRectangle(marker_x, audio_top, w, audio_height); + } if (marker->GetFeet() == AudioMarker::Feet_None) continue; diff --git a/src/audio_marker.cpp b/src/audio_marker.cpp index fe584891b8..0230562643 100644 --- a/src/audio_marker.cpp +++ b/src/audio_marker.cpp @@ -82,6 +82,36 @@ void AudioMarkerProviderKeyframes::GetMarkers(TimeRange const& range, AudioMarke out.push_back(&*a); } +class VideoPositionSnapPoint final : public AudioMarker { + int position = -1; + +public: + void SetPosition(int new_pos) { position = new_pos; } + + int GetPosition() const override { return position; } + FeetStyle GetFeet() const override { return Feet_None; } + wxPen GetStyle() const override { return wxPen{}; } +}; + +class VideoPositionRange final : public AudioMarker { + Pen style; + int position = -1; + int width = 0; + +public: + VideoPositionRange(bool previous) : style{previous ? "Colour/Audio Display/Previous Frame Range" : "Colour/Audio Display/Current Frame Range"} { } + + void SetPosition(int frame, int nextframe) { + position = frame + 1; + width = nextframe - frame - 1; + } + + int GetPosition() const override { return position; } + int GetWidth() const override { return width; } + FeetStyle GetFeet() const override { return Feet_None; } + wxPen GetStyle() const override { return style; } +}; + class VideoPositionMarker final : public AudioMarker { Pen style{"Colour/Audio Display/Play Cursor"}; int position = -1; @@ -92,7 +122,6 @@ class VideoPositionMarker final : public AudioMarker { int GetPosition() const override { return position; } FeetStyle GetFeet() const override { return Feet_None; } wxPen GetStyle() const override { return style; } - operator int() const { return position; } }; VideoPositionMarkerProvider::VideoPositionMarkerProvider(agi::Context *c) @@ -105,8 +134,16 @@ VideoPositionMarkerProvider::VideoPositionMarkerProvider(agi::Context *c) VideoPositionMarkerProvider::~VideoPositionMarkerProvider() { } -void VideoPositionMarkerProvider::Update(int frame_number) { +void VideoPositionMarkerProvider::SetPositions(int frame_number) { marker->SetPosition(vc->TimeAtFrame(frame_number)); + range1->SetPosition(vc->TimeAtFrame(frame_number - 1), vc->TimeAtFrame(frame_number)); + range2->SetPosition(vc->TimeAtFrame(frame_number), vc->TimeAtFrame(frame_number + 1)); + snap1->SetPosition(vc->TimeAtFrame(frame_number, agi::vfr::START)); + snap2->SetPosition(vc->TimeAtFrame(frame_number, agi::vfr::END)); +} + +void VideoPositionMarkerProvider::Update(int frame_number) { + SetPositions(frame_number); AnnounceMarkerMoved(); } @@ -114,17 +151,35 @@ void VideoPositionMarkerProvider::OptChanged(agi::OptionValue const& opt) { if (opt.GetBool()) { video_seek_slot.Unblock(); marker = std::make_unique(); - marker->SetPosition(vc->GetFrameN()); + range1 = std::make_unique(true); + range2 = std::make_unique(false); + snap1 = std::make_unique(); + snap2 = std::make_unique(); + SetPositions(vc->GetFrameN()); } else { video_seek_slot.Block(); marker.reset(); + range1.reset(); + range2.reset(); + snap1.reset(); + snap2.reset(); } } -void VideoPositionMarkerProvider::GetMarkers(const TimeRange &range, AudioMarkerVector &out) const { - if (marker && range.contains(*marker)) +void VideoPositionMarkerProvider::GetMarkers(const TimeRange &timerange, AudioMarkerVector &out) const { + if (marker && timerange.contains(marker->GetPosition())) out.push_back(marker.get()); + + for (auto range : {range1.get(), range2.get()}) + if (range && timerange.overlaps({range->GetPosition(), range->GetPosition() + range->GetWidth() - 1})) + out.push_back(range); +} + +void VideoPositionMarkerProvider::GetSnapMarkers(const TimeRange &timerange, AudioMarkerVector &out) const { + for (auto snap : {snap1.get(), snap2.get()}) + if (snap && timerange.contains(snap->GetPosition())) + out.push_back(snap); } SecondsMarkerProvider::SecondsMarkerProvider() diff --git a/src/audio_marker.h b/src/audio_marker.h index ba725ed4cd..7316421462 100644 --- a/src/audio_marker.h +++ b/src/audio_marker.h @@ -29,6 +29,8 @@ class Pen; class Project; class VideoController; class VideoPositionMarker; +class VideoPositionRange; +class VideoPositionSnapPoint; class wxPen; namespace agi { @@ -54,6 +56,10 @@ class AudioMarker { /// @return The marker's position in milliseconds virtual int GetPosition() const = 0; + /// @brief Get the marker's width + /// @return The marker's width if the marker should be a rectangle, or 0 if the marker should be drawn as a line (with the thickness specified by the style) + virtual int GetWidth() const { return 0; } + /// @brief Get the marker's drawing style /// @return A pen object describing the marker's drawing style virtual wxPen GetStyle() const = 0; @@ -73,9 +79,12 @@ class AudioMarkerProvider { ~AudioMarkerProvider() = default; public: - /// @brief Return markers in a time range + /// @brief Return markers in a time range to render in audio display virtual void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const = 0; + /// @brief Return markers in a time range to snap timed lines to + virtual void GetSnapMarkers(const TimeRange &range, AudioMarkerVector &out) const { GetMarkers(range, out); }; + DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener) }; @@ -143,10 +152,15 @@ class VideoPositionMarkerProvider final : public AudioMarkerProvider { VideoController *vc; std::unique_ptr marker; + std::unique_ptr range1; + std::unique_ptr range2; + std::unique_ptr snap1; + std::unique_ptr snap2; agi::signal::Connection video_seek_slot; agi::signal::Connection enable_opt_changed_slot; + void SetPositions(int frame_number); void Update(int frame_number); void OptChanged(agi::OptionValue const& opt); @@ -155,6 +169,7 @@ class VideoPositionMarkerProvider final : public AudioMarkerProvider { ~VideoPositionMarkerProvider(); void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const override; + void GetSnapMarkers(const TimeRange &range, AudioMarkerVector &out) const override; }; /// Marker provider for lines every second diff --git a/src/audio_timing_dialogue.cpp b/src/audio_timing_dialogue.cpp index 28d7ae53ec..873358d817 100644 --- a/src/audio_timing_dialogue.cpp +++ b/src/audio_timing_dialogue.cpp @@ -445,8 +445,8 @@ void AudioTimingControllerDialogue::GetMarkers(const TimeRange &range, AudioMark boost::upper_bound(markers, range.end(), marker_ptr_cmp()), back_inserter(out_markers)); - keyframes_provider.GetMarkers(range, out_markers); video_position_provider.GetMarkers(range, out_markers); + keyframes_provider.GetMarkers(range, out_markers); } void AudioTimingControllerDialogue::OnSelectedSetChanged() @@ -897,8 +897,8 @@ int AudioTimingControllerDialogue::SnapMarkers(int snap_range, std::vector