summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann Pedersen <ssp@redhat.com>2012-11-22 10:14:06 -0500
committerSøren Sandmann Pedersen <ssp@redhat.com>2012-12-08 10:50:51 -0500
commit6fd480b17c8398c217e4c11e826c82dbb8288006 (patch)
tree230b8ae95626855bc9c04611beddaac6af608406
parent7e39861da3655779ce76a72592feed3c1dd90017 (diff)
Add new filter PIXMAN_FILTER_SEPARABLE_CONVOLUTION
This filter is a new way to use a convolution matrix for filtering. In contrast to the existing CONVOLUTION filter, this new variant is different in two respects: - It is subsampled: Instead of just one convolution matrix, this filter chooses between a number of matrices based on the subpixel sample location, allowing the convolution kernel to be sampled at a higher resolution. - It is separable: Each matrix is specified as the tensor product of two vectors. This has the advantages that many fewer values have to be stored, and that the filtering can be done separately in the x and y dimensions (although the initial implementation doesn't actually do that). The motivation for this new filter is to improve image downsampling quality. Currently, the best pixman can do is the regular convolution filter which is limited to coarsely sampled convolution kernels. With this new feature, any separable filter can be used at any desired resolution.
-rw-r--r--pixman/pixman-bits-image.c102
-rw-r--r--pixman/pixman-image.c19
-rw-r--r--pixman/pixman.c8
-rw-r--r--pixman/pixman.h23
4 files changed, 149 insertions, 3 deletions
diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
index 7787ef1..97db108 100644
--- a/pixman/pixman-bits-image.c
+++ b/pixman/pixman-bits-image.c
@@ -426,6 +426,104 @@ bits_image_fetch_pixel_convolution (bits_image_t *image,
return ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot));
}
+static uint32_t
+bits_image_fetch_pixel_convolution_separable (bits_image_t *image,
+ pixman_fixed_t x,
+ pixman_fixed_t y,
+ get_pixel_t get_pixel)
+{
+ pixman_fixed_t *params = image->common.filter_params;
+ pixman_repeat_t repeat_mode = image->common.repeat;
+ int width = image->width;
+ int height = image->height;
+ int cwidth = pixman_fixed_to_int (params[0]);
+ int cheight = pixman_fixed_to_int (params[1]);
+ int x_phase_bits = pixman_fixed_to_int (params[2]);
+ int y_phase_bits = pixman_fixed_to_int (params[3]);
+ int x_phase_shift = 16 - x_phase_bits;
+ int y_phase_shift = 16 - y_phase_bits;
+ int x_off = ((cwidth << 16) - pixman_fixed_1) >> 1;
+ int y_off = ((cheight << 16) - pixman_fixed_1) >> 1;
+ pixman_fixed_t *y_params;
+ int srtot, sgtot, sbtot, satot;
+ int32_t x1, x2, y1, y2;
+ int32_t px, py;
+ int i, j;
+
+ /* 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
+ * positioned relative to a particular phase (and not relative to whatever
+ * exact fraction we happen to get here).
+ */
+ x = ((x >> x_phase_shift) << x_phase_shift) + ((1 << x_phase_shift) >> 1);
+ y = ((y >> y_phase_shift) << y_phase_shift) + ((1 << y_phase_shift) >> 1);
+
+ px = (x & 0xffff) >> x_phase_shift;
+ py = (y & 0xffff) >> y_phase_shift;
+
+ y_params = params + 4 + (1 << x_phase_bits) * cwidth + py * cheight;
+
+ 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)
+ {
+ pixman_fixed_48_16_t fy = *y_params++;
+ pixman_fixed_t *x_params = params + 4 + px * cwidth;
+
+ if (fy)
+ {
+ for (j = x1; j < x2; ++j)
+ {
+ pixman_fixed_t fx = *x_params++;
+ int rx = j;
+ int ry = i;
+
+ if (fx)
+ {
+ pixman_fixed_t f;
+ uint32_t pixel;
+
+ if (repeat_mode != PIXMAN_REPEAT_NONE)
+ {
+ repeat (repeat_mode, &rx, width);
+ repeat (repeat_mode, &ry, height);
+
+ pixel = get_pixel (image, rx, ry, FALSE);
+ }
+ else
+ {
+ pixel = get_pixel (image, rx, ry, TRUE);
+ }
+
+ f = (fy * fx + 0x8000) >> 16;
+
+ srtot += (int)RED_8 (pixel) * f;
+ sgtot += (int)GREEN_8 (pixel) * f;
+ sbtot += (int)BLUE_8 (pixel) * f;
+ satot += (int)ALPHA_8 (pixel) * f;
+ }
+ }
+ }
+ }
+
+ satot = (satot + 0x8000) >> 16;
+ srtot = (srtot + 0x8000) >> 16;
+ sgtot = (sgtot + 0x8000) >> 16;
+ sbtot = (sbtot + 0x8000) >> 16;
+
+ satot = CLIP (satot, 0, 0xff);
+ srtot = CLIP (srtot, 0, 0xff);
+ sgtot = CLIP (sgtot, 0, 0xff);
+ sbtot = CLIP (sbtot, 0, 0xff);
+
+ return ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot));
+}
+
static force_inline uint32_t
bits_image_fetch_pixel_filtered (bits_image_t *image,
pixman_fixed_t x,
@@ -449,6 +547,10 @@ bits_image_fetch_pixel_filtered (bits_image_t *image,
return bits_image_fetch_pixel_convolution (image, x, y, get_pixel);
break;
+ case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
+ return bits_image_fetch_pixel_convolution_separable (image, x, y, get_pixel);
+ break;
+
default:
break;
}
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index d9c3034..93ed17e 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -371,6 +371,7 @@ compute_image_info (pixman_image_t *image)
break;
case PIXMAN_FILTER_CONVOLUTION:
+ case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
break;
default:
@@ -515,8 +516,9 @@ compute_image_info (pixman_image_t *image)
* if all channels are opaque, so we simply turn it off
* unconditionally for those images.
*/
- if (image->common.alpha_map ||
- image->common.filter == PIXMAN_FILTER_CONVOLUTION ||
+ if (image->common.alpha_map ||
+ image->common.filter == PIXMAN_FILTER_CONVOLUTION ||
+ image->common.filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION ||
image->common.component_alpha)
{
flags &= ~(FAST_PATH_IS_OPAQUE | FAST_PATH_SAMPLES_OPAQUE);
@@ -679,6 +681,19 @@ pixman_image_set_filter (pixman_image_t * image,
if (params == common->filter_params && filter == common->filter)
return TRUE;
+ if (filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION)
+ {
+ int width = pixman_fixed_to_int (params[0]);
+ int height = pixman_fixed_to_int (params[1]);
+ int x_phase_bits = pixman_fixed_to_int (params[2]);
+ int y_phase_bits = pixman_fixed_to_int (params[3]);
+ int n_x_phases = (1 << x_phase_bits);
+ int n_y_phases = (1 << y_phase_bits);
+
+ return_val_if_fail (
+ n_params == 4 + n_x_phases * width + n_y_phases * height, FALSE);
+ }
+
new_params = NULL;
if (params)
{
diff --git a/pixman/pixman.c b/pixman/pixman.c
index e0ccd87..0661f41 100644
--- a/pixman/pixman.c
+++ b/pixman/pixman.c
@@ -455,6 +455,14 @@ analyze_extent (pixman_image_t *image,
height = params[1];
break;
+ case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
+ params = image->common.filter_params;
+ x_off = - pixman_fixed_e - ((params[0] - pixman_fixed_1) >> 1);
+ y_off = - pixman_fixed_e - ((params[1] - pixman_fixed_1) >> 1);
+ width = params[0];
+ height = params[1];
+ break;
+
case PIXMAN_FILTER_GOOD:
case PIXMAN_FILTER_BEST:
case PIXMAN_FILTER_BILINEAR:
diff --git a/pixman/pixman.h b/pixman/pixman.h
index 33ebf3f..20c95da 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -292,7 +292,28 @@ typedef enum
PIXMAN_FILTER_BEST,
PIXMAN_FILTER_NEAREST,
PIXMAN_FILTER_BILINEAR,
- PIXMAN_FILTER_CONVOLUTION
+ PIXMAN_FILTER_CONVOLUTION,
+
+ /* The SEPARABLE_CONVOLUTION filter takes the following parameters:
+ *
+ * width: integer given as 16.16 fixpoint number
+ * height: integer given as 16.16 fixpoint number
+ * x_phase_bits: integer given as 16.16 fixpoint
+ * y_phase_bits: integer given as 16.16 fixpoint
+ * xtables: (1 << x_phase_bits) tables of size width
+ * ytables: (1 << y_phase_bits) tables of size height
+ *
+ * When sampling at (x, y), the location is first rounded to one of
+ * n_x_phases * n_y_phases subpixel positions. These subpixel positions
+ * determine an xtable and a ytable to use.
+ *
+ * Conceptually a width x height matrix is then formed in which each entry
+ * is the product of the corresponding entries in the x and y tables.
+ * This matrix is then aligned with the image pixels such that its center
+ * is as close as possible to the subpixel location chosen earlier. Then
+ * the image is convolved with the matrix and the resulting pixel returned.
+ */
+ PIXMAN_FILTER_SEPARABLE_CONVOLUTION
} pixman_filter_t;
typedef enum