1 /*
2  *    rotate.c
3  *
4  *    Module for handling image rotation.
5  *
6  *    Copyright 2004-2005, Per Jonsson (per@pjd.nu)
7  *
8  *    This software is distributed under the GNU Public license
9  *    Version 2.  See also the file 'COPYING'.
10  *
11  *    Image rotation is a feature of Motion that can be used when the
12  *    camera is mounted upside-down or on the side. The module only
13  *    supports rotation in multiples of 90 degrees. Using rotation
14  *    increases the Motion CPU usage slightly.
15  *
16  *    Version history:
17  *      v6 (29-Aug-2005) - simplified the code as Motion now requires
18  *                         that width and height are multiples of 16
19  *      v5 (3-Aug-2005)  - cleanup in code comments
20  *                       - better adherence to coding standard
21  *                       - fix for __bswap_32 macro collision
22  *                       - fixed bug where initialization would be
23  *                         incomplete for invalid degrees of rotation
24  *                       - now uses MOTION_LOG for error reporting
25  *      v4 (26-Oct-2004) - new fix for width/height from imgs/conf due to
26  *                         earlier misinterpretation
27  *      v3 (11-Oct-2004) - cleanup of width/height from imgs/conf
28  *      v2 (26-Sep-2004) - separation of capture/internal dimensions
29  *                       - speed optimization, including bswap
30  *      v1 (28-Aug-2004) - initial version
31  */
32 #include "translate.h"
33 #include "rotate.h"
34 #include <stdint.h>
35 #if defined(__APPLE__)
36 #include <libkern/OSByteOrder.h>
37 #define bswap_32(x) OSSwapInt32(x)
38 #elif defined(__FreeBSD__) || defined(__DragonFly__)
39 #include <sys/endian.h>
40 #define bswap_32(x) bswap32(x)
41 #elif defined(__OpenBSD__)
42 #include <sys/types.h>
43 #define bswap_32(x) swap32(x)
44 #elif defined(__NetBSD__)
45 #include <sys/bswap.h>
46 #define bswap_32(x) bswap32(x)
47 #else
48 #include <byteswap.h>
49 #endif
50 
51 /**
52  * reverse_inplace_quad
53  *
54  *  Reverses a block of memory in-place, 4 bytes at a time. This function
55  *  requires the uint32_t type, which is 32 bits wide.
56  *
57  * Parameters:
58  *
59  *   src  - the memory block to reverse
60  *   size - the size (in bytes) of the memory block
61  *
62  * Returns: nothing
63  */
reverse_inplace_quad(unsigned char * src,int size)64 static void reverse_inplace_quad(unsigned char *src, int size)
65 {
66     uint32_t *nsrc = (uint32_t *)src;              /* first quad */
67     uint32_t *ndst = (uint32_t *)(src + size - 4); /* last quad */
68     register uint32_t tmp;
69 
70     while (nsrc < ndst) {
71         tmp = bswap_32(*ndst);
72         *ndst-- = bswap_32(*nsrc);
73         *nsrc++ = tmp;
74     }
75 }
76 
flip_inplace_horizontal(unsigned char * src,int width,int height)77 static void flip_inplace_horizontal(unsigned char *src, int width, int height) {
78     uint8_t *nsrc, *ndst;
79     register uint8_t tmp;
80     int l,w;
81 
82     for(l=0; l < height/2; l++) {
83         nsrc = (uint8_t *)(src + l*width);
84         ndst = (uint8_t *)(src + (width*(height-l-1)));
85         for(w=0; w < width; w++) {
86             tmp =*ndst;
87             *ndst++ = *nsrc;
88             *nsrc++ = tmp;
89         }
90     }
91 
92 }
93 
flip_inplace_vertical(unsigned char * src,int width,int height)94 static void flip_inplace_vertical(unsigned char *src, int width, int height)
95 {
96     uint8_t *nsrc, *ndst;
97     register uint8_t tmp;
98     int l;
99 
100     for(l=0; l < height; l++) {
101         nsrc = (uint8_t *)src + l*width;
102         ndst = nsrc + width - 1;
103         while (nsrc < ndst) {
104             tmp = *ndst;
105             *ndst-- = *nsrc;
106             *nsrc++ = tmp;
107         }
108     }
109 }
110 
111 /**
112  * rot90cw
113  *
114  *  Performs a 90 degrees clockwise rotation of the memory block pointed to
115  *  by src. The rotation is NOT performed in-place; dst must point to a
116  *  receiving memory block the same size as src.
117  *
118  * Parameters:
119  *
120  *   src    - pointer to the memory block (image) to rotate clockwise
121  *   dst    - where to put the rotated memory block
122  *   size   - the size (in bytes) of the memory blocks (both src and dst)
123  *   width  - the width of the memory block when seen as an image
124  *   height - the height of the memory block when seen as an image
125  *
126  * Returns: nothing
127  */
rot90cw(unsigned char * src,register unsigned char * dst,int size,int width,int height)128 static void rot90cw(unsigned char *src, register unsigned char *dst, int size,
129                     int width, int height)
130 {
131     unsigned char *endp;
132     register unsigned char *base;
133     int j;
134 
135     endp = src + size;
136     for (base = endp - width; base < endp; base++) {
137         src = base;
138         for (j = 0; j < height; j++, src -= width)
139             *dst++ = *src;
140 
141     }
142 }
143 
144 /**
145  * rot90ccw
146  *
147  *  Performs a 90 degrees counterclockwise rotation of the memory block pointed
148  *  to by src. The rotation is not performed in-place; dst must point to a
149  *  receiving memory block the same size as src.
150  *
151  * Parameters:
152  *
153  *   src    - pointer to the memory block (image) to rotate counterclockwise
154  *   dst    - where to put the rotated memory block
155  *   size   - the size (in bytes) of the memory blocks (both src and dst)
156  *   width  - the width of the memory block when seen as an image
157  *   height - the height of the memory block when seen as an image
158  *
159  * Returns: nothing
160  */
rot90ccw(unsigned char * src,register unsigned char * dst,int size,int width,int height)161 static inline void rot90ccw(unsigned char *src, register unsigned char *dst,
162                             int size, int width, int height)
163 {
164     unsigned char *endp;
165     register unsigned char *base;
166     int j;
167 
168     endp = src + size;
169     dst = dst + size - 1;
170     for (base = endp - width; base < endp; base++) {
171         src = base;
172         for (j = 0; j < height; j++, src -= width)
173             *dst-- = *src;
174 
175     }
176 }
177 
178 /**
179  * rotate_init
180  *
181  *  Initializes rotation data - allocates memory and determines which function
182  *  to use for 180 degrees rotation.
183  *
184  * Parameters:
185  *
186  *   cnt - the current thread's context structure
187  *
188  * Returns: nothing
189  */
rotate_init(struct context * cnt)190 void rotate_init(struct context *cnt){
191     int size_norm, size_high;
192 
193     /* Make sure buffer_norm isn't freed if it hasn't been allocated. */
194     cnt->rotate_data.buffer_norm = NULL;
195     cnt->rotate_data.buffer_high = NULL;
196 
197     /*
198      * Assign the value in conf.rotate to rotate_data.degrees. This way,
199      * we have a value that is safe from changes caused by motion-control.
200      */
201     if ((cnt->conf.rotate % 90) > 0) {
202         MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO
203             ,_("Config option \"rotate\" not a multiple of 90: %d")
204             ,cnt->conf.rotate);
205         cnt->conf.rotate = 0;     /* Disable rotation. */
206         cnt->rotate_data.degrees = 0; /* Force return below. */
207     } else {
208         cnt->rotate_data.degrees = cnt->conf.rotate % 360; /* Range: 0..359 */
209     }
210 
211     if (cnt->conf.flip_axis[0]=='h') {
212         cnt->rotate_data.axis = FLIP_TYPE_HORIZONTAL;
213     } else if (cnt->conf.flip_axis[0]=='v') {
214         cnt->rotate_data.axis = FLIP_TYPE_VERTICAL;
215     } else {
216         cnt->rotate_data.axis = FLIP_TYPE_NONE;
217     }
218 
219     /*
220      * Upon entrance to this function, imgs.width and imgs.height contain the
221      * capture dimensions (as set in the configuration file, or read from a
222      * netcam source).
223      *
224      * If rotating 90 or 270 degrees, the capture dimensions and output dimensions
225      * are not the same. Capture dimensions will be contained in capture_width_norm and
226      * capture_height_norm in cnt->rotate_data, while output dimensions will be contained
227      * in imgs.width and imgs.height.
228      */
229 
230     /* 1. Transfer capture dimensions into capture_width_norm and capture_height_norm. */
231     cnt->rotate_data.capture_width_norm  = cnt->imgs.width;
232     cnt->rotate_data.capture_height_norm = cnt->imgs.height;
233 
234     cnt->rotate_data.capture_width_high  = cnt->imgs.width_high;
235     cnt->rotate_data.capture_height_high = cnt->imgs.height_high;
236 
237     size_norm = cnt->imgs.width * cnt->imgs.height * 3 / 2;
238     size_high = cnt->imgs.width_high * cnt->imgs.height_high * 3 / 2;
239 
240     if ((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) {
241         /* 2. "Swap" imgs.width and imgs.height. */
242         cnt->imgs.width = cnt->rotate_data.capture_height_norm;
243         cnt->imgs.height = cnt->rotate_data.capture_width_norm;
244         if (size_high > 0 ) {
245             cnt->imgs.width_high = cnt->rotate_data.capture_height_high;
246             cnt->imgs.height_high = cnt->rotate_data.capture_width_high;
247         }
248     }
249 
250     /*
251      * If we're not rotating, let's exit once we have setup the capture dimensions
252      * and output dimensions properly.
253      */
254     if (cnt->rotate_data.degrees == 0) return;
255 
256     /*
257      * Allocate memory if rotating 90 or 270 degrees, because those rotations
258      * cannot be performed in-place (they can, but it would be too slow).
259      */
260     if ((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)){
261         cnt->rotate_data.buffer_norm = mymalloc(size_norm);
262         if (size_high > 0 ) cnt->rotate_data.buffer_high = mymalloc(size_high);
263     }
264 
265 }
266 
267 /**
268  * rotate_deinit
269  *
270  *  Frees resources previously allocated by rotate_init.
271  *
272  * Parameters:
273  *
274  *   cnt - the current thread's context structure
275  *
276  * Returns: nothing
277  */
rotate_deinit(struct context * cnt)278 void rotate_deinit(struct context *cnt){
279 
280     if (cnt->rotate_data.buffer_norm)
281         free(cnt->rotate_data.buffer_norm);
282 
283     if (cnt->rotate_data.buffer_high)
284         free(cnt->rotate_data.buffer_high);
285 }
286 
287 /**
288  * rotate_map
289  *
290  *  Main entry point for rotation.
291  *
292  * Parameters:
293  *
294  *   img_data- pointer to the image data to rotate
295  *   cnt - the current thread's context structure
296  *
297  * Returns:
298  *
299  *   0  - success
300  *   -1 - failure (shouldn't happen)
301  */
rotate_map(struct context * cnt,struct image_data * img_data)302 int rotate_map(struct context *cnt, struct image_data *img_data){
303     /*
304      * The image format is YUV 4:2:0 planar, which has the pixel
305      * data is divided in three parts:
306      *    Y - width x height bytes
307      *    U - width x height / 4 bytes
308      *    V - as U
309      */
310 
311     int indx, indx_max;
312     int wh, wh4 = 0, w2 = 0, h2 = 0;  /* width * height, width * height / 4 etc. */
313     int size, deg;
314     enum FLIP_TYPE axis;
315     int width, height;
316     unsigned char *img;
317     unsigned char *temp_buff;
318 
319     if (cnt->rotate_data.degrees == 0 && cnt->rotate_data.axis == FLIP_TYPE_NONE) return 0;
320 
321     indx = 0;
322     indx_max = 0;
323     if ((cnt->rotate_data.capture_width_high != 0) && (cnt->rotate_data.capture_height_high != 0)) indx_max = 1;
324 
325     while (indx <= indx_max) {
326         deg = cnt->rotate_data.degrees;
327         axis = cnt->rotate_data.axis;
328         wh4 = 0;
329         w2 = 0;
330         h2 = 0;
331         if (indx == 0 ){
332             img = img_data->image_norm;
333             width = cnt->rotate_data.capture_width_norm;
334             height = cnt->rotate_data.capture_height_norm;
335             temp_buff = cnt->rotate_data.buffer_norm;
336         } else {
337             img = img_data->image_high;
338             width = cnt->rotate_data.capture_width_high;
339             height = cnt->rotate_data.capture_height_high;
340             temp_buff = cnt->rotate_data.buffer_high;
341         }
342         /*
343          * Pre-calculate some stuff:
344          *  wh   - size of the Y plane
345          *  size - size of the entire memory block
346          *  wh4  - size of the U plane, and the V plane
347          *  w2   - width of the U plane, and the V plane
348          *  h2   - as w2, but height instead
349          */
350         wh = width * height;
351         size = wh * 3 / 2;
352         wh4 = wh / 4;
353         w2 = width / 2;
354         h2 = height / 2;
355 
356         switch (axis) {
357         case FLIP_TYPE_HORIZONTAL:
358             flip_inplace_horizontal(img,width, height);
359             flip_inplace_horizontal(img + wh, w2, h2);
360             flip_inplace_horizontal(img + wh + wh4, w2, h2);
361             break;
362         case FLIP_TYPE_VERTICAL:
363             flip_inplace_vertical(img,width, height);
364             flip_inplace_vertical(img + wh, w2, h2);
365             flip_inplace_vertical(img + wh + wh4, w2, h2);
366             break;
367         default:
368             break;
369         }
370 
371         switch (deg) {
372         case 90:
373             rot90cw(img, temp_buff, wh, width, height);
374             rot90cw(img + wh, temp_buff + wh, wh4, w2, h2);
375             rot90cw(img + wh + wh4, temp_buff + wh + wh4, wh4, w2, h2);
376             memcpy(img, temp_buff, size);
377             break;
378         case 180:
379             reverse_inplace_quad(img, wh);
380             reverse_inplace_quad(img + wh, wh4);
381             reverse_inplace_quad(img + wh + wh4, wh4);
382             break;
383         case 270:
384             rot90ccw(img, temp_buff, wh, width, height);
385             rot90ccw(img + wh, temp_buff + wh, wh4, w2, h2);
386             rot90ccw(img + wh + wh4, temp_buff + wh + wh4, wh4, w2, h2);
387             memcpy(img, temp_buff, size);
388             break;
389         default:
390             /* Invalid */
391             return -1;
392         }
393             indx++;
394     }
395 
396     return 0;
397 }
398 
399