eBookReaderSwitch/mupdf/source/fitz/geometry.c

901 lines
19 KiB
C

#include "mupdf/fitz.h"
#include <math.h>
#include <float.h>
#include <limits.h>
#define MAX4(a,b,c,d) fz_max(fz_max(a,b), fz_max(c,d))
#define MIN4(a,b,c,d) fz_min(fz_min(a,b), fz_min(c,d))
/* A useful macro to add with overflow detection and clamping.
We want to do "b = a + x", but to allow for overflow. Consider the
top bits, and the cases in which overflow occurs:
overflow a x b ~a^x a^b (~a^x)&(a^b)
no 0 0 0 1 0 0
yes 0 0 1 1 1 1
no 0 1 0 0 0 0
no 0 1 1 0 1 0
no 1 0 0 0 1 0
no 1 0 1 0 0 0
yes 1 1 0 1 1 1
no 1 1 1 1 0 0
*/
#define ADD_WITH_SAT(b,a,x) \
((b) = (a) + (x), (b) = (((~(a)^(x))&((a)^(b))) < 0 ? ((x) < 0 ? INT_MIN : INT_MAX) : (b)))
/* Matrices, points and affine transformations */
const fz_matrix fz_identity = { 1, 0, 0, 1, 0, 0 };
/*
Multiply two matrices.
The order of the two matrices are important since matrix
multiplication is not commutative.
Returns result.
*/
fz_matrix
fz_concat(fz_matrix one, fz_matrix two)
{
fz_matrix dst;
dst.a = one.a * two.a + one.b * two.c;
dst.b = one.a * two.b + one.b * two.d;
dst.c = one.c * two.a + one.d * two.c;
dst.d = one.c * two.b + one.d * two.d;
dst.e = one.e * two.a + one.f * two.c + two.e;
dst.f = one.e * two.b + one.f * two.d + two.f;
return dst;
}
/*
Create a scaling matrix.
The returned matrix is of the form [ sx 0 0 sy 0 0 ].
m: Pointer to the matrix to populate
sx, sy: Scaling factors along the X- and Y-axes. A scaling
factor of 1.0 will not cause any scaling along the relevant
axis.
Returns m.
*/
fz_matrix
fz_scale(float sx, float sy)
{
fz_matrix m;
m.a = sx; m.b = 0;
m.c = 0; m.d = sy;
m.e = 0; m.f = 0;
return m;
}
/*
Scale a matrix by premultiplication.
m: Pointer to the matrix to scale
sx, sy: Scaling factors along the X- and Y-axes. A scaling
factor of 1.0 will not cause any scaling along the relevant
axis.
Returns m (updated).
*/
fz_matrix
fz_pre_scale(fz_matrix m, float sx, float sy)
{
m.a *= sx;
m.b *= sx;
m.c *= sy;
m.d *= sy;
return m;
}
/*
Scale a matrix by postmultiplication.
m: Pointer to the matrix to scale
sx, sy: Scaling factors along the X- and Y-axes. A scaling
factor of 1.0 will not cause any scaling along the relevant
axis.
Returns m (updated).
*/
fz_matrix
fz_post_scale(fz_matrix m, float sx, float sy)
{
m.a *= sx;
m.b *= sy;
m.c *= sx;
m.d *= sy;
m.e *= sx;
m.f *= sy;
return m;
}
/*
Create a shearing matrix.
The returned matrix is of the form [ 1 sy sx 1 0 0 ].
m: pointer to place to store returned matrix
sx, sy: Shearing factors. A shearing factor of 0.0 will not
cause any shearing along the relevant axis.
Returns m.
*/
fz_matrix
fz_shear(float h, float v)
{
fz_matrix m;
m.a = 1; m.b = v;
m.c = h; m.d = 1;
m.e = 0; m.f = 0;
return m;
}
/*
Premultiply a matrix with a shearing matrix.
The shearing matrix is of the form [ 1 sy sx 1 0 0 ].
m: pointer to matrix to premultiply
sx, sy: Shearing factors. A shearing factor of 0.0 will not
cause any shearing along the relevant axis.
Returns m (updated).
*/
fz_matrix
fz_pre_shear(fz_matrix m, float h, float v)
{
float a = m.a;
float b = m.b;
m.a += v * m.c;
m.b += v * m.d;
m.c += h * a;
m.d += h * b;
return m;
}
/*
Create a rotation matrix.
The returned matrix is of the form
[ cos(deg) sin(deg) -sin(deg) cos(deg) 0 0 ].
m: Pointer to place to store matrix
degrees: Degrees of counter clockwise rotation. Values less
than zero and greater than 360 are handled as expected.
Returns m.
*/
fz_matrix
fz_rotate(float theta)
{
fz_matrix m;
float s;
float c;
while (theta < 0)
theta += 360;
while (theta >= 360)
theta -= 360;
if (fabsf(0 - theta) < FLT_EPSILON)
{
s = 0;
c = 1;
}
else if (fabsf(90.0f - theta) < FLT_EPSILON)
{
s = 1;
c = 0;
}
else if (fabsf(180.0f - theta) < FLT_EPSILON)
{
s = 0;
c = -1;
}
else if (fabsf(270.0f - theta) < FLT_EPSILON)
{
s = -1;
c = 0;
}
else
{
s = sinf(theta * FZ_PI / 180);
c = cosf(theta * FZ_PI / 180);
}
m.a = c; m.b = s;
m.c = -s; m.d = c;
m.e = 0; m.f = 0;
return m;
}
/*
Rotate a transformation by premultiplying.
The premultiplied matrix is of the form
[ cos(deg) sin(deg) -sin(deg) cos(deg) 0 0 ].
m: Pointer to matrix to premultiply.
degrees: Degrees of counter clockwise rotation. Values less
than zero and greater than 360 are handled as expected.
Returns m (updated).
*/
fz_matrix
fz_pre_rotate(fz_matrix m, float theta)
{
while (theta < 0)
theta += 360;
while (theta >= 360)
theta -= 360;
if (fabsf(0 - theta) < FLT_EPSILON)
{
/* Nothing to do */
}
else if (fabsf(90.0f - theta) < FLT_EPSILON)
{
float a = m.a;
float b = m.b;
m.a = m.c;
m.b = m.d;
m.c = -a;
m.d = -b;
}
else if (fabsf(180.0f - theta) < FLT_EPSILON)
{
m.a = -m.a;
m.b = -m.b;
m.c = -m.c;
m.d = -m.d;
}
else if (fabsf(270.0f - theta) < FLT_EPSILON)
{
float a = m.a;
float b = m.b;
m.a = -m.c;
m.b = -m.d;
m.c = a;
m.d = b;
}
else
{
float s = sinf(theta * FZ_PI / 180);
float c = cosf(theta * FZ_PI / 180);
float a = m.a;
float b = m.b;
m.a = c * a + s * m.c;
m.b = c * b + s * m.d;
m.c =-s * a + c * m.c;
m.d =-s * b + c * m.d;
}
return m;
}
/*
Create a translation matrix.
The returned matrix is of the form [ 1 0 0 1 tx ty ].
m: A place to store the created matrix.
tx, ty: Translation distances along the X- and Y-axes. A
translation of 0 will not cause any translation along the
relevant axis.
Returns m.
*/
fz_matrix
fz_translate(float tx, float ty)
{
fz_matrix m;
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = tx; m.f = ty;
return m;
}
/*
Translate a matrix by premultiplication.
m: The matrix to translate
tx, ty: Translation distances along the X- and Y-axes. A
translation of 0 will not cause any translation along the
relevant axis.
Returns m.
*/
fz_matrix
fz_pre_translate(fz_matrix m, float tx, float ty)
{
m.e += tx * m.a + ty * m.c;
m.f += tx * m.b + ty * m.d;
return m;
}
/*
Create transform matrix to draw page
at a given resolution and rotation. Adjusts the scaling
factors so that the page covers whole number of
pixels and adjust the page origin to be at 0,0.
*/
fz_matrix
fz_transform_page(fz_rect mediabox, float resolution, float rotate)
{
float user_w, user_h, pixel_w, pixel_h;
fz_rect pixel_box;
fz_matrix matrix;
/* Adjust scaling factors to cover whole pixels */
user_w = mediabox.x1 - mediabox.x0;
user_h = mediabox.y1 - mediabox.y0;
pixel_w = floorf(user_w * resolution / 72 + 0.5f);
pixel_h = floorf(user_h * resolution / 72 + 0.5f);
matrix = fz_pre_rotate(fz_scale(pixel_w / user_w, pixel_h / user_h), rotate);
/* Adjust the page origin to sit at 0,0 after rotation */
pixel_box = fz_transform_rect(mediabox, matrix);
matrix.e -= pixel_box.x0;
matrix.f -= pixel_box.y0;
return matrix;
}
/*
Create an inverse matrix.
inverse: Place to store inverse matrix.
matrix: Matrix to invert. A degenerate matrix, where the
determinant is equal to zero, can not be inverted and the
original matrix is returned instead.
Returns inverse.
*/
fz_matrix
fz_invert_matrix(fz_matrix src)
{
float a = src.a;
float det = a * src.d - src.b * src.c;
if (det < -FLT_EPSILON || det > FLT_EPSILON)
{
fz_matrix dst;
float rdet = 1 / det;
dst.a = src.d * rdet;
dst.b = -src.b * rdet;
dst.c = -src.c * rdet;
dst.d = a * rdet;
a = -src.e * dst.a - src.f * dst.c;
dst.f = -src.e * dst.b - src.f * dst.d;
dst.e = a;
return dst;
}
return src;
}
/*
Attempt to create an inverse matrix.
inverse: Place to store inverse matrix.
matrix: Matrix to invert. A degenerate matrix, where the
determinant is equal to zero, can not be inverted.
Returns 1 if matrix is degenerate (singular), or 0 otherwise.
*/
int
fz_try_invert_matrix(fz_matrix *dst, fz_matrix src)
{
double sa = (double)src.a;
double sb = (double)src.b;
double sc = (double)src.c;
double sd = (double)src.d;
double da, db, dc, dd;
double det = sa * sd - sb * sc;
if (det >= -DBL_EPSILON && det <= DBL_EPSILON)
return 1;
det = 1 / det;
da = sd * det;
dst->a = (float)da;
db = -sb * det;
dst->b = (float)db;
dc = -sc * det;
dst->c = (float)dc;
dd = sa * det;
dst->d = (float)dd;
da = -src.e * da - src.f * dc;
dst->f = (float)(-src.e * db - src.f * dd);
dst->e = (float)da;
return 0;
}
/*
Check if a transformation is rectilinear.
Rectilinear means that no shearing is present and that any
rotations present are a multiple of 90 degrees. Usually this
is used to make sure that axis-aligned rectangles before the
transformation are still axis-aligned rectangles afterwards.
*/
int
fz_is_rectilinear(fz_matrix m)
{
return (fabsf(m.b) < FLT_EPSILON && fabsf(m.c) < FLT_EPSILON) ||
(fabsf(m.a) < FLT_EPSILON && fabsf(m.d) < FLT_EPSILON);
}
/*
Calculate average scaling factor of matrix.
*/
float
fz_matrix_expansion(fz_matrix m)
{
return sqrtf(fabsf(m.a * m.d - m.b * m.c));
}
float
fz_matrix_max_expansion(fz_matrix m)
{
float max = fabsf(m.a);
float x = fabsf(m.b);
if (max < x)
max = x;
x = fabsf(m.c);
if (max < x)
max = x;
x = fabsf(m.d);
if (max < x)
max = x;
return max;
}
/*
Apply a transformation to a point.
transform: Transformation matrix to apply. See fz_concat,
fz_scale, fz_rotate and fz_translate for how to create a
matrix.
point: Pointer to point to update.
Returns transform (unchanged).
*/
fz_point
fz_transform_point(fz_point p, fz_matrix m)
{
float x = p.x;
p.x = x * m.a + p.y * m.c + m.e;
p.y = x * m.b + p.y * m.d + m.f;
return p;
}
fz_point
fz_transform_point_xy(float x, float y, fz_matrix m)
{
fz_point p;
p.x = x * m.a + y * m.c + m.e;
p.y = x * m.b + y * m.d + m.f;
return p;
}
/*
Apply a transformation to a vector.
transform: Transformation matrix to apply. See fz_concat,
fz_scale and fz_rotate for how to create a matrix. Any
translation will be ignored.
vector: Pointer to vector to update.
*/
fz_point
fz_transform_vector(fz_point p, fz_matrix m)
{
float x = p.x;
p.x = x * m.a + p.y * m.c;
p.y = x * m.b + p.y * m.d;
return p;
}
/*
Normalize a vector to length one.
*/
fz_point
fz_normalize_vector(fz_point p)
{
float len = p.x * p.x + p.y * p.y;
if (len != 0)
{
len = sqrtf(len);
p.x /= len;
p.y /= len;
}
return p;
}
/* Rectangles and bounding boxes */
/* biggest and smallest integers that a float can represent perfectly (i.e. 24 bits) */
#define MAX_SAFE_INT 16777216
#define MIN_SAFE_INT -16777216
const fz_rect fz_infinite_rect = { 1, 1, -1, -1 };
const fz_rect fz_empty_rect = { 0, 0, 0, 0 };
const fz_rect fz_unit_rect = { 0, 0, 1, 1 };
const fz_irect fz_infinite_irect = { 1, 1, -1, -1 };
const fz_irect fz_empty_irect = { 0, 0, 0, 0 };
const fz_irect fz_unit_bbox = { 0, 0, 1, 1 };
/*
Convert a rect into the minimal bounding box
that covers the rectangle.
Coordinates in a bounding box are integers, so rounding of the
rects coordinates takes place. The top left corner is rounded
upwards and left while the bottom right corner is rounded
downwards and to the right.
*/
fz_irect
fz_irect_from_rect(fz_rect r)
{
fz_irect b;
if (fz_is_empty_rect(r))
{
b.x0 = 0;
b.y0 = 0;
b.x1 = 0;
b.y1 = 0;
}
else
{
b.x0 = fz_clamp(floorf(r.x0), MIN_SAFE_INT, MAX_SAFE_INT);
b.y0 = fz_clamp(floorf(r.y0), MIN_SAFE_INT, MAX_SAFE_INT);
b.x1 = fz_clamp(ceilf(r.x1), MIN_SAFE_INT, MAX_SAFE_INT);
b.y1 = fz_clamp(ceilf(r.y1), MIN_SAFE_INT, MAX_SAFE_INT);
}
return b;
}
/*
Convert a bbox into a rect.
For our purposes, a rect can represent all the values we meet in
a bbox, so nothing can go wrong.
rect: A place to store the generated rectangle.
bbox: The bbox to convert.
Returns rect (updated).
*/
fz_rect
fz_rect_from_irect(fz_irect a)
{
fz_rect r;
r.x0 = a.x0;
r.y0 = a.y0;
r.x1 = a.x1;
r.y1 = a.y1;
return r;
}
/*
Round rectangle coordinates.
Coordinates in a bounding box are integers, so rounding of the
rects coordinates takes place. The top left corner is rounded
upwards and left while the bottom right corner is rounded
downwards and to the right.
This differs from fz_irect_from_rect, in that fz_irect_from_rect
slavishly follows the numbers (i.e any slight over/under calculations
can cause whole extra pixels to be added). fz_round_rect
allows for a small amount of rounding error when calculating
the bbox.
*/
fz_irect
fz_round_rect(fz_rect r)
{
fz_irect b;
int i;
i = floorf(r.x0 + 0.001f);
b.x0 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT);
i = floorf(r.y0 + 0.001f);
b.y0 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT);
i = ceilf(r.x1 - 0.001f);
b.x1 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT);
i = ceilf(r.y1 - 0.001f);
b.y1 = fz_clamp(i, MIN_SAFE_INT, MAX_SAFE_INT);
return b;
}
/*
Compute intersection of two rectangles.
Given two rectangles, update the first to be the smallest
axis-aligned rectangle that covers the area covered by both
given rectangles. If either rectangle is empty then the
intersection is also empty. If either rectangle is infinite
then the intersection is simply the non-infinite rectangle.
Should both rectangles be infinite, then the intersection is
also infinite.
*/
fz_rect
fz_intersect_rect(fz_rect a, fz_rect b)
{
/* Check for empty box before infinite box */
if (fz_is_empty_rect(a)) return fz_empty_rect;
if (fz_is_empty_rect(b)) return fz_empty_rect;
if (fz_is_infinite_rect(b)) return a;
if (fz_is_infinite_rect(a)) return b;
if (a.x0 < b.x0)
a.x0 = b.x0;
if (a.y0 < b.y0)
a.y0 = b.y0;
if (a.x1 > b.x1)
a.x1 = b.x1;
if (a.y1 > b.y1)
a.y1 = b.y1;
if (a.x1 < a.x0 || a.y1 < a.y0)
return fz_empty_rect;
return a;
}
/*
Compute intersection of two bounding boxes.
Similar to fz_intersect_rect but operates on two bounding
boxes instead of two rectangles.
*/
fz_irect
fz_intersect_irect(fz_irect a, fz_irect b)
{
/* Check for empty box before infinite box */
if (fz_is_empty_irect(a)) return fz_empty_irect;
if (fz_is_empty_irect(b)) return fz_empty_irect;
if (fz_is_infinite_irect(b)) return a;
if (fz_is_infinite_irect(a)) return b;
if (a.x0 < b.x0)
a.x0 = b.x0;
if (a.y0 < b.y0)
a.y0 = b.y0;
if (a.x1 > b.x1)
a.x1 = b.x1;
if (a.y1 > b.y1)
a.y1 = b.y1;
if (a.x1 < a.x0 || a.y1 < a.y0)
return fz_empty_irect;
return a;
}
/*
Compute union of two rectangles.
Given two rectangles, update the first to be the smallest
axis-aligned rectangle that encompasses both given rectangles.
If either rectangle is infinite then the union is also infinite.
If either rectangle is empty then the union is simply the
non-empty rectangle. Should both rectangles be empty, then the
union is also empty.
*/
fz_rect
fz_union_rect(fz_rect a, fz_rect b)
{
/* Check for empty box before infinite box */
if (fz_is_empty_rect(b)) return a;
if (fz_is_empty_rect(a)) return b;
if (fz_is_infinite_rect(a)) return a;
if (fz_is_infinite_rect(b)) return b;
if (a.x0 > b.x0)
a.x0 = b.x0;
if (a.y0 > b.y0)
a.y0 = b.y0;
if (a.x1 < b.x1)
a.x1 = b.x1;
if (a.y1 < b.y1)
a.y1 = b.y1;
return a;
}
/*
Translate bounding box.
Translate a bbox by a given x and y offset. Allows for overflow.
*/
fz_rect
fz_translate_rect(fz_rect a, float xoff, float yoff)
{
if (fz_is_empty_rect(a)) return a;
if (fz_is_infinite_rect(a)) return a;
a.x0 += xoff;
a.y0 += yoff;
a.x1 += xoff;
a.y1 += yoff;
return a;
}
fz_irect
fz_translate_irect(fz_irect a, int xoff, int yoff)
{
int t;
if (fz_is_empty_irect(a)) return a;
if (fz_is_infinite_irect(a)) return a;
a.x0 = ADD_WITH_SAT(t, a.x0, xoff);
a.y0 = ADD_WITH_SAT(t, a.y0, yoff);
a.x1 = ADD_WITH_SAT(t, a.x1, xoff);
a.y1 = ADD_WITH_SAT(t, a.y1, yoff);
return a;
}
/*
Apply a transform to a rectangle.
After the four corner points of the axis-aligned rectangle
have been transformed it may not longer be axis-aligned. So a
new axis-aligned rectangle is created covering at least the
area of the transformed rectangle.
transform: Transformation matrix to apply. See fz_concat,
fz_scale and fz_rotate for how to create a matrix.
rect: Rectangle to be transformed. The two special cases
fz_empty_rect and fz_infinite_rect, may be used but are
returned unchanged as expected.
*/
fz_rect
fz_transform_rect(fz_rect r, fz_matrix m)
{
fz_point s, t, u, v;
if (fz_is_infinite_rect(r))
return r;
if (fabsf(m.b) < FLT_EPSILON && fabsf(m.c) < FLT_EPSILON)
{
if (m.a < 0)
{
float f = r.x0;
r.x0 = r.x1;
r.x1 = f;
}
if (m.d < 0)
{
float f = r.y0;
r.y0 = r.y1;
r.y1 = f;
}
s = fz_transform_point_xy(r.x0, r.y0, m);
t = fz_transform_point_xy(r.x1, r.y1, m);
r.x0 = s.x; r.y0 = s.y;
r.x1 = t.x; r.y1 = t.y;
return r;
}
s.x = r.x0; s.y = r.y0;
t.x = r.x0; t.y = r.y1;
u.x = r.x1; u.y = r.y1;
v.x = r.x1; v.y = r.y0;
s = fz_transform_point(s, m);
t = fz_transform_point(t, m);
u = fz_transform_point(u, m);
v = fz_transform_point(v, m);
r.x0 = MIN4(s.x, t.x, u.x, v.x);
r.y0 = MIN4(s.y, t.y, u.y, v.y);
r.x1 = MAX4(s.x, t.x, u.x, v.x);
r.y1 = MAX4(s.y, t.y, u.y, v.y);
return r;
}
fz_irect
fz_expand_irect(fz_irect a, int expand)
{
if (fz_is_infinite_irect(a)) return a;
a.x0 -= expand;
a.y0 -= expand;
a.x1 += expand;
a.y1 += expand;
return a;
}
/*
Expand a bbox by a given amount in all directions.
*/
fz_rect
fz_expand_rect(fz_rect a, float expand)
{
if (fz_is_infinite_rect(a)) return a;
a.x0 -= expand;
a.y0 -= expand;
a.x1 += expand;
a.y1 += expand;
return a;
}
/*
Expand a bbox to include a given point.
To create a rectangle that encompasses a sequence of points, the
rectangle must first be set to be the empty rectangle at one of
the points before including the others.
*/
fz_rect fz_include_point_in_rect(fz_rect r, fz_point p)
{
if (fz_is_infinite_rect(r)) return r;
if (p.x < r.x0) r.x0 = p.x;
if (p.x > r.x1) r.x1 = p.x;
if (p.y < r.y0) r.y0 = p.y;
if (p.y > r.y1) r.y1 = p.y;
return r;
}
/*
Test rectangle inclusion.
Return true if a entirely contains b.
*/
int fz_contains_rect(fz_rect a, fz_rect b)
{
if (fz_is_empty_rect(b))
return 1;
if (fz_is_empty_rect(a))
return 0;
return ((a.x0 <= b.x0) &&
(a.y0 <= b.y0) &&
(a.x1 >= b.x1) &&
(a.y1 >= b.y1));
}
fz_rect
fz_rect_from_quad(fz_quad q)
{
fz_rect r;
r.x0 = MIN4(q.ll.x, q.lr.x, q.ul.x, q.ur.x);
r.y0 = MIN4(q.ll.y, q.lr.y, q.ul.y, q.ur.y);
r.x1 = MAX4(q.ll.x, q.lr.x, q.ul.x, q.ur.x);
r.y1 = MAX4(q.ll.y, q.lr.y, q.ul.y, q.ur.y);
return r;
}
fz_quad
fz_transform_quad(fz_quad q, fz_matrix m)
{
q.ul = fz_transform_point(q.ul, m);
q.ur = fz_transform_point(q.ur, m);
q.ll = fz_transform_point(q.ll, m);
q.lr = fz_transform_point(q.lr, m);
return q;
}
int fz_is_point_inside_rect(fz_point p, fz_rect r)
{
return (p.x >= r.x0 && p.x < r.x1 && p.y >= r.y0 && p.y < r.y1);
}
int fz_is_point_inside_irect(int x, int y, fz_irect r)
{
return (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1);
}
int fz_is_point_inside_quad(fz_point p, fz_quad q)
{
// TODO: non-rectilinear quads
return fz_is_point_inside_rect(p, fz_rect_from_quad(q));
}