Files
2025-03-12 07:13:29 -04:00

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)