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 
26 void
RRTransformInit(RRTransformPtr transform)27 RRTransformInit(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 
37 void
RRTransformFini(RRTransformPtr transform)38 RRTransformFini(RRTransformPtr transform)
39 {
40     free(transform->params);
41 }
42 
43 Bool
RRTransformEqual(RRTransformPtr a,RRTransformPtr b)44 RRTransformEqual(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 
65 Bool
RRTransformSetFilter(RRTransformPtr dst,PictFilterPtr filter,xFixed * params,int nparams,int width,int height)66 RRTransformSetFilter(RRTransformPtr dst,
67                      PictFilterPtr filter,
68                      xFixed * params, int nparams, int width, int height)
69 {
70     xFixed *new_params;
71 
72     if (nparams) {
73         new_params = xallocarray(nparams, sizeof(xFixed));
74         if (!new_params)
75             return FALSE;
76         memcpy(new_params, params, nparams * sizeof(xFixed));
77     }
78     else
79         new_params = NULL;
80     free(dst->params);
81     dst->filter = filter;
82     dst->params = new_params;
83     dst->nparams = nparams;
84     dst->width = width;
85     dst->height = height;
86     return TRUE;
87 }
88 
89 Bool
RRTransformCopy(RRTransformPtr dst,RRTransformPtr src)90 RRTransformCopy(RRTransformPtr dst, RRTransformPtr src)
91 {
92     if (src && pixman_transform_is_identity(&src->transform))
93         src = NULL;
94 
95     if (src) {
96         if (!RRTransformSetFilter(dst, src->filter,
97                                   src->params, src->nparams, src->width,
98                                   src->height))
99             return FALSE;
100         dst->transform = src->transform;
101         dst->f_transform = src->f_transform;
102         dst->f_inverse = src->f_inverse;
103     }
104     else {
105         if (!RRTransformSetFilter(dst, NULL, NULL, 0, 0, 0))
106             return FALSE;
107         pixman_transform_init_identity(&dst->transform);
108         pixman_f_transform_init_identity(&dst->f_transform);
109         pixman_f_transform_init_identity(&dst->f_inverse);
110     }
111     return TRUE;
112 }
113 
114 #define F(x)	IntToxFixed(x)
115 
116 static void
RRTransformRescale(struct pixman_f_transform * f_transform,double limit)117 RRTransformRescale(struct pixman_f_transform *f_transform, double limit)
118 {
119     double max = 0, v, scale;
120     int i, j;
121 
122     for (j = 0; j < 3; j++)
123         for (i = 0; i < 3; i++)
124             if ((v = fabs(f_transform->m[j][i])) > max)
125                 max = v;
126     scale = limit / max;
127     for (j = 0; j < 3; j++)
128         for (i = 0; i < 3; i++)
129             f_transform->m[j][i] *= scale;
130 }
131 
132 /*
133  * Compute the complete transformation matrix including
134  * client-specified transform, rotation/reflection values and the crtc
135  * offset.
136  *
137  * Return TRUE if the resulting transform is not a simple translation.
138  */
139 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)140 RRTransformCompute(int x,
141                    int y,
142                    int width,
143                    int height,
144                    Rotation rotation,
145                    RRTransformPtr rr_transform,
146                    PictTransformPtr transform,
147                    struct pixman_f_transform *f_transform,
148                    struct pixman_f_transform *f_inverse)
149 {
150     PictTransform t_transform, inverse;
151     struct pixman_f_transform tf_transform, tf_inverse;
152     Bool overflow = FALSE;
153 
154     if (!transform)
155         transform = &t_transform;
156     if (!f_transform)
157         f_transform = &tf_transform;
158     if (!f_inverse)
159         f_inverse = &tf_inverse;
160 
161     pixman_transform_init_identity(transform);
162     pixman_transform_init_identity(&inverse);
163     pixman_f_transform_init_identity(f_transform);
164     pixman_f_transform_init_identity(f_inverse);
165     if (rotation != RR_Rotate_0) {
166         double f_rot_cos, f_rot_sin, f_rot_dx, f_rot_dy;
167         double f_scale_x, f_scale_y, f_scale_dx, f_scale_dy;
168         xFixed rot_cos, rot_sin, rot_dx, rot_dy;
169         xFixed scale_x, scale_y, scale_dx, scale_dy;
170 
171         /* rotation */
172         switch (rotation & 0xf) {
173         default:
174         case RR_Rotate_0:
175             f_rot_cos = 1;
176             f_rot_sin = 0;
177             f_rot_dx = 0;
178             f_rot_dy = 0;
179             rot_cos = F(1);
180             rot_sin = F(0);
181             rot_dx = F(0);
182             rot_dy = F(0);
183             break;
184         case RR_Rotate_90:
185             f_rot_cos = 0;
186             f_rot_sin = 1;
187             f_rot_dx = height;
188             f_rot_dy = 0;
189             rot_cos = F(0);
190             rot_sin = F(1);
191             rot_dx = F(height);
192             rot_dy = F(0);
193             break;
194         case RR_Rotate_180:
195             f_rot_cos = -1;
196             f_rot_sin = 0;
197             f_rot_dx = width;
198             f_rot_dy = height;
199             rot_cos = F(~0u);
200             rot_sin = F(0);
201             rot_dx = F(width);
202             rot_dy = F(height);
203             break;
204         case RR_Rotate_270:
205             f_rot_cos = 0;
206             f_rot_sin = -1;
207             f_rot_dx = 0;
208             f_rot_dy = width;
209             rot_cos = F(0);
210             rot_sin = F(~0u);
211             rot_dx = F(0);
212             rot_dy = F(width);
213             break;
214         }
215 
216         pixman_transform_rotate(transform, &inverse, rot_cos, rot_sin);
217         pixman_transform_translate(transform, &inverse, rot_dx, rot_dy);
218         pixman_f_transform_rotate(f_transform, f_inverse, f_rot_cos, f_rot_sin);
219         pixman_f_transform_translate(f_transform, f_inverse, f_rot_dx,
220                                      f_rot_dy);
221 
222         /* reflection */
223         f_scale_x = 1;
224         f_scale_dx = 0;
225         f_scale_y = 1;
226         f_scale_dy = 0;
227         scale_x = F(1);
228         scale_dx = 0;
229         scale_y = F(1);
230         scale_dy = 0;
231         if (rotation & RR_Reflect_X) {
232             f_scale_x = -1;
233             scale_x = F(~0u);
234             if (rotation & (RR_Rotate_0 | RR_Rotate_180)) {
235                 f_scale_dx = width;
236                 scale_dx = F(width);
237             }
238             else {
239                 f_scale_dx = height;
240                 scale_dx = F(height);
241             }
242         }
243         if (rotation & RR_Reflect_Y) {
244             f_scale_y = -1;
245             scale_y = F(~0u);
246             if (rotation & (RR_Rotate_0 | RR_Rotate_180)) {
247                 f_scale_dy = height;
248                 scale_dy = F(height);
249             }
250             else {
251                 f_scale_dy = width;
252                 scale_dy = F(width);
253             }
254         }
255 
256         pixman_transform_scale(transform, &inverse, scale_x, scale_y);
257         pixman_f_transform_scale(f_transform, f_inverse, f_scale_x, f_scale_y);
258         pixman_transform_translate(transform, &inverse, scale_dx, scale_dy);
259         pixman_f_transform_translate(f_transform, f_inverse, f_scale_dx,
260                                      f_scale_dy);
261     }
262 
263 #ifdef RANDR_12_INTERFACE
264     if (rr_transform) {
265         if (!pixman_transform_multiply
266             (transform, &rr_transform->transform, transform))
267             overflow = TRUE;
268         pixman_f_transform_multiply(f_transform, &rr_transform->f_transform,
269                                     f_transform);
270         pixman_f_transform_multiply(f_inverse, f_inverse,
271                                     &rr_transform->f_inverse);
272     }
273 #endif
274     /*
275      * Compute the class of the resulting transform
276      */
277     if (!overflow && pixman_transform_is_identity(transform)) {
278         pixman_transform_init_translate(transform, F(x), F(y));
279 
280         pixman_f_transform_init_translate(f_transform, x, y);
281         pixman_f_transform_init_translate(f_inverse, -x, -y);
282         return FALSE;
283     }
284     else {
285         pixman_f_transform_translate(f_transform, f_inverse, x, y);
286         if (!pixman_transform_translate(transform, &inverse, F(x), F(y)))
287             overflow = TRUE;
288         if (overflow) {
289             struct pixman_f_transform f_scaled;
290 
291             f_scaled = *f_transform;
292             RRTransformRescale(&f_scaled, 16384.0);
293             pixman_transform_from_pixman_f_transform(transform, &f_scaled);
294         }
295         return TRUE;
296     }
297 }
298