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