122 lines
5.3 KiB
Python
122 lines
5.3 KiB
Python
import numpy as np
|
|
from typing import Dict, Callable
|
|
|
|
class EnhancedBlendMode:
|
|
"""Enhanced blend mode operations with optimized processing"""
|
|
|
|
@staticmethod
|
|
def _ensure_rgba(img: np.ndarray) -> np.ndarray:
|
|
if len(img.shape) == 3:
|
|
if img.shape[2] == 3:
|
|
alpha = np.ones((*img.shape[:2], 1), dtype=img.dtype) * 255
|
|
return np.concatenate([img, alpha], axis=-1)
|
|
return img
|
|
return np.stack([img] * 4, axis=-1)
|
|
|
|
@staticmethod
|
|
def _apply_blend(target: np.ndarray, blend: np.ndarray, operation, opacity: float = 1.0) -> np.ndarray:
|
|
target = EnhancedBlendMode._ensure_rgba(target).astype(np.float32)
|
|
blend = EnhancedBlendMode._ensure_rgba(blend).astype(np.float32)
|
|
|
|
target = target / 255.0
|
|
blend = blend / 255.0
|
|
|
|
target_rgb = target[..., :3]
|
|
blend_rgb = blend[..., :3]
|
|
target_a = target[..., 3:4]
|
|
blend_a = blend[..., 3:4]
|
|
|
|
result_rgb = operation(target_rgb, blend_rgb)
|
|
result_a = target_a + blend_a * (1 - target_a) * opacity
|
|
|
|
result = np.concatenate([
|
|
result_rgb * opacity + target_rgb * (1 - opacity),
|
|
result_a
|
|
], axis=-1)
|
|
|
|
return (np.clip(result, 0, 1) * 255).astype(np.uint8)
|
|
|
|
@classmethod
|
|
def get_blend_modes(cls) -> Dict[str, Callable]:
|
|
return {
|
|
"normal": cls.normal,
|
|
"multiply": cls.multiply,
|
|
"screen": cls.screen,
|
|
"overlay": cls.overlay,
|
|
"soft_light": cls.soft_light,
|
|
"hard_light": cls.hard_light,
|
|
"difference": cls.difference,
|
|
"exclusion": cls.exclusion,
|
|
"color_dodge": cls.color_dodge,
|
|
"color_burn": cls.color_burn,
|
|
"linear_light": cls.linear_light,
|
|
"pin_light": cls.pin_light,
|
|
}
|
|
|
|
@staticmethod
|
|
def normal(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
return EnhancedBlendMode._apply_blend(target, blend, lambda t, b: b, opacity)
|
|
|
|
@staticmethod
|
|
def multiply(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
return EnhancedBlendMode._apply_blend(target, blend, lambda t, b: t * b, opacity)
|
|
|
|
@staticmethod
|
|
def screen(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
return EnhancedBlendMode._apply_blend(target, blend, lambda t, b: 1 - (1 - t) * (1 - b), opacity)
|
|
|
|
@staticmethod
|
|
def overlay(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def overlay_op(t, b):
|
|
return np.where(t > 0.5, 1 - 2 * (1 - t) * (1 - b), 2 * t * b)
|
|
return EnhancedBlendMode._apply_blend(target, blend, overlay_op, opacity)
|
|
|
|
@staticmethod
|
|
def soft_light(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def soft_light_op(t, b):
|
|
return np.where(b > 0.5,
|
|
t + (2 * b - 1) * (t - t * t),
|
|
t - (1 - 2 * b) * t * (1 - t))
|
|
return EnhancedBlendMode._apply_blend(target, blend, soft_light_op, opacity)
|
|
|
|
@staticmethod
|
|
def hard_light(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def hard_light_op(t, b):
|
|
return np.where(b > 0.5,
|
|
1 - 2 * (1 - t) * (1 - b),
|
|
2 * t * b)
|
|
return EnhancedBlendMode._apply_blend(target, blend, hard_light_op, opacity)
|
|
|
|
@staticmethod
|
|
def difference(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
return EnhancedBlendMode._apply_blend(target, blend, lambda t, b: np.abs(t - b), opacity)
|
|
|
|
@staticmethod
|
|
def exclusion(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
return EnhancedBlendMode._apply_blend(target, blend, lambda t, b: t + b - 2 * t * b, opacity)
|
|
|
|
@staticmethod
|
|
def color_dodge(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def color_dodge_op(t, b):
|
|
return np.where(b >= 1, 1, np.minimum(1, t / (1 - b + 1e-6)))
|
|
return EnhancedBlendMode._apply_blend(target, blend, color_dodge_op, opacity)
|
|
|
|
@staticmethod
|
|
def color_burn(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def color_burn_op(t, b):
|
|
return np.where(b <= 0, 0, np.maximum(0, 1 - (1 - t) / (b + 1e-6)))
|
|
return EnhancedBlendMode._apply_blend(target, blend, color_burn_op, opacity)
|
|
|
|
@staticmethod
|
|
def linear_light(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def linear_light_op(t, b):
|
|
return np.clip(2 * b + t - 1, 0, 1)
|
|
return EnhancedBlendMode._apply_blend(target, blend, linear_light_op, opacity)
|
|
|
|
@staticmethod
|
|
def pin_light(target: np.ndarray, blend: np.ndarray, opacity: float = 1.0) -> np.ndarray:
|
|
def pin_light_op(t, b):
|
|
return np.where(b > 0.5,
|
|
np.maximum(t, 2 * (b - 0.5)),
|
|
np.minimum(t, 2 * b))
|
|
return EnhancedBlendMode._apply_blend(target, blend, pin_light_op, opacity) |