Skip to content

Buffer Management ​

The PNG library uses efficient buffer management techniques for handling image data. This guide covers advanced buffer operations and best practices.

Buffer Structure ​

PNG images are stored in a buffer with the following characteristics:

  • Each pixel takes 4 bytes (RGBA format)
  • Data is stored in a linear buffer
  • Buffer size = width height 4 bytes
typescript
import { PNG } from 'pngx'

const png = new PNG({
  width: 100,
  height: 100
})

// Buffer size = 100 * 100 * 4 = 40000 bytes
console.log(png.data.length) // 40000

Pixel Access ​

Direct Buffer Access ​

typescript
// Get pixel at (x, y)
function getPixel(x: number, y: number): [number, number, number, number] {
  const idx = (y * png.width + x) * 4
  return [
    png.data[idx], // Red
    png.data[idx + 1], // Green
    png.data[idx + 2], // Blue
    png.data[idx + 3] // Alpha
  ]
}

// Set pixel at (x, y)
function setPixel(x: number, y: number, r: number, g: number, b: number, a: number): void {
  const idx = (y * png.width + x) * 4
  png.data[idx] = r
  png.data[idx + 1] = g
  png.data[idx + 2] = b
  png.data[idx + 3] = a
}

Bit Depth Handling ​

The library supports various bit depths (1, 2, 4, 8, 16 bits per channel):

typescript
// Example: Working with 16-bit depth
const png = new PNG({
  width: 100,
  height: 100,
  depth: 16 // 16 bits per channel
})

// Access 16-bit values
function getPixel16Bit(x: number, y: number): [number, number, number, number] {
  const idx = (y * png.width + x) * 8 // 8 bytes per pixel (4 channels * 2 bytes)
  return [
    png.data.readUInt16BE(idx), // Red
    png.data.readUInt16BE(idx + 2), // Green
    png.data.readUInt16BE(idx + 4), // Blue
    png.data.readUInt16BE(idx + 6) // Alpha
  ]
}

Buffer Operations ​

Copying Regions ​

typescript
// Copy a region using BitBlt
png.bitblt(
  destinationImage,
  srcX,
  srcY,
  width,
  height,
  deltaX,
  deltaY
)

Buffer Allocation ​

typescript
// Create a new buffer with specific size
const buffer = Buffer.alloc(width * height * 4)

// Create a buffer from existing data
const buffer = Buffer.from(existingData)

Performance Considerations ​

  1. Buffer Reuse: Reuse buffers when possible to reduce memory allocation
  2. Direct Access: Use direct buffer access for better performance
  3. TypedArrays: Consider using TypedArrays for better performance with large images
  4. Memory Alignment: Keep memory alignment in mind for optimal performance

Best Practices ​

  1. Bounds Checking: Always check buffer bounds before access
  2. Memory Management: Free buffers when no longer needed
  3. Error Handling: Handle buffer-related errors appropriately
  4. Type Safety: Use TypeScript types for buffer operations

Example: Efficient Buffer Processing ​

typescript
import { PNG } from 'pngx'

function processImageEfficiently(png: PNG): void {
  // Get the buffer
  const buffer = png.data

  // Process in chunks for better performance
  const chunkSize = 1024 * 4 // Process 1024 pixels at a time
  for (let i = 0; i < buffer.length; i += chunkSize) {
    const chunk = buffer.slice(i, i + chunkSize)

    // Process chunk
    for (let j = 0; j < chunk.length; j += 4) {
      // Example: Convert to grayscale
      const gray = (chunk[j] + chunk[j + 1] + chunk[j + 2]) / 3
      chunk[j] = gray // Red
      chunk[j + 1] = gray // Green
      chunk[j + 2] = gray // Blue
      // Alpha remains unchanged
    }
  }
}

Next Steps ​