Add JAX typography, xector primitives, deferred effect chains, and GPU streaming
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m28s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m28s
- Add JAX text rendering with font atlas, styled text placement, and typography primitives - Add xector (element-wise/reduction) operations library and sexp effects - Add deferred effect chain fusion for JIT-compiled effect pipelines - Expand drawing primitives with font management, alignment, shadow, and outline - Add interpreter support for function-style define and require - Add GPU persistence mode and hardware decode support to streaming - Add new sexp effects: cell_pattern, halftone, mosaic, and derived definitions - Add path registry for asset resolution - Add integration, primitives, and xector tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
183
test_pil_options.py
Normal file
183
test_pil_options.py
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Explore PIL text options and test if we can match them.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
def load_font(font_name=None, font_size=32):
|
||||
"""Load a font."""
|
||||
candidates = [
|
||||
font_name,
|
||||
'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
|
||||
'/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
|
||||
'/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf',
|
||||
]
|
||||
for path in candidates:
|
||||
if path is None:
|
||||
continue
|
||||
try:
|
||||
return ImageFont.truetype(path, font_size)
|
||||
except (IOError, OSError):
|
||||
continue
|
||||
return ImageFont.load_default()
|
||||
|
||||
|
||||
def test_pil_options():
|
||||
"""Test various PIL text options."""
|
||||
|
||||
# Create a test frame
|
||||
frame_size = (600, 400)
|
||||
|
||||
font = load_font(None, 36)
|
||||
font_bold = load_font('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 36)
|
||||
font_italic = load_font('/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf', 36)
|
||||
|
||||
tests = []
|
||||
|
||||
# Test 1: Basic text
|
||||
def basic_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Basic Text", fill=(255, 255, 255, 255), font=font)
|
||||
return img, "basic"
|
||||
tests.append(basic_text)
|
||||
|
||||
# Test 2: Stroke/outline
|
||||
def stroke_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Stroke Text", fill=(255, 255, 255, 255), font=font,
|
||||
stroke_width=2, stroke_fill=(255, 0, 0, 255))
|
||||
return img, "stroke"
|
||||
tests.append(stroke_text)
|
||||
|
||||
# Test 3: Bold font
|
||||
def bold_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Bold Text", fill=(255, 255, 255, 255), font=font_bold)
|
||||
return img, "bold"
|
||||
tests.append(bold_text)
|
||||
|
||||
# Test 4: Italic font
|
||||
def italic_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Italic Text", fill=(255, 255, 255, 255), font=font_italic)
|
||||
return img, "italic"
|
||||
tests.append(italic_text)
|
||||
|
||||
# Test 5: Different anchors
|
||||
def anchor_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
# Draw crosshairs at anchor points
|
||||
for x in [100, 300, 500]:
|
||||
draw.line([(x-10, 50), (x+10, 50)], fill=(100, 100, 100, 255))
|
||||
draw.line([(x, 40), (x, 60)], fill=(100, 100, 100, 255))
|
||||
|
||||
draw.text((100, 50), "Left", fill=(255, 255, 255, 255), font=font, anchor="lm")
|
||||
draw.text((300, 50), "Center", fill=(255, 255, 255, 255), font=font, anchor="mm")
|
||||
draw.text((500, 50), "Right", fill=(255, 255, 255, 255), font=font, anchor="rm")
|
||||
return img, "anchor"
|
||||
tests.append(anchor_text)
|
||||
|
||||
# Test 6: Multiline text
|
||||
def multiline_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.multiline_text((20, 20), "Line One\nLine Two\nLine Three",
|
||||
fill=(255, 255, 255, 255), font=font, spacing=10)
|
||||
return img, "multiline"
|
||||
tests.append(multiline_text)
|
||||
|
||||
# Test 7: Semi-transparent text
|
||||
def alpha_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Alpha 100%", fill=(255, 255, 255, 255), font=font)
|
||||
draw.text((20, 60), "Alpha 50%", fill=(255, 255, 255, 128), font=font)
|
||||
draw.text((20, 100), "Alpha 25%", fill=(255, 255, 255, 64), font=font)
|
||||
return img, "alpha"
|
||||
tests.append(alpha_text)
|
||||
|
||||
# Test 8: Colored text
|
||||
def colored_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Red", fill=(255, 0, 0, 255), font=font)
|
||||
draw.text((20, 60), "Green", fill=(0, 255, 0, 255), font=font)
|
||||
draw.text((20, 100), "Blue", fill=(0, 0, 255, 255), font=font)
|
||||
draw.text((20, 140), "Yellow", fill=(255, 255, 0, 255), font=font)
|
||||
return img, "colored"
|
||||
tests.append(colored_text)
|
||||
|
||||
# Test 9: Large stroke
|
||||
def large_stroke():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((20, 20), "Big Stroke", fill=(255, 255, 255, 255), font=font,
|
||||
stroke_width=5, stroke_fill=(0, 0, 0, 255))
|
||||
return img, "large_stroke"
|
||||
tests.append(large_stroke)
|
||||
|
||||
# Test 10: Emoji (if supported)
|
||||
def emoji_text():
|
||||
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
try:
|
||||
# Try to find an emoji font
|
||||
emoji_font = None
|
||||
emoji_paths = [
|
||||
'/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf',
|
||||
'/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf',
|
||||
]
|
||||
for p in emoji_paths:
|
||||
try:
|
||||
emoji_font = ImageFont.truetype(p, 36)
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if emoji_font:
|
||||
draw.text((20, 20), "Hello 🎵 World 🎸", fill=(255, 255, 255, 255), font=emoji_font)
|
||||
else:
|
||||
draw.text((20, 20), "No emoji font found", fill=(255, 255, 255, 255), font=font)
|
||||
except Exception as e:
|
||||
draw.text((20, 20), f"Emoji error: {e}", fill=(255, 255, 255, 255), font=font)
|
||||
return img, "emoji"
|
||||
tests.append(emoji_text)
|
||||
|
||||
# Run all tests
|
||||
print("PIL Text Options Test")
|
||||
print("=" * 60)
|
||||
|
||||
for test_fn in tests:
|
||||
img, name = test_fn()
|
||||
fname = f"/tmp/pil_test_{name}.png"
|
||||
img.save(fname)
|
||||
print(f"Saved: {fname}")
|
||||
|
||||
print("\nCheck /tmp/pil_test_*.png for results")
|
||||
|
||||
# Print available parameters
|
||||
print("\n" + "=" * 60)
|
||||
print("PIL draw.text() parameters:")
|
||||
print(" - xy: position tuple")
|
||||
print(" - text: string to draw")
|
||||
print(" - fill: color (R,G,B) or (R,G,B,A)")
|
||||
print(" - font: ImageFont object")
|
||||
print(" - anchor: 2-char code (la=left-ascender, mm=middle-middle, etc.)")
|
||||
print(" - spacing: line spacing for multiline")
|
||||
print(" - align: 'left', 'center', 'right' for multiline")
|
||||
print(" - direction: 'rtl', 'ltr', 'ttb' (requires libraqm)")
|
||||
print(" - features: OpenType features list")
|
||||
print(" - language: language code for shaping")
|
||||
print(" - stroke_width: outline width in pixels")
|
||||
print(" - stroke_fill: outline color")
|
||||
print(" - embedded_color: use embedded color glyphs (emoji)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_pil_options()
|
||||
Reference in New Issue
Block a user