/*
* 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 .
*/
#include
#include "common.h"
#include "version.h"
#include
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();
}
}