47static size_t const N = 3;
49template<
typename InIt,
typename OutIt,
typename Size>
50inline void copy_n(InIt beg_in, Size
N, OutIt beg_out) {
51 std::copy(beg_in, beg_in+
N, beg_out);
60template<
typename T>
static inline T
sqr(T
const& v) {
return v*v; }
62template<
typename T>
static inline T
clip(T
const& v, T
const& a, T
const& b) {
63 if ( v < a )
return a;
64 if ( v > b )
return b;
68template<
typename Tt,
typename Ts>
70 static Ts
const rndoffset(.5);
71 return static_cast<Tt
>(v+rndoffset);
92template<
typename Tt,
typename Ts>
94 Ts
const minval = std::numeric_limits<Tt>::min();
95 Ts
const maxval = std::numeric_limits<Tt>::max();
96 Tt
const minval_rounded = std::numeric_limits<Tt>::min();
97 Tt
const maxval_rounded = std::numeric_limits<Tt>::max();
98 if ( v < minval )
return minval_rounded;
99 if ( v > maxval )
return maxval_rounded;
100 return round_cast<Tt>(v);
103template<
typename Tt,
typename Ts>
105 Ts
const minval = std::numeric_limits<Tt>::min();
106 Tt
const maxval = maxval_rounded;
107 Tt
const minval_rounded = std::numeric_limits<Tt>::min();
108 if ( v < minval )
return minval_rounded;
109 if ( v > maxval )
return maxval_rounded;
110 return round_cast<Tt>(v);
128 return (
int)std::ceil(std::fabs(deviation) * 3.0);
135 g_assert(scr_len >= 0);
136 double const d_sq =
sqr(deviation) * 2;
142 for (
int i = scr_len; i >= 0 ; i-- ) {
143 k[i] = std::exp(-
sqr(i) / d_sq);
144 if ( i > 0 )
sum += k[i];
152 for (
int i = scr_len; i >= 1 ; i-- ) {
154 kernel[i] = ksum-
static_cast<double>(kernelsum);
155 kernelsum += kernel[i];
157 kernel[0] =
FIRValue(1)-2*kernelsum;
182 stepsize_l2 =
clip(
static_cast<int>(
log(deviation*(3./2.))/
log(2.)), 0, 12);
188 stepsize_l2 =
clip(
static_cast<int>(
log(deviation*(3./4.))/
log(2.)), 0, 12);
194 stepsize_l2 =
clip(
static_cast<int>(
log(deviation*(3./16.))/
log(2.)), 0, 12);
204 stepsize_l2 =
clip(
static_cast<int>(
log(deviation*(3./8.))/
log(2.)), 0, 12);
212 std::complex<double>
const d1_org(1.40098, 1.00236);
213 double const d3_org = 1.85132;
215 double qend = 2*sigma;
216 double const sigmasqr =
sqr(sigma);
218 double const q = (qbeg+qend)/2;
220 std::complex<double>
const d1 = pow(d1_org, 1.0/q);
221 double const d3 = pow(d3_org, 1.0/q);
223 double const ssqr = 2*(2*(d1/
sqr(d1-1.)).
real()+d3/
sqr(d3-1.));
224 if ( ssqr < sigmasqr ) {
229 }
while(qend-qbeg>(sigma/(1<<30)));
231 double const q = (qbeg+qend)/2;
232 std::complex<double>
const d1 = pow(d1_org, 1.0/q);
233 double const d3 = pow(d3_org, 1.0/q);
234 double const absd1sqr = std::norm(d1);
235 double const re2d1 = 2*d1.real();
236 double const bscale = 1.0/(absd1sqr*d3);
238 b[1] = bscale*(d3+re2d1);
239 b[0] = -bscale*(absd1sqr+d3*re2d1);
244 double a1=b[0], a2=b[1], a3=b[2];
245 double const Mscale = 1.0/((1+a1-a2+a3)*(1-a1-a2-a3)*(1+a2+(a1-a3)*a3));
246 M[0] = 1-a2-a1*a3-
sqr(a3);
247 M[1] = (a1+a3)*(a2+a1*a3);
248 M[2] = a3*(a1+a2*a3);
250 M[4] = (1-a2)*(a2+a1*a3);
251 M[5] = a3*(1-a2-a1*a3-
sqr(a3));
252 M[6] = a1*(a1+a3)+a2*(1-a2);
253 M[7] = a1*(a2-
sqr(a3))+a3*(1+a2*(a2-1)-
sqr(a3));
254 M[8] = a3*(a1+a2*a3);
255 for(
unsigned int i=0; i<9; i++) M[i] *= Mscale;
258template<
unsigned int SIZE>
260 for(
unsigned int c=0;
c<
SIZE;
c++) {
262 for(
unsigned int i=0; i<
N; i++) uminp[i] = uold[i][
c] - uplus[
c];
263 for(
unsigned int i=0; i<
N; i++) {
265 for(
unsigned int j=0; j<
N; j++) {
266 voldf += uminp[j]*M[i*
N+j];
271 vold[i][
c] = voldf*alpha;
272 vold[i][
c] += vplus[
c];
278template<
typename PT,
unsigned int PC,
bool PREMULTIPLIED_ALPHA>
281 PT
const *
const src,
int const sstr1,
int const sstr2,
282 int const n1,
int const n2,
IIRValue const b[
N+1],
double const M[
N*
N],
287#if G_BYTE_ORDER == G_LITTLE_ENDIAN
288 static unsigned int const alpha_PC = PC-1;
289 #define PREMUL_ALPHA_LOOP for(unsigned int c=0; c<PC-1; ++c)
291 static unsigned int const alpha_PC = 0;
292 #define PREMUL_ALPHA_LOOP for(unsigned int c=1; c<PC; ++c)
297 PT
const * srcimg = src + c2*sstr2;
298 PT * dstimg = dest + c2*dstr2 +
n1*dstr1;
304 for(
unsigned int i=0; i<
N; i++)
copy_n(imin, PC, u[i]);
305 for (
int c1 = 0 ; c1 <
n1 ; c1++ ) {
306 for(
unsigned int i=
N; i>0; i--)
copy_n(u[i-1], PC, u[i]);
309 for(
unsigned int c=0;
c<PC;
c++) u[0][
c] *= b[0];
310 for(
unsigned int i=1; i<
N+1; i++) {
311 for(
unsigned int c=0;
c<PC;
c++) u[0][
c] += u[i][
c]*b[i];
313 copy_n(u[0], PC, tmpdata[tid]+c1*PC);
317 calcTriggsSdikaInitialization<PC>(M, u, iplus, iplus, b[0], v);
319 if ( PREMULTIPLIED_ALPHA ) {
320 dstimg[alpha_PC] = clip_round_cast<PT>(v[0][alpha_PC]);
321 PREMUL_ALPHA_LOOP dstimg[
c] = clip_round_cast_varmax<PT>(v[0][
c], dstimg[alpha_PC]);
323 for(
unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][
c]);
327 for(
unsigned int i=
N; i>0; i--)
copy_n(v[i-1], PC, v[i]);
328 copy_n(tmpdata[tid]+c1*PC, PC, v[0]);
329 for(
unsigned int c=0;
c<PC;
c++) v[0][
c] *= b[0];
330 for(
unsigned int i=1; i<
N+1; i++) {
331 for(
unsigned int c=0;
c<PC;
c++) v[0][
c] += v[i][
c]*b[i];
334 if ( PREMULTIPLIED_ALPHA ) {
335 dstimg[alpha_PC] = clip_round_cast<PT>(v[0][alpha_PC]);
336 PREMUL_ALPHA_LOOP dstimg[
c] = clip_round_cast_varmax<PT>(v[0][
c], dstimg[alpha_PC]);
338 for(
unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][
c]);
347template<
typename PT,
unsigned int PC>
350 PT
const *
const src,
int const sstr1,
int const sstr2,
357 PT history[scr_len + 1][PC];
360 int const src_line = c2 * sstr2;
363 int const dst_line = c2 * dstr2;
365 int skipbuf[4] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN};
368 PT imin[PC];
copy_n(src + src_line, PC, imin);
369 for(
int i=0; i<scr_len; i++)
copy_n(imin, PC, history[i]);
371 for (
int c1 = 0 ; c1 <
n1 ; c1++ ) {
373 int const src_disp = src_line + c1 * sstr1;
374 int const dst_disp = dst_line + c1 * dstr1;
377 for(
int i=scr_len; i>0; i--)
copy_n(history[i-1], PC, history[i]);
378 copy_n(src + src_disp, PC, history[0]);
381 for (
unsigned int byte = 0 ;
byte < PC ;
byte++) {
383 if(skipbuf[
byte] > c1)
continue;
387 int different_count = 0;
390 for (
int i = 0 ; i <= scr_len ; i++ ) {
392 PT in_byte = history[i][byte];
395 if(in_byte != last_in) different_count++;
399 sum += in_byte * kernel[i];
403 int nb_src_disp = src_disp + byte;
404 for (
int i = 1 ; i <= scr_len ; i++ ) {
410 nb_src_disp += sstr1;
414 PT in_byte = src[nb_src_disp];
417 if(in_byte != last_in) different_count++;
421 sum += in_byte * kernel[i];
425 dst[dst_disp + byte] = round_cast<PT>(
sum);
430 if (different_count <= 1) {
432 int nb_src_disp = src_disp + (1+scr_len)*sstr1 +
byte;
433 int nb_dst_disp = dst_disp + (1) *dstr1 +
byte;
434 while(pos + scr_len <
n1 && src[nb_src_disp] == last_in) {
435 dst[nb_dst_disp] = last_in;
437 nb_src_disp += sstr1;
438 nb_dst_disp += dstr1;
458 for(
double & i : bf) i = -i;
460 for(
size_t i=0; i<
N; i++) {
468 int stride = cairo_image_surface_get_stride(src);
469 int w = cairo_image_surface_get_width(src);
470 int h = cairo_image_surface_get_height(src);
474 switch (cairo_image_surface_get_format(src)) {
475 case CAIRO_FORMAT_A8:
476 filter2D_IIR<unsigned char,1,false>(
479 w, h, b, M, tmpdata, pool);
481 case CAIRO_FORMAT_ARGB32:
482 filter2D_IIR<unsigned char,4,true>(
485 w, h, b, M, tmpdata, pool);
488 g_warning(
"gaussian_pass_IIR: unsupported image format");
498 std::vector<FIRValue> kernel(scr_len + 1);
501 int stride = cairo_image_surface_get_stride(src);
502 int w = cairo_image_surface_get_width(src);
503 int h = cairo_image_surface_get_height(src);
507 switch (cairo_image_surface_get_format(src)) {
508 case CAIRO_FORMAT_A8:
509 filter2D_FIR<unsigned char,1>(
512 w, h, &kernel[0], scr_len, pool);
514 case CAIRO_FORMAT_ARGB32:
515 filter2D_FIR<unsigned char,4>(
518 w, h, &kernel[0], scr_len, pool);
521 g_warning(
"gaussian_pass_FIR: unsupported image format");
541 cairo_surface_destroy(cp);
551 dx *= (*bbox).width();
552 dy *= (*bbox).height();
558 double deviation_x_orig = dx * trans.
expansionX();
559 double deviation_y_orig = dy * trans.
expansionY();
563 deviation_x_orig *= device_scale;
564 deviation_y_orig *= device_scale;
566 cairo_format_t
fmt = cairo_image_surface_get_format(in);
567 int bytes_per_pixel = 0;
569 case CAIRO_FORMAT_A8:
570 bytes_per_pixel = 1;
break;
571 case CAIRO_FORMAT_ARGB32:
573 bytes_per_pixel = 4;
break;
578 int threads = pool->size();
581 bool resampling = x_step > 1 || y_step > 1;
584 int w_downsampled = resampling ?
static_cast<int>(ceil(
static_cast<double>(w_orig)/x_step))+1 : w_orig;
585 int h_downsampled = resampling ?
static_cast<int>(ceil(
static_cast<double>(h_orig)/y_step))+1 : h_orig;
586 double deviation_x = deviation_x_orig / x_step;
587 double deviation_y = deviation_y_orig / y_step;
596 bool use_IIR_x = deviation_x > 3;
597 bool use_IIR_y = deviation_y > 3;
602 std::fill_n(tmpdata, threads, (
IIRValue*)0);
603 if ( use_IIR_x || use_IIR_y ) {
604 for(
int i = 0; i < threads; ++i) {
605 tmpdata[i] =
new IIRValue[std::max(w_downsampled,h_downsampled)*bytes_per_pixel];
613 downsampled = cairo_surface_create_similar(in, cairo_surface_get_content(in),
614 w_downsampled/device_scale, h_downsampled/device_scale);
615 cairo_t *ct = cairo_create(downsampled);
616 cairo_scale(ct,
static_cast<double>(w_downsampled)/w_orig,
static_cast<double>(h_downsampled)/h_orig);
617 cairo_set_source_surface(ct, in, 0, 0);
623 cairo_surface_flush(downsampled);
642 if ( use_IIR_x || use_IIR_y ) {
643 for(
int i = 0; i < threads; ++i) {
648 cairo_surface_mark_dirty(downsampled);
650 cairo_surface_t *upsampled = cairo_surface_create_similar(downsampled, cairo_surface_get_content(downsampled),
651 w_orig / device_scale, h_orig / device_scale);
652 cairo_t *ct = cairo_create(upsampled);
653 cairo_scale(ct,
static_cast<double>(w_orig) / w_downsampled,
static_cast<double>(h_orig) / h_downsampled);
654 cairo_set_source_surface(ct, downsampled, 0, 0);
661 cairo_surface_destroy(upsampled);
662 cairo_surface_destroy(downsampled);
667 cairo_surface_destroy(downsampled);
677 int area_max = std::max(area_x, area_y);
694 return 2.0 * area_x * area_y;
699 if (std::isfinite(deviation) && deviation >= 0) {
706 if (std::isfinite(x) && x >= 0 && std::isfinite(y) && y >= 0) {
3x3 affine transformation matrix.
cairo_surface_t * ink_cairo_surface_copy(cairo_surface_t *s)
Create an exact copy of a surface.
int ink_cairo_surface_get_width(cairo_surface_t *surface)
Return width in pixels.
int ink_cairo_surface_get_height(cairo_surface_t *surface)
Return height in pixels.
void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci)
Set the color_interpolation_value for a Cairo surface.
Cairo integration helpers.
static int constexpr SIZE
3x3 matrix representing an affine transformation.
Coord expansionX() const
Calculates the amount of x-scaling imparted by the Affine.
Coord expansionY() const
Calculates the amount of y-scaling imparted by the Affine.
Axis aligned, non-empty, generic rectangle.
void expandBy(C amount)
Expand the rectangle in both directions by the specified amount.
Axis-aligned rectangle that can be empty.
void set_deviation(double deviation)
Set the standard deviation value for gaussian blur.
void render_cairo(FilterSlot &slot) const override
bool can_handle_affine(Geom::Affine const &m) const override
Indicate whether the filter primitive can handle the given affine.
~FilterGaussian() override
void area_enlarge(Geom::IntRect &area, Geom::Affine const &m) const override
double complexity(Geom::Affine const &ctm) const override
SPColorInterpolation color_interpolation
cairo_surface_t * getcairo(int slot)
Returns the pixblock in specified slot.
void set(int slot, cairo_surface_t *s)
Sets or re-sets the pixblock associated with given slot.
int get_device_scale() const
Gets the device scale; for high DPI monitors.
FilterUnits const & get_units() const
int get_blurquality() const
Gets the gaussian filtering quality.
SPFilterUnits get_primitive_units() const
Gets Primitive Units (userSpaceOnUse or objectBoundingBox)
Geom::OptRect get_item_bbox() const
Gets the item bounding box in user coordinates.
Geom::Affine get_matrix_user2pb() const
Gets the user coordinates to pixblock coordinates transformation matrix.
General-purpose, parallel thread dispatch mechanism.
void dispatch(int count, dispatch_func function)
struct _cairo_surface cairo_surface_t
Dim2
2D axis enumeration (X or Y).
static void gaussian_pass_IIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest, IIRValue **tmpdata, dispatch_pool &pool)
static void filter2D_FIR(PT *const dst, int const dstr1, int const dstr2, PT const *const src, int const sstr1, int const sstr2, int const n1, int const n2, FIRValue const *const kernel, int const scr_len, dispatch_pool &pool)
static void calcFilter(double const sigma, double b[N])
static void gaussian_pass_FIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest, dispatch_pool &pool)
static int _effect_subsample_step_log2(double const deviation, int const quality)
static void calcTriggsSdikaM(double const b[N], double M[N *N])
static void filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, PT const *const src, int const sstr1, int const sstr2, int const n1, int const n2, IIRValue const b[N+1], double const M[N *N], IIRValue *const tmpdata[], dispatch_pool &pool)
static int _effect_area_scr(double const deviation)
static void calcTriggsSdikaInitialization(double const M[N *N], IIRValue const uold[N][SIZE], IIRValue const uplus[SIZE], IIRValue const vplus[SIZE], IIRValue const alpha, IIRValue vold[N][SIZE])
static void _make_kernel(FIRValue *const kernel, double const deviation)
Helper class to stream background task notifications as a series of messages.
std::shared_ptr< dispatch_pool > get_global_dispatch_pool()
static Tt clip_round_cast_varmax(Ts const v, Tt const maxval_rounded)
void copy_n(InIt beg_in, Size N, OutIt beg_out)
Inkscape::Util::FixedPoint< unsigned int, 16 > FIRValue
static T clip(T const &v, T const &a, T const &b)
static Tt clip_round_cast(Ts const v)
static Tt round_cast(Ts v)
Piecewise< SBasis > log(Interval in)
@ SP_FILTER_UNITS_OBJECTBOUNDINGBOX
double sum(const double alpha[16], const double &x, const double &y)
G_BEGIN_DECLS typedef double real