Submitted by Søren Sandmann on April 12, 2016, 2:36 a.m.

Message ID | 1460428613-25813-8-git-send-email-soren.sandmann@gmail.com |
---|---|

State | Under Review |

Series | "Series without cover letter" |

Headers | show |

diff --git a/configure.ac b/configure.ac index 6b2134e..e833e45 100644 --- a/configure.ac +++ b/configure.ac @@ -834,6 +834,19 @@ fi AC_SUBST(PIXMAN_TIMERS) dnl =================================== +dnl gnuplot + +AC_ARG_ENABLE(gnuplot, + [AC_HELP_STRING([--enable-gnuplot], + [enable output of filters that can be piped to gnuplot [default=no]])], + [enable_gnuplot=$enableval], [enable_gnuplot=no]) + +if test $enable_gnuplot = yes ; then + AC_DEFINE(PIXMAN_GNUPLOT, 1, [enable output that can be piped to gnuplot]) +fi +AC_SUBST(PIXMAN_GNUPLOT) + +dnl =================================== dnl GTK+ AC_ARG_ENABLE(gtk, diff --git a/pixman/pixman-filter.c b/pixman/pixman-filter.c index b2bf53f..af46a43 100644 --- a/pixman/pixman-filter.c +++ b/pixman/pixman-filter.c @@ -297,6 +297,117 @@ create_1d_filter (int *width, return params; } +#ifdef PIXMAN_GNUPLOT + +/* If enable-gnuplot is configured, then you can pipe the output of a + * pixman-using program to gnuplot and get a continuously-updated plot + * of the horizontal filter. This works well with demos/scale to test + * the filter generation. + * + * The plot is all the different subposition filters shuffled + * together. This is misleading in a few cases: + * + * IMPULSE.BOX - goes up and down as the subfilters have different + * numbers of non-zero samples + * IMPULSE.TRIANGLE - somewhat crooked for the same reason + * 1-wide filters - looks triangular, but a 1-wide box would be more + * accurate + */ +static void +gnuplot_filter (int width, int n_phases, const pixman_fixed_t* p) +{ + double step; + int i, j; + int first; + + step = 1.0 / n_phases; + + printf ("set style line 1 lc rgb '#0060ad' lt 1 lw 0.5 pt 7 pi 1 ps 0.5\n"); + printf ("plot '-' with linespoints ls 1\n"); + + /* The position of the first sample of the phase corresponding to + * frac is given by: + * + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + * + * We have to find the frac that minimizes this expression. + * + * For odd widths, we have + * + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + * = ceil (frac) + K - frac + * = 1 + K - frac + * + * for some K, so this is minimized when frac is maximized and + * strictly growing with frac. So for odd widths, we can simply + * start at the last phase and go backwards. + * + * For even widths, we have + * + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + * = ceil (frac - 0.5) + K - frac + * + * The graph for this function (ignoring K) looks like this: + * + * 0.5 + * | |\ + * | | \ + * | | \ + * 0 | | \ + * |\ | + * | \ | + * | \ | + * -0.5 | \| + * --------------------------------- + * 0 0.5 1 + * + * So in this case we need to start with the phase whose frac is + * less than, but as close as possible to 0.5, then go backwards + * until we hit the first phase, then wrap around to the last + * phase and continue backwards. + * + * Which phase is as close as possible 0.5? The locations of the + * sampling point corresponding to the kth phase is given by + * 1/(2 * n_phases) + k / n_phases: + * + * 1/(2 * n_phases) + k / n_phases = 0.5 + * + * from which it follows that + * + * k = (n_phases - 1) / 2 + * + * rounded down is the phase in question. + */ + if (width & 1) + first = n_phases - 1; + else + first = (n_phases - 1) / 2; + + for (j = 0; j < width; ++j) + { + for (i = 0; i < n_phases; ++i) + { + int phase = first - i; + double frac, pos; + + if (phase < 0) + phase = n_phases + phase; + + frac = step / 2.0 + phase * step; + pos = ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + j; + + printf ("%g %g\n", + pos, + pixman_fixed_to_double (*(p + phase * width + j))); + } + } + + printf ("e\n"); + fflush (stdout); +} + +#endif + /* Create the parameter list for a SEPARABLE_CONVOLUTION filter * with the given kernels and scale parameters */ @@ -346,5 +457,9 @@ out: free (horz); free (vert); +#ifdef PIXMAN_GNUPLOT + gnuplot_filter(width, subsample_x, params + 4); +#endif + return params; } diff --git a/pixman/rounding.txt b/pixman/rounding.txt index b52b084..1c00019 100644 --- a/pixman/rounding.txt +++ b/pixman/rounding.txt @@ -160,6 +160,7 @@ which means the contents of the matrix corresponding to (frac) should contain width samplings of the function, with the first sample at: floor (frac - (width - 1) / 2.0 - e) + 0.5 - frac + = ceil (frac - width / 2.0 - 0.5) + 0.5 - frac This filter is called separable because each of the k x k convolution matrices is specified with two k-wide vectors, one for each dimension,

On Mon, Apr 11, 2016 at 7:36 PM, Søren Sandmann Pedersen < soren.sandmann@gmail.com> wrote: > From: Bill Spitzak <spitzak@gmail.com> > > If enable-gnuplot is configured, then you can pipe the output of a > pixman-using program to gnuplot and get a continuously-updated plot of > the horizontal filter. This works well with demos/scale to test the > filter generation. > > The plot is all the different subposition filters shuffled > together. This is misleading in a few cases: > > IMPULSE.BOX - goes up and down as the subfilters have different > numbers of non-zero samples > > IMPULSE.TRIANGLE - somewhat crooked for the same reason > > 1-wide filters - looks triangular, but a 1-wide box would be more > accurate > Because you are not plotting the two dummy points at (0,±width/2), a 1-wide filter is actually just a single point. You may be right that leaving the dummy points off the plot may make it easier to figure out what is going on. > Changes by Søren: Rewrote the pixman-filter.c part to > - make it generate correct coordinates > - add a comment on how coordinates are generated > - in rounding.txt, add a ceil() variant of the first-sample > formula > - make the gnuplot output slightly prettier > > v7: First time this ability was included > > v8: Use config option > Moved code to the filter generator > Modified scale demo to not call filter generator a second time. > > v10: Only print if successful generation of plots > Use #ifdef, not #if > > v11: small whitespace fixes > > Signed-off-by: Bill Spitzak <spitzak@gmail.com> > Signed-off-by: Søren Sandmann <soren.sandmann@gmail.com> > --- > configure.ac | 13 ++++++ > pixman/pixman-filter.c | 115 > +++++++++++++++++++++++++++++++++++++++++++++++++ > pixman/rounding.txt | 1 + > 3 files changed, 129 insertions(+) > > diff --git a/configure.ac b/configure.ac > index 6b2134e..e833e45 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -834,6 +834,19 @@ fi > AC_SUBST(PIXMAN_TIMERS) > > dnl =================================== > +dnl gnuplot > + > +AC_ARG_ENABLE(gnuplot, > + [AC_HELP_STRING([--enable-gnuplot], > + [enable output of filters that can be piped to gnuplot > [default=no]])], > + [enable_gnuplot=$enableval], [enable_gnuplot=no]) > + > +if test $enable_gnuplot = yes ; then > + AC_DEFINE(PIXMAN_GNUPLOT, 1, [enable output that can be piped to > gnuplot]) > +fi > +AC_SUBST(PIXMAN_GNUPLOT) > + > +dnl =================================== > dnl GTK+ > > AC_ARG_ENABLE(gtk, > diff --git a/pixman/pixman-filter.c b/pixman/pixman-filter.c > index b2bf53f..af46a43 100644 > --- a/pixman/pixman-filter.c > +++ b/pixman/pixman-filter.c > @@ -297,6 +297,117 @@ create_1d_filter (int *width, > return params; > } > > +#ifdef PIXMAN_GNUPLOT > + > +/* If enable-gnuplot is configured, then you can pipe the output of a > + * pixman-using program to gnuplot and get a continuously-updated plot > + * of the horizontal filter. This works well with demos/scale to test > + * the filter generation. > + * > + * The plot is all the different subposition filters shuffled > + * together. This is misleading in a few cases: > + * > + * IMPULSE.BOX - goes up and down as the subfilters have different > + * numbers of non-zero samples > + * IMPULSE.TRIANGLE - somewhat crooked for the same reason > + * 1-wide filters - looks triangular, but a 1-wide box would be more > + * accurate > + */ > +static void > +gnuplot_filter (int width, int n_phases, const pixman_fixed_t* p) > +{ > + double step; > + int i, j; > + int first; > + > + step = 1.0 / n_phases; > + > + printf ("set style line 1 lc rgb '#0060ad' lt 1 lw 0.5 pt 7 pi 1 ps > 0.5\n"); > + printf ("plot '-' with linespoints ls 1\n"); > + > + /* The position of the first sample of the phase corresponding to > + * frac is given by: > + * > + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac > + * > + * We have to find the frac that minimizes this expression. > + * > + * For odd widths, we have > + * > + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac > + * = ceil (frac) + K - frac > + * = 1 + K - frac > + * > + * for some K, so this is minimized when frac is maximized and > + * strictly growing with frac. So for odd widths, we can simply > + * start at the last phase and go backwards. > + * > + * For even widths, we have > + * > + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac > + * = ceil (frac - 0.5) + K - frac > + * > + * The graph for this function (ignoring K) looks like this: > + * > + * 0.5 > + * | |\ > + * | | \ > + * | | \ > + * 0 | | \ > + * |\ | > + * | \ | > + * | \ | > + * -0.5 | \| > + * --------------------------------- > + * 0 0.5 1 > + * > + * So in this case we need to start with the phase whose frac is > + * less than, but as close as possible to 0.5, then go backwards > + * until we hit the first phase, then wrap around to the last > + * phase and continue backwards. > + * > + * Which phase is as close as possible 0.5? The locations of the > + * sampling point corresponding to the kth phase is given by > + * 1/(2 * n_phases) + k / n_phases: > + * > + * 1/(2 * n_phases) + k / n_phases = 0.5 > + * > + * from which it follows that > + * > + * k = (n_phases - 1) / 2 > + * > + * rounded down is the phase in question. > + */ > + if (width & 1) > + first = n_phases - 1; > + else > + first = (n_phases - 1) / 2; > My version printed an extra point at 0,-width/2 here. Not sure if that is an improvement or a bug, you might want to try it just to get your opinion. > + > + for (j = 0; j < width; ++j) > + { > + for (i = 0; i < n_phases; ++i) > + { > + int phase = first - i; > + double frac, pos; > + > + if (phase < 0) > + phase = n_phases + phase; > + > + frac = step / 2.0 + phase * step; > + pos = ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + j; > See below I think this has to be changed to the floor() version. > + > + printf ("%g %g\n", > + pos, > + pixman_fixed_to_double (*(p + phase * width + j))); > + } > + } > My version printed an extra point at 0,width/2 here. > + > + printf ("e\n"); > + fflush (stdout); > +} > + > +#endif > + > /* Create the parameter list for a SEPARABLE_CONVOLUTION filter > * with the given kernels and scale parameters > */ > @@ -346,5 +457,9 @@ out: > free (horz); > free (vert); > > +#ifdef PIXMAN_GNUPLOT > + gnuplot_filter(width, subsample_x, params + 4); > +#endif > + > return params; > } > diff --git a/pixman/rounding.txt b/pixman/rounding.txt > index b52b084..1c00019 100644 > --- a/pixman/rounding.txt > +++ b/pixman/rounding.txt > @@ -160,6 +160,7 @@ which means the contents of the matrix corresponding > to (frac) should > contain width samplings of the function, with the first sample at: > > floor (frac - (width - 1) / 2.0 - e) + 0.5 - frac > + = ceil (frac - width / 2.0 - 0.5) + 0.5 - frac > Unfortunately this produces numbers that don't agree with the filter generator or filtering code. For a width==4 filter with n_phases==1, the generator seems to produce values at -1, 0, 1, 2, so the first sample is at -1. It also appears the filtering sampler is using the same rule, otherwise these filters would produce an obvious shift in the image. frac = step/2 = .5 width = 3 floor (frac - (width - 1) / 2.0) + 0.5 - frac = floor(.5 - 1.5) + 0.5-0.5 = floor(-1) = -1. ceil (frac - width / 2.0 - 0.5) + 0.5 - frac = ceil(.5 - 2.5) + 0.5-0.5 = ceil(-2) = -2 Whether by accident or not it looks like the correct formula is the floor one with the epsilon (e) removed, in disagreement with your rounding.txt document. I would recommend using that rather than changing the interpretation of the filter matrix.