Metadata-Version: 2.4
Name: rawstage
Version: 0.2.0
Summary: Raw-style comic stage renderer — parses ComicDirector ML XML and outputs video
Author-email: liZeXUA <flffake@lizexua.com>
License-Expression: GPL-3.0-only
Project-URL: Repository, https://github.com/liZeXUA/rawstage
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Video
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Pillow>=10.0
Requires-Dist: lxml>=5.0
Requires-Dist: tqdm>=4.0
Dynamic: license-file

# RawStage

ComicDirector ML v1.0 rendering engine. Parses XML performance instructions and outputs MP4 video by compositing PNG character sprites and facilities in a layered depth system.

## Install

```bash
pip install -e .
```

Requires `ffmpeg` on PATH for video encoding.

## Quick start

```bash
# Render a single frame at a given time
rawstage frame scene.xml --assets ./assets/ --time 3.0 --output frame.png

# Render a full PNG frame sequence for a scene
rawstage animate scene.xml --assets ./assets/ --fps 24 --output frames/
rawstage animate scene.xml --assets ./assets/ --fps 24 --output frames/ --progress

# Render a complete script to MP4
rawstage render script.xml --assets ./assets/ --output out.mp4 --fps 24
rawstage render script.xml --assets ./assets/ --output out.mp4 --fps 24 --progress
```

## Asset types

| Asset | XML tag | Description |
|-------|---------|-------------|
| Character | `<character>` | Animated character with enter/exit/move/expression support |
| Facility | `<facility>` | Static prop or background element (trees, platforms, sky, etc.) |
| Expression | `<expression>` | Alternate sprite for a character (bound via `character` attr) |
| Audio | `<audio>` | Music or sound effect file |

All assets declare `layer` (rendering group, sorted alphabetically) and `z` (depth 0-200 within layer, higher = closer). Backgrounds are just facilities with `layer="background"`.

## Layer & depth model

```
Layer "background"     z=0   sky, mountains, clouds ── farthest
Layer "background"     z=50  distant buildings
Layer "platform"       z=100 characters standing here
Layer "platform"       z=110 another character
Layer "front"          z=180 foreground tree ── closest
Subtitle                             ── always topmost (screen-space)
```

Layers render in alphabetical order. Within each layer, elements sort by `z` ascending (lower z drawn first = farther from viewer).

## Supported XML events

| Event | Attributes |
|-------|-----------|
| `enter` | character, method (fade_in/slide_*/pop_in), target_x, target_y, start, duration |
| `exit` | character, method (fade_out/slide_*), start, duration |
| `move` | character, to_x, to_y, path (waypoints), easing, start, duration |
| `camera` | target (character), center_x, center_y, scale, easing, start, duration |
| `dialogue` | character, text, start, duration, font, font_size, color, outline_width, outline_color, `<span>` children |
| `expression` | character, set (expression asset id), start |
| `audio` | ref, action (play/play_once), loop, volume, start |
| `transition` | type (fade/dissolve/wipe_left/wipe_right), duration |

### Subtitle styling

`<dialogue>` supports two forms:

**Plain text** (backward compatible) — all text in a single `text` attribute:

```xml
<dialogue character="alice" start="4.5" duration="2.5"
          text="你好，今天天气真好！" />
```

**Rich text** — use `<span>` children for mixed styles within one subtitle line:

```xml
<dialogue character="alice" start="3.0" duration="3.0"
          font_size="48" color="#FFFFFF"
          outline_width="2" outline_color="#333333">
  <span color="#00FF00" italic="true">绿色斜体，</span>
  <span color="#FF0000" underline="true" bold="true">红色粗体下划线</span>
</dialogue>
```

Dialogue-level attributes serve as defaults for all spans:

| Attribute | Default | Description |
|-----------|---------|-------------|
| `font` | (system CJK font) | Font file path |
| `font_size` | 40 | Default font size in pixels |
| `color` | `#FFFFFF` | Default text color (hex RGB / RGBA) |
| `outline_width` | 3 | Outline thickness in pixels |
| `outline_color` | `#000000` | Outline color (hex RGB / RGBA) |

Each `<span>` can override:

| Attribute | Description |
|-----------|-------------|
| `color` | Text color (hex, e.g. `#FF0000`) |
| `size` | Font size override |
| `italic` | `"true"` / `"false"` |
| `underline` | `"true"` / `"false"` |
| `bold` | `"true"` / `"false"` |
| `font` | Font file path override |

Spans are concatenated horizontally with baseline alignment. If no `<span>` children are present, the `text` attribute is rendered as a single span using dialogue-level defaults.

## Key design decisions

- **1920x1080 canvas** with unified coordinate system. Character anchor: bottom-center.
- **Absolute timeline** per scene (starts at 0s). All events use `start` (seconds) + `duration`.
- **Layer + z-index** rendering: layers sorted alphabetically, z 0-200 within layer.
- **Facilities** are static props/backgrounds placed per scene via `<initial_facilities>`.
- **Camera**: viewport defined by `center_x, center_y` + `scale` (1.0 = full canvas).
- **Quadratic easing** — half the multiplies of cubic, negligible visual difference.
- **Write-then-encode** — never holds more than one frame in memory.
- **No numpy** — Pillow + math covers all needs.

## Running tests

```bash
pytest tests/ -v
```
