summaryrefslogtreecommitdiffstats
path: root/src/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common.c')
-rw-r--r--src/common.c500
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();
+ }
+}