in Transform_V1/vf_transform_v1.c [202:527]
static inline void transform_pos(
TransformContext *ctx,
float x, float y,
float *outX, float *outY,
int *has_mapping,
float input_pixel_w
)
{
int is_right = 0;
*has_mapping = 1;
if (ctx->input_stereo_format != STEREO_FORMAT_MONO) {
if (y > Y_HALF) {
y = (y - Y_HALF) / Y_HALF;
if (ctx->vflip) {
y = 1.0f - y;
}
is_right = 1;
} else {
y = y / Y_HALF;
}
}
if (ctx->output_layout == LAYOUT_PLANE_POLES) {
if (x >= ctx->main_plane_ratio) {
float dx = (x * 2 - 1 - ctx->main_plane_ratio) / (1 - ctx->main_plane_ratio);
if (y < Y_HALF) {
// Bottom
float dy = (y - YC_BOTTOM) / PH;
*outX = (atan2f(dy, dx)) / (M_PI * 2.0f) + 0.75f;
*outY = sqrtf(dy * dy + dx * dx) * 0.25f;
} else {
// Top
float dy = (y - YC_TOP) / PH;
*outX = (atan2f(dy, dx)) / (M_PI * 2.0f) + 0.75f;
*outY = 1.0f - sqrtf(dy * dy + dx * dx) * 0.25f;
}
if (*outX > 1.0f) {
*outX -= 1.0f;
}
} else {
// Main
*outX = x / ctx->main_plane_ratio;
*outY = y * 0.5f + 0.25f;
}
} else if (ctx->output_layout == LAYOUT_PLANE_POLES_6) {
int face = (int) (x * 6);
if (face < 4) {
// Main
*outX = x * 6.0f / 4.0f;
*outY = y * 0.5f + 0.25f;
} else {
float dx, dy;
x = x * 6.0f - face;
dx = x * 2 - 1;
dy = y * 2 - 1;
if (face == 4) {
// Top
*outX = (atan2f(dy, dx)) / (M_PI * 2.0f) + 0.75f;
*outY = 1.0f - sqrtf(dy * dy + dx * dx) * 0.25f;
} else {
// Bottom
*outX = (atan2f(dy, dx)) / (M_PI * 2.0f) + 0.75f;
*outY = sqrtf(dy * dy + dx * dx) * 0.25f;
}
if (*outX > 1.0f) {
*outX -= 1.0f;
}
}
} else if (ctx->output_layout == LAYOUT_FLAT_FIXED) {
// Per the Metadata RFC for orienting the equirectangular coords:
// Heading
// -180 0 180
// 90 +-------------+-------------+ 0.0
// | | |
// P | | o |
// i | ^ |
// t 0 +-------------X-------------+ 0.5
// c | | |
// h | | |
// | | |
// -90 +-------------+-------------+ 1.0
// 0.0 0.5 1.0
// X - the default camera center
// ^ - the default up vector
// o - the image center for a pitch of 45 and a heading of 90
// Coords on left and top sides are degrees
// Coords on right and bottom axes are our X/Y in range [0..1)
// Note: Negative field of view can be supplied to flip the image.
*outX = ((x - 0.5f) * ctx->fixed_hfov + ctx->fixed_yaw) / 360.0f
+ 0.5f;
*outY = ((y - 0.5f) * ctx->fixed_vfov - ctx->fixed_pitch) / 180.0f
+ 0.5f;
normalize_equirectangular(*outX, *outY, outX, outY);
} else if (ctx->output_layout == LAYOUT_CUBEMAP ||
ctx->output_layout == LAYOUT_PLANE_POLES_CUBEMAP ||
ctx->output_layout == LAYOUT_CUBEMAP_32 ||
ctx->output_layout == LAYOUT_CUBEMAP_180 ||
ctx->output_layout == LAYOUT_PLANE_CUBEMAP_32 ||
ctx->output_layout == LAYOUT_PLANE_CUBEMAP ||
ctx->output_layout == LAYOUT_BARREL ||
ctx->output_layout == LAYOUT_TB_ONLY ||
ctx->output_layout == LAYOUT_TB_BARREL_ONLY) {
float qx, qy, qz;
float cos_y, cos_p, sin_y, sin_p;
float tx, ty, tz;
float yaw, pitch;
float d;
y = 1.0f - y;
const float *vx, *vy, *p;
int face = 0;
if (ctx->output_layout == LAYOUT_CUBEMAP) {
face = (int) (x * 6);
x = x * 6.0f - face;
} else if (ctx->output_layout == LAYOUT_CUBEMAP_32) {
int vface = (int) (y * 2);
int hface = (int) (x * 3);
x = x * 3.0f - hface;
y = y * 2.0f - vface;
face = hface + (1 - vface) * 3;
} else if (ctx->output_layout == LAYOUT_CUBEMAP_180) {
// LAYOUT_CUBEMAP_180: layout for spatial resolution downsampling with 180 degree viewport size
//
// - Given a view (yaw,pitch) we can create a customized cube mapping to make the view center at the front cube face.
// - A 180 degree viewport cut the cube into 2 equal-sized halves: front half and back half.
// - The front half contains these faces of the cube: front, half of right, half of left, half of top, half of bottom.
// The back half contains these faces of the cube: back, half of right, half of left, half of top, half of bottom.
// Illutrasion on LAYOUT_CUBEMAP_32 (mono):
//
// +---+---+---+---+---+---+
// | | | | | 5 |
// + 1 | 2 + 3 | 4 +-------+ Area 1, 4, 6, 7, 9 are in the front half
// | | | | | 6 |
// +---+---+---+---+---+---+ Area 2, 3, 5, 8, 0 are in the back half
// | 7 | | |
// +-------+ 9 + 0 +
// | 8 | | |
// +---+---+---+---+---+---+
//
// - LAYOUT_CUBEMAP_180 reduces the spatial resolution of the back half to 25% (1/2 height, 1/2 width makes 1/4 size)
// and then re-pack the cube map like this:
//
// +---+---+---+---+---+ Front half Back half (1/4 size)
// | | | c | ---------- --------------------
// + a + b +---+---- Area a = 9 Area f = 0
// | | | f | | Area b = 4 Area g = 3
// +---+---+---+---+ d + Area c = 6 Area h = 2
// |g|h|-i-| e | | Area d = 1 Area i1(top) = 5
// +---+---+---+---+---+ Area e = 7 Area i2(bottom) = 8
//
if (0.0f <= y && y < 1.0f/3 && 0.0f <= x && x < 0.8f) { // Area g, h, i1, i2, e
if (0.0f <= x && x < 0.1f) { // g
face = LEFT;
x = x/0.2f;
y = y/(1.0f/3);
}
else if (0.1f <= x && x < 0.2f) { // h
face = RIGHT;
x = (x-0.1f)/0.2f + 0.5f;
y = y/(1.0f/3);
}
else if (0.2f <= x && x < 0.4f) {
if (y >= 1.0f/6){ //i1
face = TOP;
x = (x-0.2f)/0.2f;
y =(y-1.0f/6)/(1.0f/3) + 0.5f;
}
else { // i2
face = BOTTOM;
x = (x-0.2f)/0.2f;
y = y/(1.0f/3);
}
}
else if (0.4f <= x && x < 0.8f){ // e
face = BOTTOM;
x = (x-0.4f)/0.4f;
y = y/(2.0f/3) + 0.5f;
}
}
else if (2.0f/3 <= y && y < 1.0f && 0.6f <= x && x < 1.0f) { // Area c
face = TOP;
x = (x-0.6f)/0.4f;
y = (y-2.0f/3)/(2.0f/3);
}
else { // Area a, b, f, d
if (0.0f <= x && x < 0.4f) { // a
face = FRONT;
x = x/0.4f;
y = (y-1.0/3)/(2.0f/3);
}
else if (0.4f <= x && x < 0.6f) { // b
face = LEFT;
x = (x-0.4f)/0.4f + 0.5f;
y = (y-1.0f/3)/(2.0f/3);
}
else if (0.6f <= x && x < 0.8f) { // f
face = BACK;
x = (x-0.6f)/0.2f;
y = (y-1.0f/3)/(1.0f/3);
}
else if (0.8f <= x && x < 1.0f) { // d
face = RIGHT;
x = (x-0.8f)/0.4f;
y = y/(2.0f/3);
}
}
} else if (ctx->output_layout == LAYOUT_PLANE_CUBEMAP_32) {
int vface = (int) (y * 2);
int hface = (int) (x * 3);
x = x * 3.0f - hface;
y = y * 2.0f - vface;
face = hface + (1 - vface) * 3;
face = PLANE_CUBEMAP_32_FACE_MAP[face];
} else if (ctx->output_layout == LAYOUT_PLANE_POLES_CUBEMAP) {
face = (int) (x * 4.5f);
x = x * 4.5f - face;
if (face == 4) {
x *= 2.0f;
y *= 2.0f;
if (y >= 1.0f) {
y -= 1.0f;
} else {
face = 5; // bottom
}
}
face = PLANE_POLES_FACE_MAP[face];
} else if (ctx->output_layout == LAYOUT_PLANE_CUBEMAP) {
face = (int) (x * 6);
x = x * 6.0f - face;
face = PLANE_CUBEMAP_FACE_MAP[face];
} else if (ctx->output_layout == LAYOUT_BARREL) {
if (x <= 0.8f) {
yaw = (2.5f * x - 1.0f) * ctx->expand_coef * M_PI;
pitch = (y * 0.5f - 0.25f) * ctx->expand_coef * M_PI;
face = -1;
} else {
int vFace = (int) (y * 2);
face = (vFace == 1) ? TOP : BOTTOM;
x = x * 5.0f - 4.0f;
y = y * 2.0f - vFace;
}
} else if (ctx->output_layout == LAYOUT_TB_ONLY ||
ctx->output_layout == LAYOUT_TB_BARREL_ONLY) {
int vFace = (int) (y * 2);
face = (vFace == 1) ? TOP : BOTTOM;
y = y * 2.0f - vFace;
} else {
av_assert0(0);
}
if (ctx->output_layout == LAYOUT_BARREL && face < 0) {
float sin_yaw = sin(yaw);
float sin_pitch = sin(pitch);
float cos_yaw = cos(yaw);
float cos_pitch = cos(pitch);
qx = sin_yaw * cos_pitch;
qy = sin_pitch;
qz = cos_yaw * cos_pitch;
} else {
av_assert1(x >= 0 && x <= 1);
av_assert1(y >= 0 && y <= 1);
av_assert1(face >= 0 && face < 6);
if (ctx->output_layout == LAYOUT_BARREL ||
ctx->output_layout == LAYOUT_TB_BARREL_ONLY) {
float radius = (x - 0.5f) * (x - 0.5f) + (y - 0.5f) * (y - 0.5f);
if (radius > 0.25f * ctx->expand_coef * ctx->expand_coef) {
*has_mapping = 0;
return;
}
}
x = (x - 0.5f) * ctx->expand_coef + 0.5f;
y = (y - 0.5f) * ctx->expand_coef + 0.5f;
switch (face) {
case RIGHT: p = P5; vx = NZ; vy = PY; break;
case LEFT: p = P0; vx = PZ; vy = PY; break;
case TOP: p = P6; vx = PX; vy = NZ; break;
case BOTTOM: p = P0; vx = PX; vy = PZ; break;
case FRONT: p = P4; vx = PX; vy = PY; break;
case BACK: p = P1; vx = NX; vy = PY; break;
}
qx = p [0] + vx [0] * x + vy [0] * y;
qy = p [1] + vx [1] * x + vy [1] * y;
qz = p [2] + vx [2] * x + vy [2] * y;
}
// rotation
sin_y = sin(ctx->fixed_yaw*M_PI/180.0f);
sin_p = sin(ctx->fixed_pitch*M_PI/180.0f);
cos_y = cos(ctx->fixed_yaw*M_PI/180.0f);
cos_p = cos(ctx->fixed_pitch*M_PI/180.0f);
tx = qx * cos_y - qy * sin_y*sin_p + qz * sin_y*cos_p;
ty = qy * cos_p + qz * sin_p;
tz = qx* (-sin_y) - qy * cos_y*sin_p + qz * cos_y*cos_p;
d = sqrtf(tx * tx + ty * ty + tz * tz);
*outX = -atan2f (-tx / d, tz / d) / (M_PI * 2.0f) + 0.5f;
if (ctx->output_layout == LAYOUT_BARREL) {
*outX = FFMIN(*outX, 1.0f - input_pixel_w * 0.5f);
*outX = FFMAX(*outX, input_pixel_w * 0.5f);
}
*outY = asinf (-ty / d) / M_PI + 0.5f;
}
if (ctx->input_stereo_format == STEREO_FORMAT_TB) {
if (is_right) {
*outY = *outY * Y_HALF + Y_HALF;
} else {
*outY = *outY * Y_HALF;
}
} else if (ctx->input_stereo_format == STEREO_FORMAT_LR) {
if (is_right) {
*outX = *outX * X_HALF + X_HALF;
} else {
*outX = *outX * X_HALF;
}
} else {
// mono no steps needed.
}
av_assert1(*outX >= 0 && *outX <= 1);
av_assert1(*outY >= 0 && *outY <= 1);
}