Parser
The Parser module provides low-level utilities for parsing PNG files. It's used internally by the PNG class but can also be used directly for custom parsing needs.
Constants
Color Types
typescript
const COLOR_TYPES = {
GRAYSCALE: 0,
RGB: 2,
PALETTE: 3,
GRAYSCALE_ALPHA: 4,
RGBA: 6
} as const
Filter Types
typescript
const FILTER_TYPES = {
NONE: 0,
SUB: 1,
UP: 2,
AVERAGE: 3,
PAETH: 4
} as const
Functions
parseSignature(buffer: Buffer): boolean
Checks if a buffer starts with a valid PNG signature.
typescript
import { parseSignature } from 'pngx/parser'
import { Buffer } from 'node:buffer'
const buffer = readFileSync('image.png')
if (parseSignature(buffer)) {
console.log('Valid PNG file')
}
parseIHDR(buffer: Buffer, offset: number): IHDRData
Parses the IHDR chunk of a PNG file.
typescript
import { Buffer } from 'node:buffer'
interface IHDRData {
width: number
height: number
depth: number
colorType: number
compression: number
filter: number
interlace: number
}
const ihdr = parseIHDR(buffer, 8) // Skip PNG signature
console.log('Image dimensions:', ihdr.width, 'x', ihdr.height)
parseChunk(buffer: Buffer, offset: number): ChunkData
Parses a PNG chunk at the specified offset.
typescript
import { Buffer } from 'node:buffer'
interface ChunkData {
length: number
type: string
data: Buffer
crc: number
}
const chunk = parseChunk(buffer, offset)
console.log('Chunk type:', chunk.type)
parsePalette(buffer: Buffer): number[]
Parses the PLTE chunk into a color palette.
typescript
import { Buffer } from 'node:buffer'
const palette = parsePalette(buffer)
// palette is an array of RGB values
parseTransparency(buffer: Buffer, colorType: number): number[]
Parses the tRNS chunk for transparency information.
typescript
import { Buffer } from 'node:buffer'
const transparency = parseTransparency(buffer, COLOR_TYPES.PALETTE)
parseGamma(buffer: Buffer): number
Parses the gAMA chunk for gamma information.
typescript
import { Buffer } from 'node:buffer'
const gamma = parseGamma(buffer)
console.log('Image gamma:', gamma)
Example Usage
Custom PNG Parser
typescript
import { Buffer } from 'node:buffer'
import {
parseChunk,
parseGamma,
parseIHDR,
parsePalette,
parseSignature,
parseTransparency
} from 'pngx/parser'
function parsePNG(buffer: Buffer) {
// Check PNG signature
if (!parseSignature(buffer)) {
throw new Error('Invalid PNG file')
}
// Parse IHDR
const ihdr = parseIHDR(buffer, 8)
console.log('Image dimensions:', ihdr.width, 'x', ihdr.height)
console.log('Color type:', ihdr.colorType)
console.log('Bit depth:', ihdr.depth)
// Parse chunks
let offset = 33 // After IHDR
const chunks: ChunkData[] = []
while (offset < buffer.length) {
const chunk = parseChunk(buffer, offset)
chunks.push(chunk)
// Handle specific chunks
switch (chunk.type) {
case 'PLTE':
const palette = parsePalette(chunk.data)
console.log('Palette size:', palette.length / 3)
break
case 'tRNS':
const transparency = parseTransparency(chunk.data, ihdr.colorType)
console.log('Transparency data:', transparency)
break
case 'gAMA':
const gamma = parseGamma(chunk.data)
console.log('Gamma:', gamma)
break
}
offset += 12 + chunk.length // 12 = 4 (length) + 4 (type) + 4 (crc)
}
return {
ihdr,
chunks
}
}
Reading Image Data
typescript
import { Buffer } from 'node:buffer'
function readImageData(buffer: Buffer, ihdr: IHDRData): Buffer {
const bytesPerPixel = getBytesPerPixel(ihdr.colorType, ihdr.depth)
const rowSize = ihdr.width * bytesPerPixel
const imageData = Buffer.alloc(ihdr.width * ihdr.height * bytesPerPixel)
let offset = 0
let row = 0
while (row < ihdr.height) {
const filterType = buffer[offset++]
const rowData = buffer.slice(offset, offset + rowSize)
// Apply filter
const filteredData = applyFilter(rowData, filterType, bytesPerPixel)
// Copy to image data
filteredData.copy(imageData, row * rowSize)
offset += rowSize
row++
}
return imageData
}
function getBytesPerPixel(colorType: number, depth: number): number {
switch (colorType) {
case COLOR_TYPES.GRAYSCALE:
return depth === 16 ? 2 : 1
case COLOR_TYPES.RGB:
return depth === 16 ? 6 : 3
case COLOR_TYPES.PALETTE:
return 1
case COLOR_TYPES.GRAYSCALE_ALPHA:
return depth === 16 ? 4 : 2
case COLOR_TYPES.RGBA:
return depth === 16 ? 8 : 4
default:
throw new Error('Invalid color type')
}
}
Best Practices
- Error Handling: Always check for valid PNG signature
- Memory Management: Be careful with buffer allocations
- Chunk Order: Respect PNG chunk ordering rules
- CRC Checking: Verify chunk CRCs for data integrity
Performance Considerations
- Buffer Slices: Use buffer slices instead of copying
- Memory Usage: Be mindful of memory usage with large images
- Streaming: Consider streaming for large files
- Chunk Processing: Process chunks as they are read
Next Steps
- Learn about the Packer
- Explore the PNG Class
- Check out Advanced Topics