From patchwork Fri Oct 23 10:21:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10214 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E76D0C3D3C for ; Fri, 23 Oct 2020 10:22:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AF5EA6198C; Fri, 23 Oct 2020 12:22:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="e4RggprU"; dkim-atps=neutral Received: from mail-wr1-x442.google.com (mail-wr1-x442.google.com [IPv6:2a00:1450:4864:20::442]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C98BD615D2 for ; Fri, 23 Oct 2020 12:22:04 +0200 (CEST) Received: by mail-wr1-x442.google.com with SMTP id y12so1196549wrp.6 for ; Fri, 23 Oct 2020 03:22:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mGxepSLU5YIYBTtezZ9zAh+/Fk0684f7g+bHtJyie0I=; b=e4RggprU9lKsAXtZ/GzyrikxVb8poNlvYUSJuU0VfH2dlgOSaWJAtdrJLv1Jx8UBS2 1BDFMjfiOl4VRD6mFpIPKRfkLTdsZUoca262DSe/tR0C6OFHKLlQj+bAUWnaW1KAsmb3 qdOEAvR1FV4MdkgrHdTwS9pXPqGSioZ6TMYEHc4d3JLO9v2qnkJNepIzLSO+rlgvRVmz 5HP7CUmMhMwCQJgzwO+2XML+MFcg2V83dyUmfb5iAbfQCv15bSm6xjKa8Wx1tASQYujf FIPTbXgg6/2XpUsDNg+gtomYwDI6DrFehx+kPG4TnU7rLIJDveIPbbFatKmTjpBiOiRP 62hQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mGxepSLU5YIYBTtezZ9zAh+/Fk0684f7g+bHtJyie0I=; b=FeQZaObZ0zO5kcp3i+AvCctzGjxVa2VGJ2oynx4I3XuwhIRpCDNCCygBQmhx5jQg+q ZWxG3pxafFftVYFUoRdoL9TgMWNpncF1fBCPjOYSDiqFMHNJz5L15wGHFtQZ8I4L6dWe /a02REhF0O3CWFv/hKFlRoP/wz1NugP/eaBaO0jBonsAy8E2Fn73jpl7jCTD3vWOZ8FS Y6R+z975GGxrSqqUYWOoYYsOO7lfswGDcqDQtFtuDOlQLluTmoOUGRw6CxOoCMT5mr4z EAZHDtsSvFkHL7bK97AYNd0qbTgt79ILRMwQiCBnNRvtUAPpqBBilhX79OTSrvilhQpN tErQ== X-Gm-Message-State: AOAM531n1Dl6IdRHYctr5R6frp0lOUFm5kY3dtiw6jJT8A+5xXmrMlF+ 9/gNm9aMTYmFpt6g2fhMWi76O1zIxZWSVg== X-Google-Smtp-Source: ABdhPJyZF50zeVFbyDoeCKKFqaAIv9FElOfDObeZt3CRImNcjdyxpdkoYbDHTxTIEHgKVDa2jPd59w== X-Received: by 2002:adf:f212:: with SMTP id p18mr1905731wro.386.1603448524254; Fri, 23 Oct 2020 03:22:04 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id r128sm2477831wma.20.2020.10.23.03.22.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Oct 2020 03:22:03 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 23 Oct 2020 11:21:55 +0100 Message-Id: <20201023102159.26274-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201023102159.26274-1-david.plowman@raspberrypi.com> References: <20201023102159.26274-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 1/5] libcamera: Add ScalerCrop control X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The ScalerCrop control selects how much of the sensor's output image will be scaled to form the final output image. It can be used to implement digital zoom. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- src/libcamera/control_ids.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml index 4c415545..1c1f6699 100644 --- a/src/libcamera/control_ids.yaml +++ b/src/libcamera/control_ids.yaml @@ -284,4 +284,17 @@ controls: order in an array of 9 floating point values. size: [3x3] + + - ScalerCrop: + type: Rectangle + description: | + Sets the image portion that will be scaled to form the whole of + the final output image. The (x,y) location of this rectangle is + relative to the PixelArrayActiveArea that is being used. The units + remain native sensor pixels, even if the sensor is being used in + a binning or skipping mode. + + This control is only present when the pipeline supports it. Its + maximum valid value is given by the properties::ScalerCropMaximum + property, and the two can be used to implement digital zoom. ... From patchwork Fri Oct 23 10:21:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10215 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 5FC5FC3D3C for ; Fri, 23 Oct 2020 10:22:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2423B619A3; Fri, 23 Oct 2020 12:22:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="P69RMVZY"; dkim-atps=neutral Received: from mail-wr1-x442.google.com (mail-wr1-x442.google.com [IPv6:2a00:1450:4864:20::442]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A031F615D3 for ; Fri, 23 Oct 2020 12:22:05 +0200 (CEST) Received: by mail-wr1-x442.google.com with SMTP id b8so1221216wrn.0 for ; Fri, 23 Oct 2020 03:22:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=wKds1zcbhJ1K4bXbXUJzrxeNZ9GkAN5EeGkln1GJhLg=; b=P69RMVZYXdoczjHsRkDiODoYv9wCsCc41eUye/zCZPEezViLJvPxWGLbNPUszagomG Yw0/SN0klWBBHn38OS1ynGIxxmBKaYF+sjEY4G9h9LKdcA4/3yIuzmTYAnGG3nRUkvZf KOb8A+GEA7aUR/zUKcunOFegoyn4jQLF4cjiMvww3dSSQEL4ZWsLPVT6XYAOEuMoRfnK IbbeIG7F/02w7/o0yvFH9edqQKfeVqICywRtg9/7Qbgh4IloT+69q0X3kqjr8q1X7eFo RGCYS7hf0C2tcMbJhR/4EkRcJ+IurIWXz6O0DSJ1mmtt8NL34hT+voSPJ9wiyHrd+CY1 YrKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=wKds1zcbhJ1K4bXbXUJzrxeNZ9GkAN5EeGkln1GJhLg=; b=Lvq+TZfIuqnxSUQ7zwiTKm43ClybrYaJqguuSsLhrSsLKAabl7yBdtHuLcuaSLUjdj g9oPz3d3M+EsoynKwx0GKE1oKjvzTaJ01tb9wajI94vnmelQUOEfwBkAV//pCFdvISpZ U7k0TtWp+xuXA1zxPftFH04X4NZp9KI+M78S+XfTUbh0/EwW2USjCOaIIdwWDprrBTP0 t4nAid78CpwiSX6nakG70K9QdNIGirlgQzgnU8yMMxc8ndlvT0KVmkaO/yXi0C7kQqIF iw2pX8U4HX/lM7ZNtiGuMODO/03w7gi3z2S46G1dkyyaBh6AcLWofyfnu0cGZGyP439Y OZ/Q== X-Gm-Message-State: AOAM531P5RvOp063+14+j/WC8nb4KhvVGHS5qch27jqAZuE6a52Gd6bY Y0OaVXC1HVhbl5jU0jHDs8QG1NZhEyoWRA== X-Google-Smtp-Source: ABdhPJzy6tpZh/Sj/sR7bLSGxls8SbVz3lNqx/iFU4QgPPOAMuZiARUAJDnVcOBp5LwN1hfpNNWvmg== X-Received: by 2002:adf:ab50:: with SMTP id r16mr1756182wrc.235.1603448525093; Fri, 23 Oct 2020 03:22:05 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id r128sm2477831wma.20.2020.10.23.03.22.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Oct 2020 03:22:04 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 23 Oct 2020 11:21:56 +0100 Message-Id: <20201023102159.26274-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201023102159.26274-1-david.plowman@raspberrypi.com> References: <20201023102159.26274-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 2/5] libcamera: Add SensorCropMaximum property X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The SensorCropMaximum camera property reports the location of that part of the image sensor array that is scaled to produce the output images, given in native sensor pixels. It will normally change when a new camera mode is selected, and can be used to implement digital zoom. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- src/libcamera/property_ids.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml index 7261263a..a306a422 100644 --- a/src/libcamera/property_ids.yaml +++ b/src/libcamera/property_ids.yaml @@ -663,4 +663,18 @@ controls: \todo Rename this property to ActiveAreas once we will have property categories (i.e. Properties::PixelArray::ActiveAreas) + - ScalerCropMaximum: + type: Rectangle + description: | + The maximum valid rectangle for the controls::ScalerCrop control. This + reflects the minimum mandatory cropping applied in the camera sensor and + the rest of the pipeline. + + This property is valid only after the camera has been successfully + configured and its value changes whenever a new configuration is + applied. + + \todo Turn this property into a "maximum control value" for the + ScalerCrop control once "dynamic" controls have been implemented. + ... From patchwork Fri Oct 23 10:21:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10216 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 0E217C3D3C for ; Fri, 23 Oct 2020 10:22:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A8280615D3; Fri, 23 Oct 2020 12:22:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="EJv8oi4B"; dkim-atps=neutral Received: from mail-wm1-x344.google.com (mail-wm1-x344.google.com [IPv6:2a00:1450:4864:20::344]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 52DDF615D3 for ; Fri, 23 Oct 2020 12:22:06 +0200 (CEST) Received: by mail-wm1-x344.google.com with SMTP id l20so266709wme.0 for ; Fri, 23 Oct 2020 03:22:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=uQQaHb0XmHtsHhkWw0OAl06JWoDByjyQ5++hI8L9kB8=; b=EJv8oi4BI7vfCDdDCz/8w1hBaQLBDqGqNiHq/a0KpxEGFtYfKbUZo7Ah/z1QhvKWyZ d/riNQ8/pHkY/2m26fsuLUX4Tu/yV2G+j7wEbgNx2ryTK4pIUw7BPjJm69EOQmtxA1id 9Q34rwe3VHQLhKdi/ejHBx8NfUzYsirNQPgrTxaUnh4fjQ0B0uvGAI6vKHlccWvGaGwt UcDwr8wgtpL/2LCwsVvEtX1e0Qkrk+nVQBGkin/j6ETDvN2+BzV0oftetN5AMEjf7RoQ az2CWLEZY+aqGx4fZMDu2KS4vJ6Fq5Qaxf1bp5KKSFyE+YjU52Uo+k8GAS7OJnMJHubj sDFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=uQQaHb0XmHtsHhkWw0OAl06JWoDByjyQ5++hI8L9kB8=; b=mmFDaaSmGWu87yRuakkhwjTJDG8yIzPGW4vsRvvye2/c1YHEhFtdOLLaHMlGfR6DrX UwzoJp30X9cvwfE91Kn+zP3lhomNk/IumrSZ6cY43KD5ovrDPHmDhDyXkdSfIQaVLWky yEiNCK0CSc++W84QuTTAJ+cXjfNHOvSMOv0nO4aywCb/L7/wQahWnrtAi+Qdj4eJvMNM 4KLeHjKpwOgPKuLGyCIM6hBGD5JrvdB7q/N1fEqWamgIaTY2RAsLCQVGAzo6Nakom0mb FTxnJw6ZSjHaRxo01IPn+pGTUW8EiUKBh1ttkdnE+f1J1omGmlLtF2deRzhOjRZ+SVPh Uaxg== X-Gm-Message-State: AOAM533e4o+wdBnc87KuIdissWg6AaE6qVutBX19gfDTVC9l9hXowDbH 7iY0d/o+wFky9rdEkkelWp0HYHL4V645fg== X-Google-Smtp-Source: ABdhPJwO1BsjvQMns1d3Plw6wG4zmO/luoh8XSPkgieO7pPWT1214MBCuT9tk/J9oPS2SOBiM39AwA== X-Received: by 2002:a1c:7518:: with SMTP id o24mr1623674wmc.137.1603448525868; Fri, 23 Oct 2020 03:22:05 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id r128sm2477831wma.20.2020.10.23.03.22.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Oct 2020 03:22:05 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 23 Oct 2020 11:21:57 +0100 Message-Id: <20201023102159.26274-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201023102159.26274-1-david.plowman@raspberrypi.com> References: <20201023102159.26274-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 3/5] libcamera: raspberrypi: Initialise the SensorCropMaximum property X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Initialise it to show we support its use, however, set it to all zeroes to indicate that it's not meaningful yet. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- src/libcamera/pipeline/raspberrypi/raspberrypi.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index dd62dfc7..763451a8 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -926,6 +926,13 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) /* Initialize the camera properties. */ data->properties_ = data->sensor_->properties(); + /* + * Set a default value for the ScalerCropMaximum property to show + * that we support its use, however, initialise it to zero because + * it's not meaningful until a camera mode has been chosen. + */ + data->properties_.set(properties::ScalerCropMaximum, Rectangle{}); + /* * We cache three things about the sensor in relation to transforms * (meaning horizontal and vertical flips). From patchwork Fri Oct 23 10:21:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10217 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A4F96C3D3C for ; Fri, 23 Oct 2020 10:22:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6ED50619A3; Fri, 23 Oct 2020 12:22:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="TwOjIVY/"; dkim-atps=neutral Received: from mail-wr1-x431.google.com (mail-wr1-x431.google.com [IPv6:2a00:1450:4864:20::431]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 79C8B615D3 for ; Fri, 23 Oct 2020 12:22:07 +0200 (CEST) Received: by mail-wr1-x431.google.com with SMTP id g12so1179622wrp.10 for ; Fri, 23 Oct 2020 03:22:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=IvJiNICrhZMootT3r4tEqLKNGICxjfRfS2QtfMEuoIU=; b=TwOjIVY/5Yhh9jx0avZ2QtLnoADkL3F+cKco7qcpAr2TueYjRTzrtHAe//fORnTKjU KX3mVQ9azMdFK+6rRhSwURxN0xzFDGS2bkAAaFCXfQlKu9AXcCn+ANCJKgQX2puyXnhv NFdk11bclBKH4nDuB2CKRGCj1UY+oWjtQeQrLCgYFFq3k0n/qAjuc8htfiUSMlOXYzZK 3T9TM+ZLrKYgXO8XNhTQqPiqz6yJdl5PD5TLsxaxSsU7HVGpNXBG+cjT9pjMcR4zAN2u uEALNFdzSyQd9Z91TJjPkQ8EaW25qTGcMQR9NB4VQxJUTUmdwevmpbHw+heulfDkymn3 XR9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=IvJiNICrhZMootT3r4tEqLKNGICxjfRfS2QtfMEuoIU=; b=axvLK+gkOlLeSUIMIzKHMwIFHr+J73YQAPon25v2g/zAjvmTWwV2oy5B5U2gJyEt+k RAEy7aK+6bmTyLvw+fiobmAyNOzmU8XsqdvwzvS2/j//U2F8IlYR0KbBgM+q8I42laHp 8Z0Xcz2TnwiI82kCb0MYWmJQHmVh+six4ivBssczKW2YAK+odpNybnsAUvWvvAeATy7S RUCUrrFb4zI6unfofkgS/uhfRpGIEzlIO4oYU/Z2uCSJznCdj2kkJYmrBmHRRhhWQypR HVQp3+MICFlBDhcwnD0cfQgFhcwVuWonLo43E+MfrG3Dgd8WORybTptSQIuB+HGWYpm9 RKtw== X-Gm-Message-State: AOAM532a7xc6Ntnfg1CNO6/tpV9/77YQOCgbmrmvAs/BekEh9o7AAw+M 9PIBSR0UirUvjOxt//iDnaGL1x2pwCN4Bw== X-Google-Smtp-Source: ABdhPJxTu01nzEmNvjUVVj0xcuJxxz6J/rnNy3jG7urx9sebqWcfI6YH9CLyxCytZpVtWNg9ORAVNA== X-Received: by 2002:a5d:49cc:: with SMTP id t12mr1964624wrs.342.1603448526690; Fri, 23 Oct 2020 03:22:06 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id r128sm2477831wma.20.2020.10.23.03.22.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Oct 2020 03:22:06 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 23 Oct 2020 11:21:58 +0100 Message-Id: <20201023102159.26274-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201023102159.26274-1-david.plowman@raspberrypi.com> References: <20201023102159.26274-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 4/5] libcamera: Add geometry helper functions X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" These functions are aimed at making it easier to calculate cropping rectangles, particularly in order to implement digital zoom. Signed-off-by: David Plowman Reviewed-by: Jacopo Mondi --- include/libcamera/geometry.h | 52 ++++++ src/libcamera/geometry.cpp | 307 +++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h index 02fb63c0..3fa9e926 100644 --- a/include/libcamera/geometry.h +++ b/include/libcamera/geometry.h @@ -13,6 +13,30 @@ namespace libcamera { +class Rectangle; + +class Point +{ +public: + constexpr Point() + : x(0), y(0) + { + } + + constexpr Point(int xpos, int ypos) + : x(xpos), y(ypos) + { + } + + int x; + int y; + + constexpr Point operator-() const + { + return { -x, -y }; + } +}; + class Size { public: @@ -31,6 +55,7 @@ public: bool isNull() const { return !width && !height; } const std::string toString() const; + Point center() const; Size &alignDownTo(unsigned int hAlignment, unsigned int vAlignment) { @@ -93,6 +118,17 @@ public: std::max(height, expand.height) }; } + + Size boundedToAspectRatio(const Size &ratio) const; + Size expandedToAspectRatio(const Size &ratio) const; + + Rectangle centeredTo(const Point ¢er) const; + + Size operator*(float factor) const; + Size operator/(float factor) const; + + Size &operator*=(float factor); + Size &operator/=(float factor); }; bool operator==(const Size &lhs, const Size &rhs); @@ -176,6 +212,11 @@ public: { } + constexpr explicit Rectangle(const Size &size) + : x(0), y(0), width(size.width), height(size.height) + { + } + int x; int y; unsigned int width; @@ -183,6 +224,17 @@ public: bool isNull() const { return !width && !height; } const std::string toString() const; + Point center() const; + Size size() const; + Point topLeft() const; + + Rectangle &scaleBy(const Size &numerator, const Size &denominator); + Rectangle &translateBy(const Point &point); + + Rectangle boundedTo(const Rectangle &bound) const; + Rectangle enclosedIn(const Rectangle &boundary) const; + Rectangle scaledBy(const Size &numerator, const Size &denominator) const; + Rectangle translatedBy(const Point &point) const; }; bool operator==(const Rectangle &lhs, const Rectangle &rhs); diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index b12e1a62..63a49e9a 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -17,6 +17,42 @@ namespace libcamera { +/** + * \class Point + * \brief Describe a point in two-dimensional space + * + * The Point structure defines a point in two-dimensional space with integer + * precision. The coordinates of a Point may be negative as well as positive. + */ + +/** + * \fn Point::Point() + * \brief Construct a Point with x and y set to 0 + */ + +/** + * \fn Point::Point(int xpos, int ypos) + * \brief Construct a Point at given \a x and \a y values + * \param[in] xpos The x-coordinate + * \param[in] ypos The y-coordinate + */ + +/** + * \var Point::x + * \brief The x-coordinate of the Point + */ + +/** + * \var Point::y + * \brief The y-coordinate of the Point + */ + +/** + * \fn Point Point::operator-() const + * \brief Negate a Point by negating both its x and y coordinates + * \return The negated point + */ + /** * \struct Size * \brief Describe a two-dimensional size @@ -61,6 +97,15 @@ const std::string Size::toString() const return std::to_string(width) + "x" + std::to_string(height); } +/** + * \brief Retrieve the center point of this size + * \return The center Point + */ +Point Size::center() const +{ + return { static_cast(width / 2), static_cast(height / 2) }; +} + /** * \fn Size::alignDownTo(unsigned int hAlignment, unsigned int vAlignment) * \brief Align the size down horizontally and vertically in place @@ -143,6 +188,105 @@ const std::string Size::toString() const * height of this size and the \a expand size */ +/** + * \brief Bound the size down to match the aspect ratio given by \a ratio + * \param[in] ratio The size whose aspect ratio must be matched + * \return A Size whose width and height are equal to the width and height + * of this Size aligned down to the aspect ratio of \a ratio + */ +Size Size::boundedToAspectRatio(const Size &ratio) const +{ + uint64_t ratio1 = static_cast(width) * + static_cast(ratio.height); + uint64_t ratio2 = static_cast(ratio.width) * + static_cast(height); + + if (ratio1 > ratio2) + return { static_cast(ratio2 / ratio.height), height }; + else + return { width, static_cast(ratio1 / ratio.width) }; +} + +/** + * \brief Expand the size to match the aspect ratio given by \a ratio + * \param[in] ratio The size whose aspect ratio must be matched + * \return A Size whose width and height are equal to the width and height + * of this Size expanded up to the aspect ratio of \a ratio + */ +Size Size::expandedToAspectRatio(const Size &ratio) const +{ + uint64_t ratio1 = static_cast(width) * + static_cast(ratio.height); + uint64_t ratio2 = static_cast(ratio.width) * + static_cast(height); + + if (ratio1 < ratio2) + return { static_cast(ratio2 / ratio.height), height }; + else + return { width, static_cast(ratio1 / ratio.width) }; +} + +/** + * \brief Center a rectangle of this size at a given Point + * \param[in] center The center point the Rectangle is to have + * + * A Rectangle of this object's size is positioned so that its center + * is at the given Point. + * + * \return A Rectangle of this size, centered at the given Point. + */ +Rectangle Size::centeredTo(const Point ¢er) const +{ + int x = center.x - width / 2; + int y = center.y - height / 2; + + return { x, y, width, height }; +} + +/** + * \brief Scale size up by the given factor + * \param[in] factor The factor + * \return The scaled Size + */ +Size Size::operator*(float factor) const +{ + return Size(width * factor, height * factor); +} + +/** + * \brief Scale size down by the given factor + * \param[in] factor The factor + * \return The scaled Size + */ +Size Size::operator/(float factor) const +{ + return Size(width / factor, height / factor); +} + +/** + * \brief Scale this size up by the given factor in place + * \param[in] factor The factor + * \return A reference to this object + */ +Size &Size::operator*=(float factor) +{ + width *= factor; + height *= factor; + return *this; +} + +/** + * \brief Scale this size down by the given factor in place + * \param[in] factor The factor + * \return A reference to this object + */ +Size &Size::operator/=(float factor) +{ + width /= factor; + height /= factor; + return *this; +} + /** * \brief Compare sizes for equality * \return True if the two sizes are equal, false otherwise @@ -365,6 +509,13 @@ bool operator==(const SizeRange &lhs, const SizeRange &rhs) * \param[in] height The height */ +/** + * \fn Rectangle::Rectangle(const Size &size) + * \brief Construct a Rectangle of \a size with its top left corner located + * at (0,0). + * \param[in] size The desired Rectangle size + */ + /** * \var Rectangle::x * \brief The horizontal coordinate of the rectangle's top-left corner @@ -404,6 +555,162 @@ const std::string Rectangle::toString() const return ss.str(); } +/** + * \brief Retrieve the center point of this rectangle + * \return The center Point + */ +Point Rectangle::center() const +{ + return { x + static_cast(width / 2), y + static_cast(height / 2) }; +} + +/** + * \brief Retrieve the size of this rectangle + * \return The Rectangle size + */ +Size Rectangle::size() const +{ + return Size(width, height); +} + +/** + * \brief Retrieve the coordinates of the top left corner of this Rectangle + * \return The Rectangle's top left corner + */ +Point Rectangle::topLeft() const +{ + return Point(x, y); +} + +/** + * \brief Apply a non-uniform rational scaling in place to this Rectangle + * \param[in] numerator The numerators of the x and y scaling factors + * \param[in] denominator The denominators of the x and y scaling factors + * + * A non-uniform scaling is applied in place such the the resulting x + * coordinates are multiplied by numerator.width / denominator.width, + * and similarly for the y coordinates (using height in place of width). + * + * \return A reference to this object + */ +Rectangle &Rectangle::scaleBy(const Size &numerator, const Size &denominator) +{ + x = static_cast(x) * numerator.width / denominator.width; + y = static_cast(y) * numerator.height / denominator.height; + width = static_cast(width) * numerator.width / denominator.width; + height = static_cast(height) * numerator.height / denominator.height; + + return *this; +} + +/** + * \brief Translate this Rectangle in place by the given Point + * \param[in] point The amount to translate the Rectangle by + * + * The Rectangle is translated in the x-direction by the point's x coordinate + * and in the y-direction by the point's y coordinate. + * + * \return A reference to this object + */ +Rectangle &Rectangle::translateBy(const Point &point) +{ + x += point.x; + y += point.y; + + return *this; +} + +/** + * \brief Calculate the intersection of this Rectangle with another + * \param[in] bound The Rectangle that is intersected with this Rectangle + * + * This method calculates the standard intersection of two rectangles. If the + * rectangles do not overlap in either the x or y direction, then the size + * of that dimension in the result (its width or height) is set to zero. Even + * when one dimension is set to zero, note that the other dimension may still + * have a positive value if there was some overlap. + * + * \return A Rectangle that is the intersection of the input rectangles + */ +Rectangle Rectangle::boundedTo(const Rectangle &bound) const +{ + int topLeftX = std::max(x, bound.x); + int topLeftY = std::max(y, bound.y); + int bottomRightX = std::min(x + width, bound.x + bound.width); + int bottomRightY = std::min(y + height, bound.y + bound.height); + + unsigned int newWidth = std::max(bottomRightX - topLeftX, 0); + unsigned int newHeight = std::max(bottomRightY - topLeftY, 0); + + return { topLeftX, topLeftY, newWidth, newHeight }; +} + +/** + * \brief Enclose a Rectangle so as not to exceed another Rectangle + * \param[in] boundary The limit that the returned Rectangle will not exceed + * + * The Rectangle is modified so that it does not exceeed the given \a boundary. + * This process involves translating the Rectangle if any of its edges + * lie beyond \a boundary, so that those edges then lie along the boundary + * instead. + * + * If either width or height are larger than \a boundary, then the returned + * Rectangle is clipped to be no larger. But other than this, the + * Rectangle is not clipped or reduced in size, merely translated. + * + * Note that this is not a conventional Rectangle intersection function + * which is provided by Rectangle::boundedTo. + * + * \return A Rectangle that does not extend beyond a boundary Rectangle + */ +Rectangle Rectangle::enclosedIn(const Rectangle &boundary) const +{ + /* We can't be bigger than the boundary rectangle. */ + Rectangle result = boundedTo(Rectangle{ x, y, boundary.size() }); + + result.x = std::clamp(result.x, boundary.x, + boundary.x + boundary.width - result.width); + result.y = std::clamp(result.y, boundary.y, + boundary.y + boundary.height - result.height); + + return result; +} + +/** + * \brief Apply a non-uniform rational scaling to this Rectangle + * \param[in] numerator The numerators of the x and y scaling factors + * \param[in] denominator The denominators of the x and y scaling factors + * + * A non-uniform scaling is applied such the the resulting x + * coordinates are multiplied by numerator.width / denominator.width, + * and similarly for the y coordinates (using height in place of width). + * + * \return The non-uniformly scaled Rectangle + */ +Rectangle Rectangle::scaledBy(const Size &numerator, const Size &denominator) const +{ + int scaledX = static_cast(x) * numerator.width / denominator.width; + int scaledY = static_cast(y) * numerator.height / denominator.height; + unsigned int scaledWidth = static_cast(width) * numerator.width / denominator.width; + unsigned int scaledHeight = static_cast(height) * numerator.height / denominator.height; + + return { scaledX, scaledY, scaledWidth, scaledHeight }; +} + +/** + * \brief Translate a Rectangle by the given amounts + * \param[in] point The amount to translate the Rectangle by + * + * The Rectangle is translated in the x-direction by the point's x coordinate + * and in the y-direction by the point's y coordinate. + * + * \return The translated Rectangle + */ +Rectangle Rectangle::translatedBy(const Point &point) const +{ + return { x + point.x, y + point.y, width, height }; +} + /** * \brief Compare rectangles for equality * \return True if the two rectangles are equal, false otherwise From patchwork Fri Oct 23 10:21:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10218 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 29D58C3D3C for ; Fri, 23 Oct 2020 10:22:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D5EFF61795; Fri, 23 Oct 2020 12:22:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="c3haEIYM"; dkim-atps=neutral Received: from mail-wm1-x330.google.com (mail-wm1-x330.google.com [IPv6:2a00:1450:4864:20::330]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4961D619AB for ; Fri, 23 Oct 2020 12:22:08 +0200 (CEST) Received: by mail-wm1-x330.google.com with SMTP id k18so919988wmj.5 for ; Fri, 23 Oct 2020 03:22:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Mj39ccQTSMuGU/dKsqpOkarhfi/ZGEAhU1hwhonA6KU=; b=c3haEIYMNQiW31aRlTcyRs4m5ikyOx476rc3L3QcQ7ObV2JlQlZ3zB+XMoqFR2dS30 WfUNp17nc4mfvdNnos3JUgMCMNFtZh30YsOnq2vSVMzkNVVJTrxlMWinITa9Zr6GT3oz MOtGdvq+++44q7dbrcxSFFKvFqx55xShhGl0+v0xpp2Mi57LyKq5L6+ftNgxU5mFCYgy 0vJRSwWSnVW4Z5Dl4TLuZj06d9U8GecZpqVV3PAJCl+it5sp1sr9gVaQkmaqV37+3WUK Fa+ISIarN55kSK9cifmUER+Av/jR/c/jodz4cUGkJ3DQRef08YsPR9Pk8koWj9OuUQCH JygQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Mj39ccQTSMuGU/dKsqpOkarhfi/ZGEAhU1hwhonA6KU=; b=F0Bkmrt+iUYkwD1B7XfD3Sfi2E05pmmG7vRF+x16SqglkdrOXX/pvEvOvWh7GZ3gUA aHLk5T4GadGAp4F0QupCpK5tP8LQTdrsrTA+P33wxcssGVYTnBUw340vTTH2zRAzF3E+ Xb/+xXHsg/P+nloZE44Hf73Iy0N9qYkeTPMfGjaQwjkcOZduQ8piLaaI1GuRlpKSJXJQ MTVw/3YRSx354E1MXF5kKxxUUWxRxtf+hY8lKkO8Fd5qkynaBjR1kmLGTKT/tXXgDvJz JpYTLQ2F9hyevEr1P3sRVwO6lo8j/V4fQvnqE6dD2pEMYansBHnQFuMnYZ5yvB0ImQ0m nc7A== X-Gm-Message-State: AOAM533Lm2jIsXtcV9IyhoBciL/DGKisLFBb78ilq4brKUfB+PkgrZAE OjWjPJs18nUUzZ2cVW0dsEjmrqV0pDRVlA== X-Google-Smtp-Source: ABdhPJyUVOF7LAg8vAxYUYMmEkvuQ9v4ADc0JkuSzd0LY+LFYDaQBuGbLR+qSbOOSAEzCIxc0KzPuA== X-Received: by 2002:a1c:ab0b:: with SMTP id u11mr1610697wme.165.1603448527567; Fri, 23 Oct 2020 03:22:07 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id r128sm2477831wma.20.2020.10.23.03.22.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Oct 2020 03:22:06 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 23 Oct 2020 11:21:59 +0100 Message-Id: <20201023102159.26274-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201023102159.26274-1-david.plowman@raspberrypi.com> References: <20201023102159.26274-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 5/5] libcamera: pipeline: raspberrypi: Implementation of digital zoom X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" During configure() we update the ScalerCropMaximum to the correct value for this camera mode and work out the minimum crop size allowed by the ISP. Whenever a new ScalerCrop request is received we check it's valid and apply it to the ISP V4L2 device. When the IPA returns its metadata to us we add the ScalerCrop information, rescaled to sensor native pixels. Signed-off-by: David Plowman Reviewed-by: Jacopo Mondi --- include/libcamera/ipa/raspberrypi.h | 1 + src/ipa/raspberrypi/raspberrypi.cpp | 5 ++ .../pipeline/raspberrypi/raspberrypi.cpp | 82 +++++++++++++++---- 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index b23baf2f..ff2faf86 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -62,6 +62,7 @@ static const ControlInfoMap Controls = { { &controls::Saturation, ControlInfo(0.0f, 32.0f) }, { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, + { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, }; } /* namespace RPi */ diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 1c255aa2..f338ff8b 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -699,6 +699,11 @@ void IPARPi::queueRequest(const ControlList &controls) break; } + case controls::SCALER_CROP: { + /* We do nothing with this, but should avoid the warning below. */ + break; + } + default: LOG(IPARPI, Warning) << "Ctrl " << controls::controls.at(ctrl.first)->name() diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 763451a8..83e91576 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -193,6 +193,11 @@ public: bool flipsAlterBayerOrder_; BayerFormat::Order nativeBayerOrder_; + /* For handling digital zoom. */ + CameraSensorInfo sensorInfo_; + Rectangle lastIspCrop_; + Size ispMinSize_; + unsigned int dropFrameCount_; private: @@ -677,26 +682,31 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) return ret; } - /* Adjust aspect ratio by providing crops on the input image. */ - Rectangle crop{ 0, 0, sensorFormat.size }; - - int ar = maxSize.height * sensorFormat.size.width - maxSize.width * sensorFormat.size.height; - if (ar > 0) - crop.width = maxSize.width * sensorFormat.size.height / maxSize.height; - else if (ar < 0) - crop.height = maxSize.height * sensorFormat.size.width / maxSize.width; + /* Figure out the smallest selection the ISP will allow. */ + Rectangle testCrop(0, 0, 1, 1); + data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop); + data->ispMinSize_ = testCrop.size(); - crop.width &= ~1; - crop.height &= ~1; + /* Adjust aspect ratio by providing crops on the input image. */ + Size size = sensorFormat.size.boundedToAspectRatio(maxSize); + Rectangle crop = size.centeredTo(sensorFormat.size.center()); + data->lastIspCrop_ = crop; - crop.x = (sensorFormat.size.width - crop.width) >> 1; - crop.y = (sensorFormat.size.height - crop.height) >> 1; data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); ret = data->configureIPA(config); if (ret) LOG(RPI, Error) << "Failed to configure the IPA: " << ret; + /* + * Update the ScalerCropMaximum to the correct value for this camera mode. + * For us, it's the same as the "analogue crop". + * + * \todo Make this property the ScalerCrop maximum value when dynamic + * controls are available and set it at validate() time + */ + data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop); + return ret; } @@ -1154,8 +1164,8 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) ipaConfig.data.push_back(static_cast(lsTable_.fd())); } - CameraSensorInfo sensorInfo = {}; - int ret = sensor_->sensorInfo(&sensorInfo); + /* We store the CameraSensorInfo for digital zoom calculations. */ + int ret = sensor_->sensorInfo(&sensorInfo_); if (ret) { LOG(RPI, Error) << "Failed to retrieve camera sensor info"; return ret; @@ -1164,7 +1174,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) /* Ready the IPA - it must know about the sensor resolution. */ IPAOperationData result; - ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, + ipa_->configure(sensorInfo_, streamConfig, entityControls, ipaConfig, &result); unsigned int resultIdx = 0; @@ -1243,8 +1253,23 @@ void RPiCameraData::queueFrameAction([[maybe_unused]] unsigned int frame, FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId); handleStreamBuffer(buffer, &isp_[Isp::Stats]); + /* Fill the Request metadata buffer with what the IPA has provided */ - requestQueue_.front()->metadata() = std::move(action.controls[0]); + Request *request = requestQueue_.front(); + request->metadata() = std::move(action.controls[0]); + + /* + * Also update the ScalerCrop in the metadata with what we actually + * used. But we must first rescale that from ISP (camera mode) pixels + * back into sensor native pixels. + * + * Sending this information on every frame may be helpful. + */ + Rectangle crop = lastIspCrop_.scaledBy(sensorInfo_.analogCrop.size(), + sensorInfo_.outputSize); + crop.translateBy(sensorInfo_.analogCrop.topLeft()); + request->metadata().set(controls::ScalerCrop, crop); + state_ = State::IpaComplete; break; } @@ -1595,6 +1620,31 @@ void RPiCameraData::tryRunPipeline() /* Take the first request from the queue and action the IPA. */ Request *request = requestQueue_.front(); + if (request->controls().contains(controls::ScalerCrop)) { + Rectangle crop = request->controls().get(controls::ScalerCrop); + + if (crop.width && crop.height) { + /* First scale the crop from sensor native to camera mode pixels. */ + crop.translateBy(-sensorInfo_.analogCrop.topLeft()); + crop.scaleBy(sensorInfo_.outputSize, sensorInfo_.analogCrop.size()); + + /* + * The crop that we set must be: + * 1. At least as big as ispMinSize_, once that's been + * enlarged to the same aspect ratio. + * 2. With the same mid-point, if possible. + * 3. But it can't go outside the sensor area. + */ + Size minSize = ispMinSize_.expandedToAspectRatio(crop.size()); + Size size = crop.size().expandedTo(minSize); + crop = size.centeredTo(crop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize)); + + if (crop != lastIspCrop_) + isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); + lastIspCrop_ = crop; + } + } + /* * Process all the user controls by the IPA. Once this is complete, we * queue the ISP output buffer listed in the request to start the HW