From b383486d674b3c84ccdc23ed579640583eb0acbb Mon Sep 17 00:00:00 2001
From: Quantum <quantum2048@gmail.com>
Date: Sun, 4 Oct 2020 03:07:34 -0400
Subject: [PATCH] Add borders to avoid cropping cursor shadow

---
 win2xcur/cursor.py         |  6 ++++--
 win2xcur/parser/cur.py     |  2 +-
 win2xcur/parser/xcursor.py |  2 +-
 win2xcur/shadow.py         | 20 +++++++++++++++-----
 4 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/win2xcur/cursor.py b/win2xcur/cursor.py
index f864459..47a2dd2 100644
--- a/win2xcur/cursor.py
+++ b/win2xcur/cursor.py
@@ -6,13 +6,15 @@ from wand.sequence import SingleImage
 class CursorImage:
     image: SingleImage
     hotspot: Tuple[int, int]
+    nominal: int
 
-    def __init__(self, image: SingleImage, hotspot: Tuple[int, int]) -> None:
+    def __init__(self, image: SingleImage, hotspot: Tuple[int, int], nominal: int) -> None:
         self.image = image
         self.hotspot = hotspot
+        self.nominal = nominal
 
     def __repr__(self) -> str:
-        return 'CursorImage(image=%r, hotspot=%r)' % (self.image, self.hotspot)
+        return 'CursorImage(image=%r, hotspot=%r, nominal=%r)' % (self.image, self.hotspot, self.nominal)
 
 
 class CursorFrame:
diff --git a/win2xcur/parser/cur.py b/win2xcur/parser/cur.py
index 5ae4ed9..d8cf524 100644
--- a/win2xcur/parser/cur.py
+++ b/win2xcur/parser/cur.py
@@ -22,7 +22,7 @@ class CURParser(BaseParser):
         self._image = Image(blob=blob, format='cur')
         self._hotspots = self._parse_header()
         self.frames = [CursorFrame([
-            CursorImage(image, hotspot) for image, hotspot in zip(self._image.sequence, self._hotspots)
+            CursorImage(image, hotspot, image.width) for image, hotspot in zip(self._image.sequence, self._hotspots)
         ])]
 
     def _parse_header(self) -> List[Tuple[int, int]]:
diff --git a/win2xcur/parser/xcursor.py b/win2xcur/parser/xcursor.py
index 5838d62..369a51d 100644
--- a/win2xcur/parser/xcursor.py
+++ b/win2xcur/parser/xcursor.py
@@ -81,7 +81,7 @@ class XCursorParser(BaseParser):
             image = Image(width=width, height=height)
             image.import_pixels(channel_map='BGRA', data=blob)
             images_by_size[nominal_size].append(
-                (CursorImage(image.sequence[0], (x_offset, y_offset)), delay)
+                (CursorImage(image.sequence[0], (x_offset, y_offset), nominal_size), delay)
             )
 
         if len(set(map(len, images_by_size.values()))) != 1:
diff --git a/win2xcur/shadow.py b/win2xcur/shadow.py
index 11b991b..d987535 100644
--- a/win2xcur/shadow.py
+++ b/win2xcur/shadow.py
@@ -1,5 +1,6 @@
 from typing import List
 
+from wand.color import Color
 from wand.image import BaseImage, Image
 
 from win2xcur.cursor import CursorFrame
@@ -7,18 +8,27 @@ from win2xcur.cursor import CursorFrame
 
 def apply_to_image(image: BaseImage, *, color: str, radius: float, sigma: float, xoffset: float,
                    yoffset: float) -> Image:
-    opacity = Image(width=image.width, height=image.height, pseudo='xc:white')
-    opacity.composite(image.channel_images['opacity'], left=round(xoffset * image.width),
-                      top=round(yoffset * image.height))
+    xoffset = round(xoffset * image.width)
+    yoffset = round(yoffset * image.height)
+    new_width = image.width + 3 * xoffset
+    new_height = image.height + 3 * yoffset
+
+    opacity = Image(width=new_width, height=new_height, pseudo='xc:white')
+    opacity.composite(image.channel_images['opacity'], left=xoffset, top=yoffset)
     opacity.gaussian_blur(radius * image.width, sigma * image.width)
     opacity.negate()
     opacity.modulate(50)
 
-    shadow = Image(width=image.width, height=image.height, pseudo='xc:' + color)
+    shadow = Image(width=new_width, height=new_height, pseudo='xc:' + color)
     shadow.composite(opacity, operator='copy_opacity')
 
-    result = image.clone()
+    result = Image(width=new_width, height=new_height, pseudo='xc:transparent')
+    result.composite(image)
     result.composite(shadow, operator='difference')
+
+    trimmed = result.clone()
+    trimmed.trim(color=Color('transparent'))
+    result.crop(width=max(image.width, trimmed.width), height=max(image.height, trimmed.height))
     return result