+ if (vf >= -0.5f && vf < 0.5f) {
+ vf = tanf(M_PI_2 * vf);
+ } else {
+ vf = 2.f * vf;
+ }
+
+ face = u_face + 3 * v_face;
+
+ switch (face) {
+ case TOP_LEFT:
+ l_x = -1.f;
+ l_y = -vf;
+ l_z = -uf;
+ break;
+ case TOP_MIDDLE:
+ l_x = uf;
+ l_y = -vf;
+ l_z = -1.f;
+ break;
+ case TOP_RIGHT:
+ l_x = 1.f;
+ l_y = -vf;
+ l_z = uf;
+ break;
+ case BOTTOM_LEFT:
+ l_x = -vf;
+ l_y = -1.f;
+ l_z = uf;
+ break;
+ case BOTTOM_MIDDLE:
+ l_x = -vf;
+ l_y = uf;
+ l_z = 1.f;
+ break;
+ case BOTTOM_RIGHT:
+ l_x = -vf;
+ l_y = 1.f;
+ l_z = -uf;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = l_z;
+
+ normalize_vector(vec);
+}
+
+/**
+ * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
+ *
+ * @param s filter private context
+ * @param vec coordinates on sphere
+ * @param width frame width
+ * @param height frame height
+ * @param us horizontal coordinates for interpolation window
+ * @param vs vertical coordinates for interpolation window
+ * @param du horizontal relative coordinate
+ * @param dv vertical relative coordinate
+ */
+static void xyz_to_eac(const V360Context *s,
+ const float *vec, int width, int height,
+ int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
+{
+ const float pixel_pad = 2;
+ const float u_pad = pixel_pad / width;
+ const float v_pad = pixel_pad / height;
+
+ float uf, vf;
+ int ui, vi;
+ int direction, face;
+ int u_face, v_face;
+
+ xyz_to_cube(s, vec, &uf, &vf, &direction);
+
+ face = s->in_cubemap_face_order[direction];
+ u_face = face % 3;
+ v_face = face / 3;
+
+ uf = M_2_PI * atanf(uf) + 0.5f;
+ vf = M_2_PI * atanf(vf) + 0.5f;
+
+ // These formulas are inversed from eac_to_xyz ones
+ uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
+ vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
+
+ uf *= width;
+ vf *= height;
+
+ uf -= 0.5f;
+ vf -= 0.5f;
+
+ ui = floorf(uf);
+ vi = floorf(vf);
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
+ us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
+ vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
+ }
+ }
+}
+
+/**
+ * Prepare data for processing flat output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_flat_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f);
+ s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
+
+ return 0;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void flat_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float l_x = s->flat_range[0] * (2.f * i / width - 1.f);
+ const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
+
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = -1.f;
+
+ normalize_vector(vec);
+}
+
+/**
+ * Prepare data for processing fisheye output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_fisheye_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->flat_range[0] = s->h_fov / 180.f;
+ s->flat_range[1] = s->v_fov / 180.f;
+
+ return 0;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in fisheye format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void fisheye_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f);
+ const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
+
+ const float phi = -atan2f(vf, uf);
+ const float theta = -M_PI_2 * (1.f - hypotf(uf, vf));
+
+ vec[0] = cosf(theta) * cosf(phi);
+ vec[1] = cosf(theta) * sinf(phi);
+ vec[2] = sinf(theta);
+
+ normalize_vector(vec);
+}
+
+/**
+ * Prepare data for processing fisheye input format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_fisheye_in(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->iflat_range[0] = s->ih_fov / 180.f;
+ s->iflat_range[1] = s->iv_fov / 180.f;
+
+ return 0;
+}
+
+/**
+ * Calculate frame position in fisheye format for corresponding 3D coordinates on sphere.
+ *
+ * @param s filter private context
+ * @param vec coordinates on sphere
+ * @param width frame width
+ * @param height frame height
+ * @param us horizontal coordinates for interpolation window
+ * @param vs vertical coordinates for interpolation window
+ * @param du horizontal relative coordinate
+ * @param dv vertical relative coordinate
+ */
+static void xyz_to_fisheye(const V360Context *s,
+ const float *vec, int width, int height,
+ int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
+{
+ const float phi = -atan2f(hypotf(vec[0], vec[1]), -vec[2]) / M_PI;
+ const float theta = -atan2f(vec[0], vec[1]);
+
+ float uf = sinf(theta) * phi * s->input_mirror_modifier[0] / s->iflat_range[0];
+ float vf = cosf(theta) * phi * s->input_mirror_modifier[1] / s->iflat_range[1];
+
+ const int visible = hypotf(uf, vf) <= 0.5f;
+ int ui, vi;
+
+ uf = (uf + 0.5f) * width;
+ vf = (vf + 0.5f) * height;
+
+ ui = floorf(uf);
+ vi = floorf(vf);
+
+ *du = visible ? uf - ui : 0.f;
+ *dv = visible ? vf - vi : 0.f;
+
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
+ us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0;
+ vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
+ }
+ }
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in pannini format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void pannini_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float uf = ((2.f * i) / width - 1.f);
+ const float vf = ((2.f * j) / height - 1.f);
+
+ const float d = s->h_fov;
+ const float k = uf * uf / ((d + 1.f) * (d + 1.f));
+ const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f);
+ const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f);
+ const float S = (d + 1.f) / (d + clon);
+ const float lon = -(M_PI + atan2f(uf, S * clon));
+ const float lat = -atan2f(vf, S);
+
+ vec[0] = sinf(lon) * cosf(lat);
+ vec[1] = sinf(lat);
+ vec[2] = cosf(lon) * cosf(lat);
+
+ normalize_vector(vec);
+}
+
+/**
+ * Prepare data for processing cylindrical output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_cylindrical_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->flat_range[0] = M_PI * s->h_fov / 360.f;
+ s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
+
+ return 0;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void cylindrical_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f);
+ const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
+
+ const float phi = uf;
+ const float theta = atanf(vf);
+
+ const float sin_phi = sinf(phi);
+ const float cos_phi = cosf(phi);
+ const float sin_theta = sinf(theta);
+ const float cos_theta = cosf(theta);
+
+ vec[0] = cos_theta * sin_phi;
+ vec[1] = -sin_theta;
+ vec[2] = -cos_theta * cos_phi;
+
+ normalize_vector(vec);
+}
+
+/**
+ * Prepare data for processing cylindrical input format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_cylindrical_in(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->iflat_range[0] = M_PI * s->ih_fov / 360.f;
+ s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
+
+ return 0;
+}
+
+/**
+ * Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere.
+ *
+ * @param s filter private context
+ * @param vec coordinates on sphere
+ * @param width frame width
+ * @param height frame height
+ * @param us horizontal coordinates for interpolation window
+ * @param vs vertical coordinates for interpolation window
+ * @param du horizontal relative coordinate
+ * @param dv vertical relative coordinate
+ */
+static void xyz_to_cylindrical(const V360Context *s,
+ const float *vec, int width, int height,
+ int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
+{
+ const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] / s->iflat_range[0];
+ const float theta = atan2f(-vec[1], hypotf(vec[0], vec[2])) * s->input_mirror_modifier[1] / s->iflat_range[1];
+ int visible, ui, vi;
+ float uf, vf;
+
+ uf = (phi + 1.f) * (width - 1) / 2.f;
+ vf = (tanf(theta) + 1.f) * height / 2.f;
+ ui = floorf(uf);
+ vi = floorf(vf);
+
+ visible = vi >= 0 && vi < height && ui >= 0 && ui < width &&
+ theta <= M_PI * s->iv_fov / 180.f &&
+ theta >= -M_PI * s->iv_fov / 180.f;
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = -1; i < 3; i++) {
+ for (int j = -1; j < 3; j++) {
+ us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0;
+ vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
+ }
+ }
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in perspective format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void perspective_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float uf = ((2.f * i) / width - 1.f);
+ const float vf = ((2.f * j) / height - 1.f);
+ const float rh = hypotf(uf, vf);
+ const float sinzz = 1.f - rh * rh;
+ const float h = 1.f + s->v_fov;
+ const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h);
+ const float sinz2 = sinz * sinz;
+
+ if (sinz2 <= 1.f) {
+ const float cosz = sqrtf(1.f - sinz2);
+
+ const float theta = asinf(cosz);
+ const float phi = atan2f(uf, vf);
+
+ const float sin_phi = sinf(phi);
+ const float cos_phi = cosf(phi);
+ const float sin_theta = sinf(theta);
+ const float cos_theta = cosf(theta);
+
+ vec[0] = cos_theta * sin_phi;
+ vec[1] = sin_theta;
+ vec[2] = -cos_theta * cos_phi;