Re: [Pixman] [PATCH 2/2] pixman: Use maximum precision for pixman-bits-image, v2.

2019-02-11 Thread Maarten Lankhorst
Op 09-02-2019 om 13:01 schreef Basile Clement:
> On 1/8/19 11:42 AM, Maarten Lankhorst wrote:
>
>> pixman-bits-image's wide helpers first obtains the 8-bits image,
>> then converts it to float. This destroys all the precision that
>> the wide path was offering.
>>
>> Fix this by making get_pixel() take a pointer instead of returning
>> a value. Floating point will fill in a argb_t, while the 8-bits path
>> will fill a 32-bits ARGB value. This also requires writing a floating
>> point bilinear interpolator. With this change pixman can use the full
>> floating point precision internally in all paths.
>>
>> Changes since v1:
>> - Make accum and reduce an argument to convolution functions,
>>   to remove duplication.
>>
>> Signed-off-by: Maarten Lankhorst 
> Apologies for the late reply; I have been quite busy with other things
> recently.
>
> The patch looks good to me now!  I have not figured out how to visually
> test the filters however.

Thanks, pushed.

Can you rebase the dithering patches so I can take a look?

~Maarten

___
Pixman mailing list
Pixman@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pixman


Re: [Pixman] [PATCH 2/2] pixman: Use maximum precision for pixman-bits-image, v2.

2019-02-09 Thread Basile Clement
On 1/8/19 11:42 AM, Maarten Lankhorst wrote:

> pixman-bits-image's wide helpers first obtains the 8-bits image,
> then converts it to float. This destroys all the precision that
> the wide path was offering.
>
> Fix this by making get_pixel() take a pointer instead of returning
> a value. Floating point will fill in a argb_t, while the 8-bits path
> will fill a 32-bits ARGB value. This also requires writing a floating
> point bilinear interpolator. With this change pixman can use the full
> floating point precision internally in all paths.
>
> Changes since v1:
> - Make accum and reduce an argument to convolution functions,
>   to remove duplication.
>
> Signed-off-by: Maarten Lankhorst 

Apologies for the late reply; I have been quite busy with other things
recently.

The patch looks good to me now!  I have not figured out how to visually
test the filters however.

> ---
>  pixman/pixman-bits-image.c | 414 +++--
>  pixman/pixman-inlines.h|  25 +++
>  2 files changed, 328 insertions(+), 111 deletions(-)
>
> diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
> index 9fb91ff5831d..564789e9c685 100644
> --- a/pixman/pixman-bits-image.c
> +++ b/pixman/pixman-bits-image.c
> @@ -36,43 +36,45 @@
>  #include "pixman-combine32.h"
>  #include "pixman-inlines.h"
>  
> -static uint32_t *
> -_pixman_image_get_scanline_generic_float (pixman_iter_t * iter,
> -   const uint32_t *mask)
> -{
> -pixman_iter_get_scanline_t fetch_32 = iter->data;
> -uint32_t *buffer = iter->buffer;
> -
> -fetch_32 (iter, NULL);
> +/* Fetch functions */
>  
> -pixman_expand_to_float ((argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, 
> iter->width);
> +static force_inline void
> +fetch_pixel_no_alpha_32 (bits_image_t *image,
> +  int x, int y, pixman_bool_t check_bounds,
> +  void *out)
> +{
> +uint32_t *ret = out;
>  
> -return iter->buffer;
> +if (check_bounds &&
> + (x < 0 || x >= image->width || y < 0 || y >= image->height))
> + *ret = 0;
> +else
> + *ret = image->fetch_pixel_32 (image, x, y);
>  }
>  
> -/* Fetch functions */
> -
> -static force_inline uint32_t
> -fetch_pixel_no_alpha (bits_image_t *image,
> -   int x, int y, pixman_bool_t check_bounds)
> +static force_inline void
> +fetch_pixel_no_alpha_float (bits_image_t *image,
> + int x, int y, pixman_bool_t check_bounds,
> + void *out)
>  {
> +argb_t *ret = out;
> +
>  if (check_bounds &&
>   (x < 0 || x >= image->width || y < 0 || y >= image->height))
> -{
> - return 0;
> -}
> -
> -return image->fetch_pixel_32 (image, x, y);
> + ret->a = ret->r = ret->g = ret->b = 0.f;
> +else
> + *ret = image->fetch_pixel_float (image, x, y);
>  }
>  
> -typedef uint32_t (* get_pixel_t) (bits_image_t *image,
> -   int x, int y, pixman_bool_t check_bounds);
> +typedef void (* get_pixel_t) (bits_image_t *image,
> +   int x, int y, pixman_bool_t check_bounds, void 
> *out);
>  
> -static force_inline uint32_t
> +static force_inline void
>  bits_image_fetch_pixel_nearest (bits_image_t   *image,
>   pixman_fixed_t  x,
>   pixman_fixed_t  y,
> - get_pixel_t get_pixel)
> + get_pixel_t get_pixel,
> + void   *out)
>  {
>  int x0 = pixman_fixed_to_int (x - pixman_fixed_e);
>  int y0 = pixman_fixed_to_int (y - pixman_fixed_e);
> @@ -82,19 +84,20 @@ bits_image_fetch_pixel_nearest (bits_image_t   *image,
>   repeat (image->common.repeat, , image->width);
>   repeat (image->common.repeat, , image->height);
>  
> - return get_pixel (image, x0, y0, FALSE);
> + get_pixel (image, x0, y0, FALSE, out);
>  }
>  else
>  {
> - return get_pixel (image, x0, y0, TRUE);
> + get_pixel (image, x0, y0, TRUE, out);
>  }
>  }
>  
> -static force_inline uint32_t
> -bits_image_fetch_pixel_bilinear (bits_image_t   *image,
> -  pixman_fixed_t  x,
> -  pixman_fixed_t  y,
> -  get_pixel_t get_pixel)
> +static force_inline void
> +bits_image_fetch_pixel_bilinear_32 (bits_image_t   *image,
> + pixman_fixed_t  x,
> + pixman_fixed_t  y,
> + get_pixel_t get_pixel,
> + void   *out)
>  {
>  pixman_repeat_t repeat_mode = image->common.repeat;
>  int width = image->width;
> @@ -102,6 +105,7 @@ bits_image_fetch_pixel_bilinear (bits_image_t   *image,
>  int x1, y1, x2, y2;
>  uint32_t tl, tr, bl, br;
>  int32_t distx, disty;
> +uint32_t *ret = out;
>  
>  x1 = x - 

Re: [Pixman] [PATCH 2/2] pixman: Use maximum precision for pixman-bits-image

2019-01-07 Thread Maarten Lankhorst
Op 04-01-2019 om 18:53 schreef Basile Clement:
> I am far from qualified for doing a proper review on this, but this
> looks reasonable overall.  I only have a few concerns outlined below.
>
> Is there a way to visually test this?  Adding an example in demos/ would
> be great, and would help ensuring there are no scaling issues in the
> conv/sepconv filters (see inline comments below).
>
> I am also a bit concerned by the duplication in the convolution and
> separable_convolution functions.  I understand why it's there, but
> (unlike the bilinear case) those functions are quite large by themselves
> already.  If not too complex, I'd merge them by using type-erased
> `accumulate(int *satot, int *srtot, int *sgtot, int *sbtot, void *pixel,
> pixman_fixed_t params)` and `reduce(int *satot, int *srtot, int *sgtot,
> int *sbtot, void *out)` argument functions.  They should get inlined by
> the compiler anyways.

Managed to make the scale one use the float paths. :)

diff --git a/demos/scale.c b/demos/scale.c
index 0995ad08da9d..51ab1d4a2408 100644
--- a/demos/scale.c
+++ b/demos/scale.c
@@ -289,7 +289,7 @@ on_expose (GtkWidget *da, GdkEvent *event, gpointer data)
 
 pixels = calloc (1, area->width * area->height * 4);
 tmp = pixman_image_create_bits (
-PIXMAN_a8r8g8b8, area->width, area->height, pixels, area->width * 4);
+PIXMAN_x2r10g10b10, area->width, area->height, pixels, area->width * 
4);
 
 if (area->x < app->scaled_width && area->y < app->scaled_height)
 {
@@ -301,7 +301,7 @@ on_expose (GtkWidget *da, GdkEvent *event, gpointer data)
 }
 
 surface = cairo_image_surface_create_for_data (
-(uint8_t *)pixels, CAIRO_FORMAT_ARGB32,
+(uint8_t *)pixels, CAIRO_FORMAT_RGB30,
 area->width, area->height, area->width * 4);
 
 cr = gdk_cairo_create (da->window);
@@ -423,8 +423,9 @@ int
 main (int argc, char **argv)
 {
 GtkWidget *window;
-pixman_image_t *image;
+pixman_image_t *image, *tmp;
 app_t *app;
+uint32_t *pixels;
 
 gtk_init (, );
 
@@ -440,7 +441,18 @@ main (int argc, char **argv)
return -1;
 }
 
-app = app_new (image);
+pixels = calloc (pixman_image_get_height(image), 
pixman_image_get_stride(image));
+tmp = pixman_image_create_bits (
+PIXMAN_x2r10g10b10, pixman_image_get_width(image),
+   pixman_image_get_height(image), pixels, pixman_image_get_stride(image));
+
+pixman_image_composite (
+PIXMAN_OP_SRC,
+image, NULL, tmp,
+0, 0, 0, 0, 0, 0,
+pixman_image_get_width(image), pixman_image_get_height(image));
+
+app = app_new (tmp);
 
 window = get_widget (app, "main");
 

---

Didn't notice anything wrong, so all looks correct with at least simple 
convolution.

> - Basile
>
> On 1/3/19 12:18 PM, Maarten Lankhorst wrote:
>> +static force_inline void
>> +bits_image_fetch_pixel_convolution_float (bits_image_t   *image,
>> +  pixman_fixed_t  x,
>> +  pixman_fixed_t  y,
>> +  get_pixel_t get_pixel,
>> +  void*out)
>> +{
>> +pixman_fixed_t *params = image->common.filter_params;
>> +int x_off = (params[0] - pixman_fixed_1) >> 1;
>> +int y_off = (params[1] - pixman_fixed_1) >> 1;
>> +int32_t cwidth = pixman_fixed_to_int (params[0]);
>> +int32_t cheight = pixman_fixed_to_int (params[1]);
>> +int32_t i, j, x1, x2, y1, y2;
>> +pixman_repeat_t repeat_mode = image->common.repeat;
>> +int width = image->width;
>> +int height = image->height;
>> +int srtot, sgtot, sbtot, satot;
>> +argb_t *ret = out;
>> +
>> +params += 2;
>> +
>> +x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off);
>> +y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off);
>> +x2 = x1 + cwidth;
>> +y2 = y1 + cheight;
>> +
>> +srtot = sgtot = sbtot = satot = 0;
>> +
>> +for (i = y1; i < y2; ++i)
>> +{
>> +for (j = x1; j < x2; ++j)
>> +{
>> +int rx = j;
>> +int ry = i;
>> +
>> +pixman_fixed_t f = *params;
>> +
>> +if (f)
>> +{
>> +argb_t pixel;
>> +
>> +if (repeat_mode != PIXMAN_REPEAT_NONE)
>> +{
>> +repeat (repeat_mode, , width);
>> +repeat (repeat_mode, , height);
>> +
>> +get_pixel (image, rx, ry, FALSE, );
>> +}
>> +else
>> +{
>> +get_pixel (image, rx, ry, TRUE, );
>> +}
>> +
>> +satot += pixel.a * f;
>> +srtot += pixel.r * f;
>> +sgtot += pixel.g * f;
>> +sbtot += pixel.b * f;
> I am concerned with those lines (and the corresponding lines in
> separable_convolution).  Unless I am mistaken, `sXtot` and `f` are
> integer variables while `pixel.X` are floating point variables in 

Re: [Pixman] [PATCH 2/2] pixman: Use maximum precision for pixman-bits-image

2019-01-04 Thread Basile Clement
I am far from qualified for doing a proper review on this, but this
looks reasonable overall.  I only have a few concerns outlined below.

Is there a way to visually test this?  Adding an example in demos/ would
be great, and would help ensuring there are no scaling issues in the
conv/sepconv filters (see inline comments below).

I am also a bit concerned by the duplication in the convolution and
separable_convolution functions.  I understand why it's there, but
(unlike the bilinear case) those functions are quite large by themselves
already.  If not too complex, I'd merge them by using type-erased
`accumulate(int *satot, int *srtot, int *sgtot, int *sbtot, void *pixel,
pixman_fixed_t params)` and `reduce(int *satot, int *srtot, int *sgtot,
int *sbtot, void *out)` argument functions.  They should get inlined by
the compiler anyways.

- Basile

On 1/3/19 12:18 PM, Maarten Lankhorst wrote:
> +static force_inline void
> +bits_image_fetch_pixel_convolution_float (bits_image_t   *image,
> +   pixman_fixed_t  x,
> +   pixman_fixed_t  y,
> +   get_pixel_t get_pixel,
> +   void*out)
> +{
> +pixman_fixed_t *params = image->common.filter_params;
> +int x_off = (params[0] - pixman_fixed_1) >> 1;
> +int y_off = (params[1] - pixman_fixed_1) >> 1;
> +int32_t cwidth = pixman_fixed_to_int (params[0]);
> +int32_t cheight = pixman_fixed_to_int (params[1]);
> +int32_t i, j, x1, x2, y1, y2;
> +pixman_repeat_t repeat_mode = image->common.repeat;
> +int width = image->width;
> +int height = image->height;
> +int srtot, sgtot, sbtot, satot;
> +argb_t *ret = out;
> +
> +params += 2;
> +
> +x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off);
> +y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off);
> +x2 = x1 + cwidth;
> +y2 = y1 + cheight;
> +
> +srtot = sgtot = sbtot = satot = 0;
> +
> +for (i = y1; i < y2; ++i)
> +{
> + for (j = x1; j < x2; ++j)
> + {
> + int rx = j;
> + int ry = i;
> +
> + pixman_fixed_t f = *params;
> +
> + if (f)
> + {
> + argb_t pixel;
> +
> + if (repeat_mode != PIXMAN_REPEAT_NONE)
> + {
> + repeat (repeat_mode, , width);
> + repeat (repeat_mode, , height);
> +
> + get_pixel (image, rx, ry, FALSE, );
> + }
> + else
> + {
> + get_pixel (image, rx, ry, TRUE, );
> + }
> +
> + satot += pixel.a * f;
> + srtot += pixel.r * f;
> + sgtot += pixel.g * f;
> + sbtot += pixel.b * f;

I am concerned with those lines (and the corresponding lines in
separable_convolution).  Unless I am mistaken, `sXtot` and `f` are
integer variables while `pixel.X` are floating point variables in 0-1,
so it looks like more truncation than wanted may be occuring here.

> + }
> +
> + params++;
> + }
> +}
> +
> +ret->a = CLIP (satot / 65536.f, 0.f, 1.f);
> +ret->r = CLIP (srtot / 65536.f, 0.f, 1.f);
> +ret->g = CLIP (sgtot / 65536.f, 0.f, 1.f);
> +ret->b = CLIP (sbtot / 65536.f, 0.f, 1.f);
>  }
>  
> -static uint32_t
> -bits_image_fetch_pixel_separable_convolution (bits_image_t *image,
> -  pixman_fixed_t x,
> -  pixman_fixed_t y,
> -  get_pixel_tget_pixel)
> +static void
> +bits_image_fetch_pixel_separable_convolution_32 (bits_image_t  *image,
> +  pixman_fixed_t x,
> +  pixman_fixed_t y,
> +  get_pixel_tget_pixel,
> +  void  *out)
>  {
>  pixman_fixed_t *params = image->common.filter_params;
>  pixman_repeat_t repeat_mode = image->common.repeat;
> @@ -234,6 +359,7 @@ bits_image_fetch_pixel_separable_convolution 
> (bits_image_t *image,
>  int32_t x1, x2, y1, y2;
>  int32_t px, py;
>  int i, j;
> +uint32_t *ret = out;
>  
>  /* Round x and y to the middle of the closest phase before continuing. 
> This
>   * ensures that the convolution matrix is aligned right, since it was
> @@ -278,11 +404,11 @@ bits_image_fetch_pixel_separable_convolution 
> (bits_image_t *image,
>  repeat (repeat_mode, , width);
>  repeat (repeat_mode, , height);
>  
> -pixel = get_pixel (image, rx, ry, FALSE);
> +get_pixel (image, rx, ry, FALSE, );
>  }
>  else
>  {
> -pixel = get_pixel (image, rx, ry, TRUE);
> +  

Re: [Pixman] [PATCH 2/2] pixman: Use maximum precision for pixman-bits-image

2019-01-03 Thread Maarten Lankhorst
Oops, missing commit description..

pixman-bits-image's wide helpers first obtains the 8-bits image,
then converts it to float. This destroys all the precision that
the wide path was offering.

Fix this by making get_pixel() take a pointer instead of returning
a value. Floating point will fill in a argb_t, while the 8-bits path
will fill a 32-bits ARGB value. This also requires writing a floating
point bilinear interpolator. With this change pixman can use the full
floating point precision internally in all paths.

Op 03-01-2019 om 12:18 schreef Maarten Lankhorst:
> Signed-off-by: Maarten Lankhorst 
> ---
>  pixman/pixman-bits-image.c | 478 ++---
>  pixman/pixman-inlines.h|  25 ++
>  2 files changed, 418 insertions(+), 85 deletions(-)
>
> diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
> index 9fb91ff5831d..cb1c5ce12d46 100644
> --- a/pixman/pixman-bits-image.c
> +++ b/pixman/pixman-bits-image.c
> @@ -36,43 +36,45 @@
>  #include "pixman-combine32.h"
>  #include "pixman-inlines.h"
>  
> -static uint32_t *
> -_pixman_image_get_scanline_generic_float (pixman_iter_t * iter,
> -   const uint32_t *mask)
> -{
> -pixman_iter_get_scanline_t fetch_32 = iter->data;
> -uint32_t *buffer = iter->buffer;
> -
> -fetch_32 (iter, NULL);
> +/* Fetch functions */
>  
> -pixman_expand_to_float ((argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, 
> iter->width);
> +static force_inline void
> +fetch_pixel_no_alpha_32 (bits_image_t *image,
> +  int x, int y, pixman_bool_t check_bounds,
> +  void *out)
> +{
> +uint32_t *ret = out;
>  
> -return iter->buffer;
> +if (check_bounds &&
> + (x < 0 || x >= image->width || y < 0 || y >= image->height))
> + *ret = 0;
> +else
> + *ret = image->fetch_pixel_32 (image, x, y);
>  }
>  
> -/* Fetch functions */
> -
> -static force_inline uint32_t
> -fetch_pixel_no_alpha (bits_image_t *image,
> -   int x, int y, pixman_bool_t check_bounds)
> +static force_inline void
> +fetch_pixel_no_alpha_float (bits_image_t *image,
> + int x, int y, pixman_bool_t check_bounds,
> + void *out)
>  {
> +argb_t *ret = out;
> +
>  if (check_bounds &&
>   (x < 0 || x >= image->width || y < 0 || y >= image->height))
> -{
> - return 0;
> -}
> -
> -return image->fetch_pixel_32 (image, x, y);
> + ret->a = ret->r = ret->g = ret->b = 0.f;
> +else
> + *ret = image->fetch_pixel_float (image, x, y);
>  }
>  
> -typedef uint32_t (* get_pixel_t) (bits_image_t *image,
> -   int x, int y, pixman_bool_t check_bounds);
> +typedef void (* get_pixel_t) (bits_image_t *image,
> +   int x, int y, pixman_bool_t check_bounds, void 
> *out);
>  
> -static force_inline uint32_t
> +static force_inline void
>  bits_image_fetch_pixel_nearest (bits_image_t   *image,
>   pixman_fixed_t  x,
>   pixman_fixed_t  y,
> - get_pixel_t get_pixel)
> + get_pixel_t get_pixel,
> + void   *out)
>  {
>  int x0 = pixman_fixed_to_int (x - pixman_fixed_e);
>  int y0 = pixman_fixed_to_int (y - pixman_fixed_e);
> @@ -82,19 +84,20 @@ bits_image_fetch_pixel_nearest (bits_image_t   *image,
>   repeat (image->common.repeat, , image->width);
>   repeat (image->common.repeat, , image->height);
>  
> - return get_pixel (image, x0, y0, FALSE);
> + get_pixel (image, x0, y0, FALSE, out);
>  }
>  else
>  {
> - return get_pixel (image, x0, y0, TRUE);
> + get_pixel (image, x0, y0, TRUE, out);
>  }
>  }
>  
> -static force_inline uint32_t
> -bits_image_fetch_pixel_bilinear (bits_image_t   *image,
> -  pixman_fixed_t  x,
> -  pixman_fixed_t  y,
> -  get_pixel_t get_pixel)
> +static force_inline void
> +bits_image_fetch_pixel_bilinear_32 (bits_image_t   *image,
> + pixman_fixed_t  x,
> + pixman_fixed_t  y,
> + get_pixel_t get_pixel,
> + void   *out)
>  {
>  pixman_repeat_t repeat_mode = image->common.repeat;
>  int width = image->width;
> @@ -102,6 +105,7 @@ bits_image_fetch_pixel_bilinear (bits_image_t   *image,
>  int x1, y1, x2, y2;
>  uint32_t tl, tr, bl, br;
>  int32_t distx, disty;
> +uint32_t *ret = out;
>  
>  x1 = x - pixman_fixed_1 / 2;
>  y1 = y - pixman_fixed_1 / 2;
> @@ -121,27 +125,77 @@ bits_image_fetch_pixel_bilinear (bits_image_t   *image,
>   repeat (repeat_mode, , width);
>   repeat (repeat_mode, , height);
>  
> - tl = get_pixel (image, x1, y1, FALSE);
> - bl =