Beispiel #1
0
    def _write_chunk(cls, out_file: BinaryIO, img: CursorIcon, delay: int):
        out_file.write(to_bytes(cls.IMG_CHUNK_H_SIZE, 4))
        out_file.write(to_bytes(cls.CURSOR_TYPE, 4))
        out_file.write(
            to_bytes(int(img.image.size[0] * cls.SIZE_SCALING_FACTOR), 4))
        out_file.write(to_bytes(1, 4))

        # The width and height...
        width, height = img.image.size
        x_hot, y_hot = img.hotspot
        cls._assert(width <= 0x7FFF, "Invalid width!")
        cls._assert(height <= 0x7FFF, "Invalid height!")
        out_file.write(to_bytes(width, 4))
        out_file.write(to_bytes(width, 4))
        x_hot, y_hot = (
            x_hot if (0 <= x_hot < width) else 0,
            y_hot if (0 <= y_hot < height) else 0,
        )

        # Hotspot and delay...
        out_file.write(to_bytes(x_hot, 4))
        out_file.write(to_bytes(y_hot, 4))
        out_file.write(to_bytes(delay, 4))

        # Now the image, ARGB packed in little endian integers...(So really BGRA)(ARGB -> BGRA)
        im_bytes = (np.asarray(img.image.convert("RGBA"))[:, :,
                                                          (2, 1, 0,
                                                           3)]).tobytes()
        out_file.write(im_bytes)
Beispiel #2
0
    def write(cls, cursor: AnimatedCursor, out: BinaryIO):
        """
        Write an AnimatedCursor to the specified file in the windows .ani format.

        :param cursor: The AnimatedCursor object to write.
        :param out: The file buffer to write the new .ani data to.
        """
        # Write the magic...
        out.write(cls.RIFF_MAGIC)
        # We will deal with writing the length of the entire file later...
        out.write(b"\0\0\0\0")
        out.write(cls.ACON_MAGIC)

        # Write the header...
        header = bytearray(36)
        # We write the header length twice for some dumb reason...
        header[0:4] = to_bytes(36, 4)  # Header length...
        header[4:8] = to_bytes(len(cursor), 4)  # Number of frames
        header[8:12] = to_bytes(len(cursor), 4)  # Number of steps
        # Ignore width, height, and bits per pixel...
        # The number of planes should always be 1....
        header[24:28] = to_bytes(1, 4)
        header[28:32] = to_bytes(10,
                                 4)  # We just pass 10 as the default delay...
        header[32:36] = to_bytes(
            1, 4
        )  # The flags, last flag is flipped which specifies data is stored in .cur

        write_chunk(out, b"anih", header)

        # Write the LIST of icons...
        list_data = bytearray(b"fram")
        delay_data = bytearray()

        for sub_cursor, delay in cursor:
            # Writing a single cursor to the list...
            mem_stream = BytesIO()
            CurFormat.write(sub_cursor, mem_stream)
            # We write these chunks manually to avoid wasting a ton of lines of code, as using "write_chunks" ends up
            # being just as complicated...
            cur_data = mem_stream.getvalue()
            list_data.extend(b"icon")
            list_data.extend(to_bytes(len(cur_data), 4))
            list_data.extend(cur_data)
            # Writing the delay to the rate chunk
            delay_data.extend(to_bytes(round((delay * 60) / 1000), 4))

        # Now that we have gathered the data actually write the chunks...
        write_chunk(out, b"LIST", list_data)
        write_chunk(out, b"rate", delay_data)

        # Now we are to the end, get the length of the file and write it as the RIFF chunk length...
        entire_file_len = out.tell() - 8
        out.seek(4)
        out.write(to_bytes(entire_file_len, 4))
Beispiel #3
0
def write_chunk(buffer: BinaryIO,
                chunk_id: bytes,
                chunk_data: bytes,
                byteorder="little"):
    """
    Writes a chunk to file.

    :param buffer: The file buffer to write to.
    :param chunk_id: The 4 byte chunk identifier.
    :param chunk_data: The chunk's data as a bytes object.
    :param byteorder: The byteorder to use when writing the byte's size, defaults to "little"
    """
    buffer.write(chunk_id[:4])
    buffer.write(to_bytes(len(chunk_data), 4, byteorder=byteorder))
    buffer.write(chunk_data)
    def write(cls, cursor: Cursor, out: BinaryIO):
        """
        Writes cursor to a file in the form of the windows .cur format...

        :param cursor: The cursor object to save.
        :param out: The file handle to output the cursor to.
        """
        out.write(cls.MAGIC)
        out.write(to_bytes(len(cursor), 2))

        offset = out.tell() + len(cursor) * 16
        imgs = []

        for size in sorted(cursor):
            width, height = size

            if width > 256 or height > 256:
                continue

            hot_x, hot_y = cursor[size].hotspot
            hot_x, hot_y = (
                hot_x if (0 <= hot_x < width) else 0,
                hot_y if (0 <= hot_y < height) else 0,
            )

            image_data = cls._to_bmp(cursor[size].image, (width, height))

            width, height = (
                width if (width < 256) else 0,
                height if (height < 256) else 0,
            )

            out.write(to_bytes(width, 1))  # Width, 1 byte.
            out.write(to_bytes(height, 1))  # Height, 1 byte.
            out.write(b"\0\0")
            out.write(to_bytes(hot_x, 2))
            out.write(to_bytes(hot_y, 2))
            out.write(to_bytes(len(image_data), 4))
            out.write(to_bytes(offset, 4))

            offset += len(image_data)
            imgs.append(image_data)

        for image_data in imgs:
            out.write(image_data)
def _write_bmp(img: Image.Image, out_file: BinaryIO):
    # Grab the dpi for calculations later...
    dpi = img.info.get("dpi", DEF_BMP_DPI)
    ppm = tuple(int(dpi_val * 39.3701 + 0.5) for dpi_val in dpi)

    # BMP HEADER:
    out_file.write(to_bytes(40, 4))  # BMP Header size
    out_file.write(to_signed_bytes(img.size[0], 4))  # Image Width
    out_file.write(to_signed_bytes(img.size[1] * 2, 4))  # Image Height
    out_file.write(to_bytes(1, 2))  # Number of planes
    out_file.write(to_bytes(32, 2))  # The bits per pixel...
    out_file.write(
        to_bytes(0, 4)
    )  # The compression method, we set it to raw or no compression...
    out_file.write(
        to_bytes(4 * img.size[0] * (img.size[1] * 2), 4)
    )  # The size of the image data...
    out_file.write(
        to_signed_bytes(ppm[0], 4)
    )  # The resolution of the width in pixels per meter...
    out_file.write(
        to_signed_bytes(ppm[1], 4)
    )  # The resolution of the height in pixels per meter...
    out_file.write(
        to_bytes(0, 4)
    )  # The number of colors in the color table, in this case none...
    out_file.write(
        to_bytes(0, 4)
    )  # Number of important colors in the color table, again none...

    img = img.convert("RGBA")
    data = np.array(img)
    # Create the alpha channel...
    alpha_channel = data[:, :, 3]
    alpha_channel = np.packbits(alpha_channel == 0, axis=1)[::-1]
    # Create the main image with transparency...
    bgrx_data: np.ndarray = data[::-1, :, (2, 1, 0, 3)]
    # Dump the main image...
    out_file.write(bgrx_data.tobytes())

    # We now dump the mask and some zeros to finish filling the space...
    mask_data = alpha_channel.tobytes()
    leftover_space = (img.size[0] * img.size[1] * 4) - len(mask_data)
    out_file.write(mask_data)
    out_file.write(bytes(leftover_space))
Beispiel #6
0
    def write(cls, cursor: AnimatedCursor, out: BinaryIO):
        """
        Write an AnimatedCursor to the specified file in the X-Org cursor format.

        :param cursor: The AnimatedCursor object to write.
        :param out: The file buffer to write the new X-Org Cursor data to.
        """
        cursor = cursor.copy()
        cursor.normalize()

        if len(cursor) == 0:
            return

        num_curs = sum(len(length) for length, delay in cursor)

        out.write(cls.MAGIC)
        out.write(to_bytes(cls.HEADER_SIZE, 4))
        out.write(to_bytes(cls.VERSION, 4))

        out.write(to_bytes(num_curs, 4))
        # The initial offset...
        offset = num_curs * 12 + cls.HEADER_SIZE

        sorted_sizes = sorted(cursor[0][0])

        # Write the Table of contents [type, subtype(size), offset]
        for size in sorted_sizes:
            for sub_cur, delay in cursor:
                out.write(to_bytes(cls.CURSOR_TYPE, 4))
                out.write(to_bytes(int(size[0] * cls.SIZE_SCALING_FACTOR), 4))
                out.write(to_bytes(offset, 4))
                offset += cls.IMG_CHUNK_H_SIZE + (size[0] * size[1] * 4)

        # Write the actual images...
        for size in sorted_sizes:
            for sub_cur, delay in cursor:
                cls._write_chunk(out, sub_cur[size], delay)