Message ID | Z/t0DbvYXAjjRmVB@duo.ucw.cz |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
Hi Pavel, Thank you for the patch. On Sun, Apr 13, 2025 at 10:21:33AM +0200, Pavel Machek wrote: > swIsp with libcamera produces ARGB data by default, which needs > conversion during write. Implement it and similar conversions. > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp > index 368de8bf..ddbd3263 100644 > --- a/src/apps/common/ppm_writer.cpp > +++ b/src/apps/common/ppm_writer.cpp > @@ -20,8 +20,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 (" > + int r_pos, g_pos, b_pos, bpp; > + switch (config.pixelFormat) { > + case libcamera::formats::R8: > + r_pos = 0; > + g_pos = 0; > + b_pos = 0; > + bpp = 1; > + break; > + case libcamera::formats::RGB888: > + r_pos = 2; > + g_pos = 1; > + b_pos = 0; > + bpp = 3; > + break; > + case libcamera::formats::BGR888: > + r_pos = 0; > + g_pos = 1; > + b_pos = 2; > + bpp = 3; > + break; > + case libcamera::formats::ARGB8888: > + case libcamera::formats::XRGB8888: > + r_pos = 2; > + g_pos = 1; > + b_pos = 0; > + bpp = 4; > + break; > + case libcamera::formats::RGBA8888: > + case libcamera::formats::RGBX8888: > + r_pos = 3; > + g_pos = 2; > + b_pos = 1; > + bpp = 4; > + break; > + case libcamera::formats::ABGR8888: > + case libcamera::formats::XBGR8888: > + r_pos = 0; > + g_pos = 1; > + b_pos = 2; > + bpp = 4; > + break; > + case libcamera::formats::BGRA8888: > + case libcamera::formats::BGRX8888: > + r_pos = 1; > + g_pos = 2; > + b_pos = 3; > + bpp = 4; > + break; > + default: > + std::cerr << "Only RGB output pixel formats are supported (" > << config.pixelFormat << " requested)" << std::endl; > return -EINVAL; > } > @@ -41,14 +89,35 @@ int PPMWriter::write(const char *filename, > } > > 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); > + // Output without any conversion will be slightly faster We use C-style coments. > + if (config.pixelFormat == formats::BGR888) { > + 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 -EIO; > + } > + } > + return 0; > + } > + > + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 > + // Create a temporary buffer to hold one output row. > + std::vector<char> rowBuffer(rowLength); > + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); > + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { > + for (unsigned int x = 0; x < config.size.width; x++) { > + const uint8_t *pixel = row + x * bpp; > + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); > + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); > + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); > + } I'm curious, what's the performance impact of the conversion ? > + output.write(rowBuffer.data(), rowLength); > if (!output) { > std::cerr << "Failed to write image data at row " << y << std::endl; > return -EIO; > } > } > - > return 0; > } >
Hi! > Thank you for the patch. > > On Sun, Apr 13, 2025 at 10:21:33AM +0200, Pavel Machek wrote: > > swIsp with libcamera produces ARGB data by default, which needs > > conversion during write. Implement it and similar conversions. > > > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > > + case libcamera::formats::BGRA8888: > > + case libcamera::formats::BGRX8888: > > + r_pos = 1; > > + g_pos = 2; > > + b_pos = 3; > > + bpp = 4; > > + break; > > + default: > > + std::cerr << "Only RGB output pixel formats are supported (" > > << config.pixelFormat << " requested)" << std::endl; > > return -EINVAL; > > } > > @@ -41,14 +89,35 @@ int PPMWriter::write(const char *filename, > > } > > > > 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); > > + // Output without any conversion will be slightly faster > > We use C-style coments. Can you adjust that while applying? > > + if (config.pixelFormat == formats::BGR888) { > > + 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 -EIO; > > + } > > + } > > + return 0; > > + } > > + > > + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 > > + // Create a temporary buffer to hold one output row. > > + std::vector<char> rowBuffer(rowLength); > > + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); > > + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { > > + for (unsigned int x = 0; x < config.size.width; x++) { > > + const uint8_t *pixel = row + x * bpp; > > + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); > > + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); > > + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); > > + } > > I'm curious, what's the performance impact of the conversion ? I hacked Librem 5 a lot to get libcamera working, and previous config was not working at all, so ... I can't easily benchmark that. So, this one is not free as it does one more copy of image data. Compared to swIsp, it is going to be lost in the noise. There's easy optimalization in the original case not to do writes per row if width == stride. There are other options, but they will complicate the code. Best regards, Pavel
Hi Pavel, On Sat, Apr 19, 2025 at 12:05:34PM +0200, Pavel Machek wrote: > Hi! > > > Thank you for the patch. > > > > On Sun, Apr 13, 2025 at 10:21:33AM +0200, Pavel Machek wrote: > > > swIsp with libcamera produces ARGB data by default, which needs > > > conversion during write. Implement it and similar conversions. > > > > > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > > > > + case libcamera::formats::BGRA8888: > > > + case libcamera::formats::BGRX8888: > > > + r_pos = 1; > > > + g_pos = 2; > > > + b_pos = 3; > > > + bpp = 4; > > > + break; > > > + default: > > > + std::cerr << "Only RGB output pixel formats are supported (" > > > << config.pixelFormat << " requested)" << std::endl; > > > return -EINVAL; > > > } > > > @@ -41,14 +89,35 @@ int PPMWriter::write(const char *filename, > > > } > > > > > > 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); > > > + // Output without any conversion will be slightly faster > > > > We use C-style coments. > > Can you adjust that while applying? If that's the only change required, yes. > > > + if (config.pixelFormat == formats::BGR888) { > > > + 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 -EIO; > > > + } > > > + } > > > + return 0; > > > + } > > > + > > > + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 > > > + // Create a temporary buffer to hold one output row. > > > + std::vector<char> rowBuffer(rowLength); > > > + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); > > > + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { > > > + for (unsigned int x = 0; x < config.size.width; x++) { > > > + const uint8_t *pixel = row + x * bpp; > > > + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); > > > + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); > > > + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); > > > + } > > > > I'm curious, what's the performance impact of the conversion ? > > I hacked Librem 5 a lot to get libcamera working, and previous config > was not working at all, so ... I can't easily benchmark that. > > So, this one is not free as it does one more copy of image > data. Compared to swIsp, it is going to be lost in the noise. > > There's easy optimalization in the original case not to do writes per > row if width == stride. There are other options, but they will > complicate the code. I wonder if it would make sense for the soft ISP to support other RGB formats. CC'ing Milan and Hans to get their opinion.
Hi! > > > > swIsp with libcamera produces ARGB data by default, which needs > > > > conversion during write. Implement it and similar conversions. > > > > > > > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > > > > - 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); > > > > + // Output without any conversion will be slightly faster > > > > > > We use C-style coments. > > > > Can you adjust that while applying? > > If that's the only change required, yes. Thanks! > > > > + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 > > > > + // Create a temporary buffer to hold one output row. > > > > + std::vector<char> rowBuffer(rowLength); > > > > + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); > > > > + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { > > > > + for (unsigned int x = 0; x < config.size.width; x++) { > > > > + const uint8_t *pixel = row + x * bpp; > > > > + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); > > > > + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); > > > > + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); > > > > + } > > > > > > I'm curious, what's the performance impact of the conversion ? > > > > I hacked Librem 5 a lot to get libcamera working, and previous config > > was not working at all, so ... I can't easily benchmark that. > > > > So, this one is not free as it does one more copy of image > > data. Compared to swIsp, it is going to be lost in the noise. > > > > There's easy optimalization in the original case not to do writes per > > row if width == stride. There are other options, but they will > > complicate the code. > > I wonder if it would make sense for the soft ISP to support other RGB > formats. CC'ing Milan and Hans to get their opinion. From looking at the code, soft ISP seemed to be able to do other stuff than ARGB, but for some reason ARGB is selected by default, and I guess I can get better performance by forcing right format. Conversion will still be useful for when hardware produces some other permutation than BGR. Best regards, Pavel
Hi 2025. 04. 13. 10:21 keltezéssel, Pavel Machek írta: > swIsp with libcamera produces ARGB data by default, which needs > conversion during write. Implement it and similar conversions. > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp > index 368de8bf..ddbd3263 100644 > --- a/src/apps/common/ppm_writer.cpp > +++ b/src/apps/common/ppm_writer.cpp > @@ -20,8 +20,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 (" > + int r_pos, g_pos, b_pos, bpp; I think the "pos" variables should probably be camelCase. And please add an empty line after them. > + switch (config.pixelFormat) { > + case libcamera::formats::R8: > + r_pos = 0; > + g_pos = 0; > + b_pos = 0; > + bpp = 1; > + break; > + case libcamera::formats::RGB888: > + r_pos = 2; > + g_pos = 1; > + b_pos = 0; > + bpp = 3; > + break; > + case libcamera::formats::BGR888: > + r_pos = 0; > + g_pos = 1; > + b_pos = 2; > + bpp = 3; > + break; > + case libcamera::formats::ARGB8888: > + case libcamera::formats::XRGB8888: > + r_pos = 2; > + g_pos = 1; > + b_pos = 0; > + bpp = 4; > + break; > + case libcamera::formats::RGBA8888: > + case libcamera::formats::RGBX8888: > + r_pos = 3; > + g_pos = 2; > + b_pos = 1; > + bpp = 4; > + break; > + case libcamera::formats::ABGR8888: > + case libcamera::formats::XBGR8888: > + r_pos = 0; > + g_pos = 1; > + b_pos = 2; > + bpp = 4; > + break; > + case libcamera::formats::BGRA8888: > + case libcamera::formats::BGRX8888: > + r_pos = 1; > + g_pos = 2; > + b_pos = 3; > + bpp = 4; > + break; > + default: > + std::cerr << "Only RGB output pixel formats are supported (" > << config.pixelFormat << " requested)" << std::endl; > return -EINVAL; > } > @@ -41,14 +89,35 @@ int PPMWriter::write(const char *filename, > } > > 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); > + // Output without any conversion will be slightly faster > + if (config.pixelFormat == formats::BGR888) { > + 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 -EIO; > + } > + } > + return 0; > + } > + > + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 What is the reasoning for saving this into a separate variable here? But not above in the `BGR888` case? And not `config.size`? And why "should be >= width * 4"? > + // Create a temporary buffer to hold one output row. > + std::vector<char> rowBuffer(rowLength); > + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); > + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { > + for (unsigned int x = 0; x < config.size.width; x++) { > + const uint8_t *pixel = row + x * bpp; > + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); > + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); > + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); > + } Empty line here please. > + output.write(rowBuffer.data(), rowLength); > if (!output) { > std::cerr << "Failed to write image data at row " << y << std::endl; > return -EIO; > } > } > - Don't remove this. > return 0; > } > As for performance, I have briefly taken a look at the x86 assembly of the inner loop, it looks pretty acceptable to me. Apart from the fact that the vector's operator[] adds three branches because meson sets `_GLIBCXX_ASSERTIONS=1` unless `b_ndebug=true`. So that is not too good, but it is probably not too significant. But whether or not the soft ISP will support other formats, I think it's useful to support multiple formats here, even at the cost of conversion. Especially since PPM is not a container or such, so if the user explicitly selects it, I think they expect that anything coming out of the camera will be converted appropriately. In addition, it appears to me that qcam already has a sizable set of format conversions implemented. I wonder if those could be used somehow. Regards, Barnabás Pőcze
Hi Pavel, Pavel Machek <pavel@ucw.cz> writes: > Hi! > >> > > > swIsp with libcamera produces ARGB data by default, which needs >> > > > conversion during write. Implement it and similar conversions. >> > > > >> > > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > >> > > > - 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); >> > > > + // Output without any conversion will be slightly faster >> > > >> > > We use C-style coments. >> > >> > Can you adjust that while applying? >> >> If that's the only change required, yes. > > Thanks! > >> > > > + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 >> > > > + // Create a temporary buffer to hold one output row. >> > > > + std::vector<char> rowBuffer(rowLength); >> > > > + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); >> > > > + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { >> > > > + for (unsigned int x = 0; x < config.size.width; x++) { >> > > > + const uint8_t *pixel = row + x * bpp; >> > > > + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); >> > > > + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); >> > > > + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); >> > > > + } >> > > >> > > I'm curious, what's the performance impact of the conversion ? >> > >> > I hacked Librem 5 a lot to get libcamera working, and previous config >> > was not working at all, so ... I can't easily benchmark that. >> > >> > So, this one is not free as it does one more copy of image >> > data. Compared to swIsp, it is going to be lost in the noise. >> > >> > There's easy optimalization in the original case not to do writes per >> > row if width == stride. There are other options, but they will >> > complicate the code. >> >> I wonder if it would make sense for the soft ISP to support other RGB >> formats. CC'ing Milan and Hans to get their opinion. > > From looking at the code, soft ISP seemed to be able to do other stuff > than ARGB, but for some reason ARGB is selected by default, and I > guess I can get better performance by forcing right format. Software ISP debayering supports multiple output formats and `cam' can request a particular output format. For example, cam -c1 -C4 -s pixelformat=BGR888 -Ffoo#.ppm outputs the right thing. Example of requesting an output in a different format: cam -c1 -C4 -s pixelformat=ARGB8888 -Ffoo#.raw It works; is it a problem in your application? I don't mind enhancing the PPM writer but maybe you don't need the conversion there in the first place. > Conversion will still be useful for when hardware produces some other > permutation than BGR. > > Best regards, > Pavel
Hi! > > From looking at the code, soft ISP seemed to be able to do other stuff > > than ARGB, but for some reason ARGB is selected by default, and I > > guess I can get better performance by forcing right format. > > Software ISP debayering supports multiple output formats and `cam' can > request a particular output format. For example, > > cam -c1 -C4 -s pixelformat=BGR888 -Ffoo#.ppm > > outputs the right thing. Example of requesting an output in a different > format: > > cam -c1 -C4 -s pixelformat=ARGB8888 -Ffoo#.raw > > It works; is it a problem in your application? Well, problem is that if you do something like "cam -c1 -Ffoo#.ppm" it decides to use ARGB8888 and fails writing into ppm. That is not great. > I don't mind enhancing the PPM writer but maybe you don't need the > conversion there in the first place. Agreed, I don't need it. People with hardware outputting RGB888 or ARGB variants might like it. Best regards, Pavel
Hi! > > swIsp with libcamera produces ARGB data by default, which needs > > conversion during write. Implement it and similar conversions. > > Signed-off-by: Pavel Machek <pavel@ucw.cz> > > > > diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp > > index 368de8bf..ddbd3263 100644 > > --- a/src/apps/common/ppm_writer.cpp > > +++ b/src/apps/common/ppm_writer.cpp > > @@ -20,8 +20,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 (" > > + int r_pos, g_pos, b_pos, bpp; > > I think the "pos" variables should probably be camelCase. > And please add an empty line after them. ...thanks for comments, I can fix the style. Laurent, is there still interest in this patch? swIsp does not really need it, but some hardware might and tiff writer has similar conversions. > As for performance, I have briefly taken a look at the x86 assembly of the inner > loop, it looks pretty acceptable to me. Apart from the fact that the vector's > operator[] adds three branches because meson sets `_GLIBCXX_ASSERTIONS=1` unless > `b_ndebug=true`. So that is not too good, but it is probably not too significant. Well, I strongly suspect reducing ammount of write syscalls (writev) would help significantly. But that's already problem with existing code and would make code more complex. > But whether or not the soft ISP will support other formats, I think it's useful to > support multiple formats here, even at the cost of conversion. Especially since PPM > is not a container or such, so if the user explicitly selects it, I think they > expect that anything coming out of the camera will be converted appropriately. > > In addition, it appears to me that qcam already has a sizable set of format > conversions implemented. I wonder if those could be used somehow. Ok, someone let me know if I should clean this up and retry. Best regards, Pavel
diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp index 368de8bf..ddbd3263 100644 --- a/src/apps/common/ppm_writer.cpp +++ b/src/apps/common/ppm_writer.cpp @@ -20,8 +20,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 (" + int r_pos, g_pos, b_pos, bpp; + switch (config.pixelFormat) { + case libcamera::formats::R8: + r_pos = 0; + g_pos = 0; + b_pos = 0; + bpp = 1; + break; + case libcamera::formats::RGB888: + r_pos = 2; + g_pos = 1; + b_pos = 0; + bpp = 3; + break; + case libcamera::formats::BGR888: + r_pos = 0; + g_pos = 1; + b_pos = 2; + bpp = 3; + break; + case libcamera::formats::ARGB8888: + case libcamera::formats::XRGB8888: + r_pos = 2; + g_pos = 1; + b_pos = 0; + bpp = 4; + break; + case libcamera::formats::RGBA8888: + case libcamera::formats::RGBX8888: + r_pos = 3; + g_pos = 2; + b_pos = 1; + bpp = 4; + break; + case libcamera::formats::ABGR8888: + case libcamera::formats::XBGR8888: + r_pos = 0; + g_pos = 1; + b_pos = 2; + bpp = 4; + break; + case libcamera::formats::BGRA8888: + case libcamera::formats::BGRX8888: + r_pos = 1; + g_pos = 2; + b_pos = 3; + bpp = 4; + break; + default: + std::cerr << "Only RGB output pixel formats are supported (" << config.pixelFormat << " requested)" << std::endl; return -EINVAL; } @@ -41,14 +89,35 @@ int PPMWriter::write(const char *filename, } 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); + // Output without any conversion will be slightly faster + if (config.pixelFormat == formats::BGR888) { + 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 -EIO; + } + } + return 0; + } + + const unsigned int inputRowBytes = config.stride; // should be >= width * 4 + // Create a temporary buffer to hold one output row. + std::vector<char> rowBuffer(rowLength); + const uint8_t *row = reinterpret_cast<const uint8_t *>(data.data()); + for (unsigned int y = 0; y < config.size.height; y++, row += inputRowBytes) { + for (unsigned int x = 0; x < config.size.width; x++) { + const uint8_t *pixel = row + x * bpp; + rowBuffer[x * 3 + 0] = static_cast<char>(pixel[r_pos]); + rowBuffer[x * 3 + 1] = static_cast<char>(pixel[g_pos]); + rowBuffer[x * 3 + 2] = static_cast<char>(pixel[b_pos]); + } + output.write(rowBuffer.data(), rowLength); if (!output) { std::cerr << "Failed to write image data at row " << y << std::endl; return -EIO; } } - return 0; }
swIsp with libcamera produces ARGB data by default, which needs conversion during write. Implement it and similar conversions. Signed-off-by: Pavel Machek <pavel@ucw.cz>