libpng
bitmap to png
image encoding
fast image conversion
bitmap buffer

fast encode bitmap buffer to png using libpng

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Encoding a bitmap buffer to PNG with libpng is straightforward once the pixel layout is clear, but "fast" encoding depends on reducing copies, matching the correct color type, and not asking PNG compression to do more work than the use case needs. The real performance wins come from data layout and encoder settings, not from exotic tricks.

Know Your Input Buffer

Before calling libpng, you need to know:

  • image width and height
  • bytes per pixel
  • row stride
  • channel order such as RGB or RGBA

If the bitmap buffer is already tightly packed RGBA data, the write path is simpler and faster because you can point libpng directly at each row instead of repacking the image first.

A Minimal Encoding Example

This example writes an RGBA bitmap buffer to a PNG file:

c
1#include <png.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5int write_png(const char *filename,
6              const unsigned char *buffer,
7              int width,
8              int height,
9              int stride) {
10    FILE *fp = fopen(filename, "wb");
11    if (!fp) return 0;
12
13    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
14    if (!png) {
15        fclose(fp);
16        return 0;
17    }
18
19    png_infop info = png_create_info_struct(png);
20    if (!info) {
21        png_destroy_write_struct(&png, NULL);
22        fclose(fp);
23        return 0;
24    }
25
26    if (setjmp(png_jmpbuf(png))) {
27        png_destroy_write_struct(&png, &info);
28        fclose(fp);
29        return 0;
30    }
31
32    png_init_io(png, fp);
33    png_set_IHDR(png, info, width, height, 8,
34                 PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
35                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
36
37    png_write_info(png, info);
38
39    png_bytep *rows = malloc(sizeof(png_bytep) * height);
40    if (!rows) {
41        png_destroy_write_struct(&png, &info);
42        fclose(fp);
43        return 0;
44    }
45
46    for (int y = 0; y < height; ++y) {
47        rows[y] = (png_bytep)(buffer + y * stride);
48    }
49
50    png_write_image(png, rows);
51    png_write_end(png, NULL);
52
53    free(rows);
54    png_destroy_write_struct(&png, &info);
55    fclose(fp);
56    return 1;
57}

This is a solid baseline because it avoids per-pixel conversion inside the write loop.

Where Performance Usually Goes

PNG is lossless and compressed, so encoding speed is affected by both CPU compression work and memory traffic. Common bottlenecks are:

  • converting channel order before writing
  • building temporary row buffers repeatedly
  • using a very high compression level when you mainly care about speed

If the source format is already suitable for libpng, keep it that way and feed row pointers directly from the original buffer.

Compression Settings Matter

Higher compression reduces file size but usually increases CPU cost. If speed matters more than final size, lower the compression level:

c
png_set_compression_level(png, 1);

That often gives a better tradeoff for screenshots, frame capture, or server-side export pipelines where encoding latency matters more than squeezing out the smallest possible PNG.

Avoid Unnecessary Copies

Many "slow PNG encoder" complaints are really "slow preprocessing" complaints. If you convert from BGRA to RGBA into a second full image buffer, you have already paid a significant memory cost before libpng starts compressing.

The fastest path is usually:

  • keep one source buffer
  • map row pointers onto it
  • let libpng write directly from those rows

If channel swapping is required, use libpng transform helpers or perform one deliberate conversion step rather than repeatedly rebuilding rows.

Memory and Error Handling

libpng uses setjmp and longjmp for errors, so proper cleanup matters. Performance tuning is pointless if the encoder leaks memory or leaves files half-written on failure.

For repeated encoding workloads, reusing surrounding buffers and avoiding repeated allocation of large intermediate structures can also help. The goal is not just fast compression but predictable throughput.

Common Pitfalls

  • Misunderstanding the input buffer layout and writing the wrong color type.
  • Repacking the full image when row pointers to the original buffer would work.
  • Using maximum compression when latency matters more than output size.
  • Ignoring row stride and assuming rows are tightly packed when they are not.
  • Treating libpng as slow when the real bottleneck is an avoidable color-conversion step.

Summary

  • Fast PNG encoding with libpng starts with knowing the bitmap buffer layout exactly.
  • Direct row-pointer writing is usually faster than building extra image copies.
  • Compression level is an important speed-versus-size tradeoff.
  • Row stride and color format must match the IHDR settings.
  • Many performance issues come from preprocessing around libpng, not from libpng itself.

Course illustration
Course illustration

All Rights Reserved.