X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fwx%2Ffilm_viewer.cc;h=bf082adc2e534824eb6d769cc32fa5c43cb2be85;hb=129afab72bfc026b5704c41a6bfc0f4b3a2c4033;hp=c9a72f225adce4535ad8e2a39360292f98572941;hpb=489e6a92b9aa940de64a886a1f9b5a420b50c7fc;p=dcpomatic.git diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index c9a72f225..bf082adc2 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -30,7 +30,9 @@ #include "lib/job_manager.h" #include "lib/film_state.h" #include "lib/options.h" +#include "lib/subtitle.h" #include "film_viewer.h" +#include "wx_util.h" using namespace std; using namespace boost; @@ -41,70 +43,181 @@ public: ThumbPanel (wxPanel* parent, Film* film) : wxPanel (parent) , _film (film) - , _image (0) - , _bitmap (0) - { - } + , _frame_rebuild_needed (false) + , _composition_needed (false) + {} + /** Handle a paint event */ void paint_event (wxPaintEvent& ev) { - if (!_bitmap) { + if (!_film || _film->num_thumbs() == 0) { + wxPaintDC dc (this); return; } + if (_frame_rebuild_needed) { + _image.reset (new wxImage (std_to_wx (_film->thumb_file (_index)))); + + _subtitles.clear (); + list > s = _film->thumb_subtitles (_index); + for (list >::iterator i = s.begin(); i != s.end(); ++i) { + _subtitles.push_back (SubtitleView (i->first, std_to_wx (i->second))); + } + + _frame_rebuild_needed = false; + + compose (); + _composition_needed = false; + } + + if (_composition_needed) { + compose (); + _composition_needed = false; + } + wxPaintDC dc (this); - dc.DrawBitmap (*_bitmap, 0, 0, false); + if (_bitmap) { + dc.DrawBitmap (*_bitmap, 0, 0, false); + } + + if (_film->with_subtitles ()) { + for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + dc.DrawBitmap (*i->bitmap, i->transformed_area.x, i->transformed_area.y, true); + } + } } + /** Handle a size event */ void size_event (wxSizeEvent &) { if (!_image) { return; } - resize (); + recompose (); + } + + /** @param n Thumbnail index */ + void set (int n) + { + _index = n; + _frame_rebuild_needed = true; + Refresh (); + } + + void set_film (Film* f) + { + _film = f; + if (!_film) { + clear (); + _frame_rebuild_needed = true; + Refresh (); + } else { + _frame_rebuild_needed = true; + Refresh (); + } + } + + /** Clear our thumbnail image */ + void clear () + { + _bitmap.reset (); + _image.reset (); + _subtitles.clear (); + } + + void recompose () + { + _composition_needed = true; + Refresh (); } - void resize () + DECLARE_EVENT_TABLE (); + +private: + + void compose () { + if (!_film || !_image) { + return; + } + + /* Size of the view */ int vw, vh; GetSize (&vw, &vh); - float const target = _film->format()->ratio_as_float (); + /* Cropped rectangle */ + Rectangle cropped_area ( + _film->crop().left, + _film->crop().top, + _image->GetWidth() - (_film->crop().left + _film->crop().right), + _image->GetHeight() - (_film->crop().top + _film->crop().bottom) + ); + + /* Target ratio */ + float const target = _film->format() ? _film->format()->ratio_as_float (_film) : 1.78; + + _transformed_image = _image->GetSubImage (wxRect (cropped_area.x, cropped_area.y, cropped_area.w, cropped_area.h)); + + float x_scale = 1; + float y_scale = 1; - delete _bitmap; if ((float (vw) / vh) > target) { /* view is longer (horizontally) than the ratio; fit height */ - _bitmap = new wxBitmap (_image->Scale (vh * target, vh)); + _transformed_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); + x_scale = vh * target / cropped_area.w; + y_scale = float (vh) / cropped_area.h; } else { /* view is shorter (horizontally) than the ratio; fit width */ - _bitmap = new wxBitmap (_image->Scale (vw, vw / target)); + _transformed_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); + x_scale = float (vw) / cropped_area.w; + y_scale = (vw / target) / cropped_area.h; } - Refresh (); - } + _bitmap.reset (new wxBitmap (_transformed_image)); - void load (string f) - { - delete _image; - _image = new wxImage (wxString (f.c_str(), wxConvUTF8)); - resize (); + for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + + i->transformed_area = transformed_subtitle_area ( + x_scale, y_scale, i->base_area, _film->subtitle_offset(), _film->subtitle_scale() + ); + + i->transformed_image = i->base_image; + i->transformed_image.Rescale (i->transformed_area.w, i->transformed_area.h, wxIMAGE_QUALITY_HIGH); + i->transformed_area.x -= _film->crop().left; + i->transformed_area.y -= _film->crop().top; + i->bitmap.reset (new wxBitmap (i->transformed_image)); + } } - void clear () + Film* _film; + shared_ptr _image; + wxImage _transformed_image; + /** currently-displayed thumbnail index */ + int _index; + shared_ptr _bitmap; + bool _frame_rebuild_needed; + bool _composition_needed; + + struct SubtitleView { - delete _bitmap; - _bitmap = 0; - delete _image; - _image = 0; - } + SubtitleView (Position p, wxString const & i) + : base_image (i) + { + base_area.x = p.x; + base_area.y = p.y; + base_area.w = base_image.GetWidth (); + base_area.h = base_image.GetHeight (); + } - DECLARE_EVENT_TABLE (); + Rectangle base_area; + Rectangle transformed_area; + wxImage base_image; + wxImage transformed_image; + shared_ptr bitmap; + }; -private: - Film* _film; - wxImage* _image; - wxBitmap* _bitmap; + list _subtitles; }; BEGIN_EVENT_TABLE (ThumbPanel, wxPanel) @@ -114,7 +227,7 @@ END_EVENT_TABLE () FilmViewer::FilmViewer (Film* f, wxWindow* p) : wxPanel (p) - , _film (f) + , _film (0) { _sizer = new wxBoxSizer (wxVERTICAL); SetSizer (_sizer); @@ -122,10 +235,10 @@ FilmViewer::FilmViewer (Film* f, wxWindow* p) _thumb_panel = new ThumbPanel (this, f); _sizer->Add (_thumb_panel, 1, wxEXPAND); - int const max = f ? f->num_thumbs() : 0; + int const max = f ? f->num_thumbs() - 1 : 0; _slider = new wxSlider (this, wxID_ANY, 0, 0, max); _sizer->Add (_slider, 0, wxEXPAND | wxLEFT | wxRIGHT); - load_thumbnail (0); + set_thumbnail (0); _slider->Connect (wxID_ANY, wxEVT_COMMAND_SLIDER_UPDATED, wxCommandEventHandler (FilmViewer::slider_changed), 0, this); @@ -133,129 +246,76 @@ FilmViewer::FilmViewer (Film* f, wxWindow* p) } void -FilmViewer::load_thumbnail (int n) +FilmViewer::set_thumbnail (int n) { if (_film == 0 || _film->num_thumbs() <= n) { return; } - int const left = _film->left_crop (); - int const right = _film->right_crop (); - int const top = _film->top_crop (); - int const bottom = _film->bottom_crop (); - - _thumb_panel->load (_film->thumb_file(n)); -} - -void -FilmViewer::reload_current_thumbnail () -{ - load_thumbnail (_slider->GetValue ()); + _thumb_panel->set (n); } void FilmViewer::slider_changed (wxCommandEvent &) { - reload_current_thumbnail (); -} - -string -FilmViewer::format_position_slider_value (double v) const -{ -#if 0 - stringstream s; - - if (_film && int (v) < _film->num_thumbs ()) { - int const f = _film->thumb_frame (int (v)); - s << f << " " << seconds_to_hms (f / _film->frames_per_second ()); - } else { - s << "-"; - } - - return s.str (); -#endif + set_thumbnail (_slider->GetValue ()); } void FilmViewer::film_changed (Film::Property p) { -#if 0 - if (p == Film::LEFT_CROP || p == Film::RIGHT_CROP || p == Film::TOP_CROP || p == Film::BOTTOM_CROP) { - reload_current_thumbnail (); - } else if (p == Film::THUMBS) { + switch (p) { + case Film::THUMBS: if (_film && _film->num_thumbs() > 1) { - _position_slider.set_range (0, _film->num_thumbs () - 1); + _slider->SetRange (0, _film->num_thumbs () - 1); } else { - _image.clear (); - _position_slider.set_range (0, 1); + _thumb_panel->clear (); + _slider->SetRange (0, 1); } - _position_slider.set_value (0); - reload_current_thumbnail (); - } else if (p == Film::FORMAT) { - reload_current_thumbnail (); - } else if (p == Film::CONTENT) { + _slider->SetValue (0); + set_thumbnail (0); + break; + case Film::CONTENT: setup_visibility (); _film->examine_content (); update_thumbs (); + break; + case Film::CROP: + case Film::FORMAT: + case Film::WITH_SUBTITLES: + case Film::SUBTITLE_OFFSET: + case Film::SUBTITLE_SCALE: + _thumb_panel->recompose (); + break; + default: + break; } -#endif } void FilmViewer::set_film (Film* f) { + if (_film == f) { + return; + } + _film = f; + _thumb_panel->set_film (_film); if (!_film) { - _thumb_panel->clear (); return; } -// _film->Changed.connect (sigc::mem_fun (*this, &FilmViewer::film_changed)); - + _film->Changed.connect (sigc::mem_fun (*this, &FilmViewer::film_changed)); + film_changed (Film::CROP); film_changed (Film::THUMBS); -} - -pair -FilmViewer::scaled_pixbuf_size () const -{ -#if 0 - if (_film == 0) { - return make_pair (0, 0); - } - - int const cw = _film->size().width - _film->left_crop() - _film->right_crop(); - int const ch = _film->size().height - _film->top_crop() - _film->bottom_crop(); - - float ratio = 1; - if (_film->format()) { - ratio = _film->format()->ratio_as_float() * ch / cw; - } - - Gtk::Allocation const a = _scroller.get_allocation (); - float const zoom = min (float (a.get_width()) / (cw * ratio), float (a.get_height()) / cw); - return make_pair (cw * zoom * ratio, ch * zoom); -#endif -} - -void -FilmViewer::update_scaled_pixbuf () -{ -#if 0 - pair const s = scaled_pixbuf_size (); - - if (s.first > 0 && s.second > 0 && _cropped_pixbuf) { - _scaled_pixbuf = _cropped_pixbuf->scale_simple (s.first, s.second, Gdk::INTERP_HYPER); - _image.set (_scaled_pixbuf); - } -#endif + setup_visibility (); } void FilmViewer::update_thumbs () { -#if 0 if (!_film) { return; } @@ -263,27 +323,25 @@ FilmViewer::update_thumbs () _film->update_thumbs_pre_gui (); shared_ptr s = _film->state_copy (); - shared_ptr o (new Options (s->dir ("thumbs"), ".tiff", "")); + shared_ptr o (new Options (s->dir ("thumbs"), ".png", "")); o->out_size = _film->size (); o->apply_crop = false; o->decode_audio = false; o->decode_video_frequency = 128; + o->decode_subtitles = true; - shared_ptr j (new ThumbsJob (s, o, _film->log ())); + shared_ptr j (new ThumbsJob (s, o, _film->log(), shared_ptr ())); j->Finished.connect (sigc::mem_fun (_film, &Film::update_thumbs_post_gui)); JobManager::instance()->add (j); -#endif } void FilmViewer::setup_visibility () { -#if 0 if (!_film) { return; } ContentType const c = _film->content_type (); - _position_slider.property_visible() = (c == VIDEO); -#endif + _slider->Show (c == VIDEO); }