Author: Carlos Lopez <gen...@gm...> Date: Sat Jul 21 11:19:44 2012 +0200 Rectangle: initial version of accelerated_cairorender. It works fine for non inverted rectangles but doesn't work properly for inverted rectangles yet. Blending modes not fully supported. --- synfig-core/src/modules/mod_geometry/rectangle.cpp | 189 ++++++++++++++++++++ synfig-core/src/modules/mod_geometry/rectangle.h | 1 + 2 files changed, 190 insertions(+), 0 deletions(-) diff --git a/synfig-core/src/modules/mod_geometry/rectangle.cpp b/synfig-core/src/modules/mod_geometry/rectangle.cpp index 9ec1f01..e953acd 100644 --- a/synfig-core/src/modules/mod_geometry/rectangle.cpp +++ b/synfig-core/src/modules/mod_geometry/rectangle.cpp @@ -559,6 +559,195 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons return true; } +////// + +bool +Rectangle::accelerated_cairorender(Context context,cairo_surface_t *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const +{ + if(is_disabled()) + return context.accelerated_cairorender(surface,quality,renddesc,cb); + + Color dcolor(color/*.premult_alpha()*/); + const float r(dcolor.get_r()); + const float g(dcolor.get_g()); + const float b(dcolor.get_b()); + const float a(dcolor.get_a()); + + const Point tl(renddesc.get_tl()); + const Point br(renddesc.get_br()); + + const int w(renddesc.get_w()); + const int h(renddesc.get_h()); + + // Width and Height of a pixel + const Real pw = (br[0] - tl[0]) / w; + const Real ph = (br[1] - tl[1]) / h; + Point min(point1), max(point2); + + // if x max, min are swaped then swap the x coordinate + if(min[0] > max[0]) swap(min[0],max[0]); + // if y max min are swaped then swap the y coordinate + if(min[1] > max[1]) swap(min[1],max[1]); + // min is the lower left corner and max is the upper right corner + // now we need to expand the edges + min[0]-=expand; + max[0]+=expand; + min[1]-=expand; + max[1]+=expand; + // + //synfig::info("min=%f, %f max=%f, %f", min[0], min[1], max[0], max[1]); + // + cairo_t* cr=cairo_create(surface); + // This is a rectangle with the same dimensions of the rectangle + const Rect shape(min, max); + // This is a rectangle with the same dimensions of the canvas + const Rect dest(tl, br); + Rect inter(dest); + // inter holds the intersection rectangle of both rectangles. + inter&=shape; + + Point inter_min(inter.get_min()); + Point inter_max(inter.get_max()); + // + //synfig::info("intermin=%f, %f intermax=%f, %f", inter_min[0], inter_min[1], inter_max[0], inter_max[1]); + // + if(invert) + { + if(is_solid_color()) + { + // The rectangle is inverted and is solid color + // Let's do this: + // Fill the surface with the color intially + cairo_save(cr); + cairo_set_source_rgba(cr, r, g, b, a); // a=1.0 + cairo_paint(cr); + cairo_restore(cr); + + // Check for the case where there is nothing to render + if (inter.area()<0.00000001) + { + cairo_destroy(cr); + return true; + } + // Modify the Render Description to render on the intersection. + // this will modify the w and h values in pixels. + RendDesc desc(renddesc); + //desc.set_flags(0); + desc.set_tl(Point(inter.get_min()[0], inter.get_max()[1])); + desc.set_br(Point(inter.get_max()[0], inter.get_min()[1])); + // create a new similar surface with the wxh dimensions + cairo_surface_t* subimage=cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR_ALPHA, desc.get_w(), desc.get_h()); + synfig::info("w=%d, h=%d", desc.get_w(), desc.get_h()); + synfig::info("tl=%f,%f br=%f,%f", desc.get_tl()[0], desc.get_tl()[1], desc.get_br()[0], desc.get_br()[1] ); + // Render what is behind us + if(!context.accelerated_cairorender(subimage,quality,desc,cb)) + { + if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Cairo Renderer Failure",__LINE__)); + { + cairo_surface_destroy(subimage); + cairo_destroy(cr); + return false; + } + } + cairo_save(cr); + double width (inter_max[0]-inter_min[0]); + double height(inter_max[1]-inter_min[1]); + double tx((br[0]-tl[0])/2/pw); + double ty((br[1]-tl[1])/2/ph); + double sx(1/pw); + double sy(1/ph); + cairo_translate(cr, tx , ty); + cairo_scale(cr, sx, sy); + cairo_rectangle(cr, inter_min[0], inter_min[1], width, height); + cairo_clip(cr); + cairo_set_source_surface(cr, subimage, 0, 0 /*inter_min[0], inter_min[1]*/); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_restore(cr); + cairo_surface_destroy(subimage); + cairo_destroy(cr); + return true; + } + else // not solid color so we need to render what's behind + { + // Initially render what's behind us + if(!context.accelerated_cairorender(surface,quality,renddesc,cb)) + { + if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Cairo Renderer Failure",__LINE__)); + cairo_destroy(cr); + return false; + } + // Now let's render the rectangle in other surface + // Initially I'll fill it completely with the alpha color + // Create a similar image with the same dimensions than the current renddesc + cairo_surface_t* subimage=cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR_ALPHA, renddesc.get_w(), renddesc.get_h()); + cairo_t* subcr=cairo_create(subimage); + cairo_set_source_rgba(subcr, r, g, b, a); + cairo_paint(subcr); + // now remove the area of the intersection rectangle + cairo_rectangle(subcr, inter_min[0], inter_min[1], inter_max[0]-inter_min[0], inter_max[1]-inter_min[1]); + cairo_set_operator(subcr, CAIRO_OPERATOR_CLEAR); + cairo_fill(cr); + // now let's paint the inverted rectangle with the hole on the rendered context + cairo_save(cr); + cairo_set_source_surface(cr, subimage, 0, 0); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); // TODO this has to be the real operator + cairo_paint_with_alpha(cr, get_amount()); + cairo_restore(cr); + cairo_surface_destroy(subimage); + cairo_destroy(subcr); + cairo_destroy(cr); + return true; + } + } + + // not inverted + // optimization - if the whole canvas is covered by this rectangle, + // and the rectangle is a solid color, we don't need to render + // what's behind us + if (is_solid_color() && inter==Rect(tl, br)) + { + cairo_set_source_rgba(cr, r, g, b, a); + cairo_paint(cr); + cairo_destroy(cr); + return true; + } + // The rectangle intersects the canvas partially + // Render what is behind us + if(!context.accelerated_cairorender(surface,quality,renddesc,cb)) + { + if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Cairo Renderer Failure",__LINE__)); + cairo_destroy(cr); + return false; + } + + // In the case where there is nothing to render... + if (inter.area()<0.00000001) + return true; + + cairo_set_source_rgba(cr, r, g, b, a); + double width (inter_max[0]-inter_min[0]); + double height(inter_max[1]-inter_min[1]); + double tx((br[0]-tl[0])/2/pw); + double ty((br[1]-tl[1])/2/ph); + double sx(1/pw); + double sy(1/ph); + cairo_translate(cr, tx , ty); + cairo_scale(cr, sx, sy); + cairo_rectangle(cr, inter_min[0], inter_min[1], width, height); + cairo_clip(cr); + if(is_solid_color()) + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + else + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); // TODO this has to be the real operator + cairo_paint_with_alpha(cr, get_amount()); + cairo_destroy(cr); + return true; +} + + +////// + Rect Rectangle::get_bounding_rect()const { diff --git a/synfig-core/src/modules/mod_geometry/rectangle.h b/synfig-core/src/modules/mod_geometry/rectangle.h index add217c..ab7a4a4 100644 --- a/synfig-core/src/modules/mod_geometry/rectangle.h +++ b/synfig-core/src/modules/mod_geometry/rectangle.h @@ -67,6 +67,7 @@ public: virtual synfig::Color get_color(synfig::Context context, const synfig::Point &pos)const; virtual bool accelerated_render(synfig::Context context,synfig::Surface *surface,int quality, const synfig::RendDesc &renddesc, synfig::ProgressCallback *cb)const; + virtual bool accelerated_cairorender(synfig::Context context, cairo_surface_t *surface,int quality, const synfig::RendDesc &renddesc, synfig::ProgressCallback *cb)const; synfig::Layer::Handle hit_check(synfig::Context context, const synfig::Point &point)const; |