diff --git a/vskernels/kernels/abstract.py b/vskernels/kernels/abstract.py index f5f1750..9b55204 100644 --- a/vskernels/kernels/abstract.py +++ b/vskernels/kernels/abstract.py @@ -9,7 +9,8 @@ from vstools import ( CustomIndexError, CustomRuntimeError, CustomValueError, FieldBased, FuncExceptT, GenericVSFunction, HoldsVideoFormatT, KwargsT, Matrix, MatrixT, PropEnum, T, VideoFormatT, check_correct_subsampling, - check_variable_resolution, core, depth, expect_bits, get_subclasses, get_video_format, inject_self, vs, vs_object + check_variable_resolution, core, depth, expect_bits, fallback, get_subclasses, get_video_format, inject_self, vs, + vs_object ) from ..exceptions import UnknownDescalerError, UnknownKernelError, UnknownResamplerError, UnknownScalerError @@ -175,6 +176,10 @@ def __init_subclass__(cls) -> None: if mro: raise CustomRuntimeError('You must implement kernel_radius when inheriting BaseScaler!', reason=cls) + @staticmethod + def _wh_norm(clip: vs.VideoNode, width: int | None = None, height: int | None = None) -> tuple[int, int]: + return (fallback(width, clip.width), fallback(height, clip.height)) + @classmethod def from_param( cls: type[BaseScalerT], scaler: str | type[BaseScalerT] | BaseScalerT | None = None, /, @@ -217,8 +222,10 @@ class Scaler(BaseScaler): @inject_self.cached @inject_kwargs_params def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any ) -> vs.VideoNode: + width, height = Scaler._wh_norm(clip, width, height) check_correct_subsampling(clip, width, height) return self.scale_function(clip, **_norm_props_enums(self.get_scale_args(clip, shift, width, height, **kwargs))) @@ -270,8 +277,10 @@ class Descaler(BaseScaler): @inject_self.cached @inject_kwargs_params def descale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any + self, clip: vs.VideoNode, width: int | None, height: int | None, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any ) -> vs.VideoNode: + width, height = self._wh_norm(clip, width, height) + check_correct_subsampling(clip, width, height) field_based = FieldBased.from_param_or_video(kwargs.pop('field_based', None), clip) diff --git a/vskernels/kernels/complex.py b/vskernels/kernels/complex.py index 39b3af0..44f297a 100644 --- a/vskernels/kernels/complex.py +++ b/vskernels/kernels/complex.py @@ -77,7 +77,8 @@ class _BaseLinearOperation: def _linear_op(op_name: str) -> Any: @inject_kwargs_params def func( - self: _BaseLinearOperation, clip: vs.VideoNode, width: int, height: int, + self: _BaseLinearOperation, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), *, linear: bool = False, sigmoid: bool | tuple[Slope, Center] = False, **kwargs: Any ) -> vs.VideoNode: @@ -110,7 +111,8 @@ class LinearScaler(_BaseLinearOperation, Scaler): @inject_self.cached @inject_kwargs_params def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), *, linear: bool = False, sigmoid: bool | tuple[Slope, Center] = False, **kwargs: Any ) -> vs.VideoNode: ... @@ -123,7 +125,8 @@ class LinearDescaler(_BaseLinearOperation, Descaler): @inject_self.cached @inject_kwargs_params def descale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), *, linear: bool = False, sigmoid: bool | tuple[Slope, Center] = False, **kwargs: Any ) -> vs.VideoNode: ... @@ -153,7 +156,7 @@ def _get_kwargs_keep_ar( return kwargs def _handle_crop_resize_kwargs( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift], + self, clip: vs.VideoNode, width: int | None, height: int | None, shift: tuple[TopShift, LeftShift], sar: Sar | bool | float | None, dar: Dar | bool | float | None, **kwargs: Any ) -> tuple[KwargsT, tuple[TopShift, LeftShift], Sar | None]: kwargs.setdefault('src_top', kwargs.pop('sy', shift[0])) @@ -199,11 +202,14 @@ def _handle_crop_resize_kwargs( # type: ignore[override] @inject_self.cached @inject_kwargs_params def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), *, + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), *, border_handling: BorderHandling = BorderHandling.MIRROR, sar: Sar | float | bool | None = None, dar: Dar | float | bool | None = None, keep_ar: bool = False, **kwargs: Any ) -> vs.VideoNode: + width, height = Scaler._wh_norm(clip, width, height) + check_correct_subsampling(clip, width, height) const_size = 0 not in (clip.width, clip.height) @@ -231,13 +237,15 @@ class ComplexScaler(LinearScaler, KeepArScaler): @inject_self.cached @inject_kwargs_params def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), *, border_handling: BorderHandling = BorderHandling.MIRROR, sar: Sar | bool | float | None = None, dar: Dar | bool | float | None = None, keep_ar: bool = False, linear: bool = False, sigmoid: bool | tuple[Slope, Center] = False, **kwargs: Any ) -> vs.VideoNode: + width, height = Scaler._wh_norm(clip, width, height) return super().scale( clip, width, height, shift, sar=sar, dar=dar, keep_ar=keep_ar, linear=linear, sigmoid=sigmoid, border_handling=border_handling, diff --git a/vskernels/kernels/docs.py b/vskernels/kernels/docs.py index bf5c02d..a6010dc 100644 --- a/vskernels/kernels/docs.py +++ b/vskernels/kernels/docs.py @@ -21,7 +21,8 @@ def __init__(self, b: float = 0, c: float = 1 / 2, **kwargs: Any) -> None: @inject_self.cached def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[float, float] = (0, 0), **kwargs: Any + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[float, float] = (0, 0), **kwargs: Any ) -> vs.VideoNode: """ Perform a regular scaling operation. @@ -34,6 +35,7 @@ def scale( # type: ignore[override] :rtype: ``VideoNode`` """ + width, height = self._wh_norm(clip, width, height) return core.resize.Bicubic( clip, width, height, src_top=shift[0], src_left=shift[1], filter_param_a=self.b, filter_param_b=self.c, **self.kwargs, **kwargs @@ -41,7 +43,7 @@ def scale( # type: ignore[override] @inject_self.cached def descale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[float, float] = (0, 0), **kwargs: Any + self, clip: vs.VideoNode, width: int | None, height: int | None, shift: tuple[float, float] = (0, 0), **kwargs: Any ) -> vs.VideoNode: """ Perform a regular descaling operation. @@ -54,6 +56,7 @@ def descale( # type: ignore[override] :rtype: ``VideoNode`` """ + width, height = self._wh_norm(clip, width, height) return depth(core.descale.Debicubic( depth(clip, 32), width, height, b=self.b, c=self.c, src_top=shift[0], src_left=shift[1], **kwargs ), clip) diff --git a/vskernels/kernels/impulse.py b/vskernels/kernels/impulse.py index e3388b6..fa7d13d 100644 --- a/vskernels/kernels/impulse.py +++ b/vskernels/kernels/impulse.py @@ -44,9 +44,10 @@ def __init__(self, impulse: Sequence[float], oversample: int = 8, taps: float = @inject_self.cached @inject_kwargs_params def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, shift: tuple[TopShift, LeftShift] = (-0.125, -0.125), **kwargs: Any ) -> vs.VideoNode: + width, height = self._wh_norm(clip, width, height) return super().scale(clip, width, height, shift, **kwargs) diff --git a/vskernels/kernels/placebo.py b/vskernels/kernels/placebo.py index afc005b..62e42ff 100644 --- a/vskernels/kernels/placebo.py +++ b/vskernels/kernels/placebo.py @@ -57,10 +57,11 @@ def __init__( @inject_self.cached @inject_kwargs_params def scale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), - *, linear: bool = True, sigmoid: bool | tuple[Slope, Center] = True, curve: TransferT | None = None, - **kwargs: Any + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), *, + linear: bool = True, sigmoid: bool | tuple[Slope, Center] = True, curve: TransferT | None = None, **kwargs: Any ) -> vs.VideoNode: + width, height = self._wh_norm(clip, width, height) return super().scale( clip, width, height, shift, linear=linear, sigmoid=sigmoid, trc=Transfer.from_param_or_video(curve, clip).value_libplacebo diff --git a/vskernels/kernels/zimg.py b/vskernels/kernels/zimg.py index 7778648..4d21a4d 100644 --- a/vskernels/kernels/zimg.py +++ b/vskernels/kernels/zimg.py @@ -7,7 +7,7 @@ from ..types import Center, LeftShift, Slope, TopShift from .abstract import Descaler -from .complex import ComplexKernel, BorderHandling +from .complex import BorderHandling, ComplexKernel __all__ = [ 'ZimgDescaler', @@ -20,7 +20,7 @@ class ZimgDescaler(Descaler): @inject_self.cached @inject_kwargs_params def descale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), + self, clip: vs.VideoNode, width: int | None, height: int | None, shift: tuple[TopShift, LeftShift] = (0, 0), *, blur: float = 1.0, border_handling: BorderHandling = BorderHandling.MIRROR, **kwargs: Any ) -> vs.VideoNode: ... @@ -31,7 +31,7 @@ class ZimgComplexKernel(ComplexKernel, ZimgDescaler): # type: ignore @inject_self.cached @inject_kwargs_params def descale( # type: ignore[override] - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), + self, clip: vs.VideoNode, width: int | None, height: int | None, shift: tuple[TopShift, LeftShift] = (0, 0), *, blur: float = 1.0, border_handling: BorderHandling, ignore_mask: vs.VideoNode | None = None, linear: bool = False, sigmoid: bool | tuple[Slope, Center] = False, **kwargs: Any ) -> vs.VideoNode: diff --git a/vskernels/util.py b/vskernels/util.py index 4fe0b79..1ce2d82 100644 --- a/vskernels/util.py +++ b/vskernels/util.py @@ -86,9 +86,11 @@ class NoScaleBase(Scaler): @inject_self.cached @inject_kwargs_params def scale( # type: ignore - self, clip: vs.VideoNode, width: int, height: int, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any + self, clip: vs.VideoNode, width: int | None = None, height: int | None = None, + shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any ) -> vs.VideoNode: try: + width, height = Scaler._wh_norm(clip, width, height) return super().scale(clip, clip.width, clip.height, shift, **kwargs) # type: ignore except Exception: return clip