1/*
2 * Copyright © 2007 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "randrstr.h"
24#include "rrtransform.h"
25
26void
27RRTransformInit (RRTransformPtr transform)
28{
29    pixman_transform_init_identity (&transform->transform);
30    pixman_f_transform_init_identity (&transform->f_transform);
31    pixman_f_transform_init_identity (&transform->f_inverse);
32    transform->filter = NULL;
33    transform->params = NULL;
34    transform->nparams = 0;
35}
36
37void
38RRTransformFini (RRTransformPtr transform)
39{
40    free(transform->params);
41}
42
43Bool
44RRTransformEqual (RRTransformPtr a, RRTransformPtr b)
45{
46    if (a && pixman_transform_is_identity (&a->transform))
47	a = NULL;
48    if (b && pixman_transform_is_identity (&b->transform))
49	b = NULL;
50    if (a == NULL && b == NULL)
51	return TRUE;
52    if (a == NULL || b == NULL)
53	return FALSE;
54    if (memcmp (&a->transform, &b->transform, sizeof (a->transform)) != 0)
55	return FALSE;
56    if (a->filter != b->filter)
57	return FALSE;
58    if (a->nparams != b->nparams)
59	return FALSE;
60    if (memcmp (a->params, b->params, a->nparams * sizeof (xFixed)) != 0)
61	return FALSE;
62    return TRUE;
63}
64
65Bool
66RRTransformSetFilter (RRTransformPtr	dst,
67		      PictFilterPtr	filter,
68		      xFixed		*params,
69		      int		nparams,
70		      int		width,
71		      int		height)
72{
73    xFixed  *new_params;
74
75    if (nparams)
76    {
77	new_params = malloc(nparams * sizeof (xFixed));
78	if (!new_params)
79	    return FALSE;
80	memcpy (new_params, params, nparams * sizeof (xFixed));
81    }
82    else
83	new_params = NULL;
84    free(dst->params);
85    dst->filter = filter;
86    dst->params = new_params;
87    dst->nparams = nparams;
88    dst->width = width;
89    dst->height = height;
90    return TRUE;
91}
92
93Bool
94RRTransformCopy (RRTransformPtr dst, RRTransformPtr src)
95{
96    if (src && pixman_transform_is_identity (&src->transform))
97	src = NULL;
98
99    if (src)
100    {
101	if (!RRTransformSetFilter (dst, src->filter,
102				   src->params, src->nparams, src->width, src->height))
103	    return FALSE;
104	dst->transform = src->transform;
105	dst->f_transform = src->f_transform;
106	dst->f_inverse = src->f_inverse;
107    }
108    else
109    {
110	if (!RRTransformSetFilter (dst, NULL, NULL, 0, 0, 0))
111	    return FALSE;
112	pixman_transform_init_identity (&dst->transform);
113	pixman_f_transform_init_identity (&dst->f_transform);
114	pixman_f_transform_init_identity (&dst->f_inverse);
115    }
116    return TRUE;
117}
118
119#define F(x)	IntToxFixed(x)
120
121static void
122RRTransformRescale(struct pixman_f_transform *f_transform, double limit)
123{
124    double max = 0, v, scale;
125    int i, j;
126
127    for (j = 0; j < 3; j++)
128	for (i = 0; i < 3; i++)
129	    if ((v = fabs (f_transform->m[j][i])) > max)
130		max = v;
131    scale = limit / max;
132    for (j = 0; j < 3; j++)
133	for (i = 0; i < 3; i++)
134	    f_transform->m[j][i] *= scale;
135}
136
137/*
138 * Compute the complete transformation matrix including
139 * client-specified transform, rotation/reflection values and the crtc
140 * offset.
141 *
142 * Return TRUE if the resulting transform is not a simple translation.
143 */
144Bool
145RRTransformCompute (int			    x,
146		    int			    y,
147		    int			    width,
148		    int			    height,
149		    Rotation		    rotation,
150		    RRTransformPtr	    rr_transform,
151
152		    PictTransformPtr	    transform,
153		    struct pixman_f_transform *f_transform,
154		    struct pixman_f_transform *f_inverse)
155{
156    PictTransform	    t_transform, inverse;
157    struct pixman_f_transform tf_transform, tf_inverse;
158    Bool		    overflow = FALSE;
159
160    if (!transform) transform = &t_transform;
161    if (!f_transform) f_transform = &tf_transform;
162    if (!f_inverse) f_inverse = &tf_inverse;
163
164    pixman_transform_init_identity (transform);
165    pixman_transform_init_identity (&inverse);
166    pixman_f_transform_init_identity (f_transform);
167    pixman_f_transform_init_identity (f_inverse);
168    if (rotation != RR_Rotate_0)
169    {
170	double	f_rot_cos, f_rot_sin, f_rot_dx, f_rot_dy;
171	double	f_scale_x, f_scale_y, f_scale_dx, f_scale_dy;
172	xFixed	rot_cos, rot_sin, rot_dx, rot_dy;
173	xFixed	scale_x, scale_y, scale_dx, scale_dy;
174
175	/* rotation */
176	switch (rotation & 0xf) {
177	default:
178	case RR_Rotate_0:
179	    f_rot_cos = 1;	    f_rot_sin = 0;
180	    f_rot_dx  = 0;	    f_rot_dy  = 0;
181	    rot_cos = F ( 1);	    rot_sin = F ( 0);
182	    rot_dx  = F ( 0);	    rot_dy  = F ( 0);
183	    break;
184	case RR_Rotate_90:
185	    f_rot_cos = 0;	    f_rot_sin = 1;
186	    f_rot_dx  = height;	    f_rot_dy  = 0;
187	    rot_cos = F ( 0);	    rot_sin = F ( 1);
188	    rot_dx =  F ( height);  rot_dy  = F (0);
189	    break;
190	case RR_Rotate_180:
191	    f_rot_cos = -1;	    f_rot_sin = 0;
192	    f_rot_dx  = width;	    f_rot_dy  = height;
193	    rot_cos = F (-1);	    rot_sin = F ( 0);
194	    rot_dx  = F (width);   rot_dy  = F ( height);
195	    break;
196	case RR_Rotate_270:
197	    f_rot_cos = 0;	    f_rot_sin = -1;
198	    f_rot_dx  = 0;	    f_rot_dy  = width;
199	    rot_cos = F ( 0);	    rot_sin = F (-1);
200	    rot_dx  = F ( 0);	    rot_dy  = F ( width);
201	    break;
202	}
203
204	pixman_transform_rotate (transform, &inverse, rot_cos, rot_sin);
205	pixman_transform_translate (transform, &inverse, rot_dx, rot_dy);
206	pixman_f_transform_rotate (f_transform, f_inverse, f_rot_cos, f_rot_sin);
207	pixman_f_transform_translate (f_transform, f_inverse, f_rot_dx, f_rot_dy);
208
209	/* reflection */
210	f_scale_x = 1;
211	f_scale_dx = 0;
212	f_scale_y = 1;
213	f_scale_dy = 0;
214	scale_x = F (1);
215	scale_dx = 0;
216	scale_y = F (1);
217	scale_dy = 0;
218	if (rotation & RR_Reflect_X)
219	{
220	    f_scale_x = -1;
221	    scale_x = F(-1);
222	    if (rotation & (RR_Rotate_0|RR_Rotate_180)) {
223		f_scale_dx = width;
224		scale_dx = F(width);
225	    } else {
226		f_scale_dx = height;
227		scale_dx = F(height);
228	    }
229	}
230	if (rotation & RR_Reflect_Y)
231	{
232	    f_scale_y = -1;
233	    scale_y = F(-1);
234	    if (rotation & (RR_Rotate_0|RR_Rotate_180)) {
235		f_scale_dy = height;
236		scale_dy = F(height);
237	    } else {
238		f_scale_dy = width;
239		scale_dy = F(width);
240	    }
241	}
242
243	pixman_transform_scale (transform, &inverse, scale_x, scale_y);
244	pixman_f_transform_scale (f_transform, f_inverse, f_scale_x, f_scale_y);
245	pixman_transform_translate (transform, &inverse, scale_dx, scale_dy);
246	pixman_f_transform_translate (f_transform, f_inverse, f_scale_dx, f_scale_dy);
247    }
248
249#ifdef RANDR_12_INTERFACE
250    if (rr_transform)
251    {
252        if (!pixman_transform_multiply (transform, &rr_transform->transform, transform))
253	    overflow = TRUE;
254	pixman_f_transform_multiply (f_transform, &rr_transform->f_transform, f_transform);
255	pixman_f_transform_multiply (f_inverse, f_inverse, &rr_transform->f_inverse);
256    }
257#endif
258    /*
259     * Compute the class of the resulting transform
260     */
261    if (!overflow && pixman_transform_is_identity (transform))
262    {
263	pixman_transform_init_translate (transform, F ( x), F ( y));
264
265	pixman_f_transform_init_translate (f_transform,  x,  y);
266	pixman_f_transform_init_translate (f_inverse,   -x, -y);
267	return FALSE;
268    }
269    else
270    {
271	pixman_f_transform_translate (f_transform, f_inverse, x, y);
272	if (!pixman_transform_translate (transform, &inverse, F(x), F(y)))
273	    overflow = TRUE;
274	if (overflow)
275	{
276	    struct pixman_f_transform f_scaled;
277	    f_scaled = *f_transform;
278	    RRTransformRescale(&f_scaled, 16384.0);
279	    pixman_transform_from_pixman_f_transform(transform, &f_scaled);
280	}
281	return TRUE;
282    }
283}
284