diff --git a/synfig-core/src/synfig/rendering/common/task/tasktransformation.cpp b/synfig-core/src/synfig/rendering/common/task/tasktransformation.cpp index af1b874..3f5faf1 100644 --- a/synfig-core/src/synfig/rendering/common/task/tasktransformation.cpp +++ b/synfig-core/src/synfig/rendering/common/task/tasktransformation.cpp @@ -93,36 +93,25 @@ TaskTransformation::calc_bounds() const void TaskTransformation::set_coords_sub_tasks() { - const int border = 4; - if (!sub_task()) { trunc_to_zero(); return; } if ( is_valid_coords() - && approximate_greater(supersample[0], 0.0) - && approximate_greater(supersample[1], 0.0) ) + && approximate_greater(supersample[0], Real(0)) + && approximate_greater(supersample[1], Real(0)) ) { if (Transformation::Handle transformation = get_transformation()) { if (Transformation::Handle back_transformation = transformation->create_inverted()) { - Transformation::Bounds bounds = - back_transformation->transform_bounds( - source_rect, get_pixels_per_unit().multiply_coords(supersample) ); - if (bounds.is_valid()) + Transformation::DiscreteBounds discrete_bounds = + Transformation::make_discrete_bounds( + back_transformation->transform_bounds( + source_rect, + get_pixels_per_unit().multiply_coords(supersample) )); + if (discrete_bounds.is_valid()) { - // add some pixels to border for draw valid and antialiased transformed edges - Vector size_real = bounds.resolution.multiply_coords( bounds.rect.get_size() ); - VectorInt size( 2*border + ceil(size_real[0]), - 2*border + ceil(size_real[1]) ); - Vector extra( 0.5*(size[0] - size_real[0])/bounds.resolution[0], - 0.5*(size[1] - size_real[1])/bounds.resolution[1] ); - Rect rect = bounds.rect; - rect.minx -= extra[0]; - rect.miny -= extra[1]; - rect.maxx += extra[0]; - rect.maxy += extra[1]; - sub_task()->set_coords(rect, size); + sub_task()->set_coords(discrete_bounds.rect, discrete_bounds.size); return; } } @@ -141,4 +130,5 @@ TaskTransformationAffine::get_pass_subtask_index() const return 0; return TaskTransformation::get_pass_subtask_index(); } + /* === E N T R Y P O I N T ================================================= */ diff --git a/synfig-core/src/synfig/rendering/primitive/transformation.cpp b/synfig-core/src/synfig/rendering/primitive/transformation.cpp index de5248b..f4c9157 100644 --- a/synfig-core/src/synfig/rendering/primitive/transformation.cpp +++ b/synfig-core/src/synfig/rendering/primitive/transformation.cpp @@ -44,6 +44,59 @@ using namespace rendering; /* === M E T H O D S ======================================================= */ + +Transformation::DiscreteBounds +Transformation::make_discrete_bounds(const Bounds &bounds) +{ + const int border_width = 4; + const int max_width = 16384 - 2*border_width; + const int max_height = 16384 - 2*border_width; + + const int max_area_width = 4096 - 2*border_width; + const int max_area = max_area_width * max_area_width; + + if (!bounds.is_valid()) + return DiscreteBounds(); + + const Vector raster_size_orig = bounds.rect.get_size().multiply_coords( bounds.resolution ); + + Vector raster_size_float = raster_size_orig; + if (raster_size_float[0] > max_width) + raster_size_float[0] = max_width; + if (raster_size_float[1] > max_height) + raster_size_float[1] = max_height; + + VectorInt raster_size( + std::max(1, (int)approximate_ceil(raster_size_float[0])), + std::max(1, (int)approximate_ceil(raster_size_float[1])) ); + if (raster_size[0] * raster_size[1] > max_area) { + const Real k = sqrt(Real(max_area)/(raster_size[0] * raster_size[1])); + raster_size[0] = std::max(1, (int)approximate_floor(raster_size[0]*k)); + raster_size[1] = std::max(1, (int)approximate_floor(raster_size[1]*k)); + } + + const Vector border( + border_width*raster_size_orig[0]/(bounds.resolution[0]*raster_size[0]), + border_width*raster_size_orig[1]/(bounds.resolution[1]*raster_size[1]) ); + + return DiscreteBounds( + Rect( + bounds.rect.get_min() - border, + bounds.rect.get_max() + border ), + raster_size + VectorInt(border_width, border_width) ); +} + +Matrix2 +Transformation::derivative_vfunc(const Point &x) const +{ + const Real step = real_low_precision(); + const Real step_div = 1/step; + const Point p0 = transform(x); + const Point dx = transform(Point(x[0] + step, x[1])) - p0; + const Point dy = transform(Point(x[0], x[1] + step)) - p0; + return Matrix2(dx[0]*step_div, dx[1]*step_div, dy[0]*step_div, dy[1]*step_div); +} + bool Transformation::can_merge_outer_vfunc(const Transformation&) const { return false; } diff --git a/synfig-core/src/synfig/rendering/primitive/transformation.h b/synfig-core/src/synfig/rendering/primitive/transformation.h index a3ecf60..982960b 100644 --- a/synfig-core/src/synfig/rendering/primitive/transformation.h +++ b/synfig-core/src/synfig/rendering/primitive/transformation.h @@ -45,14 +45,14 @@ class Transformation: public etl::shared_object { public: typedef etl::handle Handle; - + struct Bounds { Rect rect; Vector resolution; - + explicit Bounds(const Rect &rect = Rect(), const Vector &resolution = Vector(1.0, 1.0)): rect(rect), resolution(resolution) { } - + inline bool is_valid() const { return rect.is_valid() && !rect.is_nan_or_inf() @@ -62,56 +62,75 @@ public: && approximate_greater(resolution[1], 0.0); } }; - + + struct DiscreteBounds { + Rect rect; + VectorInt size; + explicit DiscreteBounds(const Rect &rect = Rect(), const VectorInt &size = VectorInt()): + rect(rect), size(size) { } + + inline bool is_valid() const { + return rect.is_valid() + && !rect.is_nan_or_inf() + && size[0] > 0 + && size[1] > 0; + } + }; + + static DiscreteBounds make_discrete_bounds(const Bounds &bounds); + protected: virtual Transformation* clone_vfunc() const { return 0; } virtual Transformation* create_inverted_vfunc() const { return 0; } - - virtual Point transform_vfunc(const Point &x, bool translate) const = 0; + + virtual Point transform_vfunc(const Point &x) const = 0; + virtual Matrix2 derivative_vfunc(const Point &x) const; virtual Bounds transform_bounds_vfunc(const Bounds &bounds) const = 0; - + virtual Mesh::Handle build_mesh_vfunc(const Rect &target_rect, const Vector &precision) const; - + virtual bool can_merge_outer_vfunc(const Transformation &other) const; virtual bool can_merge_inner_vfunc(const Transformation &other) const; virtual void merge_outer_vfunc(const Transformation &other); virtual void merge_inner_vfunc(const Transformation &other); - + public: virtual ~Transformation() { } - + Transformation* clone() const { return clone_vfunc(); } Transformation* create_inverted() const { return create_inverted_vfunc(); } Transformation* create_merged(const Transformation& other) const; - - Point transform(const Point &x, bool translate = true) const - { return transform_vfunc(x, translate); } + + Point transform(const Point &x) const + { return transform_vfunc(x); } + Matrix2 derivative(const Point &x) const + { return derivative_vfunc(x); } Bounds transform_bounds(const Bounds &bounds) const { return transform_bounds_vfunc(bounds); } Bounds transform_bounds(const Rect &bounds) const { return transform_bounds_vfunc(Bounds(bounds)); } Bounds transform_bounds(const Rect &bounds, const Vector &resolution) const { return transform_bounds_vfunc(Bounds(bounds, resolution)); } - + bool can_merge_outer(const Transformation& other) const; bool can_merge_inner(const Transformation& other) const; - bool merge_outer(const Transformation& other); - bool merge_inner(const Transformation& other); - + bool merge_outer(const Transformation& other); //!< current is child (inner), other is parent (outer) + bool merge_inner(const Transformation& other); //!< current is parent (outer), other is child (inner) + bool can_merge_outer(const Transformation::Handle& other) const { return other && can_merge_outer(*other); } bool can_merge_inner(const Transformation::Handle& other) const { return other && can_merge_inner(*other); } - + bool merge_outer(const Transformation::Handle& other) { return other && merge_outer(*other); } bool merge_inner(const Transformation::Handle& other) { return other && merge_inner(*other); } - + Mesh::Handle build_mesh(const Rect &target_rect, const Vector &precision) const; Mesh::Handle build_mesh(const Point &target_rect_lt, const Point &target_rect_rb, const Vector &precision) const; }; @@ -125,8 +144,10 @@ public: protected: virtual Transformation* clone_vfunc() const { return new TransformationVoid(); } - virtual Point transform_vfunc(const Point& /* x */, bool /* translate */) const + virtual Point transform_vfunc(const Point& /* x */) const { return Point::nan(); } + virtual Matrix2 derivative_vfunc(const Point& /* x */) const + { return Matrix2(real_nan(), real_nan(), real_nan(), real_nan()); } virtual Bounds transform_bounds_vfunc(const Bounds& /* bounds */) const { return Bounds(); } virtual bool can_merge_outer_vfunc(const Transformation& /* other */) const @@ -146,8 +167,10 @@ protected: { return new TransformationNone(); } virtual Transformation* create_inverted_vfunc() const { return clone_vfunc(); } - virtual Point transform_vfunc(const Point &x, bool /* translate */) const + virtual Point transform_vfunc(const Point &x) const { return x; } + virtual Matrix2 derivative_vfunc(const Point& /* x */) const + { return Matrix2(); } virtual Bounds transform_bounds_vfunc(const Bounds &bounds) const { return bounds; } }; diff --git a/synfig-core/src/synfig/rendering/primitive/transformationaffine.cpp b/synfig-core/src/synfig/rendering/primitive/transformationaffine.cpp index 031dc5f..884e5e5 100644 --- a/synfig-core/src/synfig/rendering/primitive/transformationaffine.cpp +++ b/synfig-core/src/synfig/rendering/primitive/transformationaffine.cpp @@ -53,43 +53,76 @@ TransformationAffine::create_inverted_vfunc() const { return new TransformationAffine(Matrix(matrix).invert()); } Point -TransformationAffine::transform_vfunc(const Point &x, bool translate) const - { return matrix.get_transformed(x, translate); } +TransformationAffine::transform_vfunc(const Point &x) const + { return matrix.get_transformed(x); } + +Matrix2 +TransformationAffine::derivative_vfunc(const Point&) const + { return Matrix2(matrix.m00, matrix.m01, matrix.m10, matrix.m11); } + +Vector +TransformationAffine::calc_optimal_resolution(const Matrix2 &matrix) { + const Real max_overscale_sqr = 1.0*4.0; + const Real max_overscale_sqrsqr = max_overscale_sqr*max_overscale_sqr; + const Real real_precision_sqr = real_precision() * real_precision(); + + const Real a = matrix.m00 * matrix.m00; + const Real b = matrix.m01 * matrix.m01; + const Real c = matrix.m10 * matrix.m10; + const Real d = matrix.m11 * matrix.m11; + Real e = fabs(matrix.det()); + if (e < real_precision_sqr) + return Vector(); + e = 1.0/e; + + const Real sum = a*d + b*c; + Vector scale; + if (2*a*b + real_precision_sqr >= sum) { + scale[0] = sqrt(2*b)*e; + scale[1] = sqrt(2*a)*e; + } else + if (2*c*d + real_precision_sqr >= sum) { + scale[0] = sqrt(2*d)*e; + scale[1] = sqrt(2*c)*e; + } else { + const Real dif = a*d - b*c; + scale[0] = sqrt(dif/(a - c))*e; + scale[1] = sqrt(dif/(d - b))*e; + } + + const Real sx2 = scale[0]*scale[0]; + const Real sy2 = scale[1]*scale[1]; + const Real sqrsqr = (a*sx2 + b*sy2)*(c*sx2 + d*sy2); + if (sqrsqr > max_overscale_sqrsqr) + scale *= sqrt(sqrt(max_overscale_sqrsqr / sqrsqr)); + + return scale[0] <= real_precision() || scale[1] <= real_precision() + ? Vector() : scale; +} Transformation::Bounds -TransformationAffine::transform_bounds_vfunc(const Bounds &bounds) const +TransformationAffine::transform_bounds_affine(const Matrix &matrix, const Bounds &bounds) { if (!bounds.is_valid()) return Bounds(); - - // calculate bounds rect - Vector corners[] = { - matrix.get_transformed( bounds.rect.get_min() ), - matrix.get_transformed( Vector(bounds.rect.maxx, bounds.rect.miny) ), - matrix.get_transformed( Vector(bounds.rect.minx, bounds.rect.maxy) ), - matrix.get_transformed( bounds.rect.get_max() ) }; - - Rect rect = Rect( corners[0] ) - .expand( corners[1] ) - .expand( corners[2] ) - .expand( corners[3] ); - - // calculate units per "pixel" - Real upp_x0 = fabs(corners[1][0] - corners[0][0]) / (bounds.resolution[0] * bounds.rect.get_width()); - Real upp_x1 = fabs(corners[2][0] - corners[0][0]) / (bounds.resolution[1] * bounds.rect.get_height()); - Real upp_y0 = fabs(corners[1][1] - corners[0][1]) / (bounds.resolution[0] * bounds.rect.get_width()); - Real upp_y1 = fabs(corners[2][1] - corners[0][1]) / (bounds.resolution[1] * bounds.rect.get_height()); - Vector upp( - std::max(upp_x0, upp_x1), - std::max(upp_y0, upp_y1) ); - - if ( approximate_less_or_equal(upp[0], Real(0.0)) - && approximate_less_or_equal(upp[1], Real(0.0)) ) - return Bounds(); - - return Bounds(rect, Vector(1.0/upp[0], 1.0/upp[1])); + + const Real kx = 1/bounds.resolution[0]; + const Real ky = 1/bounds.resolution[1]; + + return Bounds( + Rect( matrix.get_transformed( Vector(bounds.rect.minx, bounds.rect.miny) ) ) + .expand( matrix.get_transformed( Vector(bounds.rect.minx, bounds.rect.maxy) ) ) + .expand( matrix.get_transformed( Vector(bounds.rect.maxx, bounds.rect.miny) ) ) + .expand( matrix.get_transformed( Vector(bounds.rect.maxx, bounds.rect.maxy) ) ), + calc_optimal_resolution(Matrix2( + matrix.m00*kx, matrix.m01*kx, + matrix.m10*ky, matrix.m11*ky )) ); } +Transformation::Bounds +TransformationAffine::transform_bounds_vfunc(const Bounds &bounds) const + { return transform_bounds_affine(matrix, bounds); } + bool TransformationAffine::can_merge_outer_vfunc(const Transformation &other) const { return (bool)dynamic_cast(&other); } diff --git a/synfig-core/src/synfig/rendering/primitive/transformationaffine.h b/synfig-core/src/synfig/rendering/primitive/transformationaffine.h index d202c98..b0a8e5a 100644 --- a/synfig-core/src/synfig/rendering/primitive/transformationaffine.h +++ b/synfig-core/src/synfig/rendering/primitive/transformationaffine.h @@ -51,11 +51,15 @@ public: TransformationAffine() { } explicit TransformationAffine(const Matrix &matrix): matrix(matrix) { } + + static Vector calc_optimal_resolution(const Matrix2 &matrix); + static Bounds transform_bounds_affine(const Matrix &matrix, const Bounds &bounds); protected: virtual Transformation* clone_vfunc() const; virtual Transformation* create_inverted_vfunc() const; - virtual Point transform_vfunc(const Point &x, bool direction) const; + virtual Point transform_vfunc(const Point &x) const; + virtual Matrix2 derivative_vfunc(const Point &x) const; virtual Bounds transform_bounds_vfunc(const Bounds &bounds) const; virtual bool can_merge_outer_vfunc(const Transformation &other) const; virtual bool can_merge_inner_vfunc(const Transformation &other) const;