| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
|
|
|
| |
Also remove unneeded event arg
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
If static libass is linked into a binary defining functions
of the same name there will be issues. To avoid this use an
ass_ prefix for namespacing.
Before this commit we already did this for most but not yet
all internal API.
read_file is renamed to ass_load_file as ass_read_file
already exists as a public API function. All other functions
are simply prefixed with ass_.
Fixes: https://github.com/libass/libass/issues/222
Fixes: https://github.com/libass/libass/issues/654
|
|
|
|
|
|
| |
They are both used only once outside of ass_parse.c
to apply fade, so instead export one function handling
VSFilter-compatible fade application.
|
|
|
|
|
|
| |
Although declared and defined in ass_utils.{h,c},
those functions are only used in one other file
and aren't useful at other places.
|
|
|
|
| |
It has not been used since 7af78014635dbe81c3328405aa68dd2cfef94bc4.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
\kt allows to set the karaoke timing offset to a value other than the
sum of preceeding karaoke durations. Notably this means multiple karaoke
sequences of one Event can be ative at the same time.
Like in VSFilter, \kt is available regardless of the format version.
Using \kt after a karaoke tag in the same override sequence
always makes the preceeding karaoke act as if already completed.
Using \kt within a run resets timing for the next karaoke run.
Addresses part of https://github.com/libass/libass/issues/461.
Further support for v4++ requires at least an ABI break.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Discovered by OSS-Fuzz.
For sufficiently large valued or numerous karaoke tags
the addition to effect_skip_timing can overflow.
Since the operands were signed, this was undefined behaviour.
For utmost VSFilter compatibility, we'd like to ideally emulate its
effective wraparound behaviour. Although the exact order pf calculation
differs, merely ensuring our values safely wraparound too should yield
the same end result.
To that end, we first change the types of the relevant members to
int32_t guaranteeing the same size as in VSFilter and complement-of-two
representation and then also cast one operand to uint32_t.
Through integer promotion both operands will be treated as unsigned,
so any overflow will be (perfectly defined) unsigend overflow.
The final implicit cast back to int32_t is technically implementation
defined and may raise an "implementation-defined signal", but due to
the complement-of-two semantics in pratice we just get a wraparound
matching the wraparaound expected for the original signed
complement-of-two overflow, but while avoiding UB.
Compilers appear to emit the same machine code on x86 with or without
the uint32_t cast, if any optimisations are enabled and otherwise
at most minor differences.
Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47993
|
|
|
|
|
|
| |
If state.effect_timing is zero adding it does nothing.
Also checking for zero and branching is likely at least
as costly as adding zero.
|
|
|
|
|
|
| |
Omission in 85c8c6d7be14cc2602b92ec715834b9c1069a325.
Of course, the same reasoning as in the mentioned commit
also applies to (at least) all other karaoke tags.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Usually the delay parameter of legacy effects is scaled
to be relative to the PlayRes canvas. This happens explicitly
in VSFilter and automatically in libass.
However, this scaling in VSFilter happens _before_ applying
max(delay, 1) which means, if e.g. delay=0 it ends up as
1 ms per _storage pixel_. To get the same effect in libass we
must explicitly "unscale" the fallback for small or negative delays.
VSFilter also casts the scaled delay value to int afterwards,
which can lead to noticeable differences if the scaled value isn't
an integer. To emulate this in libass we do not want delay
to be an int (which would also ruin the unscaling for delay=0),
but we need to convert our already PlayRes-relative value to
a storage-relative one, then cast to int and finally convert
back to a PlayRes-relative value.
This rounding error can already be observed after just one second
for PlayResX=StorageX/8 and delay=25.
|
|
|
|
|
|
|
| |
This caused animations to out-of-range alpha values to be clipped to the
uint8_t range _before_ performing the lerp, rather than only after.
VSFilter only truncates after. Using e.g. `\1a0\t(\1a1FF)` should run through
the entire 0-FF range _twice_.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Casting floating point values to an integer type is undefined
behaviour if it's not a regular number or the integral part cannot be
represented in the integer type.
This fixes issues found by UBSAN in libass' public OSS-Fuzz corpus
where NAN ("be") or a too large value ("k") was casted to int.
Sample IDs (one instance each there are duplicates):
OSSFuzz-3617a28ea3900c2603059049ce4c70c01a535a3e
OSSFuzz-292a3032ea273cc9dbaaa0a4291dd84e0cc07c65
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This continues our transition towards fixed-width types to
improve VSFilter compatibility regardless of the platform libass
is compiled for.
Local variables are also switched to int32_t,
but struct members are left as is for now.
By dropping mystrtoi, which was only used by argtoi, we also fix
float-cast-overflow issues in it identified by UBSAN in libass'
public OSS-Fuzz corpus. mystroi32 does not suffer from this problem.
Sample ID (one instance there are duplicates):
OSSFuzz-123cf9a553c5745854037a52e87947721257f1f3fb
|
|
|
|
| |
That eliminates most uses of strdup() in the rendering process.
|
|
|
|
| |
This mainly affects trimmed trailing whitespace.
|
| |
|
|
|
|
| |
This matches VSFilter.
|
|
|
|
|
| |
This allows decisions based on the angle values to be
slightly more accurate and might save us a few flops.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Don't break runs when zero-duration karaoke starts unless the karaoke
*type* differs. The zero-duration karaoke block ends up glued to the
previous block (if any). In case of subsequent karaoke override tags,
like {\k100\k0}, the intervening tags will advance the next karaoke
block's start time, but not this combined block's start or end time.
Of course, runs may still be broken in the same place if there's another
reason for a run break besides karaoke, so zero-duration karaoke blocks
can still occur. Run breaks that have no karaoke tags at all also still
produce zero-duration karaoke blocks (if there is karaoke at all).
|
|
|
|
|
|
|
|
| |
Our frame timestamps are long long. Don't truncate them to int here.
This also avoids overflow in (tm_end - tm_start) if \k... tag arguments
are big, although, of course, parse_tags still reads them in as doubles
to begin with and their conversion from double may still be undefined.
|
|
|
|
| |
Both ways make sense, but traditional VSFilter does it this way.
|
|
|
|
| |
Fixes \k and \ko in https://github.com/libass/libass/issues/357.
|
| |
|
|
|
|
|
|
|
| |
In particular, don't divide by zero given \kf0.
This fixes https://github.com/libass/libass/issues/124.
The order is important: \kf accepts negative values.
|
|
|
|
|
|
|
|
|
|
|
| |
This matches VSFilter.
In particular, compared to what we did before, karaoke blocks additionally
end when an override tag changes something, as well as on any line break
(after the trimmed leading whitespace on the new line) and after any trimmed
leading whitespace on the first line. The text that follows the break has
a zero karaoke duration, and its karaoke effect starts immediately after
the karaoke effect ends for the block before the break.
|
| |
|
|
|
|
|
|
|
| |
We already ensure at creation that all styles have a non-null FontName.
However font family strings are strduped at various places and NULLs
cannot be fully avoided, since already the very first font strdup may
fail, so additional checks are required.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The "bitmap runs" that are currently used only when combining bitmaps
are also relevant at other stages, mainly for VSFilter compatibility.
They are not currently used because this information is not available
until bitmap combining (except during shaping, which has its own "shape
runs" that duplicate a good chunk of the "bitmap run" logic).
Move some code around to compute run boundaries as early as possible.
This lays the foundation for future commits that will make use this
information in more places where it can simplify code or improve
VSFilter compatibility.
For VSFilter compatibility, rather than break runs immediately upon line
breaks, break runs after line-leading whitespace, even in the first line.
These runs correspond to VSFilter's CWord instances.
|
| |
|
|
|
|
| |
This matches VSFilter.
|
|
|
|
| |
VSFilter does no such thing.
|
| |
|
|
|
|
| |
That's what VSFilter does.
|
|
|
|
|
|
|
|
|
| |
Make Banner default to \q2, but allow explicit line breaks and \q overrides.
Justify the lines according to \a etc., and wrap lines as usual if \q is
overridden, but make sure to keep the left/right edge of the whole event flush
with the edge of the screen at the event's start time as required by Banner.
This is what VSFilter does.
|
|
|
|
|
|
| |
For what it's worth, VSFilter does this too, and our mult_alpha
now gives the same results as the corresponding code in VSFilter
for all possible inputs.
|
|
|
|
|
|
|
|
|
|
|
|
| |
This function is passed alpha values from \fade, which are restricted
to nonnegative values but have no upper limit. Given a large value,
the subtraction and the multiply-divide could wrap around or even
overflow (in case of integer promotion to signed int). Overflow
is undesirable due to undefined behavior, and wraparound is also
undesirable due to VSFilter compatibility.
Rewrite the expression as a simpler equivalent that does not need to deal
with negative numbers and works correctly regardless of integer promotion.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We generally support 64-bit timestamps. However, for better
VSFilter compatibility and to avoid overflow, when handling
\t, \move and \fad(e), parse timestamps with 32 bits like
VSFilter does and perform wraparound 32-bit subtraction.
To match VSFilter results when the parsed arguments are large
but the video/event durations are short, clip the current
frame time and event duration to 32 bits during this.
Note: we generally handle large event and video timestamps differently
from VSFilter (which uses 32 bits throughout) regardless of this commit,
so in a sense, this commit actually breaks our saner rendering of long-
duration events while it remains VSFilter-incompatible. (It does not break
large event/video timestamps per se as long as each individual event's
duration fits in 31 bits.) The gain is VSFilter-compatible rendering when
all video/event timestamps fit in 31 bits but override tags have values
that don't (or whose differences don't) fit.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=542.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=602.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=624.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2721.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=3880.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11632.
Closes https://github.com/libass/libass/pull/337.
|
|
|
|
|
|
|
|
|
|
|
| |
If t3 is initially negative, it should be set to a value larger
than the duration of the event. This triggers the `now < t3` branch
in interpolate_alpha (if none of the earlier branches are taken).
The same effect can be achieved by setting t3 to the duration itself.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=531.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=3905.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11736.
|
|
|
|
|
|
|
|
|
| |
The value may be bigger than INT32_MAX as long as its *integral part* isn't.
Furthermore, make sure to avoid UB on odd platforms where int32_t limits
aren't exactly representable in double, as this isn't too hard. Do assume
that they're representable at all though. We won't necessarily match
x86 values on such platforms, but we will avoid the undefined behavior.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Providing a negative acceleration to \t could result in undefined behavior due
to overflow in float->int conversion. This codifies the same behavior we
currently have on x86, which matches vsfilter's, without actually invoking UB.
Additionally, vsfilter color animations work subtly differently than ours did.
We used to lerp each individual color component's byte value, while vsfilter
performs the lerp on the component _in place within a larger int_. This didn't
result in major issues for most cases, but could probably result in subtle
rounding errors, and gave vastly different results for \t with negative
acceleration.
Negative \t acceleration is probably completely useless, but our behavior was
wacky in a different way from vsfilter's, and I'd rather have portable
wackiness than libass-specific wackiness.
It might still be possible to invoke UB in negative-acceleration \t using tags
other than colors; we should fix those cases as well.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The assertion in commit 66cef6774386d558e1e39096db926d677dad6882
fires on the following ASS code:
\t(\t(foobar,)
(where "foobar" is any nonblank sequence that does not contain a backslash)
The outer \t is parsed as having a single argument "\t(foobar,"
including the comma. The inner \t is parsed as having a single
argument "foobar" *excluding* the comma. As a result, in the inner
parse_tags, args[cnt].end == end - 1 && nested, and the assert fires.
This is because arguments that contain backslashes are parsed
differently from arguments that do not. But if the argument to \t
contains no backslashes, why bother parsing it at all?
It clearly has no override tags and affects nothing.
Rather than try to make the assert more clever (and more convoluted),
this commit skips parsing the last \t argument if it has no backslash.
The assert is now valid. This probably does not significantly affect
parsing speed in either direction.
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25796.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Before commit 6835731c2fe4164a0c50bc91d12c43b2a2b4e799,
parse_tags used to recurse for each nested \t(). The depth
of this recursion was not limited, and each \t in \t(\t(\t(...
added another level. This could lead to stack overflow.
Since that commit, parse_tags still recurses, but at most once:
it is called with nested=false at the top level and recurses with
nested=true for the outermost \t() (except rare cases in which
even this one level of recursion is avoided). Parsing stops at
the closing ) both for the outermost \t() and for any inner \t()
nested inside it, so the inner recursive call cannot recurse further.
This was not immediately obvious when reading the code,
and therefore it was not obvious that stack overflow is avoided.
Make it so by adding an assertion.
|
|
|
|
|
|
|
|
|
|
| |
Yes, this actually makes a major difference on some tag-heavy scripts.
This lets the whole function get inlined, rather than making a call out
to the libc's strcmp implementation. The libc's version is likely
much faster on longer strings, but we're only ever comparing against
strings that are a few characters long, so that doesn't really matter.
It also avoids making an extra pass for the strlen.
|
| |
|
|
|
|
|
| |
This previously gave the pre-transition value; VSFilter's behavior is to give
the post-transition value.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit forces construction of cache values using only data
available in its companion keys. That ensures logical correctness:
keys are guaranteed to have all the necessary data, and prevents
accidental collisions.
Most fixes of cache logic correspond to minor problem
when rendering is done with double parameter but cache key stores
its approximate fixed-point representation. The only serious problem
is missing scale of clip drawing. Also this commit removes unused
scale parameters from glyph metrics cache key.
Due to missing scale clip shapes that differed only in scale
treated by cache system as identical. That can lead to incorrect reuse
of cached bitmap of different scale instead of correct one.
The only hack left is in glyph metrics cache with its
unicode >= VERTICAL_LOWER_BOUND check.
|
|
|
|
| |
Purpose of this commit is to simplify logic behind drawing handling.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
\t with no parantheses inside \t() resets the animation parameters
of the \t() for subsequent tags, so they are animated as if the \t()
was the single-argument version regardless of the actual number
of arguments the \t() has.
Equivalently, you could say parentheses are implied for \t inside \t().
For example, \t(20,60,\frx0\t\fry0\frz0) animates \frx from 20 to 60 ms
and animates \fry and \frz for the whole duration of the line,
just like \t(20,60,\frx0)\t(\fry0\frz0) or \t(20,60,\frx0\t(\fry0\frz0)).
Technically, VSFilter simply resets the animation parameters for any \t
it encounters but parses the embedded tags only if the \t has the right
number of arguments. However, top-level animation parameters don't matter
because top-level tags are not animated, while any nested \t that has
parentheses terminates the containing \t because they share the closing
parenthesis, so the fact that a nested \t with empty parentheses or with
at least four arguments changes the animation parameters also doesn't
matter because the containing \t immediately ends and the changed
parameters have nothing to apply to. Thus the only situation where
this has a visible effect is a nested \t without parentheses.
Closes https://github.com/libass/libass/pull/296.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=4892
(stack overflow on deeply nested \t()).
This is possible because parentheses do not nest and the first ')'
terminates the whole tag. Thus something like \t(\t(\t(\t(\t() can be
read in a simple loop with no recursion required. Recursion is also
not required if the ')' is missing entirely and the outermost \t(...
never ends.
See https://github.com/libass/libass/pull/296 for more backstory.
|
|
|
|
| |
This commit is mostly transparent to `git blame -w`.
|
|
| |