Skip to content

File: Broken/Core/BrokenResolution.py

Broken.Core.BrokenResolution

BrokenResolution

Source code in Broken/Core/BrokenResolution.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class BrokenResolution:

    @staticmethod
    def round(*numbers: Number, multiple: int=2) -> Union[int, tuple[int, ...]]:
        """Round to the nearest multiple of 2, returns a single value or a tuple of values"""
        values = tuple(max(multiple, multiple*round(value/multiple)) for value in numbers)
        return (values[0] if (len(values) == 1) else values)

    @classmethod
    def fit(cls,
        old: Optional[tuple[int, int]] = None,
        new: Optional[tuple[int, int]] = None,
        max: Optional[tuple[int, int]] = None,
        ar: Optional[float] = None,
        scale: float = 1.0,
        multiple: int = 2,
    ) -> tuple[int, int]:
        """Fit, Scale and optionally force Aspect Ratio on a base to a (un)limited target resolution

        This method solves the following problem:
            "A window is at some initial size (ow, oh) and a resize was asked to (nw, nh); what
            final resolution the window should be, optionally enforcing an aspect ratio (ar),
            and limited by the monitor resolution (mw, mh)?"

        To which, the behavior is as follows in the two branches:
            No aspect ratio (ar=None) is send:
                - Returns the original resolution overridden by any new (nw, nh)

            Aspect ratio (ar!=None) is send:
                - If any of the new (nw, nh) is missing, find the other based on the aspect ratio;
                - Else, prioritize width changes, and downscale/upscale accordingly;
                - Post-limits resolution to (mw, mh) by multiplying both components to max fit it

        Notes
        -----
            - The resolution is rounded to the nearest multiple of 2, so FFmpeg is happy

        Parameters
        ----------
        old : tuple[int, int] or None
            Old resolution
        new : tuple[int, int] or None
            New resolution
        max : tuple[int, int] or None
            Maximum resolution
        scale : float or None
            Scale factor
        ar : float or None
            Force aspect ratio, if any

        Returns
        -------
        (int, int)
            The new best-fit width and height
        """
        old_width, old_height = (old or (None, None))
        new_width, new_height = (new or (None, None))
        max_width, max_height = (max or (None, None))

        log.debug(f"Fit resolution: ({old_width}, {old_height}) -> ({new_width}, {new_height})^({max_width}, {max_height}), AR {ar}")

        # Force or keep either component
        (width, height) = ((new_width or old_width), (new_height or old_height))

        if not all((width, height)):
            raise ValueError(f"Can't build a resolution with missing component(s): ({width=}, {height=})")

        if (ar is not None):

            # Build from width (W) or from height (H)
            from_width  = (width, width/ar)
            from_height = (height*ar, height)

            # Pick the non missing component's
            if (new_height is None):
                (width, height) = from_width
            elif (new_width is None):
                (width, height) = from_height

            # Based on upscale or downscale
            elif (new_width != old_width):
                (width, height) = from_width
            elif (new_height != old_height):
                (width, height) = from_height
            else:
                (width, height) = from_width

            # Limit the resolution to (mw, mh) bounding box and keep aspect ratio
            # - The idea is to find the maximum reduce factor for either component so it normalizes
            #   to the respective (mw, mh), and apply it to both components to scale down
            reduce = __builtins__["max"](
                width/(min(width, max_width or math.inf) or 1),
                height/(min(height, max_height or math.inf) or 1)
            ) or 1

            width, height = (width/reduce, height/reduce)

        else:
            # Limit each component independently
            width  = min(width,  max_width or math.inf)
            height = min(height, max_height or math.inf)

        return cls.round(width*scale, height*scale, multiple=multiple)

round

round(
    *numbers: Number, multiple: int = 2
) -> Union[int, tuple[int, ...]]

Round to the nearest multiple of 2, returns a single value or a tuple of values

Source code in Broken/Core/BrokenResolution.py
11
12
13
14
15
@staticmethod
def round(*numbers: Number, multiple: int=2) -> Union[int, tuple[int, ...]]:
    """Round to the nearest multiple of 2, returns a single value or a tuple of values"""
    values = tuple(max(multiple, multiple*round(value/multiple)) for value in numbers)
    return (values[0] if (len(values) == 1) else values)

fit

fit(
    old: Optional[tuple[int, int]] = None,
    new: Optional[tuple[int, int]] = None,
    max: Optional[tuple[int, int]] = None,
    ar: Optional[float] = None,
    scale: float = 1.0,
    multiple: int = 2,
) -> tuple[int, int]

Fit, Scale and optionally force Aspect Ratio on a base to a (un)limited target resolution

This method solves the following problem

"A window is at some initial size (ow, oh) and a resize was asked to (nw, nh); what final resolution the window should be, optionally enforcing an aspect ratio (ar), and limited by the monitor resolution (mw, mh)?"

To which, the behavior is as follows in the two branches: No aspect ratio (ar=None) is send: - Returns the original resolution overridden by any new (nw, nh)

1
2
3
4
Aspect ratio (ar!=None) is send:
    - If any of the new (nw, nh) is missing, find the other based on the aspect ratio;
    - Else, prioritize width changes, and downscale/upscale accordingly;
    - Post-limits resolution to (mw, mh) by multiplying both components to max fit it
Notes
1
- The resolution is rounded to the nearest multiple of 2, so FFmpeg is happy
Parameters

old : tuple[int, int] or None Old resolution new : tuple[int, int] or None New resolution max : tuple[int, int] or None Maximum resolution scale : float or None Scale factor ar : float or None Force aspect ratio, if any

Returns

(int, int) The new best-fit width and height

Source code in Broken/Core/BrokenResolution.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@classmethod
def fit(cls,
    old: Optional[tuple[int, int]] = None,
    new: Optional[tuple[int, int]] = None,
    max: Optional[tuple[int, int]] = None,
    ar: Optional[float] = None,
    scale: float = 1.0,
    multiple: int = 2,
) -> tuple[int, int]:
    """Fit, Scale and optionally force Aspect Ratio on a base to a (un)limited target resolution

    This method solves the following problem:
        "A window is at some initial size (ow, oh) and a resize was asked to (nw, nh); what
        final resolution the window should be, optionally enforcing an aspect ratio (ar),
        and limited by the monitor resolution (mw, mh)?"

    To which, the behavior is as follows in the two branches:
        No aspect ratio (ar=None) is send:
            - Returns the original resolution overridden by any new (nw, nh)

        Aspect ratio (ar!=None) is send:
            - If any of the new (nw, nh) is missing, find the other based on the aspect ratio;
            - Else, prioritize width changes, and downscale/upscale accordingly;
            - Post-limits resolution to (mw, mh) by multiplying both components to max fit it

    Notes
    -----
        - The resolution is rounded to the nearest multiple of 2, so FFmpeg is happy

    Parameters
    ----------
    old : tuple[int, int] or None
        Old resolution
    new : tuple[int, int] or None
        New resolution
    max : tuple[int, int] or None
        Maximum resolution
    scale : float or None
        Scale factor
    ar : float or None
        Force aspect ratio, if any

    Returns
    -------
    (int, int)
        The new best-fit width and height
    """
    old_width, old_height = (old or (None, None))
    new_width, new_height = (new or (None, None))
    max_width, max_height = (max or (None, None))

    log.debug(f"Fit resolution: ({old_width}, {old_height}) -> ({new_width}, {new_height})^({max_width}, {max_height}), AR {ar}")

    # Force or keep either component
    (width, height) = ((new_width or old_width), (new_height or old_height))

    if not all((width, height)):
        raise ValueError(f"Can't build a resolution with missing component(s): ({width=}, {height=})")

    if (ar is not None):

        # Build from width (W) or from height (H)
        from_width  = (width, width/ar)
        from_height = (height*ar, height)

        # Pick the non missing component's
        if (new_height is None):
            (width, height) = from_width
        elif (new_width is None):
            (width, height) = from_height

        # Based on upscale or downscale
        elif (new_width != old_width):
            (width, height) = from_width
        elif (new_height != old_height):
            (width, height) = from_height
        else:
            (width, height) = from_width

        # Limit the resolution to (mw, mh) bounding box and keep aspect ratio
        # - The idea is to find the maximum reduce factor for either component so it normalizes
        #   to the respective (mw, mh), and apply it to both components to scale down
        reduce = __builtins__["max"](
            width/(min(width, max_width or math.inf) or 1),
            height/(min(height, max_height or math.inf) or 1)
        ) or 1

        width, height = (width/reduce, height/reduce)

    else:
        # Limit each component independently
        width  = min(width,  max_width or math.inf)
        height = min(height, max_height or math.inf)

    return cls.round(width*scale, height*scale, multiple=multiple)