📄 rotate.c
字号:
/* * rotate.c * * Module for handling image rotation. * * Copyright 2004-2005, Per Jonsson (per@pjd.nu) * * This software is distributed under the GNU Public license * Version 2. See also the file 'COPYING'. * * Image rotation is a feature of Motion that can be used when the * camera is mounted upside-down or on the side. The module only * supports rotation in multiples of 90 degrees. Using rotation * increases the Motion CPU usage slightly. * * Version history: * v6 (29-Aug-2005) - simplified the code as Motion now requires * that width and height are multiples of 16 * v5 (3-Aug-2005) - cleanup in code comments * - better adherence to coding standard * - fix for __bswap_32 macro collision * - fixed bug where initialization would be * incomplete for invalid degrees of rotation * - now uses motion_log for error reporting * v4 (26-Oct-2004) - new fix for width/height from imgs/conf due to * earlier misinterpretation * v3 (11-Oct-2004) - cleanup of width/height from imgs/conf * v2 (26-Sep-2004) - separation of capture/internal dimensions * - speed optimization, including bswap * v1 (28-Aug-2004) - initial version */#include "rotate.h"#ifndef __uint32/** * We don't have a 32-bit unsigned integer type, so define it, given * a 32-bit type was found by configure. */# ifdef TYPE_32BITtypedef unsigned TYPE_32BIT __uint32;# else# error "Failed to find a 32-bit integer type."# endif#endif/*============================================================================= Start of code from bits/byteswap.h =============================================================================*//** * The code below is copied (with modification) from bits/byteswap.h. It provides * a macro/function named rot__bswap_32 that swaps the bytes in a 32-bit integer, * preferably using the bswap assembler instruction if configure found support * for it. * * It would be neater to simply include byteswap.h and use the bswap_32 macro * defined there, but the problem is that the bswap asm instruction would then * only be used for certain processor architectures, excluding athlon (and * probably athlon64 as well). Moreover, byteswap.h doesn't seem to exist on * FreeBSD. So, we rely on the HAVE_BSWAP macro defined by configure instead. * * Note that the macro names have been prefixed with "rot" in order to avoid * collision since we have the include chain rotate.h -> motion.h -> netcam.h -> * netinet/in.h -> ... -> byteswap.h -> bits/byteswap.h. *//* Swap bytes in 32 bit value. This is used as a fallback and for constants. */#define rot__bswap_constant_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))#ifdef __GNUC__# if (__GNUC__ >= 2) && (i386 || __i386 || __i386__)/* We're on an Intel-compatible platform, so we can use inline Intel assembler * for the swapping. */# ifndef HAVE_BSWAP/* Bswap is not available, we have to use three instructions instead. */# define rot__bswap_32(x) \ (__extension__ \ ({ register __uint32 __v, __x = (x); \ if (__builtin_constant_p (__x)) \ __v = rot__bswap_constant_32 (__x); \ else \ __asm__ ("rorw $8, %w0;" \ "rorl $16, %0;" \ "rorw $8, %w0" \ : "=r" (__v) \ : "0" (__x) \ : "cc"); \ __v; }))# else# define rot__bswap_32(x) \ (__extension__ \ ({ register __uint32 __v, __x = (x); \ if (__builtin_constant_p (__x)) \ __v = rot__bswap_constant_32 (__x); \ else \ __asm__ ("bswap %0" : "=r" (__v) : "0" (__x)); \ __v; }))# endif# else/* Non-Intel platform or too old version of gcc. */# define rot__bswap_32(x) \ (__extension__ \ ({ register __uint32 __x = (x); \ rot__bswap_constant_32 (__x); }))# endif#else/* Not a GNU compiler. */static inline __uint32 rot__bswap_32(__uint32 __bsx) { return __bswap_constant_32 (__bsx);}#endif/*============================================================================= End of code from bits/byteswap.h =============================================================================*//* Finally define a macro with a more appropriate name, to be used below. */#define swap_bytes(x) rot__bswap_32(x)/** * reverse_inplace_quad * * Reverses a block of memory in-place, 4 bytes at a time. This function * requires the __uint32 type, which is 32 bits wide. * * Parameters: * * src - the memory block to reverse * size - the size (in bytes) of the memory block * * Returns: nothing */static void reverse_inplace_quad(unsigned char *src, int size) { __uint32 *nsrc = (__uint32 *)src; /* first quad */ __uint32 *ndst = (__uint32 *)(src + size - 4); /* last quad */ register __uint32 tmp; while (nsrc < ndst) { tmp = swap_bytes(*ndst); *ndst-- = swap_bytes(*nsrc); *nsrc++ = tmp; }}/** * rot90cw * * Performs a 90 degrees clockwise rotation of the memory block pointed to * by src. The rotation is NOT performed in-place; dst must point to a * receiving memory block the same size as src. * * Parameters: * * src - pointer to the memory block (image) to rotate clockwise * dst - where to put the rotated memory block * size - the size (in bytes) of the memory blocks (both src and dst) * width - the width of the memory block when seen as an image * height - the height of the memory block when seen as an image * * Returns: nothing */static void rot90cw(unsigned char *src, register unsigned char *dst, int size, int width, int height) { unsigned char *endp; register unsigned char *base; int j; endp = src + size; for (base = endp - width; base < endp; base++) { src = base; for (j = 0; j < height; j++, src -= width) { *dst++ = *src; } }}/** * rot90ccw * * Performs a 90 degrees counterclockwise rotation of the memory block pointed * to by src. The rotation is not performed in-place; dst must point to a * receiving memory block the same size as src. * * Parameters: * * src - pointer to the memory block (image) to rotate counterclockwise * dst - where to put the rotated memory block * size - the size (in bytes) of the memory blocks (both src and dst) * width - the width of the memory block when seen as an image * height - the height of the memory block when seen as an image * * Returns: nothing */static inline void rot90ccw(unsigned char *src, register unsigned char *dst, int size, int width, int height) { unsigned char *endp; register unsigned char *base; int j; endp = src + size; dst = dst + size - 1; for(base = endp - width; base < endp; base++) { src = base; for(j = 0; j < height; j++, src -= width) { *dst-- = *src; } }}/** * rotate_init * * Initializes rotation data - allocates memory and determines which function * to use for 180 degrees rotation. * * Parameters: * * cnt - the current thread's context structure * * Returns: nothing */void rotate_init(struct context *cnt) { int size; /* Make sure temp_buf isn't freed if it hasn't been allocated. */ cnt->rotate_data.temp_buf = NULL; /* Assign the value in conf.rotate_deg to rotate_data.degrees. This way, * we have a value that is safe from changes caused by motion-control. */ if((cnt->conf.rotate_deg % 90) > 0) { motion_log(LOG_ERR, 0, "Config option \"rotate\" not a multiple of 90: %d", cnt->conf.rotate_deg); cnt->conf.rotate_deg = 0; /* disable rotation */ cnt->rotate_data.degrees = 0; /* force return below */ } else { cnt->rotate_data.degrees = cnt->conf.rotate_deg % 360; /* range: 0..359 */ } /* Upon entrance to this function, imgs.width and imgs.height contain the * capture dimensions (as set in the configuration file, or read from a * netcam source). * * If rotating 90 or 270 degrees, the capture dimensions and output dimensions * are not the same. Capture dimensions will be contained in cap_width and * cap_height in cnt->rotate_data, while output dimensions will be contained * in imgs.width and imgs.height. */ /* 1. Transfer capture dimensions into cap_width and cap_height. */ cnt->rotate_data.cap_width = cnt->imgs.width; cnt->rotate_data.cap_height = cnt->imgs.height; if((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) { /* 2. "Swap" imgs.width and imgs.height. */ cnt->imgs.width = cnt->rotate_data.cap_height; cnt->imgs.height = cnt->rotate_data.cap_width; } /* If we're not rotating, let's exit once we have setup the capture dimensions * and output dimensions properly. */ if(cnt->rotate_data.degrees == 0) { return; } switch(cnt->imgs.type) { case VIDEO_PALETTE_YUV420P: /* For YUV 4:2:0 planar, the memory block used for 90/270 degrees * rotation needs to be width x height x 1.5 bytes large. */ size = cnt->imgs.width * cnt->imgs.height * 3 / 2; break; case VIDEO_PALETTE_GREY: /* For greyscale, the memory block used for 90/270 degrees rotation * needs to be width x height bytes large. */ size = cnt->imgs.width * cnt->imgs.height; break; default: cnt->rotate_data.degrees = 0; motion_log(LOG_ERR, 0, "Unsupported palette (%d), rotation is disabled", cnt->imgs.type); return; } /* Allocate memory if rotating 90 or 270 degrees, because those rotations * cannot be performed in-place (they can, but it would be too slow). */ if((cnt->rotate_data.degrees == 90) || (cnt->rotate_data.degrees == 270)) { cnt->rotate_data.temp_buf = mymalloc(size); }}/** * rotate_deinit * * Frees resources previously allocated by rotate_init. * * Parameters: * * cnt - the current thread's context structure * * Returns: nothing */void rotate_deinit(struct context *cnt) { if(cnt->rotate_data.temp_buf) { free(cnt->rotate_data.temp_buf); }}/** * rotate_map * * Main entry point for rotation. This is the function that is called from * video.c/video_freebsd.c to perform the rotation. * * Parameters: * * map - pointer to the image/data to rotate * cnt - the current thread's context structure * * Returns: * * 0 - success * -1 - failure (shouldn't happen) */int rotate_map(struct context *cnt, unsigned char *map){ /* The image format is either YUV 4:2:0 planar, in which case the pixel * data is divided in three parts: * Y - width x height bytes * U - width x height / 4 bytes * V - as U * or, it is in greyscale, in which case the pixel data simply consists * of width x height bytes. */ int wh, wh4 = 0, w2 = 0, h2 = 0; /* width*height, width*height/4 etc. */ int size, deg; int width, height; deg = cnt->rotate_data.degrees; width = cnt->rotate_data.cap_width; height = cnt->rotate_data.cap_height; /* Pre-calculate some stuff: * wh - size of the Y plane, or the entire greyscale image * size - size of the entire memory block * wh4 - size of the U plane, and the V plane * w2 - width of the U plane, and the V plane * h2 - as w2, but height instead */ wh = width * height; if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { size = wh * 3 / 2; wh4 = wh / 4; w2 = width / 2; h2 = height / 2; } else { /* VIDEO_PALETTE_GREY */ size = wh; } switch (deg) { case 90: /* first do the Y part */ rot90cw(map, cnt->rotate_data.temp_buf, wh, width, height); if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { /* then do U and V */ rot90cw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); rot90cw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, wh4, w2, h2); } /* then copy back from the temp buffer to map */ memcpy(map, cnt->rotate_data.temp_buf, size); break; case 180: /* 180 degrees is easy - just reverse the data within * Y, U and V. */ reverse_inplace_quad(map, wh); if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { reverse_inplace_quad(map + wh, wh4); reverse_inplace_quad(map + wh + wh4, wh4); } break; case 270: /* first do the Y part */ rot90ccw(map, cnt->rotate_data.temp_buf, wh, width, height); if(cnt->imgs.type == VIDEO_PALETTE_YUV420P) { /* then do U and V */ rot90ccw(map + wh, cnt->rotate_data.temp_buf + wh, wh4, w2, h2); rot90ccw(map + wh + wh4, cnt->rotate_data.temp_buf + wh + wh4, wh4, w2, h2); } /* then copy back from the temp buffer to map */ memcpy(map, cnt->rotate_data.temp_buf, size); break; default: /* invalid */ return -1; } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -