X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-studio%2Fsrc%2Fgui%2Fdock_navigator.cpp;fp=synfig-studio%2Fsrc%2Fgui%2Fdock_navigator.cpp;h=ad5061ef1dcb08b7e600cb8e8279108f64761963;hb=254e11cc9af58ba7978466da54cbebf69096eb91;hp=0000000000000000000000000000000000000000;hpb=c11c4966980ed301f40b3dcc24e4fbec525f93e3;p=synfig.git diff --git a/synfig-studio/src/gui/dock_navigator.cpp b/synfig-studio/src/gui/dock_navigator.cpp new file mode 100644 index 0000000..ad5061e --- /dev/null +++ b/synfig-studio/src/gui/dock_navigator.cpp @@ -0,0 +1,522 @@ +/* === S Y N F I G ========================================================= */ +/*! \file dock_navigator.cpp +** \brief Dock Nagivator File +** +** $Id$ +** +** \legal +** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley +** Copyright (c) 2007 Chris Moore +** +** This package 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 package 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. +** \endlegal +*/ +/* ========================================================================= */ + +/* === H E A D E R S ======================================================= */ + +#ifdef USING_PCH +# include "pch.h" +#else +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "dock_navigator.h" +#include "canvasview.h" +#include "workarea.h" + +#include +#include +#include +#include +#include + +#include + +#include "asyncrenderer.h" + +#include "general.h" + +#endif + +/* === U S I N G =========================================================== */ + +using namespace std; +using namespace etl; +using namespace synfig; + +/* === M A C R O S ========================================================= */ + +const double log_10_2 = log(2.0); + +/* === G L O B A L S ======================================================= */ + +/* === P R O C E D U R E S ================================================= */ + +/* === M E T H O D S ======================================================= */ + +/* === E N T R Y P O I N T ================================================= */ +studio::Widget_NavView::Widget_NavView(CanvasView::LooseHandle cv) +:canvview(cv), +adj_zoom(0,-4,4,1,2), +scrolling(false), +surface(new synfig::Surface) +{ + attach(drawto,0,4,0,1); + + attach(*manage(new Gtk::HSeparator),0,4,1,2,Gtk::SHRINK|Gtk::FILL,Gtk::SHRINK|Gtk::FILL); + + //zooming stuff + attach(zoom_print,0,1,2,3,Gtk::SHRINK|Gtk::FILL,Gtk::SHRINK|Gtk::FILL); + zoom_print.set_size_request(40,-1); + + Gtk::HScale *s = manage(new Gtk::HScale(adj_zoom)); + s->set_draw_value(false); + //s->set_update_policy(Gtk::UPDATE_DELAYED); + //s->signal_event().connect(sigc::mem_fun(*this,&Dock_Navigator::on_scroll_event)); + attach(*s,1,4,2,3,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL); + + show_all(); + + adj_zoom.signal_value_changed().connect(sigc::mem_fun(*this,&Widget_NavView::on_number_modify)); + + if(cv) + { + drawto.signal_expose_event().connect(sigc::mem_fun(*this,&Widget_NavView::on_expose_draw)); + drawto.signal_event().connect(sigc::mem_fun(*this,&Widget_NavView::on_mouse_event)); + + drawto.add_events(Gdk::BUTTON_MOTION_MASK|Gdk::BUTTON_PRESS_MASK); + + //get_canvas_view()->canvas_interface()->signal_dirty_preview() + // .connect(sigc::mem_fun(*this,&Widget_NavView::on_dirty_preview)); + get_canvas_view()->work_area->signal_rendering() + .connect(sigc::mem_fun(*this,&Widget_NavView::on_dirty_preview)); + + get_canvas_view()->work_area->signal_view_window_changed() + .connect(sigc::mem_fun(*this,&Widget_NavView::on_workarea_view_change)); + + //update with this canvas' view + on_workarea_view_change(); + + dirty = true; + queue_draw(); + } + + adj_zoom.set_value(0); +} + +studio::Widget_NavView::~Widget_NavView() +{ +} + + +static void freegu8(const guint8 *p) +{ + delete [] p; +} + +void studio::Widget_NavView::on_start_render() +{ + if(dirty) + { + //synfig::warning("Nav: Starting render"); + //synfig::warning("Nav: Rendering canvas"); + etl::handle targ = surface_target(surface.get()); + + targ->set_canvas(get_canvas_view()->get_canvas()); + targ->set_remove_alpha(); + targ->set_avoid_time_sync(); + targ->set_quality(get_canvas_view()->get_work_area()->get_quality()); + //synfig::info("Set the quality level to: %d", get_canvas_view()->get_work_area()->get_quality()); + + //this should set it to render a single frame + RendDesc r = get_canvas_view()->get_canvas()->rend_desc(); + r.set_time(get_canvas_view()->canvas_interface()->get_time()); + + //this changes the size of the canvas to the closest thing we can find + int sw = r.get_w(), sh = r.get_h(); + + //synfig::warning("Nav: source image is %d x %d", sw,sh); + + //resize so largest dimension is 128 + int dw = sw > sh ? 128 : sw*128/sh, + dh = sh > sw ? 128 : sh*128/sw; + + //synfig::warning("Nav: dest image is %d x %d", dw,dh); + + r.set_w(dw); + r.set_h(dh); + + //get the pw and ph + //float pw = r.get_pw(); + //float ph = r.get_ph(); + //synfig::warning("Nav: pixel size is %f x %f", pw,ph); + + //this renders that single frame + targ->set_rend_desc(&r); + + //synfig::warning("Nav: Building async renderer and starting it..."); + + renderer = new AsyncRenderer(targ); + renderer->signal_success().connect(sigc::mem_fun(*this,&Widget_NavView::on_finish_render)); + dirty = false; + renderer->start(); + } +} + +void studio::Widget_NavView::on_finish_render() +{ + //convert it into our pixmap + PixelFormat pf(PF_RGB); + + //synfig::warning("Nav: It hath succeeded!!!"); + + //assert(renderer && renderer->has_success()); + //synfig::warning("Nav: now we know it really succeeded"); + if(!*surface) + { + synfig::warning("dock_navigator: Bad surface"); + return; + } + + int w = 0, h = 0; + int dw = surface->get_w(); + int dh = surface->get_h(); + + if(prev) + { + w = prev->get_width(); + h = prev->get_height(); + } + + if(w != dw || h != dh || !prev) + { + const int total_bytes(dw*dh*synfig::channels(pf)); + + //synfig::warning("Nav: Updating the pixbuf to be the right size, etc. (%d bytes)", total_bytes); + + prev.clear(); + guint8 *bytes = new guint8[total_bytes]; //24 bits per pixel + + //convert into our buffered dataS + //synfig::warning("Nav: converting color format into buffer"); + convert_color_format((unsigned char *)bytes, (*surface)[0], dw*dh, pf, App::gamma); + + prev = + Gdk::Pixbuf::create_from_data( + bytes, // pointer to the data + Gdk::COLORSPACE_RGB, // the colorspace + ((pf&PF_A)==PF_A), // has alpha? + 8, // bits per sample + dw, // width + dh, // height + dw*synfig::channels(pf), // stride (pitch) + sigc::ptr_fun(freegu8) + ); + } + else + { + //synfig::warning("Nav: Don't need to resize"); + //convert into our buffered dataS + //synfig::warning("Nav: converting color format into buffer"); + if(prev) //just in case we're stupid + { + convert_color_format((unsigned char *)prev->get_pixels(), (*surface)[0], dw*dh, pf, App::gamma); + } + } + queue_draw(); +} + +/* zoom slider is on exponential scale + + map: -4,4 -> small number,1600 with 100 at 0 + + f(x) = 100*2^x +*/ + +static double unit_to_zoom(double f) +{ + return pow(2.0,f); +} + +static double zoom_to_unit(double f) +{ + if(f > 0) + { + return log(f) / log_10_2; + }else return -999999.0; +} + +bool studio::Widget_NavView::on_expose_draw(GdkEventExpose */*exp*/) +{ +#ifdef SINGLE_THREADED + // don't redraw if the previous redraw is still running single-threaded + // or we end up destroying the renderer that's rendering it + if (App::single_threaded && renderer && renderer->updating) + return false; +#endif + + //print out the zoom + //HACK kind of... + //zoom_print.set_text(strprintf("%.1f%%",100*unit_to_zoom(adj_zoom.get_value()))); + + //draw the good stuff + on_start_render(); + + //if we've got a preview etc. display it... + if(get_canvas_view() && prev) + { + //axis transform from units to pixel coords + float xaxis = 0, yaxis = 0; + + int canvw = get_canvas_view()->get_canvas()->rend_desc().get_w(); + //int canvh = get_canvas_view()->get_canvas()->rend_desc().get_h(); + + float pw = get_canvas_view()->get_canvas()->rend_desc().get_pw(); + float ph = get_canvas_view()->get_canvas()->rend_desc().get_ph(); + + int w = prev->get_width(); + int h = prev->get_height(); + + //scale up/down to the nearest pixel ratio... + //and center in center + int offx=0, offy=0; + + float sx, sy; + int nw,nh; + + sx = drawto.get_width() / (float)w; + sy = drawto.get_height() / (float)h; + + //synfig::warning("Nav redraw: now to scale the bitmap: %.3f x %.3f",sx,sy); + + //round to smallest scale (fit entire thing in window without distortion) + if(sx > sy) sx = sy; + //else sy = sx; + + //scaling and stuff + // the point to navpixel space conversion should be: + // (navpixels / canvpixels) * (canvpixels / canvsize) + // or (navpixels / prevpixels) * (prevpixels / navpixels) + xaxis = sx * w / (float)canvw; + yaxis = xaxis/ph; + xaxis /= pw; + + //scale to a new pixmap and then copy over to the window + nw = (int)(w*sx); + nh = (int)(h*sx); + + //must now center to be cool + offx = (drawto.get_width() - nw)/2; + offy = (drawto.get_height() - nh)/2; + + //trivial escape + if(nw == 0 || nh == 0)return true; + + //draw to drawing area + Glib::RefPtr gc = Gdk::GC::create(drawto.get_window()); + + //synfig::warning("Nav: Scaling pixmap to off (%d,%d) with size (%d,%d)", offx,offy,nw, nh); + Glib::RefPtr scalepx = prev->scale_simple(nw,nh,Gdk::INTERP_NEAREST); + + //synfig::warning("Nav: Drawing scaled bitmap"); + drawto.get_window()->draw_pixbuf( + gc, //GC + scalepx, //pixbuf + 0, 0, // Source X and Y + offx, offy, // Dest X and Y + -1,-1, // Width and Height + Gdk::RGB_DITHER_MAX, // RgbDither + 2, 2 // Dither offset X and Y + ); + + //draw fancy red rectangle around focus point + const Point &wtl = get_canvas_view()->work_area->get_window_tl(), + &wbr = get_canvas_view()->work_area->get_window_br(); + + gc->set_rgb_fg_color(Gdk::Color("#ff0000")); + gc->set_line_attributes(2,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER); + + //it must be clamped to the drawing area though + int l=0,rw=0,t=0,rh=0; + const Point fp = -get_canvas_view()->work_area->get_focus_point(); + + //get focus point in normal space + rw = (int)(abs((wtl[0]-wbr[0])*xaxis)); + rh = (int)(abs((wtl[1]-wbr[1])*yaxis)); + + //transform into pixel space + l = (int)(drawto.get_width()/2 + fp[0]*xaxis - rw/2); + t = (int)(drawto.get_height()/2 + fp[1]*yaxis - rh/2); + + //coord system: + // tl : (offx,offy) + // axis multipliers = xaxis,yaxis + //synfig::warning("Nav: tl (%f,%f), br (%f,%f)", wtl[0],wtl[1],wbr[0],wbr[1]); + //synfig::warning("Nav: tl (%f,%f), br (%f,%f)", wtl[0],wtl[1],wbr[0],wbr[1]); + //synfig::warning("Nav: Drawing Rectangle (%d,%d) with dim (%d,%d)", l,t,rw,rh); + drawto.get_window()->draw_rectangle(gc,false,l,t,rw,rh); + } + + return false; //draw everything else too +} + +void studio::Widget_NavView::on_dirty_preview() +{ + dirty = true; + queue_draw(); +} + +bool studio::Widget_NavView::on_scroll_event(GdkEvent *event) +{ + if(get_canvas_view() && get_canvas_view()->get_work_area()) + { + double z = unit_to_zoom(adj_zoom.get_value()); + + switch(event->type) + { + case GDK_BUTTON_PRESS: + { + if(event->button.button == 1) + { + scrolling = true; + get_canvas_view()->get_work_area()->set_zoom(z); + scrolling = false; + } + break; + } + + case GDK_MOTION_NOTIFY: + { + if(Gdk::ModifierType(event->motion.state) & Gdk::BUTTON1_MASK) + { + scrolling = true; + get_canvas_view()->get_work_area()->set_zoom(z); + scrolling = false; + } + break; + } + + default: + break; + } + } + + return false; +} + +void studio::Widget_NavView::on_number_modify() +{ + double z = unit_to_zoom(adj_zoom.get_value()); + zoom_print.set_text(strprintf("%.1f%%",z*100.0)); + //synfig::warning("Updating zoom to %f",adj_zoom.get_value()); + + if(get_canvas_view() && z != get_canvas_view()->get_work_area()->get_zoom()) + { + scrolling = true; + get_canvas_view()->get_work_area()->set_zoom(z); + scrolling = false; + } +} + +void studio::Widget_NavView::on_workarea_view_change() +{ + double wz = get_canvas_view()->get_work_area()->get_zoom(); + double z = zoom_to_unit(wz); + + //synfig::warning("Updating zoom to %f -> %f",wz,z); + if(!scrolling && z != adj_zoom.get_value()) + { + adj_zoom.set_value(z); + //adj_zoom.value_changed(); + } + queue_draw(); +} + +bool studio::Widget_NavView::on_mouse_event(GdkEvent * e) +{ + Point p; + bool setpos = false; + + if(e->type == GDK_BUTTON_PRESS && e->button.button == 1) + { + p[0] = e->button.x - drawto.get_width()/2; + p[1] = e->button.y - drawto.get_height()/2; + + setpos = true; + } + + if(e->type == GDK_MOTION_NOTIFY && (Gdk::ModifierType(e->motion.state) & Gdk::BUTTON1_MASK)) + { + p[0] = e->motion.x - drawto.get_width()/2; + p[1] = e->motion.y - drawto.get_height()/2; + + setpos = true; + } + + if(setpos && prev && get_canvas_view()) + { + const Point &tl = get_canvas_view()->get_canvas()->rend_desc().get_tl(); + const Point &br = get_canvas_view()->get_canvas()->rend_desc().get_br(); + + float max = abs((br[0]-tl[0]) / drawto.get_width()); + + if((float(prev->get_width()) / drawto.get_width()) < (float(prev->get_height()) / drawto.get_height())) + max = abs((br[1]-tl[1]) / drawto.get_height()); + + float signx = (br[0]-tl[0]) < 0 ? -1 : 1; + float signy = (br[1]-tl[1]) < 0 ? -1 : 1; + + Point pos; + + pos[0] = p[0] * max * signx; + pos[1] = p[1] * max * signy; + + get_canvas_view()->get_work_area()->set_focus_point(-pos); + + return true; + } + + return false; +} + +//Navigator Dock Definitions + +studio::Dock_Navigator::Dock_Navigator() +:Dock_CanvasSpecific("navigator",_("Navigator"),Gtk::StockID("synfig-navigator")) +{ + add(dummy); +} + +studio::Dock_Navigator::~Dock_Navigator() +{ +} + +void studio::Dock_Navigator::changed_canvas_view_vfunc(etl::loose_handle canvas_view) +{ + if(canvas_view) + { + Widget *v = canvas_view->get_ext_widget("navview"); + + if(!v) + { + v = new Widget_NavView(canvas_view); + canvas_view->set_ext_widget("navview",v); + } + + add(*v); + }else + { + clear_previous(); + //add(dummy); + } +}