diff --git a/README.MD b/README.MD
index cbefdd3..7c36883 100644
--- a/README.MD
+++ b/README.MD
@@ -7,6 +7,7 @@ The completed nodes are as follows, and further work is in progress.
[䏿–‡è¯´æ˜Žç‚¹è¿™é‡Œ](./README_CN.MD)
## Update
+* Add GradientOverlay and ColorOverlay nodes. Add invalid mask input judgment and ignore it when invalid mask is input.
* Commit InnerGlow, InnerShadow and MotionBlur nodes.
* Renaming all completed nodes, the nodes are divided into 4 groups:LayerStyle, LayerMask, LayerUtility, LayerFilter. workflows containing old version nodes need to be manually replaced with new version nodes.
* OuterGlow node has undergone significant modifications by adding options for **_brightness_**, **_light_color_**, and **_glow_color_**.
@@ -21,8 +22,9 @@ The completed nodes are as follows, and further work is in progress.
* Commit DropShadow node.
# LayerStyle
+
-### DropShadow:
+### DropShadow
Generate shadow

@@ -41,7 +43,7 @@ Node options:
* blur: Shadow blur level.
* shadow_color4: Shadow color.
-### OuterGlow:
+### OuterGlow
Generate outer glow

@@ -59,7 +61,7 @@ Node options:
* light_color4: Center part color of glow.
* glow_color4: Edge part color of glow.
-### InnerShadow:
+### InnerShadow
Generate inner shadow

@@ -77,7 +79,7 @@ Node options:
* blur: Shadow blur level.
* shadow_color4: Shadow color.
-### InnerGlow:
+### InnerGlow
Generate inner glow

@@ -96,7 +98,7 @@ Node options:
* glow_color4: Edge part color of glow.
-### Stroke:
+### Stroke
Generate a stroke of layer。

@@ -112,6 +114,37 @@ Node options:
* stroke_width: Stroke width.
* stroke_color4: Stroke color, described in hexadecimal RGB format.
+### GradientOverlay
+Generate gradient overlay
+
+
+Node options:
+* background_image1: The background image.
+* layer_image1: Layer image for composite.
+* layer_mask1,2: Mask for layer_image.
+* invert_mask: Whether to reverse the mask.
+* blend_mode3: Blending mode of gradient.
+* opacity: Opacity of stroke.
+* start_color: Color at the beginning of the gradient.
+* start_alpha: Transparency at the beginning of the gradient.
+* end_color: Color at the end of the gradient.
+* end_alpha: Transparency at the end of the gradient.
+* angle: Gradient rotation angle.
+
+### ColorOverlay
+Generate color overlay
+
+
+Node options:
+
+* background_image1: The background image.
+* layer_image1: Layer image for composite.
+* layer_mask1,2: Mask for layer_image.
+* invert_mask: Whether to reverse the mask.
+* blend_mode3: Blending mode of color.
+* opacity: Opacity of stroke.
+* color: Color of overlay.
+
#### Annotation for LayerStyle nodes:
1 The layer_image, layer_mask and the background_image(if have input), These three items must be of the same size.。
@@ -123,6 +156,7 @@ Node options:
4 The RGB color described by hexadecimal RGB format, like '#FA3D86'.
# LayerMask
+
### MaskPreview:
Preview the input mask
@@ -134,6 +168,7 @@ Invert the mask
# LayerUtility
+
### ImageBlend:
Composit layer image and background image, multiple blend modes are available for option, and transparency can be set.
@@ -169,6 +204,7 @@ Output Type:
* string
# LayerFilter
+
### MotionBlur:
Make the image motion blur
@@ -183,8 +219,26 @@ image Some JSON workflow files in the workflow directory, that is example for Co
## The following tasks are in the planned list
-* Color Overlay
-* Gradient Overlay
+* LayerUtility:
+imageblend advance
+color image
+gradient image
+
+* LayerMask:
+text mask
+gradient mask
+mask grow&blur
+mask subtract
+mask combine
+mask diffrent
+mask gradient
+mask softedge
+
+* LayerFilter:
+bulr
+emboss
+contour
+findedge
## How to install
diff --git a/README_CN.MD b/README_CN.MD
index 24dfed1..e7f81d1 100644
--- a/README_CN.MD
+++ b/README_CN.MD
@@ -1,10 +1,11 @@
-# ComfyUI Layer Style£º
+# ComfyUI Layer Style
ComfyUI²å¼þ£¬Í¨¹ýÒ»×é½ÚµãʵÏÖ·ÂÕÕAdobe PhotoshopµÄͼ²ãÑùʽ¡£
ÒÑÍê³ÉµÄ½ÚµãÈçÏ£¬ºóÐø¹¤×÷½øÐÐÖС£

-## ¸üÐÂ˵Ã÷£º
+## ¸üÐÂ˵Ã÷:
+* Ìí¼ÓGradientOverlay,ColorOverlay½Úµã¡£Ôö¼ÓÎÞЧmaskÊäÈëÅжϣ¬µ±ÊäÈëÎÞЧmaskʱ½«ÆäºöÂÔ¡£
* Ìí¼ÓInnerGlow, InnerShadow, MotionBlur½Úµã¡£
* ËùÓÐÒÑÍê³ÉµÄ½ÚµãÖØÐÂÃüÃû£¬½Úµã·ÖΪ4×飺LayerStyle, LayerMask, LayerUtility, LayerFilter¡£ÒòÎªÖØÐÂÃüÃû£¬°üº¬¾É°æ½ÚµãµÄ¹¤×÷Á÷ÐèÊÖ¶¯Ì滻аæ½Úµã¡£
* OuterGlow½ÚµãÐ޸ģ¬Ôö¼ÓÁÁ¶È¡¢µÆ¹âÑÕÉ«¡¢»Ô¹âÑÕɫѡÏî¡£
@@ -18,12 +19,13 @@ ComfyUI
* Ê׸ö½ÚµãDropShadowÌá½»¡£
# LayerStyle
+
-### DropShadow:
+### DropShadow
Éú³ÉÒõÓ°¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:

* background_image1: ±³¾°Í¼Ïñ¡£
* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
@@ -34,14 +36,14 @@ ComfyUI
* distance_x: ÒõÓ°µÄˮƽ·½ÏòÆ«ÒÆÁ¿¡£
* distance_y: ÒõÓ°µÄ´¹Ö±·½ÏòÆ«ÒÆÁ¿¡£
* grow: ÒõÓ°À©ÕÅ·ù¶È¡£
-* blur£ºÒõÓ°Ä£ºý³Ì¶È¡£
+* blur:ÒõÓ°Ä£ºý³Ì¶È¡£
* shadow_color4: ÒõÓ°ÑÕÉ«¡£
-### OuterGlow£º
+### OuterGlow
Éú³ÉÍâ·¢¹â¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:

* background_image1: ±³¾°Í¼Ïñ¡£
* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
@@ -51,15 +53,15 @@ ComfyUI
* opacity: ·¢¹âµÄ²»Í¸Ã÷¶È¡£
* brightness: ·¢¹âÁÁ¶È¡£
* glow_range: ·¢¹â·¶Î§¡£
-* blur£º·¢¹âÄ£ºý³Ì¶È¡£
+* blur:·¢¹âÄ£ºý³Ì¶È¡£
* light_color4: ·¢¹âÖÐÐÄÑÕÉ«¡£
* glow_colo4: »Ô¹âÍâΧÑÕÉ«¡£
-### InnerShadow:
+### InnerShadow
Éú³ÉÄÚÒõÓ°¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:

* background_image1: ±³¾°Í¼Ïñ¡£
* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
@@ -70,14 +72,14 @@ ComfyUI
* distance_x: ÒõÓ°µÄˮƽ·½ÏòÆ«ÒÆÁ¿¡£
* distance_y: ÒõÓ°µÄ´¹Ö±·½ÏòÆ«ÒÆÁ¿¡£
* grow: ÒõÓ°À©ÕÅ·ù¶È¡£
-* blur£ºÒõÓ°Ä£ºý³Ì¶È¡£
+* blur:ÒõÓ°Ä£ºý³Ì¶È¡£
* shadow_color4: ÒõÓ°ÑÕÉ«¡£
-### InnerGlow£º
+### InnerGlow
Éú³ÉÄÚ·¢¹â¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:

* background_image1: ±³¾°Í¼Ïñ¡£
* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
@@ -87,15 +89,15 @@ ComfyUI
* opacity: ·¢¹âµÄ²»Í¸Ã÷¶È¡£
* brightness: ·¢¹âÁÁ¶È¡£
* glow_range: ·¢¹â·¶Î§¡£
-* blur£º·¢¹âÄ£ºý³Ì¶È¡£
+* blur:·¢¹âÄ£ºý³Ì¶È¡£
* light_color4: ·¢¹âÖÐÐÄÑÕÉ«¡£
* glow_colo4: »Ô¹âÍâΧÑÕÉ«¡£
-### Stroke£º
+### Stroke
Éú³ÉÃè±ß¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:

* background_image1: ±³¾°Í¼Ïñ¡£
* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
@@ -103,18 +105,51 @@ ComfyUI
* invert_mask: ÊÇ·ñ·´×ªÕÚÕÖ¡£
* blend_mode3: Ãè±ßµÄ»ìºÏģʽ¡£
* opacity: ²»Í¸Ã÷¶È¡£
-* stroke_grow: Ãè±ßÀ©ÕÅ/ÊÕËõ·ù¶È£¬ÕýÖµÊÇÀ©ÕÅ£¬¸ºÖµÊÇÊÕËõ¡£
-* stroke_width£ºÃè±ß¿í¶È¡£
+* stroke_grow: Ãè±ßÀ©ÕÅ/ÊÕËõ·ù¶È£¬ÕýÖµÊÇÀ©ÕÅ£¬¸ºÖµÊÇÊÕËõ¡£
+* stroke_width: Ãè±ß¿í¶È¡£
* stroke_color4: Ãè±ßÑÕÉ«¡£
+### GradientOverlay
+½¥±ä¸²¸Ç
+
-#### ½ÚµãÑ¡Ïî×¢½â£º
+½ÚµãÑ¡Ïî˵Ã÷:
+
+* background_image1: ±³¾°Í¼Ïñ¡£
+* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
+* layer_mask1£¬2: ²ãͼÏñµÄÕÚÕÖ¡£
+* invert_mask: ÊÇ·ñ·´×ªÕÚÕÖ¡£
+* blend_mode3: Ãè±ßµÄ»ìºÏģʽ¡£
+* opacity: ²»Í¸Ã÷¶È¡£
+* start_color: ½¥±ä¿ªÊ¼¶ËµÄÑÕÉ«¡£
+* start_alpha: ½¥±ä¿ªÊ¼¶ËµÄ͸Ã÷¶È¡£
+* end_color: ½¥±ä½áÊø¶ËµÄÑÕÉ«¡£
+* end_alpha: ½¥±ä½áÊø¶ËµÄ͸Ã÷¶È¡£
+* angle: ½¥±äÐýת½Ç¶È¡£
+
+### ColorOverlay
+ÑÕÉ«¸²¸Ç
+
+
+½ÚµãÑ¡Ïî˵Ã÷:
+
+* background_image1: ±³¾°Í¼Ïñ¡£
+* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
+* layer_mask1£¬2: ²ãͼÏñµÄÕÚÕÖ¡£
+* invert_mask: ÊÇ·ñ·´×ªÕÚÕÖ¡£
+* blend_mode3: Ãè±ßµÄ»ìºÏģʽ¡£
+* opacity: ²»Í¸Ã÷¶È¡£
+* color: ¸²¸ÇµÄÑÕÉ«¡£
+
+
+#### ½ÚµãÑ¡Ïî×¢½â:
1 image¡¢maskºÍbackground_image(Èç¹ûÓÐÊäÈë)ÕâÈýÏî±ØÐëÊÇÏàͬµÄ³ß´ç¡£
2 mask²»ÊDZØÐëµÄÊäÈëÏĬÈÏʹÓÃimageµÄalphaͨµÀ£¬Èç¹ûimageÊäÈë²»°üº¬alphaͨµÀ½«×Ô¶¯´´½¨Õû¸öͼÏñµÄalphaͨµÀ¡£Èç¹ûÊäÈëmask£¬Ô±¾µÄalphaͨµÀ½«±»mask¸²¸Ç¡£
3: »ìºÏģʽ°üÀ¨normal¡¢multply¡¢screen¡¢add¡¢subtract¡¢difference¡¢darkerºÍlighter¡£
4: ÑÕɫʹÓÃ16½øÖÆRGB×Ö·û´®¸ñʽÃèÊö£¬ÀýÈç '#FA3D86'¡£
# LayerMask
+
### MaskPreview:
Ô¤ÀÀmask
@@ -125,12 +160,13 @@ mask

# LayerUtility
+
### ImageBlend£º
ÓÃÓںϳÉͼ²ã£¬Ìṩ¶àÖÖ»ìºÏģʽ¹©Ñ¡Ôñ£¬¿ÉÉèÖÃ͸Ã÷¶È¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:

* background_image1: ±³¾°Í¼Ïñ¡£
* layer_image1: ÓÃÓںϳɵIJãͼÏñ¡£
@@ -143,7 +179,7 @@ mask
µ÷ÕûͼÏñ²»Í¸Ã÷¶È¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:
* image: ͼÏñÊäÈ룬֧³ÖRGBºÍRGBAÊäÈë¡£
* mask: ÕÚÕÖÊäÈë¡£
* invert_mask: ÊÇ·ñ·´×ªÕÚÕÖ¡£
@@ -154,31 +190,50 @@ mask
¸Ä×Ô[mtb nodes](https://github.com/melMass/comfy_mtb)µÄweb extensions¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:
* mode£º Êä³ö¸ñʽ£¬¿ÉѡʮÁù½øÖÆ(HEX)»òÊ®½øÖÆ(DEC)¡£
-Êä³ö£º
+Êä³ö:
* string
# LayerFilter
+
### MotionBlur:
Ô˶¯Ä£ºý¡£

-½ÚµãÑ¡Ïî˵Ã÷£º
+½ÚµãÑ¡Ïî˵Ã÷:
* angle: Ä£ºý½Ç¶È¡£
* blur: Ä£ºý´óС¡£
-## ʹÓÃʾÀý£º
+## ʹÓÃʾÀý:
ÔÚworkflowĿ¼ÏÂÓÐjson¸ñʽµÄ¹¤×÷Á÷ʾÀýÎļþ¡£
## ÒÔϹ¤×÷Ôڼƻ®ÁбíÖÐ
-* Color Overlay
-* Gradient Overlay
+* LayerUtility:
+imageblend advance
+color image
+gradient image
+
+* LayerMask:
+text mask
+gradient mask
+mask grow&blur
+mask subtract
+mask combine
+mask diffrent
+mask gradient
+mask softedge
+
+* LayerFilter:
+bulr
+emboss
+contour
+findedge
## °²×°·½·¨£º
diff --git a/image/color_overlay_example.png b/image/color_overlay_example.png
new file mode 100644
index 0000000..01eca7a
Binary files /dev/null and b/image/color_overlay_example.png differ
diff --git a/image/color_overlay_node.png b/image/color_overlay_node.png
new file mode 100644
index 0000000..367dcff
Binary files /dev/null and b/image/color_overlay_node.png differ
diff --git a/image/gradient_overlay_example.png b/image/gradient_overlay_example.png
new file mode 100644
index 0000000..a3ecc9c
Binary files /dev/null and b/image/gradient_overlay_example.png differ
diff --git a/image/gradient_overlay_node.png b/image/gradient_overlay_node.png
new file mode 100644
index 0000000..2c7feb3
Binary files /dev/null and b/image/gradient_overlay_node.png differ
diff --git a/image/layerfilter_nodes.png b/image/layerfilter_nodes.png
new file mode 100644
index 0000000..ccfd8c8
Binary files /dev/null and b/image/layerfilter_nodes.png differ
diff --git a/image/layermask_nodes.png b/image/layermask_nodes.png
new file mode 100644
index 0000000..ff7d5d7
Binary files /dev/null and b/image/layermask_nodes.png differ
diff --git a/image/layerstyle_nodes.png b/image/layerstyle_nodes.png
new file mode 100644
index 0000000..040ceb3
Binary files /dev/null and b/image/layerstyle_nodes.png differ
diff --git a/image/layerutility_nodes.png b/image/layerutility_nodes.png
new file mode 100644
index 0000000..4db758b
Binary files /dev/null and b/image/layerutility_nodes.png differ
diff --git a/image/title.png b/image/title.png
index 8e3c36d..20a511d 100644
Binary files a/image/title.png and b/image/title.png differ
diff --git a/py/color_overlay.py b/py/color_overlay.py
new file mode 100644
index 0000000..d1223d9
--- /dev/null
+++ b/py/color_overlay.py
@@ -0,0 +1,66 @@
+from .imagefunc import *
+
+class ColorOverlay:
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(self):
+ chop_mode = ['normal','multply','screen','add','subtract','difference','darker','lighter']
+ return {
+ "required": {
+ "background_image": ("IMAGE", ), #
+ "layer_image": ("IMAGE",), #
+ "invert_mask": ("BOOLEAN", {"default": True}), # �转mask
+ "blend_mode": (chop_mode,), # 混�模�
+ "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # �明度
+ "color": ("STRING", {"default": "#FFBF30"}), # ��开始颜色
+ },
+ "optional": {
+ "layer_mask": ("MASK",), #
+ }
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ RETURN_NAMES = ("image",)
+ FUNCTION = 'color_overlay'
+ CATEGORY = '😺dzNodes/LayerStyle'
+ OUTPUT_NODE = True
+
+ def color_overlay(self, background_image, layer_image,
+ invert_mask, blend_mode, opacity, color,
+ layer_mask=None
+ ):
+
+ # preprocess
+ _canvas = tensor2pil(background_image).convert('RGB')
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
+ if layer_mask is not None:
+ if invert_mask:
+ layer_mask = 1 - layer_mask
+ _mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
+
+ _color = Image.new('RGB', size=_layer.size, color=color)
+ # ��layer
+ _comp = chop_image(_layer, _color, blend_mode, opacity)
+ _canvas.paste(_comp, mask=_mask)
+ ret_image = _canvas
+ log('ColorOverlay Processed.')
+ return (pil2tensor(ret_image),)
+
+NODE_CLASS_MAPPINGS = {
+ "LayerStyle: ColorOverlay": ColorOverlay
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "LayerStyle: ColorOverlay": "LayerStyle: ColorOverlay"
+}
\ No newline at end of file
diff --git a/py/drop_shadow.py b/py/drop_shadow.py
index e13aa49..85adb45 100644
--- a/py/drop_shadow.py
+++ b/py/drop_shadow.py
@@ -38,16 +38,24 @@ class DropShadow:
layer_mask=None
):
- distance_x = -distance_x
- distance_y = -distance_y
+ # preprocess
_canvas = tensor2pil(background_image).convert('RGB')
- _layer = tensor2pil(layer_image).convert('RGB')
- _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
- # 处�mask
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
if layer_mask is not None:
if invert_mask:
layer_mask = 1 - layer_mask
_mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
+
+ distance_x = -distance_x
+ distance_y = -distance_y
if distance_x != 0 or distance_y != 0:
__mask = shift_image(_mask, distance_x, distance_y) # �移
shadow_mask = expand_mask(image2mask(__mask), grow, blur) #æ‰©å¼ ï¼Œæ¨¡ç³Š
diff --git a/py/gradient_overlay.py b/py/gradient_overlay.py
new file mode 100644
index 0000000..4b04c51
--- /dev/null
+++ b/py/gradient_overlay.py
@@ -0,0 +1,79 @@
+from .imagefunc import *
+
+class GradientOverlay:
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(self):
+ chop_mode = ['normal','multply','screen','add','subtract','difference','darker','lighter']
+ return {
+ "required": {
+ "background_image": ("IMAGE", ), #
+ "layer_image": ("IMAGE",), #
+ "invert_mask": ("BOOLEAN", {"default": True}), # �转mask
+ "blend_mode": (chop_mode,), # 混�模�
+ "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # �明度
+ "start_color": ("STRING", {"default": "#FFBF30"}), # ��开始颜色
+ "start_alpha": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
+ "end_color": ("STRING", {"default": "#FE0000"}), # ��结�颜色
+ "end_alpha": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
+ "angle": ("INT", {"default": 0, "min": -180, "max": 180, "step": 1}), # ��角度
+ },
+ "optional": {
+ "layer_mask": ("MASK",), #
+ }
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ RETURN_NAMES = ("image",)
+ FUNCTION = 'gradient_overlay'
+ CATEGORY = '😺dzNodes/LayerStyle'
+ OUTPUT_NODE = True
+
+ def gradient_overlay(self, background_image, layer_image,
+ invert_mask, blend_mode, opacity,
+ start_color, start_alpha, end_color, end_alpha, angle,
+ layer_mask=None
+ ):
+
+ # preprocess
+ _canvas = tensor2pil(background_image).convert('RGB')
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
+ if layer_mask is not None:
+ if invert_mask:
+ layer_mask = 1 - layer_mask
+ _mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
+
+ _gradient = gradint(start_color, end_color, _layer.width, _layer.height, float(angle))
+
+ # ��layer
+ _comp = chop_image(_layer, _gradient, blend_mode, opacity)
+ if start_alpha < 255 or end_alpha < 255:
+ #
+ start_color = RGB_to_Hex((start_alpha, start_alpha, start_alpha))
+ end_color = RGB_to_Hex((end_alpha, end_alpha, end_alpha))
+ comp_alpha = gradint(start_color, end_color, _layer.width, _layer.height, float(angle))
+ comp_alpha = ImageChops.invert(comp_alpha).convert('L')
+ _comp.paste(_layer, comp_alpha)
+ _canvas.paste(_comp, mask=_mask)
+ ret_image = _canvas
+ log('GradientOverlay Processed.')
+ return (pil2tensor(ret_image),)
+
+NODE_CLASS_MAPPINGS = {
+ "LayerStyle: GradientOverlay": GradientOverlay
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "LayerStyle: GradientOverlay": "LayerStyle: GradientOverlay"
+}
\ No newline at end of file
diff --git a/py/imagefunc.py b/py/imagefunc.py
index e4d8cdc..35b2963 100644
--- a/py/imagefunc.py
+++ b/py/imagefunc.py
@@ -6,7 +6,7 @@ import torch
import scipy.ndimage
import cv2
from typing import Union, List
-from PIL import Image, ImageFilter, ImageChops
+from PIL import Image, ImageFilter, ImageChops, ImageDraw
def log(message):
name = 'LayerStyle'
@@ -119,9 +119,70 @@ def motion_blur(image:Image, angle:int, blur:int) -> Image:
ret_image = cv22pil(blurred)
return ret_image
-def direction_blur(image:Image, angle:int, blur:int, color:str) -> Image:
+def __rotate_expand(image:Image, angle:float, SSAA:int=0) -> Image:
+ images = pil2tensor(image)
+ expand = "true"
+ height, width = images[0, :, :, 0].shape
- ret_image = image
+ def rotate_tensor(tensor):
+ resize_sampler = Image.LANCZOS
+ rotate_sampler = Image.BICUBIC
+ if SSAA > 1:
+ img = tensor.tensor_to_image()
+ img_us_scaled = img.resize((width * SSAA, height * SSAA), resize_sampler)
+ img_rotated = img_us_scaled.rotate(angle, rotate_sampler, expand == "true", fillcolor=(0, 0, 0, 0))
+ img_down_scaled = img_rotated.resize((img_rotated.width // SSAA, img_rotated.height // SSAA), resize_sampler)
+ result = img_down_scaled.image_to_tensor()
+ else:
+ img = tensor.tensor_to_image()
+ img_rotated = img.rotate(angle, rotate_sampler, expand == "true", fillcolor=(0, 0, 0, 0))
+ result = img_rotated.image_to_tensor()
+ return result
+
+ if angle == 0.0 or angle == 360.0:
+ return tensor2pil(images)
+ else:
+ rotated_tensor = torch.stack([rotate_tensor(images[i]) for i in range(len(images))])
+ return tensor2pil(rotated_tensor).convert('RGB')
+
+def image_rotate_extend_with_alpha(image:Image, alpha:Image, angle:float, SSAA:int=0) -> tuple:
+ _image = __rotate_expand(image.convert('RGB'), angle, SSAA)
+ _alpha = __rotate_expand(alpha.convert('RGB'), angle, SSAA)
+ R, G, B = _image.split()
+ A = _alpha.convert('L')
+ ret_image = Image.merge('RGBA', (R, G, B, A))
+ return (_image, _alpha, ret_image)
+
+
+def create_gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int) -> Image:
+ start_color = Hex_to_RGB(start_color_inhex)
+ end_color = Hex_to_RGB(end_color_inhex)
+ ret_image = Image.new("RGB", (width, height), start_color)
+ draw = ImageDraw.Draw(ret_image)
+ for i in range(height):
+ R = int(start_color[0] * (height - i) / height + end_color[0] * i / height)
+ G = int(start_color[1] * (height - i) / height + end_color[1] * i / height)
+ B = int(start_color[2] * (height - i) / height + end_color[2] * i / height)
+ color = (R, G, B)
+ draw.line((0, i, width, i), fill=color)
+ return ret_image
+
+def gradint(start_color_inhex:str, end_color_inhex:str, width:int, height:int, angle:float, ) -> Image:
+ radius = int((width + height) / 4)
+ g = create_gradient(start_color_inhex, end_color_inhex, radius, radius)
+ _canvas = Image.new('RGB', size=(radius, radius*3), color=start_color_inhex)
+ top = Image.new('RGB', size=(radius, radius), color=start_color_inhex)
+ bottom = Image.new('RGB', size=(radius, radius),color=end_color_inhex)
+ _canvas.paste(top, box=(0, 0, radius, radius))
+ _canvas.paste(g, box=(0, radius, radius, radius * 2))
+ _canvas.paste(bottom,box=(0, radius * 2, radius, radius * 3))
+ _canvas = _canvas.resize((radius * 3, radius * 3))
+ _canvas = __rotate_expand(_canvas,angle)
+ center = int(_canvas.width / 2)
+ _x = int(width / 3)
+ _y = int(height / 3)
+ ret_image = _canvas.crop((center - _x, center - _y, center + _x, center + _y))
+ ret_image = ret_image.resize((width, height))
return ret_image
'''Mask Functions'''
@@ -180,18 +241,15 @@ def step_value(start_value, end_value, total_step, step) -> float: # 按当�
factor = step / total_step
return (end_value - start_value) * factor + start_value
-def step_color(start_color, end_color, total_step, step): # 按当å‰�æ¥æ•°åœ¨æ€»æ¥æ•°ä¸çš„ä½�置返回比例颜色
- if isinstance(start_color, str):
- start_color = tuple(Hex_to_RGB(start_color))
- if isinstance(end_color, str):
- end_color = tuple(Hex_to_RGB(end_color))
+def step_color(start_color_inhex:str, end_color_inhex:str, total_step:int, step:int) -> str: # 按当å‰�æ¥æ•°åœ¨æ€»æ¥æ•°ä¸çš„ä½�置返回比例颜色
+ start_color = tuple(Hex_to_RGB(start_color_inhex))
+ end_color = tuple(Hex_to_RGB(end_color_inhex))
start_R, start_G, start_B = start_color[0], start_color[1], start_color[2]
end_R, end_G, end_B = end_color[0], end_color[1], end_color[2]
ret_color = (int(step_value(start_R, end_R, total_step, step)),
int(step_value(start_G, end_G, total_step, step)),
int(step_value(start_B, end_B, total_step, step)),
)
- if isinstance(start_color, str):
- return RGB_to_Hex(ret_color)
- else:
- return ret_color
+ return RGB_to_Hex(ret_color)
+
+
diff --git a/py/inner_glow.py b/py/inner_glow.py
index eee6107..e6db05e 100644
--- a/py/inner_glow.py
+++ b/py/inner_glow.py
@@ -38,14 +38,21 @@ class InnerGlow:
layer_mask=None
):
+ # preprocess
_canvas = tensor2pil(background_image).convert('RGB')
- _layer = tensor2pil(layer_image).convert('RGB')
- _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
- # 处�mask
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
if layer_mask is not None:
if invert_mask:
layer_mask = 1 - layer_mask
_mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
blur_factor = blur / 20.0
grow = glow_range
diff --git a/py/inner_shadow.py b/py/inner_shadow.py
index c60622a..35cc20d 100644
--- a/py/inner_shadow.py
+++ b/py/inner_shadow.py
@@ -39,16 +39,24 @@ class InnerShadow:
layer_mask=None
):
- distance_x = -distance_x
- distance_y = -distance_y
+ # preprocess
_canvas = tensor2pil(background_image).convert('RGB')
- _layer = tensor2pil(layer_image).convert('RGB')
- _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
- # 处�mask
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
if layer_mask is not None:
if invert_mask:
layer_mask = 1 - layer_mask
_mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
+
+ distance_x = -distance_x
+ distance_y = -distance_y
if distance_x != 0 or distance_y != 0:
__mask = shift_image(_mask, distance_x, distance_y) # �移
shadow_mask = expand_mask(image2mask(__mask), grow, blur) #æ‰©å¼ ï¼Œæ¨¡ç³Š
diff --git a/py/outer_glow.py b/py/outer_glow.py
index b9c696e..f13b74d 100644
--- a/py/outer_glow.py
+++ b/py/outer_glow.py
@@ -38,14 +38,22 @@ class OuterGlow:
layer_mask=None
):
+ # preprocess
_canvas = tensor2pil(background_image).convert('RGB')
- _layer = tensor2pil(layer_image).convert('RGB')
- _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
- # 处�mask
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
if layer_mask is not None:
if invert_mask:
layer_mask = 1 - layer_mask
_mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
+
blur_factor = blur / 20.0
grow = glow_range
for x in range(brightness):
diff --git a/py/stroke.py b/py/stroke.py
index 5c2e209..ff50d3b 100644
--- a/py/stroke.py
+++ b/py/stroke.py
@@ -37,13 +37,21 @@ class Stroke:
layer_mask=None
):
+ # preprocess
_canvas = tensor2pil(background_image).convert('RGB')
- _layer = tensor2pil(layer_image).convert('RGB')
- _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ _layer = tensor2pil(layer_image)
+ if _layer.mode == 'RGBA':
+ _mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
+ else:
+ _mask = Image.new('L', _layer.size, 'white')
+ _layer = _layer.convert('RGB')
if layer_mask is not None:
if invert_mask:
layer_mask = 1 - layer_mask
_mask = mask2image(layer_mask).convert('L')
+ if _mask.size != _layer.size:
+ _mask = Image.new('L', _layer.size, 'white')
+ log('Warning: mask mismatch, droped!')
grow_offset = int(stroke_width / 2)
inner_stroke = stroke_grow - grow_offset
diff --git a/workflow/color_overlay_example.json b/workflow/color_overlay_example.json
new file mode 100644
index 0000000..4232f82
--- /dev/null
+++ b/workflow/color_overlay_example.json
@@ -0,0 +1,203 @@
+{
+ "last_node_id": 25,
+ "last_link_id": 61,
+ "nodes": [
+ {
+ "id": 15,
+ "type": "LoadImage",
+ "pos": [
+ 659,
+ 386
+ ],
+ "size": {
+ "0": 315,
+ "1": 314
+ },
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 59
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": [
+ 60
+ ],
+ "shape": 3,
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "512x512 (14).png",
+ "image"
+ ]
+ },
+ {
+ "id": 2,
+ "type": "LoadImage",
+ "pos": [
+ 307,
+ 382
+ ],
+ "size": {
+ "0": 315,
+ "1": 314
+ },
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 58
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": [],
+ "shape": 3,
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "512x512bk.jpg",
+ "image"
+ ]
+ },
+ {
+ "id": 24,
+ "type": "LayerStyle: ColorOverlay",
+ "pos": [
+ 1008,
+ 440
+ ],
+ "size": {
+ "0": 315,
+ "1": 170
+ },
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "background_image",
+ "type": "IMAGE",
+ "link": 58
+ },
+ {
+ "name": "layer_image",
+ "type": "IMAGE",
+ "link": 59
+ },
+ {
+ "name": "layer_mask",
+ "type": "MASK",
+ "link": 60
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 61
+ ],
+ "shape": 3,
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerStyle: ColorOverlay"
+ },
+ "widgets_values": [
+ true,
+ "multply",
+ 66,
+ "#FFBF30"
+ ]
+ },
+ {
+ "id": 4,
+ "type": "PreviewImage",
+ "pos": [
+ 1358,
+ 386
+ ],
+ "size": {
+ "0": 310.00006103515625,
+ "1": 308.33331298828125
+ },
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 61
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ }
+ }
+ ],
+ "links": [
+ [
+ 58,
+ 2,
+ 0,
+ 24,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 59,
+ 15,
+ 0,
+ 24,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 60,
+ 15,
+ 1,
+ 24,
+ 2,
+ "MASK"
+ ],
+ [
+ 61,
+ 24,
+ 0,
+ 4,
+ 0,
+ "IMAGE"
+ ]
+ ],
+ "groups": [],
+ "config": {},
+ "extra": {},
+ "version": 0.4
+}
\ No newline at end of file
diff --git a/workflow/gradident_overlay_example.json b/workflow/gradident_overlay_example.json
new file mode 100644
index 0000000..22e3d9f
--- /dev/null
+++ b/workflow/gradident_overlay_example.json
@@ -0,0 +1,207 @@
+{
+ "last_node_id": 29,
+ "last_link_id": 66,
+ "nodes": [
+ {
+ "id": 4,
+ "type": "PreviewImage",
+ "pos": [
+ 1358,
+ 386
+ ],
+ "size": {
+ "0": 310.00006103515625,
+ "1": 308.33331298828125
+ },
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 66
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ }
+ },
+ {
+ "id": 2,
+ "type": "LoadImage",
+ "pos": [
+ 307,
+ 382
+ ],
+ "size": {
+ "0": 315,
+ "1": 314
+ },
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 62
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": [],
+ "shape": 3,
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "512x512bk.jpg",
+ "image"
+ ]
+ },
+ {
+ "id": 15,
+ "type": "LoadImage",
+ "pos": [
+ 659,
+ 386
+ ],
+ "size": {
+ "0": 315,
+ "1": 314
+ },
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 63
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": [
+ 64
+ ],
+ "shape": 3,
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "512x512 (14).png",
+ "image"
+ ]
+ },
+ {
+ "id": 27,
+ "type": "LayerStyle: GradientOverlay",
+ "pos": [
+ 1015,
+ 408
+ ],
+ "size": {
+ "0": 315,
+ "1": 266
+ },
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "background_image",
+ "type": "IMAGE",
+ "link": 62
+ },
+ {
+ "name": "layer_image",
+ "type": "IMAGE",
+ "link": 63
+ },
+ {
+ "name": "layer_mask",
+ "type": "MASK",
+ "link": 64
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 66
+ ],
+ "shape": 3,
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerStyle: GradientOverlay"
+ },
+ "widgets_values": [
+ true,
+ "normal",
+ 100,
+ "#FFBF30",
+ 60,
+ "#FE0000",
+ 255,
+ 0
+ ]
+ }
+ ],
+ "links": [
+ [
+ 62,
+ 2,
+ 0,
+ 27,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 63,
+ 15,
+ 0,
+ 27,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 64,
+ 15,
+ 1,
+ 27,
+ 2,
+ "MASK"
+ ],
+ [
+ 66,
+ 27,
+ 0,
+ 4,
+ 0,
+ "IMAGE"
+ ]
+ ],
+ "groups": [],
+ "config": {},
+ "extra": {},
+ "version": 0.4
+}
\ No newline at end of file