Message ID | 20240322195032.1803017-2-mzamazal@redhat.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Milan, Thank you for the patch. On Fri, Mar 22, 2024 at 08:50:32PM +0100, Milan Zamazal wrote: > When file output is requested from cam app, it simply dumps the processed data > and it must be converted to a readable image format manually. Let's add support > for PPM output file format to make files produced by cam directly readable by > image display and processing software. > > For now, only BGR888 output format, which is the simplest one to use, is > supported but nothing prevents adding support for other output formats if > needed. Nevertheless, they would typically need byte reordering or other > conversions for PPM file format. It may be better to find a way to dump the > image data in other output formats directly using some of the already existing > file formats or raw file format converters. > > Signed-off-by: Milan Zamazal <mzamazal@redhat.com> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > src/apps/cam/file_sink.cpp | 11 +++++++ > src/apps/cam/main.cpp | 2 ++ > src/apps/common/meson.build | 1 + > src/apps/common/ppm_writer.cpp | 53 ++++++++++++++++++++++++++++++++++ > src/apps/common/ppm_writer.h | 20 +++++++++++++ > 5 files changed, 87 insertions(+) > create mode 100644 src/apps/common/ppm_writer.cpp > create mode 100644 src/apps/common/ppm_writer.h > > diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp > index dca350c4..906b50e6 100644 > --- a/src/apps/cam/file_sink.cpp > +++ b/src/apps/cam/file_sink.cpp > @@ -17,6 +17,7 @@ > > #include "../common/dng_writer.h" > #include "../common/image.h" > +#include "../common/ppm_writer.h" > > #include "file_sink.h" > > @@ -76,6 +77,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, > #ifdef HAVE_TIFF > bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos; > #endif /* HAVE_TIFF */ > + bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos; > > if (filename.empty() || filename.back() == '/') > filename += "frame-#.bin"; > @@ -102,6 +104,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, > return; > } > #endif /* HAVE_TIFF */ > + if (ppm) { > + ret = PPMWriter::write(filename.c_str(), stream->configuration(), > + image->data(0)); > + if (ret < 0) > + std::cerr << "failed to write PPM file `" << filename > + << "'" << std::endl; > + > + return; > + } > > fd = open(filename.c_str(), O_CREAT | O_WRONLY | > (pos == std::string::npos ? O_APPEND : O_TRUNC), > diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp > index 179cc376..1aabee01 100644 > --- a/src/apps/cam/main.cpp > +++ b/src/apps/cam/main.cpp > @@ -154,6 +154,8 @@ int CamApp::parseOptions(int argc, char *argv[]) > "If the file name ends with '.dng', then the frame will be written to\n" > "the output file(s) in DNG format.\n" > #endif > + "If the file name ends with '.ppm', then the frame will be written to\n" > + "the output file(s) in PPM format.\n" > "The default file name is 'frame-#.bin'.", > "file", ArgumentOptional, "filename", false, > OptCamera); > diff --git a/src/apps/common/meson.build b/src/apps/common/meson.build > index 479326cd..5b683390 100644 > --- a/src/apps/common/meson.build > +++ b/src/apps/common/meson.build > @@ -3,6 +3,7 @@ > apps_sources = files([ > 'image.cpp', > 'options.cpp', > + 'ppm_writer.cpp', > 'stream_options.cpp', > ]) > > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp > new file mode 100644 > index 00000000..04a49499 > --- /dev/null > +++ b/src/apps/common/ppm_writer.cpp > @@ -0,0 +1,53 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024 Red Hat, Inc. > + * > + * ppm_writer.cpp - PPM writer > + */ > + > +#include <fstream> > +#include <iostream> > + > +#include <libcamera/formats.h> > +#include <libcamera/pixel_format.h> > + > +#include "ppm_writer.h" > + > +using namespace libcamera; > + > +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 (" > + << config.pixelFormat << " requested)" << std::endl; > + return -EINVAL; > + } > + > + std::ofstream output(filename, std::ios::binary); > + if (!output) { > + std::cerr << "Failed to open ppm file: " << filename << std::endl; > + return -EINVAL; > + } > + > + output << "P6" << std::endl > + << config.size.width << " " << config.size.height << std::endl > + << "255" << std::endl; > + if (!output) { > + std::cerr << "Failed to write the file header" << std::endl; > + return -EINVAL; > + } > + > + const unsigned int rowLength = config.size.width * 3; > + const char *row = reinterpret_cast<const char *>(data.data()); > + for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) { > + output.write(row, rowLength); > + if (!output) { > + std::cerr << "Failed to write image data at row " << y << std::endl; > + return -EINVAL; > + } > + } > + > + return 0; > +} > diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h > new file mode 100644 > index 00000000..4c38f5ce > --- /dev/null > +++ b/src/apps/common/ppm_writer.h > @@ -0,0 +1,20 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Red Hat, Inc. > + * > + * ppm_writer.h - PPM writer > + */ > + > +#pragma once > + > +#include <libcamera/base/span.h> > + > +#include <libcamera/stream.h> > + > +class PPMWriter > +{ > +public: > + static int write(const char *filename, > + const libcamera::StreamConfiguration &config, > + const libcamera::Span<uint8_t> &data); > +};
Actually, On Thu, Mar 28, 2024 at 03:06:23AM +0200, Laurent Pinchart wrote: > On Fri, Mar 22, 2024 at 08:50:32PM +0100, Milan Zamazal wrote: > > When file output is requested from cam app, it simply dumps the processed data > > and it must be converted to a readable image format manually. Let's add support > > for PPM output file format to make files produced by cam directly readable by > > image display and processing software. > > > > For now, only BGR888 output format, which is the simplest one to use, is > > supported but nothing prevents adding support for other output formats if > > needed. Nevertheless, they would typically need byte reordering or other > > conversions for PPM file format. It may be better to find a way to dump the > > image data in other output formats directly using some of the already existing > > file formats or raw file format converters. > > > > Signed-off-by: Milan Zamazal <mzamazal@redhat.com> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > > --- > > src/apps/cam/file_sink.cpp | 11 +++++++ > > src/apps/cam/main.cpp | 2 ++ > > src/apps/common/meson.build | 1 + > > src/apps/common/ppm_writer.cpp | 53 ++++++++++++++++++++++++++++++++++ > > src/apps/common/ppm_writer.h | 20 +++++++++++++ > > 5 files changed, 87 insertions(+) > > create mode 100644 src/apps/common/ppm_writer.cpp > > create mode 100644 src/apps/common/ppm_writer.h > > > > diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp > > index dca350c4..906b50e6 100644 > > --- a/src/apps/cam/file_sink.cpp > > +++ b/src/apps/cam/file_sink.cpp > > @@ -17,6 +17,7 @@ > > > > #include "../common/dng_writer.h" > > #include "../common/image.h" > > +#include "../common/ppm_writer.h" > > > > #include "file_sink.h" > > > > @@ -76,6 +77,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, > > #ifdef HAVE_TIFF > > bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos; > > #endif /* HAVE_TIFF */ > > + bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos; > > > > if (filename.empty() || filename.back() == '/') > > filename += "frame-#.bin"; > > @@ -102,6 +104,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, > > return; > > } > > #endif /* HAVE_TIFF */ > > + if (ppm) { > > + ret = PPMWriter::write(filename.c_str(), stream->configuration(), > > + image->data(0)); > > + if (ret < 0) > > + std::cerr << "failed to write PPM file `" << filename > > + << "'" << std::endl; > > + > > + return; > > + } > > > > fd = open(filename.c_str(), O_CREAT | O_WRONLY | > > (pos == std::string::npos ? O_APPEND : O_TRUNC), > > diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp > > index 179cc376..1aabee01 100644 > > --- a/src/apps/cam/main.cpp > > +++ b/src/apps/cam/main.cpp > > @@ -154,6 +154,8 @@ int CamApp::parseOptions(int argc, char *argv[]) > > "If the file name ends with '.dng', then the frame will be written to\n" > > "the output file(s) in DNG format.\n" > > #endif > > + "If the file name ends with '.ppm', then the frame will be written to\n" > > + "the output file(s) in PPM format.\n" > > "The default file name is 'frame-#.bin'.", > > "file", ArgumentOptional, "filename", false, > > OptCamera); > > diff --git a/src/apps/common/meson.build b/src/apps/common/meson.build > > index 479326cd..5b683390 100644 > > --- a/src/apps/common/meson.build > > +++ b/src/apps/common/meson.build > > @@ -3,6 +3,7 @@ > > apps_sources = files([ > > 'image.cpp', > > 'options.cpp', > > + 'ppm_writer.cpp', > > 'stream_options.cpp', > > ]) > > > > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp > > new file mode 100644 > > index 00000000..04a49499 > > --- /dev/null > > +++ b/src/apps/common/ppm_writer.cpp > > @@ -0,0 +1,53 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2024 Red Hat, Inc. > > + * > > + * ppm_writer.cpp - PPM writer > > + */ > > + > > +#include <fstream> > > +#include <iostream> > > + > > +#include <libcamera/formats.h> > > +#include <libcamera/pixel_format.h> > > + > > +#include "ppm_writer.h" checkstyle.py told me that this should go to the top. I'll fix when applying. > > + > > +using namespace libcamera; > > + > > +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 (" > > + << config.pixelFormat << " requested)" << std::endl; > > + return -EINVAL; > > + } > > + > > + std::ofstream output(filename, std::ios::binary); > > + if (!output) { > > + std::cerr << "Failed to open ppm file: " << filename << std::endl; > > + return -EINVAL; > > + } > > + > > + output << "P6" << std::endl > > + << config.size.width << " " << config.size.height << std::endl > > + << "255" << std::endl; > > + if (!output) { > > + std::cerr << "Failed to write the file header" << std::endl; > > + return -EINVAL; > > + } > > + > > + const unsigned int rowLength = config.size.width * 3; > > + const char *row = reinterpret_cast<const char *>(data.data()); > > + for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) { > > + output.write(row, rowLength); > > + if (!output) { > > + std::cerr << "Failed to write image data at row " << y << std::endl; > > + return -EINVAL; > > + } > > + } > > + > > + return 0; > > +} > > diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h > > new file mode 100644 > > index 00000000..4c38f5ce > > --- /dev/null > > +++ b/src/apps/common/ppm_writer.h > > @@ -0,0 +1,20 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2024, Red Hat, Inc. > > + * > > + * ppm_writer.h - PPM writer > > + */ > > + > > +#pragma once > > + > > +#include <libcamera/base/span.h> > > + > > +#include <libcamera/stream.h> > > + > > +class PPMWriter > > +{ > > +public: > > + static int write(const char *filename, > > + const libcamera::StreamConfiguration &config, > > + const libcamera::Span<uint8_t> &data); > > +};
Laurent Pinchart <laurent.pinchart@ideasonboard.com> writes: > Actually, > > On Thu, Mar 28, 2024 at 03:06:23AM +0200, Laurent Pinchart wrote: >> On Fri, Mar 22, 2024 at 08:50:32PM +0100, Milan Zamazal wrote: >> > When file output is requested from cam app, it simply dumps the processed data >> > and it must be converted to a readable image format manually. Let's add support >> > for PPM output file format to make files produced by cam directly readable by >> > image display and processing software. >> > >> > For now, only BGR888 output format, which is the simplest one to use, is >> > supported but nothing prevents adding support for other output formats if >> > needed. Nevertheless, they would typically need byte reordering or other >> > conversions for PPM file format. It may be better to find a way to dump the >> > image data in other output formats directly using some of the already existing >> > file formats or raw file format converters. >> > >> > Signed-off-by: Milan Zamazal <mzamazal@redhat.com> >> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> >> >> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> >> >> > --- >> > src/apps/cam/file_sink.cpp | 11 +++++++ >> > src/apps/cam/main.cpp | 2 ++ >> > src/apps/common/meson.build | 1 + >> > src/apps/common/ppm_writer.cpp | 53 ++++++++++++++++++++++++++++++++++ >> > src/apps/common/ppm_writer.h | 20 +++++++++++++ >> > 5 files changed, 87 insertions(+) >> > create mode 100644 src/apps/common/ppm_writer.cpp >> > create mode 100644 src/apps/common/ppm_writer.h >> > >> > diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp >> > index dca350c4..906b50e6 100644 >> > --- a/src/apps/cam/file_sink.cpp >> > +++ b/src/apps/cam/file_sink.cpp >> > @@ -17,6 +17,7 @@ >> > >> > #include "../common/dng_writer.h" >> > #include "../common/image.h" >> > +#include "../common/ppm_writer.h" >> > >> > #include "file_sink.h" >> > >> > @@ -76,6 +77,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, >> > #ifdef HAVE_TIFF >> > bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos; >> > #endif /* HAVE_TIFF */ >> > + bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos; >> > >> > if (filename.empty() || filename.back() == '/') >> > filename += "frame-#.bin"; >> > @@ -102,6 +104,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, >> > return; >> > } >> > #endif /* HAVE_TIFF */ >> > + if (ppm) { >> > + ret = PPMWriter::write(filename.c_str(), stream->configuration(), >> > + image->data(0)); >> > + if (ret < 0) >> > + std::cerr << "failed to write PPM file `" << filename >> > + << "'" << std::endl; >> > + >> > + return; >> > + } >> > >> > fd = open(filename.c_str(), O_CREAT | O_WRONLY | >> > (pos == std::string::npos ? O_APPEND : O_TRUNC), >> > diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp >> > index 179cc376..1aabee01 100644 >> > --- a/src/apps/cam/main.cpp >> > +++ b/src/apps/cam/main.cpp >> > @@ -154,6 +154,8 @@ int CamApp::parseOptions(int argc, char *argv[]) >> > "If the file name ends with '.dng', then the frame will be written to\n" >> > "the output file(s) in DNG format.\n" >> > #endif >> > + "If the file name ends with '.ppm', then the frame will be written to\n" >> > + "the output file(s) in PPM format.\n" >> > "The default file name is 'frame-#.bin'.", >> > "file", ArgumentOptional, "filename", false, >> > OptCamera); >> > diff --git a/src/apps/common/meson.build b/src/apps/common/meson.build >> > index 479326cd..5b683390 100644 >> > --- a/src/apps/common/meson.build >> > +++ b/src/apps/common/meson.build >> > @@ -3,6 +3,7 @@ >> > apps_sources = files([ >> > 'image.cpp', >> > 'options.cpp', >> > + 'ppm_writer.cpp', >> > 'stream_options.cpp', >> > ]) >> > >> > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp >> > new file mode 100644 >> > index 00000000..04a49499 >> > --- /dev/null >> > +++ b/src/apps/common/ppm_writer.cpp >> > @@ -0,0 +1,53 @@ >> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> > +/* >> > + * Copyright (C) 2024 Red Hat, Inc. >> > + * >> > + * ppm_writer.cpp - PPM writer >> > + */ >> > + >> > +#include <fstream> >> > +#include <iostream> >> > + >> > +#include <libcamera/formats.h> >> > +#include <libcamera/pixel_format.h> >> > + >> > +#include "ppm_writer.h" > > checkstyle.py told me that this should go to the top. I'll fix when > applying. Thanks! (And sorry for this omission.) >> > + >> > +using namespace libcamera; >> > + >> > +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 (" >> > + << config.pixelFormat << " requested)" << std::endl; >> > + return -EINVAL; >> > + } >> > + >> > + std::ofstream output(filename, std::ios::binary); >> > + if (!output) { >> > + std::cerr << "Failed to open ppm file: " << filename << std::endl; >> > + return -EINVAL; >> > + } >> > + >> > + output << "P6" << std::endl >> > + << config.size.width << " " << config.size.height << std::endl >> > + << "255" << std::endl; >> > + if (!output) { >> > + std::cerr << "Failed to write the file header" << std::endl; >> > + return -EINVAL; >> > + } >> > + >> > + const unsigned int rowLength = config.size.width * 3; >> > + const char *row = reinterpret_cast<const char *>(data.data()); >> > + for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) { >> > + output.write(row, rowLength); >> > + if (!output) { >> > + std::cerr << "Failed to write image data at row " << y << std::endl; >> > + return -EINVAL; >> > + } >> > + } >> > + >> > + return 0; >> > +} >> > diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h >> > new file mode 100644 >> > index 00000000..4c38f5ce >> > --- /dev/null >> > +++ b/src/apps/common/ppm_writer.h >> > @@ -0,0 +1,20 @@ >> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ >> > +/* >> > + * Copyright (C) 2024, Red Hat, Inc. >> > + * >> > + * ppm_writer.h - PPM writer >> > + */ >> > + >> > +#pragma once >> > + >> > +#include <libcamera/base/span.h> >> > + >> > +#include <libcamera/stream.h> >> > + >> > +class PPMWriter >> > +{ >> > +public: >> > + static int write(const char *filename, >> > + const libcamera::StreamConfiguration &config, >> > + const libcamera::Span<uint8_t> &data); >> > +};
diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp index dca350c4..906b50e6 100644 --- a/src/apps/cam/file_sink.cpp +++ b/src/apps/cam/file_sink.cpp @@ -17,6 +17,7 @@ #include "../common/dng_writer.h" #include "../common/image.h" +#include "../common/ppm_writer.h" #include "file_sink.h" @@ -76,6 +77,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, #ifdef HAVE_TIFF bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos; #endif /* HAVE_TIFF */ + bool ppm = filename.find(".ppm", filename.size() - 4) != std::string::npos; if (filename.empty() || filename.back() == '/') filename += "frame-#.bin"; @@ -102,6 +104,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, return; } #endif /* HAVE_TIFF */ + if (ppm) { + ret = PPMWriter::write(filename.c_str(), stream->configuration(), + image->data(0)); + if (ret < 0) + std::cerr << "failed to write PPM file `" << filename + << "'" << std::endl; + + return; + } fd = open(filename.c_str(), O_CREAT | O_WRONLY | (pos == std::string::npos ? O_APPEND : O_TRUNC), diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp index 179cc376..1aabee01 100644 --- a/src/apps/cam/main.cpp +++ b/src/apps/cam/main.cpp @@ -154,6 +154,8 @@ int CamApp::parseOptions(int argc, char *argv[]) "If the file name ends with '.dng', then the frame will be written to\n" "the output file(s) in DNG format.\n" #endif + "If the file name ends with '.ppm', then the frame will be written to\n" + "the output file(s) in PPM format.\n" "The default file name is 'frame-#.bin'.", "file", ArgumentOptional, "filename", false, OptCamera); diff --git a/src/apps/common/meson.build b/src/apps/common/meson.build index 479326cd..5b683390 100644 --- a/src/apps/common/meson.build +++ b/src/apps/common/meson.build @@ -3,6 +3,7 @@ apps_sources = files([ 'image.cpp', 'options.cpp', + 'ppm_writer.cpp', 'stream_options.cpp', ]) diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp new file mode 100644 index 00000000..04a49499 --- /dev/null +++ b/src/apps/common/ppm_writer.cpp @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, Inc. + * + * ppm_writer.cpp - PPM writer + */ + +#include <fstream> +#include <iostream> + +#include <libcamera/formats.h> +#include <libcamera/pixel_format.h> + +#include "ppm_writer.h" + +using namespace libcamera; + +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 (" + << config.pixelFormat << " requested)" << std::endl; + return -EINVAL; + } + + std::ofstream output(filename, std::ios::binary); + if (!output) { + std::cerr << "Failed to open ppm file: " << filename << std::endl; + return -EINVAL; + } + + output << "P6" << std::endl + << config.size.width << " " << config.size.height << std::endl + << "255" << std::endl; + if (!output) { + std::cerr << "Failed to write the file header" << std::endl; + return -EINVAL; + } + + const unsigned int rowLength = config.size.width * 3; + const char *row = reinterpret_cast<const char *>(data.data()); + for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) { + output.write(row, rowLength); + if (!output) { + std::cerr << "Failed to write image data at row " << y << std::endl; + return -EINVAL; + } + } + + return 0; +} diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h new file mode 100644 index 00000000..4c38f5ce --- /dev/null +++ b/src/apps/common/ppm_writer.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Red Hat, Inc. + * + * ppm_writer.h - PPM writer + */ + +#pragma once + +#include <libcamera/base/span.h> + +#include <libcamera/stream.h> + +class PPMWriter +{ +public: + static int write(const char *filename, + const libcamera::StreamConfiguration &config, + const libcamera::Span<uint8_t> &data); +};