Documentation
¶
Overview ¶
Package xtx implements encoding and decoding of Xteink XTG/XTH image formats and XTC/XTCH comic container formats for ESP32 e-paper displays.
Image types Monochrome (1-bit) and Grayscale (2-bit, 4-level) implement the image.Image interface and can be used with the standard library's image processing tools.
The Encode function writes any image.Image to XTG or XTH format:
// Encode as monochrome (XTG) with dithering
xtx.Encode(w, img, &xtx.Options{Dither: true})
// Encode as grayscale (XTH)
xtx.Encode(w, img, &xtx.Options{Format: xtx.FormatGrayscale})
The Decode function auto-detects the format from magic bytes:
img, err := xtx.Decode(r)
XTG and XTH formats are also registered with image.RegisterFormat, so importing this package enables image.Decode to handle them:
import _ "github.com/phrozen/xtx" img, format, err := image.Decode(r)
The XTC type provides a builder for comic containers:
comic := xtx.NewXTC()
comic.SetMetadata(xtx.Metadata{Title: "My Manga"})
comic.AddPage(page1)
comic.Encode(w)
Index ¶
- Constants
- Variables
- func Decode(r io.Reader) (image.Image, error)
- func DecodeConfig(r io.Reader) (image.Config, error)
- func Encode(w io.Writer, img image.Image, opts *Options) error
- func NewWhitePage(w, h int) *image.RGBA
- func ReadCBZ(path string) ([]image.Image, error)
- func Resize(img image.Image, width, height int) *image.RGBA
- func ResizeToHeight(img image.Image, targetHeight int) *image.RGBA
- func ResizeToWidth(img image.Image, targetWidth int) *image.RGBA
- func RotateCCW(img image.Image) *image.RGBA
- func RotateCW(img image.Image) *image.RGBA
- type Chapter
- type Dither
- type DitherAlgorithm
- type ErrorCoeff
- type Format
- type Gray4
- type Grayscale
- func (img *Grayscale) At(x, y int) color.Color
- func (img *Grayscale) Bounds() image.Rectangle
- func (img *Grayscale) ColorModel() color.Model
- func (img *Grayscale) DataSize() uint32
- func (img *Grayscale) Gray4At(x, y int) Gray4
- func (img *Grayscale) Set(x, y int, c color.Color)
- func (img *Grayscale) SetGray4(x, y int, c Gray4)
- type Header
- type Metadata
- type Mono
- type Monochrome
- func (img *Monochrome) At(x, y int) color.Color
- func (img *Monochrome) Bounds() image.Rectangle
- func (img *Monochrome) ColorModel() color.Model
- func (img *Monochrome) DataSize() uint32
- func (img *Monochrome) MonoAt(x, y int) Mono
- func (img *Monochrome) Set(x, y int, c color.Color)
- func (img *Monochrome) SetMono(x, y int, c Mono)
- type Options
- type ReadDirection
- type XTC
- func (c *XTC) AddChapter(ch Chapter)
- func (c *XTC) AddPage(img image.Image) error
- func (c *XTC) AddPageXTH(img image.Image) error
- func (c *XTC) AddThumbnail(img *Monochrome) error
- func (c *XTC) Chapters() []Chapter
- func (c *XTC) Direction() ReadDirection
- func (c *XTC) Encode(w io.Writer) error
- func (c *XTC) GetMetadata() *Metadata
- func (c *XTC) Page(i int) (image.Image, error)
- func (c *XTC) PageCount() int
- func (c *XTC) SetDirection(d ReadDirection)
- func (c *XTC) SetMetadata(m Metadata)
Constants ¶
const ( MagicXTG uint32 = 0x00475458 // "XTG\0" MagicXTH uint32 = 0x00485458 // "XTH\0" MagicXTC uint32 = 0x00435458 // "XTC\0" MagicXTCH uint32 = 0x48435458 // "XTCH" )
File format magic bytes (little-endian uint32).
const ( HeaderSize = 22 // Image header (XTG/XTH) XTCHeaderSize = 56 // XTC/XTCH container header MetadataSize = 256 // XTC metadata block ChapterSize = 96 // XTC chapter entry IndexEntrySize = 16 // XTC page index entry )
Structure sizes in bytes.
const ( MetaTitleSize = 128 MetaAuthorSize = 64 MetaPublisherSize = 32 MetaLanguageSize = 16 )
Field sizes for metadata strings.
Variables ¶
var ( MonoBlack = Mono{V: false} MonoWhite = Mono{V: true} )
Predefined monochrome colors.
var ( Gray4White = Gray4{V: 0} Gray4DarkGrey = Gray4{V: 1} Gray4LightGrey = Gray4{V: 2} Gray4Black = Gray4{V: 3} )
Predefined Gray4 colors.
var ( DitherFloydSteinberg = DitherAlgorithm{ Name: "floyd-steinberg", Divisor: 16, Coeffs: []ErrorCoeff{ {1, 0, 7}, {-1, 1, 3}, {0, 1, 5}, {1, 1, 1}, }, } DitherAtkinson = DitherAlgorithm{ Name: "atkinson", Divisor: 8, Coeffs: []ErrorCoeff{ {1, 0, 1}, {2, 0, 1}, {-1, 1, 1}, {0, 1, 1}, {1, 1, 1}, {0, 2, 1}, }, } DitherSierra = DitherAlgorithm{ Name: "sierra", Divisor: 32, Coeffs: []ErrorCoeff{ {1, 0, 5}, {2, 0, 3}, {-2, 1, 2}, {-1, 1, 4}, {0, 1, 5}, {1, 1, 4}, {2, 1, 2}, {-1, 2, 2}, {0, 2, 3}, {1, 2, 2}, }, } DitherSierraLite = DitherAlgorithm{ Name: "sierra-lite", Divisor: 4, Coeffs: []ErrorCoeff{ {1, 0, 2}, {-1, 1, 1}, {0, 1, 1}, }, } DitherStucki = DitherAlgorithm{ Name: "stucki", Divisor: 42, Coeffs: []ErrorCoeff{ {1, 0, 8}, {2, 0, 4}, {-2, 1, 2}, {-1, 1, 4}, {0, 1, 8}, {1, 1, 4}, {2, 1, 2}, {-2, 2, 1}, {-1, 2, 2}, {0, 2, 4}, {1, 2, 2}, {2, 2, 1}, }, } )
Standard dithering algorithms.
var ( ErrInvalidMagic = errors.New("xtx: invalid magic bytes") ErrDimensionOverflow = errors.New("xtx: image dimensions exceed uint16 range") ErrDimensionZero = errors.New("xtx: image dimensions must be non-zero") ErrDataSizeMismatch = errors.New("xtx: data size does not match expected") ErrUnsupportedCompression = errors.New("xtx: compression is not supported") )
Sentinel errors.
var DitherNone draw.Drawer = draw.Src
DitherNone is a draw.Drawer that performs no dithering (nearest-color mapping).
var Gray4Model = color.ModelFunc(gray4Model)
Gray4Model converts any color.Color to Gray4 using nearest-level quantization. The thresholds account for the non-linear Xteink LUT mapping.
var MonoModel = color.ModelFunc(monoModel)
MonoModel converts any color.Color to Mono using luminance thresholding. Colors with luminance >= 128 become white; below 128 become black.
Functions ¶
func Decode ¶
Decode reads an XTG or XTH image from r, auto-detecting the format from the magic bytes in the header.
func DecodeConfig ¶
DecodeConfig returns the image dimensions and color model without reading the pixel data.
func Encode ¶
Encode writes img to w in XTG or XTH format.
The target format is determined by:
- If img is *Monochrome, encodes as XTG (skips conversion).
- If img is *Grayscale, encodes as XTH (skips conversion).
- Otherwise, uses opts.Format (default FormatMonochrome).
When opts.Ditherer is provided and the source requires conversion, it is used to dither the image into the target color space.
A nil opts is equivalent to &Options{Format: FormatMonochrome}.
func NewWhitePage ¶
NewWhitePage creates a new RGBA image of the given dimensions filled with white.
func ReadCBZ ¶
ReadCBZ opens a CBZ (ZIP) file and returns all images sorted alphabetically by filename. It skips directories, hidden files, and macOS metadata (__MACOSX).
func Resize ¶
Resize scales img to the given width and height using bilinear interpolation. For aspect-ratio-preserving variants, use ResizeToWidth or ResizeToHeight.
func ResizeToHeight ¶
ResizeToHeight scales img so its height equals targetHeight, maintaining the original aspect ratio. Uses bilinear interpolation.
func ResizeToWidth ¶
ResizeToWidth scales img so its width equals targetWidth, maintaining the original aspect ratio. Uses bilinear interpolation.
Types ¶
type Chapter ¶
type Chapter struct {
Name string
StartPage uint16 // 0-based, inclusive
EndPage uint16 // 0-based, inclusive
}
Chapter represents a named range of pages within an XTC/XTCH container.
type Dither ¶ added in v0.2.0
type Dither struct {
Algo DitherAlgorithm
}
Dither is a draw.Drawer that applies an error diffusion algorithm.
type DitherAlgorithm ¶ added in v0.2.0
type DitherAlgorithm struct {
Name string
Divisor int
Coeffs []ErrorCoeff
}
DitherAlgorithm defines an error diffusion matrix.
func AllDitherAlgorithms ¶ added in v0.2.0
func AllDitherAlgorithms() []DitherAlgorithm
AllDitherAlgorithms returns a list of supported error diffusion algorithms.
func ParseDitherAlgorithm ¶ added in v0.2.0
func ParseDitherAlgorithm(name string) (DitherAlgorithm, bool)
ParseDitherAlgorithm returns the algorithm matching the given name, or DitherFloydSteinberg if not found. Name matching is case-insensitive.
type ErrorCoeff ¶ added in v0.2.0
ErrorCoeff represents an error diffusion distribution step.
type Gray4 ¶
type Gray4 struct {
V uint8 // 0–3
}
Gray4 represents a 2-bit grayscale color with 4 levels. Values 0–3 map to the Xteink e-paper LUT:
0 = White 1 = Dark Grey (non-linear: swapped with level 2) 2 = Light Grey (non-linear: swapped with level 1) 3 = Black
func (Gray4) RGBA ¶
RGBA implements the color.Color interface. Maps Gray4 levels to 8-bit grayscale, accounting for the non-linear LUT.
type Grayscale ¶
type Grayscale struct {
// Pix holds both bit planes concatenated: [plane1][plane2].
// Each plane uses vertical scan order: columns right-to-left,
// 8 vertical pixels packed per byte (MSB = topmost).
Pix []uint8
// Rect is the image bounds.
Rect image.Rectangle
// PlaneSize is the size of one bit plane in bytes.
PlaneSize int
// ColBytes is the number of bytes per column: (height + 7) / 8.
ColBytes int
}
Grayscale is a 2-bit per pixel, 4-level grayscale image corresponding to the XTH format. It implements image.Image and stores pixels in dual bit-plane format with vertical scan order (column-major, right-to-left) matching the Xteink e-paper display refresh pattern.
The pixel value mapping follows the non-linear Xteink LUT:
0 = White, 1 = Dark Grey, 2 = Light Grey, 3 = Black
func NewGrayscale ¶
NewGrayscale creates a new Grayscale image with the given bounds.
func (*Grayscale) ColorModel ¶
ColorModel returns Gray4Model.
func (*Grayscale) DataSize ¶
DataSize returns the total size of the pixel data in bytes (both planes), matching the XTH spec: ((width * height + 7) / 8) * 2.
func (*Grayscale) Set ¶
Set sets the pixel at (x, y) to color c, converting through Gray4Model.
type Header ¶
type Header struct {
Mark uint32 // File identifier (MagicXTG or MagicXTH)
Width uint16 // Image width in pixels
Height uint16 // Image height in pixels
ColorMode uint8 // Color mode (0 = monochrome)
Compression uint8 // Compression (0 = uncompressed)
DataSize uint32 // Image data size in bytes
MD5 uint64 // Truncated MD5 checksum (first 8 bytes); 0 = unused
}
Header represents the 22-byte image header shared by XTG and XTH formats.
type Metadata ¶
type Metadata struct {
Title string
Author string
Publisher string
Language string
CreateTime uint32 // Unix timestamp
CoverPage uint16 // 0-based page index; 0xFFFF = none
}
Metadata holds optional metadata for an XTC/XTCH container.
type Mono ¶
type Mono struct {
V bool // true = white, false = black
}
Mono represents a 1-bit monochrome color (black or white).
func (Mono) RGBA ¶
RGBA implements the color.Color interface.
type Monochrome ¶
type Monochrome struct {
// Pix holds the packed bitmap data. Bit 7 of each byte is the leftmost pixel.
// A set bit (1) represents white; a cleared bit (0) represents black.
Pix []uint8
// Stride is the number of bytes per row: (width + 7) / 8.
Stride int
// Rect is the image bounds.
Rect image.Rectangle
}
Monochrome is a 1-bit per pixel image (black and white) corresponding to the XTG format. It implements image.Image and stores pixels in packed bitmap format: row-major, MSB-first, 8 pixels per byte.
func NewMonochrome ¶
func NewMonochrome(r image.Rectangle) *Monochrome
NewMonochrome creates a new Monochrome image with the given bounds.
func (*Monochrome) At ¶
func (img *Monochrome) At(x, y int) color.Color
At returns the color of the pixel at (x, y).
func (*Monochrome) Bounds ¶
func (img *Monochrome) Bounds() image.Rectangle
Bounds returns the image bounds.
func (*Monochrome) ColorModel ¶
func (img *Monochrome) ColorModel() color.Model
ColorModel returns MonoModel.
func (*Monochrome) DataSize ¶
func (img *Monochrome) DataSize() uint32
DataSize returns the size of the pixel data in bytes, matching the XTG spec: ((width + 7) / 8) * height.
func (*Monochrome) MonoAt ¶
func (img *Monochrome) MonoAt(x, y int) Mono
MonoAt returns the Mono color at (x, y) without bounds checking.
type Options ¶
type Options struct {
// Format selects XTG (FormatMonochrome) or XTH (FormatGrayscale).
// Ignored when the source image is already *Monochrome or *Grayscale.
Format Format
// Ditherer specifies the algorithm to use for error-diffusion dithering
// during encoding (when the source image requires conversion).
// If nil, defaults to DitherNone (no dithering, simple threshold/quantization).
Ditherer draw.Drawer
}
Options controls encoding behavior. A nil *Options is valid and uses defaults (FormatMonochrome, no dithering).
type ReadDirection ¶
type ReadDirection uint8
ReadDirection specifies the reading direction for comic containers.
const ( LeftToRight ReadDirection = 0 // Normal (left to right) RightToLeft ReadDirection = 1 // Japanese manga (right to left) TopToBottom ReadDirection = 2 // Vertical (top to bottom) )
type XTC ¶
type XTC struct {
// contains filtered or unexported fields
}
XTC represents an XTC or XTCH comic container. Use NewXTC to create one, add pages and metadata, then call XTC.Encode to write it out.
func DecodeXTC ¶
func DecodeXTC(r io.ReadSeeker) (*XTC, error)
DecodeXTC reads an XTC/XTCH container from r.
func NewXTC ¶
NewXTC creates a new empty container. If xtch is true, the container uses the XTCH variant; otherwise standard XTC.
func (*XTC) AddChapter ¶
AddChapter appends a chapter to the container.
func (*XTC) AddPageXTH ¶
AddPageXTH encodes img as XTH and appends it as a page to the container.
func (*XTC) AddThumbnail ¶
func (c *XTC) AddThumbnail(img *Monochrome) error
AddThumbnail appends a pre-built *Monochrome image as a thumbnail. Thumbnails are optional and stored in the thumbnail area of the container.
func (*XTC) Direction ¶
func (c *XTC) Direction() ReadDirection
Direction returns the reading direction.
func (*XTC) GetMetadata ¶
GetMetadata returns the container metadata, or nil if not set.
func (*XTC) Page ¶
Page decodes and returns the i-th page as an image.Image. The returned image is either *Monochrome or *Grayscale depending on the page's format.
func (*XTC) SetDirection ¶
func (c *XTC) SetDirection(d ReadDirection)
SetDirection sets the reading direction of the comic.
func (*XTC) SetMetadata ¶
SetMetadata sets the container's metadata block.