Show a patch.

GET /api/patches/17926/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 17926,
    "url": "https://patchwork.libcamera.org/api/patches/17926/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/17926/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20221201092733.2042078-5-chenghaoyang@google.com>",
    "date": "2022-12-01T09:27:31",
    "name": "[libcamera-devel,v7,4/6] Move generateThumbnail from PostProcessorJpeg to Encoder",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "934ba731c3c91d49633f47148ac56b6bd4e6be6f",
    "submitter": {
        "id": 117,
        "url": "https://patchwork.libcamera.org/api/people/117/?format=api",
        "name": "Cheng-Hao Yang",
        "email": "chenghaoyang@chromium.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/17926/mbox/",
    "series": [
        {
            "id": 3652,
            "url": "https://patchwork.libcamera.org/api/series/3652/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3652",
            "date": "2022-12-01T09:27:27",
            "name": "Add CrOS JEA implementation",
            "version": 7,
            "mbox": "https://patchwork.libcamera.org/series/3652/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/17926/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/17926/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CE83EC3284\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  1 Dec 2022 09:27:50 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8ACCC63345;\n\tThu,  1 Dec 2022 10:27:50 +0100 (CET)",
            "from mail-pf1-x435.google.com (mail-pf1-x435.google.com\n\t[IPv6:2607:f8b0:4864:20::435])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8C1E363336\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  1 Dec 2022 10:27:48 +0100 (CET)",
            "by mail-pf1-x435.google.com with SMTP id z17so1344222pff.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 01 Dec 2022 01:27:48 -0800 (PST)",
            "from chenghaoyang-low.c.googlers.com.com\n\t(46.165.189.35.bc.googleusercontent.com. [35.189.165.46])\n\tby smtp.gmail.com with ESMTPSA id\n\tz9-20020a1709027e8900b00186b8752a78sm3108050pla.80.2022.12.01.01.27.45\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 01 Dec 2022 01:27:46 -0800 (PST)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1669886870;\n\tbh=Z5msnFGTDVHbIG7OKAF2z9RNv9X6UVpxLCMk5dyA1kE=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=eG0G9jJBzuA45YSpr3eQI2IBN/PcxttjNt2q4jN99d7MiebxqNAtjX9XBQ7NAXJHG\n\tuZu+c5ikX9SH4Gjsx76fh3YT+AIXGHP6IhNs7yUBhatW+h5p2qLYYsms5Qw3CmbxsO\n\teZJlSxkosbCpLm2/DQ20D2/5+JBKXjPUKMtsxpr8rtJwhESpxiKfThddFYrJJucr+9\n\tkxEaY67syKtDmRyU00K4kM+qLVzhK1718BWpahwmpxgfdSONQiQtyand83fTHZ57jH\n\t+zuxdQlzEayX50FLBEee8fNyoJem92ZOvXNhHfeIdBMxOKo9hQ/bCSUM9Nad08MUGr\n\t5bB1NJkEiNXog==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=GppenPRfYE9AngDAbvr9EW4YaLp/osn673u8/Q+g9zI=;\n\tb=OmTjnt8PoPHnUybMEwHPsc4if0S4Q9Ker5ri3dR/3QeSx40U4446fCnygfWUkH9eZ8\n\t2UXNWb549AWxsO5GZliW0Y0zejAJwmTNw2jvKZ0s5AJHruUkz5otlma5KS9qbIwvvvsc\n\t4EY1nJZtefNKEVRuZspLZPToIhifLh6O16+gQ="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"OmTjnt8P\"; \n\tdkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=GppenPRfYE9AngDAbvr9EW4YaLp/osn673u8/Q+g9zI=;\n\tb=xXYZVCs/wWb/1KHpef9A/a5tiGHncTkOAkmKWBSyaguF7U46SlsJoA3GAd4AmNSefY\n\tz5slT4HNz2ElaVzzgeWj7+N8hk3jwD4sqp7rMCEjRSWlZsVbXw6vvicQSRl1m1sGoEAe\n\trSwXAY2FEvwZlpmgwPJQAOpxJtn2gVVVjQLl/JeRX21yrQbfZQem6tFrIZ3XFgOjMWKd\n\tAiwaRhxgkFMQm2dA++0eqT9swRqnjgnSCkiooof1eSfnQ/Bv9WaNBC5DiCYHtKiITL4V\n\t4WMK6q32suQSjE4RJ+vBluGIqn3S0IMd3v7YDG1FxPvqlkNh03gBzSNGNk122y6OIM6m\n\texqA==",
        "X-Gm-Message-State": "ANoB5pm6Fl21IrOjAwiHRbZGBTJq4kIJE2Ik/OhleLSPV2PHSIn256lk\n\tqqYwzFFUs1OdI0KKtNJvyDYLrIeDqkMBB594",
        "X-Google-Smtp-Source": "AA0mqf6SsrDN2eOU+sRt8bPL8mFcLriaLrJaBEmS4BT56WPRfOhh1WHQ/zP7KRNFAR23g3ghz/aJ1A==",
        "X-Received": "by 2002:a63:d117:0:b0:476:c781:d3ae with SMTP id\n\tk23-20020a63d117000000b00476c781d3aemr40493070pgg.183.1669886866648; \n\tThu, 01 Dec 2022 01:27:46 -0800 (PST)",
        "X-Google-Original-From": "Harvey Yang <chenghaoyang@google.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Thu,  1 Dec 2022 09:27:31 +0000",
        "Message-Id": "<20221201092733.2042078-5-chenghaoyang@google.com>",
        "X-Mailer": "git-send-email 2.38.1.584.g0f3c55d4c2-goog",
        "In-Reply-To": "<20221201092733.2042078-1-chenghaoyang@google.com>",
        "References": "<20221201092733.2042078-1-chenghaoyang@google.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v7 4/6] Move generateThumbnail from\n\tPostProcessorJpeg to Encoder",
        "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>",
        "From": "Harvey Yang via libcamera-devel <libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Harvey Yang <chenghaoyang@chromium.org>",
        "Cc": "Harvey Yang <chenghaoyang@chromium.org>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "From: Harvey Yang <chenghaoyang@chromium.org>\n\nIn the following patch, generateThumbnail will have a different\nimplementation in the jea encoder. Therefore, this patch moves the\ngenerateThumbnail function from PostProcessorJpeg to Encoder.\n\nSigned-off-by: Harvey Yang <chenghaoyang@chromium.org>\n---\n src/android/jpeg/encoder.h               |   5 +\n src/android/jpeg/encoder_libjpeg.cpp     | 122 ++++++++++++++++++-----\n src/android/jpeg/encoder_libjpeg.h       |  28 +++++-\n src/android/jpeg/post_processor_jpeg.cpp |  54 +---------\n src/android/jpeg/post_processor_jpeg.h   |  11 +-\n 5 files changed, 130 insertions(+), 90 deletions(-)",
    "diff": "diff --git a/src/android/jpeg/encoder.h b/src/android/jpeg/encoder.h\nindex b974d367..7dc1ee27 100644\n--- a/src/android/jpeg/encoder.h\n+++ b/src/android/jpeg/encoder.h\n@@ -22,4 +22,9 @@ public:\n \t\t\t   libcamera::Span<uint8_t> destination,\n \t\t\t   libcamera::Span<const uint8_t> exifData,\n \t\t\t   unsigned int quality) = 0;\n+\tvirtual int generateThumbnail(\n+\t\tconst libcamera::FrameBuffer &source,\n+\t\tconst libcamera::Size &targetSize,\n+\t\tunsigned int quality,\n+\t\tstd::vector<unsigned char> *thumbnail) = 0;\n };\ndiff --git a/src/android/jpeg/encoder_libjpeg.cpp b/src/android/jpeg/encoder_libjpeg.cpp\nindex fd62bd9c..cc37fde3 100644\n--- a/src/android/jpeg/encoder_libjpeg.cpp\n+++ b/src/android/jpeg/encoder_libjpeg.cpp\n@@ -71,29 +71,43 @@ const struct JPEGPixelFormatInfo &findPixelInfo(const PixelFormat &format)\n EncoderLibJpeg::EncoderLibJpeg()\n {\n \t/* \\todo Expand error handling coverage with a custom handler. */\n-\tcompress_.err = jpeg_std_error(&jerr_);\n+\timage_data_.compress.err = jpeg_std_error(&image_data_.jerr);\n+\tthumbnail_data_.compress.err = jpeg_std_error(&thumbnail_data_.jerr);\n \n-\tjpeg_create_compress(&compress_);\n+\tjpeg_create_compress(&image_data_.compress);\n+\tjpeg_create_compress(&thumbnail_data_.compress);\n }\n \n EncoderLibJpeg::~EncoderLibJpeg()\n {\n-\tjpeg_destroy_compress(&compress_);\n+\tjpeg_destroy_compress(&image_data_.compress);\n+\tjpeg_destroy_compress(&thumbnail_data_.compress);\n }\n \n int EncoderLibJpeg::configure(const StreamConfiguration &cfg)\n {\n-\tconst struct JPEGPixelFormatInfo info = findPixelInfo(cfg.pixelFormat);\n+\tthumbnailer_.configure(cfg.size, cfg.pixelFormat);\n+\n+\treturn configureEncoder(&image_data_.compress, cfg.pixelFormat,\n+\t\t\t\tcfg.size);\n+}\n+\n+int EncoderLibJpeg::configureEncoder(struct jpeg_compress_struct *compress,\n+\t\t\t\t     libcamera::PixelFormat pixelFormat,\n+\t\t\t\t     libcamera::Size size)\n+{\n+\tconst struct JPEGPixelFormatInfo info = findPixelInfo(pixelFormat);\n+\n \tif (info.colorSpace == JCS_UNKNOWN)\n \t\treturn -ENOTSUP;\n \n-\tcompress_.image_width = cfg.size.width;\n-\tcompress_.image_height = cfg.size.height;\n-\tcompress_.in_color_space = info.colorSpace;\n+\tcompress->image_width = size.width;\n+\tcompress->image_height = size.height;\n+\tcompress->in_color_space = info.colorSpace;\n \n-\tcompress_.input_components = info.colorSpace == JCS_GRAYSCALE ? 1 : 3;\n+\tcompress->input_components = info.colorSpace == JCS_GRAYSCALE ? 1 : 3;\n \n-\tjpeg_set_defaults(&compress_);\n+\tjpeg_set_defaults(compress);\n \n \tpixelFormatInfo_ = &info.pixelFormatInfo;\n \n@@ -107,13 +121,13 @@ void EncoderLibJpeg::compressRGB(const std::vector<Span<uint8_t>> &planes)\n {\n \tunsigned char *src = const_cast<unsigned char *>(planes[0].data());\n \t/* \\todo Stride information should come from buffer configuration. */\n-\tunsigned int stride = pixelFormatInfo_->stride(compress_.image_width, 0);\n+\tunsigned int stride = pixelFormatInfo_->stride(compress_->image_width, 0);\n \n \tJSAMPROW row_pointer[1];\n \n-\twhile (compress_.next_scanline < compress_.image_height) {\n-\t\trow_pointer[0] = &src[compress_.next_scanline * stride];\n-\t\tjpeg_write_scanlines(&compress_, row_pointer, 1);\n+\twhile (compress_->next_scanline < compress_->image_height) {\n+\t\trow_pointer[0] = &src[compress_->next_scanline * stride];\n+\t\tjpeg_write_scanlines(compress_, row_pointer, 1);\n \t}\n }\n \n@@ -123,7 +137,7 @@ void EncoderLibJpeg::compressRGB(const std::vector<Span<uint8_t>> &planes)\n  */\n void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)\n {\n-\tuint8_t tmprowbuf[compress_.image_width * 3];\n+\tuint8_t tmprowbuf[compress_->image_width * 3];\n \n \t/*\n \t * \\todo Use the raw api, and only unpack the cb/cr samples to new line\n@@ -133,10 +147,10 @@ void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)\n \t * Possible hints at:\n \t * https://sourceforge.net/p/libjpeg/mailman/message/30815123/\n \t */\n-\tunsigned int y_stride = pixelFormatInfo_->stride(compress_.image_width, 0);\n-\tunsigned int c_stride = pixelFormatInfo_->stride(compress_.image_width, 1);\n+\tunsigned int y_stride = pixelFormatInfo_->stride(compress_->image_width, 0);\n+\tunsigned int c_stride = pixelFormatInfo_->stride(compress_->image_width, 1);\n \n-\tunsigned int horzSubSample = 2 * compress_.image_width / c_stride;\n+\tunsigned int horzSubSample = 2 * compress_->image_width / c_stride;\n \tunsigned int vertSubSample = pixelFormatInfo_->planes[1].verticalSubSampling;\n \n \tunsigned int c_inc = horzSubSample == 1 ? 2 : 0;\n@@ -149,14 +163,14 @@ void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)\n \tJSAMPROW row_pointer[1];\n \trow_pointer[0] = &tmprowbuf[0];\n \n-\tfor (unsigned int y = 0; y < compress_.image_height; y++) {\n+\tfor (unsigned int y = 0; y < compress_->image_height; y++) {\n \t\tunsigned char *dst = &tmprowbuf[0];\n \n \t\tconst unsigned char *src_y = src + y * y_stride;\n \t\tconst unsigned char *src_cb = src_c + (y / vertSubSample) * c_stride + cb_pos;\n \t\tconst unsigned char *src_cr = src_c + (y / vertSubSample) * c_stride + cr_pos;\n \n-\t\tfor (unsigned int x = 0; x < compress_.image_width; x += 2) {\n+\t\tfor (unsigned int x = 0; x < compress_->image_width; x += 2) {\n \t\t\tdst[0] = *src_y;\n \t\t\tdst[1] = *src_cb;\n \t\t\tdst[2] = *src_cr;\n@@ -174,13 +188,67 @@ void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)\n \t\t\tdst += 3;\n \t\t}\n \n-\t\tjpeg_write_scanlines(&compress_, row_pointer, 1);\n+\t\tjpeg_write_scanlines(compress_, row_pointer, 1);\n \t}\n }\n \n+int EncoderLibJpeg::generateThumbnail(const libcamera::FrameBuffer &source,\n+\t\t\t\t      const libcamera::Size &targetSize,\n+\t\t\t\t      unsigned int quality,\n+\t\t\t\t      std::vector<unsigned char> *thumbnail)\n+{\n+\t/* Stores the raw scaled-down thumbnail bytes. */\n+\tstd::vector<unsigned char> rawThumbnail;\n+\n+\tthumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);\n+\n+\tint ret = configureEncoder(&thumbnail_data_.compress,\n+\t\t\t\t   thumbnailer_.pixelFormat(), targetSize);\n+\tcompress_ = &thumbnail_data_.compress;\n+\n+\tif (!rawThumbnail.empty() && !ret) {\n+\t\t/*\n+\t\t * \\todo Avoid value-initialization of all elements of the\n+\t\t * vector.\n+\t\t */\n+\t\tthumbnail->resize(rawThumbnail.size());\n+\n+\t\t/*\n+\t\t * Split planes manually as the encoder expects a vector of\n+\t\t * planes.\n+\t\t *\n+\t\t * \\todo Pass a vector of planes directly to\n+\t\t * Thumbnailer::createThumbnailer above and remove the manual\n+\t\t * planes split from here.\n+\t\t */\n+\t\tstd::vector<Span<uint8_t>> thumbnailPlanes;\n+\t\tconst PixelFormatInfo &formatNV12 =\n+\t\t\tPixelFormatInfo::info(formats::NV12);\n+\t\tsize_t yPlaneSize = formatNV12.planeSize(targetSize, 0);\n+\t\tsize_t uvPlaneSize = formatNV12.planeSize(targetSize, 1);\n+\t\tthumbnailPlanes.push_back({ rawThumbnail.data(), yPlaneSize });\n+\t\tthumbnailPlanes.push_back({ rawThumbnail.data() + yPlaneSize,\n+\t\t\t\t\t    uvPlaneSize });\n+\n+\t\tint jpeg_size = encode(thumbnailPlanes, *thumbnail, {},\n+\t\t\t\t       quality);\n+\t\tthumbnail->resize(jpeg_size);\n+\n+\t\tLOG(JPEG, Debug)\n+\t\t\t<< \"Thumbnail compress returned \"\n+\t\t\t<< jpeg_size << \" bytes\";\n+\n+\t\treturn jpeg_size;\n+\t}\n+\n+\treturn -1;\n+}\n+\n int EncoderLibJpeg::encode(const FrameBuffer &source, Span<uint8_t> dest,\n \t\t\t   Span<const uint8_t> exifData, unsigned int quality)\n {\n+\tcompress_ = &image_data_.compress;\n+\n \tMappedFrameBuffer frame(&source, MappedFrameBuffer::MapFlag::Read);\n \tif (!frame.isValid()) {\n \t\tLOG(JPEG, Error) << \"Failed to map FrameBuffer : \"\n@@ -198,7 +266,7 @@ int EncoderLibJpeg::encode(const std::vector<Span<uint8_t>> &src,\n \tunsigned char *destination = dest.data();\n \tunsigned long size = dest.size();\n \n-\tjpeg_set_quality(&compress_, quality, TRUE);\n+\tjpeg_set_quality(compress_, quality, TRUE);\n \n \t/*\n \t * The jpeg_mem_dest will reallocate if the required size is not\n@@ -208,18 +276,18 @@ int EncoderLibJpeg::encode(const std::vector<Span<uint8_t>> &src,\n \t * \\todo Implement our own custom memory destination to prevent\n \t * reallocation and prefer failure with correct reporting.\n \t */\n-\tjpeg_mem_dest(&compress_, &destination, &size);\n+\tjpeg_mem_dest(compress_, &destination, &size);\n \n-\tjpeg_start_compress(&compress_, TRUE);\n+\tjpeg_start_compress(compress_, TRUE);\n \n \tif (exifData.size())\n \t\t/* Store Exif data in the JPEG_APP1 data block. */\n-\t\tjpeg_write_marker(&compress_, JPEG_APP0 + 1,\n+\t\tjpeg_write_marker(compress_, JPEG_APP0 + 1,\n \t\t\t\t  static_cast<const JOCTET *>(exifData.data()),\n \t\t\t\t  exifData.size());\n \n-\tLOG(JPEG, Debug) << \"JPEG Encode Starting:\" << compress_.image_width\n-\t\t\t << \"x\" << compress_.image_height;\n+\tLOG(JPEG, Debug) << \"JPEG Encode Starting:\" << compress_->image_width\n+\t\t\t << \"x\" << compress_->image_height;\n \n \tASSERT(src.size() == pixelFormatInfo_->numPlanes());\n \n@@ -228,7 +296,7 @@ int EncoderLibJpeg::encode(const std::vector<Span<uint8_t>> &src,\n \telse\n \t\tcompressRGB(src);\n \n-\tjpeg_finish_compress(&compress_);\n+\tjpeg_finish_compress(compress_);\n \n \treturn size;\n }\ndiff --git a/src/android/jpeg/encoder_libjpeg.h b/src/android/jpeg/encoder_libjpeg.h\nindex 1b3ac067..1ec2ba13 100644\n--- a/src/android/jpeg/encoder_libjpeg.h\n+++ b/src/android/jpeg/encoder_libjpeg.h\n@@ -15,6 +15,8 @@\n \n #include <jpeglib.h>\n \n+#include \"thumbnailer.h\"\n+\n class EncoderLibJpeg : public Encoder\n {\n public:\n@@ -26,20 +28,40 @@ public:\n \t\t   libcamera::Span<uint8_t> destination,\n \t\t   libcamera::Span<const uint8_t> exifData,\n \t\t   unsigned int quality) override;\n+\tint generateThumbnail(\n+\t\tconst libcamera::FrameBuffer &source,\n+\t\tconst libcamera::Size &targetSize,\n+\t\tunsigned int quality,\n+\t\tstd::vector<unsigned char> *thumbnail) override;\n+\n+private:\n+\tstruct JpegData {\n+\t\tstruct jpeg_compress_struct compress;\n+\t\tstruct jpeg_error_mgr jerr;\n+\t};\n+\n+\tint configureEncoder(struct jpeg_compress_struct *compress,\n+\t\t\t     libcamera::PixelFormat pixelFormat,\n+\t\t\t     libcamera::Size size);\n+\n \tint encode(const std::vector<libcamera::Span<uint8_t>> &planes,\n \t\t   libcamera::Span<uint8_t> destination,\n \t\t   libcamera::Span<const uint8_t> exifData,\n \t\t   unsigned int quality);\n \n-private:\n \tvoid compressRGB(const std::vector<libcamera::Span<uint8_t>> &planes);\n \tvoid compressNV(const std::vector<libcamera::Span<uint8_t>> &planes);\n \n-\tstruct jpeg_compress_struct compress_;\n-\tstruct jpeg_error_mgr jerr_;\n+\tJpegData image_data_;\n+\tJpegData thumbnail_data_;\n+\n+\t// |&image_data_.compress| or |&thumbnail_data_.compress|.\n+\tstruct jpeg_compress_struct *compress_;\n \n \tconst libcamera::PixelFormatInfo *pixelFormatInfo_;\n \n \tbool nv_;\n \tbool nvSwap_;\n+\n+\tThumbnailer thumbnailer_;\n };\ndiff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp\nindex 0cf56716..60eebb11 100644\n--- a/src/android/jpeg/post_processor_jpeg.cpp\n+++ b/src/android/jpeg/post_processor_jpeg.cpp\n@@ -44,60 +44,11 @@ int PostProcessorJpeg::configure(const StreamConfiguration &inCfg,\n \n \tstreamSize_ = outCfg.size;\n \n-\tthumbnailer_.configure(inCfg.size, inCfg.pixelFormat);\n-\n \tencoder_ = std::make_unique<EncoderLibJpeg>();\n \n \treturn encoder_->configure(inCfg);\n }\n \n-void PostProcessorJpeg::generateThumbnail(const FrameBuffer &source,\n-\t\t\t\t\t  const Size &targetSize,\n-\t\t\t\t\t  unsigned int quality,\n-\t\t\t\t\t  std::vector<unsigned char> *thumbnail)\n-{\n-\t/* Stores the raw scaled-down thumbnail bytes. */\n-\tstd::vector<unsigned char> rawThumbnail;\n-\n-\tthumbnailer_.createThumbnail(source, targetSize, &rawThumbnail);\n-\n-\tStreamConfiguration thCfg;\n-\tthCfg.size = targetSize;\n-\tthCfg.pixelFormat = thumbnailer_.pixelFormat();\n-\tint ret = thumbnailEncoder_.configure(thCfg);\n-\n-\tif (!rawThumbnail.empty() && !ret) {\n-\t\t/*\n-\t\t * \\todo Avoid value-initialization of all elements of the\n-\t\t * vector.\n-\t\t */\n-\t\tthumbnail->resize(rawThumbnail.size());\n-\n-\t\t/*\n-\t\t * Split planes manually as the encoder expects a vector of\n-\t\t * planes.\n-\t\t *\n-\t\t * \\todo Pass a vector of planes directly to\n-\t\t * Thumbnailer::createThumbnailer above and remove the manual\n-\t\t * planes split from here.\n-\t\t */\n-\t\tstd::vector<Span<uint8_t>> thumbnailPlanes;\n-\t\tconst PixelFormatInfo &formatNV12 = PixelFormatInfo::info(formats::NV12);\n-\t\tsize_t yPlaneSize = formatNV12.planeSize(targetSize, 0);\n-\t\tsize_t uvPlaneSize = formatNV12.planeSize(targetSize, 1);\n-\t\tthumbnailPlanes.push_back({ rawThumbnail.data(), yPlaneSize });\n-\t\tthumbnailPlanes.push_back({ rawThumbnail.data() + yPlaneSize, uvPlaneSize });\n-\n-\t\tint jpeg_size = thumbnailEncoder_.encode(thumbnailPlanes,\n-\t\t\t\t\t\t\t *thumbnail, {}, quality);\n-\t\tthumbnail->resize(jpeg_size);\n-\n-\t\tLOG(JPEG, Debug)\n-\t\t\t<< \"Thumbnail compress returned \"\n-\t\t\t<< jpeg_size << \" bytes\";\n-\t}\n-}\n-\n void PostProcessorJpeg::process(Camera3RequestDescriptor::StreamBuffer *streamBuffer)\n {\n \tASSERT(encoder_);\n@@ -164,8 +115,9 @@ void PostProcessorJpeg::process(Camera3RequestDescriptor::StreamBuffer *streamBu\n \n \t\tif (thumbnailSize != Size(0, 0)) {\n \t\t\tstd::vector<unsigned char> thumbnail;\n-\t\t\tgenerateThumbnail(source, thumbnailSize, quality, &thumbnail);\n-\t\t\tif (!thumbnail.empty())\n+\t\t\tret = encoder_->generateThumbnail(source, thumbnailSize,\n+\t\t\t\t\t\t\t  quality, &thumbnail);\n+\t\t\tif (ret > 0 && !thumbnail.empty())\n \t\t\t\texif.setThumbnail(std::move(thumbnail), Exif::Compression::JPEG);\n \t\t}\n \ndiff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h\nindex 98309b01..55b23d7d 100644\n--- a/src/android/jpeg/post_processor_jpeg.h\n+++ b/src/android/jpeg/post_processor_jpeg.h\n@@ -8,11 +8,11 @@\n #pragma once\n \n #include \"../post_processor.h\"\n-#include \"encoder_libjpeg.h\"\n-#include \"thumbnailer.h\"\n \n #include <libcamera/geometry.h>\n \n+#include \"encoder.h\"\n+\n class CameraDevice;\n \n class PostProcessorJpeg : public PostProcessor\n@@ -25,14 +25,7 @@ public:\n \tvoid process(Camera3RequestDescriptor::StreamBuffer *streamBuffer) override;\n \n private:\n-\tvoid generateThumbnail(const libcamera::FrameBuffer &source,\n-\t\t\t       const libcamera::Size &targetSize,\n-\t\t\t       unsigned int quality,\n-\t\t\t       std::vector<unsigned char> *thumbnail);\n-\n \tCameraDevice *const cameraDevice_;\n \tstd::unique_ptr<Encoder> encoder_;\n \tlibcamera::Size streamSize_;\n-\tEncoderLibJpeg thumbnailEncoder_;\n-\tThumbnailer thumbnailer_;\n };\n",
    "prefixes": [
        "libcamera-devel",
        "v7",
        "4/6"
    ]
}