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