summaryrefslogtreecommitdiffstats
path: root/libass/ass_render.c
Commit message (Collapse)AuthorAgeFilesLines
* ass_render: Replace Segment with RectOneric2020-10-221-34/+34
| | | | | | | | | Contrary to what the comments suggest Segment does not use one absolute reference point and relative offsets, but two absolute points, making it practically identical to the previously defined Rect with a <-> y0, b <-> y1, ha <-> x0, hb <-> x1 For simplicity replace Segment with Rect.
* Return bool from ass_shaper_shapeOleg Oshmyan2020-10-221-1/+1
|
* Make harfbuzz a hard dependency; closes #199rcombs2020-10-221-4/+0
|
* Split glyph runs earlyOleg Oshmyan2020-10-191-40/+46
| | | | | | | | | | | | | | | | | | 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.
* Ignore font substitution when splitting combined bitmapsOleg Oshmyan2020-10-181-2/+0
| | | | | | | | | | | Well, that was easy. font->desc.family is the raw family name from FontName/\fn, barring the leading @ for vertical fonts. So, for reference, even if the script requests two different font names and neither exists on the system and font fallback picks the same font for both, is_new_bm_run will treat them as distinct. This is indeed the desired behaviour that matches VSFilter.
* Fix Scroll effects with rectangle \clip/\iclipOleg Oshmyan2020-10-181-2/+10
|
* Fix Scroll effect coordinatesOleg Oshmyan2020-10-181-2/+3
| | | | | | | | The coordinate computed for the text's top edge was actually used for the baseline of the text's top line, causing the first line's ascender to protrude up and the whole text to be shifted up by that ascender. (Note: in this context, bbox.y_min == text_info->lines[0].asc.)
* Support Banner/Scroll effects with \pos/\moveOleg Oshmyan2020-10-181-34/+33
|
* Support line breaks with Banner effectOleg Oshmyan2020-10-181-12/+5
| | | | | | | | | 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.
* ass_render_event: simplify clip conversion to screen coordinatesOleg Oshmyan2020-10-181-53/+15
| | | | | | | | The "depends on alignment" block is useless: if use_margins, then the coordinates will be overridden, but if not, then all of the various x/y2scr... calls delegate to x/y2scr_pos... Rewrite several lines using FFMIN/FFMAX to keep them short.
* Scale everything from script resolution if storage size is unknownOleg Oshmyan2020-10-151-3/+1
| | | | | | | | | | | | | | | | | | | With this, our output can no longer change visually if display resolution is varied via ass_set_frame_size while everything else is kept constant, e. g. when a video player window is being resized, regardless of API (mis)use and script/track properties. In particular, this stops our \blur from affecting a varying portion of the screen and our 3D transforms involving \frx or \fry from moving around as display resolution is being changed. FWIW, this makes our blur and 3D transforms match MPC-HC's when storage size is unset. They already matched before this commit when ScaledBorderAndShadow was true, but now they also match when it is false. This also makes our borders and shadows scale with display resolution, effectively making ScaledBorderAndShadow a no-op (always true) if storage size is unset. FWIW, this does *not* agree with MPC-HC, but it just seems sensibleā„¢.
* outline: fix overflows in outline processingDr.Smile2020-10-081-2/+6
| | | | | | | This commit enforces strict invariant on ASS_Outline to contain point coordinates into predetermined range. Fixes https://github.com/libass/libass/issues/431.
* Ignore metrics of trimmable whitespace on nonblank linesOleg Oshmyan2020-09-191-8/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Fixes https://github.com/libass/libass/issues/418. Fixes https://github.com/libass/libass/issues/427. Fixes https://github.com/mpv-player/mpv/issues/7712. A longstanding problem we had was that we ignored leading line breaks in events. We somewhat accidentally assigned line breaks zero height, and the code in `measure_text` that halved the height of empty lines explicitly ignored leading empty lines for some reason. #285 fixed both of these things: the former in e8d98dafb8d4d1fd80e9d398cfbef7db1e2ccb73, the latter in 5d03af99c6d8f43be973cb6dacb5d6dd0ada33b1. But life is not so simple! It turns out that VSFilter [discards metrics of the line break and leading/trailing spaces for lines that stay nonempty after whitespace trimming][1]. So we actually handled nonblank (trimmable-space-less) lines correctly *before* #285 and have been *mishandling* them since #285 landed, adding the line break's metrics when they should be ignored. This is not noticeable in plain text, but it affects cases when a line's contents have a different height from the line breaks: {\fs96}foo\N{\p1}m 0 0 l 0 1 1 1 1 0{\p0}\Nbar {\fs96}foo\N{\fs1}bar{\fs96}\Nbaz (the middle line should be 1px high, not 96px) [1]: https://github.com/Cyberbeing/xy-VSFilter/blob/3.0.0.306/src/subtitles/RTS.cpp#L1401-L1422 More complicated cases with trimmable spaces have never been handled correctly, but this is nevertheless a regression. To fix this: * move the `trim_whitespace` call before the `measure_text` call (they seem independent, so this is easy), * mark trimmed whitespace with a new dedicated flag in addition to `.skip` so it can be distinguished from invisible glyphs, * clear accumulated (line-leading-whitespace) metrics when each line's first non-trimmed-whitespace glyph is found, * skip trimmed-whitespace glyphs when the line is already proven nonblank, because they are certainly line-trailing whitespace. Note: line breaks themselves do have `.skip` and `.is_trimmed_whitespace`. Note: our `.linebreak` is set on the glyph *after* the line break. As a result, a nonblank line will include the metrics only of the glyphs that (would) remain after trimming leading and trailing whitespace (including the line break), while a blank line includes the metrics of all glyphs from its first in-line glyph up to and including the terminating line break (if any). At the same time, line height is only halved for lines that are truly empty and don't even contain any whitespace. (Before the halving, the line height comes from the line break glyph.) This matches VSFilter.
* Use bool and true/false assignments for GlyphInfo::skipOleg Oshmyan2020-09-191-5/+5
|
* Don't halve nonempty line height even if wholly invisibleOleg Oshmyan2020-09-191-1/+1
| | | | | VSFilter compatibility: VSFilter does no special handling of invisible control characters and sees the line as nonempty.
* render: match VSFilter's behavior when painting fill in shadow/borderrcombs2020-08-301-10/+27
| | | | | | | Some releases rely on this. See corresponding VSFilter code: https://github.com/Cyberbeing/xy-VSFilter/blob/cf8f5b27de77fe649341bfab0fdfd498e1ad2fa6/src/subtitles/RTS.cpp#L1270 https://github.com/Cyberbeing/xy-VSFilter/blob/cf8f5b27de77fe649341bfab0fdfd498e1ad2fa6/src/subtitles/RTS.cpp#L1291
* Disable bidi bracket matching unless enabled via ASS_FeatureOleg Oshmyan2020-07-141-0/+5
| | | | | | | | | | | | | | | | | | | | | Bracket matching is incompatible with VSFilter (even on modern Windows), so disable it by default. But as it's generally a good thing (and 100% more compliant with current Unicode), keep it available as an ASS_Feature. It can be toggled individually or enabled as part of the catch-all ASS_FEATURE_INCOMPATIBLE_EXTENSIONS feature. If libass is compiled against FriBidi older than 1.0, bracket matching is impossible. Signal this at runtime by failing to recognize the ASS_FEATURE_BIDI_BRACKETS feature. This way, clients who want to use bracket matching can set the feature without any compile-time checks for FriBidi and can be freely linked against libass that is itself compiled against any version of FriBidi; and yet they can detect at runtime whether the feature is actually enabled. Fixes https://github.com/libass/libass/issues/374.
* shaper: drop prealloc parameter of ass_shaper_newOleg Oshmyan2020-07-121-1/+1
| | | | | | The only prealloc value actually used is 0, which is not useful and invokes implementation-defined (and potentially obsolescent as per C11 DR400) behavior.
* Perform 3D transforms relative to shadow positionOleg Oshmyan2020-07-081-2/+5
| | | | | | | | | | | | | | | | | | | | Fixes https://github.com/libass/libass/issues/141. Fixes https://github.com/libass/libass/issues/300. VSFilter's original intention with this seems to have been to transform the event as a whole, including the shadows as an integral part, as opposed to transforming the shadows separately: imagine the event being rasterized into a single image including the shadows, and then that image being transformed. Unfortunately, due to a caching bug, what actually ends up happening is that the shadow is transformed the intended way, but the main body is then simply shifted back by \shad from the transformed & rasterized shadow, instead of being transformed & rasterized on its own. The result seems sensible if you look at the shadow only but incomprehensible if you look at the main body. Transforms with \shad are actually used and this behavior is relied upon, as evidenced by https://github.com/libass/libass/issues/300 (in which the main body is made invisible and the shadow is used instead of it due to having \t-animatable position and full-area blur despite \bord).
* Take border into account during collision detectionOneric2020-07-051-8/+26
| | | | | | | | closes #304 With this commit the padding of the BorderStyle=4 box, given by \shad, is no longer measured from the text without borders, but from the border of the text. (Alternative description: padding changed from \shad to \shad+\bord)
* Refactor: Avoid code duplication in measure_textOneric2020-07-051-6/+11
|
* Match *VSF's shift direction for \an(4|5|6)Oneric2020-07-051-1/+1
| | | | | | closes #143 As libass doesn't support the 'Collsions' header, we are only concerned with the default stacking direction of *VSF here
* Fix incorrect collision detection coordinates with anamorphic videoOleg Oshmyan2020-07-051-1/+1
| | | | device_x is in anamorphic coordinates, the product of x2scr (not x2scr_scaled).
* renderer: fix default aspect ratio calculationOleg Oshmyan2020-07-051-4/+4
| | | | | | The ratio was accidentally flipped. Use the actual video size, not the screen size that includes margins.
* renderer: fix subtitles to full screen frame iff use_marginsOleg Oshmyan2020-07-051-44/+48
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Based on a commit by wm4. Nowadays, margins are used by players such as mpv to implement video zoom & pan, although this was not expected when margins were first implemented in libass. This results in unpleasant rendering when panning too far, and it is argued that subtitles should not change size or move when panning and zooming at all. libass also makes an attempt to keep subtitles on screen even when the use of margins is disabled. This is unintuitive and prone to break. Fix this by strictly separating events which render as if they were part of the video, and events which should use margins. The latter will now use the entire screen as canvas, rather than using the video frame. This actually simplifies the various y2scr functions. To preserve scaling (mainly for styled subtitles where line breaks are carefully chosen based on font/video size ratio) and to avoid badly stretching out things like ASS Margins due to aspect ratio differences between video and screen, estimate the unpanned & unzoomed video size from the video aspect ratio and the screen size, and base all scaling on that. This means that if the user plays a video in letterboxed mode without extra margins, they get the same scaling as if they were playing the same video with the same video rectangle size without any margins at all (with some elements merely spaced out to make use of the black bars); and when they zoom & pan afterwards, the subtitles don't move or change size. This changes behavior even with ass_set_use_margins(_, 0). Before this, normal dialogue was forced into the visible video area (if negative margins were set); now it renders it as if it were part of the video. This also changes the behavior of left and right margins even with ass_set_use_margins(_, 1). Before this, normal dialogue was forced into the visible video width (with both positive and negative margins); now it renders across the entire width of the screen/window. For 4:3 video letterboxed on 16:9 screen, this means text will cross the edges of the video, which may look worse than before.
* renderer: remove bogus clip rect for unclipped subtitleswm42020-07-051-0/+6
| | | | | | | | | | | | | | Normal subtitles in use_margins mode, which do not have \clip tags or similar, were clipped in a nonsensical way. It was especially visible when moving subtitles up with ass_set_line_position(). This happened because state.clip_* is initialized with a clipping rectangle for the video area. Later it tries to translate the clipping rect accordingly, but this does not make much sense if you account for the margins. Just reset the clipping rect to the screen in these cases. explicit=0 is enough to know that the clipping rect was never explicitly set.
* ass_render: fix crash on parse_events failure after a drawingrcombs2020-05-271-1/+3
| | | | Closes #397
* render: silence a couple LLVM static analyzer warningsrcombs2020-05-261-4/+4
| | | | | | | | The while() checked the pointer for nullness, so the analyzer assumed it could potentially be null, and thus warned on the reference to it later. Using a do/while instead means we're only checking for subsequent linked-list entries, which was the intent here anyway, and avoids the warning.
* render: handle failure to realloc max_glyphsrcombs2020-05-261-10/+14
|
* render: handle more allocation failures in ass_renderer_initrcombs2020-05-261-12/+19
|
* render: allow passing null to ass_renderer_donercombs2020-05-261-0/+3
|
* render: silence warningRodger Combs2019-11-071-1/+1
|
* renderer: fix incorrect deallocationDr.Smile2019-09-261-5/+4
| | | | | | | | | shift_event() can change "bitmap" field of ASS_Image struct so direct deallocation is no longer possible. This commit introduces additional field "buffer" into ASS_ImagePriv for that purpose. Fixes https://github.com/libass/libass/issues/310.
* Fix bunch of UBDr.Smile2019-05-211-2/+4
| | | | Found by Coverity Scan and -fsanitize=undefined
* renderer: synchronize glyph motion to help composite cacheDr.Smile2019-05-201-14/+29
| | | | | | | | | | | | Slow movement of one glyph looks like periodic jumps by quantization step. In case of multiple glyphs at different subpixel shifts jumps of individual glyphs occur at different frames. That leads to performance penalty due to composite image regeneration at every such jump. This commit aligns glyphs in such a way that all jumps coincide at the same frames, greatly improving performance of \move commands. That optimization also helps in case of fast motion.
* outline: rearrange allocationsDr.Smile2019-05-201-8/+4
|
* Move outline transformations to ass_outline.cDr.Smile2019-05-201-21/+8
| | | | | | | | | This also potentially improves performance by copying and transforming in a single operation rather than copying first and then transforming the result. Also transformation function is specialized for case where expensive perspective division is not necessary.
* renderer: quantize blur radius and shadow offsetDr.Smile2019-05-201-8/+64
|
* renderer: eliminate use of bitmap pointers as mode flagsDr.Smile2019-05-201-42/+23
| | | | | | Render logic should depend only on input subs and not on some internal state such as bitmap pointers. That can prevent incorrect behavior in case of allocation failure.
* renderer: correctly handle case of small but nonzero \bordDr.Smile2019-05-201-1/+4
| | | | Fixes https://github.com/libass/libass/pull/309.
* renderer: rearrange render flagsDr.Smile2019-05-201-19/+28
| | | | Compatibility flag FILTER_DRAW_SHADOW has removed completely.
* bitmap: make ass_synth_blur() work on single bitmapDr.Smile2019-05-201-2/+3
| | | | | | Now ass_synth_blur() blurs one bitmap only. Higher level decisions (to blur or not to blur) have moved outside of that function.
* bitmap: remove level of indirection in bitmap functionsDr.Smile2019-05-201-113/+115
| | | | | This allows to use Bitmap struct directly as cache value and to remove bunch of unnecessary allocations.
* renderer: improve usability of rectangle_combine()Dr.Smile2019-05-201-6/+6
|
* renderer: improve handling of subpixel shiftDr.Smile2019-05-201-33/+41
| | | | | | | Integral pixel shift is extracted in quantization function now, taking account of full glyph transformation and not only translation part of it. It makes program logic more straight and ensures that subpixel shift from cache key never exceed full pixel.
* renderer: implement correct error estimation for strokingDr.Smile2019-05-201-74/+109
| | | | | Accuracy of border outline calculation should depend on subsequent transformation.
* renderer: move outline stroking immediately before rasterizationDr.Smile2019-05-201-64/+56
|
* Consolidate and quantize all transformationsDr.Smile2019-05-201-352/+487
| | | | | | | | | | | | | | This commit defers all outline transformations until rasterization stage. Combined transformation is then quantized and used as bitmap key. That should improve performance of slow animations. Also caching of initial and stroked outlines and bitmaps is now separate in preparation to proper error estimation for stroker stage. Note that Z-clipping for perspective transformations is now done differently compared to VSFilter. That clipping is mostly safety feature to protect from overflows and divisions by zero and is almost never triggered in real-world subtitles.
* renderer: fix potential memory leakDr.Smile2019-05-191-1/+3
|
* font: extract transformation from ass_font_get_glyph()Dr.Smile2019-05-191-9/+9
|
* drawing: extract transformation from parsingDr.Smile2019-05-191-28/+38
|
* cache: construct cache values only from corresponding keysDr.Smile2019-05-191-318/+313
| | | | | | | | | | | | | | | | | | | | 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.
* renderer: rewrite measure_text() to correctly account for leading newlinesDr.Smile2019-05-191-31/+22
|
* font: remove dependency on symbol codesDr.Smile2019-05-191-3/+5
| | | | | Leading newlines are now rendered, but still incorrectly: at full height rather than at half-height as required.
* renderer: use C99 features in some functionsDr.Smile2019-05-191-37/+30
| | | | | Note that return value is reversed in parse_events(), ass_render_event() and ass_start_frame() functions.
* drawing: separate drawing text reading from outline constructionDr.Smile2019-05-191-51/+46
| | | | Purpose of this commit is to simplify logic behind drawing handling.
* Eliminate advance.y from drawings and glyphsDr.Smile2019-05-191-12/+8
| | | | | Drawings always have advance.y = 0 and FreeType guarantees that for horizontal writing.
* drawing: remove unnecessary fields from ASS_Drawing structDr.Smile2019-05-191-2/+0
|
* font: remove unused fieldDr.Smile2019-05-191-2/+1
|
* render: simplify detection of hard overridesDr.Smile2018-01-211-7/+7
| | | | | | | | | | | Previously each \r triggered full rescan of event string. After this commit such scanning is done once in init_render_context(). Additionally some lines have moved around to correctly account for state.evt_type (calculated in apply_transition_effects) and state.explicit (used in reset_render_context). That should fix cases with incorrectly applied style overrides for subs with banner scrolling effect before the first \r.
* parse_tags: handle argumentless \t inside \t() like VSFilterOleg Oshmyan2018-01-081-1/+1