diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp
index 368de8bf6..9ba0216a0 100644
--- a/src/apps/common/ppm_writer.cpp
+++ b/src/apps/common/ppm_writer.cpp
@@ -10,6 +10,8 @@
 #include <errno.h>
 #include <fstream>
 #include <iostream>
+#include <stddef.h>
+#include <vector>
 
 #include <libcamera/formats.h>
 #include <libcamera/pixel_format.h>
@@ -20,8 +22,56 @@ int PPMWriter::write(const char *filename,
 		     const StreamConfiguration &config,
 		     const Span<uint8_t> &data)
 {
-	if (config.pixelFormat != formats::BGR888) {
-		std::cerr << "Only BGR888 output pixel format is supported ("
+	size_t rPos, gPos, bPos, bytesPerPixel;
+	switch (config.pixelFormat) {
+	case libcamera::formats::R8:
+		rPos = 0;
+		gPos = 0;
+		bPos = 0;
+		bytesPerPixel = 1;
+		break;
+	case libcamera::formats::RGB888:
+		rPos = 2;
+		gPos = 1;
+		bPos = 0;
+		bytesPerPixel = 3;
+		break;
+	case libcamera::formats::BGR888:
+		rPos = 0;
+		gPos = 1;
+		bPos = 2;
+		bytesPerPixel = 3;
+		break;
+	case libcamera::formats::ARGB8888:
+	case libcamera::formats::XRGB8888:
+		rPos = 2;
+		gPos = 1;
+		bPos = 0;
+		bytesPerPixel = 4;
+		break;
+	case libcamera::formats::RGBA8888:
+	case libcamera::formats::RGBX8888:
+		rPos = 3;
+		gPos = 2;
+		bPos = 1;
+		bytesPerPixel = 4;
+		break;
+	case libcamera::formats::ABGR8888:
+	case libcamera::formats::XBGR8888:
+		rPos = 0;
+		gPos = 1;
+		bPos = 2;
+		bytesPerPixel = 4;
+		break;
+	case libcamera::formats::BGRA8888:
+	case libcamera::formats::BGRX8888:
+		rPos = 1;
+		gPos = 2;
+		bPos = 3;
+		bytesPerPixel = 4;
+		break;
+	default:
+		std::cerr << "Only RGB output pixel formats are supported ("
 			  << config.pixelFormat << " requested)" << std::endl;
 		return -EINVAL;
 	}
@@ -42,8 +92,18 @@ int PPMWriter::write(const char *filename,
 
 	const unsigned int rowLength = config.size.width * 3;
 	const char *row = reinterpret_cast<const char *>(data.data());
+	const bool transform = config.pixelFormat != formats::BGR888;
+	std::vector<char> transformedRow(transform ? rowLength : 0);
+
 	for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) {
-		output.write(row, rowLength);
+		if (transform)
+			for (unsigned int x = 0; x < config.size.width; x++) {
+				transformedRow[x * 3] = row[x * bytesPerPixel + rPos];
+				transformedRow[x * 3 + 1] = row[x * bytesPerPixel + gPos];
+				transformedRow[x * 3 + 2] = row[x * bytesPerPixel + bPos];
+			}
+
+		output.write((transform ? transformedRow.data() : row), rowLength);
 		if (!output) {
 			std::cerr << "Failed to write image data at row " << y << std::endl;
 			return -EIO;
