wip
This commit is contained in:
@@ -52,8 +52,8 @@ def generate_texture_image(props, width, height):
|
||||
lanczos_filter = getattr(resampling_module, 'LANCZOS', getattr(Image, 'LANCZOS', Image.BICUBIC))
|
||||
bicubic_filter = getattr(resampling_module, 'BICUBIC', getattr(Image, 'BICUBIC', Image.BILINEAR))
|
||||
|
||||
def _iter_enabled_overlays():
|
||||
"""Yield (overlay, image_path) tuples for overlays that should be applied."""
|
||||
def _iter_enabled_components():
|
||||
"""Yield enabled composition components."""
|
||||
overlays = getattr(props, 'image_overlays', [])
|
||||
try:
|
||||
overlay_list = list(overlays)
|
||||
@@ -62,17 +62,14 @@ def generate_texture_image(props, width, height):
|
||||
for overlay in overlay_list:
|
||||
if not getattr(overlay, 'enabled', True):
|
||||
continue
|
||||
image_path = getattr(overlay, 'image_path', '')
|
||||
if not image_path:
|
||||
continue
|
||||
yield overlay, image_path
|
||||
yield overlay
|
||||
|
||||
def _calculate_overlay_position(overlay, overlay_size):
|
||||
"""Calculate top-left pixel position for an overlay image."""
|
||||
overlay_width, overlay_height = overlay_size
|
||||
canvas_width = width
|
||||
canvas_height = height
|
||||
mode = getattr(overlay, 'positioning_mode', 'ABSOLUTE') or 'ABSOLUTE'
|
||||
mode = getattr(overlay, 'placement_mode', 'ABSOLUTE') or 'ABSOLUTE'
|
||||
|
||||
x_norm = float(getattr(overlay, 'x_position', 0.5) or 0.5)
|
||||
y_norm = float(getattr(overlay, 'y_position', 0.5) or 0.5)
|
||||
@@ -95,16 +92,57 @@ def generate_texture_image(props, width, height):
|
||||
y = max(0, min(max_y, y))
|
||||
return x, y
|
||||
|
||||
def _apply_image_overlays(pil_canvas):
|
||||
"""Composite enabled image overlays onto the base canvas."""
|
||||
overlays_to_apply = sorted(
|
||||
_iter_enabled_overlays(),
|
||||
key=lambda item: getattr(item[0], 'z_index', 0)
|
||||
def _apply_composition_components(pil_canvas):
|
||||
"""Composite enabled components (text or image) onto the base canvas."""
|
||||
components = sorted(
|
||||
_iter_enabled_components(),
|
||||
key=lambda comp: getattr(comp, 'z_index', 0)
|
||||
)
|
||||
|
||||
for overlay, image_path in overlays_to_apply:
|
||||
resolved_path = image_path
|
||||
scale = getattr(overlay, 'scale', 1.0)
|
||||
for component in components:
|
||||
if getattr(component, 'component_type', 'IMAGE') == 'TEXT':
|
||||
text_value = getattr(component, 'text_content', '').strip()
|
||||
if not text_value:
|
||||
continue
|
||||
scale = getattr(component, 'scale', 1.0)
|
||||
try:
|
||||
scale = float(scale if scale is not None else 1.0)
|
||||
except (TypeError, ValueError):
|
||||
scale = 1.0
|
||||
text_size = max(8, int(round(props.font_size * max(scale, 0.1))))
|
||||
text_font = load_font_for_size(text_size)
|
||||
try:
|
||||
text_bbox = text_font.getbbox(text_value)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_height = text_bbox[3] - text_bbox[1]
|
||||
except AttributeError:
|
||||
try:
|
||||
text_width, text_height = text_font.getsize(text_value)
|
||||
except AttributeError:
|
||||
# Fallback to a temporary draw context
|
||||
tmp_draw = ImageDraw.Draw(Image.new('RGBA', (1, 1)))
|
||||
text_width, text_height = tmp_draw.textsize(text_value, font=text_font)
|
||||
|
||||
overlay_img = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0))
|
||||
overlay_draw = ImageDraw.Draw(overlay_img)
|
||||
text_color = tuple(int(c * 255) for c in props.text_color[:4])
|
||||
overlay_draw.text((0, 0), text_value, font=text_font, fill=text_color)
|
||||
|
||||
rotation = float(getattr(component, 'rotation', 0.0) or 0.0)
|
||||
if rotation % 360:
|
||||
overlay_img = overlay_img.rotate(-rotation, expand=True, resample=bicubic_filter)
|
||||
|
||||
x_pos, y_pos = _calculate_overlay_position(component, overlay_img.size)
|
||||
|
||||
overlay_layer = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
overlay_layer.paste(overlay_img, (x_pos, y_pos), overlay_img)
|
||||
pil_canvas = Image.alpha_composite(pil_canvas, overlay_layer)
|
||||
continue
|
||||
|
||||
resolved_path = getattr(component, 'image_path', '')
|
||||
if not resolved_path:
|
||||
continue
|
||||
scale = getattr(component, 'scale', 1.0)
|
||||
try:
|
||||
scale = float(scale if scale is not None else 1.0)
|
||||
except (TypeError, ValueError):
|
||||
@@ -121,7 +159,7 @@ def generate_texture_image(props, width, height):
|
||||
resolved_path = raster_info.path
|
||||
scale_to_apply = raster_info.applied_scale
|
||||
except SVGConversionError as svg_error:
|
||||
print(f"WARNING: Failed to rasterize SVG overlay '{image_path}': {svg_error}")
|
||||
print(f"WARNING: Failed to rasterize SVG overlay '{component.image_path}': {svg_error}")
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -139,11 +177,11 @@ def generate_texture_image(props, width, height):
|
||||
if new_size != overlay_img.size:
|
||||
overlay_img = overlay_img.resize(new_size, lanczos_filter)
|
||||
|
||||
rotation = float(getattr(overlay, 'rotation', 0.0) or 0.0)
|
||||
rotation = float(getattr(component, 'rotation', 0.0) or 0.0)
|
||||
if rotation % 360:
|
||||
overlay_img = overlay_img.rotate(-rotation, expand=True, resample=bicubic_filter)
|
||||
|
||||
x_pos, y_pos = _calculate_overlay_position(overlay, overlay_img.size)
|
||||
x_pos, y_pos = _calculate_overlay_position(component, overlay_img.size)
|
||||
|
||||
overlay_layer = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
overlay_layer.paste(overlay_img, (x_pos, y_pos), overlay_img)
|
||||
@@ -358,7 +396,7 @@ def generate_texture_image(props, width, height):
|
||||
pass
|
||||
|
||||
# Apply any configured image overlays before converting to Blender pixels
|
||||
pil_img = _apply_image_overlays(pil_img)
|
||||
pil_img = _apply_composition_components(pil_img)
|
||||
|
||||
# Convert PIL image to Blender format (flip vertically to fix mirroring)
|
||||
pil_img = pil_img.transpose(Image.FLIP_TOP_BOTTOM) # Fix mirroring issue
|
||||
|
||||
Reference in New Issue
Block a user