You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
11 KiB
352 lines
11 KiB
/*****************************************************************************
|
|
* orient.c: image reorientation video conversion
|
|
*****************************************************************************
|
|
* Copyright (C) 2000-2006 VLC authors and VideoLAN
|
|
* Copyright (C) 2010 Laurent Aimar
|
|
* Copyright (C) 2012 Rémi Denis-Courmont
|
|
*
|
|
* Authors: Samuel Hocevar <sam@zoy.org>
|
|
* Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
#include <limits.h>
|
|
#include <stdbit.h>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_cpu.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_filter.h>
|
|
#include <vlc_mouse.h>
|
|
#include <vlc_picture.h>
|
|
|
|
#include "orient.h"
|
|
|
|
#define TRANSFORMS(bits) \
|
|
static void hflip_##bits(void *restrict dst, ptrdiff_t dst_stride, \
|
|
const void *restrict src, ptrdiff_t src_stride, \
|
|
int width, int height) \
|
|
{ \
|
|
const uint##bits##_t *restrict src_pixels = src; \
|
|
uint##bits##_t *restrict dst_pixels = dst; \
|
|
\
|
|
dst_stride /= bits / 8; \
|
|
src_stride /= bits / 8; \
|
|
dst_pixels += width - 1; \
|
|
\
|
|
for (int y = 0; y < height; y++) { \
|
|
for (int x = 0; x < width; x++) \
|
|
dst_pixels[-x] = src_pixels[x]; \
|
|
\
|
|
src_pixels += src_stride; \
|
|
dst_pixels += dst_stride; \
|
|
} \
|
|
} \
|
|
\
|
|
static void transpose_##bits(void *restrict dst, ptrdiff_t dst_stride, \
|
|
const void *restrict src, ptrdiff_t src_stride, \
|
|
int src_width, int src_height) \
|
|
{ \
|
|
const uint##bits##_t *restrict src_pixels = src; \
|
|
uint##bits##_t *restrict dst_pixels = dst; \
|
|
\
|
|
dst_stride /= bits / 8; \
|
|
src_stride /= bits / 8; \
|
|
\
|
|
for (int y = 0; y < src_height; y++) { \
|
|
for (int x = 0; x < src_width; x++) \
|
|
dst_pixels[x * dst_stride] = src_pixels[x]; \
|
|
src_pixels += src_stride; \
|
|
dst_pixels++; \
|
|
} \
|
|
}
|
|
|
|
TRANSFORMS(8)
|
|
TRANSFORMS(16)
|
|
TRANSFORMS(32)
|
|
TRANSFORMS(64)
|
|
|
|
static struct plane_transforms transforms = {
|
|
{ hflip_8, hflip_16, hflip_32, hflip_64, },
|
|
{ transpose_8, transpose_16, transpose_32, transpose_64, },
|
|
};
|
|
|
|
static void hflip(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src, ptrdiff_t src_stride,
|
|
int width, int height, int order)
|
|
{
|
|
transforms.hflip[order](dst, dst_stride, src, src_stride, width, height);
|
|
}
|
|
|
|
static void vflip(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src, ptrdiff_t src_stride,
|
|
int width, int height, int order)
|
|
{
|
|
const unsigned char *src_pixels = src;
|
|
unsigned char *restrict dst_pixels = dst;
|
|
size_t visible_pitch = width << order;
|
|
|
|
dst_pixels += dst_stride * height;
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
dst_pixels -= dst_stride;
|
|
memcpy(dst_pixels, src_pixels, visible_pitch);
|
|
src_pixels += src_stride;
|
|
}
|
|
}
|
|
|
|
static void r180(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src, ptrdiff_t src_stride,
|
|
int width, int height, int order)
|
|
{
|
|
const unsigned char *src_pixels = src;
|
|
|
|
src_pixels += (height - 1) * src_stride;
|
|
src_stride *= -1;
|
|
hflip(dst, dst_stride, src_pixels, src_stride, width, height, order);
|
|
}
|
|
|
|
static void transpose(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src, ptrdiff_t src_stride,
|
|
int src_width, int src_height, int order)
|
|
{
|
|
transforms.transpose[order](dst, dst_stride, src, src_stride,
|
|
src_width, src_height);
|
|
}
|
|
|
|
static void r270(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src, ptrdiff_t src_stride,
|
|
int src_width, int src_height, int order)
|
|
{
|
|
unsigned char *dst_pixels = dst;
|
|
|
|
dst_pixels += (src_width - 1) * dst_stride;
|
|
dst_stride *= -1;
|
|
transpose(dst_pixels, dst_stride, src, src_stride, src_width, src_height,
|
|
order);
|
|
}
|
|
|
|
static void r90(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src, ptrdiff_t src_stride,
|
|
int src_width, int src_height, int order)
|
|
{
|
|
const unsigned char *src_pixels = src;
|
|
|
|
src_pixels += (src_height - 1) * src_stride;
|
|
src_stride *= -1;
|
|
transpose(dst, dst_stride, src_pixels, src_stride, src_width, src_height,
|
|
order);
|
|
}
|
|
|
|
static void antitranspose(void *restrict dst, ptrdiff_t dst_stride,
|
|
const void *restrict src,
|
|
ptrdiff_t src_stride,
|
|
int src_width, int src_height, int order)
|
|
{
|
|
const unsigned char *src_pixels = src;
|
|
|
|
src_pixels += (src_height - 1) * src_stride;
|
|
src_stride *= -1;
|
|
r270(dst, dst_stride, src_pixels, src_stride, src_width, src_height,
|
|
order);
|
|
}
|
|
|
|
typedef void (*transform_description_t)(void *, ptrdiff_t, const void *,
|
|
ptrdiff_t, int, int, int);
|
|
|
|
static const transform_description_t descriptions[] = {
|
|
[TRANSFORM_R90] = r90,
|
|
[TRANSFORM_R180] = r180,
|
|
[TRANSFORM_R270] = r270,
|
|
[TRANSFORM_HFLIP] = hflip,
|
|
[TRANSFORM_VFLIP] = vflip,
|
|
[TRANSFORM_TRANSPOSE] = transpose,
|
|
[TRANSFORM_ANTI_TRANSPOSE] = antitranspose,
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
video_transform_t transform;
|
|
transform_description_t plane;
|
|
unsigned char plane_size_order[PICTURE_PLANE_MAX];
|
|
} filter_sys_t;
|
|
|
|
static picture_t *Filter(filter_t *filter, picture_t *src)
|
|
{
|
|
filter_sys_t *sys = filter->p_sys;
|
|
picture_t *dst = filter_NewPicture(filter);
|
|
|
|
if (likely(dst != NULL)) {
|
|
for (int i = 0; i < src->i_planes; i++)
|
|
sys->plane(dst->p[i].p_pixels, dst->p[i].i_pitch,
|
|
src->p[i].p_pixels, src->p[i].i_pitch,
|
|
src->p[i].i_visible_pitch / src->p[i].i_pixel_pitch,
|
|
src->p[i].i_visible_lines, sys->plane_size_order[i]);
|
|
|
|
picture_CopyProperties(dst, src);
|
|
}
|
|
|
|
picture_Release(src);
|
|
return dst;
|
|
}
|
|
|
|
static int Mouse(filter_t *filter, vlc_mouse_t *mouse,
|
|
const vlc_mouse_t *mold)
|
|
{
|
|
VLC_UNUSED( mold );
|
|
|
|
const video_format_t *fmt = &filter->fmt_out.video;
|
|
const filter_sys_t *sys = filter->p_sys;
|
|
int dw = fmt->i_visible_width, dh = fmt->i_visible_height;
|
|
int dx = mouse->i_x, dy = mouse->i_y;
|
|
int sx, sy;
|
|
|
|
switch (sys->transform) {
|
|
case TRANSFORM_HFLIP:
|
|
case TRANSFORM_R180:
|
|
sx = dw - 1 - dx;
|
|
break;
|
|
//case TRANSFORM_IDENTITY:
|
|
case TRANSFORM_VFLIP:
|
|
sx = dx;
|
|
break;
|
|
case TRANSFORM_TRANSPOSE:
|
|
case TRANSFORM_R90:
|
|
sx = dy;
|
|
break;
|
|
case TRANSFORM_R270:
|
|
case TRANSFORM_ANTI_TRANSPOSE:
|
|
sx = dh - 1 - dy;
|
|
break;
|
|
default:
|
|
vlc_assert_unreachable();
|
|
}
|
|
|
|
switch (sys->transform) {
|
|
//case TRANSFORM_IDENTITY:
|
|
case TRANSFORM_HFLIP:
|
|
sy = dy;
|
|
break;
|
|
case TRANSFORM_VFLIP:
|
|
case TRANSFORM_R180:
|
|
sy = dh - 1 - dy;
|
|
break;
|
|
case TRANSFORM_TRANSPOSE:
|
|
case TRANSFORM_R270:
|
|
sy = dx;
|
|
break;
|
|
case TRANSFORM_R90:
|
|
case TRANSFORM_ANTI_TRANSPOSE:
|
|
sy = dw - 1 - dx;
|
|
break;
|
|
default:
|
|
vlc_assert_unreachable();
|
|
}
|
|
|
|
mouse->i_x = sx;
|
|
mouse->i_y = sy;
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static int Open(filter_t *filter)
|
|
{
|
|
const video_format_t *src = &filter->fmt_in.video;
|
|
video_format_t *dst = &filter->fmt_out.video;
|
|
video_transform_t transform = video_format_GetTransform(src->orientation,
|
|
dst->orientation);
|
|
|
|
if (transform == TRANSFORM_IDENTITY)
|
|
return VLC_ENOTSUP; /* Nothing for this module to work at */
|
|
|
|
video_format_t src_trans = *src;
|
|
video_format_TransformBy(&src_trans, transform);
|
|
|
|
if (!video_format_IsSameChroma( dst, &src_trans ) ||
|
|
dst->i_width != src_trans.i_width ||
|
|
dst->i_visible_width != src_trans.i_visible_width ||
|
|
dst->i_height != src_trans.i_height ||
|
|
dst->i_visible_height != src_trans.i_visible_height ||
|
|
dst->i_x_offset != src_trans.i_x_offset ||
|
|
dst->i_y_offset != src_trans.i_y_offset)
|
|
return VLC_ENOTSUP; /* This module cannot rescale or change chroma */
|
|
|
|
const vlc_chroma_description_t *chroma =
|
|
vlc_fourcc_GetChromaDescription(src->i_chroma);
|
|
if (chroma == NULL)
|
|
return VLC_ENOTSUP;
|
|
|
|
if (ORIENT_IS_SWAP(transform)) /* Cannot transform non-square samples */
|
|
for (unsigned i = 0; i < chroma->plane_count; i++)
|
|
if (chroma->p[i].w.num * chroma->p[i].h.den
|
|
!= chroma->p[i].h.num * chroma->p[i].w.den)
|
|
return VLC_ENOTSUP;
|
|
|
|
vlc_CPU_functions_init_once("video transform", &transforms);
|
|
|
|
switch (chroma->pixel_size) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
break;
|
|
default:
|
|
return VLC_ENOTSUP;
|
|
}
|
|
|
|
filter_sys_t *sys = vlc_obj_malloc(VLC_OBJECT(filter), sizeof(*sys));
|
|
if (unlikely(sys == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
int order = stdc_trailing_zeros(chroma->pixel_size);
|
|
|
|
sys->transform = transform;
|
|
sys->plane = descriptions[transform];
|
|
memset(sys->plane_size_order, order, sizeof (sys->plane_size_order));
|
|
|
|
/* Deal with weird packed formats */
|
|
switch (src->i_chroma) {
|
|
case VLC_CODEC_NV12:
|
|
case VLC_CODEC_NV21:
|
|
case VLC_CODEC_NV16:
|
|
case VLC_CODEC_NV61:
|
|
case VLC_CODEC_NV24:
|
|
case VLC_CODEC_NV42:
|
|
case VLC_CODEC_P010:
|
|
case VLC_CODEC_P016:
|
|
/* Double-size samples on second plane */
|
|
sys->plane_size_order[1]++;
|
|
break;
|
|
}
|
|
|
|
static const struct vlc_filter_operations filter_ops =
|
|
{
|
|
.filter_video = Filter,
|
|
.video_mouse = Mouse,
|
|
};
|
|
filter->ops = &filter_ops;
|
|
filter->p_sys = sys;
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
vlc_module_begin()
|
|
set_description(N_("Video reorientation"))
|
|
set_shortname(N_("Reorient"))
|
|
set_subcategory(SUBCAT_VIDEO_VFILTER)
|
|
set_callback_video_converter(Open, 200)
|
|
vlc_module_end()
|
|
|