source: synfigstudio/trunk/fuentes/src/gui/preview.cpp @ 481

Last change on this file since 481 was 481, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 36.6 KB
Line 
1/* === S Y N F I G ========================================================= */
2/*!     \file preview.cpp
3**      \brief Preview implementation file
4**
5**      $Id$
6**
7**      \legal
8**      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9**      Copyright (c) 2007 Chris Moore
10**      Copyright (c) 2011 Nikita Kitaev
11**      Coypright (c) 2012 Yu Chen
12**
13**      This package is free software; you can redistribute it and/or
14**      modify it under the terms of the GNU General Public License as
15**      published by the Free Software Foundation; either version 2 of
16**      the License, or (at your option) any later version.
17**
18**      This package is distributed in the hope that it will be useful,
19**      but WITHOUT ANY WARRANTY; without even the implied warranty of
20**      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21**      General Public License for more details.
22**      \endlegal
23*/
24/* ========================================================================= */
25
26/* === H E A D E R S ======================================================= */
27
28#ifdef USING_PCH
29#       include "pch.h"
30#else
31#ifdef HAVE_CONFIG_H
32#       include <config.h>
33#endif
34
35#include "preview.h"
36#include "app.h"
37#include "audiocontainer.h"
38#include <gtkmm/stock.h>
39#include <gtkmm/separator.h>
40#include <gdkmm/general.h>
41
42#include <synfig/target_scanline.h>
43#include <synfig/target_cairo.h>
44#include <synfig/surface.h>
45
46#include <algorithm>
47#include "asyncrenderer.h"
48
49#include "general.h"
50
51#include <cmath>
52#include <cassert>
53#include <algorithm>
54#include <cstdio>
55#include <ctype.h>
56#include <math.h>
57#include <synfig/string.h>
58
59#endif
60
61/* === U S I N G =========================================================== */
62
63using namespace std;
64using namespace etl;
65using namespace synfig;
66using namespace studio;
67
68#define tolower ::tolower
69
70/* === M A C R O S ========================================================= */
71
72/* === G L O B A L S ======================================================= */
73
74/* === P R O C E D U R E S ================================================= */
75
76/* === M E T H O D S ======================================================= */
77
78/* === E N T R Y P O I N T ================================================= */
79
80class studio::Preview::Preview_Target_Cairo : public Target_Cairo
81{
82        Preview *prev;
83public:
84        Preview_Target_Cairo(Preview *prev_): prev(prev_)
85        {
86        }
87       
88        virtual bool set_rend_desc(RendDesc *r)
89        {
90                return Target_Cairo::set_rend_desc(r);
91        }
92       
93        virtual bool obtain_surface(cairo_surface_t*& surface)
94        {
95                int w=desc.get_w(), h=desc.get_h();
96                surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
97                return true;
98        }
99
100        bool put_surface(cairo_surface_t *surf, synfig::ProgressCallback *cb)
101        {
102                if(!prev)
103                        return false;
104                gamma_filter(surf, studio::App::gamma);
105                if(cairo_surface_status(surf))
106                {
107                        if(cb) cb->error(_("Cairo Surface bad status"));
108                        return false;
109                }
110                FlipbookElem    fe;
111                Preview pr = *prev;
112                float time = get_canvas()->get_time();
113                fe.t = time;
114                fe.surface=cairo_surface_reference(surf);
115                prev->push_back(fe);
116                prev->signal_changed()();
117               
118                cairo_surface_destroy(surf);
119                return true;
120        }
121};
122
123class studio::Preview::Preview_Target : public Target_Scanline
124{
125        Surface surface;
126
127        sigc::signal<void, const Preview_Target *>              signal_frame_done_;
128
129        int scanline;
130
131        double  tbegin,tend;
132
133        int             nframes,curframe;
134
135public:
136
137        Preview_Target()
138        {
139                set_alpha_mode(TARGET_ALPHA_MODE_FILL);
140                tbegin = tend = 0;
141                scanline = 0;
142                nframes = curframe = 0;
143        }
144
145        const RendDesc &get_rend_desc() const { return desc; }
146
147        virtual bool set_rend_desc(RendDesc *r)
148        {
149                if(Target_Scanline::set_rend_desc(r))
150                {
151                        /*synfig::warning("Succeeded in setting the desc to new one: %d x %d, %.2f fps [%.2f,%.2f]",
152                                                        desc.get_w(),desc.get_h(),desc.get_frame_rate(),
153                                        (float)desc.get_time_start(),(float)desc.get_time_end());*/
154
155                        surface.set_wh(desc.get_w(),desc.get_h());
156
157                        curframe = 0;
158                        nframes = (int)floor((desc.get_time_end() - desc.get_time_start())*desc.get_frame_rate());
159
160                        tbegin = desc.get_time_start();
161                        tend = tbegin + nframes/desc.get_frame_rate();
162
163                        return true;
164                }
165                return false;
166        }
167
168        virtual bool start_frame(ProgressCallback */*cb*/=NULL)
169        {
170                return true;
171        }
172
173        virtual void end_frame()
174        {
175                //ok... notify our subscribers...
176                signal_frame_done_(this);
177                curframe += 1;
178                //synfig::warning("Finished the frame stuff, and changed time to %.3f",t);
179        }
180
181        virtual Color * start_scanline(int scanline)
182        {
183                return surface[scanline];
184        }
185
186        virtual bool end_scanline() {return true;}
187
188        sigc::signal<void, const Preview_Target *>      &signal_frame_done() {return signal_frame_done_;}
189
190        const Surface &get_surface() const {return surface;}
191
192        float get_time() const
193        {
194                double time = ((nframes-curframe)/(double)nframes)*tbegin
195                                        + ((curframe)/(double)nframes)*tend;
196                return time;
197        }
198};
199
200studio::Preview::Preview(const studio::CanvasView::LooseHandle &h, float zoom, float f):
201        canvasview(h),
202        zoom(zoom),
203        fps(f),
204        begintime(),
205        endtime(),
206        overbegin(false),
207        overend(false),
208        use_cairo(),
209        quality(),
210        global_fps()
211{ }
212
213void studio::Preview::set_canvasview(const studio::CanvasView::LooseHandle &h)
214{
215        canvasview = h;
216
217        if(canvasview)
218        {
219                //perhaps reset override values...
220                const RendDesc &r = canvasview->get_canvas()->rend_desc();
221                if(r.get_frame_rate())
222                {
223                        float rate = 1/r.get_frame_rate();
224                        overbegin = false; begintime = r.get_time_start() + r.get_frame_start()*rate;
225                        overend = false; endtime = r.get_time_start() + r.get_frame_end()*rate;
226                }
227        }
228}
229
230studio::Preview::~Preview()
231{
232        signal_destroyed_(this); //tell anything that attached to us, we're dying
233}
234
235void studio::Preview::render()
236{
237        if(canvasview)
238        {
239
240                //render description
241                RendDesc desc = get_canvas()->rend_desc();
242
243                //set the global fps of the preview
244                set_global_fps(desc.get_frame_rate());
245
246                desc.clear_flags();
247
248                int neww = (int)floor(desc.get_w()*zoom+0.5),
249                        newh = (int)floor(desc.get_h()*zoom+0.5);
250                float newfps = fps;
251
252                /*synfig::warning("Setting the render description: %d x %d, %f fps, [%f,%f]",
253                                                neww,newh,newfps, overbegin?begintime:(float)desc.get_time_start(),
254                                                overend?endtime:(float)desc.get_time_end());*/
255                desc.set_w(neww);
256                desc.set_h(newh);
257                desc.set_frame_rate(newfps);
258                desc.set_render_excluded_contexts(false);
259
260                if(overbegin)
261                {
262                        desc.set_time_start(begintime);
263                        //synfig::warning("Set start time to %.2f...",(float)desc.get_time_start());
264                }
265                if(overend)
266                {
267                        desc.set_time_end(endtime);
268                        //synfig::warning("Set end time to %.2f...",(float)desc.get_time_end());
269                }
270
271                //setting the description
272
273                //HACK - add on one extra frame because the renderer can't render the last frame
274                // Maybe this can be removed now because the next_time(&t) was refacgorized to consider the last frame too
275                //TODO: do not use get_time on Preview_Target
276                desc.set_time_end(desc.get_time_end() + 1.000001/fps);
277
278                // Render using a Preview target (cairo or not)
279                etl::handle<Target> target;
280                if(use_cairo)
281                {
282                        target = etl::handle<Target>::cast_dynamic(new Preview_Target_Cairo(this));
283                }
284                else
285                {
286                        etl::handle<Preview_Target> t_target = new Preview_Target;
287                        //connect our information to his...
288                        t_target->signal_frame_done().connect(sigc::mem_fun(*this,&Preview::frame_finish));
289                        target =etl::handle<Target>::cast_dynamic(t_target);
290                }
291                //set the options
292                target->set_canvas(get_canvas());
293                target->set_quality(quality);
294                // Set the render description
295                target->set_rend_desc(&desc);
296
297                //... first we must clear our current selves of space
298                frames.resize(0);
299
300                //now tell it to go... with inherited prog. reporting...
301                if(renderer) renderer->stop();
302                renderer = new AsyncRenderer(target);
303                renderer->start();
304        }
305}
306
307void studio::Preview::clear()
308{
309        frames.clear();
310}
311
312
313static void free_guint8(const guint8 *mem)
314{
315        free((void*)mem);
316}
317
318void studio::Preview::frame_finish(const Preview_Target *targ)
319{
320        //copy image with time to next frame (can just push back)
321        FlipbookElem    fe;
322        float time = targ->get_time();
323        const Surface &surf = targ->get_surface();
324        const RendDesc& r = targ->get_rend_desc();
325
326        //synfig::warning("Finished a frame at %f s",time);
327
328        //copy EVERYTHING!
329        PixelFormat pf(PF_RGB);
330        const int total_bytes(r.get_w()*r.get_h()*synfig::channels(pf));
331
332        //synfig::warning("Creating a buffer");
333        unsigned char *buffer((unsigned char*)malloc(total_bytes));
334
335        if(!buffer)
336                return;
337
338        //convert all the pixels to the pixbuf... buffer... thing...
339        //synfig::warning("Converting...");
340        convert_color_format(buffer, surf[0], surf.get_w()*surf.get_h(), pf, App::gamma);
341
342        //load time
343        fe.t = time;
344        //uses and manages the memory for the buffer...
345        //synfig::warning("Create a pixmap...");
346        fe.buf =
347        Gdk::Pixbuf::create_from_data(
348                buffer, // pointer to the data
349                Gdk::COLORSPACE_RGB, // the colorspace
350                ((pf&PF_A)==PF_A), // has alpha?
351                8, // bits per sample
352                surf.get_w(),   // width
353                surf.get_h(),   // height
354                surf.get_w()*synfig::channels(pf), // stride (pitch)
355                sigc::ptr_fun(free_guint8)
356        );
357
358        //add the flipbook element to the list (assume time is correct)
359        //synfig::info("Prev: Adding %f s to the list", time);
360        frames.push_back(fe);
361
362        signal_changed()();
363}
364
365#define IMAGIFY_BUTTON(button,stockid,tooltip) \
366        icon = manage(new Gtk::Image(Gtk::StockID(stockid), Gtk::ICON_SIZE_BUTTON)); \
367        button->set_tooltip_text(tooltip); \
368        button->add(*icon); \
369        button->set_relief(Gtk::RELIEF_NONE); \
370        button->show(); \
371        icon->set_padding(0,0); \
372        icon->show();
373
374Widget_Preview::Widget_Preview():
375        Gtk::Table(1, 5),
376        adj_time_scrub(Gtk::Adjustment::create(0, 0, 1000, 0, 10, 0)),
377        scr_time_scrub(adj_time_scrub),
378        b_loop(/*_("Loop")*/),
379        current_surface(NULL),
380        currentindex(-100000),//TODO get the value from canvas setting or preview option
381        audiotime(0),
382        adj_sound(Gtk::Adjustment::create(0, 0, 4)),
383        l_lasttime("0s"),
384        playing(false),
385        jackdial(NULL),
386        jack_enabled(false),
387        jack_is_playing(false),
388        jack_time(0),
389        jack_initial_time(0),
390        jack_offset(0),
391#ifdef WITH_JACK
392        jack_client(NULL),
393        jack_synchronizing(false),
394#endif
395        zoom_preview(true)
396{
397        //catch key press event for shortcut keys
398        signal_key_press_event().connect(sigc::mem_fun(*this, &Widget_Preview::on_key_pressed));
399
400        //connect to expose events
401        //signal_expose_event().connect(sigc::mem_fun(*this, &studio::Widget_Preview::redraw));
402
403        //manage all the change in values etc...
404
405        //1st row: preview content
406        preview_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
407        //pack preview content into scrolled window
408        preview_window.add(draw_area);
409
410        //preview window background color
411        Gdk::RGBA bg_color;
412        bg_color.set_red(54*256);
413        bg_color.set_blue(59*256);
414        bg_color.set_green(59*256);
415        draw_area.override_background_color(bg_color);
416
417
418        adj_time_scrub->signal_value_changed().connect(sigc::mem_fun(*this,&Widget_Preview::slider_move));
419        scr_time_scrub.signal_event().connect(sigc::mem_fun(*this,&Widget_Preview::scroll_move_event));
420        draw_area.signal_draw().connect(sigc::mem_fun(*this,&Widget_Preview::redraw));
421       
422        scr_time_scrub.set_draw_value(0);
423
424        timedisp = -1;
425
426        Gtk::Button *button = 0;
427        Gtk::Image *icon = 0;
428
429        #if 1
430
431        //2nd row: prevframe play/pause nextframe loop | halt-render re-preview erase-all 
432        toolbar = Gtk::manage(new class Gtk::HBox(false, 0));
433
434        //prev rendered frame
435        Gtk::Button *prev_framebutton;
436        Gtk::Image *icon0 = manage(new Gtk::Image(Gtk::StockID("synfig-animate_seek_prev_frame"), Gtk::ICON_SIZE_BUTTON));
437        prev_framebutton = manage(new class Gtk::Button());
438        prev_framebutton->set_tooltip_text(_("Prev frame"));
439        icon0->set_padding(0,0);
440        icon0->show();
441        prev_framebutton->add(*icon0);
442        prev_framebutton->set_relief(Gtk::RELIEF_NONE);
443        prev_framebutton->show();
444        prev_framebutton->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&Widget_Preview::seek_frame), -1));
445
446        toolbar->pack_start(*prev_framebutton, Gtk::PACK_SHRINK, 0);
447
448        { //play
449                Gtk::Image *icon = manage(new Gtk::Image(Gtk::StockID("synfig-animate_play"), Gtk::ICON_SIZE_BUTTON));
450                play_button = manage(new class Gtk::Button());
451                play_button->set_tooltip_text(_("Play"));
452                icon->set_padding(0,0);
453                icon->show();
454                play_button->add(*icon);
455                play_button->set_relief(Gtk::RELIEF_NONE);
456                play_button->show();
457                play_button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::on_play_pause_pressed));
458                toolbar->pack_start(*play_button, Gtk::PACK_SHRINK, 0);
459        }
460
461        { //pause
462                Gtk::Image *icon = manage(new Gtk::Image(Gtk::StockID("synfig-animate_pause"), Gtk::ICON_SIZE_BUTTON));
463                pause_button = manage(new class Gtk::Button());
464                pause_button->set_tooltip_text(_("Pause"));
465                icon->set_padding(0,0);
466                icon->show();
467                pause_button->add(*icon);
468                pause_button->set_relief(Gtk::RELIEF_NONE);
469                pause_button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::on_play_pause_pressed));
470                toolbar->pack_start(*pause_button, Gtk::PACK_SHRINK, 0);
471        }
472
473
474        //next rendered frame
475        Gtk::Button *next_framebutton;
476        Gtk::Image *icon2 = manage(new Gtk::Image(Gtk::StockID("synfig-animate_seek_next_frame"), Gtk::ICON_SIZE_BUTTON));
477        next_framebutton = manage(new class Gtk::Button());
478        next_framebutton->set_tooltip_text(_("Next frame"));
479        icon2->set_padding(0,0);
480        icon2->show();
481        next_framebutton->add(*icon2);
482        next_framebutton->set_relief(Gtk::RELIEF_NONE);
483        next_framebutton->show();
484        next_framebutton->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&Widget_Preview::seek_frame), 1));
485
486        toolbar->pack_start(*next_framebutton, Gtk::PACK_SHRINK, 0);
487
488        //spacing
489        Gtk::Alignment *space = Gtk::manage(new Gtk::Alignment());
490        space->set_size_request(8);
491        toolbar->pack_start(*space, false, true);
492
493
494        //loop
495        button = &b_loop;
496        IMAGIFY_BUTTON(button,"synfig-animate_loop", _("Loop"));
497        toolbar->pack_start(b_loop, Gtk::PACK_SHRINK,0);
498
499        //spacing
500        Gtk::Alignment *space1 = Gtk::manage(new Gtk::Alignment());
501        space1->set_size_request(24);
502        toolbar->pack_start(*space1, false, true);
503
504
505        //halt render
506        button = manage(new Gtk::Button(/*_("Halt Render")*/));
507        button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::stoprender));
508        IMAGIFY_BUTTON(button,Gtk::Stock::STOP, _("Halt render"));
509
510        toolbar->pack_start(*button, Gtk::PACK_SHRINK, 0);
511
512        //re-preview
513        button = manage(new Gtk::Button(/*_("Re-Preview")*/));
514        button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::repreview));
515        IMAGIFY_BUTTON(button, Gtk::Stock::EDIT, _("Re-preview"));
516
517        toolbar->pack_start(*button, Gtk::PACK_SHRINK, 0);
518
519        //erase all
520        button = manage(new Gtk::Button(/*_("Erase All")*/));
521        button->signal_clicked().connect(sigc::mem_fun(*this,&Widget_Preview::eraseall));
522        IMAGIFY_BUTTON(button, Gtk::Stock::CLEAR, _("Erase all rendered frame(s)"));
523
524        toolbar->pack_start(*button, Gtk::PACK_SHRINK, 0);
525
526        //spacing
527        Gtk::Alignment *space2 = Gtk::manage(new Gtk::Alignment());
528        space1->set_size_request(24);
529        toolbar->pack_start(*space2, false, true);
530
531        //jack
532        jackdial = Gtk::manage(new JackDial());
533#ifdef WITH_JACK
534        jack_dispatcher.connect(sigc::mem_fun(*this, &Widget_Preview::on_jack_sync));
535        jack_dispatcher.connect(sigc::mem_fun(*this, &Widget_Preview::on_jack_sync));
536
537        jackbutton = jackdial->get_toggle_jackbutton();
538        jackdial->signal_toggle_jack().connect(sigc::mem_fun(*this, &studio::Widget_Preview::toggle_jack_button));
539        jackdial->signal_offset_changed().connect(sigc::mem_fun(*this, &studio::Widget_Preview::on_jack_offset_changed));
540#endif
541        //FIXME: Hardcoded FPS!
542        jackdial->set_fps(24.f);
543        jackdial->set_offset(jack_offset);
544        if ( !getenv("SYNFIG_DISABLE_JACK") )
545                jackdial->show();
546        toolbar->pack_start(*jackdial, false, true);
547
548        //zoom preview
549        factor_refTreeModel = Gtk::ListStore::create(factors);
550        zoom_preview.set_model(factor_refTreeModel);
551        zoom_preview.property_has_frame() = true;
552        zoom_preview.signal_changed().connect(sigc::mem_fun(*this, &Widget_Preview::preview_draw));
553
554        Gtk::TreeModel::Row row = *(factor_refTreeModel->append());
555        row[factors.factor_id] = "1";
556        row[factors.factor_value] = "25%";
557
558        row = *(factor_refTreeModel->append());
559        row[factors.factor_id] = "2";
560        row[factors.factor_value] = "50%";
561
562        row = *(factor_refTreeModel->append());
563        row[factors.factor_id] = "3";
564        row[factors.factor_value] = "100%";
565
566        row = *(factor_refTreeModel->append());
567        row[factors.factor_id] = "4";
568        row[factors.factor_value] = "200%";
569
570        row = *(factor_refTreeModel->append());
571        row[factors.factor_id] = "5";
572        row[factors.factor_value] = _("Fit");
573        zoom_preview.set_entry_text_column(factors.factor_value);
574
575        Gtk::Entry* entry = zoom_preview.get_entry();
576        entry->set_text("100%"); //default zoom level
577        entry->set_icon_from_stock(Gtk::StockID("synfig-zoom"));
578        entry->signal_activate().connect(sigc::mem_fun(*this, &Widget_Preview::on_zoom_entry_activated));
579
580        //set the zoom widget width
581        zoom_preview.set_size_request(100, -1);
582        zoom_preview.show();
583
584        toolbar->pack_end(zoom_preview, Gtk::PACK_SHRINK, 0);
585
586        show_toolbar();
587
588        //3rd row: previewing frame numbering and rendered frame numbering
589        Gtk::HBox *status = manage(new Gtk::HBox);
590        status->pack_start(l_currenttime, Gtk::PACK_SHRINK, 5);
591        Gtk::Label *separator = manage(new Gtk::Label(" / "));
592        status->pack_start(*separator, Gtk::PACK_SHRINK, 0);
593        status->pack_start(l_lasttime, Gtk::PACK_SHRINK, 5);
594
595        status->show_all();
596
597        // attach all widgets   
598        attach(preview_window, 0, 1, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0);
599        attach(scr_time_scrub, 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK);
600        attach(*toolbar, 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL);
601        attach(*status, 0, 1, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK);
602        preview_window.show_all();
603        scr_time_scrub.show_all();
604
605        //if(draw_area.get_window()) gc_area = Gdk::GC::create(draw_area.get_window());
606        #endif
607}
608
609studio::Widget_Preview::~Widget_Preview()
610{
611        clear();
612}
613
614void studio::Widget_Preview::update()
615{
616        //the meat goes in this locker...
617        double time = adj_time_scrub->get_value();
618
619        //find the frame and display it...
620        if(preview)
621        {
622                #ifdef WITH_JACK
623                if (jack_enabled && !jack_synchronizing && !is_time_equal_to_current_frame(jack_time - jack_offset))
624                {
625                        jack_nframes_t sr = jack_get_sample_rate(jack_client);
626                        jack_nframes_t nframes = ((double)sr * (time + jack_offset));
627                        jack_transport_locate(jack_client, nframes);
628                }
629                #endif
630
631                if (fabs(soundProcessor.get_position() - time) > 0.5)
632                        soundProcessor.set_position(time);
633
634                //synfig::warning("Updating at %.3f s",time);
635
636                //use time to find closest frame...
637                studio::Preview::FlipBook::const_iterator       beg = preview->begin(),end = preview->end();
638                studio::Preview::FlipBook::const_iterator       i;
639
640                i = beg;
641
642                //go to current hint if need be...
643                if(currentindex >= 0 && currentindex < (int)preview->numframes())
644                {
645                        i = beg+currentindex;
646                }
647
648                //we can't have a picture if there are none to get
649                if(beg != end)
650                {
651                        //don't bother with binary search it will just be slower...
652
653                        //synfig::info("Search for time %f",time);
654
655                        //incrementally go in either direction
656                        //(bias downward towards beg, because that's what we want)
657                        for(;i != end;++i)
658                        {
659                                //synfig::info("Look at %f",i->t);
660                                if(i->t > time) break;
661                                //synfig::info("Go past...");
662                        }
663
664                        //if(i!=beg)--i;
665
666                        //bias down, so we can't be at end... and it still is valid...
667                        for(;i != beg;)
668                        {
669                                --i;
670                                //synfig::info("Look at %f",i->t);
671                                if(i->t <= time) break;
672                                //synfig::info("Go past...");
673                        }
674
675                        /*i = preview->begin(); end = preview->end();
676                        if(i == end) return;
677
678                        j = i;
679                        for(;i != end; j = i++)
680                        {
681                                if(i->t > time) break;
682                        }*/
683
684                        //we should be at a valid edge since we biased downward
685
686                        //don't get the closest, round down... (if we can)
687                        if(i == end)
688                        {
689                                synfig::error("i == end....");
690                                //assert(0);
691                                currentbuf.clear();
692                                current_surface=NULL;
693                                currentindex = 0;
694                                timedisp = -1;
695                        }else
696                        {
697                                currentbuf = i->buf;
698                                currentindex = i-beg;
699                                if(current_surface)
700                                        cairo_surface_destroy(current_surface);
701                                current_surface= cairo_surface_reference(i->surface);
702                                if(timedisp != i->t)
703                                {
704                                        timedisp = i->t;
705                                        //synfig::warning("Update at: %f seconds (%f s)",time,timedisp);
706                                        preview_draw();
707                                        //synfig::warning("success!");
708                                }
709                        }
710                }
711        }
712
713        //current frame in previewing
714        Glib::ustring timecode(Time((double)timedisp).round(preview->get_global_fps())
715                .get_string(preview->get_global_fps(), App::get_time_format()));
716        l_currenttime.set_text(timecode);
717
718}
719void studio::Widget_Preview::preview_draw()
720{
721        draw_area.queue_draw();//on_expose_event();
722}
723
724bool studio::Widget_Preview::redraw(const Cairo::RefPtr<Cairo::Context> &cr)
725{
726        //And render the drawing area
727        Glib::RefPtr<Gdk::Pixbuf> pxnew, px = currentbuf;
728        cairo_surface_t* cs;
729        bool use_cairo= preview->get_use_cairo();
730        if(use_cairo)
731        {
732                if(current_surface)
733                        cs=cairo_surface_reference(current_surface);
734                else
735                        return true;
736        }
737       
738        int dw = draw_area.get_width();
739        int dh = draw_area.get_height();
740       
741        if(use_cairo && !cs)
742                return true;
743        else if(!use_cairo && !px)
744                return true;
745        //made not need this line
746        //if ( draw_area.get_height() == 0 || px->get_height() == 0 || px->get_width() == 0)
747        //      return true;
748
749        //figure out the scaling factors...
750        float sx, sy;
751        float q = 1 / preview->get_zoom();
752        int nw, nh;
753        int w,h;
754       
755        // grab the source dimensions
756        if(use_cairo)
757        {
758                w=cairo_image_surface_get_width(cs);
759                h=cairo_image_surface_get_height(cs);
760        }
761        else
762        {
763                w=px->get_width();
764                h=px->get_height();
765        }
766
767        Gtk::Entry* entry = zoom_preview.get_entry();
768        String str(entry->get_text());
769        Glib::ustring text = str;
770        const char *c = text.c_str();
771
772        if (text == _("Fit") || text == "fit")
773        {
774                sx = dw / (float)w;
775                sy = dh/ (float)h;
776
777                //synfig::info("widget_preview redraw: now to scale the bitmap: %.3f x %.3f",sx,sy);
778
779                //round to smallest scale (fit entire thing in window without distortion)
780                if(sx > sy) sx = sy;
781
782                //cleanup previous size request
783                draw_area.set_size_request();
784        }
785
786        //limit zoom level from 0.01 to 10 times
787        else if (atof(c) > 1000)
788        {
789                sx = sy = 10 * q;
790        }
791
792        else if (atof(c) <= 0 )
793        {
794                sx = sy = 0 ;
795                draw_area.set_size_request(0, 0);
796        }
797
798        else sx = sy = atof(c) / 100 * q;
799
800        //scale to a new pixmap and then copy over to the window
801        nw = (int)(w * sx);
802        nh = (int)(h * sx);
803
804        if(nw == 0 || nh == 0)return true;
805
806        if(!use_cairo)
807                pxnew = px->scale_simple(nw, nh, Gdk::INTERP_NEAREST);
808
809        //except "Fit" or "fit", we need to set size request for scrolled window
810        if (text != _("Fit") && text != "fit")
811        {
812                draw_area.set_size_request(nw, nh);
813                dw = draw_area.get_width();
814                dh = draw_area.get_height();
815        }
816
817        //synfig::info("Now to draw to the window...");
818        //copy to window
819        Glib::RefPtr<Gdk::Window> wind = draw_area.get_window();
820        if(!wind) synfig::warning("The destination window is broken...");
821
822        if(!use_cairo)
823        {
824                /* Options for drawing...
825                        1) store with alpha, then clear and render with alpha every frame
826                                - more time consuming
827                                + more expandable
828                        2) store with just pixel info
829                                - less expandable
830                                + faster
831                                + better memory footprint
832                */
833                //px->composite(const Glib::RefPtr<Gdk::Pixbuf>& dest, int dest_x, int dest_y, int dest_width, int dest_height, double offset_x, double offset_y, double scale_x, double scale_y, InterpType interp_type, int overall_alpha) const
834
835                cr->save();
836                Gdk::Cairo::set_source_pixbuf(
837                        cr, //cairo context
838                        pxnew, //pixbuf
839                        //coordinates to place center of the preview window
840                        (dw - nw) / 2, (dh - nh) / 2
841                        );
842                cr->paint();
843                cr->restore();
844        }
845        else
846        {
847                cr->save();
848                cr->scale(sx, sx);
849                cairo_set_source_surface(cr->cobj(), cs, (dw - nw)/(2*sx), (dh - nh)/(2*sx));
850                cairo_pattern_set_filter(cairo_get_source(cr->cobj()), CAIRO_FILTER_NEAREST);
851                cairo_surface_destroy(cs);
852                cr->paint();
853                cr->restore();
854        }
855
856        //synfig::warning("Refresh the draw area");
857        //make sure the widget refreshes
858
859        return false;
860}
861
862bool studio::Widget_Preview::play_update()
863{
864        float diff = timer.pop_time();
865        //synfig::info("Play update: diff = %.2f",diff);
866
867        if(playing)
868        {
869                //we go to the next one...
870                double time = adj_time_scrub->get_value() + diff;
871#ifdef WITH_JACK
872                bool stop_on_end = true;
873#endif
874
875                if (jack_enabled)
876                {
877#ifdef WITH_JACK
878                        jack_position_t pos;
879                        jack_transport_state_t state = jack_transport_query(jack_client, &pos);
880                        if (state != JackTransportRolling && state != JackTransportStarting)
881                                { on_jack_sync(); return true; }
882                        jack_time = Time((Time::value_type)pos.frame/(Time::value_type)pos.frame_rate);
883                        time = jack_time - jack_offset;
884                        stop_on_end = false;
885#endif
886                }
887                else
888                {
889                        //time = soundProcessor.get_position();
890                }
891
892                if (fabs(soundProcessor.get_position() - time) > 0.5)
893                        soundProcessor.set_position(time);
894
895                //Looping conditions...
896                if(time >= adj_time_scrub->get_upper())
897                {
898                        if(get_loop_flag())
899                        {
900                                time = adj_time_scrub->get_lower();// + time-adj_time_scrub.get_upper();
901                                currentindex = 0;
902                        }else
903                        {
904                                time = adj_time_scrub->get_upper();
905                                adj_time_scrub->set_value(time);
906                                pause();
907                                update();
908
909                                //synfig::info("Play Stopped: time set to %f",adj_time_scrub.get_value());
910                                return false;
911                        }
912                }
913
914                //set the new time...
915                adj_time_scrub->set_value(time);
916                adj_time_scrub->value_changed();
917
918                //update the window to the correct image we might want to do this later...
919                //update();
920                //synfig::warning("Did update pu");
921        }
922        return true;
923}
924
925void studio::Widget_Preview::slider_move()
926{
927        //if(!playing)
928        {
929                update();
930                //synfig::warning("Did update sm");
931        }
932}
933
934//for other things updating the value changed signal...
935void studio::Widget_Preview::scrub_updated(double t)
936{
937        if (playing) on_play_pause_pressed();
938
939        //synfig::info("Scrubbing to %.3f, setting adj to %.3f",oldt,t);
940
941        if(adj_time_scrub->get_value() != t)
942        {
943                adj_time_scrub->set_value(t);
944                adj_time_scrub->value_changed();
945        }
946}
947
948void studio::Widget_Preview::disconnect_preview(Preview *prev)
949{
950        if(prev == preview)
951        {
952                preview = 0;
953                prevchanged.disconnect();
954                soundProcessor.clear();
955        }
956}
957
958void studio::Widget_Preview::set_preview(etl::handle<Preview>   prev)
959{
960        disconnect_preview(preview.get());
961
962        preview = prev;
963
964        synfig::info("Setting preview");
965
966        //stop playing the mini animation...
967        pause();
968
969        if(preview)
970        {
971                //set the internal values
972                float rate = preview->get_fps();
973                jackdial->set_fps(rate);
974                jackdial->set_offset(preview->get_jack_offset());
975                synfig::info("  FPS = %f",rate);
976                if(rate)
977                {
978                        float start = preview->get_begintime();
979                        float end = preview->get_endtime();
980
981                        rate = 1/rate;
982
983                        adj_time_scrub->set_lower(start);
984                        adj_time_scrub->set_upper(end);
985                        adj_time_scrub->set_value(start);
986                        adj_time_scrub->set_step_increment(rate);
987                        adj_time_scrub->set_page_increment(10*rate);
988
989                        //if the begin time and the end time are the same there is only a single frame
990                        singleframe = end==start;
991                }else
992                {
993                        adj_time_scrub->set_lower(0);
994                        adj_time_scrub->set_upper(0);
995                        adj_time_scrub->set_value(0);
996                        adj_time_scrub->set_step_increment(0);
997                        adj_time_scrub->set_page_increment(0);
998                        singleframe = true;
999                }
1000
1001                preview->get_canvas()->fill_sound_processor(soundProcessor);
1002                set_jack_enabled( preview && preview->get_canvasview()->get_jack_enabled_in_preview() );
1003
1004                //connect so future information will be found...
1005                prevchanged = prev->signal_changed().connect(sigc::mem_fun(*this,&Widget_Preview::whenupdated));
1006                prev->signal_destroyed().connect(sigc::mem_fun(*this,&Widget_Preview::disconnect_preview));
1007                update();
1008                //synfig::warning("Did update sp");
1009                queue_draw();
1010        }
1011}
1012
1013void studio::Widget_Preview::whenupdated()
1014{
1015        l_lasttime.set_text((Time((double)(--preview->end())->t)
1016                                                        .round(preview->get_global_fps())
1017                                                        .get_string(preview->get_global_fps(),App::get_time_format())));
1018        update();
1019}
1020
1021void studio::Widget_Preview::clear()
1022{
1023        disconnect_preview(preview.get());
1024        set_jack_enabled(false);
1025}
1026
1027void studio::Widget_Preview::play()
1028{
1029        if (playing) return;
1030        if(preview)
1031        {
1032                if (!jack_enabled && get_position() == get_time_end()) seek(get_time_start());
1033                soundProcessor.set_position(get_position());
1034                soundProcessor.set_playing(true);
1035
1036                //synfig::info("Playing at %lf",adj_time_scrub->get_value());
1037                //audiotime = adj_time_scrub->get_value();
1038                playing = true;
1039
1040                play_button->hide();
1041                pause_button->show();
1042
1043                //adj_time_scrub->set_value(adj_time_scrub->get_lower());
1044                update(); //we don't want to call play update because that will try to advance the timer
1045
1046                //approximate length of time in seconds, right?
1047                double rate = /*std::min(*/adj_time_scrub->get_step_increment()/*,1/30.0)*/;
1048                int timeout = (int)floor(1000*rate);
1049
1050                //synfig::info("        rate = %.3lfs = %d ms",rate,timeout);
1051
1052                //signal_play_(adj_time_scrub->get_value());
1053
1054                timecon = Glib::signal_timeout().connect(sigc::mem_fun(*this,&Widget_Preview::play_update),timeout);
1055                timer.reset();
1056        }
1057}
1058
1059void studio::Widget_Preview::pause()
1060{
1061        //synfig::warning("stopping");
1062        timecon.disconnect();
1063        playing = false;
1064        pause_button->hide();
1065        play_button->show();
1066        soundProcessor.set_playing(false);
1067}
1068
1069void studio::Widget_Preview::on_play_pause_pressed()
1070{
1071        bool play_flag;
1072        //! Commented out , build warnings
1073//      float begin = preview->get_begintime();
1074//      float end = preview->get_endtime();
1075//      float current = adj_time_scrub->get_value();
1076//      Gtk::Image *icon;
1077
1078        play_flag = !playing;
1079       
1080#ifdef WITH_JACK
1081        if (jack_enabled)
1082        {
1083                if (jack_is_playing) {
1084                        jack_transport_stop(jack_client);
1085                        on_jack_sync();
1086                } else
1087                        jack_transport_start(jack_client);
1088                return;
1089        }
1090#endif
1091
1092        if(play_flag) play(); else pause();
1093}
1094
1095void studio::Widget_Preview::seek_frame(int frames)
1096{
1097//      if(!frames)     return;
1098
1099        if(playing) on_play_pause_pressed();    //pause playing when seek frame called
1100
1101        double fps = preview->get_fps();
1102
1103        double currenttime = adj_time_scrub->get_value();
1104        int currentframe = (int)floor(currenttime * fps);
1105        Time newtime(double((currentframe + frames + 0.5) / fps));
1106       
1107        adj_time_scrub->set_value(newtime);
1108}
1109
1110bool studio::Widget_Preview::scroll_move_event(GdkEvent *event)
1111{
1112        switch(event->type)
1113        {
1114                case GDK_BUTTON_PRESS:
1115                {
1116                        if(event->button.button == 1 || event->button.button == 3)
1117                        {
1118                                pause();
1119                        }
1120                        break;
1121                }
1122
1123                default: break;
1124        }
1125
1126        return false;
1127}
1128
1129synfig::Time studio::Widget_Preview::get_position() const
1130        { return adj_time_scrub->get_value(); }
1131synfig::Time studio::Widget_Preview::get_time_start() const
1132        { return adj_time_scrub->get_lower(); }
1133synfig::Time studio::Widget_Preview::get_time_end() const
1134        { return adj_time_scrub->get_upper(); }
1135
1136void studio::Widget_Preview::seek(const synfig::Time &t)
1137{
1138        pause();
1139        adj_time_scrub->set_value(t);
1140}
1141
1142void studio::Widget_Preview::repreview()
1143{
1144        if(preview)
1145        {
1146                stoprender();
1147                pause();
1148                preview->get_canvasview()->preview_option();
1149        }
1150}
1151
1152void studio::Widget_Preview::stoprender()
1153{
1154        if(preview)
1155        {
1156                // don't crash if the render has already been stopped
1157                if (!preview->renderer)
1158                        return;
1159
1160#ifdef SINGLE_THREADED
1161                if (preview->renderer->updating)
1162                        preview->renderer->pause();
1163                else
1164#endif
1165                        preview->renderer.detach();
1166        }
1167}
1168
1169void studio::Widget_Preview::eraseall()
1170{
1171        pause();
1172        stoprender();
1173
1174        currentbuf.clear();
1175        if(current_surface)
1176                cairo_surface_destroy(current_surface);
1177        current_surface=NULL;
1178        currentindex = 0;
1179        timedisp = 0;
1180        queue_draw();
1181
1182        if(preview)
1183        {
1184                preview->clear();
1185        }
1186}
1187
1188void Widget_Preview::on_zoom_entry_activated()
1189{
1190        Gtk::Entry* entry = zoom_preview.get_entry();
1191        String str(entry->get_text());
1192        string digi = "0123456789";
1193        size_t first = str.find_first_of(digi);
1194
1195        if (first == string::npos)
1196        {
1197                entry->set_text(_("Fit"));
1198
1199                //release the focus to enable accelerator keys
1200                preview_window.grab_focus();
1201
1202                return ;
1203        }
1204
1205        size_t last = str.find_first_not_of(digi);
1206
1207        if (last == string::npos)
1208        {
1209                last = str.find_last_of(digi) + 1;
1210        }
1211
1212        if (first > last)
1213        {
1214                entry->set_text (_("Fit"));
1215        }
1216
1217        else entry->set_text(str.substr(first, last - first) + "%");
1218
1219        //release the focus to enable accelerator keys
1220        preview_window.grab_focus();
1221}
1222
1223void Widget_Preview::hide_toolbar()
1224{
1225        toolbar->hide();
1226        toolbarisshown = 0;
1227
1228        //release the focus to enable accelerator keys
1229        preview_window.grab_focus();
1230}
1231
1232void Widget_Preview::show_toolbar()
1233{
1234        toolbar->show();
1235        toolbarisshown = 1;
1236        toolbar->grab_focus();
1237}
1238
1239//shortcut keys TODO: customizable shortcut keys would be awesome.
1240bool studio::Widget_Preview::on_key_pressed(GdkEventKey *ev)
1241{
1242        //hide and show toolbar
1243        if (ev->keyval == gdk_keyval_from_name("h"))
1244        {
1245                if (toolbarisshown) hide_toolbar();
1246                else show_toolbar();
1247                return true;
1248        }
1249
1250        //previous rendered frame
1251        if (ev->keyval == gdk_keyval_from_name("a"))
1252        {
1253                if(playing) pause();
1254                seek_frame(-1);
1255                return true;
1256        }
1257
1258        //play/pause
1259        if (ev->keyval == gdk_keyval_from_name("s"))
1260        {
1261                on_play_pause_pressed();
1262                return true;
1263        }
1264
1265        //next render frame
1266        if (ev->keyval == gdk_keyval_from_name("d"))
1267        {
1268                if(playing) pause();
1269                seek_frame(+1);
1270                return true;
1271        }
1272
1273        //loop
1274        if (ev->keyval == gdk_keyval_from_name("f"))
1275        {
1276                if(get_loop_flag()) set_loop_flag(false);
1277                else set_loop_flag(true);
1278                return true;
1279        }
1280
1281        //zoom level switching
1282        //zoom to 25%
1283        Gtk::Entry* entry = zoom_preview.get_entry();
1284        Glib::ustring text = entry->get_text();
1285
1286        if (ev->keyval == gdk_keyval_from_name("1"))
1287        {
1288                if(entry->get_text() != "25%")
1289                {
1290                        entry->set_text("25%");
1291                }
1292                return true;
1293        }
1294
1295        if (ev->keyval == gdk_keyval_from_name("2"))
1296        {
1297                if(entry->get_text() != "50%")
1298                {
1299                        entry->set_text("50%");
1300                }
1301                return true;
1302        }
1303
1304        if (ev->keyval == gdk_keyval_from_name("3"))
1305        {
1306                if(entry->get_text() != "100%")
1307                {
1308                        entry->set_text("100%");
1309                }
1310                return true;
1311        }
1312
1313        if (ev->keyval == gdk_keyval_from_name("4"))
1314        {
1315                if(entry->get_text() != "200%")
1316                {
1317                        entry->set_text("200%");
1318                }
1319                return true;
1320        }
1321
1322        if (ev->keyval == gdk_keyval_from_name("5"))
1323        {
1324                if(entry->get_text() != _("Fit"))
1325                {
1326                        entry->set_text(_("Fit"));
1327                }
1328                return true;
1329        }
1330
1331        return false;
1332}
1333
1334bool
1335Widget_Preview::is_time_equal_to_current_frame(const synfig::Time &time)
1336{
1337        float fps = preview ? preview->get_fps() : 25.f;
1338        Time starttime = get_time_start();
1339        Time endtime = get_time_end();
1340
1341        synfig::Time t0 = get_position();
1342        synfig::Time t1 = time;
1343
1344        if (fps != 0.f) {
1345                t0 = t0.round(fps);
1346                t1 = t1.round(fps);
1347        }
1348
1349        t0 = std::max(starttime, std::min(endtime, t0));
1350        t1 = std::max(starttime, std::min(endtime, t1));
1351
1352        return t0.is_equal(t1);
1353}
1354
1355void Widget_Preview::on_show()
1356{
1357        Table::on_show();
1358        set_jack_enabled( preview && preview->get_canvasview()->get_jack_enabled_in_preview() );
1359}
1360
1361void Widget_Preview::on_hide()
1362{
1363        Table::on_hide();
1364        if (preview)
1365        {
1366                bool enabled = get_jack_enabled();
1367                set_jack_enabled(false);
1368                preview->get_canvasview()->set_jack_enabled_in_preview(enabled);
1369        }
1370        pause();
1371        stoprender();
1372}
1373
1374void Widget_Preview::set_jack_enabled(bool value) {
1375        if (jack_enabled == value) return;
1376
1377#ifdef WITH_JACK
1378        if (playing) pause();
1379        jack_enabled = value;
1380        if (jack_enabled)
1381        {
1382                // lock jack in canvas views
1383                App::jack_lock();
1384
1385                // initialize jack
1386                jack_client = jack_client_open("synfigstudiopreview", JackNullOption, 0);
1387                jack_set_sync_callback(jack_client, jack_sync_callback, this);
1388                if (jack_activate(jack_client) != 0)
1389                {
1390                        jack_client_close(jack_client);
1391                        jack_client = NULL;
1392                        jack_enabled = false;
1393                        App::jack_unlock();
1394                } else {
1395                        // remember time
1396                        on_jack_sync();
1397                        jack_initial_time = jack_time;
1398                }
1399        }
1400        else
1401        {
1402                // restore time
1403                jack_nframes_t sr = jack_get_sample_rate(jack_client);
1404                jack_nframes_t nframes = ((double)sr * (jack_initial_time));
1405                jack_transport_locate(jack_client, nframes);
1406
1407                // deinitialize jack
1408                jack_deactivate(jack_client);
1409                jack_client_close(jack_client);
1410                jack_client = NULL;
1411
1412                // unlock jack in canvas views
1413                App::jack_unlock();
1414        }
1415
1416        //jackdial->toggle_enable_jack(jack_enabled);
1417
1418        Gtk::IconSize iconsize=Gtk::IconSize::from_name("synfig-small_icon_16x16");
1419        Gtk::Image *icon;
1420        offset_widget = jackdial->get_offsetwidget();
1421
1422        if (jackbutton->get_active())
1423        {
1424                icon = manage(new Gtk::Image(Gtk::StockID("synfig-jack"),iconsize));
1425                jackbutton->remove();
1426                jackbutton->add(*icon);
1427                jackbutton->set_tooltip_text(_("Disable JACK"));
1428                icon->set_padding(0,0);
1429                icon->show();
1430
1431                offset_widget->show();
1432        }
1433        else
1434        {
1435                icon = manage(new Gtk::Image(Gtk::StockID("synfig-jack"),iconsize));
1436                jackbutton->remove();
1437                jackbutton->add(*icon);
1438                jackbutton->set_tooltip_text(_("Enable JACK"));
1439                icon->set_padding(0,0);
1440                icon->show();
1441
1442                offset_widget->hide();
1443        }
1444#endif
1445
1446        if (preview) preview->get_canvasview()->set_jack_enabled_in_preview( get_jack_enabled() );
1447}
1448
1449
1450#ifdef WITH_JACK
1451void Widget_Preview::toggle_jack_button()
1452{
1453        set_jack_enabled(!get_jack_enabled());
1454}
1455
1456void Widget_Preview::on_jack_offset_changed() {
1457        jack_offset = jackdial->get_offset();
1458        if (get_jack_enabled()) on_jack_sync();
1459}
1460
1461void Widget_Preview::on_jack_sync() {
1462        jack_position_t pos;
1463        jack_transport_state_t state = jack_transport_query(jack_client, &pos);
1464
1465        jack_is_playing = state == JackTransportRolling || state == JackTransportStarting;
1466        jack_time = Time((Time::value_type)pos.frame/(Time::value_type)pos.frame_rate);
1467
1468        if (playing != jack_is_playing)
1469        {
1470                if (jack_is_playing)
1471                        play();
1472                else
1473                        pause();
1474        }
1475
1476        if (!is_time_equal_to_current_frame(jack_time - jack_offset))
1477        {
1478                jack_synchronizing = true;
1479                seek(jack_time - jack_offset);
1480                jack_synchronizing = false;
1481        }
1482}
1483
1484int Widget_Preview::jack_sync_callback(jack_transport_state_t /* state */, jack_position_t * /* pos */, void *arg) {
1485        Widget_Preview *widget_preview = static_cast<Widget_Preview*>(arg);
1486        widget_preview->jack_dispatcher.emit();
1487        return 1;
1488}
1489#endif
Note: See TracBrowser for help on using the repository browser.