diff options
Diffstat (limited to 'youtube_dl/utils.py')
| -rw-r--r-- | youtube_dl/utils.py | 108 | 
1 files changed, 108 insertions, 0 deletions
| diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index 97ddd9883..ddbfcd2f1 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -47,6 +47,7 @@ from .compat import (      compat_socket_create_connection,      compat_str,      compat_struct_pack, +    compat_struct_unpack,      compat_urllib_error,      compat_urllib_parse,      compat_urllib_parse_urlencode, @@ -2969,3 +2970,110 @@ def parse_m3u8_attributes(attrib):  def urshift(val, n):      return val >> n if val >= 0 else (val + 0x100000000) >> n + + +# Based on png2str() written by @gdkchan and improved by @yokrysty +# Originally posted at https://github.com/rg3/youtube-dl/issues/9706 +def decode_png(png_data): +    # Reference: https://www.w3.org/TR/PNG/ +    header = png_data[8:] + +    if png_data[:8] != b'\x89PNG\x0d\x0a\x1a\x0a' or header[4:8] != b'IHDR': +        raise IOError('Not a valid PNG file.') + +    int_map = {1: '>B', 2: '>H', 4: '>I'} +    unpack_integer = lambda x: compat_struct_unpack(int_map[len(x)], x)[0] + +    chunks = [] + +    while header: +        length = unpack_integer(header[:4]) +        header = header[4:] + +        chunk_type = header[:4] +        header = header[4:] + +        chunk_data = header[:length] +        header = header[length:] + +        header = header[4:]  # Skip CRC + +        chunks.append({ +            'type': chunk_type, +            'length': length, +            'data': chunk_data +        }) + +    ihdr = chunks[0]['data'] + +    width = unpack_integer(ihdr[:4]) +    height = unpack_integer(ihdr[4:8]) + +    idat = b'' + +    for chunk in chunks: +        if chunk['type'] == b'IDAT': +            idat += chunk['data'] + +    if not idat: +        raise IOError('Unable to read PNG data.') + +    decompressed_data = bytearray(zlib.decompress(idat)) + +    stride = width * 3 +    pixels = [] + +    def _get_pixel(idx): +        x = idx % stride +        y = idx // stride +        return pixels[y][x] + +    for y in range(height): +        basePos = y * (1 + stride) +        filter_type = decompressed_data[basePos] + +        current_row = [] + +        pixels.append(current_row) + +        for x in range(stride): +            color = decompressed_data[1 + basePos + x] +            basex = y * stride + x +            left = 0 +            up = 0 + +            if x > 2: +                left = _get_pixel(basex - 3) +            if y > 0: +                up = _get_pixel(basex - stride) + +            if filter_type == 1:  # Sub +                color = (color + left) & 0xff +            elif filter_type == 2:  # Up +                color = (color + up) & 0xff +            elif filter_type == 3:  # Average +                color = (color + ((left + up) >> 1)) & 0xff +            elif filter_type == 4:  # Paeth +                a = left +                b = up +                c = 0 + +                if x > 2 and y > 0: +                    c = _get_pixel(basex - stride - 3) + +                p = a + b - c + +                pa = abs(p - a) +                pb = abs(p - b) +                pc = abs(p - c) + +                if pa <= pb and pa <= pc: +                    color = (color + a) & 0xff +                elif pb <= pc: +                    color = (color + b) & 0xff +                else: +                    color = (color + c) & 0xff + +            current_row.append(color) + +    return width, height, pixels | 
