diff options
Diffstat (limited to 'src/common.c')
-rw-r--r-- | src/common.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..8c8a4f0 --- /dev/null +++ b/src/common.c @@ -0,0 +1,500 @@ +/* + * This file is part of libplacebo. + * + * libplacebo 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. + * + * libplacebo 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 libplacebo. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> + +#include "common.h" +#include "version.h" + +#include <libplacebo/common.h> + +int pl_fix_ver(void) +{ + return BUILD_FIX_VER; +} + +const char *pl_version(void) +{ + return BUILD_VERSION; +} + +void pl_rect2d_normalize(pl_rect2d *rc) +{ + *rc = (pl_rect2d) { + .x0 = PL_MIN(rc->x0, rc->x1), + .x1 = PL_MAX(rc->x0, rc->x1), + .y0 = PL_MIN(rc->y0, rc->y1), + .y1 = PL_MAX(rc->y0, rc->y1), + }; +} + +void pl_rect3d_normalize(pl_rect3d *rc) +{ + *rc = (pl_rect3d) { + .x0 = PL_MIN(rc->x0, rc->x1), + .x1 = PL_MAX(rc->x0, rc->x1), + .y0 = PL_MIN(rc->y0, rc->y1), + .y1 = PL_MAX(rc->y0, rc->y1), + .z0 = PL_MIN(rc->z0, rc->z1), + .z1 = PL_MAX(rc->z0, rc->z1), + }; +} + +void pl_rect2df_normalize(pl_rect2df *rc) +{ + *rc = (pl_rect2df) { + .x0 = PL_MIN(rc->x0, rc->x1), + .x1 = PL_MAX(rc->x0, rc->x1), + .y0 = PL_MIN(rc->y0, rc->y1), + .y1 = PL_MAX(rc->y0, rc->y1), + }; +} + +void pl_rect3df_normalize(pl_rect3df *rc) +{ + *rc = (pl_rect3df) { + .x0 = PL_MIN(rc->x0, rc->x1), + .x1 = PL_MAX(rc->x0, rc->x1), + .y0 = PL_MIN(rc->y0, rc->y1), + .y1 = PL_MAX(rc->y0, rc->y1), + .z0 = PL_MIN(rc->z0, rc->z1), + .z1 = PL_MAX(rc->z0, rc->z1), + }; +} + +pl_rect2d pl_rect2df_round(const pl_rect2df *rc) +{ + return (pl_rect2d) { + .x0 = roundf(rc->x0), + .x1 = roundf(rc->x1), + .y0 = roundf(rc->y0), + .y1 = roundf(rc->y1), + }; +} + +pl_rect3d pl_rect3df_round(const pl_rect3df *rc) +{ + return (pl_rect3d) { + .x0 = roundf(rc->x0), + .x1 = roundf(rc->x1), + .y0 = roundf(rc->y0), + .y1 = roundf(rc->y1), + .z0 = roundf(rc->z0), + .z1 = roundf(rc->z1), + }; +} + +const pl_matrix3x3 pl_matrix3x3_identity = {{ + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, +}}; + +void pl_matrix3x3_apply(const pl_matrix3x3 *mat, float vec[3]) +{ + float x = vec[0], y = vec[1], z = vec[2]; + + for (int i = 0; i < 3; i++) + vec[i] = mat->m[i][0] * x + mat->m[i][1] * y + mat->m[i][2] * z; +} + +void pl_matrix3x3_apply_rc(const pl_matrix3x3 *mat, pl_rect3df *rc) +{ + float x0 = rc->x0, x1 = rc->x1, + y0 = rc->y0, y1 = rc->y1, + z0 = rc->z0, z1 = rc->z1; + + rc->x0 = mat->m[0][0] * x0 + mat->m[0][1] * y0 + mat->m[0][2] * z0; + rc->y0 = mat->m[1][0] * x0 + mat->m[1][1] * y0 + mat->m[1][2] * z0; + rc->z0 = mat->m[2][0] * x0 + mat->m[2][1] * y0 + mat->m[2][2] * z0; + + rc->x1 = mat->m[0][0] * x1 + mat->m[0][1] * y1 + mat->m[0][2] * z1; + rc->y1 = mat->m[1][0] * x1 + mat->m[1][1] * y1 + mat->m[1][2] * z1; + rc->z1 = mat->m[2][0] * x1 + mat->m[2][1] * y1 + mat->m[2][2] * z1; +} + +void pl_matrix3x3_scale(pl_matrix3x3 *mat, float scale) +{ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) + mat->m[i][j] *= scale; + } +} + +void pl_matrix3x3_invert(pl_matrix3x3 *mat) +{ + double m00 = mat->m[0][0], m01 = mat->m[0][1], m02 = mat->m[0][2], + m10 = mat->m[1][0], m11 = mat->m[1][1], m12 = mat->m[1][2], + m20 = mat->m[2][0], m21 = mat->m[2][1], m22 = mat->m[2][2]; + + // calculate the adjoint + double a00 = (m11 * m22 - m21 * m12); + double a01 = -(m01 * m22 - m21 * m02); + double a02 = (m01 * m12 - m11 * m02); + double a10 = -(m10 * m22 - m20 * m12); + double a11 = (m00 * m22 - m20 * m02); + double a12 = -(m00 * m12 - m10 * m02); + double a20 = (m10 * m21 - m20 * m11); + double a21 = -(m00 * m21 - m20 * m01); + double a22 = (m00 * m11 - m10 * m01); + + // calculate the determinant (as inverse == 1/det * adjoint, + // adjoint * m == identity * det, so this calculates the det) + double det = m00 * a00 + m10 * a01 + m20 * a02; + det = 1.0 / det; + + mat->m[0][0] = det * a00; + mat->m[0][1] = det * a01; + mat->m[0][2] = det * a02; + mat->m[1][0] = det * a10; + mat->m[1][1] = det * a11; + mat->m[1][2] = det * a12; + mat->m[2][0] = det * a20; + mat->m[2][1] = det * a21; + mat->m[2][2] = det * a22; +} + +void pl_matrix3x3_mul(pl_matrix3x3 *a, const pl_matrix3x3 *b) +{ + float a00 = a->m[0][0], a01 = a->m[0][1], a02 = a->m[0][2], + a10 = a->m[1][0], a11 = a->m[1][1], a12 = a->m[1][2], + a20 = a->m[2][0], a21 = a->m[2][1], a22 = a->m[2][2]; + + for (int i = 0; i < 3; i++) { + a->m[0][i] = a00 * b->m[0][i] + a01 * b->m[1][i] + a02 * b->m[2][i]; + a->m[1][i] = a10 * b->m[0][i] + a11 * b->m[1][i] + a12 * b->m[2][i]; + a->m[2][i] = a20 * b->m[0][i] + a21 * b->m[1][i] + a22 * b->m[2][i]; + } +} + +void pl_matrix3x3_rmul(const pl_matrix3x3 *a, pl_matrix3x3 *b) +{ + pl_matrix3x3 m = *a; + pl_matrix3x3_mul(&m, b); + *b = m; +} + +const pl_transform3x3 pl_transform3x3_identity = { + .mat = {{ + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + }}, +}; + +void pl_transform3x3_apply(const pl_transform3x3 *t, float vec[3]) +{ + pl_matrix3x3_apply(&t->mat, vec); + + for (int i = 0; i < 3; i++) + vec[i] += t->c[i]; +} + +void pl_transform3x3_apply_rc(const pl_transform3x3 *t, pl_rect3df *rc) +{ + pl_matrix3x3_apply_rc(&t->mat, rc); + + rc->x0 += t->c[0]; + rc->x1 += t->c[0]; + rc->y0 += t->c[1]; + rc->y1 += t->c[1]; + rc->z0 += t->c[2]; + rc->z1 += t->c[2]; +} + +void pl_transform3x3_scale(pl_transform3x3 *t, float scale) +{ + pl_matrix3x3_scale(&t->mat, scale); + + for (int i = 0; i < 3; i++) + t->c[i] *= scale; +} + +// based on DarkPlaces engine (relicensed from GPL to LGPL) +void pl_transform3x3_invert(pl_transform3x3 *t) +{ + pl_matrix3x3_invert(&t->mat); + + float m00 = t->mat.m[0][0], m01 = t->mat.m[0][1], m02 = t->mat.m[0][2], + m10 = t->mat.m[1][0], m11 = t->mat.m[1][1], m12 = t->mat.m[1][2], + m20 = t->mat.m[2][0], m21 = t->mat.m[2][1], m22 = t->mat.m[2][2]; + + // fix the constant coefficient + // rgb = M * yuv + C + // M^-1 * rgb = yuv + M^-1 * C + // yuv = M^-1 * rgb - M^-1 * C + // ^^^^^^^^^^ + float c0 = t->c[0], c1 = t->c[1], c2 = t->c[2]; + t->c[0] = -(m00 * c0 + m01 * c1 + m02 * c2); + t->c[1] = -(m10 * c0 + m11 * c1 + m12 * c2); + t->c[2] = -(m20 * c0 + m21 * c1 + m22 * c2); +} + +const pl_matrix2x2 pl_matrix2x2_identity = {{ + { 1, 0 }, + { 0, 1 }, +}}; + +pl_matrix2x2 pl_matrix2x2_rotation(float a) +{ + return (pl_matrix2x2) {{ + { cosf(a), -sinf(a) }, + { sinf(a), cosf(a) }, + }}; +} + +void pl_matrix2x2_apply(const pl_matrix2x2 *mat, float vec[2]) +{ + float x = vec[0], y = vec[1]; + + for (int i = 0; i < 2; i++) + vec[i] = mat->m[i][0] * x + mat->m[i][1] * y; +} + +void pl_matrix2x2_apply_rc(const pl_matrix2x2 *mat, pl_rect2df *rc) +{ + float x0 = rc->x0, x1 = rc->x1, + y0 = rc->y0, y1 = rc->y1; + + rc->x0 = mat->m[0][0] * x0 + mat->m[0][1] * y0; + rc->y0 = mat->m[1][0] * x0 + mat->m[1][1] * y0; + + rc->x1 = mat->m[0][0] * x1 + mat->m[0][1] * y1; + rc->y1 = mat->m[1][0] * x1 + mat->m[1][1] * y1; +} + +void pl_matrix2x2_mul(pl_matrix2x2 *a, const pl_matrix2x2 *b) +{ + float a00 = a->m[0][0], a01 = a->m[0][1], + a10 = a->m[1][0], a11 = a->m[1][1]; + + for (int i = 0; i < 2; i++) { + a->m[0][i] = a00 * b->m[0][i] + a01 * b->m[1][i]; + a->m[1][i] = a10 * b->m[0][i] + a11 * b->m[1][i]; + } +} + +void pl_matrix2x2_rmul(const pl_matrix2x2 *a, pl_matrix2x2 *b) +{ + pl_matrix2x2 m = *a; + pl_matrix2x2_mul(&m, b); + *b = m; +} + +void pl_matrix2x2_scale(pl_matrix2x2 *mat, float scale) +{ + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) + mat->m[i][j] *= scale; + } +} + +void pl_matrix2x2_invert(pl_matrix2x2 *mat) +{ + float m00 = mat->m[0][0], m01 = mat->m[0][1], + m10 = mat->m[1][0], m11 = mat->m[1][1]; + float invdet = 1.0f / (m11 * m00 - m10 * m01); + + mat->m[0][0] = m11 * invdet; + mat->m[0][1] = -m01 * invdet; + mat->m[1][0] = -m10 * invdet; + mat->m[1][1] = m00 * invdet; +} + +const pl_transform2x2 pl_transform2x2_identity = { + .mat = {{ + { 1, 0 }, + { 0, 1 }, + }}, +}; + +void pl_transform2x2_apply(const pl_transform2x2 *t, float vec[2]) +{ + pl_matrix2x2_apply(&t->mat, vec); + + for (int i = 0; i < 2; i++) + vec[i] += t->c[i]; +} + +void pl_transform2x2_apply_rc(const pl_transform2x2 *t, pl_rect2df *rc) +{ + pl_matrix2x2_apply_rc(&t->mat, rc); + + rc->x0 += t->c[0]; + rc->x1 += t->c[0]; + rc->y0 += t->c[1]; + rc->y1 += t->c[1]; +} + +void pl_transform2x2_mul(pl_transform2x2 *a, const pl_transform2x2 *b) +{ + float c[2] = { b->c[0], b->c[1] }; + pl_transform2x2_apply(a, c); + memcpy(a->c, c, sizeof(c)); + pl_matrix2x2_mul(&a->mat, &b->mat); +} + +void pl_transform2x2_rmul(const pl_transform2x2 *a, pl_transform2x2 *b) +{ + pl_transform2x2_apply(a, b->c); + pl_matrix2x2_rmul(&a->mat, &b->mat); +} + +void pl_transform2x2_scale(pl_transform2x2 *t, float scale) +{ + pl_matrix2x2_scale(&t->mat, scale); + + for (int i = 0; i < 2; i++) + t->c[i] *= scale; +} + +void pl_transform2x2_invert(pl_transform2x2 *t) +{ + pl_matrix2x2_invert(&t->mat); + + float m00 = t->mat.m[0][0], m01 = t->mat.m[0][1], + m10 = t->mat.m[1][0], m11 = t->mat.m[1][1]; + float c0 = t->c[0], c1 = t->c[1]; + t->c[0] = -(m00 * c0 + m01 * c1); + t->c[1] = -(m10 * c0 + m11 * c1); +} + +pl_rect2df pl_transform2x2_bounds(const pl_transform2x2 *t, const pl_rect2df *rc) +{ + float p[4][2] = { + { rc->x0, rc->y0 }, + { rc->x0, rc->y1 }, + { rc->x1, rc->y0 }, + { rc->x1, rc->y1 }, + }; + for (int i = 0; i < PL_ARRAY_SIZE(p); i++) + pl_transform2x2_apply(t, p[i]); + + return (pl_rect2df) { + .x0 = fminf(fminf(p[0][0], p[1][0]), fminf(p[2][0], p[3][0])), + .x1 = fmaxf(fmaxf(p[0][0], p[1][0]), fmaxf(p[2][0], p[3][0])), + .y0 = fminf(fminf(p[0][1], p[1][1]), fminf(p[2][1], p[3][1])), + .y1 = fmaxf(fmaxf(p[0][1], p[1][1]), fmaxf(p[2][1], p[3][1])), + }; +} + +float pl_rect2df_aspect(const pl_rect2df *rc) +{ + float w = fabsf(pl_rect_w(*rc)), h = fabsf(pl_rect_h(*rc)); + return h ? (w / h) : 0.0; +} + +void pl_rect2df_aspect_set(pl_rect2df *rc, float aspect, float panscan) +{ + pl_assert(aspect >= 0); + float orig_aspect = pl_rect2df_aspect(rc); + if (!aspect || !orig_aspect) + return; + + float scale_x, scale_y; + if (aspect > orig_aspect) { + // New aspect is wider than the original, so we need to either grow in + // scale_x (panscan=1) or shrink in scale_y (panscan=0) + scale_x = powf(aspect / orig_aspect, panscan); + scale_y = powf(aspect / orig_aspect, panscan - 1.0); + } else if (aspect < orig_aspect) { + // New aspect is taller, so either grow in scale_y (panscan=1) or + // shrink in scale_x (panscan=0) + scale_x = powf(orig_aspect / aspect, panscan - 1.0); + scale_y = powf(orig_aspect / aspect, panscan); + } else { + return; // No change in aspect + } + + pl_rect2df_stretch(rc, scale_x, scale_y); +} + +void pl_rect2df_aspect_fit(pl_rect2df *rc, const pl_rect2df *src, float panscan) +{ + float orig_w = fabs(pl_rect_w(*rc)), + orig_h = fabs(pl_rect_h(*rc)); + if (!orig_w || !orig_h) + return; + + // If either one of these is larger than 1, then we need to shrink to fit, + // otherwise we can just directly stretch the rect. + float scale_x = fabs(pl_rect_w(*src)) / orig_w, + scale_y = fabs(pl_rect_h(*src)) / orig_h; + + if (scale_x > 1.0 || scale_y > 1.0) { + pl_rect2df_aspect_copy(rc, src, panscan); + } else { + pl_rect2df_stretch(rc, scale_x, scale_y); + } +} + +void pl_rect2df_stretch(pl_rect2df *rc, float stretch_x, float stretch_y) +{ + float midx = (rc->x0 + rc->x1) / 2.0, + midy = (rc->y0 + rc->y1) / 2.0; + + rc->x0 = rc->x0 * stretch_x + midx * (1.0 - stretch_x); + rc->x1 = rc->x1 * stretch_x + midx * (1.0 - stretch_x); + rc->y0 = rc->y0 * stretch_y + midy * (1.0 - stretch_y); + rc->y1 = rc->y1 * stretch_y + midy * (1.0 - stretch_y); +} + +void pl_rect2df_offset(pl_rect2df *rc, float offset_x, float offset_y) +{ + if (rc->x1 < rc->x0) + offset_x = -offset_x; + if (rc->y1 < rc->y0) + offset_y = -offset_y; + + rc->x0 += offset_x; + rc->x1 += offset_x; + rc->y0 += offset_y; + rc->y1 += offset_y; +} + +void pl_rect2df_rotate(pl_rect2df *rc, pl_rotation rot) +{ + if (!(rot = pl_rotation_normalize(rot))) + return; + + float x0 = rc->x0, y0 = rc->y0, x1 = rc->x1, y1 = rc->y1; + if (rot >= PL_ROTATION_180) { + rot -= PL_ROTATION_180; + PL_SWAP(x0, x1); + PL_SWAP(y0, y1); + } + + switch (rot) { + case PL_ROTATION_0: + *rc = (pl_rect2df) { + .x0 = x0, + .y0 = y0, + .x1 = x1, + .y1 = y1, + }; + return; + case PL_ROTATION_90: + *rc = (pl_rect2df) { + .x0 = y1, + .y0 = x0, + .x1 = y0, + .y1 = x1, + }; + return; + default: pl_unreachable(); + } +} |