0
# Convenience Functions
1
2
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.
3
4
## Capabilities
5
6
### Array to PNG Conversion
7
8
The primary convenience function for creating PNG images from Python data structures.
9
10
```python { .api }
11
def from_array(a, mode=None, info={}):
12
"""
13
Create PNG Image from 2D array or nested sequences.
14
15
Parameters:
16
- a: 2D array-like structure containing pixel data
17
- mode: str, color mode specification:
18
- 'L': Greyscale (1 value per pixel)
19
- 'LA': Greyscale with alpha (2 values per pixel)
20
- 'RGB': Red, green, blue (3 values per pixel)
21
- 'RGBA': Red, green, blue, alpha (4 values per pixel)
22
- None: Auto-detect based on data structure
23
- info: dict, additional PNG metadata options
24
25
Returns:
26
Image: PNG Image object with save() and write() methods
27
"""
28
29
# Alternative name for from_array
30
fromarray = from_array
31
```
32
33
### Low-Level PNG Creation
34
35
Direct PNG file creation by writing chunk data for advanced users who need complete control over PNG structure.
36
37
```python { .api }
38
def write_chunks(out, chunks):
39
"""
40
Create PNG file by writing chunk data directly.
41
42
Parameters:
43
- out: file-like object opened in binary write mode
44
- chunks: iterable of (chunk_type, chunk_data) tuples
45
46
Note: This is a low-level function. PNG signature is written automatically,
47
but all required chunks (IHDR, IDAT, IEND) must be provided.
48
"""
49
```
50
51
### Module Constants and Utilities
52
53
Important module-level constants and utility information for PNG manipulation.
54
55
```python { .api }
56
import collections
57
58
# Module version
59
__version__: str = "0.20220715.0"
60
61
# PNG file signature (8-byte header that identifies PNG files)
62
signature: bytes = b'\x89PNG\r\n\x1a\n'
63
64
# Adam7 interlacing pattern coordinates (xstart, ystart, xstep, ystep)
65
adam7: tuple = ((0, 0, 8, 8), (4, 0, 8, 8), (0, 4, 4, 8),
66
(2, 0, 4, 4), (0, 2, 2, 4), (1, 0, 2, 2), (0, 1, 1, 2))
67
68
# Alternative name for from_array function (PIL compatibility)
69
fromarray = from_array
70
71
# Named tuple for resolution metadata (from pHYs chunk)
72
Resolution = collections.namedtuple('_Resolution', 'x y unit_is_meter')
73
```
74
75
## Usage Examples
76
77
### Simple Image Creation
78
79
```python
80
import png
81
82
# Create greyscale image from simple 2D list
83
grey_pixels = [
84
[0, 64, 128, 192, 255], # Row 1: gradient from black to white
85
[255, 192, 128, 64, 0], # Row 2: gradient from white to black
86
[128, 128, 128, 128, 128] # Row 3: uniform grey
87
]
88
89
# Create and save PNG (mode automatically detected as 'L')
90
image = png.from_array(grey_pixels, 'L')
91
image.save('simple_grey.png')
92
```
93
94
### RGB Image Creation
95
96
```python
97
import png
98
99
# Create RGB image with explicit color values
100
rgb_pixels = [
101
[255, 0, 0, 0, 255, 0, 0, 0, 255], # Red, Green, Blue pixels
102
[255, 255, 0, 255, 0, 255, 0, 255, 255], # Yellow, Magenta, Cyan pixels
103
[255, 255, 255, 0, 0, 0, 128, 128, 128] # White, Black, Grey pixels
104
]
105
106
# Create RGB PNG
107
image = png.from_array(rgb_pixels, 'RGB')
108
image.save('rgb_colors.png')
109
```
110
111
### RGBA with Transparency
112
113
```python
114
import png
115
116
# Create RGBA image with transparency
117
rgba_pixels = [
118
[255, 0, 0, 255, 255, 0, 0, 128, 255, 0, 0, 0], # Red: opaque, half, transparent
119
[0, 255, 0, 255, 0, 255, 0, 128, 0, 255, 0, 0], # Green: opaque, half, transparent
120
[0, 0, 255, 255, 0, 0, 255, 128, 0, 0, 255, 0] # Blue: opaque, half, transparent
121
]
122
123
# Create RGBA PNG
124
image = png.from_array(rgba_pixels, 'RGBA')
125
image.save('rgba_transparency.png')
126
```
127
128
### Auto-Detection of Color Mode
129
130
```python
131
import png
132
133
# Let from_array auto-detect the color mode
134
pixels_1_channel = [[100, 150, 200]] # Detected as 'L' (greyscale)
135
pixels_2_channel = [[100, 255, 150, 128]] # Detected as 'LA' (greyscale + alpha)
136
pixels_3_channel = [[255, 0, 0, 0, 255, 0]] # Detected as 'RGB'
137
pixels_4_channel = [[255, 0, 0, 255, 0, 255, 0, 128]] # Detected as 'RGBA'
138
139
# Auto-detection based on values per pixel
140
image1 = png.from_array(pixels_1_channel) # Mode: 'L'
141
image2 = png.from_array(pixels_2_channel) # Mode: 'LA'
142
image3 = png.from_array(pixels_3_channel) # Mode: 'RGB'
143
image4 = png.from_array(pixels_4_channel) # Mode: 'RGBA'
144
145
image1.save('auto_grey.png')
146
image2.save('auto_grey_alpha.png')
147
image3.save('auto_rgb.png')
148
image4.save('auto_rgba.png')
149
```
150
151
### NumPy Array Integration
152
153
```python
154
import png
155
import numpy as np
156
157
# Create NumPy array
158
width, height = 64, 64
159
x, y = np.meshgrid(np.linspace(0, 1, width), np.linspace(0, 1, height))
160
161
# Generate mathematical pattern
162
pattern = np.sin(x * 10) * np.cos(y * 10)
163
# Scale to 0-255 range
164
pattern = ((pattern + 1) * 127.5).astype(np.uint8)
165
166
# Convert to PNG (NumPy arrays work directly)
167
image = png.from_array(pattern, 'L')
168
image.save('numpy_pattern.png')
169
170
# For RGB, reshape or create 3D array
171
rgb_pattern = np.zeros((height, width * 3), dtype=np.uint8)
172
rgb_pattern[:, 0::3] = pattern # Red channel
173
rgb_pattern[:, 1::3] = pattern // 2 # Green channel
174
rgb_pattern[:, 2::3] = 255 - pattern # Blue channel
175
176
rgb_image = png.from_array(rgb_pattern, 'RGB')
177
rgb_image.save('numpy_rgb_pattern.png')
178
```
179
180
### Working with Different Bit Depths
181
182
```python
183
import png
184
185
# 16-bit greyscale data (values 0-65535)
186
high_depth_pixels = [
187
[0, 16383, 32767, 49151, 65535], # Full 16-bit range
188
[65535, 49151, 32767, 16383, 0] # Reverse gradient
189
]
190
191
# Specify 16-bit depth in info dict
192
info = {'bitdepth': 16}
193
image = png.from_array(high_depth_pixels, 'L', info)
194
image.save('16bit_grey.png')
195
196
# For 16-bit RGB, values are still 0-65535 per channel
197
rgb_16bit = [
198
[65535, 0, 0, 0, 65535, 0, 0, 0, 65535], # Bright RGB
199
[32767, 32767, 32767, 16383, 16383, 16383] # Two greys
200
]
201
202
info_rgb = {'bitdepth': 16}
203
rgb_image = png.from_array(rgb_16bit, 'RGB', info_rgb)
204
rgb_image.save('16bit_rgb.png')
205
```
206
207
### Palette Images via Convenience Function
208
209
```python
210
import png
211
212
# Create palette-based image data (indices into palette)
213
palette_indices = [
214
[0, 1, 2, 1, 0],
215
[1, 2, 3, 2, 1],
216
[2, 3, 0, 3, 2]
217
]
218
219
# Define palette and include in info
220
palette = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]
221
info = {'palette': palette}
222
223
# Create palette PNG
224
image = png.from_array(palette_indices, mode=None, info=info)
225
image.save('palette_convenience.png')
226
```
227
228
### Low-Level Chunk Writing
229
230
```python
231
import png
232
import struct
233
import zlib
234
235
def create_minimal_png(width, height, pixel_data):
236
"""Create PNG using low-level chunk writing"""
237
238
# IHDR chunk data
239
ihdr_data = struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0)
240
241
# IDAT chunk - compress pixel data
242
# Add filter bytes (0 = no filter) to each row
243
filtered_data = b''
244
for row in pixel_data:
245
filtered_data += b'\x00' # Filter type
246
filtered_data += bytes(row)
247
248
idat_data = zlib.compress(filtered_data)
249
250
# Create chunk list
251
chunks = [
252
(b'IHDR', ihdr_data),
253
(b'IDAT', idat_data),
254
(b'IEND', b'')
255
]
256
257
# Write PNG file
258
with open('minimal.png', 'wb') as f:
259
png.write_chunks(f, chunks)
260
261
# Create simple 2x2 RGB image
262
pixel_data = [
263
[255, 0, 0, 0, 255, 0], # Red, Green
264
[0, 0, 255, 255, 255, 255] # Blue, White
265
]
266
267
create_minimal_png(2, 2, pixel_data)
268
```
269
270
### Error Handling
271
272
```python
273
import png
274
275
try:
276
# Invalid mode
277
invalid_pixels = [[255, 0, 0]]
278
image = png.from_array(invalid_pixels, 'INVALID_MODE')
279
except png.ProtocolError as e:
280
print(f"Protocol error: {e}")
281
282
try:
283
# Inconsistent row lengths
284
inconsistent_pixels = [
285
[255, 0, 0], # 3 values
286
[0, 255, 0, 128] # 4 values - inconsistent!
287
]
288
image = png.from_array(inconsistent_pixels, 'RGB')
289
except png.ProtocolError as e:
290
print(f"Data format error: {e}")
291
292
try:
293
# Empty or invalid data
294
empty_pixels = []
295
image = png.from_array(empty_pixels)
296
except (png.ProtocolError, ValueError) as e:
297
print(f"Invalid data: {e}")
298
```