diff --git a/stuff/config/current.txt b/stuff/config/current.txt index 97020a9..9810bda 100644 --- a/stuff/config/current.txt +++ b/stuff/config/current.txt @@ -1160,10 +1160,14 @@ "STD_inoRadialBlurFx.center" "Center" "STD_inoRadialBlurFx.radius" "Radius" "STD_inoRadialBlurFx.blur" "Blur" + "STD_inoRadialBlurFx.type" "Type" "STD_inoRadialBlurFx.twist" "Twist" "STD_inoRadialBlurFx.alpha_rendering" "Alpha Rendering" "STD_inoRadialBlurFx.anti_alias" "Anti Alias" "STD_inoRadialBlurFx.reference" "Reference" + "STD_inoRadialBlurFx.ellipse_aspect_ratio" "Ellipse Aspect Ratio" + "STD_inoRadialBlurFx.ellipse_angle" "Ellipse Angle" + "STD_inoRadialBlurFx.intensity_correlation_with_ellipse" "Intensity Correlation" "STD_inoSpinBlurFx" "Spin Blur Ino" "STD_inoSpinBlurFx.center" "Center" "STD_inoSpinBlurFx.radius" "Radius" diff --git a/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml index eace9eb..162ea51 100644 --- a/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoRadialBlurFx.xml @@ -3,8 +3,13 @@ center radius blur + type twist alpha_rendering + + ellipse_aspect_ratio + ellipse_angle + intensity_correlation_with_ellipse anti_alias reference diff --git a/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml b/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml index 6f215c4..ea2d4da 100644 --- a/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml +++ b/stuff/profiles/layouts/fxs/STD_inoSpinBlurFx.xml @@ -5,8 +5,9 @@ blur type alpha_rendering - ellipse_aspect_ratio - ellipse_angle + + ellipse_aspect_ratio + ellipse_angle anti_alias reference diff --git a/toonz/sources/include/tools/tool.h b/toonz/sources/include/tools/tool.h index 5ccf7e7..ab30f5a 100644 --- a/toonz/sources/include/tools/tool.h +++ b/toonz/sources/include/tools/tool.h @@ -677,6 +677,7 @@ public: /*-- Toolで画面の内外を判断するため --*/ virtual TRectD getGeometry() const = 0; + virtual TRectD getCameraRect() const { return TRectD(); } virtual void bindFBO() {} virtual void releaseFBO() {} diff --git a/toonz/sources/include/tparamuiconcept.h b/toonz/sources/include/tparamuiconcept.h index dfc6419..167008e 100644 --- a/toonz/sources/include/tparamuiconcept.h +++ b/toonz/sources/include/tparamuiconcept.h @@ -70,7 +70,7 @@ public: RAINBOW_WIDTH, - ELLIPSE, // used in spin blur ino + ELLIPSE, // used in spin blur ino and radial blur ino TYPESCOUNT }; diff --git a/toonz/sources/stdfx/igs_radial_blur.cpp b/toonz/sources/stdfx/igs_radial_blur.cpp index 35f5d73..5a5d63d 100644 --- a/toonz/sources/stdfx/igs_radial_blur.cpp +++ b/toonz/sources/stdfx/igs_radial_blur.cpp @@ -4,1000 +4,444 @@ #include // std::domain_error() #include "igs_ifx_common.h" #include "igs_radial_blur.h" + +#include +#include + namespace { //------------------------------------------------------------------ -template +enum Type { Accelerator = 0, Uniform_Length }; + class radial_ { + const float* in_top_; + const int hh_; + const int ww_; + const int cc_; + const TPointD center_; + const bool antialias_sw_; + const bool alpha_rendering_sw_; + const double intensity_; + const double blur_radius_; + const double twist_radian_; + const double pivot_radius_; + const int type_; + const double ellipse_aspect_ratio_; + const double ellipse_angle_; + const double intensity_correlation_with_ellipse_; + QTransform tr_, tr_inv_; + public: - radial_(const T *in_top, const int height, const int width, - const int channels, const double xc, const double yc, - const double sub_size, const int imax, const double dmax, - const double intensity /* 平均値ぼかし強度 */ - , - const double radius /* 平均値ぼかしの始まる半径 */ - ) + radial_(const float* in_top, const int height, const int width, + const int channels, const TPointD center, const bool antialias_sw, + const bool alpha_rendering_sw, + const double intensity, /* 平均値ぼかし強度 */ + const double blur_radius, /* 平均値ぼかしの始まる半径 */ + const double twist_radian, const double pivot_radius, const int type, + const double ellipse_aspect_ratio, const double ellipse_angle, + const double intensity_correlation_with_ellipse) : in_top_(in_top) , hh_(height) , ww_(width) , cc_(channels) - , xc_(xc) - , yc_(yc) - , sub_size_(sub_size) - , imax_(imax) - , dmax_(dmax) + , center_(center) + , antialias_sw_(antialias_sw) + , alpha_rendering_sw_(alpha_rendering_sw) , intensity_(intensity) - , radius_(radius) {} - void pixel_value(const T *in_current_pixel, const int xx, const int yy, - const int z1, const int z2, const double ref_increase_val, - const double ref_decrease_val, - const double each_pixel_blur_ratio, T *result_pixel) { - /* Pixel位置(0.5 1.5 2.5 ...) */ - const double xp = static_cast(xx) + 0.5; - const double yp = static_cast(yy) + 0.5; - - /* 中心からPixel位置へのベクトルと長さ */ - const double xv = xp - this->xc_; - const double yv = yp - this->yc_; - const double dist = sqrt(xv * xv + yv * yv); - - /* 指定半径の範囲内なら何もしない */ - if (dist <= this->radius_) { - for (int zz = z1; zz <= z2; ++zz) { - result_pixel[zz] = in_current_pixel[zz]; - } - return; - } - - /* 中心からPixel位置への単位ベクトル */ - const double cosval = xv / dist; - const double sinval = yv / dist; - - /* Radial方向のSamplingの開始位置と終了位置 */ - double scale = this->intensity_; - if (0.0 <= each_pixel_blur_ratio) { - scale *= each_pixel_blur_ratio; - } - const double length = (dist - this->radius_) * scale; - const double count_half = floor(length / 2.0 / this->sub_size_); - const double sub_sta = -this->sub_size_ * count_half; - const double sub_end = this->sub_size_ * count_half; - - /* 積算値と積算回数 */ - std::vector accum_val(this->cc_); - int accum_counter = 0; - - /* 円周接線方向Samplingの相対位置 */ - for (double ss = this->sub_size_ / 2.0 - 0.5; ss < 0.5; - ss += this->sub_size_) { - /* 円周接線方向Sampling位置 */ - const double xps = xp + ss * sinval; - const double yps = yp + ss * cosval; - - /* 中心からSampling位置へのベクトルと長さ */ - const double xvs = xps - this->xc_; - const double yvs = yps - this->yc_; - const double dists = sqrt(xvs * xvs + yvs * yvs); - - /* 中心からSampling位置への単位ベクトル */ - const double cosvals = xvs / dists; - const double sinvals = yvs / dists; - - /* Radial方向のSampling */ - for (double tt = sub_sta; tt <= sub_end; tt += this->sub_size_) { - /* Sampling位置からPixel位置を得る */ - int xi = static_cast(xps + tt * cosvals); - int yi = static_cast(yps + tt * sinvals); - - /* clamp */ - xi = (xi < 0) ? 0 : ((this->ww_ <= xi) ? this->ww_ - 1 : xi); - yi = (yi < 0) ? 0 : ((this->hh_ <= yi) ? this->hh_ - 1 : yi); - - /* 画像のPixel位置 */ - const T *in_current = - this->in_top_ + this->cc_ * this->ww_ * yi + this->cc_ * xi; - - /* 積算 */ - for (int zz = z1; zz <= z2; ++zz) { - accum_val[zz] += static_cast(in_current[zz]); - } - ++accum_counter; - } - } - /* 積算しなかったとき(念のためのCheck) */ - if (accum_counter <= 0) { - for (int zz = z1; zz <= z2; ++zz) { - result_pixel[zz] = in_current_pixel[zz]; - } - return; + , blur_radius_(blur_radius) + , twist_radian_(twist_radian) + , pivot_radius_(pivot_radius) + , type_(type) + , ellipse_aspect_ratio_(ellipse_aspect_ratio) + , ellipse_angle_(ellipse_angle) + , intensity_correlation_with_ellipse_( + intensity_correlation_with_ellipse) { + if (ellipse_aspect_ratio_ != 1.0) { + double axis_x = + 2.0 * ellipse_aspect_ratio_ / (ellipse_aspect_ratio_ + 1.0); + double axis_y = axis_x / ellipse_aspect_ratio_; + tr_ = QTransform() + .rotateRadians(this->ellipse_angle_) + .scale(axis_x, axis_y); + tr_inv_ = QTransform(tr_).inverted(); } - /* ここで画像Pixelに保存 */ - for (int zz = z1; zz <= z2; ++zz) { - accum_val[zz] /= static_cast(accum_counter); - - if ((0 <= ref_increase_val) && (in_current_pixel[zz] < accum_val[zz])) { - /* 増分のみMask! */ - accum_val[zz] = - static_cast(in_current_pixel[zz]) + - (accum_val[zz] - in_current_pixel[zz]) * ref_increase_val; - } else if ((0 <= ref_decrease_val) && - (accum_val[zz] < in_current_pixel[zz])) { - /* 減分のみMask! */ - accum_val[zz] = - static_cast(in_current_pixel[zz]) + - (accum_val[zz] - in_current_pixel[zz]) * ref_decrease_val; + } + void pixel_value(const float* in_current_pixel, const int xx, const int yy, + const bool isRGB, const double refVal, float* result_pixel) { + auto in_pixel = [&](int x, int y) { + /* clamp */ + x = (x < 0) ? 0 : ((this->ww_ <= x) ? this->ww_ - 1 : x); + y = (y < 0) ? 0 : ((this->hh_ <= y) ? this->hh_ - 1 : y); + return this->in_top_ + this->cc_ * y * this->ww_ + this->cc_ * x; + }; + auto interp = [&](float v1, float v2, float r) { + return v1 * (1.f - r) + v2 * r; + }; + auto accum_interp_in_values = [&](QPointF pos, std::vector& accumP, + int z1, int z2, float weight) { + int xId = (int)std::floor(pos.x()); + float rx = pos.x() - (float)xId; + int yId = (int)std::floor(pos.y()); + float ry = pos.y() - (float)yId; + const float* p00 = in_pixel(xId, yId); + const float* p01 = in_pixel(xId + 1, yId); + const float* p10 = in_pixel(xId, yId + 1); + const float* p11 = in_pixel(xId + 1, yId + 1); + for (int zz = z1; zz <= z2; zz++) { + accumP[zz] += weight * interp(interp(p00[zz], p01[zz], rx), + interp(p10[zz], p11[zz], rx), ry); } - - accum_val[zz] += 0.5; /* 誤差対策 */ - if (this->dmax_ < accum_val[zz]) { - result_pixel[zz] = static_cast(this->imax_); - } else if (accum_val[zz] < 0) { - result_pixel[zz] = 0; - } else { - result_pixel[zz] = static_cast(accum_val[zz]); + }; + auto accum_in_values = [&](QPointF pos, std::vector& accumP, int z1, + int z2, float weight) { + int xId = (int)std::round(pos.x()); + int yId = (int)std::round(pos.y()); + const float* p = in_pixel(xId, yId); + for (int zz = z1; zz <= z2; zz++) { + accumP[zz] += weight * p[zz]; } - } - } - -private: - radial_() {} - - const T *in_top_; - const int hh_; - const int ww_; - const int cc_; - const double xc_; - const double yc_; - const double sub_size_; - const int imax_; - const double dmax_; - const double intensity_; - const double radius_; - - /* copy constructorを無効化 */ - radial_(const radial_ &); - - /* 代入演算子を無効化 */ - radial_ &operator=(const radial_ &); -}; -template -void radial_convert_template_( - const IT *in, const int margin /* 参照画像(in)がもつ余白 */ - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_bits, const int ref_mode // R,G,B,A,luminance - - , - IT *out, const int hh /* 求める画像(out)の高さ */ - , - const int ww /* 求める画像(out)の幅 */ - , - const int cc, const double xc, const double yc, - const double intensity /* 強度。ゼロより大きく2以下 */ - /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい */ - , - const double radius /* 平均値ぼかしの始まる半径 */ - , - const int sub_div /* 1ならJaggy、2以上はAntialias */ - , - const bool alpha_rendering_sw) { - ref_bits; // for warning - - /* 強度のないとき、または、サブ分割がないとき、なにもしない */ - if (intensity <= 0.0 || sub_div <= 0) { - return; - } - - radial_ cl_ra( - in, hh + margin * 2, ww + margin * 2, cc, xc + margin, yc + margin, - 1.0 / sub_div, std::numeric_limits::max() /* サンプリング値 */ - , - static_cast(std::numeric_limits::max()), intensity, radius); + }; + int c1, c2; + if (isRGB) { #if defined RGBA_ORDER_OF_TOONZ6 - const int z1 = igs::image::rgba::blu; - const int z2 = igs::image::rgba::red; + c1 = igs::image::rgba::blu; + c2 = igs::image::rgba::red; #elif defined RGBA_ORDER_OF_OPENGL - const int z1 = igs::image::rgba::red; - const int z2 = igs::image::rgba::blu; + c1 = igs::image::rgba::red; + c2 = igs::image::rgba::blu; #else - Must be define / DRGBA_ORDER_OF_TOONZ6 or - / DRGBA_ORDER_OF_OPENGL + Must be define / DRGBA_ORDER_OF_TOONZ6 or / DRGBA_ORDER_OF_OPENGL #endif - const IT *p_in = in + margin * (ww + margin * 2) * cc + margin * cc; - IT *pout = out; - - if (0 == ref) { /* 参照なし */ - if (igs::image::rgba::siz == cc) { - using namespace igs::image::rgba; - if (alpha_rendering_sw) { /* Alphaも処理する */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc) { - cl_ra.pixel_value(p_in, xx, yy, alp, alp, -1.0, -1.0, -1.0, pout); - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ra.pixel_value(p_in, xx, yy, z1, z2, -1.0, -1.0, -1.0, pout); - } - } - } else { /* Alpha処理しない、RGB増分をAlphaでMaskする */ - const unsigned int val_max = std::numeric_limits::max(); - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc) { - pout[alp] = p_in[alp]; - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - /* Alpha値あるならRGB増分のみAlphaでMaskする */ - cl_ra.pixel_value(p_in, xx, yy, z1, z2, - static_cast(p_in[alp]) / val_max, -1.0, - -1.0, pout); - } - } - } - } else { /* Alphaがない, RGB/Grayscale... */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; ++xx, p_in += cc, pout += cc) { - cl_ra.pixel_value(p_in, xx, yy, 0, cc - 1, -1.0, -1.0, -1.0, pout); - } - } + } else { + c1 = igs::image::rgba::alp; + c2 = igs::image::rgba::alp; } - } else { /* 参照あり */ - const RT *refe = ref; - const int r_max = std::numeric_limits::max(); - if (igs::image::rgba::siz == cc) { - using namespace igs::image::rgba; - if (alpha_rendering_sw) { /* Alpha処理する */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc, refe += cc) { - const double refv = - igs::color::ref_value(refe, cc, r_max, ref_mode); - if (0 == refv) { - for (int zz = 0; zz < cc; ++zz) { - pout[zz] = p_in[zz]; - } - continue; - } - - cl_ra.pixel_value(p_in, xx, yy, alp, alp, -1.0, -1.0, refv, pout); - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ra.pixel_value(p_in, xx, yy, z1, z2, -1.0, -1.0, refv, pout); - } - } - } else { /* Alpha処理しない、RGB増分をAlphaでMaskする */ - const unsigned int val_max = std::numeric_limits::max(); - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc, refe += cc) { - const double refv = - igs::color::ref_value(refe, cc, r_max, ref_mode); - if (0 == refv) { - for (int zz = 0; zz < cc; ++zz) { - pout[zz] = p_in[zz]; - } - continue; - } - - pout[alp] = p_in[alp]; - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ra.pixel_value(p_in, xx, yy, z1, z2, - static_cast(p_in[alp]) / val_max, -1.0, - refv, pout); - } - } - } - } else { /* Alphaがない, RGB/Grayscale... */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc, refe += cc) { - const double refv = igs::color::ref_value(refe, cc, r_max, ref_mode); - if (0 == refv) { - for (int zz = 0; zz < cc; ++zz) { - pout[zz] = p_in[zz]; - } - continue; - } - cl_ra.pixel_value(p_in, xx, yy, 0, cc - 1, -1.0, -1.0, refv, pout); - } - } - } - } -} -} -//-------------------------------------------------------------------- -namespace { -class twist_ { -public: - twist_(const int height, const double xc, const double yc, - const double twist_radian, const double twist_radius) - : xc_(xc) - , yc_(yc) - , twist_xp_(0) - , twist_yp_(0) - , twist_radian_(twist_radian) - , twist_radius_(twist_radius) - , current_xp_(0) - , current_yp_(0) - , current_radian_(0) - , current_cos_(0) - , current_sin_(0) - , current_radius_(0) { - /* twist_radiusは、Twist曲線上半径1のPixel画像上半径 */ - /* twist_radiusはゼロのとき、高さの半分を設定する */ - if (this->twist_radius_ <= 0.0) { - this->twist_radius_ = height / 2.0; - } - /* ゼロだと計算不能になる */ - if (this->twist_radius_ <= 0.0) { - throw std::domain_error("twist_radius is equal less than zero"); - } - } - void current_pos(const double xp, const double yp) { - /* 現在位置 */ - this->current_xp_ = xp; - this->current_yp_ = yp; - - /* 中心からの距離 */ - const double xv = xp - this->xc_; - const double yv = yp - this->yc_; - this->current_radius_ = sqrt(xv * xv + yv * yv); - - /* 基準からの距離比 */ - const double tt = this->current_radius_ / this->twist_radius_; - - /* Twist曲線上の位置 */ - const double xx = tt * cos(tt * this->twist_radian_); - const double yy = tt * sin(tt * this->twist_radian_); - - /* Twist曲線の回転角度 */ - this->current_radian_ = atan2(yv, xv) - atan2(yy, xx); - - /* Twist曲線上の位置を回転角度に傾けた位置 */ - this->current_cos_ = cos(this->current_radian_); - this->current_sin_ = sin(this->current_radian_); - this->twist_xp_ = xx * this->current_cos_ - yy * this->current_sin_; - this->twist_yp_ = xx * this->current_sin_ + yy * this->current_cos_; - } - void twist_pos(const double pixel_pos, int &xi, int &yi) { - /* 基準半径からの比 */ - const double tt = (this->current_radius_ + pixel_pos) / this->twist_radius_; - - /* Twist曲線上の比による位置 */ - const double xx = tt * cos(tt * this->twist_radian_); - const double yy = tt * sin(tt * this->twist_radian_); + const QPointF center(this->center_.x, this->center_.y); - /* その位置の角度に傾ける */ - double x2 = xx * this->current_cos_ - yy * this->current_sin_; - double y2 = xx * this->current_sin_ + yy * this->current_cos_; - - /* Twist曲線上の位置の差 */ - x2 = (x2 - this->twist_xp_); - y2 = (y2 - this->twist_yp_); - - /* Pixel画像上の位置の差 */ - x2 *= this->twist_radius_; - y2 *= this->twist_radius_; - - /* Pixel画像上の位置 */ - x2 += this->current_xp_; - y2 += this->current_yp_; - - xi = static_cast(x2); - yi = static_cast(y2); - } - -private: - twist_(); - - const double xc_; - const double yc_; - - double twist_xp_; - double twist_yp_; - double twist_radian_; - double twist_radius_; - - double current_xp_; - double current_yp_; - double current_radian_; - double current_cos_; - double current_sin_; - double current_radius_; - - /* copy constructorを無効化 */ - twist_(const twist_ &); - - /* 代入演算子を無効化 */ - twist_ &operator=(const twist_ &); -}; -//-------------------------------- -template -class radial_twist_ { -public: - radial_twist_(const T *in_top, const int height /* 求める画像(out)の高さ */ - , - const int width /* 求める画像(out)の幅 */ - , - const int channels, const double xc, const double yc, - const double sub_size, const int imax, const double dmax, - const double intensity /* 平均値ぼかし強度 */ - , - const double radius /* 平均値ぼかしの始まる半径 */ - , - const double twist_radian, const double twist_radius) - : in_top_(in_top) - , hh_(height) - , ww_(width) - , cc_(channels) - , xc_(xc) - , yc_(yc) - , sub_size_(sub_size) - , imax_(imax) - , dmax_(dmax) - , intensity_(intensity) - , radius_(radius) - , cl_tw_(height, xc, yc, twist_radian, twist_radius) {} - void pixel_value(const T *in_current_pixel, const int xx, const int yy, - const int z1, const int z2, const double ref_increase_val, - const double ref_decrease_val, - const double each_pixel_blur_ratio, T *result_pixel) { /* Pixel位置(0.5 1.5 2.5 ...) */ - const double xp = static_cast(xx) + 0.5; - const double yp = static_cast(yy) + 0.5; + const QPointF p(static_cast(xx) + 0.5f, + static_cast(yy) + 0.5f); /* 中心からPixel位置へのベクトルと長さ */ - const double xv = xp - this->xc_; - const double yv = yp - this->yc_; - const double dist = sqrt(xv * xv + yv * yv); + const QVector2D v(p - center); + const float dist = v.length(); /* 指定半径の範囲内なら何もしない */ - if (dist <= this->radius_) { - for (int zz = z1; zz <= z2; ++zz) { - result_pixel[zz] = in_current_pixel[zz]; + bool is_in_blur_radius = false; + if (this->ellipse_aspect_ratio_ == 1.f) { + is_in_blur_radius = (dist <= this->blur_radius_); + } else { + is_in_blur_radius = + QVector2D(this->tr_inv_.map(v.toPointF())).lengthSquared() <= + (this->blur_radius_ * this->blur_radius_); + } + if (is_in_blur_radius) { + for (int c = c1; c <= c2; ++c) { + result_pixel[c] = in_current_pixel[c]; } return; } + /* 積算値と積算回数 */ + std::vector accum_val(this->cc_); + float accum_counter = 0.f; + /* Radial方向のSamplingの開始位置と終了位置 */ - double scale = this->intensity_; - if (0.0 <= each_pixel_blur_ratio) { - scale *= each_pixel_blur_ratio; + float scale = this->intensity_ * refVal; + + float distanceRatio = 1.f; + float length; + if (this->ellipse_aspect_ratio_ == 1.) { + if (this->type_ == Accelerator) + length = (dist - this->blur_radius_) * scale; + else // Uniform_Length + length = (this->pivot_radius_ - this->blur_radius_) * scale; + } else { + float dist_mod = QVector2D(this->tr_inv_.map(v.toPointF())).length(); + distanceRatio = dist / dist_mod; + if (this->type_ == Accelerator) + length = (dist - this->blur_radius_ * distanceRatio) * scale * + std::pow(distanceRatio, intensity_correlation_with_ellipse_); + else // Uniform_Length + length = (this->pivot_radius_ - this->blur_radius_) * scale * + std::pow(distanceRatio, intensity_correlation_with_ellipse_); } - const double length = (dist - this->radius_) * scale; - const double count_half = floor(length / 2.0 / this->sub_size_); - const double sub_sta = -this->sub_size_ * count_half; - const double sub_end = this->sub_size_ * count_half; - /* 積算値と積算回数 */ - std::vector accum_val(this->cc_); - int accum_counter = 0; - - /* SubPixelによるSamplingの相対位置 */ - for (double xsub = this->sub_size_ / 2.0 - 0.5; xsub < 0.5; - xsub += this->sub_size_) { - for (double ysub = this->sub_size_ / 2.0 - 0.5; ysub < 0.5; - ysub += this->sub_size_) { - /* Sampling位置をセット */ - this->cl_tw_.current_pos(xp + xsub, yp + ysub); - - /* Radial&Twist方向のSampling */ - for (double tt = sub_sta; tt <= sub_end; tt += this->sub_size_) { - /* Sampling位置からPixel位置を得る */ - int xi = 0; - int yi = 0; - this->cl_tw_.twist_pos(tt, xi, yi); - - /* clamp */ - xi = (xi < 0) ? 0 : ((this->ww_ <= xi) ? this->ww_ - 1 : xi); - yi = (yi < 0) ? 0 : ((this->hh_ <= yi) ? this->hh_ - 1 : yi); - - /* 画像のPixel位置 */ - const T *in_current = - this->in_top_ + this->cc_ * this->ww_ * yi + this->cc_ * xi; - - /* 積算 */ - for (int zz = z1; zz <= z2; ++zz) { - accum_val[zz] += static_cast(in_current[zz]); + float blur_length_half = length * 0.5f; + + // sampling in both directions + float sample_length = 0.f; + while (sample_length < blur_length_half) { + // compute weight + float weight = 1.f; + if (sample_length > blur_length_half - 1.f) { + if (antialias_sw_) + weight = blur_length_half - sample_length; + else + break; + } + // advance to the next sample + sample_length += weight; + + float sample_dists[2] = {dist + sample_length, dist - sample_length}; + for (int i = 0; i < 2; i++) { + if (sample_dists[i] < 0.f) continue; // 中心を突き抜けた場合は無視する + QPointF v_smpl; + float scaleVal = sample_dists[i] / dist; + if (this->twist_radian_ == 0.0) { + if (this->ellipse_aspect_ratio_ == 1.f) + v_smpl = v.toPointF() * scaleVal; + else + v_smpl = (this->tr_inv_ * + QTransform(this->tr_).scale(scaleVal, scaleVal)) + .map(v.toPointF()); + } else { // twisted case + if (this->ellipse_aspect_ratio_ == 1.f) { + double tmp_twist_angle = + twist_radian_ * (sample_dists[i] - dist) / pivot_radius_; + + float cos = std::cos(tmp_twist_angle); + float sin = std::sin(tmp_twist_angle); + + v_smpl = + QPointF(cos * v.x() - sin * v.y(), sin * v.x() + cos * v.y()) * + scaleVal; + } else { + double tmp_twist_angle = twist_radian_ * (sample_dists[i] - dist) * + distanceRatio / pivot_radius_; + v_smpl = (this->tr_inv_ * QTransform(this->tr_) + .rotateRadians(tmp_twist_angle) + .scale(scaleVal, scaleVal)) + .map(v.toPointF()); } - ++accum_counter; } + + if (antialias_sw_) + accum_interp_in_values(v_smpl + center, accum_val, c1, c2, weight); + else + accum_in_values(v_smpl + center, accum_val, c1, c2, weight); + accum_counter += weight; } } + // sample the original pos + if (antialias_sw_) + accum_interp_in_values(p, accum_val, c1, c2, 1.f); + else + accum_in_values(p, accum_val, c1, c2, 1.f); + accum_counter += 1.f; + /* 積算しなかったとき(念のためのCheck) */ - if (accum_counter <= 0) { - for (int zz = z1; zz <= z2; ++zz) { - result_pixel[zz] = in_current_pixel[zz]; + if (accum_counter <= 0.f) { + for (int c = c1; c <= c2; ++c) { + result_pixel[c] = in_current_pixel[c]; } return; } /* ここで画像Pixelに保存 */ - for (int zz = z1; zz <= z2; ++zz) { - accum_val[zz] /= static_cast(accum_counter); - - if ((0 <= ref_increase_val) && (in_current_pixel[zz] < accum_val[zz])) { - /* 増分のみMask! */ - accum_val[zz] = - static_cast(in_current_pixel[zz]) + - (accum_val[zz] - in_current_pixel[zz]) * ref_increase_val; - } else if ((0 <= ref_decrease_val) && - (accum_val[zz] < in_current_pixel[zz])) { - /* 減分のみMask! */ - accum_val[zz] = - static_cast(in_current_pixel[zz]) + - (accum_val[zz] - in_current_pixel[zz]) * ref_decrease_val; - } - - accum_val[zz] += 0.5; /* 誤差対策 */ - if (this->dmax_ < accum_val[zz]) { - result_pixel[zz] = static_cast(this->imax_); - } else if (accum_val[zz] < 0) { - result_pixel[zz] = 0; + for (int c = c1; c <= c2; ++c) { + accum_val[c] /= accum_counter; + + if (isRGB && !this->alpha_rendering_sw_ && + (in_current_pixel[c] < accum_val[c]) && + result_pixel[igs::image::rgba::alp] < 1.f) { + result_pixel[c] = + in_current_pixel[c] + (accum_val[c] - in_current_pixel[c]) * + result_pixel[igs::image::rgba::alp]; } else { - result_pixel[zz] = static_cast(accum_val[zz]); + result_pixel[c] = accum_val[c]; } } } private: - radial_twist_() {} - - const T *in_top_; - const int hh_; - const int ww_; - const int cc_; - const double xc_; - const double yc_; - const double sub_size_; - const int imax_; - const double dmax_; - const double intensity_; - const double radius_; - twist_ cl_tw_; + radial_(); /* copy constructorを無効化 */ - radial_twist_(const radial_twist_ &); + radial_(const radial_&); /* 代入演算子を無効化 */ - radial_twist_ &operator=(const radial_twist_ &); + radial_& operator=(const radial_&); }; -//-------------------------------- -template -void twist_convert_template_( - const IT *in, const int margin /* 参照画像(in)がもつ余白 */ - - , - const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */ - , - const int ref_bits, const int ref_mode // R,G,B,A,luminance - - , - IT *out, const int hh /* 求める画像(out)の高さ */ - , - const int ww /* 求める画像(out)の幅 */ - , - const int cc, const double xc, const double yc, const double twist_radian, - const double twist_radius, - const double intensity /* 強度。ゼロより大きく2以下 */ - /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい */ - , - const double radius /* 平均値ぼかしの始まる半径 */ - , - const int sub_div /* 1ならJaggy、2以上はAntialias */ - , - const bool alpha_rendering_sw) { - ref_bits; // for warning - - /* 強度のないとき、または、サブ分割がないとき、なにもしない */ - if (intensity <= 0.0 || sub_div <= 0) { - return; - } - radial_twist_ cl_ratw(in, hh + margin * 2, ww + margin * 2, cc, - xc + margin, yc + margin, 1.0 / sub_div, - std::numeric_limits::max(), - static_cast(std::numeric_limits::max()), - intensity, radius, twist_radian, twist_radius); - -#if defined RGBA_ORDER_OF_TOONZ6 - const int z1 = igs::image::rgba::blu; - const int z2 = igs::image::rgba::red; -#elif defined RGBA_ORDER_OF_OPENGL - const int z1 = igs::image::rgba::red; - const int z2 = igs::image::rgba::blu; -#else - Must be define / DRGBA_ORDER_OF_TOONZ6 or - / DRGBA_ORDER_OF_OPENGL -#endif - - const IT *p_in = in + margin * (ww + margin * 2) * cc + margin * cc; - IT *pout = out; - - if (0 == ref) { /* 参照なし */ - if (igs::image::rgba::siz == cc) { +void radial_convert( + const float* in, float* out, const int margin, /* 参照画像(in)がもつ余白 */ + const TDimension out_dim, /* 求める画像(out)のサイズ */ + const int channels, const float* ref, /* 求める画像(out)と同じ高さ、幅 */ + const TPointD center, + const double intensity, /* 強度。ゼロより大きく2以下 */ + /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい */ + const double radius, /* 平均値ぼかしの始まる半径 */ + const double twist_radian, const double pivot_radius, + const int type, // 0: Accelerator, 1: Uniform Length + const bool antialias_sw, /* when true, sampled pixel will be + bilinear-interpolated */ + const bool alpha_rendering_sw, const double ellipse_aspect_ratio, + const double ellipse_angle, + const double intensity_correlation_with_ellipse) { + assert(intensity > 0.0); + + radial_ radial(in, out_dim.ly + margin * 2, out_dim.lx + margin * 2, channels, + center, antialias_sw, alpha_rendering_sw, intensity, radius, + twist_radian, pivot_radius, type, ellipse_aspect_ratio, + ellipse_angle * M_PI_180, intensity_correlation_with_ellipse); + + const float* p_in = + in + margin * (out_dim.lx + margin * 2) * channels + margin * channels; + + float* p_out = out; + const float* p_ref = ref; + + for (int yy = margin; yy < out_dim.ly + margin; + ++yy, p_in += 2 * margin * channels) { + for (int xx = margin; xx < out_dim.lx + margin; + ++xx, p_in += channels, p_out += channels) { using namespace igs::image::rgba; - if (alpha_rendering_sw) { /* Alphaも処理する */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc) { - cl_ratw.pixel_value(p_in, xx, yy, alp, alp, -1.0, -1.0, -1.0, pout); - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ratw.pixel_value(p_in, xx, yy, z1, z2, -1.0, -1.0, -1.0, pout); - } - } - } else { /* Alpha処理しない、RGB増分をAlphaでMaskする */ - const unsigned int val_max = std::numeric_limits::max(); - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc) { - pout[alp] = p_in[alp]; - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ratw.pixel_value(p_in, xx, yy, z1, z2, - static_cast(p_in[alp]) / val_max, -1.0, - -1.0, pout); - } - } - } - } else { /* Alphaがない, RGB/Grayscale... */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; ++xx, p_in += cc, pout += cc) { - cl_ratw.pixel_value(p_in, xx, yy, 0, cc - 1, -1.0, -1.0, -1.0, pout); - } + float refVal = (ref) ? *p_ref : 1.f; + + // if the reference value is zero + if (refVal == 0.f) { + for (int c = 0; c < channels; ++c) p_out[c] = p_in[c]; + p_ref++; + continue; } - } - } else { /* 参照あり */ - const RT *refe = ref; - const int r_max = std::numeric_limits::max(); - if (igs::image::rgba::siz == cc) { - using namespace igs::image::rgba; - if (alpha_rendering_sw) { /* Alphaも処理する */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc, refe += cc) { - const double refv = - igs::color::ref_value(refe, cc, r_max, ref_mode); - if (0 == refv) { - for (int zz = 0; zz < cc; ++zz) { - pout[zz] = p_in[zz]; - } - continue; - } - - cl_ratw.pixel_value(p_in, xx, yy, alp, alp, -1.0, -1.0, refv, pout); - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ratw.pixel_value(p_in, xx, yy, z1, z2, -1.0, -1.0, refv, pout); - } - } - } else { /* Alpha処理しない、RGB増分をAlphaでMaskする */ - const unsigned int val_max = std::numeric_limits::max(); - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc, refe += cc) { - const double refv = - igs::color::ref_value(refe, cc, r_max, ref_mode); - if (0 == refv) { - for (int zz = 0; zz < cc; ++zz) { - pout[zz] = p_in[zz]; - } - continue; - } - - pout[alp] = p_in[alp]; - if (0 == pout[alp]) { - pout[red] = p_in[red]; - pout[gre] = p_in[gre]; - pout[blu] = p_in[blu]; - continue; - } - cl_ratw.pixel_value(p_in, xx, yy, z1, z2, - static_cast(p_in[alp]) / val_max, -1.0, - refv, pout); - } - } + + if (alpha_rendering_sw) { // blur alpha + radial.pixel_value(p_in, xx, yy, false, refVal, p_out); + } else { // use the src alpha as-is + p_out[alp] = p_in[alp]; } - } else { /* Alphaがない, RGB/Grayscale... */ - for (int yy = margin; yy < hh + margin; ++yy, p_in += 2 * margin * cc) { - for (int xx = margin; xx < ww + margin; - ++xx, p_in += cc, pout += cc, refe += cc) { - const double refv = igs::color::ref_value(refe, cc, r_max, ref_mode); - if (0 == refv) { - for (int zz = 0; zz < cc; ++zz) { - pout[zz] = p_in[zz]; - } - continue; - } - cl_ratw.pixel_value(p_in, xx, yy, 0, cc - 1, -1.0, -1.0, refv, pout); - } + if (p_out[alp] == 0.f) { + p_out[red] = p_in[red]; + p_out[gre] = p_in[gre]; + p_out[blu] = p_in[blu]; + if (ref) p_ref++; + continue; } + + // blur RGB channels + radial.pixel_value(p_in, xx, yy, true, refVal, p_out); + + if (ref) p_ref++; } } } -} +} // namespace + //-------------------------------------------------------------------- void igs::radial_blur::convert( - const unsigned char *in, const int margin /* 参照画像(in)がもつ余白 */ - - , - const unsigned char *ref /* outと同じ高さ、幅、チャンネル数 */ - , - const int ref_bits, const int ref_mode // R,G,B,A,luminance - - , - unsigned char *out - - , - const int height /* 求める画像(out)の高さ */ - , - const int width /* 求める画像(out)の幅 */ - , - const int channels, const int bits - - , - const double xc, const double yc, const double twist_radian, - const double twist_radius - /* -Twist Radius - ひねりの基準半径を指定します。 - 単位はミリメートルです。 - 最小は0。最大1000mm(1m)です。 - 初期値は0でひねりはありません。 -*/ - , - const double intensity /* 強度。ゼロより大きく2以下 */ + const float* in, float* out, const int margin, /* 参照画像(in)がもつ余白 */ + const TDimension out_dim, /* 求める画像(out)の大きさ*/ + const int channels, const float* ref, /* outと同じ高さ、幅 */ + const TPointD center, const double twist_radian, const double twist_radius, + const double intensity, /* 強度。ゼロより大きく2以下 */ /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい */ - , - const double radius /* 平均値ぼかしの始まる半径 */ - , - const int sub_div /* 1ならJaggy、2以上はAntialias */ - , - const bool alpha_rendering_sw) { - if ((igs::image::rgba::siz != channels) && - (igs::image::rgb::siz != channels) && (1 != channels) /* bit(monoBW) */ - ) { - throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); - } - - if ((std::numeric_limits::digits != bits) && - (std::numeric_limits::digits != bits)) { - throw std::domain_error("Bad in bits,Not uchar/ushort"); - } - if ((0 != ref) && (std::numeric_limits::digits != ref_bits) && - (std::numeric_limits::digits != ref_bits)) { - throw std::domain_error("Bad ref bits,Not uchar/ushort"); - } - - /* 強度のないとき、または、サブ分割がないとき */ - if (intensity <= 0.0 || sub_div <= 0) { - if (std::numeric_limits::digits == bits) { - igs::image::copy_except_margin(in, margin, out, height, width, channels); - } else if (std::numeric_limits::digits == bits) { - igs::image::copy_except_margin( - reinterpret_cast(in), margin, - reinterpret_cast(out), height, width, channels); - } + const double radius, /* 平均値ぼかしの始まる半径 */ + const int type, const bool antialias_sw, const bool alpha_rendering_sw, + const double ellipse_aspect_ratio, const double ellipse_angle, + const double intensity_correlation_with_ellipse) { + /* 強度のないとき */ + if (intensity <= 0.0) { + igs::image::copy_except_margin(in, margin, out, out_dim.ly, out_dim.lx, + channels); return; } - - if (0.0 == twist_radian) { - if (std::numeric_limits::digits == ref_bits) { - if (std::numeric_limits::digits == bits) { - radial_convert_template_(in, margin, ref, ref_bits, ref_mode, out, - height, width, channels, xc, yc, intensity, - radius, sub_div, alpha_rendering_sw); - } else if (std::numeric_limits::digits == bits) { - radial_convert_template_( - reinterpret_cast(in), margin, ref, ref_bits, - ref_mode, reinterpret_cast(out), height, width, - channels, xc, yc, intensity, radius, sub_div, alpha_rendering_sw); - } - } else { /* ref_bitsがゼロでも(refがなくても)ここにくる */ - if (std::numeric_limits::digits == bits) { - radial_convert_template_( - in, margin, reinterpret_cast(ref), ref_bits, - ref_mode, out, height, width, channels, xc, yc, intensity, radius, - sub_div, alpha_rendering_sw); - } else if (std::numeric_limits::digits == bits) { - radial_convert_template_( - reinterpret_cast(in), margin, - reinterpret_cast(ref), ref_bits, ref_mode, - reinterpret_cast(out), height, width, channels, - xc, yc, intensity, radius, sub_div, alpha_rendering_sw); - } - } - } else { - if (std::numeric_limits::digits == ref_bits) { - if (std::numeric_limits::digits == bits) { - twist_convert_template_(in, margin, ref, ref_bits, ref_mode, out, - height, width, channels, xc, yc, twist_radian, - twist_radius, intensity, radius, sub_div, - alpha_rendering_sw); - } else if (std::numeric_limits::digits == bits) { - twist_convert_template_( - reinterpret_cast(in), margin, ref, ref_bits, - ref_mode, reinterpret_cast(out), height, width, - channels, xc, yc, twist_radian, twist_radius, intensity, radius, - sub_div, alpha_rendering_sw); - } - } else { /* ref_bitsがゼロでも(refがなくても)ここにくる */ - if (std::numeric_limits::digits == bits) { - twist_convert_template_( - in, margin, reinterpret_cast(ref), ref_bits, - ref_mode, out, height, width, channels, xc, yc, twist_radian, - twist_radius, intensity, radius, sub_div, alpha_rendering_sw); - } else if (std::numeric_limits::digits == bits) { - twist_convert_template_( - reinterpret_cast(in), margin, - reinterpret_cast(ref), ref_bits, ref_mode, - reinterpret_cast(out), height, width, channels, - xc, yc, twist_radian, twist_radius, intensity, radius, sub_div, - alpha_rendering_sw); - } - } - } + radial_convert(in, out, margin, out_dim, channels, ref, center, intensity, + radius, twist_radian, twist_radius, type, antialias_sw, + alpha_rendering_sw, ellipse_aspect_ratio, ellipse_angle, + intensity_correlation_with_ellipse); } -#if 0 //-------------------- comment out start ------------------------ +//-------------------- comment out end ------------------------- namespace { - double enlarge_margin_length_( - const double xc - ,const double yc - ,const double xp - ,const double yp - ,const double intensity - ,const double radius - ,const double sub_size - ) { - const double xv = xp - xc; - const double yv = yp - yc; - const double dist = sqrt(xv*xv + yv*yv); - - /* "dist = len - (len - radius) * intensity / 2.0;"より */ - const double len = (2.0 * dist - radius * intensity) / - (2.0-intensity); - - /***const double length = (len - radius) * intensity; - const double count_half = floor(length / sub_size); - return sub_size * count_half;***/ - - if (len <= radius) { return 0; } - return (len - radius) * intensity / 2.0; - } -} -int igs::radial_blur::enlarge_margin( /* Twist時は正確ではない... */ - const int height - ,const int width - ,const double xc - ,const double yc - ,const double twist_radian - ,const double twist_radius - ,const double intensity/* 強度。ゼロより大きく2以下 */ - /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい */ - ,const double radius /* 平均値ぼかしの始まる半径 */ - ,const int sub_div /* 1ならJaggy、2以上はAntialias */ -) { - /* 強度のないとき、2以上のとき、または、サブ分割がないとき、 - なにもしない */ - if (intensity <= 0.0 || 2.0 <= intensity || sub_div <= 0) { - return 0; - } - - double margin1 = 0; - double margin2 = 0; - - margin1 = enlarge_margin_length_( - xc,yc,-width/2.0,-height/2.0,intensity,radius,1.0/sub_div); - - margin2 = enlarge_margin_length_( - xc,yc,-width/2.0, height/2.0,intensity,radius,1.0/sub_div); - if (margin1 < margin2) { margin1 = margin2; } - - margin2 = enlarge_margin_length_( - xc,yc, width/2.0,-height/2.0,intensity,radius,1.0/sub_div); - if (margin1 < margin2) { margin1 = margin2; } - - margin2 = enlarge_margin_length_( - xc,yc, width/2.0, height/2.0,intensity,radius,1.0/sub_div); - if (margin1 < margin2) { margin1 = margin2; } - - return static_cast(ceil(margin1)); -} -#endif //-------------------- comment out end ------------------------- -namespace { -double reference_margin_length_(const double xc, const double yc, - const double xp, const double yp, - const double intensity, const double radius, - const double sub_size) { - const double xv = xp - xc; - const double yv = yp - yc; - const double dist = sqrt(xv * xv + yv * yv); + +double reference_margin_length_(const TPointD center, const double xp, + const double yp, const double intensity, + const double radius, const double pivot_radius, + const int type, + const double ellipse_aspect_ratio, + const double intensity_correlation_with_ellipse, + const QTransform& tr_inv) { + const QPointF c(center.x, center.y); + const QPointF p(xp, yp); + const QVector2D v(p - c); + const float dist = v.length(); if (dist <= radius) { return 0; } - const double half_length = (dist - radius) * intensity / 2.0; - const double count_half = floor(half_length / sub_size); - return sub_size * count_half; -} + double length; + if (ellipse_aspect_ratio == 1.) { + if (type == Accelerator) + length = (dist - radius) * intensity; + else + length = (pivot_radius - radius) * intensity; + } else { + float dist_mod = QVector2D(tr_inv.map(v.toPointF())).length(); + double distanceRatio = dist / dist_mod; + if (type == Accelerator) + length = (dist - radius * distanceRatio) * intensity * + std::pow(distanceRatio, intensity_correlation_with_ellipse); + else // Uniform_Length + length = (pivot_radius - radius) * intensity * + std::pow(distanceRatio, intensity_correlation_with_ellipse); + } + return length; } + +} // namespace int igs::radial_blur:: reference_margin(/* Twist時は正確ではない... */ - const int height, const int width, const double xc, - const double yc, const double twist_radian, - const double twist_radius, - const double intensity /* 強度。ゼロより大きく2以下 */ + const int height, const int width, const TPointD center, + const double twist_radian, const double twist_radius, + const double intensity, /* 強度。ゼロより大きく2以下 */ /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい - */ - , - const double radius /* 平均値ぼかしの始まる半径 */ - , - const int sub_div /* 1ならJaggy、2以上はAntialias */ - ) { - twist_radius; // for warning - twist_radian; // for warning - - /* 強度のないとき、2以上のとき、または、サブ分割がないとき、 - なにもしない */ - if (intensity <= 0.0 || 2.0 <= intensity || sub_div <= 0) { + */ + const double radius, /* 平均値ぼかしの始まる半径 */ + const int type, const double ellipse_aspect_ratio, + const double ellipse_angle, + const double intensity_correlation_with_ellipse) { + /* 強度のないとき、2以上のとき、なにもしない */ + if (intensity <= 0.0 || 2.0 <= intensity) { return 0; } double margin1 = 0; double margin2 = 0; + QTransform tr_inv; + if (ellipse_aspect_ratio != 1.0) { + double axis_x = 2.0 * ellipse_aspect_ratio / (ellipse_aspect_ratio + 1.0); + double axis_y = axis_x / ellipse_aspect_ratio; + tr_inv = QTransform() + .rotateRadians(ellipse_angle) + .scale(axis_x, axis_y) + .inverted(); + } + /* 四隅から参照する外部への最大マージンを計算する */ - margin1 = reference_margin_length_(xc, yc, -width / 2.0, -height / 2.0, - intensity, radius, 1.0 / sub_div); + margin1 = reference_margin_length_( + center, -width / 2.0, -height / 2.0, intensity, radius, twist_radius, + type, ellipse_aspect_ratio, intensity_correlation_with_ellipse, tr_inv); - margin2 = reference_margin_length_(xc, yc, -width / 2.0, height / 2.0, - intensity, radius, 1.0 / sub_div); + margin2 = reference_margin_length_( + center, -width / 2.0, height / 2.0, intensity, radius, twist_radius, type, + ellipse_aspect_ratio, intensity_correlation_with_ellipse, tr_inv); if (margin1 < margin2) { margin1 = margin2; } - margin2 = reference_margin_length_(xc, yc, width / 2.0, -height / 2.0, - intensity, radius, 1.0 / sub_div); + margin2 = reference_margin_length_( + center, width / 2.0, -height / 2.0, intensity, radius, twist_radius, type, + ellipse_aspect_ratio, intensity_correlation_with_ellipse, tr_inv); if (margin1 < margin2) { margin1 = margin2; } - margin2 = reference_margin_length_(xc, yc, width / 2.0, height / 2.0, - intensity, radius, 1.0 / sub_div); + margin2 = reference_margin_length_( + center, width / 2.0, height / 2.0, intensity, radius, twist_radius, type, + ellipse_aspect_ratio, intensity_correlation_with_ellipse, tr_inv); if (margin1 < margin2) { margin1 = margin2; } diff --git a/toonz/sources/stdfx/igs_radial_blur.h b/toonz/sources/stdfx/igs_radial_blur.h index 02b8b1e..4fd2b16 100644 --- a/toonz/sources/stdfx/igs_radial_blur.h +++ b/toonz/sources/stdfx/igs_radial_blur.h @@ -7,39 +7,23 @@ #define IGS_RADIAL_BLUR_EXPORT #endif +#include "tgeometry.h" + namespace igs { namespace radial_blur { IGS_RADIAL_BLUR_EXPORT void convert( - const unsigned char *in /* 余白付き */ - , - const int margin /* 参照画像(in)がもつ余白 */ - - , - const unsigned char *ref /* outと同じ高さ、幅、チャンネル数 */ - , - const int ref_bits, - const int ref_mode // R,G,B,A,luminance or R,G,B,luminance - - , - unsigned char *out /* 余白なし */ - - , - const int height /* 求める画像(out)の高さ */ - , - const int width /* 求める画像(out)の幅 */ - , - const int channels, const int bits - - , - const double xc, const double yc, const double twist_radian = 0.0, + const float* in, float* out, const int margin, /* 参照画像(in)がもつ余白 */ + const TDimension out_dim, /* 求める画像(out)の大きさ*/ + const int channels, const float* ref, /* outと同じ高さ、幅 */ + const TPointD center, const double twist_radian = 0.0, const double twist_radius = 0.0, - const double intensity = 0.2 /* 強度。ゼロより大きく2以下 */ - , - const double radius = 0.0 /* ぼかしの始まる半径 */ - , - const int sub_div = 4 /* 1ならJaggy、2以上はAntialias */ - , - const bool alpha_rendering_sw = true); + const double intensity = 0.2, /* 強度。ゼロより大きく2以下 */ + /* radius円境界での平均値ぼかしゼロとするためintensityは2より小さい */ + const double radius = 0.0, /* 平均値ぼかしの始まる半径 */ + const int type = 0, const bool antialias_sw = true, + const bool alpha_rendering_sw = true, + const double ellipse_aspect_ratio = 1.0, const double ellipse_angle = 0.0, + const double intensity_correlation_with_ellipse = 0.0); #if 0 //-------------------- comment out start ------------------------ IGS_RADIAL_BLUR_EXPORT int enlarge_margin( /* Twist時正確でない... */ const int height @@ -56,18 +40,16 @@ IGS_RADIAL_BLUR_EXPORT void convert( #endif //-------------------- comment out end ------------------------- IGS_RADIAL_BLUR_EXPORT int reference_margin(/* Twist時正確でない... */ - const int height, const int width, const double xc, - const double yc, const double twist_radian, - const double twist_radius, - const double intensity = 0.2 /* 強度。ゼロより大きく2以下 */ + const int height, const int width, const TPointD center, + const double twist_radian, const double twist_radius, + const double intensity = 0.2, /* 強度。ゼロより大きく2以下 */ /* radius円境界での平均値ぼかしゼロとするためintensityは2以下 - */ - , - const double radius = 0.0 /* 平均値ぼかしの始まる半径 */ - , - const int sub_div = 4 /* 1ならJaggy、2以上はAntialias */ - ); -} -} + */ + const double radius = 0.0, /* 平均値ぼかしの始まる半径 */ + const int type = 0, const double ellipse_aspect_ratio = 1.0, + const double ellipse_angle = 0.0, + const double intensity_correlation_with_ellipse = 0.0); +} // namespace radial_blur +} // namespace igs #endif /* !igs_radial_blur_h */ diff --git a/toonz/sources/stdfx/ino_radial_blur.cpp b/toonz/sources/stdfx/ino_radial_blur.cpp index 32c2d28..d04ea0d 100644 --- a/toonz/sources/stdfx/ino_radial_blur.cpp +++ b/toonz/sources/stdfx/ino_radial_blur.cpp @@ -24,6 +24,11 @@ class ino_radial_blur final : public TStandardRasterFx { TBoolParamP m_alpha_rendering; TBoolParamP m_anti_alias; TIntEnumParamP m_ref_mode; + TIntEnumParamP m_type; + // elliptical shape + TDoubleParamP m_ellipse_aspect_ratio; + TDoubleParamP m_ellipse_angle; + TDoubleParamP m_intensity_correlation_with_ellipse; public: ino_radial_blur() @@ -34,7 +39,11 @@ public: // ,m_twist_radius(0.0) , m_alpha_rendering(true) , m_anti_alias(false) - , m_ref_mode(new TIntEnumParam(0, "Red")) { + , m_ref_mode(new TIntEnumParam(0, "Red")) + , m_type(new TIntEnumParam(0, "Accelerator")) + , m_ellipse_aspect_ratio(1.0) + , m_ellipse_angle(0.0) + , m_intensity_correlation_with_ellipse(0.0) { this->m_center->getX()->setMeasureName("fxLength"); this->m_center->getY()->setMeasureName("fxLength"); this->m_radius->setMeasureName("fxLength"); @@ -50,10 +59,17 @@ public: bindParam(this, "alpha_rendering", this->m_alpha_rendering); bindParam(this, "anti_alias", this->m_anti_alias); bindParam(this, "reference", this->m_ref_mode); + bindParam(this, "type", this->m_type); + bindParam(this, "ellipse_aspect_ratio", this->m_ellipse_aspect_ratio); + bindParam(this, "ellipse_angle", this->m_ellipse_angle); + bindParam(this, "intensity_correlation_with_ellipse", + this->m_intensity_correlation_with_ellipse); this->m_radius->setValueRange(0, (std::numeric_limits::max)()); /* 拡大のしすぎを防ぐためにMaxを制限する */ this->m_blur->setValueRange(0.0 * ino_param_range, 1.0 * ino_param_range); + this->m_ellipse_aspect_ratio->setValueRange(0.1, 10.0); + this->m_ellipse_angle->setValueRange(-180.0, 180.0); this->m_twist->setValueRange(-180.0, 180.0); // this->m_twist_radius->setValueRange(0.0,1000.0); this->m_ref_mode->addItem(1, "Green"); @@ -61,6 +77,8 @@ public: this->m_ref_mode->addItem(3, "Alpha"); this->m_ref_mode->addItem(4, "Luminance"); this->m_ref_mode->addItem(-1, "Nothing"); + this->m_type->addItem(1, "Uniform Length"); + this->m_intensity_correlation_with_ellipse->setValueRange(-1.0, 1.0); } //------------------------------------------------------------ TPointD get_render_center(const double frame, const TPointD &pos, @@ -75,30 +93,26 @@ public: return center - pos; } int get_render_int_margin(const double frame, const TRectD &bBox, - const TAffine affine, TPointD center) { + const TAffine affine, TPointD center, + double blur_radius, double pivot_radius) { /*--- 単位変換(mm-->render用pixel)と長さ(scale)変換 ---*/ const double scale = ino::pixel_per_mm() * sqrt(fabs(affine.det())); /*--- margin計算...Twist時正確でない ---*/ return igs::radial_blur::reference_margin( static_cast(ceil(bBox.getLy())), - static_cast(ceil(bBox.getLx())), center.x, center.y, - this->m_twist->getValue(frame) * 3.14159265358979 / 180.0, - 0.0 // this->m_twist_radius->getValue(frame) * scale - , - this->m_blur->getValue(frame) / ino_param_range - - //,this->m_radius->getValue(frame) * scale - , - 0 /* debug:2012-02-01:ゼロ以上だとmarginが小さすぎになり画像が切れてしまう - */ - - , - (this->m_anti_alias->getValue() ? 4 : 1)); + static_cast(ceil(bBox.getLx())), center, + this->m_twist->getValue(frame) * M_PI_180, pivot_radius, + this->m_blur->getValue(frame) / ino_param_range, blur_radius, + this->m_type->getValue(), this->m_ellipse_aspect_ratio->getValue(frame), + this->m_ellipse_angle->getValue(frame), + this->m_intensity_correlation_with_ellipse->getValue(frame)); } void get_render_enlarge(const double frame, const TAffine affine, - TRectD &bBox) { + TRectD &bBox, double blur_radius, + double pivot_radius) { TPointD center(this->get_render_center(frame, bBox.getP00(), affine)); - int margin = this->get_render_int_margin(frame, bBox, affine, center); + int margin = this->get_render_int_margin(frame, bBox, affine, center, + blur_radius, pivot_radius); if (0 < margin) { /* 拡大のしすぎを防ぐテキトーな制限 */ if (4096 < margin) { @@ -114,14 +128,20 @@ public: bBox = TRectD(); return false; } - const bool ret = this->m_input->doGetBBox(frame, bBox, info); - this->get_render_enlarge(frame, info.m_affine, bBox); + const bool ret = this->m_input->doGetBBox(frame, bBox, info); + const double scale = ino::pixel_per_mm() * sqrt(fabs(info.m_affine.det())); + const double radius = this->m_radius->getValue(frame) * scale; + const double pivot_radius = info.m_cameraBox.getLy() / 2.0; + this->get_render_enlarge(frame, info.m_affine, bBox, radius, pivot_radius); return ret; } int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) override { TRectD bBox(rect); - this->get_render_enlarge(frame, info.m_affine, bBox); + const double scale = ino::pixel_per_mm() * sqrt(fabs(info.m_affine.det())); + const double radius = this->m_radius->getValue(frame) * scale; + const double pivot_radius = info.m_cameraBox.getLy() / 2.0; + this->get_render_enlarge(frame, info.m_affine, bBox, radius, pivot_radius); return TRasterFx::memorySize(bBox, info.m_bpp); } void transform(double frame, int port, const TRectD &rectOnOutput, @@ -129,7 +149,12 @@ public: TRenderSettings &infoOnInput) override { rectOnInput = rectOnOutput; infoOnInput = infoOnOutput; - this->get_render_enlarge(frame, infoOnOutput.m_affine, rectOnInput); + const double scale = + ino::pixel_per_mm() * sqrt(fabs(infoOnInput.m_affine.det())); + const double radius = this->m_radius->getValue(frame) * scale; + const double pivot_radius = infoOnInput.m_cameraBox.getLy() / 2.0; + this->get_render_enlarge(frame, infoOnOutput.m_affine, rectOnInput, radius, + pivot_radius); } bool canHandle(const TRenderSettings &info, double frame) override { // return false; // toonz has geometry control @@ -143,75 +168,69 @@ public: // add 20140130 void getParamUIs(TParamUIConcept *&concepts, int &length) override { - concepts = new TParamUIConcept[length = 3]; - - concepts[0].m_type = TParamUIConcept::POINT; - concepts[0].m_label = "Center"; + concepts = new TParamUIConcept[length = 2]; + concepts[0].m_type = TParamUIConcept::ELLIPSE; + concepts[0].m_label = "Radius"; + concepts[0].m_params.push_back(m_radius); concepts[0].m_params.push_back(m_center); + concepts[0].m_params.push_back(m_ellipse_aspect_ratio); + concepts[0].m_params.push_back(m_ellipse_angle); + concepts[0].m_params.push_back(m_twist); - concepts[1].m_type = TParamUIConcept::RADIUS; - concepts[1].m_label = "Radius"; - concepts[1].m_params.push_back(m_radius); + concepts[1].m_type = TParamUIConcept::COMPASS; concepts[1].m_params.push_back(m_center); - - concepts[2].m_type = TParamUIConcept::COMPASS; - concepts[2].m_params.push_back(m_center); + concepts[1].m_params.push_back(m_ellipse_aspect_ratio); + concepts[1].m_params.push_back(m_ellipse_angle); + concepts[1].m_params.push_back(m_twist); } // add 20140130 }; FX_PLUGIN_IDENTIFIER(ino_radial_blur, "inoRadialBlurFx"); //-------------------------------------------------------------------- namespace { -void fx_(const TRasterP in_ras // with margin - , - const int margin - - /* ここではinとrefは同サイズで使用している */ - , - const TRasterP refer_ras // no margin - , - const int refer_mode - - , - TRasterP out_ras // no margin +void fx_(const TRasterP in_ras, // with margin + const int margin, /* ここではinとrefは同サイズで使用している */ + const TRasterP refer_ras, // no margin + const int refer_mode, + TRasterP out_ras, // no margin + const TPointD center, const double twist, const double blur, + const double radius, const double pivot_radius, + const bool alpha_rendering_sw, const bool anti_alias_sw, + const int type, const double ellipse_aspect_ratio, + const double ellipse_angle, + const double intensity_correlation_with_ellipse) { + TRasterGR8P ref_gr8; + if ((refer_ras != nullptr) && (0 <= refer_mode)) { + ref_gr8 = TRasterGR8P(out_ras->getLy(), out_ras->getLx() * sizeof(float)); + ref_gr8->lock(); + ino::ras_to_ref_float_arr(refer_ras, + reinterpret_cast(ref_gr8->getRawData()), + refer_mode); + } - , - const double xp, const double yp, const double twist, - const double twist_radius, const double blur, const double radius, - const bool alpha_rendering_sw, const bool anti_alias_sw) { TRasterGR8P in_gr8(in_ras->getLy(), - in_ras->getLx() * ino::channels() * - ((TRaster64P)in_ras ? sizeof(unsigned short) - : sizeof(unsigned char))); + in_ras->getLx() * ino::channels() * sizeof(float)); in_gr8->lock(); + ino::ras_to_float_arr(in_ras, ino::channels(), + reinterpret_cast(in_gr8->getRawData())); - igs::radial_blur::convert( - in_ras->getRawData() // BGRA - , - 0 /* margin機能は使っていない、のでinとref画像は同サイズ */ - - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? refer_ras->getRawData() - : nullptr) // BGRA - , - (((refer_ras != nullptr) && (0 <= refer_mode)) ? ino::bits(refer_ras) - : 0), - refer_mode - - , - in_gr8->getRawData() // BGRA - - , - in_ras->getLy(), in_ras->getLx(), ino::channels(), ino::bits(out_ras) - - , - xp + margin, yp + margin, twist, twist_radius, blur, radius + TRasterGR8P out_buffer(out_ras->getLy(), + out_ras->getLx() * ino::channels() * sizeof(float)); + out_buffer->lock(); - , - (anti_alias_sw ? 4 : 1), alpha_rendering_sw); + igs::radial_blur::convert( + reinterpret_cast(in_gr8->getRawData()), + reinterpret_cast(out_buffer->getRawData()), margin, + out_ras->getSize(), ino::channels(), + (ref_gr8) ? reinterpret_cast(ref_gr8->getRawData()) : nullptr, + center + TPointD(margin, margin), twist, pivot_radius, blur, radius, type, + anti_alias_sw, alpha_rendering_sw, ellipse_aspect_ratio, ellipse_angle, + intensity_correlation_with_ellipse); - ino::arr_to_ras(in_gr8->getRawData(), ino::channels(), out_ras, margin); in_gr8->unlock(); + ino::float_arr_to_ras(out_buffer->getRawData(), ino::channels(), out_ras, 0); + out_buffer->unlock(); + if (ref_gr8) ref_gr8->unlock(); } } // namespace //------------------------------------------------------------ @@ -236,6 +255,13 @@ void ino_radial_blur::doCompute(TTile &tile, double frame, const bool alpha_rend_sw = this->m_alpha_rendering->getValue(); const bool anti_alias_sw = this->m_anti_alias->getValue(); const int refer_mode = this->m_ref_mode->getValue(); + const int type = this->m_type->getValue(); + const double ellipse_aspect_ratio = + this->m_ellipse_aspect_ratio->getValue(frame); + const double ellipse_angle = this->m_ellipse_angle->getValue(frame); + const double pivot_radius = ri.m_cameraBox.getLy() / 2.0; + const double intensity_correlation_with_ellipse = + this->m_intensity_correlation_with_ellipse->getValue(frame); TPointD center = this->m_center->getValue(frame); TPointD render_center( @@ -248,7 +274,7 @@ void ino_radial_blur::doCompute(TTile &tile, double frame, tile.getRaster()->getLx(), tile.getRaster()->getLy())); /*------ 入力画像のエリアの拡大 ----------------------------*/ int margin = this->get_render_int_margin(frame, tile_with_margin, ri.m_affine, - render_center); + render_center, radius, pivot_radius); if (0 < margin) { /* 拡大のしすぎを防ぐテキトーな制限 */ if (4096 < margin) { @@ -270,10 +296,8 @@ void ino_radial_blur::doCompute(TTile &tile, double frame, if (this->m_refer.isConnected()) { refer_sw = true; this->m_refer->allocateAndCompute( - refer_tile, tile_with_margin.getP00(), - TDimensionI(/* Pixel単位で四捨五入 */ - static_cast(tile_with_margin.getLx() + 0.5), - static_cast(tile_with_margin.getLy() + 0.5)), + refer_tile, tile.m_pos, + TDimension(tile.getRaster()->getLx(), tile.getRaster()->getLy()), tile.getRaster(), frame, ri); } /* ------ 保存すべき画像メモリを塗りつぶしクリア ---------- */ @@ -313,18 +337,10 @@ void ino_radial_blur::doCompute(TTile &tile, double frame, if (refer_tile.getRaster() != nullptr) { refer_tile.getRaster()->lock(); } - fx_(enlarge_tile.getRaster(), margin - - , - refer_tile.getRaster(), refer_mode - - , - tile.getRaster() - - , - render_center.x, render_center.y, twist, 0.0 // twist_radius - , - blur, radius, alpha_rend_sw, anti_alias_sw); + fx_(enlarge_tile.getRaster(), margin, refer_tile.getRaster(), refer_mode, + tile.getRaster(), render_center, twist, blur, radius, pivot_radius, + alpha_rend_sw, anti_alias_sw, type, ellipse_aspect_ratio, ellipse_angle, + intensity_correlation_with_ellipse); if (refer_tile.getRaster() != nullptr) { refer_tile.getRaster()->unlock(); } diff --git a/toonz/sources/tnztools/edittoolgadgets.cpp b/toonz/sources/tnztools/edittoolgadgets.cpp index b9b9fc1..d474f2c 100644 --- a/toonz/sources/tnztools/edittoolgadgets.cpp +++ b/toonz/sources/tnztools/edittoolgadgets.cpp @@ -19,6 +19,10 @@ #include "historytypes.h" #include "toonzqt/gutil.h" +#include "toonz/tscenehandle.h" +#include "toonz/toonzscene.h" +#include "toonz/tcamera.h" + #include #include #include @@ -111,6 +115,126 @@ void drawSpinField(const TRectD geom, const TPointD center, glPopMatrix(); } +#define RADIAL_FIELD_NUMSEGMENTS 5 +#define RADIAL_COMPASS_NUMSEGMENTS 20 + +void drawRadialField(const TRectD geom, const TPointD center, + const double lineInterval, const double e_aspect_ratio, + const double e_angle, const double twist, + const double pivot) { + // obtain the nearest and the furthest pos inside the geom + TPointD nearestPos; + nearestPos.x = (center.x <= geom.x0) + ? geom.x0 + : (center.x >= geom.x1) ? geom.x1 : center.x; + nearestPos.y = (center.y <= geom.y0) + ? geom.y0 + : (center.y >= geom.y1) ? geom.y1 : center.y; + double minDist = norm(nearestPos - center); + TPointD farthestPos; + farthestPos.x = (center.x <= geom.x0) + ? geom.x1 + : (center.x >= geom.x1) + ? geom.x0 + : ((center.x - geom.x0) >= (geom.x1 - center.x)) + ? geom.x0 + : geom.x1; + farthestPos.y = (center.y <= geom.y0) + ? geom.y1 + : (center.y >= geom.y1) + ? geom.y0 + : ((center.y - geom.y0) >= (geom.y1 - center.y)) + ? geom.y0 + : geom.y1; + double maxDist = norm(farthestPos - center); + double scale[2] = {1.0, 1.0}; + // adjust size for ellipse + if (e_aspect_ratio != 1.0) { + scale[0] = 2.0 * e_aspect_ratio / (e_aspect_ratio + 1); + scale[1] = scale[0] / e_aspect_ratio; + minDist *= std::min(scale[0], scale[1]); + maxDist *= std::max(scale[0], scale[1]); + } + // obtain id range + int minId = + (minDist == 0.) + ? 0 + : (int)std::floor(std::log2(M_PI * minDist / lineInterval)) + 1; + int maxId = (int)std::ceil(std::log2(M_PI * maxDist / lineInterval)) + 1; + + struct LineInfo { + double anglePos; // original direction of the line + int birthId; // generation where the line started to be drawn + }; + // register lines information + QList infoList; + // initial lines at minId + int initLineAmount = std::pow(2, minId); + for (int li = 0; li < initLineAmount; li++) + infoList.append({360.0 * (double)li / (double)initLineAmount, minId}); + for (int id = minId + 1; id <= maxId; id++) { + // insert between the existing lines + QList::iterator itr = infoList.end(); + while (itr != infoList.begin()) { + double ap; + if (itr == infoList.end()) + ap = (360.0 + (*(itr - 1)).anglePos) * 0.5; + else + ap = ((*itr).anglePos + (*(itr - 1)).anglePos) * 0.5; + itr = infoList.insert(itr, {ap, id}); + itr--; + } + } + + int unitDist = + std::pow(2, (maxId - 1)) - ((minId == 0) ? 0 : std::pow(2, (minId - 1))); + GLdouble *vertices = + new GLdouble[(unitDist * RADIAL_FIELD_NUMSEGMENTS + 1) * 2]; + + double radiStep = (lineInterval / M_PI) / (double)RADIAL_FIELD_NUMSEGMENTS; + double tmpRad = + (minId == 0) ? 0. : (std::pow(2, (minId - 1)) * lineInterval / M_PI); + for (int v = 0; v < unitDist * RADIAL_FIELD_NUMSEGMENTS + 1; v++) { + double tw = twist * tmpRad / pivot; + vertices[v * 2] = tmpRad * std::cos(tw); + vertices[v * 2 + 1] = tmpRad * std::sin(tw); + tmpRad += radiStep; + } + + glColor3d(0, 0, 1); + glEnableClientState(GL_VERTEX_ARRAY); + glLineStipple(1, 0x00FF); + glEnable(GL_LINE_STIPPLE); + + glVertexPointer(2, GL_DOUBLE, 0, vertices); + + glPushMatrix(); + + glTranslated(center.x, center.y, 0.0); + glRotated(e_angle, 0., 0., 1.); + glScaled(scale[0], scale[1], 1.); + int vertexIdOffset = + (minId == 0) ? 0 : std::pow(2, (minId - 1)) * RADIAL_FIELD_NUMSEGMENTS; + for (auto line : infoList) { + glPushMatrix(); + glRotated(line.anglePos, 0., 0., 1.); + int startId = (line.birthId == 0) ? 0 + : std::pow(2, (line.birthId - 1)) * + RADIAL_FIELD_NUMSEGMENTS - + vertexIdOffset; + // draw using vertex array + glDrawArrays(GL_LINE_STRIP, startId, + unitDist * RADIAL_FIELD_NUMSEGMENTS - startId); + glPopMatrix(); + } + + glDisable(GL_LINE_STIPPLE); + glDisableClientState(GL_VERTEX_ARRAY); + glPopMatrix(); + + delete[] vertices; +} + } // namespace //************************************************************************************* @@ -1579,6 +1703,7 @@ class CompassFxGadget final : public FxGadget { TPointParamP m_center; TDoubleParamP m_ellipse_aspect_ratio; TDoubleParamP m_ellipse_angle; + TDoubleParamP m_twist; enum HANDLE { Body = 0, Near, Far, None } m_handle = None; @@ -1591,7 +1716,8 @@ public: CompassFxGadget(FxGadgetController *controller, const TPointParamP ¢erPoint, bool isSpin = false, const TDoubleParamP &ellipse_aspect_ratio = TDoubleParamP(), - const TDoubleParamP &ellipse_angle = TDoubleParamP()); + const TDoubleParamP &ellipse_angle = TDoubleParamP(), + const TDoubleParamP &twist = TDoubleParamP()); void draw(bool picking) override; @@ -1605,12 +1731,14 @@ public: CompassFxGadget::CompassFxGadget(FxGadgetController *controller, const TPointParamP ¢erPoint, bool isSpin, const TDoubleParamP &ellipse_aspect_ratio, - const TDoubleParamP &ellipse_angle) + const TDoubleParamP &ellipse_angle, + const TDoubleParamP &twist) : FxGadget(controller, 3) , m_center(centerPoint) , m_isSpin(isSpin) , m_ellipse_aspect_ratio(ellipse_aspect_ratio) - , m_ellipse_angle(ellipse_angle) { + , m_ellipse_angle(ellipse_angle) + , m_twist(twist) { addParam(centerPoint->getX()); addParam(centerPoint->getY()); if (ellipse_aspect_ratio) addParam(ellipse_aspect_ratio); @@ -1660,7 +1788,10 @@ void CompassFxGadget::draw(bool picking) { double dCenter = norm(center); double e_aspect_ratio = (m_ellipse_aspect_ratio) ? getValue(m_ellipse_aspect_ratio) : 1.0; - double e_angle = (m_ellipse_angle) ? getValue(m_ellipse_angle) : 0.0; + double e_angle = (m_ellipse_angle) ? getValue(m_ellipse_angle) : 0.0; + TRectD cameraRect = m_controller->getCameraRect(); + double pivot = getPixelSize() * cameraRect.getLy() / 2.0; + double twist = (m_twist) ? getValue(m_twist) * M_PI_180 : 0.0; TPointD handleVec; if (dCenter > lineHalf) { @@ -1677,11 +1808,15 @@ void CompassFxGadget::draw(bool picking) { double theta = M_180_PI * lineInterval / dCenter; // draw spin lines field - if (isSelected() && !isSelected(None) && m_isSpin && - m_ellipse_aspect_ratio && m_ellipse_angle) { + if (isSelected() && !isSelected(None) && m_ellipse_aspect_ratio && + m_ellipse_angle) { TRectD geom = m_controller->getGeometry(); + if (m_isSpin) + drawSpinField(geom, center, lineInterval, e_aspect_ratio, e_angle); + else + drawRadialField(geom, center, lineInterval, e_aspect_ratio, e_angle, + twist, pivot); - drawSpinField(geom, center, lineInterval, e_aspect_ratio, e_angle); } else { // draw guides glColor3d(0, 0, 1); @@ -1690,14 +1825,72 @@ void CompassFxGadget::draw(bool picking) { glPushMatrix(); glTranslated(center.x, center.y, 0); if (!m_isSpin) { // radial direction - for (int i = -3; i <= 3; i++) { - if (i == 0) continue; + + if (areAlmostEqual(twist, 0.0)) { + for (int i = -3; i <= 3; i++) { + if (i == 0) continue; + + glPushMatrix(); + glRotated(theta * (double)i + angle, 0, 0, 1); + glBegin(GL_LINES); + glVertex2d(dCenter - lineHalf, 0.0); + glVertex2d(dCenter + lineHalf, 0.0); + glEnd(); + glPopMatrix(); + } + } else if (areAlmostEqual(e_aspect_ratio, 1.0)) { // twist case + for (int i = -3; i <= 3; i++) { + if (i == 0) continue; + + glPushMatrix(); + glRotated(theta * (double)i + angle, 0, 0, 1); + + glBegin(GL_LINE_STRIP); + for (int j = 0; j <= RADIAL_COMPASS_NUMSEGMENTS; j++) { + double tmp_d = dCenter - lineHalf + + (double)j * 2.0 * lineHalf / + (double)RADIAL_COMPASS_NUMSEGMENTS; + double tmp_tw_radian = twist * (tmp_d - dCenter) / pivot; + glVertex2d(tmp_d * std::cos(tmp_tw_radian), + tmp_d * std::sin(tmp_tw_radian)); + } + glEnd(); + glPopMatrix(); + } + } else { // elliptical + twist case + double scale[2]; + scale[0] = 2.0 * e_aspect_ratio / (e_aspect_ratio + 1); + scale[1] = scale[0] / e_aspect_ratio; glPushMatrix(); - glRotated(theta * (double)i + angle, 0, 0, 1); - glBegin(GL_LINES); - glVertex2d(dCenter - lineHalf, 0.0); - glVertex2d(dCenter + lineHalf, 0.0); - glEnd(); + glRotated(e_angle, 0., 0., 1.); + glScaled(scale[0], scale[1], 1.); + + QTransform tr = + QTransform().rotate(e_angle).scale(scale[0], scale[1]).inverted(); + + for (int i = -3; i <= 3; i++) { + if (i == 0) continue; + double tmp_angle_radian = (theta * (double)i + angle) * M_PI_180; + QPointF lineCenter(dCenter * std::cos(tmp_angle_radian), + dCenter * std::sin(tmp_angle_radian)); + lineCenter = tr.map(lineCenter); + + tmp_angle_radian = std::atan2(lineCenter.y(), lineCenter.x()); + double dLineCenter = QVector2D(lineCenter).length(); + double tmpLineHalf = lineHalf * dLineCenter / dCenter; + glBegin(GL_LINE_STRIP); + for (int j = 0; j <= RADIAL_COMPASS_NUMSEGMENTS; j++) { + double tmp_d = dLineCenter - tmpLineHalf + + (double)j * 2.0 * tmpLineHalf / + (double)RADIAL_COMPASS_NUMSEGMENTS; + double tmp_tw_radian = twist * (tmp_d - dLineCenter) / pivot; + + QPointF p(tmp_d * std::cos(tmp_angle_radian + tmp_tw_radian), + tmp_d * std::sin(tmp_angle_radian + tmp_tw_radian)); + glVertex2d(p.x(), p.y()); + } + glEnd(); + } glPopMatrix(); } } else { // rotational direction @@ -1713,7 +1906,7 @@ void CompassFxGadget::draw(bool picking) { } glEnd(); } - } else { + } else { // elliptical case double scale[2]; scale[0] = 2.0 * e_aspect_ratio / (e_aspect_ratio + 1); scale[1] = scale[0] / e_aspect_ratio; @@ -1907,26 +2100,33 @@ class EllipseFxGadget final : public FxGadget { TDoubleParamP m_xParam, m_yParam; TDoubleParamP m_aspect_ratio; TDoubleParamP m_angle; + TDoubleParamP m_twist; TPointD m_pos; - enum HANDLE { Radius = 0, Center, AngleAndAR, None } m_handle = None; + bool m_isSpin; + + enum HANDLE { Radius = 0, Center, AngleAndAR, Twist, None } m_handle = None; public: EllipseFxGadget(FxGadgetController *controller, const TDoubleParamP &radius, const TPointParamP ¢er, const TDoubleParamP &aspect_ratio, - const TDoubleParamP &angle) + const TDoubleParamP &angle, + const TDoubleParamP &twist = TDoubleParamP()) : FxGadget(controller, 4) , m_radius(radius) , m_xParam(center->getX()) , m_yParam(center->getY()) , m_aspect_ratio(aspect_ratio) - , m_angle(angle) { + , m_angle(angle) + , m_twist(twist) { addParam(radius); addParam(m_xParam); addParam(m_yParam); addParam(m_aspect_ratio); addParam(m_angle); + + m_isSpin = !m_twist; } TPointD getCenter() const; @@ -1962,12 +2162,20 @@ void EllipseFxGadget::draw(bool picking) { TPointD center = getCenter(); double aspect_ratio = getValue(m_aspect_ratio); double angle = getValue(m_angle); + TRectD cameraRect = m_controller->getCameraRect(); + double pivot = getPixelSize() * cameraRect.getLy() / 2.0; // draw spin lines field if (isSelected() && !isSelected(None)) { double lineInterval = getPixelSize() * 50; TRectD geom = m_controller->getGeometry(); - drawSpinField(geom, center, lineInterval, aspect_ratio, angle); + if (m_isSpin) + drawSpinField(geom, center, lineInterval, aspect_ratio, angle); + else { // radial case + double twist = getValue(m_twist) * M_PI_180; + drawRadialField(geom, center, lineInterval, aspect_ratio, angle, twist, + pivot); + } } double unit = getPixelSize(); @@ -2005,6 +2213,27 @@ void EllipseFxGadget::draw(bool picking) { drawTooltip(TPointD(namePos.x(), namePos.y()), getLabel()); } + //--- twist --- + if (m_twist) { + setColorById(Twist); + glPushName(idBase + Twist); + glPushMatrix(); + + glRotated(angle, 0., 0., 1.); + glScaled(scale[0], scale[1], 1.0); + + glLineStipple(1, 0x0F0F); + glEnable(GL_LINE_STIPPLE); + tglDrawCircle(TPointD(), pivot); + glDisable(GL_LINE_STIPPLE); + + glPopMatrix(); + glPopName(); + if (isSelected(Twist)) { + QPointF namePos = transform.map(QPointF(0.707, 0.707) * pivot); + drawTooltip(TPointD(namePos.x(), namePos.y()), "Twist"); + } + } //--- center --- setColorById(Center); glPushName(idBase + Center); @@ -2022,7 +2251,7 @@ void EllipseFxGadget::draw(bool picking) { glPopName(); if (isSelected(Center)) { - drawTooltip(TPointD(), "Center"); + drawTooltip(TPointD(7, 3) * unit, "Center"); } //---- AR and rotate @@ -2114,6 +2343,23 @@ void EllipseFxGadget::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { setValue(m_angle, new_angle); m_pos = pos; + } else if (m_handle == Twist) { + TPointD center = getCenter(); + TPointD old_v = m_pos - center; + TPointD new_v = pos - center; + if (old_v == TPointD() || new_v == TPointD()) return; + // angle + double twist = getValue(m_twist); + double d_twist = + std::atan2(new_v.y, new_v.x) - std::atan2(old_v.y, old_v.x); + double new_twist = twist + d_twist * M_180_PI; + if (new_twist < -180.0) + new_twist = -180.0; + else if (new_twist > 180.0) + new_twist = 180.0; + setValue(m_twist, new_twist); + + m_pos = pos; } } @@ -2308,8 +2554,13 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) { } case TParamUIConcept::COMPASS: { - assert(uiConcept.m_params.size() == 1); - gadget = new CompassFxGadget(this, uiConcept.m_params[0]); + assert(uiConcept.m_params.size() == 1 || uiConcept.m_params.size() == 4); + if (uiConcept.m_params.size() == 1) + gadget = new CompassFxGadget(this, uiConcept.m_params[0]); + else + gadget = new CompassFxGadget(this, uiConcept.m_params[0], false, + uiConcept.m_params[1], uiConcept.m_params[2], + uiConcept.m_params[3]); break; } @@ -2335,10 +2586,17 @@ FxGadget *FxGadgetController::allocateGadget(const TParamUIConcept &uiConcept) { } case TParamUIConcept::ELLIPSE: { - assert(uiConcept.m_params.size() == 4); - gadget = - new EllipseFxGadget(this, uiConcept.m_params[0], uiConcept.m_params[1], - uiConcept.m_params[2], uiConcept.m_params[3]); + assert(uiConcept.m_params.size() == 4 || uiConcept.m_params.size() == 5); + // radial blur has one more parameter (twist). + if (uiConcept.m_params.size() == 5) + gadget = new EllipseFxGadget( + this, uiConcept.m_params[0], uiConcept.m_params[1], + uiConcept.m_params[2], uiConcept.m_params[3], uiConcept.m_params[4]); + // spin blur case + else + gadget = new EllipseFxGadget(this, uiConcept.m_params[0], + uiConcept.m_params[1], uiConcept.m_params[2], + uiConcept.m_params[3]); break; } @@ -2444,3 +2702,11 @@ int FxGadgetController::getDevPixRatio() { TRectD FxGadgetController::getGeometry() { return (m_tool->getViewer()) ? m_tool->getViewer()->getGeometry() : TRectD(); } + +//--------------------------------------------------------------------------- + +TRectD FxGadgetController::getCameraRect() { + return (m_tool->getViewer()) ? m_tool->getViewer()->getCameraRect() + : TRectD(); +} + diff --git a/toonz/sources/tnztools/edittoolgadgets.h b/toonz/sources/tnztools/edittoolgadgets.h index a15dbc4..7abe9f5 100644 --- a/toonz/sources/tnztools/edittoolgadgets.h +++ b/toonz/sources/tnztools/edittoolgadgets.h @@ -156,6 +156,8 @@ public: // get the current viewer geometry TRectD getGeometry(); + TRectD getCameraRect(); + public slots: void onFxSwitched(); diff --git a/toonz/sources/toonz/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index 40fd9d6..641a8a9 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -3273,6 +3273,20 @@ TRectD SceneViewer::getGeometry() const { } //----------------------------------------------------------------------------- + +TRectD SceneViewer::getCameraRect() const { + TRectD cameraRect = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getCurrentCamera() + ->getStageRect(); + + // return m_drawCameraAff * TRectD(cameraRect.x0, cameraRect.y0, cameraRect.x1 + // - m_pixelSize, cameraRect.y1 - m_pixelSize); + return m_drawCameraAff * cameraRect; +} + +//----------------------------------------------------------------------------- /*! delete preview - subcamera executed from context menu */ void SceneViewer::doDeleteSubCamera() { diff --git a/toonz/sources/toonz/sceneviewer.h b/toonz/sources/toonz/sceneviewer.h index 71946e4..09ac470 100644 --- a/toonz/sources/toonz/sceneviewer.h +++ b/toonz/sources/toonz/sceneviewer.h @@ -278,6 +278,7 @@ public: // the viewer // when using the stylepicker and the finger tools TRectD getGeometry() const override; + TRectD getCameraRect() const override; void setFocus(Qt::FocusReason reason) { QWidget::setFocus(reason); };