Re: [Pixman] [PATCH v10 15/15] pixman-image: Implement PIXMAN_FILTER_GOOD/BEST as separable convolutions

2016-02-04 Thread Bill Spitzak

On 02/04/2016 05:42 AM, Oded Gabbay wrote:

On Tue, Feb 2, 2016 at 8:28 AM,   wrote:

From: Bill Spitzak 

Detects and uses PIXMAN_FILTER_NEAREST for all 8 90-degree rotations and
reflections when the scale is 1.0 and integer translation.

GOOD uses:

  scale < 1/16 : BOX.BOX at size 16
  scale < 3/4 : BOX.BOX at size 1/scale
  larger : BOX.BOX at size 1

  If both directions have a scale >= 3/4 or a scale of 1/2 and an integer
  translation, the faster PIXMAN_FILTER_BILINEAR code is used. This is
  compatable at these scales with older versions of pixman where bilinear
  was always used for GOOD.

BEST uses:

  scale < 1/24 : BOX.BOX at size 24
  scale < 1/16 : BOX.BOX at size 1/scale
  scale < 1 : IMPULSE.LANCZOS2 at size 1/scale
  scale < 2.333 : IMPULSE.LANCZOS2 at size 1
  scale < 128 : BOX.LANCZOS2 at size 1/(scale-1) (antialiased square pixels)
  larger : BOX.LANCZOS2 at size 1/127 (antialias blur gets thicker)

v8: Cutoff in BEST between IMPULSE.LANCZOS2 and BOX.LANCZOS2 adjusted for
 a better match between the filters.

v9: Use the new negative subsample controls to scale the subsamples. These
 were chosen by finding the lowest number that did not add visible
 artifacts to the zone plate image.

 Scale demo altered to default to GOOD and locked-together x+y scale


Let's separate pixman core changes and demo changes. It's bad for
bisectability and maintainability.

Do the core changes first, then another patch for the scale demo.



 Fixed divide-by-zero from all-zero matrix found by stress-test

Signed-off-by: Bill Spitzak 
---
  demos/scale.c |  10 +-
  demos/scale.ui|   1 +
  pixman/pixman-image.c | 289 --
  3 files changed, 216 insertions(+), 84 deletions(-)

diff --git a/demos/scale.c b/demos/scale.c
index 881004e..3df4442 100644
--- a/demos/scale.c
+++ b/demos/scale.c
@@ -340,7 +340,7 @@ on_expose (GtkWidget *da, GdkEvent *event, gpointer data)

  static void
  set_up_combo_box (app_t *app, const char *box_name,
-  int n_entries, const named_int_t table[])
+  int n_entries, const named_int_t table[], int active)
  {
  GtkWidget *widget = get_widget (app, box_name);
  GtkListStore *model;
@@ -366,7 +366,7 @@ set_up_combo_box (app_t *app, const char *box_name,
 gtk_list_store_set (model, , 0, info->name, -1);
  }

-gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active);

  g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
  }
@@ -374,7 +374,7 @@ set_up_combo_box (app_t *app, const char *box_name,
  static void
  set_up_filter_box (app_t *app, const char *box_name)
  {
-set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters);
+set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters, 0);
  }

  static char *
@@ -422,14 +422,14 @@ app_new (pixman_image_t *original)
  widget = get_widget (app, "drawing_area");
  g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app);

-set_up_combo_box (app, "filter_combo_box", G_N_ELEMENTS (filter_types), 
filter_types);
+set_up_combo_box (app, "filter_combo_box", G_N_ELEMENTS (filter_types), 
filter_types, 3);
  set_up_filter_box (app, "reconstruct_x_combo_box");
  set_up_filter_box (app, "reconstruct_y_combo_box");
  set_up_filter_box (app, "sample_x_combo_box");
  set_up_filter_box (app, "sample_y_combo_box");

  set_up_combo_box (
-app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats);
+   app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats, 0);

  g_signal_connect (
 gtk_builder_get_object (app->builder, "lock_checkbutton"),
diff --git a/demos/scale.ui b/demos/scale.ui
index 1e77f56..13e0e0d 100644
--- a/demos/scale.ui
+++ b/demos/scale.ui
@@ -177,6 +177,7 @@
   id="lock_checkbutton">
 Lock X and Y 
Dimensions
 0.0
+   True
   

  False
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 1ff1a49..c381260 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -28,6 +28,7 @@
  #include 
  #include 
  #include 
+#include 

  #include "pixman-private.h"

@@ -274,112 +275,242 @@ compute_image_info (pixman_image_t *image)
   FAST_PATH_X_UNIT_POSITIVE |
   FAST_PATH_Y_UNIT_ZERO |
   FAST_PATH_AFFINE_TRANSFORM);
+   switch (image->common.filter)
+   {
+   case PIXMAN_FILTER_CONVOLUTION:
+   break;
+   case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
+   flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER;
+   break;
+   default:
+   flags |= (FAST_PATH_NEAREST_FILTER | 
FAST_PATH_NO_CONVOLUTION_FILTER);
+   break;
+   

Re: [Pixman] [PATCH v10 15/15] pixman-image: Implement PIXMAN_FILTER_GOOD/BEST as separable convolutions

2016-02-04 Thread Oded Gabbay
On Tue, Feb 2, 2016 at 8:28 AM,   wrote:
> From: Bill Spitzak 
>
> Detects and uses PIXMAN_FILTER_NEAREST for all 8 90-degree rotations and
> reflections when the scale is 1.0 and integer translation.
>
> GOOD uses:
>
>  scale < 1/16 : BOX.BOX at size 16
>  scale < 3/4 : BOX.BOX at size 1/scale
>  larger : BOX.BOX at size 1
>
>  If both directions have a scale >= 3/4 or a scale of 1/2 and an integer
>  translation, the faster PIXMAN_FILTER_BILINEAR code is used. This is
>  compatable at these scales with older versions of pixman where bilinear
>  was always used for GOOD.
>
> BEST uses:
>
>  scale < 1/24 : BOX.BOX at size 24
>  scale < 1/16 : BOX.BOX at size 1/scale
>  scale < 1 : IMPULSE.LANCZOS2 at size 1/scale
>  scale < 2.333 : IMPULSE.LANCZOS2 at size 1
>  scale < 128 : BOX.LANCZOS2 at size 1/(scale-1) (antialiased square pixels)
>  larger : BOX.LANCZOS2 at size 1/127 (antialias blur gets thicker)
>
> v8: Cutoff in BEST between IMPULSE.LANCZOS2 and BOX.LANCZOS2 adjusted for
> a better match between the filters.
>
> v9: Use the new negative subsample controls to scale the subsamples. These
> were chosen by finding the lowest number that did not add visible
> artifacts to the zone plate image.
>
> Scale demo altered to default to GOOD and locked-together x+y scale

Let's separate pixman core changes and demo changes. It's bad for
bisectability and maintainability.

Do the core changes first, then another patch for the scale demo.

>
> Fixed divide-by-zero from all-zero matrix found by stress-test
>
> Signed-off-by: Bill Spitzak 
> ---
>  demos/scale.c |  10 +-
>  demos/scale.ui|   1 +
>  pixman/pixman-image.c | 289 
> --
>  3 files changed, 216 insertions(+), 84 deletions(-)
>
> diff --git a/demos/scale.c b/demos/scale.c
> index 881004e..3df4442 100644
> --- a/demos/scale.c
> +++ b/demos/scale.c
> @@ -340,7 +340,7 @@ on_expose (GtkWidget *da, GdkEvent *event, gpointer data)
>
>  static void
>  set_up_combo_box (app_t *app, const char *box_name,
> -  int n_entries, const named_int_t table[])
> +  int n_entries, const named_int_t table[], int active)
>  {
>  GtkWidget *widget = get_widget (app, box_name);
>  GtkListStore *model;
> @@ -366,7 +366,7 @@ set_up_combo_box (app_t *app, const char *box_name,
> gtk_list_store_set (model, , 0, info->name, -1);
>  }
>
> -gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
> +gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active);
>
>  g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
>  }
> @@ -374,7 +374,7 @@ set_up_combo_box (app_t *app, const char *box_name,
>  static void
>  set_up_filter_box (app_t *app, const char *box_name)
>  {
> -set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters);
> +set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters, 0);
>  }
>
>  static char *
> @@ -422,14 +422,14 @@ app_new (pixman_image_t *original)
>  widget = get_widget (app, "drawing_area");
>  g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app);
>
> -set_up_combo_box (app, "filter_combo_box", G_N_ELEMENTS (filter_types), 
> filter_types);
> +set_up_combo_box (app, "filter_combo_box", G_N_ELEMENTS (filter_types), 
> filter_types, 3);
>  set_up_filter_box (app, "reconstruct_x_combo_box");
>  set_up_filter_box (app, "reconstruct_y_combo_box");
>  set_up_filter_box (app, "sample_x_combo_box");
>  set_up_filter_box (app, "sample_y_combo_box");
>
>  set_up_combo_box (
> -app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats);
> +   app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats, 0);
>
>  g_signal_connect (
> gtk_builder_get_object (app->builder, "lock_checkbutton"),
> diff --git a/demos/scale.ui b/demos/scale.ui
> index 1e77f56..13e0e0d 100644
> --- a/demos/scale.ui
> +++ b/demos/scale.ui
> @@ -177,6 +177,7 @@
>   id="lock_checkbutton">
> Lock X and Y 
> Dimensions
> 0.0
> +   True
>   
>
>  False
> diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
> index 1ff1a49..c381260 100644
> --- a/pixman/pixman-image.c
> +++ b/pixman/pixman-image.c
> @@ -28,6 +28,7 @@
>  #include 
>  #include 
>  #include 
> +#include 
>
>  #include "pixman-private.h"
>
> @@ -274,112 +275,242 @@ compute_image_info (pixman_image_t *image)
>   FAST_PATH_X_UNIT_POSITIVE |
>   FAST_PATH_Y_UNIT_ZERO |
>   FAST_PATH_AFFINE_TRANSFORM);
> +   switch (image->common.filter)
> +   {
> +   case PIXMAN_FILTER_CONVOLUTION:
> +   break;
> +   case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
> +   flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER;
> +

[Pixman] [PATCH v10 15/15] pixman-image: Implement PIXMAN_FILTER_GOOD/BEST as separable convolutions

2016-02-01 Thread spitzak
From: Bill Spitzak 

Detects and uses PIXMAN_FILTER_NEAREST for all 8 90-degree rotations and
reflections when the scale is 1.0 and integer translation.

GOOD uses:

 scale < 1/16 : BOX.BOX at size 16
 scale < 3/4 : BOX.BOX at size 1/scale
 larger : BOX.BOX at size 1

 If both directions have a scale >= 3/4 or a scale of 1/2 and an integer
 translation, the faster PIXMAN_FILTER_BILINEAR code is used. This is
 compatable at these scales with older versions of pixman where bilinear
 was always used for GOOD.

BEST uses:

 scale < 1/24 : BOX.BOX at size 24
 scale < 1/16 : BOX.BOX at size 1/scale
 scale < 1 : IMPULSE.LANCZOS2 at size 1/scale
 scale < 2.333 : IMPULSE.LANCZOS2 at size 1
 scale < 128 : BOX.LANCZOS2 at size 1/(scale-1) (antialiased square pixels)
 larger : BOX.LANCZOS2 at size 1/127 (antialias blur gets thicker)

v8: Cutoff in BEST between IMPULSE.LANCZOS2 and BOX.LANCZOS2 adjusted for
a better match between the filters.

v9: Use the new negative subsample controls to scale the subsamples. These
were chosen by finding the lowest number that did not add visible
artifacts to the zone plate image.

Scale demo altered to default to GOOD and locked-together x+y scale

Fixed divide-by-zero from all-zero matrix found by stress-test

Signed-off-by: Bill Spitzak 
---
 demos/scale.c |  10 +-
 demos/scale.ui|   1 +
 pixman/pixman-image.c | 289 --
 3 files changed, 216 insertions(+), 84 deletions(-)

diff --git a/demos/scale.c b/demos/scale.c
index 881004e..3df4442 100644
--- a/demos/scale.c
+++ b/demos/scale.c
@@ -340,7 +340,7 @@ on_expose (GtkWidget *da, GdkEvent *event, gpointer data)
 
 static void
 set_up_combo_box (app_t *app, const char *box_name,
-  int n_entries, const named_int_t table[])
+  int n_entries, const named_int_t table[], int active)
 {
 GtkWidget *widget = get_widget (app, box_name);
 GtkListStore *model;
@@ -366,7 +366,7 @@ set_up_combo_box (app_t *app, const char *box_name,
gtk_list_store_set (model, , 0, info->name, -1);
 }
 
-gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+gtk_combo_box_set_active (GTK_COMBO_BOX (widget), active);
 
 g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
 }
@@ -374,7 +374,7 @@ set_up_combo_box (app_t *app, const char *box_name,
 static void
 set_up_filter_box (app_t *app, const char *box_name)
 {
-set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters);
+set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters, 0);
 }
 
 static char *
@@ -422,14 +422,14 @@ app_new (pixman_image_t *original)
 widget = get_widget (app, "drawing_area");
 g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app);
 
-set_up_combo_box (app, "filter_combo_box", G_N_ELEMENTS (filter_types), 
filter_types);
+set_up_combo_box (app, "filter_combo_box", G_N_ELEMENTS (filter_types), 
filter_types, 3);
 set_up_filter_box (app, "reconstruct_x_combo_box");
 set_up_filter_box (app, "reconstruct_y_combo_box");
 set_up_filter_box (app, "sample_x_combo_box");
 set_up_filter_box (app, "sample_y_combo_box");
 
 set_up_combo_box (
-app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats);
+   app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats, 0);
 
 g_signal_connect (
gtk_builder_get_object (app->builder, "lock_checkbutton"),
diff --git a/demos/scale.ui b/demos/scale.ui
index 1e77f56..13e0e0d 100644
--- a/demos/scale.ui
+++ b/demos/scale.ui
@@ -177,6 +177,7 @@
  id="lock_checkbutton">
Lock X and Y 
Dimensions
0.0
+   True
  
   
 False
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 1ff1a49..c381260 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -28,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "pixman-private.h"
 
@@ -274,112 +275,242 @@ compute_image_info (pixman_image_t *image)
  FAST_PATH_X_UNIT_POSITIVE |
  FAST_PATH_Y_UNIT_ZERO |
  FAST_PATH_AFFINE_TRANSFORM);
+   switch (image->common.filter)
+   {
+   case PIXMAN_FILTER_CONVOLUTION:
+   break;
+   case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
+   flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER;
+   break;
+   default:
+   flags |= (FAST_PATH_NEAREST_FILTER | 
FAST_PATH_NO_CONVOLUTION_FILTER);
+   break;
+   }
 }
 else
 {
+   pixman_fixed_t (*m)[3] = image->common.transform->matrix;
+   double dx, dy;
+   int nearest_ok, bilinear_ok;
+
flags |= FAST_PATH_HAS_TRANSFORM;
 
-   if (image->common.transform->matrix[2][0] == 0  &&
-   image->common.transform->matrix[2][1]