Skip to content

Instantly share code, notes, and snippets.

@CrazyCoder
Last active December 5, 2025 12:59
Show Gist options
  • Select an option

  • Save CrazyCoder/b125f26d6987c0620058249f59f1327d to your computer and use it in GitHub Desktop.

Select an option

Save CrazyCoder/b125f26d6987c0620058249f59f1327d to your computer and use it in GitHub Desktop.
XTC/XTG/XTH/XTCH Format Technical Specification (for Xteink X4)

This document is generated by AI, and there may be issues with the specific details.

XTC/XTG/XTH/XTCH Format Technical Specification

Document Information

  • Version: 1.0
  • Date: 2025-01-XX
  • Purpose: Public technical specification for format conversion
  • Target Platform: ESP32 E-Paper Display Devices

Format Overview

Four proprietary file formats designed for ESP32 e-paper displays:

Format Extension Purpose Description
XTG .xtg Monochrome Image 1-bit per pixel bitmap
XTH .xth 4-Level Grayscale Image 2-bit per pixel bitmap
XTC .xtc Comic Container Container format with multiple XTG pages
XTCH .xtch Comic Container Variant Variant of XTC format

Byte Order

All multi-byte values are stored in Little-Endian format.

  • Little-Endian: Least significant byte at lowest address
  • Example: uint16_t 0x1234[0x34, 0x12]
  • Example: uint32_t 0x12345678[0x78, 0x56, 0x34, 0x12]

XTG Format (Monochrome Image)

File Structure

XTG stores 1-bit per pixel monochrome bitmaps optimized for e-paper displays.

Header (22 bytes)

Offset Size Type Field Description Value
0x00 4 uint32_t mark File identifier 0x00475458 ("XTG\0")
0x04 2 uint16_t width Image width (pixels) 1-65535
0x06 2 uint16_t height Image height (pixels) 1-65535
0x08 1 uint8_t colorMode Color mode 0=monochrome
0x09 1 uint8_t compression Compression 0=uncompressed
0x0A 4 uint32_t dataSize Image data size (bytes) Calculated
0x0E 8 uint64_t md5 MD5 checksum (first 8 bytes) Optional

Image Data

  • Location: After header (offset 22 bytes)
  • Format: Bitmap data, 1 bit per pixel
  • Data Size Calculation: dataSize = ((width + 7) / 8) * height
  • Pixel Storage:
    • Rows stored top to bottom
    • Each row stored left to right
    • 8 pixels per byte (MSB first)
    • Bit order: bit 7 (MSB) = leftmost pixel, bit 0 (LSB) = rightmost pixel

Pixel Value Mapping

Bit Value Display Color
0 Black
1 White

Note: When using inverted bitmap drawing, the display may invert these values (1=black, 0=white).


XTH Format (4-Level Grayscale Image)

File Structure

XTH stores 2-bit per pixel grayscale bitmaps for 4-level grayscale e-paper displays.

Header (22 bytes)

Same structure as XTG, but with different file identifier:

Offset Size Type Field Description Value
0x00 4 uint32_t mark File identifier 0x00485458 ("XTH\0")
0x04 2 uint16_t width Image width (pixels) 1-65535
0x06 2 uint16_t height Image height (pixels) 1-65535
0x08 1 uint8_t colorMode Color mode 0=monochrome
0x09 1 uint8_t compression Compression 0=uncompressed
0x0A 4 uint32_t dataSize Image data size (bytes) Calculated
0x0E 8 uint64_t md5 MD5 checksum (first 8 bytes) Optional

Image Data

  • Location: After header (offset 22 bytes)
  • Format: Two bit planes, 2 bits per pixel total
  • Data Size Calculation: dataSize = ((width * height + 7) / 8) * 2 (rounded up to bytes)
  • Storage:
    • First bit plane: offset 22, size (width * height + 7) / 8 bytes (rounded up)
    • Second bit plane: immediately after first plane, same size
    • Each bit plane stores pixels in vertical scan order (column-major):
      • Columns scanned from right to left (x = width-1 to 0)
      • 8 vertical pixels packed per byte (MSB = topmost pixel in group)
    • See "Data Storage Details" section for complete specification

Pixel Value Mapping

IMPORTANT: The Xteink e-paper LUT has non-linear grayscale mapping with swapped middle values:

Pixel Value Binary Bit1 Bit2 LUT Level Display Color
0 00 0 0 L0 White
1 01 0 1 L1 Dark Grey
2 10 1 0 L2 Light Grey
3 11 1 1 L3 Black

Note: Values 1 and 2 are swapped compared to typical linear grayscale ordering. This matches the Xteink device's e-paper LUT behavior.

Bit Plane Usage:

  • Bit1 (first plane): Sent via command 0x24
  • Bit2 (second plane): Sent via command 0x26

Pixel Value Calculation: pixelValue = (bit1 << 1) | bit2


XTC Format (Comic Container)

File Structure

XTC is a container format storing multiple XTG-format pages for comic/PDF reading.

Header (48 bytes)

Offset Size Type Field Description Value
0x00 4 uint32_t mark File identifier 0x00435458 ("XTC\0")
0x04 2 uint16_t version Version number 0x0100 (v1.0)
0x06 2 uint16_t pageCount Total pages 1-65535
0x08 1 uint8_t readDirection Reading direction 0-2
0x09 1 uint8_t hasMetadata Has metadata 0-1
0x0A 1 uint8_t hasThumbnails Has thumbnails 0-1
0x0B 1 uint8_t hasChapters Has chapters 0-1
0x0C 4 uint32_t currentPage Current page (1-based) 0-65535
0x10 8 uint64_t metadataOffset Metadata offset Byte offset
0x18 8 uint64_t indexOffset Index table offset Byte offset
0x20 8 uint64_t dataOffset Data area offset Byte offset
0x28 8 uint64_t thumbOffset Thumbnail offset Byte offset

Reading Direction

Value Direction Description
0 L→R Left to right (normal)
1 R→L Right to left (Japanese manga)
2 Top→Bottom Top to bottom (vertical)

Metadata Structure (256 bytes, optional)

If hasMetadata == 1, stored at metadataOffset:

Offset Size Type Field Description
0x00 128 char[] title Title (UTF-8, null-terminated)
0x80 64 char[] author Author (UTF-8, null-terminated)
0xC0 32 char[] publisher Publisher/source (UTF-8, null-terminated)
0xE0 16 char[] language Language code (e.g., "zh-CN", "en-US")
0xF0 4 uint32_t createTime Creation time (Unix timestamp)
0xF4 2 uint16_t coverPage Cover page (0-based, 0xFFFF=none)
0xF6 2 uint16_t chapterCount Number of chapters
0xF8 8 uint64_t reserved Reserved (zero-filled)

Chapter Structure (96 bytes per chapter, optional)

If hasChapters == 1, stored after metadata:

Offset Size Type Field Description
0x00 80 char[] chapterName Chapter name (UTF-8, null-terminated)
0x50 2 uint16_t startPage Start page (0-based)
0x52 2 uint16_t endPage End page (0-based, inclusive)
0x54 4 uint32_t reserved1 Reserved 1 (zero-filled)
0x58 4 uint32_t reserved2 Reserved 2 (zero-filled)
0x5C 4 uint32_t reserved3 Reserved 3 (zero-filled)

Number of chapters specified by XtcMetadata.chapterCount.

Page Index Table (16 bytes per page)

Stored at indexOffset, one entry per page:

Offset Size Type Field Description
0x00 8 uint64_t offset XTG/XTH image offset (absolute, from file start)
0x08 4 uint32_t size XTG/XTH image size in bytes (including 22-byte header)
0x0C 2 uint16_t width Image width (pixels)
0x0E 2 uint16_t height Image height (pixels)

Total index table size: pageCount * 16 bytes.

Data Area

All XTG/XTH page images stored starting at dataOffset. Each page's data is stored contiguously, with position specified by the index table's offset field (absolute offset from file start).

Thumbnail Area (optional)

If hasThumbnails == 1, thumbnails stored at thumbOffset. Thumbnails are also in XTG format with the same index structure.

File Layout

[Header: 48 bytes]
[Metadata: 256 bytes] (optional)
[Chapters: N × 96 bytes] (optional)
[Page Index Table: pageCount × 16 bytes]
[Data Area: All XTG page data]
[Thumbnail Area] (optional)

XTCH Format (Comic Container Variant)

File Structure

XTCH is identical to XTC in all aspects except the file identifier.

Header (48 bytes)

Same structure as XTC, only mark field differs:

Offset Size Type Field Description Value
0x00 4 uint32_t mark File identifier 0x48435458 ("XTCH")

File Identifier Comparison

Format Identifier (Little-Endian) ASCII
XTC 0x00435458 "XTC\0"
XTCH 0x48435458 "XTCH"

All other structures, fields, and data formats are identical to XTC.


String Encoding

  • All string fields use UTF-8 encoding
  • Strings are null-terminated (\0)
  • Remaining space in fixed-size string fields is zero-filled

Data Storage Details

Bitmap Storage Rules

XTG (1 bit/pixel)

  • Rows: top to bottom
  • Pixels per row: left to right
  • 8 pixels per byte
  • Bit order: MSB (bit 7) = leftmost pixel, LSB (bit 0) = rightmost pixel

Example: 10-pixel wide image

  • 2 bytes per row (rounded up)
  • Byte 1: pixels 0-7 (bit 7 = pixel 0, bit 0 = pixel 7)
  • Byte 2: pixels 8-9 (bit 7 = pixel 8, bit 6 = pixel 9, unused bits = 0)

XTH (2 bits/pixel)

  • Two bit planes stored sequentially
  • First plane: All pixels' Bit1 (pixel value bit 1), packed 8 pixels per byte
  • Second plane: All pixels' Bit2 (pixel value bit 0), packed 8 pixels per byte
  • IMPORTANT: Uses vertical scan order (column-major), NOT row-major:
    • Columns are scanned from right to left (x = width-1 down to 0)
    • Within each column, 8 vertical pixels are packed per byte (top to bottom)
    • Each byte contains 8 vertically-adjacent pixels, MSB (bit 7) = topmost pixel in group
  • This scan order matches the Xteink e-paper display refresh pattern

Pixel Value Calculation: pixelValue = (bit1 << 1) | bit2

Example: 480×800 pixel image

  • Total pixels: 384000
  • Each column: 800 pixels = 100 bytes (800/8 = 100 vertical groups)
  • Total columns: 480 (scanned right to left)
  • Each bit plane: 480 × 100 = 48000 bytes
  • First plane: offset 22, size 48000 bytes (contains Bit1 for all pixels)
  • Second plane: offset 48022, size 48000 bytes (contains Bit2 for all pixels)
  • Total data size: 96000 bytes

Conversion Guidelines

From Other Formats to XTG/XTH

  1. Prepare Source Image:

    • Convert to grayscale
    • Resize to target resolution
    • For XTG: Apply threshold/binarization
    • For XTH: Map grayscale to 4 levels (0-3)
  2. Generate Bitmap Data:

    • XTG: Pack 1 bit per pixel, calculate dataSize = ((width + 7) / 8) * height
    • XTH: Generate two bit planes, calculate dataSize = ((width * height + 7) / 8) * 2
  3. Write File:

    • Write header with correct file identifier
    • Write bitmap data
    • Ensure Little-Endian byte order for all multi-byte values

From XTC/XTCH to Other Formats

  1. Read header to get page count and offsets
  2. Read page index table to locate each page
  3. Extract XTG data for each page from data area
  4. Convert each XTG page using XTG conversion process

File Identifier Reference

Format Little-Endian uint32_t Byte Sequence ASCII
XTG 0x00475458 [0x58, 0x54, 0x47, 0x00] "XTG\0"
XTH 0x00485458 [0x58, 0x54, 0x48, 0x00] "XTH\0"
XTC 0x00435458 [0x58, 0x54, 0x43, 0x00] "XTC\0"
XTCH 0x48435458 [0x58, 0x54, 0x43, 0x48] "XTCH"

Notes

  • All offsets are byte offsets from the start of the file
  • Page numbers in XTC/XTCH are 1-based for display, but 0-based in internal structures
  • Reserved fields should be zero-filled
  • MD5 checksum field is optional and may be zero
  • Compression is currently not implemented (compression field = 0)
@CrazyCoder
Copy link
Author

FYI: chapters feature is not implemented yet in the Xteink firmware (as of 3.1.0).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment