Files

263 lines
10 KiB
Python

from ..helpers import inv
from .darken import darken_gamma_dark
from .hsy_helpers import get_luminosity
from .lighten_helpers import penumbra
from ..blend_modes_enum import BlendModes
import torch
def lighten_color_dodge(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
max_val = torch.tensor(1.0, device=blend_image.device, dtype=blend_image.dtype)
zero_val = torch.tensor(0.0, device=blend_image.device, dtype=blend_image.dtype)
result = torch.where(
blend_image == max_val,
torch.where(base_image == zero_val, zero_val, max_val),
torch.clamp(base_image / (max_val - blend_image), 0, 1)
)
return result
def lighten_linear_dodge(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = base_image + blend_image
return result.clamp( 0, 1)
def lighten_lighten(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
return torch.max(base_image, blend_image)
def lighten_linear_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = (blend_image + blend_image + base_image) - 1.0
return torch.clamp(result, 0, 1)
def lighten_screen(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = 1 - (1 - blend_image) * (1 - base_image)
return torch.clamp(result, 0, 1)
def lighten_pin_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
blend_image2 = 2 * blend_image
min = torch.min(base_image, blend_image2)
result = torch.max(blend_image2 - 1.0, min)
return result.clamp(0, 1)
def lighten_vivid_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
half_val = 0.5
one_val = 1.0
zero_val = 0.0
result = torch.where(
blend_image < half_val,
torch.where(
blend_image == zero_val,
torch.where(base_image == one_val, one_val, zero_val),
torch.clamp(one_val - (1 - base_image) / (2 * blend_image), 0, 1)
),
torch.where(
blend_image == one_val,
torch.where(base_image == zero_val, zero_val, one_val),
torch.clamp(base_image / (2 * (1 - blend_image)), 0, 1)
)
)
return result
def lighten_flat_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
mask_src_zero = torch.isclose(blend_image, torch.tensor(0.0))
result = torch.zeros_like(base_image)
mask_src_nonzero = ~mask_src_zero
inv_src = 1 - blend_image[mask_src_nonzero]
sum_tensor = inv_src + base_image[mask_src_nonzero]
hard_mix_result = torch.where(sum_tensor > 1.0, torch.ones_like(sum_tensor), torch.zeros_like(sum_tensor))
penumbra_a_result = penumbra(base_image[mask_src_nonzero], blend_image[mask_src_nonzero], lighten_color_dodge)
penumbra_b_result = penumbra(blend_image[mask_src_nonzero], base_image[mask_src_nonzero], lighten_color_dodge)
result[mask_src_nonzero] = torch.where(
torch.isclose(hard_mix_result, torch.tensor(1.0)),
penumbra_b_result,
penumbra_a_result
)
return result
def lighten_hard_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
ones = torch.ones_like(blend_image)
sum = blend_image + blend_image
screen_result = torch.where(
blend_image > 0.5,
base_image + sum - ones - base_image * (sum - ones),
torch.zeros_like(base_image)
)
multiply_result = torch.where(
blend_image <= 0.5,
base_image * sum,
torch.zeros_like(base_image)
)
return torch.clamp(screen_result + multiply_result, 0.0, 1.0)
def lighten_soft_light_ifs_illusions(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
exponent = torch.pow(2.0, 2.0 * (0.5 - blend_image))
result = torch.pow(base_image, exponent)
return torch.clamp(result, 0.0, 1.0)
def lighten_soft_light_pegtop_delphi(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
term1 = base_image * lighten_screen(blend_image, base_image)
term2 = blend_image * base_image * (1 - base_image)
result = lighten_linear_dodge(term1, term2)
return torch.clamp(result, 0.0, 1.0)
def lighten_soft_light_ps(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
condition = blend_image > 0.5
high_result = base_image + (2 * blend_image - 1) * (torch.sqrt(base_image) - base_image)
low_result = base_image - (1 - 2 * blend_image) * base_image * (1 - base_image)
result = torch.where(condition, high_result, low_result)
return torch.clamp(result, 0.0, 1.0)
def lighten_soft_light_svg(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
condition_src = blend_image > 0.5
condition_dst = base_image > 0.25
D_high = torch.sqrt(base_image)
D_low = ((16.0 * base_image - 12.0) * base_image + 4.0) * base_image
D = torch.where(condition_dst, D_high, D_low)
high_result = base_image + (2.0 * blend_image - 1.0) * (D - base_image)
low_result = base_image - (1.0 - 2.0 * blend_image) * base_image * (1.0 - base_image)
result = torch.where(condition_src, high_result, low_result)
return torch.clamp(result, 0.0, 1.0)
def lighten_gamma_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = torch.pow(base_image, blend_image)
return torch.clamp(result, 0.0, 1.0)
def lighten_gamma_illumination(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
inv_base = 1.0 - base_image
inv_blend = 1.0 - blend_image
result = 1 - darken_gamma_dark(inv_blend, inv_base)
return torch.clamp(result, 0.0, 1.0)
def lighten_lighter_color(blend_image: torch.Tensor, base_image: torch.Tensor) -> torch.Tensor:
lum_base = get_luminosity(base_image)
lum_blend = get_luminosity(blend_image)
mask = lum_blend > lum_base
mask = mask.unsqueeze(-1).expand(-1, -1, -1, 3)
result = torch.where(mask, blend_image, base_image)
return torch.clamp(result, 0.0, 1.0)
def lighten_pnorm_a(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
power_factor = 2.3333333333333333
inverse_power_factor = 0.428571428571434
src_power = torch.pow(blend_image, power_factor)
dst_power = torch.pow(base_image, power_factor)
sum_power = src_power + dst_power
result = torch.pow(sum_power, inverse_power_factor)
return result.clamp(0, 1)
def lighten_pnorm_b(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
power_factor = 4.0
inverse_power_factor = 0.25
src_power = torch.pow(blend_image, power_factor)
dst_power = torch.pow(base_image, power_factor)
sum_power = src_power + dst_power
result = torch.pow(sum_power, inverse_power_factor)
return result.clamp(0, 1)
def lighten_super_light(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
def pow_tensor(tensor, power):
return torch.pow(tensor, power)
def inv(tensor):
return 1.0 - tensor
power_val = 2.875
result = torch.where(
blend_image < 0.5,
inv(pow_tensor(pow_tensor(inv(base_image), power_val) + pow_tensor(inv(2.0 * blend_image), power_val), 1.0 / power_val)),
pow_tensor(pow_tensor(base_image, power_val) + pow_tensor(2.0 * blend_image - 1.0, power_val), 1.0 / power_val)
)
return result.clamp(0, 1)
def lighten_tint_ifs_illusions(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = base_image * (1 - blend_image) + torch.sqrt(blend_image)
return result.clamp(0, 1)
def lighten_fog_lighten_ifs_illusions(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = torch.where(
blend_image < 0.5,
inv(inv(blend_image) * blend_image) - inv(base_image) * inv(blend_image),
blend_image - inv(base_image) * inv(blend_image) + torch.pow(inv(blend_image), 2)
)
return result.clamp(0, 1)
def lighten_easy_dodge(base_image: torch.Tensor, blend_image: torch.Tensor) -> torch.Tensor:
result = torch.where(
blend_image == 1.0,
torch.tensor(1.0, device=blend_image.device, dtype=blend_image.dtype),
torch.pow(base_image, inv(torch.where(blend_image != 1.0, blend_image, torch.tensor(0.999999999999, device=blend_image.device, dtype=blend_image.dtype))) * 1.039999999)
)
return result.clamp(0, 1)
def lighten_luminosity_sai(src: torch.Tensor, dst: torch.Tensor) -> torch.Tensor:
# Check if alpha channel is present
has_alpha = src.shape[-1] == 4
if has_alpha:
src_rgb, src_a = src[..., :3], src[..., 3:]
dst_rgb, dst_a = dst[..., :3], dst[..., 3:]
else:
src_rgb, src_a = src, torch.ones_like(src[..., :1])
dst_rgb, dst_a = dst, torch.ones_like(dst[..., :1])
result_rgb = src_rgb * src_a + dst_rgb
result_rgb = torch.clamp(result_rgb, 0, 1)
if has_alpha:
result = torch.cat([result_rgb, dst_a], dim=-1)
else:
result = result_rgb
return result
lighten_blend_functions = {
BlendModes.LIGHTEN_COLOR_DODGE: lighten_color_dodge,
BlendModes.LIGHTEN_LINEAR_DODGE: lighten_linear_dodge,
BlendModes.LIGHTEN_LIGHTEN: lighten_lighten,
BlendModes.LIGHTEN_LINEAR_LIGHT: lighten_linear_light,
BlendModes.LIGHTEN_SCREEN: lighten_screen,
BlendModes.LIGHTEN_PIN_LIGHT: lighten_pin_light,
BlendModes.LIGHTEN_VIVID_LIGHT: lighten_vivid_light,
BlendModes.LIGHTEN_FLAT_LIGHT: lighten_flat_light,
BlendModes.LIGHTEN_HARD_LIGHT: lighten_hard_light,
BlendModes.LIGHTEN_SOFT_LIGHT_IFS_ILLUSIONS: lighten_soft_light_ifs_illusions,
BlendModes.LIGHTEN_SOFT_LIGHT_PEGTOP_DELPHI: lighten_soft_light_pegtop_delphi,
BlendModes.LIGHTEN_SOFT_LIGHT_PS: lighten_soft_light_ps,
BlendModes.LIGHTEN_SOFT_LIGHT_SVG: lighten_soft_light_svg,
BlendModes.LIGHTEN_GAMMA_LIGHT: lighten_gamma_light,
BlendModes.LIGHTEN_GAMMA_ILLUMINATION: lighten_gamma_illumination,
BlendModes.LIGHTEN_LIGHTER_COLOR: lighten_lighter_color,
BlendModes.LIGHTEN_PNORM_A: lighten_pnorm_a,
BlendModes.LIGHTEN_PNORM_B: lighten_pnorm_b,
BlendModes.LIGHTEN_SUPER_LIGHT: lighten_super_light,
BlendModes.LIGHTEN_TINT_IFS_ILLUSIONS: lighten_tint_ifs_illusions,
BlendModes.LIGHTEN_FOG_LIGHTEN_IFS_ILLUSIONS: lighten_fog_lighten_ifs_illusions,
BlendModes.LIGHTEN_EASY_DODGE: lighten_easy_dodge,
BlendModes.LIGHTEN_LUMINOSITY_SAI: lighten_luminosity_sai,
}