From a50de53f5e4b23b1e3c0d79866a8503758422dba Mon Sep 17 00:00:00 2001 From: dooglus Date: Tue, 22 Jan 2008 13:42:55 +0000 Subject: [PATCH] First attempt at fixing the 'straight' blend method. It now applies to the whole layer, rather than just the pixels modified by the layer. Loading old canvases that use 'straight' will convert the blend method to 'composite' for all layers other than PasteCanvas, since the old 'straight' worked much like composite for them. The 'straight onto' blend method still needs work. git-svn-id: http://svn.voria.com/code@1422 1f10aa63-cdf2-0310-b900-c93c546f37ac --- synfig-core/trunk/src/synfig/canvas.cpp | 24 +++++++ synfig-core/trunk/src/synfig/canvas.h | 2 +- synfig-core/trunk/src/synfig/context.cpp | 78 +++++++++++++++++----- synfig-core/trunk/src/synfig/layer_composite.cpp | 16 +++++ synfig-core/trunk/src/synfig/layer_pastecanvas.cpp | 61 +++++++++++++++-- 5 files changed, 160 insertions(+), 21 deletions(-) diff --git a/synfig-core/trunk/src/synfig/canvas.cpp b/synfig-core/trunk/src/synfig/canvas.cpp index 967bc60..270a71f 100644 --- a/synfig-core/trunk/src/synfig/canvas.cpp +++ b/synfig-core/trunk/src/synfig/canvas.cpp @@ -1170,6 +1170,30 @@ synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bo dynamic_cast(new_layer.get())->set_muck_with_time(true); layer=new_layer; } + else // not a PasteCanvas - does it use blend method 'Straight'? + { + /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath, + * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a + * new pastecanvas to ensure that the straight blend affects the full plane, not just the area + * within the layer's bounding box + */ + + // \todo: this code probably needs modification to work properly with motionblur and duplicate + etl::handle composite = etl::handle::cast_dynamic(layer); + if (composite && composite->get_blend_method() == Color::BLEND_STRAIGHT) + { + Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas)); + sub_canvas->push_back(composite = composite->clone()); + sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set + layer = Layer::create("PasteCanvas"); + layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str())); + Layer_PasteCanvas* paste_canvas(static_cast(layer.get())); + paste_canvas->set_sub_canvas(sub_canvas); + paste_canvas->set_blend_method(Color::BLEND_STRAIGHT); + paste_canvas->set_amount(composite->get_amount()); + composite->set_amount(1.0f); + } + } sort_list.push_back(std::pair(z_depth,layer)); //op_canvas->push_back_simple(layer); diff --git a/synfig-core/trunk/src/synfig/canvas.h b/synfig-core/trunk/src/synfig/canvas.h index 43a562d..d0eb88d 100644 --- a/synfig-core/trunk/src/synfig/canvas.h +++ b/synfig-core/trunk/src/synfig/canvas.h @@ -76,7 +76,7 @@ * non-pastecanvas layers with 'composite' */ -#define CURRENT_CANVAS_VERSION "0.2" +#define CURRENT_CANVAS_VERSION "0.3" /* === T Y P E D E F S ===================================================== */ diff --git a/synfig-core/trunk/src/synfig/context.cpp b/synfig-core/trunk/src/synfig/context.cpp index dba4e1b..024b6d8 100644 --- a/synfig-core/trunk/src/synfig/context.cpp +++ b/synfig-core/trunk/src/synfig/context.cpp @@ -31,6 +31,7 @@ #include "context.h" #include "layer.h" +#include "layer_composite.h" #include "string.h" #include "vector.h" #include "color.h" @@ -178,32 +179,54 @@ Context::accelerated_render(Surface *surface,int quality, const RendDesc &rendde const Rect bbox(renddesc.get_rect()); + // this is going to be set to true if this layer contributes + // nothing, but it's a straight blend with non-zero amount, and so + // it has an effect anyway + bool straight_and_empty = false; + etl::handle composite; Context context(*this); + for(;!(context)->empty();++context) { - // If we are not active - // then move on to next layer + // If we are not active then move on to next layer if(!(*context)->active()) continue; const Rect layer_bounds((*context)->get_bounding_rect()); - - // If the box area is less than zero - // then move on to next layer - if(layer_bounds.area()<=0.0000000000001) - continue; - - // If the boxes do not intersect - // then move on to next layer - if(!(layer_bounds && bbox)) + composite = etl::handle::cast_dynamic(*context); + + // If the box area is less than zero or the boxes do not + // intersect then move on to next layer, unless the layer is + // using a straight blend and has a non-zero amount, in which + // case it will still affect the result + if(layer_bounds.area() <= 0.0000000000001 || !(layer_bounds && bbox)) + { + if (composite && + composite->get_blend_method() == Color::BLEND_STRAIGHT && + composite->get_amount() != 0.0f) + { + straight_and_empty = true; + break; + } continue; + } + + // If this layer has Straight as the blend method and amount is 1.0 + // then we don't want to render the context + if (composite && composite->get_blend_method() == Color::BLEND_STRAIGHT && + composite->get_amount() == 1.0f) + { + Layer::Handle layer = *context; + while (!context->empty()) context++; // skip the context + return layer->accelerated_render(context,surface,quality,renddesc, cb); + } // Break out of the loop--we have found a good layer break; } // If this layer isn't defined, return alpha - if((context)->empty()) + if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f)) { #ifdef SYNFIG_DEBUG_LAYERS synfig::info("Context::accelerated_render(): Hit end of list"); @@ -228,8 +251,33 @@ Context::accelerated_render(Surface *surface,int quality, const RendDesc &rendde depth++; curr_layer=(*context)->get_name(); //make sure the layer inside is referring to the correct layer outside profile_timer.reset(); // + - bool ret((*context)->accelerated_render(context+1,surface,quality,renddesc, cb)); +#endif // SYNFIG_PROFILE_LAYERS + + bool ret; + // this layer doesn't draw anything onto the canvas we're + // rendering, but it uses straight blending, so we need to render + // the stuff under us and then blit transparent pixels over it + // using the appropriate 'amount' + if (straight_and_empty) + { + if (ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb)) + { + Surface clearsurface; + clearsurface.set_wh(renddesc.get_w(),renddesc.get_h()); + clearsurface.clear(); + + Surface::alpha_pen apen(surface->begin()); + apen.set_alpha(composite->get_amount()); + apen.set_blend_method(Color::BLEND_STRAIGHT); + + clearsurface.blit_to(apen); + } + } + else + ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb); + +#ifdef SYNFIG_PROFILE_LAYERS //post work for the previous layer time_table[curr_layer]+=profile_timer(); //- if(run_table.count(curr_layer))run_table[curr_layer]++; @@ -241,11 +289,9 @@ Context::accelerated_render(Surface *surface,int quality, const RendDesc &rendde //print out the table it we're done... if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear(); profile_timer.reset(); //+ - return ret; -#else // SYNFIG_PROFILE_LAYERS - return (*context)->accelerated_render(context+1,surface,quality,renddesc, cb); #endif // SYNFIG_PROFILE_LAYERS + return ret; } catch(std::bad_alloc) { diff --git a/synfig-core/trunk/src/synfig/layer_composite.cpp b/synfig-core/trunk/src/synfig/layer_composite.cpp index 2c20204..b788011 100644 --- a/synfig-core/trunk/src/synfig/layer_composite.cpp +++ b/synfig-core/trunk/src/synfig/layer_composite.cpp @@ -163,6 +163,22 @@ Layer_Composite::set_param(const String & param, const ValueBase &value) blend_method_ = Color::BLEND_COMPOSITE; return false; } + + if (blend_method_ == Color::BLEND_STRAIGHT) + { + Canvas::Handle canvas(get_canvas()); + if (canvas) + { + String version(canvas->get_version()); + + if (version == "0.1" || version == "0.2") + if (get_name() == "PasteCanvas") + warning("loaded a version %s canvas with a 'Straight' blended PasteCanvas (%s) - check it renders OK", + version.c_str(), get_non_empty_description().c_str()); + else + blend_method_ = Color::BLEND_COMPOSITE; + } + } } else return Layer::set_param(param,value); diff --git a/synfig-core/trunk/src/synfig/layer_pastecanvas.cpp b/synfig-core/trunk/src/synfig/layer_pastecanvas.cpp index d19f066..d343530 100644 --- a/synfig-core/trunk/src/synfig/layer_pastecanvas.cpp +++ b/synfig-core/trunk/src/synfig/layer_pastecanvas.cpp @@ -202,7 +202,8 @@ Layer_PasteCanvas::set_sub_canvas(etl::handle x) ); */ if(canvas) - bounds=(canvas->get_context().get_full_bounding_rect()-canvas->rend_desc().get_focus())*exp(zoom)+origin+canvas->rend_desc().get_focus(); + bounds = ((canvas->get_context().get_full_bounding_rect() - canvas->rend_desc().get_focus()) * exp(zoom) + + origin + canvas->rend_desc().get_focus()); if(canvas && muck_with_time_) add_child(canvas.get()); @@ -332,6 +333,7 @@ Layer_PasteCanvas::accelerated_render(Context context,Surface *surface,int quali Color::BlendMethod blend_method(get_blend_method()); const Rect full_bounding_rect(canvas->get_context().get_full_bounding_rect()); + bool blend_using_straight = false; // use 'straight' just for the central blit if(context->empty()) { @@ -347,10 +349,14 @@ Layer_PasteCanvas::accelerated_render(Context context,Surface *surface,int quali * effect if the affected area of the lower layer is * transparent; however, if we're not clipping the blit to * just the bounding rectangle, the affected area is the whole - * tile, so we can't use this optimisation + * tile, so we can't use this optimisation. if we are + * clipping, then we can use 'straight' to blit the clipped + * rectangle, but we shouldn't set blend_method to 'straight', + * or the surrounding areas will be blanked, which we don't + * want. */ #ifdef SYNFIG_CLIP_PASTECANVAS - if (blend_method==Color::BLEND_COMPOSITE) blend_method=Color::BLEND_STRAIGHT; + if (blend_method==Color::BLEND_COMPOSITE) blend_using_straight = true; #endif // SYNFIG_CLIP_PASTECANVAS } @@ -368,6 +374,9 @@ Layer_PasteCanvas::accelerated_render(Context context,Surface *surface,int quali const int w( ceil_to_int((max[0] - desc.get_tl()[0]) / desc.get_pw()) - x); const int h( ceil_to_int((max[1] - desc.get_tl()[1]) / desc.get_ph()) - y); + const int tw = desc.get_w(); + const int th = desc.get_h(); + desc.set_subwindow(x,y,w,h); // \todo this used to also have "area.area()<=0.000001 || " - is it useful? @@ -377,6 +386,50 @@ Layer_PasteCanvas::accelerated_render(Context context,Surface *surface,int quali if(cb && !cb->amount_complete(10000,10000)) return false; return true; } + + // SYNFIG_CLIP_PASTECANVAS is defined, so we are only touching the + // pixels within the affected rectangle. If the blend method is + // 'straight', then we need to blend transparent pixels with the + // clipped areas of this tile, because with the 'straight' blend + // method, even transparent pixels have an effect on the layers below + if (blend_method == Color::BLEND_STRAIGHT) + { + Surface clearsurface; + + Surface::alpha_pen apen(surface->begin()); + apen.set_alpha(get_amount()); + apen.set_blend_method(Color::BLEND_STRAIGHT); + + /* This represents the area we're pasting into the tile, + * within the tile as a whole. Areas (A), (B), (C) and (D) + * need blending with the underlying context if they're not + * zero-sized: + * + * 0 x x+w tw + * 0 +------------------------+ + * | | + * | (A) | + * | | + * y | - - +----------+ - - - | + * | | | | + * | (C) | w by h | (D) | + * | | | | + * y+h | - - +----------+ - - - | + * | | + * | (B) | + * | | + * tw +------------------------+ + */ + + if (y > 0) // draw the full-width strip above the rectangle (A) + { apen.move_to(0,0); clearsurface.set_wh(tw,y); clearsurface.clear(); clearsurface.blit_to(apen); } + if (y+h < th) // draw the full-width strip below the rectangle (B) + { apen.move_to(0,y+h); clearsurface.set_wh(tw,th-(y+h)); clearsurface.clear(); clearsurface.blit_to(apen); } + if (x > 0) // draw the box directly left of the rectangle (C) + { apen.move_to(0,y); clearsurface.set_wh(x,h); clearsurface.clear(); clearsurface.blit_to(apen); } + if (x+w < tw) // draw the box directly right of the rectangle (D) + { apen.move_to(x+w,y); clearsurface.set_wh(tw-(x+w),h); clearsurface.clear(); clearsurface.blit_to(apen); } + } #endif // SYNFIG_CLIP_PASTECANVAS // render the canvas to be pasted onto pastesurface @@ -391,7 +444,7 @@ Layer_PasteCanvas::accelerated_render(Context context,Surface *surface,int quali #endif // SYNFIG_CLIP_PASTECANVAS apen.set_alpha(get_amount()); - apen.set_blend_method(blend_method); + apen.set_blend_method(blend_using_straight ? Color::BLEND_STRAIGHT : blend_method); pastesurface.blit_to(apen); if(cb && !cb->amount_complete(10000,10000)) return false; -- 2.7.4