CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pypng

Pure Python library for saving and loading PNG images without external dependencies

Pending
Overview
Eval results
Files

convenience.mddocs/

Convenience Functions

High-level utility functions for quick PNG creation and manipulation without requiring detailed format knowledge or class instantiation. These functions provide the simplest interface for common PNG operations.

Capabilities

Array to PNG Conversion

The primary convenience function for creating PNG images from Python data structures.

def from_array(a, mode=None, info={}):
    """
    Create PNG Image from 2D array or nested sequences.
    
    Parameters:
    - a: 2D array-like structure containing pixel data
    - mode: str, color mode specification:
        - 'L': Greyscale (1 value per pixel)
        - 'LA': Greyscale with alpha (2 values per pixel)  
        - 'RGB': Red, green, blue (3 values per pixel)
        - 'RGBA': Red, green, blue, alpha (4 values per pixel)
        - None: Auto-detect based on data structure
    - info: dict, additional PNG metadata options
    
    Returns:
    Image: PNG Image object with save() and write() methods
    """

# Alternative name for from_array
fromarray = from_array

Low-Level PNG Creation

Direct PNG file creation by writing chunk data for advanced users who need complete control over PNG structure.

def write_chunks(out, chunks):
    """
    Create PNG file by writing chunk data directly.
    
    Parameters:
    - out: file-like object opened in binary write mode
    - chunks: iterable of (chunk_type, chunk_data) tuples
    
    Note: This is a low-level function. PNG signature is written automatically,
    but all required chunks (IHDR, IDAT, IEND) must be provided.
    """

Module Constants and Utilities

Important module-level constants and utility information for PNG manipulation.

import collections

# Module version
__version__: str = "0.20220715.0"

# PNG file signature (8-byte header that identifies PNG files)
signature: bytes = b'\x89PNG\r\n\x1a\n'

# Adam7 interlacing pattern coordinates (xstart, ystart, xstep, ystep)
adam7: tuple = ((0, 0, 8, 8), (4, 0, 8, 8), (0, 4, 4, 8), 
                (2, 0, 4, 4), (0, 2, 2, 4), (1, 0, 2, 2), (0, 1, 1, 2))

# Alternative name for from_array function (PIL compatibility)
fromarray = from_array

# Named tuple for resolution metadata (from pHYs chunk)
Resolution = collections.namedtuple('_Resolution', 'x y unit_is_meter')

Usage Examples

Simple Image Creation

import png

# Create greyscale image from simple 2D list
grey_pixels = [
    [0, 64, 128, 192, 255],     # Row 1: gradient from black to white
    [255, 192, 128, 64, 0],     # Row 2: gradient from white to black
    [128, 128, 128, 128, 128]   # Row 3: uniform grey
]

# Create and save PNG (mode automatically detected as 'L')
image = png.from_array(grey_pixels, 'L')
image.save('simple_grey.png')

RGB Image Creation

import png

# Create RGB image with explicit color values
rgb_pixels = [
    [255, 0, 0,   0, 255, 0,   0, 0, 255],    # Red, Green, Blue pixels
    [255, 255, 0, 255, 0, 255, 0, 255, 255],  # Yellow, Magenta, Cyan pixels  
    [255, 255, 255, 0, 0, 0,   128, 128, 128] # White, Black, Grey pixels
]

# Create RGB PNG
image = png.from_array(rgb_pixels, 'RGB')
image.save('rgb_colors.png')

RGBA with Transparency

import png

# Create RGBA image with transparency
rgba_pixels = [
    [255, 0, 0, 255,   255, 0, 0, 128,   255, 0, 0, 0],      # Red: opaque, half, transparent
    [0, 255, 0, 255,   0, 255, 0, 128,   0, 255, 0, 0],      # Green: opaque, half, transparent  
    [0, 0, 255, 255,   0, 0, 255, 128,   0, 0, 255, 0]       # Blue: opaque, half, transparent
]

# Create RGBA PNG
image = png.from_array(rgba_pixels, 'RGBA')
image.save('rgba_transparency.png')

Auto-Detection of Color Mode

import png

# Let from_array auto-detect the color mode
pixels_1_channel = [[100, 150, 200]]                    # Detected as 'L' (greyscale)
pixels_2_channel = [[100, 255, 150, 128]]               # Detected as 'LA' (greyscale + alpha)
pixels_3_channel = [[255, 0, 0, 0, 255, 0]]             # Detected as 'RGB'
pixels_4_channel = [[255, 0, 0, 255, 0, 255, 0, 128]]   # Detected as 'RGBA'

# Auto-detection based on values per pixel
image1 = png.from_array(pixels_1_channel)  # Mode: 'L'
image2 = png.from_array(pixels_2_channel)  # Mode: 'LA'  
image3 = png.from_array(pixels_3_channel)  # Mode: 'RGB'
image4 = png.from_array(pixels_4_channel)  # Mode: 'RGBA'

image1.save('auto_grey.png')
image2.save('auto_grey_alpha.png')
image3.save('auto_rgb.png')
image4.save('auto_rgba.png')

NumPy Array Integration

import png
import numpy as np

# Create NumPy array
width, height = 64, 64
x, y = np.meshgrid(np.linspace(0, 1, width), np.linspace(0, 1, height))

# Generate mathematical pattern
pattern = np.sin(x * 10) * np.cos(y * 10)
# Scale to 0-255 range
pattern = ((pattern + 1) * 127.5).astype(np.uint8)

# Convert to PNG (NumPy arrays work directly)
image = png.from_array(pattern, 'L')
image.save('numpy_pattern.png')

# For RGB, reshape or create 3D array
rgb_pattern = np.zeros((height, width * 3), dtype=np.uint8)
rgb_pattern[:, 0::3] = pattern      # Red channel
rgb_pattern[:, 1::3] = pattern // 2 # Green channel  
rgb_pattern[:, 2::3] = 255 - pattern # Blue channel

rgb_image = png.from_array(rgb_pattern, 'RGB')
rgb_image.save('numpy_rgb_pattern.png')

Working with Different Bit Depths

import png

# 16-bit greyscale data (values 0-65535)
high_depth_pixels = [
    [0, 16383, 32767, 49151, 65535],        # Full 16-bit range
    [65535, 49151, 32767, 16383, 0]         # Reverse gradient
]

# Specify 16-bit depth in info dict
info = {'bitdepth': 16}
image = png.from_array(high_depth_pixels, 'L', info)
image.save('16bit_grey.png')

# For 16-bit RGB, values are still 0-65535 per channel
rgb_16bit = [
    [65535, 0, 0,   0, 65535, 0,   0, 0, 65535],    # Bright RGB
    [32767, 32767, 32767,  16383, 16383, 16383]      # Two greys
]

info_rgb = {'bitdepth': 16}
rgb_image = png.from_array(rgb_16bit, 'RGB', info_rgb)
rgb_image.save('16bit_rgb.png')

Palette Images via Convenience Function

import png

# Create palette-based image data (indices into palette)
palette_indices = [
    [0, 1, 2, 1, 0],
    [1, 2, 3, 2, 1], 
    [2, 3, 0, 3, 2]
]

# Define palette and include in info
palette = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]
info = {'palette': palette}

# Create palette PNG
image = png.from_array(palette_indices, mode=None, info=info)
image.save('palette_convenience.png')

Low-Level Chunk Writing

import png
import struct
import zlib

def create_minimal_png(width, height, pixel_data):
    """Create PNG using low-level chunk writing"""
    
    # IHDR chunk data
    ihdr_data = struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0)
    
    # IDAT chunk - compress pixel data
    # Add filter bytes (0 = no filter) to each row
    filtered_data = b''
    for row in pixel_data:
        filtered_data += b'\x00'  # Filter type
        filtered_data += bytes(row)
    
    idat_data = zlib.compress(filtered_data)
    
    # Create chunk list
    chunks = [
        (b'IHDR', ihdr_data),
        (b'IDAT', idat_data),
        (b'IEND', b'')
    ]
    
    # Write PNG file
    with open('minimal.png', 'wb') as f:
        png.write_chunks(f, chunks)

# Create simple 2x2 RGB image
pixel_data = [
    [255, 0, 0,   0, 255, 0],    # Red, Green
    [0, 0, 255,   255, 255, 255] # Blue, White  
]

create_minimal_png(2, 2, pixel_data)

Error Handling

import png

try:
    # Invalid mode
    invalid_pixels = [[255, 0, 0]]
    image = png.from_array(invalid_pixels, 'INVALID_MODE')
except png.ProtocolError as e:
    print(f"Protocol error: {e}")

try:
    # Inconsistent row lengths
    inconsistent_pixels = [
        [255, 0, 0],        # 3 values
        [0, 255, 0, 128]    # 4 values - inconsistent!
    ]
    image = png.from_array(inconsistent_pixels, 'RGB')
except png.ProtocolError as e:
    print(f"Data format error: {e}")

try:
    # Empty or invalid data
    empty_pixels = []
    image = png.from_array(empty_pixels)
except (png.ProtocolError, ValueError) as e:
    print(f"Invalid data: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-pypng

docs

convenience.md

image-container.md

index.md

reading.md

writing.md

tile.json