feat(avatar): add SVG, PNG and HDR avatar exports
This commit is contained in:
@@ -1,10 +1,145 @@
|
||||
Generate avatar
|
||||
===
|
||||
This repo is a collection of codes that generate avatars.
|
||||
# Matrix avatar
|
||||
|
||||
# "The Matrix"-like avatar
|
||||
`cmatrix_avatar.py` generating avatars imitate "The Matrix".
|
||||
`cmatrix_avatar.py` generates a square avatar directly, with the original
|
||||
green character rain, yellow G/W/B highlights, and blue block-letter GWB.
|
||||
It writes both an outlined SVG and a PNG; terminal screenshots are no longer needed.
|
||||
|
||||
## Run
|
||||
|
||||
Create the environment in this directory with [uv](https://docs.astral.sh/uv/):
|
||||
|
||||
```shell
|
||||
python cmatrix_avatar.py
|
||||
```
|
||||
uv venv
|
||||
uv sync
|
||||
uv run python cmatrix_avatar.py
|
||||
```
|
||||
|
||||
The default outputs are `avatar.svg` and `avatar.png` (2048 × 2048).
|
||||
The default background grid is 43 rows × 77 columns. The original six-line GWB
|
||||
is rendered as a separate vector layer, uniformly scaled to 72% of the image
|
||||
width (roughly 2.1 times the background cell size) and centered.
|
||||
The complete background grid fits inside the square with a safety margin.
|
||||
Whole background cells intersecting the central logo area are left empty;
|
||||
all remaining cells are preserved without cropping. The reserved area includes
|
||||
a quarter-cell gap, and actual glyph bounds are checked to avoid sliced letters.
|
||||
Colors are fixed, independent of terminal themes.
|
||||
Rain brightness uses 32 nonlinear levels, from 0.5% to 100% of its peak.
|
||||
Green and yellow characters share the logo's peak luminance. The SDR logo stays
|
||||
exactly its original blue (#3b73fa); rain peaks are reduced to its luminance.
|
||||
HDR scales linear RGB without adding white, preserving the original chromaticity.
|
||||
The stronger gradient remains; this is not a pixel-identical restoration of the
|
||||
older rain palette. Shape, size, and placement are unchanged.
|
||||
|
||||
```shell
|
||||
# Reproduce a background and also generate small previews.
|
||||
uv run python cmatrix_avatar.py --seed 42 --previews
|
||||
|
||||
# Also write a true 16-bit HDR PNG with a shared 400-nit luminance peak.
|
||||
uv run python cmatrix_avatar.py --seed 42 --hdr --peak-nits 400
|
||||
|
||||
# Customize the output, resolution, and complete grid dimensions.
|
||||
uv run python cmatrix_avatar.py --output portraits/gwb.png --size 2048 --rows 43 --columns 77 --logo-width 0.72
|
||||
|
||||
# Use the original grid dimensions while retaining the independently sized logo.
|
||||
uv run python cmatrix_avatar.py --rows 28 --columns 67
|
||||
|
||||
# Select a different monospace font.
|
||||
uv run python cmatrix_avatar.py --font C:/Windows/Fonts/consola.ttf
|
||||
|
||||
```
|
||||
|
||||
Each run prints its random seed. Use that seed and the same font to reproduce
|
||||
the image with the same row/column settings. Without `--seed`, each run generates a new background. Existing
|
||||
outputs at the selected path are replaced. `--output` accepts a basename, a
|
||||
`.png` path, or a `.svg` path; both formats are always generated.
|
||||
|
||||
`--previews` adds `avatar-128.png` and `avatar-64.png` (using the chosen output
|
||||
basename). Use the full-size PNG for a WeChat avatar and keep the SVG as the
|
||||
scalable original.
|
||||
|
||||
## HDR output
|
||||
|
||||
`--hdr` adds `avatar-hdr.png`, a 16-bit RGB PNG encoded with BT.2020 primaries
|
||||
and the ST 2084 (PQ) transfer function. PNG `cICP` identifies that encoding;
|
||||
`cLLI` records the measured maximum and average luminance of the encoded image.
|
||||
Rendering and antialiasing are composited in linear light before PQ encoding.
|
||||
This is an HDR signal, rather than an SDR image with a brightness filter.
|
||||
See the [PNG specification](https://www.w3.org/TR/png-3/).
|
||||
|
||||
`--peak-nits` accepts 100–2000 (default 400). This is the target luminance Y
|
||||
of fully covered peak characters and GWB, not the maximum individual RGB
|
||||
channel value. At the default setting, rain levels span 2–400 nits, with a
|
||||
0.02-nit background. Quantization can introduce very small differences.
|
||||
|
||||
Actual HDR appearance requires an HDR display, enabled HDR output, and a viewer
|
||||
that understands PQ PNG metadata. An ordinary chat preview cannot demonstrate
|
||||
the encoded peak brightness. The regular PNG and SVG remain SDR alternatives;
|
||||
use the regular PNG for a WeChat avatar rather than relying on HDR preservation.
|
||||
The PQ PNG does not contain an SDR fallback: do not upload it expecting an
|
||||
unsupported viewer to reproduce the regular PNG. Display tone mapping and color
|
||||
volume limits can also alter the HDR appearance despite preserved encoded chromaticity.
|
||||
|
||||
### WeChat compatibility research (2026-10-18)
|
||||
|
||||
No explicit official guarantee for HDR preservation in personal profile pictures
|
||||
was found. Tencent Cloud's [HDR processing documentation](https://intl.cloud.tencent.com/zh/document/product/1045/74729)
|
||||
describes a separate cloud service, not a guarantee for WeChat avatars.
|
||||
[Ultra HDR JPEG](https://developer.android.com/media/platform/hdr-image-format)
|
||||
can carry an SDR base and an HDR gain map, enabling SDR fallback in legacy
|
||||
viewers. That would be preferable to PQ-only PNG for a single adaptive file,
|
||||
but WeChat's upload/crop/re-encode pipeline still needs device testing to verify
|
||||
gain-map preservation. Use avatar.png for dependable SDR source colors;
|
||||
no promise is made about pixel-identical output after WeChat processing.
|
||||
|
||||
To package the current matching PNG/SVG pair as an adaptive Ultra HDR JPEG:
|
||||
|
||||
```shell
|
||||
uv run python avatar_ultrahdr.py
|
||||
```
|
||||
|
||||
This writes `avatar-ultrahdr.jpg` using pyultrahdr: a quality-100, 4:4:4 SDR
|
||||
JPEG base plus a full-resolution grayscale gain map and XMP/MPF metadata.
|
||||
The base comes directly from avatar.png, with no tone mapping or palette change.
|
||||
JPEG compression introduces small pixel differences. The map targets 400-nit
|
||||
character/logo peaks while preserving the base chromaticity; shadows never
|
||||
become darker than the SDR base. This differs intentionally from the PQ PNG's
|
||||
black level. The script reports ordinary-decoding error and checks both images
|
||||
are present. Actual HDR display and WeChat gain-map survival require device testing.
|
||||
|
||||
## Rendering
|
||||
|
||||
OCR A Extended (`C:/Windows/Fonts/OCRAEXT.TTF`) is preferred on Windows:
|
||||
its distinctive diamond-shaped O, D, S, and W match the reference screenshot.
|
||||
The screenshot's historical terminal settings are unavailable; this identification
|
||||
is based on comparing glyph shapes, not the current Windows Terminal setting.
|
||||
See [Microsoft's font entry](https://learn.microsoft.com/en-us/typography/font-list/ocr-a-extended).
|
||||
|
||||
OCR A Extended has no block/box glyphs, so GWB uses Consolas as a fallback,
|
||||
with the original character proportions. This preserves the logo design; the screenshot alone
|
||||
does not establish its original fallback font. `--logo-font` can override it.
|
||||
No proprietary font files are copied into the project.
|
||||
|
||||
If OCR A Extended is unavailable, the defaults fall back to Consolas, Cascadia
|
||||
Mono, DejaVu Sans Mono (Linux), or Menlo (macOS). `--font` selects the background
|
||||
font explicitly. A font that includes the logo glyphs is also used for GWB
|
||||
unless `--logo-font` overrides it.
|
||||
Cells use a height/width ratio of 1.8 to match the reference terminal; each font's
|
||||
outlines are first fitted to that cell aspect ratio. The assembled GWB is then
|
||||
scaled uniformly using `--logo-width` (0.4 to 0.85, default 0.72), independently
|
||||
of the number of background rows and columns.
|
||||
|
||||
FontTools converts glyphs to vector paths, and resvg renders that same SVG to
|
||||
PNG. The SVG embeds no raster images and requires no installed fonts to view.
|
||||
Pillow creates optional thumbnail previews. Dependencies are recorded in
|
||||
`pyproject.toml` and pinned in `uv.lock`.
|
||||
|
||||
## Verify
|
||||
|
||||
```shell
|
||||
uv run python -m unittest discover -s tests
|
||||
```
|
||||
|
||||
Checks cover reproducible SVG paths, matching PNG rendering, logo placement,
|
||||
thumbnail dimensions, custom output paths, and invalid command-line options.
|
||||
HDR checks also cover PQ reference values, equal luminance peaks, gradients,
|
||||
16-bit PNG data, color metadata, and decoded luminance measurements.
|
||||
|
||||
Reference in New Issue
Block a user