"""
LunaEngine Image Conversion Demo
With quality compression parameter
"""
import sys
import os
import math
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from lunaengine.core import LunaEngine, Scene
from lunaengine.ui.elements import *
from lunaengine.utils import EmbeddedImage, ImageConverter
import pygame
class ImageConversionDemoScene(Scene):
def on_exit(self, next_scene = None):
return super().on_exit(next_scene)
def on_enter(self, previous_scene = None):
return super().on_enter(previous_scene)
def __init__(self, engine: LunaEngine = None):
super().__init__(engine)
self.embedded_images = []
self.animation_time = 0
self.converted_images = []
self.selected_image_index = 0
self.available_images = []
self.status_message = "Click 'Find Images' to start"
self.ui_elements_created = False
self.quality = 1.0 # Default quality
def on_enter(self, previous_scene = None):
if not self.ui_elements_created:
self._setup_ui()
self.ui_elements_created = True
return super().on_enter(previous_scene)
def _setup_ui(self):
"""Setup all UI elements with proper layout"""
self.ui_elements.clear()
# Title and subtitle
title = TextLabel(20, 20, "Image Conversion Demo", 32, (255, 255, 0))
self.ui_elements.append(title)
subtitle = TextLabel(20, 60, "PYGAME ONLY - NO PILLOW REQUIRED", 18, (100, 255, 100))
self.ui_elements.append(subtitle)
# Status display
self.status_text = TextLabel(20, 90, self.status_message, 18, (255, 200, 100))
self.ui_elements.append(self.status_text)
# Control buttons
find_btn = Button(20, 130, 150, 40, "Find Images")
find_btn.set_on_click(self._find_images)
self.ui_elements.append(find_btn)
convert_all_btn = Button(180, 130, 150, 40, "Convert All Images")
convert_all_btn.set_on_click(self._convert_all_images)
self.ui_elements.append(convert_all_btn)
view_btn = Button(340, 130, 180, 40, "View Converted Images")
view_btn.set_on_click(self._view_converted_images)
self.ui_elements.append(view_btn)
# Quality slider
quality_label = TextLabel(20, 190, "Quality/Size:", 16, (200, 200, 255))
self.ui_elements.append(quality_label)
self.quality_slider = Slider(120, 190, 200, 20, 0.1, 1.0, self.quality)
self.quality_slider.on_value_changed = self._on_quality_changed
self.ui_elements.append(self.quality_slider)
self.quality_value = TextLabel(330, 190, f"{self.quality:.1f}", 16, (255, 255, 255))
self.ui_elements.append(self.quality_value)
# Statistics display
self.stats_text = TextLabel(20, 220, "Images found: 0 | Converted: 0", 16, (100, 255, 100))
self.ui_elements.append(self.stats_text)
# Instructions
instructions = [
"How to use:",
"1. Click 'Find Images' to discover images",
"2. Adjust quality slider for size/quality tradeoff",
"3. Select an image from the dropdown",
"4. Click 'Convert Selected Image' or 'Convert All Images'",
"5. Click 'View Converted Images' to see results",
"6. Use arrow keys to cycle through converted images",
"",
"Quality: 1.0 = original, 0.5 = half size, 0.25 = quarter size",
"Supported formats: PNG, JPG, BMP, TGA, TIF"
]
for i, instruction in enumerate(instructions):
text = TextLabel(20, 500 + i * 25, instruction, 16, (200, 200, 255))
self.ui_elements.append(text)
def _on_quality_changed(self, value):
"""Handle quality slider change"""
self.quality = value
self.quality_value.set_text(f"{value:.1f}")
def _find_images(self):
"""Find images in project directory"""
self.status_message = "Searching for images..."
self.status_text.set_text(self.status_message)
project_root = Path(__file__).parent.parent
self.available_images = self._find_image_files(project_root)
if not self.available_images:
self.status_message = "No images found! Add image files to project."
self.status_text.set_text(self.status_message)
else:
self.status_message = f"Found {len(self.available_images)} images"
self.status_text.set_text(self.status_message)
# Remove existing dropdown if present
if hasattr(self, 'image_dropdown'):
self.ui_elements.remove(self.image_dropdown)
# Create dropdown with unique image names
image_names = [img.stem for img in self.available_images]
self.image_dropdown = Dropdown(20, 250, 300, 30, image_names)
self.ui_elements.append(self.image_dropdown)
# Add Convert Selected button if not present
if not hasattr(self, 'convert_btn') or self.convert_btn not in self.ui_elements:
self.convert_btn = Button(20, 290, 200, 40, "Convert Selected Image")
self.convert_btn.set_on_click(self._convert_selected)
self.ui_elements.append(self.convert_btn)
def _convert_selected(self):
"""Convert the currently selected image"""
if self.available_images and hasattr(self, 'image_dropdown'):
selected_index = self.image_dropdown.selected_index
if selected_index < len(self.available_images):
image_path = self.available_images[selected_index]
converted = self._convert_image_file(image_path, self.quality)
if converted:
self.converted_images.append(converted)
self.status_message = f"Converted: {image_path.name} (Quality: {self.quality})"
self.status_text.set_text(self.status_message)
def _convert_all_images(self):
"""Convert all found images"""
if not self.available_images:
self.status_message = "No images to convert! Click 'Find Images' first."
self.status_text.set_text(self.status_message)
return
self.status_message = f"Converting all images (Quality: {self.quality})..."
self.status_text.set_text(self.status_message)
converted_count = 0
for image_file in self.available_images[:8]: # Limit to 8 images
converted = self._convert_image_file(image_file, self.quality)
if converted:
self.converted_images.append(converted)
converted_count += 1
self.status_message = f"Converted {converted_count} images (Quality: {self.quality})"
self.status_text.set_text(self.status_message)
def _view_converted_images(self):
"""Show converted images dropdown"""
if not self.converted_images:
self.status_message = "No converted images! Convert some images first."
self.status_text.set_text(self.status_message)
return
# Remove existing dropdown if present
if hasattr(self, 'converted_dropdown'):
self.ui_elements.remove(self.converted_dropdown)
# Create dropdown for converted images
converted_names = [f"{img['name']} ({img['image'].width}x{img['image'].height})"
for img in self.converted_images]
self.converted_dropdown = Dropdown(20, 340, 300, 30, converted_names)
self.converted_dropdown.set_on_selection_changed(
lambda i, n: setattr(self, 'selected_image_index', i)
)
self.ui_elements.append(self.converted_dropdown)
self.status_message = f"Viewing {len(self.converted_images)} converted images"
self.status_text.set_text(self.status_message)
def _find_image_files(self, directory: Path, max_depth: int = 3) -> list:
"""Find all image files using Pygame-supported formats"""
image_files = []
supported_extensions = {'.png', '.jpg', '.jpeg', '.bmp', '.tga', '.tiff'}
def search_path(current_path: Path, depth: int):
if depth > max_depth:
return
try:
for item in current_path.iterdir():
if item.is_file() and item.suffix.lower() in supported_extensions:
if item.stat().st_size < 10 * 1024 * 1024: # Skip files >10MB
image_files.append(item)
elif item.is_dir() and not item.name.startswith('.'):
search_path(item, depth + 1)
except (PermissionError, OSError):
pass
search_path(directory, 0)
return image_files
def _convert_image_file(self, image_path: Path, quality: float = 1.0) -> dict:
"""Convert an image file to embedded format with quality setting"""
try:
python_code = ImageConverter.image_to_python_code(
str(image_path),
output_var_name=f"embedded_{image_path.stem}",
max_size=(256, 256), # Max size constraint
method='compressed',
quality=quality
)
# Execute the code to get image data
namespace = {}
exec(python_code, namespace)
image_data = namespace.get(f"embedded_{image_path.stem}")
if not image_data:
return None
embedded_image = EmbeddedImage(image_data)
# Save the converted code
output_file = image_path.parent / f"{image_path.stem}_embedded.py"
with open(output_file, 'w', encoding='utf-8') as f:
header = [
'# Auto-generated by LunaEngine Image Conversion Tool',
f'# Source: {image_path.name}',
f'# Quality: {quality}',
'# Converted with: Pygame + base64 + zlib compression',
''
]
f.write('\n'.join(header) + python_code)
return {
'name': image_path.stem,
'path': image_path,
'image': embedded_image,
'output_file': output_file,
'quality': quality
}
except Exception:
return None
def _update_stats(self):
"""Update statistics display"""
found_count = len(self.available_images)
converted_count = len(self.converted_images)
self.stats_text.set_text(f"Images found: {found_count} | Converted: {converted_count}")
def update(self, dt):
self.animation_time += dt
for element in self.ui_elements:
element.update(dt)
self._update_stats()
def render(self, renderer):
# Draw background
renderer.draw_rect(0, 0, 800, 600, (30, 30, 50))
renderer.draw_rect(0, 0, 800, 60, (50, 50, 80))
# Draw converted images in dedicated area
if self.converted_images:
current_image = self.converted_images[self.selected_image_index]
x, y = 450, 150 # Fixed position away from UI
# Draw with subtle animation
offset_y = math.sin(self.animation_time * 2) * 5
current_image['image'].draw(renderer, int(x), int(y + offset_y))
# Draw border and info
renderer.draw_rect(int(x), int(y + offset_y),
current_image['image'].width,
current_image['image'].height,
(255, 255, 255), fill=False)
# Draw image info
font = pygame.font.Font(None, 20)
info_text = f"{current_image['name']} - {current_image['image'].width}x{current_image['image'].height}"
text_surface = font.render(info_text, True, (255, 255, 255))
renderer.draw_surface(text_surface, x, y + current_image['image'].height + 15)
# Draw quality info
quality_text = f"Quality: {current_image.get('quality', 1.0):.1f}"
quality_surface = font.render(quality_text, True, (200, 255, 200))
renderer.draw_surface(quality_surface, x, y + current_image['image'].height + 35)
def main():
engine = LunaEngine("Image Conversion Demo", 800, 600)
@engine.on_event(pygame.KEYDOWN)
def handle_key_event(event):
if engine.current_scene and hasattr(engine.current_scene, 'converted_images'):
scene = engine.current_scene
if scene.converted_images:
if event.key == pygame.K_RIGHT:
scene.selected_image_index = (scene.selected_image_index + 1) % len(scene.converted_images)
elif event.key == pygame.K_LEFT:
scene.selected_image_index = (scene.selected_image_index - 1) % len(scene.converted_images)
engine.add_scene("main", ImageConversionDemoScene)
engine.set_scene("main")
engine.run()
if __name__ == "__main__":
main()