/* * Copyright © 2004 David Reveman * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of * David Reveman not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior permission. * David Reveman makes no representations about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: David Reveman */ #ifdef HAVE_CONFIG_H # include "../config.h" #endif #include "glitzint.h" struct _glitz_filter_params_t { int fp_type; int id; glitz_vec4_t *vectors; int n_vectors; }; static glitz_status_t _glitz_filter_params_ensure (glitz_surface_t *surface, int vectors) { int size; size = sizeof (glitz_filter_params_t) + vectors * sizeof (glitz_vec4_t); if (!surface->filter_params || surface->filter_params->n_vectors != vectors) { if (surface->filter_params) free (surface->filter_params); surface->filter_params = malloc (size); if (surface->filter_params == NULL) return GLITZ_STATUS_NO_MEMORY; surface->filter_params->fp_type = 0; surface->filter_params->id = 0; surface->filter_params->vectors = (glitz_vec4_t *) (surface->filter_params + 1); surface->filter_params->n_vectors = vectors; } return GLITZ_STATUS_SUCCESS; } static void _glitz_filter_params_set (glitz_float_t *value, const glitz_float_t default_value, glitz_fixed16_16_t **params, int *n_params) { if (*n_params > 0) { *value = FIXED_TO_FLOAT (**params); (*params)++; (*n_params)--; } else *value = default_value; } static int _glitz_color_stop_compare (const void *elem1, const void *elem2) { return (((glitz_vec4_t *) elem1)->v[2] == ((glitz_vec4_t *) elem2)->v[2]) ? /* equal offsets, sort on id */ ((((glitz_vec4_t *) elem1)->v[3] < ((glitz_vec4_t *) elem2)->v[3]) ? -1 : 1) : /* sort on offset */ ((((glitz_vec4_t *) elem1)->v[2] < ((glitz_vec4_t *) elem2)->v[2]) ? -1 : 1); } glitz_status_t glitz_filter_set_params (glitz_surface_t *surface, glitz_filter_t filter, glitz_fixed16_16_t *params, int n_params) { glitz_vec4_t *vecs; int i, size = 0; switch (filter) { case GLITZ_FILTER_CONVOLUTION: { glitz_float_t dm, dn; int cx, cy, m, n, j; _glitz_filter_params_set (&dm, 3.0f, ¶ms, &n_params); _glitz_filter_params_set (&dn, 3.0f, ¶ms, &n_params); glitz_clamp_value (&dm, 1.0f, 1024.0f); glitz_clamp_value (&dn, 1.0f, 1024.0f); m = dm; n = dn; size = m * n; if (_glitz_filter_params_ensure (surface, size)) return GLITZ_STATUS_NO_MEMORY; vecs = surface->filter_params->vectors; surface->filter_params->id = 0; /* center point is rounded down in case dimensions are not even */ cx = m / 2; cy = n / 2; for (i = 0; i < m; i++) { glitz_vec4_t *vec; glitz_float_t weight; for (j = 0; j < n; j++) { _glitz_filter_params_set (&weight, 0.0f, ¶ms, &n_params); if (weight != 0.0f) { vec = &vecs[surface->filter_params->id++]; vec->v[0] = (i - cx) * surface->texture.texcoord_width_unit; vec->v[1] = (cy - j) * surface->texture.texcoord_height_unit; vec->v[2] = weight; vec->v[3] = 0.0f; } } } } break; case GLITZ_FILTER_GAUSSIAN: { glitz_float_t radius, sigma, alpha, scale, xy_scale, sum; int half_size, x, y; _glitz_filter_params_set (&radius, 1.0f, ¶ms, &n_params); glitz_clamp_value (&radius, 0.0f, 1024.0f); _glitz_filter_params_set (&sigma, radius / 2.0f, ¶ms, &n_params); glitz_clamp_value (&sigma, 0.0f, 1024.0f); _glitz_filter_params_set (&alpha, radius, ¶ms, &n_params); glitz_clamp_value (&alpha, 0.0f, 1024.0f); scale = 1.0f / (2.0f * GLITZ_PI * sigma * sigma); half_size = alpha + 0.5f; if (half_size == 0) half_size = 1; size = half_size * 2 + 1; xy_scale = 2.0f * radius / size; if (_glitz_filter_params_ensure (surface, size * size)) return GLITZ_STATUS_NO_MEMORY; vecs = surface->filter_params->vectors; surface->filter_params->id = 0; sum = 0.0f; for (x = 0; x < size; x++) { glitz_vec4_t *vec; glitz_float_t fx, fy, amp; fx = xy_scale * (x - half_size); for (y = 0; y < size; y++) { fy = xy_scale * (y - half_size); amp = scale * exp ((-1.0f * (fx * fx + fy * fy)) / (2.0f * sigma * sigma)); if (amp > 0.0f) { vec = &vecs[surface->filter_params->id++]; vec->v[0] = fx * surface->texture.texcoord_width_unit; vec->v[1] = fy * surface->texture.texcoord_height_unit; vec->v[2] = amp; vec->v[3] = 0.0f; sum += amp; } } } /* normalize */ if (sum != 0.0f) sum = 1.0f / sum; for (i = 0; i < surface->filter_params->id; i++) vecs[i].v[2] *= sum; } break; case GLITZ_FILTER_LINEAR_GRADIENT: case GLITZ_FILTER_RADIAL_GRADIENT: if (n_params <= 4) { if (surface->box.x2 == 1) size = surface->box.y2; else if (surface->box.y2 == 1) size = surface->box.x2; } else size = (n_params - 2) / 3; if (size < 2) size = 2; if (filter == GLITZ_FILTER_LINEAR_GRADIENT) { glitz_float_t length, dh, dv; glitz_float_t start_x, start_y, stop_x, stop_y; if (_glitz_filter_params_ensure (surface, size + 1)) return GLITZ_STATUS_NO_MEMORY; vecs = surface->filter_params->vectors; _glitz_filter_params_set (&start_x, 0.0f, ¶ms, &n_params); _glitz_filter_params_set (&start_y, 0.0f, ¶ms, &n_params); _glitz_filter_params_set (&stop_x, 1.0f, ¶ms, &n_params); _glitz_filter_params_set (&stop_y, 0.0f, ¶ms, &n_params); dh = stop_x - start_x; dv = stop_y - start_y; length = dh * dh + dv * dv; vecs->v[0] = dh / length; vecs->v[1] = dv / length; vecs->v[2] = -vecs->v[0] * start_x - vecs->v[1] * start_y; vecs->v[3] = 0.0f; } else { glitz_float_t r0, r1, r, x0, y0, x1, y1, dx, dy; if (_glitz_filter_params_ensure (surface, size + 2)) return GLITZ_STATUS_NO_MEMORY; vecs = surface->filter_params->vectors; _glitz_filter_params_set (&x0, 0.5f, ¶ms, &n_params); _glitz_filter_params_set (&y0, 0.5f, ¶ms, &n_params); _glitz_filter_params_set (&r0, 0.0f, ¶ms, &n_params); _glitz_filter_params_set (&x1, x0, ¶ms, &n_params); _glitz_filter_params_set (&y1, y0, ¶ms, &n_params); _glitz_filter_params_set (&r1, r0 + 0.5f, ¶ms, &n_params); glitz_clamp_value (&r0, 0.0f, r1); r = r0 / r1; dx = x1 - x0; dy = y1 - y0; vecs->v[0] = x0 - r * dx; vecs->v[1] = y0 - r * dy; vecs->v[2] = dx * 2.0f; vecs->v[3] = dy * 2.0f; vecs++; vecs->v[0] = 1.0f / (1.0f + r); vecs->v[1] = -r * vecs->v[0]; vecs->v[2] = (r1 * r1 - dx * dx - dy * dy) * 4.0f; vecs->v[3] = 2.0f / vecs->v[2]; } vecs++; surface->filter_params->id = size; for (i = 0; i < size; i++) { glitz_float_t x_default, y_default, o_default; o_default = i / (glitz_float_t) (size - 1); x_default = (surface->box.x2 * i) / (glitz_float_t) size; y_default = (surface->box.y2 * i) / (glitz_float_t) size; _glitz_filter_params_set (&vecs[i].v[2], o_default, ¶ms, &n_params); _glitz_filter_params_set (&vecs[i].v[0], x_default, ¶ms, &n_params); _glitz_filter_params_set (&vecs[i].v[1], y_default, ¶ms, &n_params); glitz_clamp_value (&vecs[i].v[2], 0.0f, 1.0f); glitz_clamp_value (&vecs[i].v[0], 0.0f, surface->box.x2 - 1.0f); glitz_clamp_value (&vecs[i].v[1], 0.0f, surface->box.y2 - 1.0f); vecs[i].v[0] += 0.5f; vecs[i].v[1] += 0.5f; vecs[i].v[0] += surface->texture.box.x1; vecs[i].v[1] = surface->texture.box.y2 - vecs[i].v[1]; vecs[i].v[0] *= surface->texture.texcoord_width_unit; vecs[i].v[1] *= surface->texture.texcoord_height_unit; vecs[i].v[3] = i; } /* sort color stops in ascending order */ qsort (vecs, surface->filter_params->id, sizeof (glitz_vec4_t), _glitz_color_stop_compare); for (i = 0; i < size; i++) { glitz_float_t diff; if ((i + 1) == size) diff = 1.0f - vecs[i].v[2]; else diff = vecs[i + 1].v[2] - vecs[i].v[2]; if (diff != 0.0f) vecs[i].v[3] = 1.0f / diff; else vecs[i].v[3] = 2147483647.0f; } break; case GLITZ_FILTER_BILINEAR: case GLITZ_FILTER_NEAREST: switch (surface->format->color.fourcc) { case GLITZ_FOURCC_YV12: if (_glitz_filter_params_ensure (surface, 2)) return GLITZ_STATUS_NO_MEMORY; vecs = surface->filter_params->vectors; vecs->v[0] = 0.0f; vecs->v[1] = surface->texture.texcoord_height_unit * ((surface->texture.box.y2 + 1) & ~1); vecs->v[2] = surface->texture.texcoord_width_unit * (surface->texture.width >> 1); vecs->v[3] = 0.0f; vecs++; vecs->v[0] = surface->texture.texcoord_width_unit; vecs->v[1] = surface->texture.texcoord_height_unit; vecs->v[2] = surface->texture.texcoord_width_unit * surface->texture.box.x2 - vecs->v[0]; vecs->v[3] = surface->texture.texcoord_height_unit * surface->texture.box.y2 - vecs->v[1]; surface->filter_params->id = 1; break; default: free (surface->filter_params); surface->filter_params = NULL; } break; } glitz_filter_set_type (surface, filter); return GLITZ_STATUS_SUCCESS; } glitz_gl_uint_t glitz_filter_get_fragment_program (glitz_surface_t *surface, glitz_composite_op_t *op) { if (surface->filter_params->fp_type == GLITZ_FP_UNSUPPORTED) return 0; return glitz_get_fragment_program (op, surface->filter_params->fp_type, surface->filter_params->id); } void glitz_filter_set_type (glitz_surface_t *surface, glitz_filter_t filter) { switch (surface->format->color.fourcc) { case GLITZ_FOURCC_YV12: switch (filter) { case GLITZ_FILTER_BILINEAR: case GLITZ_FILTER_NEAREST: surface->filter_params->fp_type = GLITZ_FP_COLORSPACE_YV12; break; default: surface->filter_params->fp_type = GLITZ_FP_UNSUPPORTED; break; } break; default: switch (filter) { case GLITZ_FILTER_CONVOLUTION: case GLITZ_FILTER_GAUSSIAN: surface->filter_params->fp_type = GLITZ_FP_CONVOLUTION; break; case GLITZ_FILTER_LINEAR_GRADIENT: if (surface->flags & GLITZ_SURFACE_FLAG_REPEAT_MASK) { if (SURFACE_MIRRORED (surface)) surface->filter_params->fp_type = GLITZ_FP_LINEAR_GRADIENT_REFLECT; else surface->filter_params->fp_type = GLITZ_FP_LINEAR_GRADIENT_REPEAT; } else if (surface->flags & GLITZ_SURFACE_FLAG_PAD_MASK) { surface->filter_params->fp_type = GLITZ_FP_LINEAR_GRADIENT_NEAREST; } else surface->filter_params->fp_type = GLITZ_FP_LINEAR_GRADIENT_TRANSPARENT; break; case GLITZ_FILTER_RADIAL_GRADIENT: if (surface->flags & GLITZ_SURFACE_FLAG_REPEAT_MASK) { if (SURFACE_MIRRORED (surface)) surface->filter_params->fp_type = GLITZ_FP_RADIAL_GRADIENT_REFLECT; else surface->filter_params->fp_type = GLITZ_FP_RADIAL_GRADIENT_REPEAT; } else if (surface->flags & GLITZ_SURFACE_FLAG_PAD_MASK) { surface->filter_params->fp_type = GLITZ_FP_RADIAL_GRADIENT_NEAREST; } else surface->filter_params->fp_type = GLITZ_FP_RADIAL_GRADIENT_TRANSPARENT; default: break; } } } void glitz_filter_enable (glitz_surface_t *surface, glitz_composite_op_t *op) { glitz_gl_proc_address_list_t *gl = op->gl; int i; gl->enable (GLITZ_GL_FRAGMENT_PROGRAM); gl->bind_program (GLITZ_GL_FRAGMENT_PROGRAM, op->fp); switch (surface->filter) { case GLITZ_FILTER_GAUSSIAN: case GLITZ_FILTER_CONVOLUTION: for (i = 0; i < surface->filter_params->id; i++) gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, i, surface->filter_params->vectors[i].v); break; case GLITZ_FILTER_LINEAR_GRADIENT: case GLITZ_FILTER_RADIAL_GRADIENT: { int j, fp_type = surface->filter_params->fp_type; glitz_vec4_t *vec; vec = surface->filter_params->vectors; gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, 0, vec->v); vec++; j = 1; if (surface->filter == GLITZ_FILTER_RADIAL_GRADIENT) { gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, j++, vec->v); vec++; } if (fp_type == GLITZ_FP_LINEAR_GRADIENT_TRANSPARENT || fp_type == GLITZ_FP_RADIAL_GRADIENT_TRANSPARENT) { glitz_vec4_t v; v.v[0] = v.v[1] = -1.0f; v.v[2] = 0.0f; v.v[3] = (vec->v[3])? 1.0f / vec->v[3]: 1.0f; gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, j++, v.v); } for (i = 0; i < surface->filter_params->id; i++, vec++) gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, i + j, vec->v); if (fp_type == GLITZ_FP_LINEAR_GRADIENT_TRANSPARENT || fp_type == GLITZ_FP_RADIAL_GRADIENT_TRANSPARENT) { glitz_vec4_t v; v.v[0] = v.v[1] = -1.0f; v.v[2] = v.v[3] = 1.0f; gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, i + j, v.v); } } break; case GLITZ_FILTER_BILINEAR: case GLITZ_FILTER_NEAREST: switch (surface->format->color.fourcc) { case GLITZ_FOURCC_YV12: { glitz_vec4_t *vec; vec = surface->filter_params->vectors; gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, 0, vec[0].v); gl->program_local_param_4fv (GLITZ_GL_FRAGMENT_PROGRAM, 1, vec[1].v); } break; } break; } }