⚠️ 警告
本文根据 libass 源码编写。尽管 libass 项目以与 VSFilter 项目的兼容性为核心,但不保证 VSFilter 的逻辑和本文所描述的完全一致。
ToC
前言
这个问题发生在 2020 年 6 月 9 日米粒垃圾群
产生的效果是这样的:
可以看到,前半段还是正常的旋转,但后面就突然变慢了。
产生这个问题的原因很简单:它没有应用卡拉 OK 模板。但在没有应用卡拉 OK 模板的情况下,它究竟被理解成了什么呢?这就是这篇文章探索的内容。
太长不看
先说结论,如果直接只想要标题中这个问题的结果的话,那接下来的文章就不用看了。这一行其实相当于应用下面这个卡拉 OK 模板行的效果:
如果你对这个结论如何出现有所好奇的话,那接下来的部分就是为你而准备的了(笑)
!
表达式的处理
对这个问题的探究总共分为两部分,第一部分就是 !
表达式的处理了。在 \t
中,!line.duration!
这样的代码究竟被理解成了什么是这一部分需要探究的重点。
我们知道kara-templater
的作用下,!line.duration!
实际上是被替换成了 return (line.duration)
的运算结果。但在没有 kara-templater
的情景下,这样的替换显然是不存在的。也就是说,!line.duration!
被完整地当成了一个 \t
的参数。我们来看 libass
对 \t
是怎么定义的:
也就是说,这里对参数的处理都是交给 argtoll
这个函数进行的,ll
是 long long
的缩写。这里我们不去深究具体是怎么转换的,但是基本是这样的:一系列的 if
都没通过,又没找到合法的数字,于是最后变成了 **0**
。
好了,现在我们的代码变成了这样:
t2 = 0?
第二部分其实相当简单,只需要两行代码就够了:
到这里,可以说标题中提出的问题已经解决了。
认识 \t
说是认识 \t
,其实也只是认识 libass
中 \t
的实现罢了。我们知道,\t
最多是可以接收四个参数的t1
、t2
、accel
和 tags
。我们从上到下来看:
四参数
首先是 cnt == 3
的情况,这里的 cnt
指的是除最后一个参数之外的参数数量。3
代表着 t1
、t2
和 accel
都用到了,因此在这里就不存在默认情况了。
三参数
接下来是 cnt == 2
的情况。cnt == 2
意味着指定了 t1
和 t2
,而这里的 accel
,则被设置成了默认值 1
。
二参数
然后是 cnt == 1
的情况。这种情况下,反而是设置了 accel
,而将 t1
和 t0
都设置成了 0。
其他参数情况
最后是其他情况。这种情况下,所有值都会采用默认,也就是上面默认值的综合。
无视碰撞
对于 \t
而言,由于是与时间轴密切相关的效果,因此关闭了碰撞检测。于是这里就出现了一个可以利用的点:使用错误的 \t
关闭碰撞检测,以代替某些 \pos
的使用场景。
t2
为 0
这就是上面说过的了,这里略过。
nested
与速度因子 pwd
接下来这部分就比较特殊了。我们知道,\t
是可以指定 accel
的,而这个 accel
也就必须传入到对 \t
中使用的标签的解析中。
对于 t
小于 t1
的,其实就可以直接不显示了,这里将 k
设为 0。
对于 t
大于 t2
的,需要原样显示,因此 k
设为 1。
对于 t
位于 t1 ~ t2
的,需要按照指定的 accel
设置速度。设置公式为:
对于 nested
的情况,也就是目前的 \t
标签就在 \t
标签里的情况,这里直接将 pwr
覆盖为刚才计算出的 k
,也就是以内部 \t
的速度为准,不叠加 accel
。
忽略错误情况
对于参数数量不正确的情况,直接略过下面的处理。
解析标签
最后就是解析标签的步骤了,值得注意的是这里针对 end
的情况作了简单处理。
到这里为止,对 \k
的分析也就基本完成了。
结语
我个人是比较喜欢这种说来就来的旅行(分析)的。发现问题——解决问题——挖掘问题——总结问题,这样的学习过程给人带来的正反馈是最强的。
借着这个机会,或者说是契机吧,我简单了解了 \t
这个平时非常常用但却似乎不大了解的特效标签,也算是有所收获吧(
一时兴起,也就没考虑太多东西。有用就好(笑)