[{"id":4720,"web_url":"https://patchwork.libcamera.org/comment/4720/","msgid":"<20200502124134.GB5862@pendragon.ideasonboard.com>","date":"2020-05-02T12:41:34","subject":"Re: [libcamera-devel] [PATCH v4 2/3] qcam: Add DNGWriter","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nThank you for the patch.\n\nOn Sat, May 02, 2020 at 02:13:55PM +0200, Niklas Söderlund wrote:\n> Add an initial DNG file writer. The writer can only deal with a small\n> set of pixel formats. The generated file is consumable by standard\n> tools. The writer needs to be extended to write more metadata to the\n> generated file.\n> \n> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> * Changes since v2\n> - Mark input arg as const\n> - Fold getFormatInfo() into only caller\n> - Male DNGWriter::write() static and take filename as argument\n> - Make DNGWriter::write() take StreamConfiguraiton instead of Stream\n> - Optimize scanline loop\n> - Make libtiff dependecy optional\n> - Add 12 byte formats from Laurent\n> - Reformat table from Laurent\n> \n> * Changes since v3\n> - Make data and buffer arguments const for DNGWriter::write()\n> - Fix memory size of uint8_t scanline[].\n> ---\n>  src/qcam/dng_writer.cpp | 165 ++++++++++++++++++++++++++++++++++++++++\n>  src/qcam/dng_writer.h   |  28 +++++++\n>  src/qcam/meson.build    |  20 ++++-\n>  3 files changed, 210 insertions(+), 3 deletions(-)\n>  create mode 100644 src/qcam/dng_writer.cpp\n>  create mode 100644 src/qcam/dng_writer.h\n> \n> diff --git a/src/qcam/dng_writer.cpp b/src/qcam/dng_writer.cpp\n> new file mode 100644\n> index 0000000000000000..08078369529bb780\n> --- /dev/null\n> +++ b/src/qcam/dng_writer.cpp\n> @@ -0,0 +1,165 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.\n> + *\n> + * dng_writer.cpp - DNG writer\n> + */\n> +\n> +#include \"dng_writer.h\"\n> +\n> +#include <iostream>\n> +#include <map>\n> +\n> +#include <tiffio.h>\n> +\n> +using namespace libcamera;\n> +\n> +enum CFAPatternColour : uint8_t {\n> +\tCFAPatternRed = 0,\n> +\tCFAPatternGreen = 1,\n> +\tCFAPatternBlue = 2,\n> +};\n> +\n> +struct FormatInfo {\n> +\tuint8_t bitsPerSample;\n> +\tCFAPatternColour pattern[4];\n> +\tvoid (*packScanline)(void *, const void *, unsigned int);\n> +};\n> +\n> +void packScanlineSBGGR10P(void *output, const void *input, unsigned int width)\n> +{\n> +\tconst uint8_t *in = static_cast<const uint8_t *>(input);\n> +\tuint8_t *out = static_cast<uint8_t *>(output);\n> +\n> +\t/* \\todo Can this be made more efficient? */\n> +\tfor (unsigned int x = 0; x < width; x += 4) {\n> +\t\t*out++ = in[0];\n> +\t\t*out++ = (in[4] & 0x03) << 6 | in[1] >> 2;\n> +\t\t*out++ = (in[1] & 0x03) << 6 | (in[4] & 0x0c) << 2 | in[2] >> 4;\n> +\t\t*out++ = (in[2] & 0x0f) << 4 | (in[4] & 0x30) >> 2 | in[3] >> 6;\n> +\t\t*out++ = (in[3] & 0x3f) << 2 | (in[4] & 0xc0) >> 6;\n> +\t\tin += 5;\n> +\t}\n> +}\n> +\n> +void packScanlineSBGGR12P(void *output, const void *input, unsigned int width)\n> +{\n> +\tconst uint8_t *in = static_cast<const uint8_t *>(input);\n> +\tuint8_t *out = static_cast<uint8_t *>(output);\n> +\n> +\t/* \\todo: Can this be made more efficient? */\n> +\tfor (unsigned int i = 0; i < width; i += 2) {\n> +\t\t*out++ = in[0];\n> +\t\t*out++ = (in[2] & 0x0f) << 4 | in[1] >> 4;\n> +\t\t*out++ = (in[1] & 0x0f) << 4 | in[2] >> 4;\n> +\t\tin += 3;\n> +\t}\n> +}\n> +\n> +static const std::map<PixelFormat, FormatInfo> formatInfo = {\n> +\t{ PixelFormat(DRM_FORMAT_SBGGR10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 10,\n> +\t\t.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },\n> +\t\t.packScanline = packScanlineSBGGR10P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SGBRG10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 10,\n> +\t\t.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },\n> +\t\tpackScanlineSBGGR10P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SGRBG10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 10,\n> +\t\t.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },\n> +\t\t.packScanline = packScanlineSBGGR10P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SRGGB10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 10,\n> +\t\t.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },\n> +\t\t.packScanline = packScanlineSBGGR10P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SBGGR12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 12,\n> +\t\t.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },\n> +\t\t.packScanline = packScanlineSBGGR12P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SGBRG12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 12,\n> +\t\t.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },\n> +\t\t.packScanline = packScanlineSBGGR12P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SGRBG12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 12,\n> +\t\t.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },\n> +\t\t.packScanline = packScanlineSBGGR12P,\n> +\t} },\n> +\t{ PixelFormat(DRM_FORMAT_SRGGB12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> +\t\t.bitsPerSample = 12,\n> +\t\t.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },\n> +\t\t.packScanline = packScanlineSBGGR12P,\n> +\t} },\n> +};\n> +\n> +int DNGWriter::write(const char *filename, const Camera *camera,\n> +\t\t     const StreamConfiguration &config,\n> +\t\t     const FrameBuffer *buffer, const void *data)\n> +{\n> +\tconst auto it = formatInfo.find(config.pixelFormat);\n> +\tif (it == formatInfo.cend()) {\n> +\t\tstd::cerr << \"Unsupported pixel format\" << std::endl;\n> +\t\treturn -EINVAL;\n> +\t}\n> +\tconst FormatInfo *info = &it->second;\n> +\n> +\tTIFF *tif = TIFFOpen(filename, \"w\");\n> +\tif (!tif) {\n> +\t\tstd::cerr << \"Failed to open tiff file\" << std::endl;\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\tconst uint8_t version[] = { 1, 2, 0, 0 };\n> +\tconst uint16_t cfa_repeatpatterndim[] = { 2, 2 };\n> +\tTIFFSetField(tif, TIFFTAG_DNGVERSION, version);\n> +\tTIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, version);\n> +\tTIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);\n> +\tTIFFSetField(tif, TIFFTAG_IMAGEWIDTH, config.size.width);\n> +\tTIFFSetField(tif, TIFFTAG_IMAGELENGTH, config.size.height);\n> +\tTIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, info->bitsPerSample);\n> +\tTIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);\n> +\tTIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);\n> +\tTIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);\n> +\tTIFFSetField(tif, TIFFTAG_MAKE, \"libcamera\");\n> +\tTIFFSetField(tif, TIFFTAG_MODEL, camera->name().c_str());\n> +\tTIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, camera->name().c_str());\n> +\tTIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);\n> +\tTIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);\n> +\tTIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);\n> +\tTIFFSetField(tif, TIFFTAG_SOFTWARE, \"qcam\");\n> +\tTIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);\n> +\tTIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfa_repeatpatterndim);\n> +\tTIFFSetField(tif, TIFFTAG_CFAPATTERN, info->pattern);\n> +\tTIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, \"\\00\\01\\02\");\n> +\tTIFFSetField(tif, TIFFTAG_CFALAYOUT, 1);\n> +\n> +\t/* \\todo Add more EXIF fields to output. */\n> +\n> +\t/* Write RAW content. */\n> +\tconst uint8_t *row = static_cast<const uint8_t *>(data);\n> +\tuint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8];\n> +\tfor (unsigned int y = 0; y < config.size.height; y++) {\n> +\t\tinfo->packScanline(&scanline, row, config.size.width);\n> +\n> +\t\tif (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {\n> +\t\t\tstd::cerr << \"Failed to write scanline\" << std::endl;\n> +\t\t\tTIFFClose(tif);\n> +\t\t\treturn -EINVAL;\n> +\t\t}\n> +\n> +\t\trow += config.stride;\n> +\t}\n> +\n> +\tTIFFWriteDirectory(tif);\n> +\n> +\tTIFFClose(tif);\n> +\n> +\treturn 0;\n> +}\n> diff --git a/src/qcam/dng_writer.h b/src/qcam/dng_writer.h\n> new file mode 100644\n> index 0000000000000000..4dd904db4ce68c22\n> --- /dev/null\n> +++ b/src/qcam/dng_writer.h\n> @@ -0,0 +1,28 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.\n> + *\n> + * dng_writer.h - DNG writer\n> + */\n> +#ifndef __LIBCAMERA_DNG_WRITER_H__\n> +#define __LIBCAMERA_DNG_WRITER_H__\n> +\n> +#ifdef HAVE_TIFF\n> +#define HAVE_DNG\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/camera.h>\n> +#include <libcamera/stream.h>\n> +\n> +using namespace libcamera;\n> +\n> +class DNGWriter\n> +{\n> +public:\n> +\tstatic int write(const char *filename, const Camera *camera,\n> +\t\t\t const StreamConfiguration &config,\n> +\t\t\t const FrameBuffer *buffer, const void *data);\n\nA very const function :-)\n\n> +};\n\nBlank line ?\n\n> +#endif /* HAVE_DNG */\n\ns/HAVE_DNG/HAVE_TIFF/ here\n\nAnd you can keep my tag.\n\n> +\n> +#endif /* __LIBCAMERA_DNG_WRITER_H__ */\n> diff --git a/src/qcam/meson.build b/src/qcam/meson.build\n> index 895264be4a3388f4..949ef6149c8a187e 100644\n> --- a/src/qcam/meson.build\n> +++ b/src/qcam/meson.build\n> @@ -1,9 +1,9 @@\n>  qcam_sources = files([\n> +    '../cam/options.cpp',\n> +    '../cam/stream_options.cpp',\n>      'format_converter.cpp',\n>      'main.cpp',\n>      'main_window.cpp',\n> -    '../cam/options.cpp',\n> -    '../cam/stream_options.cpp',\n>      'viewfinder.cpp',\n>  ])\n>  \n> @@ -23,8 +23,22 @@ qt5_dep = dependency('qt5',\n>                       required : false)\n>  \n>  if qt5_dep.found()\n> +    qcam_deps = [\n> +        libcamera_dep,\n> +        qt5_dep,\n> +    ]\n> +\n>      qt5_cpp_args = [ '-DQT_NO_KEYWORDS' ]\n>  \n> +    tiff_dep = dependency('libtiff-4', required : false)\n> +    if tiff_dep.found()\n> +        qt5_cpp_args += [ '-DHAVE_TIFF' ]\n> +        qcam_deps += [ tiff_dep ]\n> +        qcam_sources += files([\n> +            'dng_writer.cpp',\n> +        ])\n> +    endif\n> +\n>      # gcc 9 introduced a deprecated-copy warning that is triggered by Qt until\n>      # Qt 5.13. clang 10 introduced the same warning, but detects more issues\n>      # that are not fixed in Qt yet. Disable the warning manually in both cases.\n> @@ -40,6 +54,6 @@ if qt5_dep.found()\n>  \n>      qcam  = executable('qcam', qcam_sources, resources,\n>                         install : true,\n> -                       dependencies : [libcamera_dep, qt5_dep],\n> +                       dependencies : qcam_deps,\n>                         cpp_args : qt5_cpp_args)\n>  endif","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4CE4C603F4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat,  2 May 2020 14:41:38 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B185B521;\n\tSat,  2 May 2020 14:41:37 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"AdeqJEQx\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1588423297;\n\tbh=LaA/8ITnnyz5JOt2Gv2YLXPiC2sLEZAm63mJj/4Sluw=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=AdeqJEQxObrQ2ZMfjqTizdk6gkqNGX92V08PdYNEuHG1UQdfiQYLVv+H5Po2yrn7O\n\tN6TXxo3D4yPx2srwtm2T4gJFDMpu/G27WppwS0pTwsFL3y5Z5zQ0iC6b197r2BsgMy\n\tdrVI3x08muMN3M2ltgwkqxojeGiZDsgZPi7NFJyQ=","Date":"Sat, 2 May 2020 15:41:34 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200502124134.GB5862@pendragon.ideasonboard.com>","References":"<20200502121356.1045727-1-niklas.soderlund@ragnatech.se>\n\t<20200502121356.1045727-3-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20200502121356.1045727-3-niklas.soderlund@ragnatech.se>","Subject":"Re: [libcamera-devel] [PATCH v4 2/3] qcam: Add DNGWriter","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 02 May 2020 12:41:38 -0000"}},{"id":4721,"web_url":"https://patchwork.libcamera.org/comment/4721/","msgid":"<20200502125033.GA2890466@oden.dyn.berto.se>","date":"2020-05-02T12:50:33","subject":"Re: [libcamera-devel] [PATCH v4 2/3] qcam: Add DNGWriter","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your comments.\n\nI have taken them in and merged this series.\n\nOn 2020-05-02 15:41:34 +0300, Laurent Pinchart wrote:\n> Hi Niklas,\n> \n> Thank you for the patch.\n> \n> On Sat, May 02, 2020 at 02:13:55PM +0200, Niklas Söderlund wrote:\n> > Add an initial DNG file writer. The writer can only deal with a small\n> > set of pixel formats. The generated file is consumable by standard\n> > tools. The writer needs to be extended to write more metadata to the\n> > generated file.\n> > \n> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > * Changes since v2\n> > - Mark input arg as const\n> > - Fold getFormatInfo() into only caller\n> > - Male DNGWriter::write() static and take filename as argument\n> > - Make DNGWriter::write() take StreamConfiguraiton instead of Stream\n> > - Optimize scanline loop\n> > - Make libtiff dependecy optional\n> > - Add 12 byte formats from Laurent\n> > - Reformat table from Laurent\n> > \n> > * Changes since v3\n> > - Make data and buffer arguments const for DNGWriter::write()\n> > - Fix memory size of uint8_t scanline[].\n> > ---\n> >  src/qcam/dng_writer.cpp | 165 ++++++++++++++++++++++++++++++++++++++++\n> >  src/qcam/dng_writer.h   |  28 +++++++\n> >  src/qcam/meson.build    |  20 ++++-\n> >  3 files changed, 210 insertions(+), 3 deletions(-)\n> >  create mode 100644 src/qcam/dng_writer.cpp\n> >  create mode 100644 src/qcam/dng_writer.h\n> > \n> > diff --git a/src/qcam/dng_writer.cpp b/src/qcam/dng_writer.cpp\n> > new file mode 100644\n> > index 0000000000000000..08078369529bb780\n> > --- /dev/null\n> > +++ b/src/qcam/dng_writer.cpp\n> > @@ -0,0 +1,165 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.\n> > + *\n> > + * dng_writer.cpp - DNG writer\n> > + */\n> > +\n> > +#include \"dng_writer.h\"\n> > +\n> > +#include <iostream>\n> > +#include <map>\n> > +\n> > +#include <tiffio.h>\n> > +\n> > +using namespace libcamera;\n> > +\n> > +enum CFAPatternColour : uint8_t {\n> > +\tCFAPatternRed = 0,\n> > +\tCFAPatternGreen = 1,\n> > +\tCFAPatternBlue = 2,\n> > +};\n> > +\n> > +struct FormatInfo {\n> > +\tuint8_t bitsPerSample;\n> > +\tCFAPatternColour pattern[4];\n> > +\tvoid (*packScanline)(void *, const void *, unsigned int);\n> > +};\n> > +\n> > +void packScanlineSBGGR10P(void *output, const void *input, unsigned int width)\n> > +{\n> > +\tconst uint8_t *in = static_cast<const uint8_t *>(input);\n> > +\tuint8_t *out = static_cast<uint8_t *>(output);\n> > +\n> > +\t/* \\todo Can this be made more efficient? */\n> > +\tfor (unsigned int x = 0; x < width; x += 4) {\n> > +\t\t*out++ = in[0];\n> > +\t\t*out++ = (in[4] & 0x03) << 6 | in[1] >> 2;\n> > +\t\t*out++ = (in[1] & 0x03) << 6 | (in[4] & 0x0c) << 2 | in[2] >> 4;\n> > +\t\t*out++ = (in[2] & 0x0f) << 4 | (in[4] & 0x30) >> 2 | in[3] >> 6;\n> > +\t\t*out++ = (in[3] & 0x3f) << 2 | (in[4] & 0xc0) >> 6;\n> > +\t\tin += 5;\n> > +\t}\n> > +}\n> > +\n> > +void packScanlineSBGGR12P(void *output, const void *input, unsigned int width)\n> > +{\n> > +\tconst uint8_t *in = static_cast<const uint8_t *>(input);\n> > +\tuint8_t *out = static_cast<uint8_t *>(output);\n> > +\n> > +\t/* \\todo: Can this be made more efficient? */\n> > +\tfor (unsigned int i = 0; i < width; i += 2) {\n> > +\t\t*out++ = in[0];\n> > +\t\t*out++ = (in[2] & 0x0f) << 4 | in[1] >> 4;\n> > +\t\t*out++ = (in[1] & 0x0f) << 4 | in[2] >> 4;\n> > +\t\tin += 3;\n> > +\t}\n> > +}\n> > +\n> > +static const std::map<PixelFormat, FormatInfo> formatInfo = {\n> > +\t{ PixelFormat(DRM_FORMAT_SBGGR10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 10,\n> > +\t\t.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },\n> > +\t\t.packScanline = packScanlineSBGGR10P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SGBRG10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 10,\n> > +\t\t.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },\n> > +\t\tpackScanlineSBGGR10P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SGRBG10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 10,\n> > +\t\t.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },\n> > +\t\t.packScanline = packScanlineSBGGR10P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SRGGB10, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 10,\n> > +\t\t.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },\n> > +\t\t.packScanline = packScanlineSBGGR10P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SBGGR12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 12,\n> > +\t\t.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },\n> > +\t\t.packScanline = packScanlineSBGGR12P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SGBRG12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 12,\n> > +\t\t.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },\n> > +\t\t.packScanline = packScanlineSBGGR12P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SGRBG12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 12,\n> > +\t\t.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },\n> > +\t\t.packScanline = packScanlineSBGGR12P,\n> > +\t} },\n> > +\t{ PixelFormat(DRM_FORMAT_SRGGB12, MIPI_FORMAT_MOD_CSI2_PACKED), {\n> > +\t\t.bitsPerSample = 12,\n> > +\t\t.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },\n> > +\t\t.packScanline = packScanlineSBGGR12P,\n> > +\t} },\n> > +};\n> > +\n> > +int DNGWriter::write(const char *filename, const Camera *camera,\n> > +\t\t     const StreamConfiguration &config,\n> > +\t\t     const FrameBuffer *buffer, const void *data)\n> > +{\n> > +\tconst auto it = formatInfo.find(config.pixelFormat);\n> > +\tif (it == formatInfo.cend()) {\n> > +\t\tstd::cerr << \"Unsupported pixel format\" << std::endl;\n> > +\t\treturn -EINVAL;\n> > +\t}\n> > +\tconst FormatInfo *info = &it->second;\n> > +\n> > +\tTIFF *tif = TIFFOpen(filename, \"w\");\n> > +\tif (!tif) {\n> > +\t\tstd::cerr << \"Failed to open tiff file\" << std::endl;\n> > +\t\treturn -EINVAL;\n> > +\t}\n> > +\n> > +\tconst uint8_t version[] = { 1, 2, 0, 0 };\n> > +\tconst uint16_t cfa_repeatpatterndim[] = { 2, 2 };\n> > +\tTIFFSetField(tif, TIFFTAG_DNGVERSION, version);\n> > +\tTIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, version);\n> > +\tTIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);\n> > +\tTIFFSetField(tif, TIFFTAG_IMAGEWIDTH, config.size.width);\n> > +\tTIFFSetField(tif, TIFFTAG_IMAGELENGTH, config.size.height);\n> > +\tTIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, info->bitsPerSample);\n> > +\tTIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);\n> > +\tTIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);\n> > +\tTIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);\n> > +\tTIFFSetField(tif, TIFFTAG_MAKE, \"libcamera\");\n> > +\tTIFFSetField(tif, TIFFTAG_MODEL, camera->name().c_str());\n> > +\tTIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, camera->name().c_str());\n> > +\tTIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);\n> > +\tTIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);\n> > +\tTIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);\n> > +\tTIFFSetField(tif, TIFFTAG_SOFTWARE, \"qcam\");\n> > +\tTIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);\n> > +\tTIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfa_repeatpatterndim);\n> > +\tTIFFSetField(tif, TIFFTAG_CFAPATTERN, info->pattern);\n> > +\tTIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, \"\\00\\01\\02\");\n> > +\tTIFFSetField(tif, TIFFTAG_CFALAYOUT, 1);\n> > +\n> > +\t/* \\todo Add more EXIF fields to output. */\n> > +\n> > +\t/* Write RAW content. */\n> > +\tconst uint8_t *row = static_cast<const uint8_t *>(data);\n> > +\tuint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8];\n> > +\tfor (unsigned int y = 0; y < config.size.height; y++) {\n> > +\t\tinfo->packScanline(&scanline, row, config.size.width);\n> > +\n> > +\t\tif (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {\n> > +\t\t\tstd::cerr << \"Failed to write scanline\" << std::endl;\n> > +\t\t\tTIFFClose(tif);\n> > +\t\t\treturn -EINVAL;\n> > +\t\t}\n> > +\n> > +\t\trow += config.stride;\n> > +\t}\n> > +\n> > +\tTIFFWriteDirectory(tif);\n> > +\n> > +\tTIFFClose(tif);\n> > +\n> > +\treturn 0;\n> > +}\n> > diff --git a/src/qcam/dng_writer.h b/src/qcam/dng_writer.h\n> > new file mode 100644\n> > index 0000000000000000..4dd904db4ce68c22\n> > --- /dev/null\n> > +++ b/src/qcam/dng_writer.h\n> > @@ -0,0 +1,28 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.\n> > + *\n> > + * dng_writer.h - DNG writer\n> > + */\n> > +#ifndef __LIBCAMERA_DNG_WRITER_H__\n> > +#define __LIBCAMERA_DNG_WRITER_H__\n> > +\n> > +#ifdef HAVE_TIFF\n> > +#define HAVE_DNG\n> > +\n> > +#include <libcamera/buffer.h>\n> > +#include <libcamera/camera.h>\n> > +#include <libcamera/stream.h>\n> > +\n> > +using namespace libcamera;\n> > +\n> > +class DNGWriter\n> > +{\n> > +public:\n> > +\tstatic int write(const char *filename, const Camera *camera,\n> > +\t\t\t const StreamConfiguration &config,\n> > +\t\t\t const FrameBuffer *buffer, const void *data);\n> \n> A very const function :-)\n> \n> > +};\n> \n> Blank line ?\n> \n> > +#endif /* HAVE_DNG */\n> \n> s/HAVE_DNG/HAVE_TIFF/ here\n> \n> And you can keep my tag.\n> \n> > +\n> > +#endif /* __LIBCAMERA_DNG_WRITER_H__ */\n> > diff --git a/src/qcam/meson.build b/src/qcam/meson.build\n> > index 895264be4a3388f4..949ef6149c8a187e 100644\n> > --- a/src/qcam/meson.build\n> > +++ b/src/qcam/meson.build\n> > @@ -1,9 +1,9 @@\n> >  qcam_sources = files([\n> > +    '../cam/options.cpp',\n> > +    '../cam/stream_options.cpp',\n> >      'format_converter.cpp',\n> >      'main.cpp',\n> >      'main_window.cpp',\n> > -    '../cam/options.cpp',\n> > -    '../cam/stream_options.cpp',\n> >      'viewfinder.cpp',\n> >  ])\n> >  \n> > @@ -23,8 +23,22 @@ qt5_dep = dependency('qt5',\n> >                       required : false)\n> >  \n> >  if qt5_dep.found()\n> > +    qcam_deps = [\n> > +        libcamera_dep,\n> > +        qt5_dep,\n> > +    ]\n> > +\n> >      qt5_cpp_args = [ '-DQT_NO_KEYWORDS' ]\n> >  \n> > +    tiff_dep = dependency('libtiff-4', required : false)\n> > +    if tiff_dep.found()\n> > +        qt5_cpp_args += [ '-DHAVE_TIFF' ]\n> > +        qcam_deps += [ tiff_dep ]\n> > +        qcam_sources += files([\n> > +            'dng_writer.cpp',\n> > +        ])\n> > +    endif\n> > +\n> >      # gcc 9 introduced a deprecated-copy warning that is triggered by Qt until\n> >      # Qt 5.13. clang 10 introduced the same warning, but detects more issues\n> >      # that are not fixed in Qt yet. Disable the warning manually in both cases.\n> > @@ -40,6 +54,6 @@ if qt5_dep.found()\n> >  \n> >      qcam  = executable('qcam', qcam_sources, resources,\n> >                         install : true,\n> > -                       dependencies : [libcamera_dep, qt5_dep],\n> > +                       dependencies : qcam_deps,\n> >                         cpp_args : qt5_cpp_args)\n> >  endif\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lf1-x144.google.com (mail-lf1-x144.google.com\n\t[IPv6:2a00:1450:4864:20::144])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E924F603FD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat,  2 May 2020 14:50:35 +0200 (CEST)","by mail-lf1-x144.google.com with SMTP id w14so5893301lfk.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 02 May 2020 05:50:35 -0700 (PDT)","from localhost (h-209-203.A463.priv.bahnhof.se. [155.4.209.203])\n\tby smtp.gmail.com with ESMTPSA id\n\ta28sm4356177lfo.47.2020.05.02.05.50.34\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSat, 02 May 2020 05:50:34 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=ragnatech-se.20150623.gappssmtp.com\n\theader.i=@ragnatech-se.20150623.gappssmtp.com header.b=\"tMZ3+B5b\"; \n\tdkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to;\n\tbh=yicKW4XaXomI1gcAMpSuOLFROACW+lTamdK46KXZePM=;\n\tb=tMZ3+B5bZG7wK9w+Se0agsRKCiAgO8ZjzCwUhXhmm/mLC4xU3xonXqbDa7/b2arfBl\n\tJXlsbPsZtGGVUlXTaFJWQKGmZKVlMJk59GarsMuqROOFXHV+HpAgXAK4yocAKOZHUv9y\n\tmKt/3xaDOSx1MHjSW1XZYmirtzrHzauJ8Nfix5Q+gC+p2eV5xjFcfDQ5z+8WsCMA8R1O\n\tSEtOUdWDbYGbiMwcM9l7g/gBFSNmxbrfRiV/TSgGynX4CEHV93uFS8euoBZlxaT6NmS9\n\tYaRfozDOLI/SLQyIr2x6jQRqyCz4pdePynyRAvNTs8gvmY5WuIUAFnkGeMwoUlZNAWJE\n\tg9FA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to;\n\tbh=yicKW4XaXomI1gcAMpSuOLFROACW+lTamdK46KXZePM=;\n\tb=p32kVYtNbLNai7RvyEo7BVTe6AweNSlOxqTllYWljW2nPDiprHX3yXn3F0pFlAvFJb\n\tp6s5LSyRyCw2+LLfTMXkf6ifvn5IYm//DJyxAA5EjOJoLM9fqB03DZFXWiIoVcX4ieZl\n\t1SQ7fStL6KIjXiveW40qR7jt1s+qezY6b0F8BrIbbwPMi2R2oIK9DsiGHdcKEKNB0h4I\n\taajiWuu9hRfykUXlrgAtXR721xjeiFMMZpMq6ksc04HvJ1da0BoK1JOw3lyrGrKzMvI0\n\t2iiPJ9SRr+jJuEZvWMsOhfqL7k+npDSyakBynsNHR8/5Di2DjYAOUqo9wih3J1o45a2N\n\ttV0Q==","X-Gm-Message-State":"AGi0PuY0fbjJoi843xOxceQyPMpHd+TryCqQotJMWoqQRoH5JEBuYeav\n\tcHr7oX8acB2BA9ZcSaWhIXqT5ncgU9k=","X-Google-Smtp-Source":"APiQypJoQtCiWme7ZBupcf7pMjL4NSSzondtGnFvQuJvVrhpV4Gj7HdSEErPuLCtW/nAK3YmUJF/CQ==","X-Received":"by 2002:a19:e049:: with SMTP id g9mr5673702lfj.198.1588423835226;\n\tSat, 02 May 2020 05:50:35 -0700 (PDT)","Date":"Sat, 2 May 2020 14:50:33 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200502125033.GA2890466@oden.dyn.berto.se>","References":"<20200502121356.1045727-1-niklas.soderlund@ragnatech.se>\n\t<20200502121356.1045727-3-niklas.soderlund@ragnatech.se>\n\t<20200502124134.GB5862@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20200502124134.GB5862@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v4 2/3] qcam: Add DNGWriter","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 02 May 2020 12:50:36 -0000"}}]