Show a patch.

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

{
    "id": 16314,
    "url": "https://patchwork.libcamera.org/api/patches/16314/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/16314/",
    "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": "<20220622123706.2946976-5-chenghaoyang@google.com>",
    "date": "2022-06-22T12:37:04",
    "name": "[libcamera-devel,v5,4/6] Move generateThumbnail from PostProcessorJpeg to Encoder",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "9c51b2836dce764992abf1affc02823b23c16c03",
    "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/16314/mbox/",
    "series": [
        {
            "id": 3203,
            "url": "https://patchwork.libcamera.org/api/series/3203/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3203",
            "date": "2022-06-22T12:37:00",
            "name": "Add CrOS JEA implementation",
            "version": 5,
            "mbox": "https://patchwork.libcamera.org/series/3203/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/16314/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/16314/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 3FE1FBE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 22 Jun 2022 12:37:30 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DF3C46563F;\n\tWed, 22 Jun 2022 14:37:29 +0200 (CEST)",
            "from mail-pj1-x102d.google.com (mail-pj1-x102d.google.com\n\t[IPv6:2607:f8b0:4864:20::102d])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 94CC7633A7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 22 Jun 2022 14:37:28 +0200 (CEST)",
            "by mail-pj1-x102d.google.com with SMTP id cv13so13180460pjb.4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 22 Jun 2022 05:37:28 -0700 (PDT)",
            "from chenghaoyang-low.c.googlers.com.com\n\t(21.160.199.104.bc.googleusercontent.com. [104.199.160.21])\n\tby smtp.gmail.com with ESMTPSA id\n\tj3-20020a170903024300b0016378bfeb90sm12684038plh.227.2022.06.22.05.37.22\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 22 Jun 2022 05:37:24 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1655901449;\n\tbh=6b69vrX8JoYmcX6xQ9b85KN/lzHWsiSQ1C03JuAjYLI=;\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=tmIfFVX2lDafDqUYyceKSgXjR5BWiRV8pfXfUXC1449u3aXy87s5E4FBe81jjPr0b\n\ta7RtT1o0vMks0FhnHzCyKfT/4llDHaTJmUNZMsHjXs/ulzZlpMU65evNjUOCuV+Soc\n\tSZPaMqapVA37rmQ+V/0ccyBxcGTNH6ZklXwBuuLk8xLAoVLt10KAk3XX14WxoxA/MU\n\tOWY92sJAryY+SQ/phCMmGCqp0XywXK9OmJoWx2horkAo/t7RigLdovkFnL1ts4QGss\n\tx6VXJHeWj9wEpTPkURJAWgxGOB9YQDBQsyv8jGC7a0bJNhUq5RbsztcFSVsiXTz/4t\n\tHaJFGSKl8/tpg==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=RZ+ZM2DuXrE6i/obwdyVRj2wvMwtgO5ai/NtOfxhN7c=;\n\tb=MSINvFC8MDGD37jFN36zDuCiC0pz0E2BDriTHNSEeS14oOXZUZggViMWUbVh3SSgd6\n\tqqpM0hDIPJmzxOPp4aOwylkC2ZtxJi3GE9v0cD0sss+IYucDvICW+IzdCsthvJsm7NjZ\n\tNQnWyWT50JWHmTjDJacGRYSa7IpfbN80hMbAg="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"MSINvFC8\"; \n\tdkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=RZ+ZM2DuXrE6i/obwdyVRj2wvMwtgO5ai/NtOfxhN7c=;\n\tb=JhyK3fzEY1YqpOPdZhRCqQk0no2ap/cBKo35bBwFE7M91pxNOd93jDm0H4UDQl5GgI\n\ty3XRd8ZmVjGQaxTCr+v2UmilBPPHGXksZydAg8Qdvl3eEMhelvDSd3+I+vrGie5yZqxb\n\tiTh3BX7ZsDN62Pfx61C0ZyCWQztAJM9cD5Tz6fLVfytKgSYhrdimshoXM7KQfNOAzNf3\n\tSrAYN/v/4ISCi4PQ/+HcgHmvu4gl87l3t3SS17rSa/uSjU9an/k3o3lfKbujjsnL6wk6\n\tJjq318K9PQppDZZsqo9H9HpjKVoxfoBClH0OH5R/2Q6RtUQQ27P7GJrn0vzskgIYI9Op\n\tVGRQ==",
        "X-Gm-Message-State": "AJIora9l02/Gx81LPNEdLwBmVfdeV9tzPB89WJtRT81LHSbgN9ck8Gkq\n\t7plJsE9dn0DlAbrnTZKoz+74WDNLiF/6ug==",
        "X-Google-Smtp-Source": "AGRyM1sIMG1+bF0hCLuN/r7+Mt0C7NwKQjujbKWARKxZkmG0KzFALlksjyKI0kq6DTcEzBooJQa5IQ==",
        "X-Received": "by 2002:a17:90b:4c06:b0:1e3:17fa:e387 with SMTP id\n\tna6-20020a17090b4c0600b001e317fae387mr49158874pjb.53.1655901446557; \n\tWed, 22 Jun 2022 05:37:26 -0700 (PDT)",
        "X-Google-Original-From": "Harvey Yang <chenghaoyang@google.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed, 22 Jun 2022 12:37:04 +0000",
        "Message-Id": "<20220622123706.2946976-5-chenghaoyang@google.com>",
        "X-Mailer": "git-send-email 2.37.0.rc0.104.g0611611a94-goog",
        "In-Reply-To": "<20220622123706.2946976-1-chenghaoyang@google.com>",
        "References": "<20220622123706.2946976-1-chenghaoyang@google.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v5 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 21a3b33d..e0aca821 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 d72ebc3c..233cdedb 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(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",
        "v5",
        "4/6"
    ]
}