Spaces:
Paused
Paused
| from PIL import Image, ImageDraw, ImageFont | |
| config = { | |
| "ids": ["falling_plain"], | |
| "name": "Falling Plain", | |
| "panels": ["color", "font", "size", "position"], | |
| "default_font": "vazir" | |
| } | |
| frontend_template = "" | |
| def draw_frame(draw, img, width, height, style_config, lines, line_metrics, active_idx, font, color_parser, word_infos=None): | |
| is_rtl = getattr(style_config, 'text_direction', 'rtl') != 'ltr' | |
| # رنگ متن پایه (خاموش) | |
| custom_text = style_config.styleColors.get('falling_plain', '#FFFFFF') | |
| base_text_color = color_parser(custom_text, (255, 255, 255, 255)) | |
| # رنگ کلمه فعال (روشن) | |
| active_hex = style_config.styleActiveColors.get('falling_plain', '#FFEB3B') | |
| active_text_color = color_parser(active_hex, (255, 235, 59, 255)) | |
| line_height_px = int(style_config.fontSize * 1.5) | |
| total_block_height = len(lines) * line_height_px | |
| bottom_reference = height - style_config.marginV | |
| start_y_of_block = bottom_reference - total_block_height | |
| space_w = draw.textlength(" ", font=font) | |
| try: | |
| ref_bbox = draw.textbbox((0, 0), "سلام", font=font, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) | |
| standard_text_visual_center_y = (ref_bbox[1] + ref_bbox[3]) / 2.0 | |
| standard_text_offset_y = ref_bbox[1] | |
| standard_text_h = ref_bbox[3] - ref_bbox[1] | |
| except: | |
| standard_text_visual_center_y = style_config.fontSize / 2.0 | |
| standard_text_offset_y = 0 | |
| standard_text_h = style_config.fontSize | |
| words_to_draw = [] | |
| global_word_counter = 0 | |
| temp_y = start_y_of_block | |
| # محاسبه موقعیت هندسی تکتک کلمات در تصویر | |
| for line_idx, metrics in enumerate(line_metrics): | |
| line_start_idx = sum(len(m["words"]) for m in line_metrics[:line_idx]) | |
| cursor_x = (width + metrics["width"]) / 2 + (style_config.x or 0) if is_rtl else (width - metrics["width"]) / 2 + (style_config.x or 0) | |
| line_center_y = temp_y + (line_height_px / 2.0) | |
| for w_i, word in enumerate(metrics["words"]): | |
| g_idx = line_start_idx + w_i | |
| w_len = metrics["word_widths"][w_i] | |
| text_y_pos = line_center_y - standard_text_visual_center_y | |
| word_x = (cursor_x - w_len) if is_rtl else cursor_x | |
| words_to_draw.append({ | |
| "text": word, "x": word_x, "y": text_y_pos, "width": w_len, | |
| "global_idx": global_word_counter | |
| }) | |
| cursor_x += (-(w_len + space_w) if is_rtl else (w_len + space_w)) | |
| global_word_counter += 1 | |
| temp_y += line_height_px | |
| VERTICAL_CORRECTION = int(style_config.fontSize * 0.1) | |
| current_render_time = getattr(style_config, 'current_render_time', 0.0) | |
| # رسم کلمات با انیمیشن فرود از ارتفاع -45px | |
| for item in words_to_draw: | |
| should_draw = False | |
| y_offset = 0 | |
| alpha_factor = 1.0 | |
| is_active = False | |
| if word_infos and item["global_idx"] < len(word_infos): | |
| w_info = word_infos[item["global_idx"]] | |
| if current_render_time >= w_info.start: | |
| should_draw = True | |
| is_active = current_render_time < w_info.end | |
| # اعمال انیمیشن سقوط برای کلمه فعال | |
| if is_active: | |
| time_passed = current_render_time - w_info.start | |
| anim_dur = 0.25 | |
| if time_passed < anim_dur: | |
| progress = time_passed / anim_dur | |
| ease = 1 - (1 - progress) * (1 - progress) | |
| y_offset = -45 * (1 - ease) | |
| alpha_factor = ease | |
| else: | |
| y_offset = 0 | |
| alpha_factor = 1.0 | |
| else: | |
| y_offset = 0 | |
| alpha_factor = 1.0 | |
| else: | |
| # کلمات جلو (آینده) به هیچ عنوان ترسیم نمیشوند | |
| should_draw = False | |
| elif active_idx != -1: | |
| if item["global_idx"] <= active_idx: | |
| should_draw = True | |
| is_active = (item["global_idx"] == active_idx) | |
| else: | |
| # کلمات آینده در حالت بدون فریمبهفریم هم ترسیم نمیشوند | |
| should_draw = False | |
| if should_draw: | |
| # اعمال کمرنگ شدن کلمات قبل (گذشته) در صورت فعال بودن دکمه | |
| if not is_active and word_infos and item["global_idx"] < len(word_infos): | |
| w_info = word_infos[item["global_idx"]] | |
| if current_render_time >= w_info.end and getattr(style_config, 'fadeSurrounding', False): | |
| alpha_factor = 0.35 | |
| fill_color = active_text_color if (is_active and getattr(style_config, 'useActiveColor', True)) else base_text_color | |
| if word_infos and item["global_idx"] < len(word_infos): | |
| w_obj = word_infos[item["global_idx"]] | |
| if hasattr(w_obj, 'color') and w_obj.color: | |
| fill_color = color_parser(w_obj.color, fill_color) | |
| final_alpha = int(fill_color[3] * alpha_factor) | |
| final_color = (fill_color[0], fill_color[1], fill_color[2], final_alpha) | |
| final_text_y = item["y"] + VERTICAL_CORRECTION | |
| draw.text((item["x"], final_text_y + y_offset), item["text"], font=font, fill=final_color, direction=getattr(style_config, 'text_direction', 'rtl'), language=getattr(style_config, 'lang_code', 'fa')) |