/*
- Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2015-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
-#include "video_waveform_plot.h"
+
#include "film_viewer.h"
+#include "video_waveform_plot.h"
#include "wx_util.h"
-#include "lib/image.h"
#include "lib/dcp_video.h"
+#include "lib/film.h"
+#include "lib/image.h"
+#include "lib/player_video.h"
#include <dcp/locale_convert.h>
#include <dcp/openjpeg_image.h>
#include <wx/rawbmp.h>
#include <wx/graphics.h>
-#include <boost/bind.hpp>
+#include <boost/bind/bind.hpp>
#include <iostream>
+
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<const Film> film, weak_ptr<FilmViewer> 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 ()
{
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));
}
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) {
} else if (n < 1000) {
x += extra[2];
}
- gc->DrawText (std_to_wx (locale_convert<string> (n)), x, y - (label_height / 2));
+ gc->DrawText (std_to_wx(locale_convert<string>(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 ()
{
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<Image>(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<PlayerVideo> image)
+VideoWaveformPlot::set_image (shared_ptr<PlayerVideo> image)
{
if (!_enabled) {
return;
}
- shared_ptr<PlayerVideo> 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)
{
Refresh ();
}
+
/** Set `contrast', i.e. a fudge multiplication factor to make low-level signals easier to see,
* between 0 and 256.
*/
_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<double> (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<double> (_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);
+}