From 46a7db90e0bbfa3fbed1a652a6d788a66091dd33 Mon Sep 17 00:00:00 2001 From: IrisRainbowNeko Date: Sat, 8 Feb 2025 10:51:29 +0800 Subject: [PATCH 1/6] multi scale; fix scale --- win2xcur/cursor.py | 5 +++++ win2xcur/main/win2xcur.py | 10 +++++++--- win2xcur/scale.py | 13 ++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/win2xcur/cursor.py b/win2xcur/cursor.py index 2925153..70227fd 100644 --- a/win2xcur/cursor.py +++ b/win2xcur/cursor.py @@ -16,6 +16,8 @@ class CursorImage: def __repr__(self) -> str: return f'CursorImage(image={self.image!r}, hotspot={self.hotspot!r}, nominal={self.nominal!r})' + def clone(self): + return CursorImage(self.image.clone(), self.hotspot, self.nominal) class CursorFrame: images: List[CursorImage] @@ -36,3 +38,6 @@ class CursorFrame: def __repr__(self) -> str: return f'CursorFrame(images={self.images!r}, delay={self.delay!r})' + + def clone(self): + return CursorFrame([img.clone() for img in self.images], self.delay) diff --git a/win2xcur/main/win2xcur.py b/win2xcur/main/win2xcur.py index 211b0cb..2ff7264 100644 --- a/win2xcur/main/win2xcur.py +++ b/win2xcur/main/win2xcur.py @@ -32,8 +32,8 @@ def main() -> None: help='y-offset of shadow (as fraction of height)') parser.add_argument('-c', '--shadow-color', default='#000000', help='color of the shadow') - parser.add_argument('--scale', default=None, type=float, - help='Scale the cursor by the specified factor.') + parser.add_argument('--scale', default=None, type=str, + help='Scale the cursor by the specified factor. Multi-scale "[0.125.0.1875,0.25]"') args = parser.parse_args() print_lock = Lock() @@ -49,7 +49,11 @@ def main() -> None: traceback.print_exc() else: if args.scale: - scale.apply_to_frames(cursor.frames, scale=args.scale) + scales = eval(args.scale) + if isinstance(scales, (int, float)): + scale.apply_to_frames(cursor.frames, scale=scales) + else: + cursor.frames = scale.apply_to_frames_MS(cursor.frames, scales=scales) if args.shadow: shadow.apply_to_frames(cursor.frames, color=args.shadow_color, radius=args.shadow_radius, sigma=args.shadow_sigma, xoffset=args.shadow_x, yoffset=args.shadow_y) diff --git a/win2xcur/scale.py b/win2xcur/scale.py index b25793e..95cf68c 100644 --- a/win2xcur/scale.py +++ b/win2xcur/scale.py @@ -8,5 +8,16 @@ def apply_to_frames(frames: List[CursorFrame], *, scale: float) -> None: for cursor in frame: cursor.image.scale( int(round(cursor.image.width * scale)), - int(round(cursor.image.height) * scale), + int(round(cursor.image.height * scale)), ) + cursor.nominal = int(cursor.nominal * scale) + hx, hy = cursor.hotspot + cursor.hotspot = (int(hx * scale), int(hy * scale)) + +def apply_to_frames_MS(frames: List[CursorFrame], *, scales: List[float]) -> List[CursorFrame]: + frames_MS = [] + for scale in scales: + frames_s = [frame.clone() for frame in frames] + apply_to_frames(frames_s, scale=scale) + frames_MS.extend(frames_s) + return frames_MS \ No newline at end of file From b49e61aea58d7e5cbd2284607fda4898b24cfc3f Mon Sep 17 00:00:00 2001 From: IrisRainbowNeko Date: Sat, 8 Feb 2025 10:56:50 +0800 Subject: [PATCH 2/6] multi scale --- win2xcur/main/x2wincur.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/win2xcur/main/x2wincur.py b/win2xcur/main/x2wincur.py index efcd37e..c9783cd 100644 --- a/win2xcur/main/x2wincur.py +++ b/win2xcur/main/x2wincur.py @@ -35,7 +35,11 @@ def main() -> None: traceback.print_exc() else: if args.scale: - scale.apply_to_frames(cursor.frames, scale=args.scale) + scales = eval(args.scale) + if isinstance(scales, (int, float)): + scale.apply_to_frames(cursor.frames, scale=scales) + else: + cursor.frames = scale.apply_to_frames_MS(cursor.frames, scales=scales) ext, result = to_smart(cursor.frames) output = os.path.join(args.output, os.path.basename(name) + ext) with open(output, 'wb') as f: From f46196f851e7538e29d4fff66a5770b6d1280e99 Mon Sep 17 00:00:00 2001 From: IrisRainbowNeko Date: Sat, 8 Feb 2025 11:06:47 +0800 Subject: [PATCH 3/6] multi scale --- win2xcur/main/x2wincur.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win2xcur/main/x2wincur.py b/win2xcur/main/x2wincur.py index c9783cd..311cc2e 100644 --- a/win2xcur/main/x2wincur.py +++ b/win2xcur/main/x2wincur.py @@ -18,8 +18,8 @@ def main() -> None: help='X11 cursor files to convert (no extension)') parser.add_argument('-o', '--output', '--output-dir', default=os.curdir, help='Directory to store converted cursor files.') - parser.add_argument('-S', '--scale', default=None, type=float, - help='Scale the cursor by the specified factor.') + parser.add_argument('-S', '--scale', default=None, type=str, + help='Scale the cursor by the specified factor. Multi-scale "[0.125.0.1875,0.25]"') args = parser.parse_args() print_lock = Lock() From 133e509929b4c07ff46cc17febc9098e30dfe1b2 Mon Sep 17 00:00:00 2001 From: IrisRainbowNeko Date: Sat, 8 Feb 2025 11:29:13 +0800 Subject: [PATCH 4/6] multi size --- win2xcur/main/win2xcur.py | 11 ++++++++++- win2xcur/main/x2wincur.py | 11 ++++++++++- win2xcur/scale.py | 28 +++++++++++++++++++--------- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/win2xcur/main/win2xcur.py b/win2xcur/main/win2xcur.py index 2ff7264..ad570e0 100644 --- a/win2xcur/main/win2xcur.py +++ b/win2xcur/main/win2xcur.py @@ -33,7 +33,9 @@ def main() -> None: parser.add_argument('-c', '--shadow-color', default='#000000', help='color of the shadow') parser.add_argument('--scale', default=None, type=str, - help='Scale the cursor by the specified factor. Multi-scale "[0.125.0.1875,0.25]"') + help='Scale the cursor by the specified factor. Multi-scale "[0.125,0.1875,0.25]"') + parser.add_argument('--size', default=None, type=str, + help='Scale the cursor to the specified size. Multi-size "[32,28,64]"') args = parser.parse_args() print_lock = Lock() @@ -54,6 +56,13 @@ def main() -> None: scale.apply_to_frames(cursor.frames, scale=scales) else: cursor.frames = scale.apply_to_frames_MS(cursor.frames, scales=scales) + elif args.size: + sizes = eval(args.size) + if isinstance(sizes, (int, float)): + scale.apply_to_frames(cursor.frames, size=sizes) + else: + cursor.frames = scale.apply_to_frames_MS(cursor.frames, sizes=sizes) + if args.shadow: shadow.apply_to_frames(cursor.frames, color=args.shadow_color, radius=args.shadow_radius, sigma=args.shadow_sigma, xoffset=args.shadow_x, yoffset=args.shadow_y) diff --git a/win2xcur/main/x2wincur.py b/win2xcur/main/x2wincur.py index 311cc2e..298a9d6 100644 --- a/win2xcur/main/x2wincur.py +++ b/win2xcur/main/x2wincur.py @@ -19,7 +19,9 @@ def main() -> None: parser.add_argument('-o', '--output', '--output-dir', default=os.curdir, help='Directory to store converted cursor files.') parser.add_argument('-S', '--scale', default=None, type=str, - help='Scale the cursor by the specified factor. Multi-scale "[0.125.0.1875,0.25]"') + help='Scale the cursor by the specified factor. Multi-scale "[0.125,0.1875,0.25]"') + parser.add_argument('--size', default=None, type=str, + help='Scale the cursor to the specified size. Multi-size "[32,28,64]"') args = parser.parse_args() print_lock = Lock() @@ -40,6 +42,13 @@ def main() -> None: scale.apply_to_frames(cursor.frames, scale=scales) else: cursor.frames = scale.apply_to_frames_MS(cursor.frames, scales=scales) + elif args.size: + sizes = eval(args.size) + if isinstance(sizes, (int, float)): + scale.apply_to_frames(cursor.frames, size=sizes) + else: + cursor.frames = scale.apply_to_frames_MS(cursor.frames, sizes=sizes) + ext, result = to_smart(cursor.frames) output = os.path.join(args.output, os.path.basename(name) + ext) with open(output, 'wb') as f: diff --git a/win2xcur/scale.py b/win2xcur/scale.py index 95cf68c..337bd04 100644 --- a/win2xcur/scale.py +++ b/win2xcur/scale.py @@ -3,21 +3,31 @@ from typing import List from win2xcur.cursor import CursorFrame -def apply_to_frames(frames: List[CursorFrame], *, scale: float) -> None: +def apply_to_frames(frames: List[CursorFrame], *, scale: float = None, size: int = None) -> None: for frame in frames: for cursor in frame: + if size: + scale = size / cursor.image.width cursor.image.scale( - int(round(cursor.image.width * scale)), - int(round(cursor.image.height * scale)), + size or int(round(cursor.image.width * scale)), + size or int(round(cursor.image.height * scale)), ) cursor.nominal = int(cursor.nominal * scale) hx, hy = cursor.hotspot cursor.hotspot = (int(hx * scale), int(hy * scale)) -def apply_to_frames_MS(frames: List[CursorFrame], *, scales: List[float]) -> List[CursorFrame]: + +def apply_to_frames_MS(frames: List[CursorFrame], *, scales: List[float] = None, + sizes: List[int] = None) -> List[CursorFrame]: frames_MS = [] - for scale in scales: - frames_s = [frame.clone() for frame in frames] - apply_to_frames(frames_s, scale=scale) - frames_MS.extend(frames_s) - return frames_MS \ No newline at end of file + if scales is not None: + for scale in scales: + frames_s = [frame.clone() for frame in frames] + apply_to_frames(frames_s, scale=scale) + frames_MS.extend(frames_s) + else: + for size in sizes: + frames_s = [frame.clone() for frame in frames] + apply_to_frames(frames_s, size=size) + frames_MS.extend(frames_s) + return frames_MS From 02e58f071901183270d0e26d6ab991d2db339376 Mon Sep 17 00:00:00 2001 From: IrisRainbowNeko Date: Mon, 26 May 2025 15:24:00 +0800 Subject: [PATCH 5/6] improve code; remove eval --- win2xcur/main/win2xcur.py | 20 +++++--------- win2xcur/main/x2wincur.py | 18 +++++-------- win2xcur/scale.py | 57 ++++++++++++++++++++------------------- 3 files changed, 43 insertions(+), 52 deletions(-) diff --git a/win2xcur/main/win2xcur.py b/win2xcur/main/win2xcur.py index ad570e0..5c5505d 100644 --- a/win2xcur/main/win2xcur.py +++ b/win2xcur/main/win2xcur.py @@ -32,10 +32,10 @@ def main() -> None: help='y-offset of shadow (as fraction of height)') parser.add_argument('-c', '--shadow-color', default='#000000', help='color of the shadow') - parser.add_argument('--scale', default=None, type=str, + parser.add_argument('--scale', nargs='*', type=float, default=None, help='Scale the cursor by the specified factor. Multi-scale "[0.125,0.1875,0.25]"') - parser.add_argument('--size', default=None, type=str, - help='Scale the cursor to the specified size. Multi-size "[32,28,64]"') + parser.add_argument('--size', nargs='*', type=int, default=None, + help='Scale the cursor to the specified size. Multi-size "[32,48,64]"') args = parser.parse_args() print_lock = Lock() @@ -51,17 +51,11 @@ def main() -> None: traceback.print_exc() else: if args.scale: - scales = eval(args.scale) - if isinstance(scales, (int, float)): - scale.apply_to_frames(cursor.frames, scale=scales) - else: - cursor.frames = scale.apply_to_frames_MS(cursor.frames, scales=scales) + cursor.frames = scale.apply_to_frames_by_scales(cursor.frames, scales=args.scale) elif args.size: - sizes = eval(args.size) - if isinstance(sizes, (int, float)): - scale.apply_to_frames(cursor.frames, size=sizes) - else: - cursor.frames = scale.apply_to_frames_MS(cursor.frames, sizes=sizes) + cursor.frames = scale.apply_to_frames_to_sizes(cursor.frames, sizes=args.size) + else: + raise NotImplementedError('Please specify either --scale or --size') if args.shadow: shadow.apply_to_frames(cursor.frames, color=args.shadow_color, radius=args.shadow_radius, diff --git a/win2xcur/main/x2wincur.py b/win2xcur/main/x2wincur.py index 298a9d6..7bdeacd 100644 --- a/win2xcur/main/x2wincur.py +++ b/win2xcur/main/x2wincur.py @@ -18,9 +18,9 @@ def main() -> None: help='X11 cursor files to convert (no extension)') parser.add_argument('-o', '--output', '--output-dir', default=os.curdir, help='Directory to store converted cursor files.') - parser.add_argument('-S', '--scale', default=None, type=str, + parser.add_argument('-S', '--scale', nargs='*', type=float, default=None, help='Scale the cursor by the specified factor. Multi-scale "[0.125,0.1875,0.25]"') - parser.add_argument('--size', default=None, type=str, + parser.add_argument('--size', nargs='*', type=int, default=None, help='Scale the cursor to the specified size. Multi-size "[32,28,64]"') args = parser.parse_args() @@ -37,17 +37,11 @@ def main() -> None: traceback.print_exc() else: if args.scale: - scales = eval(args.scale) - if isinstance(scales, (int, float)): - scale.apply_to_frames(cursor.frames, scale=scales) - else: - cursor.frames = scale.apply_to_frames_MS(cursor.frames, scales=scales) + cursor.frames = scale.apply_to_frames_by_scales(cursor.frames, scales=args.scale) elif args.size: - sizes = eval(args.size) - if isinstance(sizes, (int, float)): - scale.apply_to_frames(cursor.frames, size=sizes) - else: - cursor.frames = scale.apply_to_frames_MS(cursor.frames, sizes=sizes) + cursor.frames = scale.apply_to_frames_to_sizes(cursor.frames, sizes=args.size) + else: + raise NotImplementedError('Please specify either --scale or --size') ext, result = to_smart(cursor.frames) output = os.path.join(args.output, os.path.basename(name) + ext) diff --git a/win2xcur/scale.py b/win2xcur/scale.py index 337bd04..e9c111e 100644 --- a/win2xcur/scale.py +++ b/win2xcur/scale.py @@ -2,32 +2,35 @@ from typing import List from win2xcur.cursor import CursorFrame +def apply_to_frames_by_scales(frames: List[CursorFrame], *, scales: List[float] = None) -> List[CursorFrame]: + all_frames = [] + for scale in scales: + for frame in frames: + frame = frame.clone() + for cursor in frame: + cursor.image.scale( + int(round(cursor.image.width * scale)), + int(round(cursor.image.height * scale)), + ) + cursor.nominal = int(cursor.nominal * scale) + hx, hy = cursor.hotspot + cursor.hotspot = (int(hx * scale), int(hy * scale)) + all_frames.append(frame) + return all_frames -def apply_to_frames(frames: List[CursorFrame], *, scale: float = None, size: int = None) -> None: - for frame in frames: - for cursor in frame: - if size: +def apply_to_frames_to_sizes(frames: List[CursorFrame], *, sizes: List[int] = None) -> List[CursorFrame]: + all_frames = [] + for size in sizes: + for frame in frames: + frame = frame.clone() + for cursor in frame: scale = size / cursor.image.width - cursor.image.scale( - size or int(round(cursor.image.width * scale)), - size or int(round(cursor.image.height * scale)), - ) - cursor.nominal = int(cursor.nominal * scale) - hx, hy = cursor.hotspot - cursor.hotspot = (int(hx * scale), int(hy * scale)) - - -def apply_to_frames_MS(frames: List[CursorFrame], *, scales: List[float] = None, - sizes: List[int] = None) -> List[CursorFrame]: - frames_MS = [] - if scales is not None: - for scale in scales: - frames_s = [frame.clone() for frame in frames] - apply_to_frames(frames_s, scale=scale) - frames_MS.extend(frames_s) - else: - for size in sizes: - frames_s = [frame.clone() for frame in frames] - apply_to_frames(frames_s, size=size) - frames_MS.extend(frames_s) - return frames_MS + cursor.image.scale( + size, + size, + ) + cursor.nominal = int(cursor.nominal * scale) + hx, hy = cursor.hotspot + cursor.hotspot = (int(hx * scale), int(hy * scale)) + all_frames.append(frame) + return all_frames From 10804a68c84b20a9c7dd816930cc894f29f87f4d Mon Sep 17 00:00:00 2001 From: IrisRainbowNeko Date: Mon, 26 May 2025 16:34:19 +0800 Subject: [PATCH 6/6] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 46569f1..89b42c0 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,13 @@ For example, if you want to convert [the sample cursor](sample/crosshair.cur) to Linux format: mkdir output/ - win2xcur sample/crosshair.cur -o output/ + win2xcur sample/crosshair.cur -o output/ --size 32 48 64 `-s` can be specified to enable shadows. Multiple cursors files can be specified on the command line. For example, to convert a directory of cursors with shadows enabled: - win2xcur input/*.{ani,cur} -o output/ + win2xcur input/*.{ani,cur} -o output/ --size 32 48 64 For more information, run `win2xcur --help`. @@ -43,7 +43,7 @@ For more information, run `win2xcur --help`. For example, if you want to convert DMZ-White to Windows: mkdir dmz-white/ - x2wincur /usr/share/icons/DMZ-White/cursors/* -o dmz-white/ + x2wincur /usr/share/icons/DMZ-White/cursors/* -o dmz-white/ --size 32 48 64 ## Troubleshooting