298 lines
9.3 KiB
C
298 lines
9.3 KiB
C
/*
|
|
* Copyright © 2007 Keith Packard
|
|
*
|
|
* 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 the copyright holders not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no representations
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS 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.
|
|
*/
|
|
|
|
#include "randrstr.h"
|
|
#include "rrtransform.h"
|
|
|
|
void
|
|
RRTransformInit(RRTransformPtr transform)
|
|
{
|
|
pixman_transform_init_identity(&transform->transform);
|
|
pixman_f_transform_init_identity(&transform->f_transform);
|
|
pixman_f_transform_init_identity(&transform->f_inverse);
|
|
transform->filter = NULL;
|
|
transform->params = NULL;
|
|
transform->nparams = 0;
|
|
}
|
|
|
|
void
|
|
RRTransformFini(RRTransformPtr transform)
|
|
{
|
|
free(transform->params);
|
|
}
|
|
|
|
Bool
|
|
RRTransformEqual(RRTransformPtr a, RRTransformPtr b)
|
|
{
|
|
if (a && pixman_transform_is_identity(&a->transform))
|
|
a = NULL;
|
|
if (b && pixman_transform_is_identity(&b->transform))
|
|
b = NULL;
|
|
if (a == NULL && b == NULL)
|
|
return TRUE;
|
|
if (a == NULL || b == NULL)
|
|
return FALSE;
|
|
if (memcmp(&a->transform, &b->transform, sizeof(a->transform)) != 0)
|
|
return FALSE;
|
|
if (a->filter != b->filter)
|
|
return FALSE;
|
|
if (a->nparams != b->nparams)
|
|
return FALSE;
|
|
if (memcmp(a->params, b->params, a->nparams * sizeof(xFixed)) != 0)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
RRTransformSetFilter(RRTransformPtr dst,
|
|
PictFilterPtr filter,
|
|
xFixed * params, int nparams, int width, int height)
|
|
{
|
|
xFixed *new_params;
|
|
|
|
if (nparams) {
|
|
new_params = xallocarray(nparams, sizeof(xFixed));
|
|
if (!new_params)
|
|
return FALSE;
|
|
memcpy(new_params, params, nparams * sizeof(xFixed));
|
|
}
|
|
else
|
|
new_params = NULL;
|
|
free(dst->params);
|
|
dst->filter = filter;
|
|
dst->params = new_params;
|
|
dst->nparams = nparams;
|
|
dst->width = width;
|
|
dst->height = height;
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
RRTransformCopy(RRTransformPtr dst, RRTransformPtr src)
|
|
{
|
|
if (src && pixman_transform_is_identity(&src->transform))
|
|
src = NULL;
|
|
|
|
if (src) {
|
|
if (!RRTransformSetFilter(dst, src->filter,
|
|
src->params, src->nparams, src->width,
|
|
src->height))
|
|
return FALSE;
|
|
dst->transform = src->transform;
|
|
dst->f_transform = src->f_transform;
|
|
dst->f_inverse = src->f_inverse;
|
|
}
|
|
else {
|
|
if (!RRTransformSetFilter(dst, NULL, NULL, 0, 0, 0))
|
|
return FALSE;
|
|
pixman_transform_init_identity(&dst->transform);
|
|
pixman_f_transform_init_identity(&dst->f_transform);
|
|
pixman_f_transform_init_identity(&dst->f_inverse);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#define F(x) IntToxFixed(x)
|
|
|
|
static void
|
|
RRTransformRescale(struct pixman_f_transform *f_transform, double limit)
|
|
{
|
|
double max = 0, v, scale;
|
|
int i, j;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
for (i = 0; i < 3; i++)
|
|
if ((v = fabs(f_transform->m[j][i])) > max)
|
|
max = v;
|
|
scale = limit / max;
|
|
for (j = 0; j < 3; j++)
|
|
for (i = 0; i < 3; i++)
|
|
f_transform->m[j][i] *= scale;
|
|
}
|
|
|
|
/*
|
|
* Compute the complete transformation matrix including
|
|
* client-specified transform, rotation/reflection values and the crtc
|
|
* offset.
|
|
*
|
|
* Return TRUE if the resulting transform is not a simple translation.
|
|
*/
|
|
Bool
|
|
RRTransformCompute(int x,
|
|
int y,
|
|
int width,
|
|
int height,
|
|
Rotation rotation,
|
|
RRTransformPtr rr_transform,
|
|
PictTransformPtr transform,
|
|
struct pixman_f_transform *f_transform,
|
|
struct pixman_f_transform *f_inverse)
|
|
{
|
|
PictTransform t_transform, inverse;
|
|
struct pixman_f_transform tf_transform, tf_inverse;
|
|
Bool overflow = FALSE;
|
|
|
|
if (!transform)
|
|
transform = &t_transform;
|
|
if (!f_transform)
|
|
f_transform = &tf_transform;
|
|
if (!f_inverse)
|
|
f_inverse = &tf_inverse;
|
|
|
|
pixman_transform_init_identity(transform);
|
|
pixman_transform_init_identity(&inverse);
|
|
pixman_f_transform_init_identity(f_transform);
|
|
pixman_f_transform_init_identity(f_inverse);
|
|
if (rotation != RR_Rotate_0) {
|
|
double f_rot_cos, f_rot_sin, f_rot_dx, f_rot_dy;
|
|
double f_scale_x, f_scale_y, f_scale_dx, f_scale_dy;
|
|
xFixed rot_cos, rot_sin, rot_dx, rot_dy;
|
|
xFixed scale_x, scale_y, scale_dx, scale_dy;
|
|
|
|
/* rotation */
|
|
switch (rotation & 0xf) {
|
|
default:
|
|
case RR_Rotate_0:
|
|
f_rot_cos = 1;
|
|
f_rot_sin = 0;
|
|
f_rot_dx = 0;
|
|
f_rot_dy = 0;
|
|
rot_cos = F(1);
|
|
rot_sin = F(0);
|
|
rot_dx = F(0);
|
|
rot_dy = F(0);
|
|
break;
|
|
case RR_Rotate_90:
|
|
f_rot_cos = 0;
|
|
f_rot_sin = 1;
|
|
f_rot_dx = height;
|
|
f_rot_dy = 0;
|
|
rot_cos = F(0);
|
|
rot_sin = F(1);
|
|
rot_dx = F(height);
|
|
rot_dy = F(0);
|
|
break;
|
|
case RR_Rotate_180:
|
|
f_rot_cos = -1;
|
|
f_rot_sin = 0;
|
|
f_rot_dx = width;
|
|
f_rot_dy = height;
|
|
rot_cos = F(~0u);
|
|
rot_sin = F(0);
|
|
rot_dx = F(width);
|
|
rot_dy = F(height);
|
|
break;
|
|
case RR_Rotate_270:
|
|
f_rot_cos = 0;
|
|
f_rot_sin = -1;
|
|
f_rot_dx = 0;
|
|
f_rot_dy = width;
|
|
rot_cos = F(0);
|
|
rot_sin = F(~0u);
|
|
rot_dx = F(0);
|
|
rot_dy = F(width);
|
|
break;
|
|
}
|
|
|
|
pixman_transform_rotate(transform, &inverse, rot_cos, rot_sin);
|
|
pixman_transform_translate(transform, &inverse, rot_dx, rot_dy);
|
|
pixman_f_transform_rotate(f_transform, f_inverse, f_rot_cos, f_rot_sin);
|
|
pixman_f_transform_translate(f_transform, f_inverse, f_rot_dx,
|
|
f_rot_dy);
|
|
|
|
/* reflection */
|
|
f_scale_x = 1;
|
|
f_scale_dx = 0;
|
|
f_scale_y = 1;
|
|
f_scale_dy = 0;
|
|
scale_x = F(1);
|
|
scale_dx = 0;
|
|
scale_y = F(1);
|
|
scale_dy = 0;
|
|
if (rotation & RR_Reflect_X) {
|
|
f_scale_x = -1;
|
|
scale_x = F(~0u);
|
|
if (rotation & (RR_Rotate_0 | RR_Rotate_180)) {
|
|
f_scale_dx = width;
|
|
scale_dx = F(width);
|
|
}
|
|
else {
|
|
f_scale_dx = height;
|
|
scale_dx = F(height);
|
|
}
|
|
}
|
|
if (rotation & RR_Reflect_Y) {
|
|
f_scale_y = -1;
|
|
scale_y = F(~0u);
|
|
if (rotation & (RR_Rotate_0 | RR_Rotate_180)) {
|
|
f_scale_dy = height;
|
|
scale_dy = F(height);
|
|
}
|
|
else {
|
|
f_scale_dy = width;
|
|
scale_dy = F(width);
|
|
}
|
|
}
|
|
|
|
pixman_transform_scale(transform, &inverse, scale_x, scale_y);
|
|
pixman_f_transform_scale(f_transform, f_inverse, f_scale_x, f_scale_y);
|
|
pixman_transform_translate(transform, &inverse, scale_dx, scale_dy);
|
|
pixman_f_transform_translate(f_transform, f_inverse, f_scale_dx,
|
|
f_scale_dy);
|
|
}
|
|
|
|
#ifdef RANDR_12_INTERFACE
|
|
if (rr_transform) {
|
|
if (!pixman_transform_multiply
|
|
(transform, &rr_transform->transform, transform))
|
|
overflow = TRUE;
|
|
pixman_f_transform_multiply(f_transform, &rr_transform->f_transform,
|
|
f_transform);
|
|
pixman_f_transform_multiply(f_inverse, f_inverse,
|
|
&rr_transform->f_inverse);
|
|
}
|
|
#endif
|
|
/*
|
|
* Compute the class of the resulting transform
|
|
*/
|
|
if (!overflow && pixman_transform_is_identity(transform)) {
|
|
pixman_transform_init_translate(transform, F(x), F(y));
|
|
|
|
pixman_f_transform_init_translate(f_transform, x, y);
|
|
pixman_f_transform_init_translate(f_inverse, -x, -y);
|
|
return FALSE;
|
|
}
|
|
else {
|
|
pixman_f_transform_translate(f_transform, f_inverse, x, y);
|
|
if (!pixman_transform_translate(transform, &inverse, F(x), F(y)))
|
|
overflow = TRUE;
|
|
if (overflow) {
|
|
struct pixman_f_transform f_scaled;
|
|
|
|
f_scaled = *f_transform;
|
|
RRTransformRescale(&f_scaled, 16384.0);
|
|
pixman_transform_from_pixman_f_transform(transform, &f_scaled);
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|