X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fwx%2Fvideo_waveform_plot.cc;h=2e45f3493b69e1e0af0f29f95dfb2c9dbe538c40;hb=f802565830bed9ec9e7ad6c16ccaa7b0700625d9;hp=357b2ca0cb6d9b83fbd6aa6f9c0b20678a5f366b;hpb=631f905234537d2b0a24e9b3ff40f08a17c0fd8b;p=dcpomatic.git diff --git a/src/wx/video_waveform_plot.cc b/src/wx/video_waveform_plot.cc index 357b2ca0c..2e45f3493 100644 --- a/src/wx/video_waveform_plot.cc +++ b/src/wx/video_waveform_plot.cc @@ -1,62 +1,79 @@ /* - Copyright (C) 2015 Carl Hetherington + Copyright (C) 2015-2021 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + DCP-o-matic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with DCP-o-matic. If not, see . */ -#include "video_waveform_plot.h" + #include "film_viewer.h" +#include "video_waveform_plot.h" #include "wx_util.h" -#include "lib/image.h" -#include "lib/raw_convert.h" #include "lib/dcp_video.h" +#include "lib/film.h" +#include "lib/image.h" +#include "lib/player_video.h" +#include #include #include #include -#include +#include #include + using std::cout; +using std::make_shared; +using std::max; using std::min; +using std::shared_ptr; using std::string; -using boost::weak_ptr; -using boost::shared_ptr; +using std::weak_ptr; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif +using dcp::locale_convert; + int const VideoWaveformPlot::_vertical_margin = 8; +int const VideoWaveformPlot::_pixel_values = 4096; +int const VideoWaveformPlot::_x_axis_width = 52; -VideoWaveformPlot::VideoWaveformPlot (wxWindow* parent, FilmViewer* viewer) + +VideoWaveformPlot::VideoWaveformPlot (wxWindow* parent, weak_ptr film, weak_ptr viewer) : wxPanel (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE) - , _dirty (true) - , _enabled (false) - , _component (0) - , _contrast (0) + , _film (film) { #ifndef __WXOSX__ SetDoubleBuffered (true); #endif - _viewer_connection = viewer->ImageChanged.connect (boost::bind (&VideoWaveformPlot::set_image, this, _1)); + auto fv = viewer.lock (); + DCPOMATIC_ASSERT (fv); + + _viewer_connection = fv->ImageChanged.connect (boost::bind(&VideoWaveformPlot::set_image, this, _1)); - Bind (wxEVT_PAINT, boost::bind (&VideoWaveformPlot::paint, this)); - Bind (wxEVT_SIZE, boost::bind (&VideoWaveformPlot::sized, this, _1)); + Bind (wxEVT_PAINT, boost::bind(&VideoWaveformPlot::paint, this)); + Bind (wxEVT_SIZE, boost::bind(&VideoWaveformPlot::sized, this, _1)); + Bind (wxEVT_MOTION, boost::bind(&VideoWaveformPlot::mouse_moved, this, _1)); SetMinSize (wxSize (640, 512)); SetBackgroundColour (wxColour (0, 0, 0)); } + void VideoWaveformPlot::paint () { @@ -71,12 +88,11 @@ VideoWaveformPlot::paint () return; } - wxGraphicsContext* gc = wxGraphicsContext::Create (dc); + auto gc = wxGraphicsContext::Create (dc); if (!gc) { return; } - int const axis_x = 48; int const height = _waveform->size().height; gc->SetPen (wxPen (wxColour (255, 255, 255), 1, wxPENSTYLE_SOLID)); @@ -103,13 +119,13 @@ VideoWaveformPlot::paint () } for (int i = 0; i < label_gaps + 1; ++i) { - wxGraphicsPath p = gc->CreatePath (); + auto p = gc->CreatePath (); int const y = _vertical_margin + height - (i * height / label_gaps) - 1; p.MoveToPoint (label_width + 8, y); - p.AddLineToPoint (axis_x, y); + p.AddLineToPoint (_x_axis_width - 4, y); gc->StrokePath (p); int x = 4; - int const n = i * 4096 / label_gaps; + int const n = i * _pixel_values / label_gaps; if (n < 10) { x += extra[0]; } else if (n < 100) { @@ -117,16 +133,17 @@ VideoWaveformPlot::paint () } else if (n < 1000) { x += extra[2]; } - gc->DrawText (std_to_wx (raw_convert (n)), x, y - (label_height / 2)); + gc->DrawText (std_to_wx(locale_convert(n)), x, y - (label_height / 2)); } wxImage waveform (_waveform->size().width, height, _waveform->data()[0], true); wxBitmap bitmap (waveform); - gc->DrawBitmap (bitmap, axis_x + 4, _vertical_margin, _waveform->size().width, height); + gc->DrawBitmap (bitmap, _x_axis_width, _vertical_margin, _waveform->size().width, height); delete gc; } + void VideoWaveformPlot::create_waveform () { @@ -136,71 +153,70 @@ VideoWaveformPlot::create_waveform () return; } - dcp::Size const size = _image->size(); - _waveform.reset (new Image (AV_PIX_FMT_RGB24, dcp::Size (size.width, size.height), true)); + auto const image_size = _image->size(); + int const waveform_height = GetSize().GetHeight() - _vertical_margin * 2; + _waveform = make_shared(AV_PIX_FMT_RGB24, dcp::Size (image_size.width, waveform_height), true); - for (int x = 0; x < size.width; ++x) { + for (int x = 0; x < image_size.width; ++x) { /* Work out one vertical `slice' of waveform pixels. Each value in strip is the number of samples in image with the corresponding group of values. */ - int strip[size.height]; - for (int i = 0; i < size.height; ++i) { - strip[i] = 0; - } + int strip[waveform_height]; + memset (strip, 0, waveform_height * sizeof(int)); int* ip = _image->data (_component) + x; - for (int y = 0; y < size.height; ++y) { - strip[*ip * size.height / 4096]++; - ip += size.width; + for (int y = 0; y < image_size.height; ++y) { + strip[*ip * waveform_height / _pixel_values]++; + ip += image_size.width; } /* Copy slice into the waveform */ uint8_t* wp = _waveform->data()[0] + x * 3; - for (int y = size.height - 1; y >= 0; --y) { - wp[0] = wp[1] = wp[2] = min (255, (strip[y] * 255 / size.height) * _contrast); + for (int y = waveform_height - 1; y >= 0; --y) { + wp[0] = wp[1] = wp[2] = min (255, (strip[y] * 255 / waveform_height) * _contrast); wp += _waveform->stride()[0]; } } _waveform = _waveform->scale ( - dcp::Size (GetSize().GetWidth() - 32, GetSize().GetHeight() - _vertical_margin * 2), - dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGB24, false, true + dcp::Size (GetSize().GetWidth() - _x_axis_width, waveform_height), + dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, false, false ); } -static void -note () -{ - -} void -VideoWaveformPlot::set_image (weak_ptr image) +VideoWaveformPlot::set_image (shared_ptr image) { if (!_enabled) { return; } - shared_ptr pv = image.lock (); - _image = DCPVideo::convert_to_xyz (pv, boost::bind (¬e)); + /* We must copy the PlayerVideo here as we will call ::image() on it, potentially + with a different pixel_format than was used when ::prepare() was called. + */ + _image = DCPVideo::convert_to_xyz (image->shallow_copy(), [](dcp::NoteType, string) {}); _dirty = true; Refresh (); } + void VideoWaveformPlot::sized (wxSizeEvent &) { _dirty = true; } + void VideoWaveformPlot::set_enabled (bool e) { _enabled = e; } + void VideoWaveformPlot::set_component (int c) { @@ -209,6 +225,7 @@ VideoWaveformPlot::set_component (int c) Refresh (); } + /** Set `contrast', i.e. a fudge multiplication factor to make low-level signals easier to see, * between 0 and 256. */ @@ -219,3 +236,35 @@ VideoWaveformPlot::set_contrast (int b) _dirty = true; Refresh (); } + + +void +VideoWaveformPlot::mouse_moved (wxMouseEvent& ev) +{ + if (!_image) { + return; + } + + if (_dirty) { + create_waveform (); + _dirty = false; + } + + auto film = _film.lock (); + if (!film) { + return; + } + + auto const full = film->frame_size (); + + auto const xs = static_cast (full.width) / _waveform->size().width; + int const x1 = max (0, min (full.width - 1, int (floor (ev.GetPosition().x - _x_axis_width - 0.5) * xs))); + int const x2 = max (0, min (full.width - 1, int (floor (ev.GetPosition().x - _x_axis_width + 0.5) * xs))); + + auto const ys = static_cast (_pixel_values) / _waveform->size().height; + int const fy = _waveform->size().height - (ev.GetPosition().y - _vertical_margin); + int const y1 = max (0, min (_pixel_values - 1, int (floor (fy - 0.5) * ys))); + int const y2 = max (0, min (_pixel_values - 1, int (floor (fy + 0.5) * ys))); + + MouseMoved (x1, x2, y1, y2); +}