geomotif.io.jpeg
¶
Write a JPEG still, in pure standard library.
A JPEG is the still picture that trades a little of a PNG's exactness for a smaller file, and this module is the lossy half of the pair: where the GIF squeezes into 256 colors and the PNG keeps every one, a JPEG throws away what an eye would not miss -- the small high-frequency detail in an 8x8 block -- and codes what is left with Huffman's method. It renders a design once, as the same full-color frame the PNG and GIF writers draw, and then encodes it with nothing but the standard library and a little arithmetic.
Baseline JPEG is, at heart, a small pipeline that this module walks exactly
once: the RGB sample is changed into the luminance-plus-color space the human
eye is better at, the two color channels are halved (4:2:0, because the eye
cares less about their detail), every 8x8 block is transformed with the DCT,
the transformed coefficients are divided by a quality-scaled table (that is
the lossy step), and what survives is zig-zag-scanned and Huffman-coded
against the standard tables. quality chooses how much survives::
from geomotif.io.jpeg import save_jpeg
from geomotif.motifs import Rose
save_jpeg(Rose(n=7).build(), "rose.jpg", quality=92)
The writer answers to the same styling vocabulary as the PNG and GIF writers
(ink, background, thickness, padding, antialias), so a
design that looks right in one looks the same way here.
Functions:
| Name | Description |
|---|---|
to_jpeg |
Render a design -- or re-encode a :class: |
save_jpeg |
Render a design -- or re-encode a raster -- as JPEG and write it. |
to_jpeg
¶
to_jpeg(source: Design | Raster, *, width: int = 480, height: int = 480, padding: float = 8.0, ink: str = '#0b0b0b', background: str = '#ffffff', thickness: int = 1, dot_radius: int | None = None, antialias: bool = False, aa_level: int = 8, quality: int = 85) -> bytes
Render a design -- or re-encode a :class:~geomotif.io.Raster -- as JPEG.
A still is drawn once, exactly the way :func:to_png draws a frame, and
then encoded with baseline JPEG: 4:2:0 chroma subsampling, an 8x8 DCT on
each block, quality-scaled quantization, and Huffman coding against the
reference tables. The styling parameters are shared with the PNG and GIF
writers so one summoning controls all three.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Design or Raster
|
What to write. A design is drawn; a raster is encoded as it is. |
required |
width
|
int
|
Canvas size in pixels. Ignored when |
480
|
height
|
int
|
Canvas size in pixels. Ignored when |
480
|
padding
|
float
|
Margin reserved on all four sides, in pixels. |
8.0
|
ink
|
str
|
Default stroke color and the color behind everything. |
'#0b0b0b'
|
background
|
str
|
Default stroke color and the color behind everything. |
'#0b0b0b'
|
thickness
|
int
|
Stroke width in pixels. |
1
|
dot_radius
|
int
|
Radius for loose points. Defaults to |
None
|
antialias
|
bool
|
Supersample and blend edges. Off by default, so the edges are the same hard Bresenham edges they have always been. |
False
|
aa_level
|
int
|
How many shades an antialiased edge may blend into. A JPEG keeps full color anyway, so this only bounds the drawing, not the encode. |
8
|
quality
|
int
|
From 0 (smallest, most loss) to 100 (closest to the original). This is what the quantization tables are scaled by. |
85
|
Returns:
| Type | Description |
|---|---|
bytes
|
A complete baseline JPEG file. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/geomotif/io/jpeg.py
save_jpeg
¶
Render a design -- or re-encode a raster -- as JPEG and write it.
Keyword arguments are passed straight through to :func:to_jpeg.