more updates
[synfig.git] / synfig-core / trunk / src / synfig / layer_pastecanvas.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file layer_pastecanvas.h
3 **      \brief Template Header
4 **
5 **      $Id: layer_pastecanvas.cpp,v 1.3 2005/01/24 03:08:18 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "layer_pastecanvas.h"
32 #include "string.h"
33 #include "time.h"
34 #include "context.h"
35 #include "paramdesc.h"
36 #include "renddesc.h"
37 #include "surface.h"
38 #include "value.h"
39 #include "valuenode.h"
40 #include "canvas.h"
41
42 #endif
43
44 /* === U S I N G =========================================================== */
45
46 using namespace etl;
47 using namespace std;
48 using namespace sinfg;
49
50 /* === M A C R O S ========================================================= */
51
52 #define MAX_DEPTH 10
53
54 //#ifdef __APPLE__
55 //#define SINFG_NO_CLIP
56 //#endif
57
58 /* === C L A S S E S ======================================================= */
59
60 class depth_counter     // Makes our recursive depth counter exception-safe
61 {
62         int *depth;
63 public:
64         depth_counter(int &x):depth(&x) { (*depth)++; }
65         ~depth_counter() { (*depth)--; }
66 };
67
68 /* === G L O B A L S ======================================================= */
69
70 SINFG_LAYER_INIT(Layer_PasteCanvas);
71 SINFG_LAYER_SET_NAME(Layer_PasteCanvas,"PasteCanvas");
72 SINFG_LAYER_SET_LOCAL_NAME(Layer_PasteCanvas,_("Paste Canvas"));
73 SINFG_LAYER_SET_CATEGORY(Layer_PasteCanvas,_("Default"));
74 SINFG_LAYER_SET_VERSION(Layer_PasteCanvas,"0.1");
75 SINFG_LAYER_SET_CVS_ID(Layer_PasteCanvas,"$Id: layer_pastecanvas.cpp,v 1.3 2005/01/24 03:08:18 darco Exp $");
76
77 /* === M E T H O D S ======================================================= */
78
79 Layer_PasteCanvas::Layer_PasteCanvas():
80         origin(0,0),
81         zoom(0),
82         time_offset(0)
83 {
84         children_lock=false;
85         do_not_muck_with_time_=false;
86         curr_time=Time::begin();
87 }
88
89 Layer_PasteCanvas::~Layer_PasteCanvas()
90 {
91 /*      if(canvas)
92                 canvas->parent_set.erase(this);
93 */
94
95         //if(canvas)DEBUGINFO(strprintf("%d",canvas->count()));
96         
97         set_sub_canvas(0);
98
99         //if(canvas && (canvas->is_inline() || !get_canvas() || get_canvas()->get_root()!=canvas->get_root()))
100         //      canvas->unref();
101 }
102
103 String
104 Layer_PasteCanvas::get_local_name()const
105 {
106         if(!canvas)     return _("Pasted Canvas");
107         if(canvas->is_inline()) return _("Inline Canvas");
108         if(canvas->get_root()==get_canvas()->get_root()) return '[' + canvas->get_id() + ']';
109                 
110         return '[' + canvas->get_file_name() + ']';
111 }
112
113 Layer::Vocab
114 Layer_PasteCanvas::get_param_vocab()const
115 {
116         Layer::Vocab ret(Layer_Composite::get_param_vocab());
117         
118         ret.push_back(ParamDesc("origin")
119                 .set_local_name(_("Origin"))
120                 .set_description(_("Point where you want the origin to be"))
121         );
122         ret.push_back(ParamDesc("canvas")
123                 .set_local_name(_("Canvas"))
124                 .set_description(_("Canvas to paste"))
125         );
126         ret.push_back(ParamDesc("zoom")
127                 .set_local_name(_("Zoom"))
128                 .set_description(_("Size of canvas"))
129         );
130
131         ret.push_back(ParamDesc("time_offset")
132                 .set_local_name(_("Time Offset"))
133         );
134
135         ret.push_back(ParamDesc("children_lock")
136                 .set_local_name(_("Children Lock"))
137         );
138         
139         return ret;
140 }
141
142 bool
143 Layer_PasteCanvas::set_param(const String & param, const ValueBase &value)
144 {
145         IMPORT(origin);
146         
147         if(param=="canvas" && value.same_as(Canvas::Handle()))
148         {
149                 set_sub_canvas(value.get(Canvas::Handle()));
150                 return true;
151         }
152
153 //      IMPORT(canvas);
154         IMPORT(children_lock);
155         IMPORT(zoom);
156         IMPORT(time_offset);
157         
158         return Layer_Composite::set_param(param,value);
159 }
160
161 void
162 Layer_PasteCanvas::set_sub_canvas(etl::handle<sinfg::Canvas> x)
163 {
164         if(canvas && !do_not_muck_with_time_)
165                 remove_child(canvas.get());
166         
167         if(canvas && (canvas->is_inline() || !get_canvas() || get_canvas()->get_root()!=canvas->get_root()))
168                 canvas->unref();
169         
170         child_changed_connection.disconnect();
171         
172         canvas=x;
173         
174         /*if(canvas)
175                 child_changed_connection=canvas->signal_changed().connect(
176                         sigc::mem_fun(
177                                 *this,
178                                 &Layer_PasteCanvas::changed
179                         )
180                 );
181         */
182         if(canvas)
183                 bounds=(canvas->get_context().get_full_bounding_rect()-canvas->rend_desc().get_focus())*exp(zoom)+origin+canvas->rend_desc().get_focus();
184         
185         if(canvas && !do_not_muck_with_time_)
186                 add_child(canvas.get());
187
188         if(canvas && (canvas->is_inline() || !get_canvas() || get_canvas()->get_root()!=canvas->get_root()))
189                 canvas->ref();
190         
191         if(canvas)
192                 on_canvas_set();
193 }
194
195 // This is called whenever the parent canvas gets set/changed
196 void
197 Layer_PasteCanvas::on_canvas_set()
198 {
199         //sinfg::info("before count()=%d",count());
200         if(get_canvas() && canvas && canvas->is_inline() && canvas->parent()!=get_canvas())
201         {
202                 //sinfg::info("during count()=%d",count());
203                 canvas->set_inline(get_canvas());
204         }
205         //sinfg::info("after count()=%d",count());
206 }
207
208 ValueBase
209 Layer_PasteCanvas::get_param(const String& param)const
210 {
211         EXPORT(origin);
212         EXPORT(canvas);
213         EXPORT(zoom);
214         EXPORT(time_offset);
215         EXPORT(children_lock);
216         
217         EXPORT_NAME();
218         EXPORT_VERSION();
219
220         return Layer_Composite::get_param(param);
221 }
222
223 void
224 Layer_PasteCanvas::set_time(Context context, Time time)const
225 {
226         if(depth==MAX_DEPTH)return;depth_counter counter(depth);
227         curr_time=time;
228         
229         context.set_time(time);
230         if(canvas)
231         {
232                 canvas->set_time(time);
233                 
234                 bounds=(canvas->get_context().get_full_bounding_rect()-canvas->rend_desc().get_focus())*exp(zoom)+origin+canvas->rend_desc().get_focus();
235         }
236         else
237                 bounds=Rect::zero();
238 }
239
240 sinfg::Layer::Handle
241 Layer_PasteCanvas::hit_check(sinfg::Context context, const sinfg::Point &pos)const
242 {
243         if(depth==MAX_DEPTH)return 0;depth_counter counter(depth);
244
245         Point target_pos=(pos-canvas->rend_desc().get_focus()-origin)/exp(zoom)+canvas->rend_desc().get_focus();
246
247         if(canvas && get_amount() && canvas->get_context().get_color(target_pos).get_a()>=0.25)
248         {
249                 if(!children_lock)
250                 {
251                         return canvas->get_context().hit_check(target_pos);
252                 }
253                 return const_cast<Layer_PasteCanvas*>(this);
254         }
255         else
256                 return context.hit_check(pos);
257 }
258
259 Color
260 Layer_PasteCanvas::get_color(Context context, const Point &pos)const
261 {
262         if(!canvas || !get_amount())
263                 return context.get_color(pos);
264
265         if(depth==MAX_DEPTH)return Color::alpha();depth_counter counter(depth);
266
267         Point target_pos=(pos-canvas->rend_desc().get_focus()-origin)/exp(zoom)+canvas->rend_desc().get_focus();
268         
269         return Color::blend(canvas->get_context().get_color(target_pos),context.get_color(pos),get_amount(),get_blend_method());
270 }
271
272
273 bool
274 Layer_PasteCanvas::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
275 {
276         if(cb && !cb->amount_complete(0,10000)) return false;
277
278         if(depth==MAX_DEPTH)
279         {
280                 DEBUGPOINT();
281                 // if we are at the extent of our depth,
282                 // then we should just return whatever is under us.
283                 return context.accelerated_render(surface,quality,renddesc,cb);
284         }
285         depth_counter counter(depth);
286                 
287         if(!canvas || !get_amount())
288                 return context.accelerated_render(surface,quality,renddesc,cb);
289         
290         if(!do_not_muck_with_time_ &&   curr_time!=Time::begin() && canvas->get_time()!=curr_time+time_offset)
291         {
292                 canvas->set_time(curr_time+time_offset);
293         }
294         
295         SuperCallback stageone(cb,0,4500,10000);
296         SuperCallback stagetwo(cb,4500,9000,10000);
297         SuperCallback stagethree(cb,9000,9999,10000);
298
299         RendDesc desc(renddesc);
300         Vector::value_type zoomfactor=1.0/exp(zoom);
301         desc.clear_flags();
302         desc.set_tl((desc.get_tl()-canvas->rend_desc().get_focus()-origin)*zoomfactor+canvas->rend_desc().get_focus());
303         desc.set_br((desc.get_br()-canvas->rend_desc().get_focus()-origin)*zoomfactor+canvas->rend_desc().get_focus());
304         desc.set_flags(RendDesc::PX_ASPECT);
305
306         if(is_solid_color() || context->empty())
307         {
308                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
309                 surface->clear();
310         }
311         else if(!context.accelerated_render(surface,quality,renddesc,&stageone))
312                 return false;
313         Color::BlendMethod blend_method(get_blend_method());
314         
315         const Rect full_bounding_rect(canvas->get_context().get_full_bounding_rect());
316
317         if(context->empty())
318         {
319                 if(Color::is_onto(blend_method))
320                         return true;
321                         
322                 if(blend_method==Color::BLEND_COMPOSITE)
323                         blend_method=Color::BLEND_STRAIGHT;
324         }
325         else
326         if(!etl::intersect(context.get_full_bounding_rect(),full_bounding_rect))
327         {
328                 if(Color::is_onto(blend_method))
329                         return true;
330                         
331                 if(blend_method==Color::BLEND_COMPOSITE)
332                         blend_method=Color::BLEND_STRAIGHT;
333         }
334         
335 #ifndef SINFG_NO_CLIP
336         {
337                 //sinfg::info("PasteCanv Clip");
338                 Rect area(desc.get_rect()&full_bounding_rect);
339                                 
340                 Point min(area.get_min());
341                 Point max(area.get_max());
342
343                 if(desc.get_tl()[0]>desc.get_br()[0])
344                         swap(min[0],max[0]);
345                 if(desc.get_tl()[1]>desc.get_br()[1])
346                         swap(min[1],max[1]);
347
348                 const int 
349                         x(floor_to_int((min[0]-desc.get_tl()[0])/desc.get_pw())),
350                         y(floor_to_int((min[1]-desc.get_tl()[1])/desc.get_ph())),
351                         w(ceil_to_int((max[0]-desc.get_tl()[0])/desc.get_pw())-x),
352                         h(ceil_to_int((max[1]-desc.get_tl()[1])/desc.get_ph())-y);
353                                 
354                 desc.set_subwindow(x,y,w,h);
355         
356                 Surface pastesurface;
357                 
358                 if(area.area()<=0.000001 || desc.get_w()==0 || desc.get_h()==0)
359                 {
360                         if(cb && !cb->amount_complete(10000,10000)) return false;
361
362                         return true;
363                 }
364
365                 if(!canvas->get_context().accelerated_render(&pastesurface,quality,desc,&stagetwo))
366                         return false;
367                 
368                 Surface::alpha_pen apen(surface->get_pen(x,y));
369         
370                 apen.set_alpha(get_amount());
371                 apen.set_blend_method(blend_method);
372         
373                 pastesurface.blit_to(apen);
374         }
375 #else
376         {
377                 Surface pastesurface;
378         
379                 if(!canvas->get_context().accelerated_render(&pastesurface,quality,desc,&stagetwo))
380                         return false;
381         
382                 Surface::alpha_pen apen(surface->begin());
383         
384                 apen.set_alpha(get_amount());
385                 apen.set_blend_method(blend_method);
386         
387                 pastesurface.blit_to(apen);
388         }
389 #endif
390         
391         if(cb && !cb->amount_complete(10000,10000)) return false;
392
393         return true;
394 }
395
396 Rect
397 Layer_PasteCanvas::get_bounding_rect()const
398 {
399         return bounds;
400 }
401
402 void Layer_PasteCanvas::get_times_vfunc(Node::time_set &set) const
403 {
404         Node::time_set tset;
405         if(canvas) tset = canvas->get_times();
406
407         Node::time_set::iterator        i = tset.begin(),
408                                                                         end = tset.end();       
409         
410         //Make sure we offset the time...
411         //TODO: SOMETHING STILL HAS TO BE DONE WITH THE OTHER DIRECTION 
412         //              (recursing down the tree needs to take this into account too...)
413         for(; i != end; ++i)
414         {
415                 set.insert(*i + time_offset);
416         }
417                 
418         Layer::get_times_vfunc(set);
419 }