From 0f6e9b81327f4e2ead4d118b53791bd16918b4b2 Mon Sep 17 00:00:00 2001 From: Abdulkaiz Khatri <24286590+ful1e5@users.noreply.github.com> Date: Tue, 21 May 2024 20:53:09 +0530 Subject: [PATCH] feat: cursor re-canvasing by specifying sizes This can be specified using the `cursor_size:canvas_size` format. --- CHANGELOG.md | 4 ++++ src/clickgen/parser/png.py | 46 +++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 569e7b4..4581d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +### What's New? + +- Clickgen now allows cursor bitmap re-canvasing by specifying size using the `cursor_size:canvas_size` format. + ## [v2.2.2] - 24 April 2024 ### Important Changes diff --git a/src/clickgen/parser/png.py b/src/clickgen/parser/png.py index ac82c5c..11dc507 100644 --- a/src/clickgen/parser/png.py +++ b/src/clickgen/parser/png.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import io -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Union from PIL import Image @@ -24,14 +24,14 @@ def __init__( self, blob: bytes, hotspot: Tuple[int, int], - sizes: Optional[List[int]] = None, + sizes: Optional[List[Union[int, str]]] = None, delay: Optional[int] = None, ) -> None: super().__init__(blob) self._image = Image.open(io.BytesIO(self.blob)) + # 'set' to prevent value duplication if not sizes: - # 'set' to prevent value duplication self.sizes = set(SIZES) else: self.sizes = set(sizes) @@ -58,9 +58,45 @@ def _dim(i: int) -> int: def _parse(self) -> List[CursorFrame]: images: List[CursorImage] = [] for s in sorted(self.sizes): - res_img = self._image.resize((s, s), 1) + size: int = 0 + canvas_size: int = 0 + + if isinstance(s, str): + try: + if ":" in s: + size_str, canvas_size_str = s.split(":") + size = int(size_str) + canvas_size = int(canvas_size_str) + else: + size = int(s) + canvas_size = size + except ValueError: + raise ValueError( + f"'sizes' input '{s}' must be an integer or integers separated by ':'." + ) + except IndexError: + raise ValueError( + f"'sizes' input '{s}' must contain one ':' to separate sizes." + ) + elif isinstance(s, int): + size = s + canvas_size = s + else: + raise TypeError( + "Input must be 'cursor_size:canvas_size' or an integer." + ) + + res_img = self._image.resize((size, size), 1) + + if size != canvas_size: + canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + canvas.paste(res_img, (0, 0)) + res_img = canvas + res_hotspot = self._cal_hotspot(res_img) - images.append(CursorImage(image=res_img, hotspot=res_hotspot, nominal=s)) + images.append( + CursorImage(image=res_img, hotspot=res_hotspot, nominal=canvas_size) + ) return [CursorFrame(images, delay=self.delay)]