# modules/art_tools.py import os import math import numpy as np from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageOps import gradio as gr import io # --- ADOPTABLE GRID MAKER --- def hex_to_rgb(hex_color): """Hex color kodunu (ör: #FF0000) (255, 0, 0) tuple'ına çevirir.""" hex_color = hex_color.lstrip('#') return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) + (255,) # Alpha ekle def create_grid_collage(files, columns, add_labels, label_type, start_number, bg_color_hex, title_text, banner_color_hex, title_color_hex): """ Adoptable Style Grid Collage Maker """ if not files: return None, "⚠️ Lütfen resim dosyalarını seçin." try: images = [] for file_obj in files: try: img = Image.open(file_obj.name).convert("RGBA") images.append(img) except: continue if not images: return None, "⚠️ Geçerli resim bulunamadı." count = len(images) cols = int(columns) rows = math.ceil(count / cols) # 1. Boyut Standardizasyonu (İlk resme göre) base_w, base_h = images[0].size resized_images = [] for img in images: if img.size != (base_w, base_h): resized_images.append(img.resize((base_w, base_h), Image.Resampling.LANCZOS)) else: resized_images.append(img) # Tasarım Parametreleri padding = 40 # Resimler arası boşluk biraz arttı frame_width = 15 # Çerçeve kalınlığı arttı # Renkler bg_color = hex_to_rgb(bg_color_hex) banner_color = hex_to_rgb(banner_color_hex) title_color = hex_to_rgb(title_color_hex) frame_color = title_color # Çerçeve rengi başlık rengiyle aynı olsun # Canvas Boyutu (Önce hesapla) grid_w = (cols * base_w) + ((cols + 1) * padding) # Header Yüksekliği: Grid genişliğine orantılı olsun (Genişlik arttıkça yükseklik de artsın ki text büyüyebilsin) # Min 250px, yoksa genişliğin %15'i header_height = max(250, int(grid_w * 0.15)) grid_h = header_height + (rows * base_h) + ((rows + 1) * padding) grid_img = Image.new("RGBA", (grid_w, grid_h), bg_color) draw = ImageDraw.Draw(grid_img) # --- HEADER (BANNER) ÇİZİMİ --- draw.rectangle([(0, 0), (grid_w, header_height)], fill=banner_color) draw.rectangle([(0, header_height-15), (grid_w, header_height)], fill=frame_color) # Başlangıç Font Boyutu (Çok daha büyük başlıyoruz - Grid genişliğine orantılı) font_title_size = int(grid_w * 0.1) # Genişliğin %10'u ile başla # --- BAŞLIK FONTU BÜYÜKLÜĞÜNÜ AYARLA (Agresif Scaling) --- target_width = grid_w * 0.95 # Banner genişliğinin %95'ini doldurmalı max_height_text = header_height * 0.8 # Yüksekliğin %80'ini geçmesin (Hava kalsın) try: font_path = "timesbd.ttf" font_title = ImageFont.truetype(font_path, font_title_size) except: font_path = None font_title = ImageFont.load_default() if font_path: # İterasyonla en uygun boyutu bul for _ in range(50): # Sonsuz döngüden kaçınmak için limitli bbox_title = draw.textbbox((0, 0), title_text, font=font_title) w_total = (bbox_title[2] - bbox_title[0]) h_max = bbox_title[3] - bbox_title[1] # Çok büyükse küçült if w_total > target_width or h_max > max_height_text: font_title_size -= 5 if font_title_size < 20: break font_title = ImageFont.truetype(font_path, font_title_size) # Çok küçükse büyüt (Eğer dikeyde ve yatayda yer varsa) elif w_total < target_width * 0.9 and h_max < max_height_text * 0.9: font_title_size += 5 font_title = ImageFont.truetype(font_path, font_title_size) else: break # Tamamdır # Son Ölçümler & Çizim bbox_title = draw.textbbox((0, 0), title_text, font=font_title) w_title = bbox_title[2] - bbox_title[0] h_title = bbox_title[3] - bbox_title[1] start_x_text = (grid_w - w_title) // 2 bg_center_y = header_height // 2 # Başlık çiz draw.text((start_x_text, bg_center_y - h_title//2), title_text, font=font_title, fill=title_color, stroke_width=int(font_title_size*0.02), stroke_fill="black") # --- RESİMLERİ YERLEŞTİR --- # Numara Fontunu Resim Boyutuna Göre Ayarla (Çok Büyük) try: num_font_size = int(base_w * 0.25) # Resim genişliğinin %25'i kadar (Kocaman) font_num = ImageFont.truetype("impact.ttf", num_font_size) except: font_num = ImageFont.load_default() current_num = int(start_number) for i, img in enumerate(resized_images): r = i // cols c = i % cols x = padding + (c * (base_w + padding)) y = header_height + padding + (r * (base_h + padding)) # Kalın Çerçeve draw.rectangle( [(x - frame_width, y - frame_width), (x + base_w + frame_width, y + base_h + frame_width)], fill=None, outline=frame_color, width=frame_width ) # Resim grid_img.paste(img, (x, y), mask=img) # --- OVERLAY NUMARA (DEVASA) --- if add_labels: if str(label_type).startswith("Sadece"): num_str = f"{current_num}" else: num_str = f"#{current_num}" current_num += 1 bbox_num = draw.textbbox((0, 0), num_str, font=font_num) w_num = bbox_num[2] - bbox_num[0] h_num = bbox_num[3] - bbox_num[1] # Ortala ve Biraz daha yukarı al (Resmin altından %10 yukarıda) num_x = x + (base_w - w_num) // 2 num_y = y + base_h - h_num - int(base_h * 0.05) - 20 # Çok Kalın Outline outline_width = int(num_font_size * 0.08) # Font boyutuna göre outline kalınlığı draw.text((num_x, num_y), num_str, font=font_num, fill="white", stroke_width=outline_width, stroke_fill="black") return grid_img, "✅ Adoptable Grid oluşturuldu!" except Exception as e: import traceback traceback.print_exc() return None, f"❌ Hata: {str(e)}"