From patchwork Mon Oct 19 12:51:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10095 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 61A63BDB1F for ; Mon, 19 Oct 2020 12:52:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1D066610C4; Mon, 19 Oct 2020 14:52: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="ExwhhcBo"; dkim-atps=neutral Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 14F28610C4 for ; Mon, 19 Oct 2020 14:52:06 +0200 (CEST) Received: by mail-wm1-x32f.google.com with SMTP id f21so10170655wml.3 for ; Mon, 19 Oct 2020 05:52: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=hJsZqhRBKPAhfb5Do1hHK1Ex21turn5cBuhGVM8owwA=; b=ExwhhcBoQnxC7I9G6YG/w+6vhWsChaVlCqKxzUBmrGeDRR3dkDgxggBk6ahTwkVpcj bY0VMnu5GNad9hXjFtMXrw2O7LhXzy/hvTlx/VhcSLBzHz2QDnE/tcTSOvftQGs6WjoY GxG3nwP6R1KzAQErCKYytzmPqjZjEcQJB251GNNXocWUTnRFYpZ3NSVkrQEVig7ZNIAa COvKm67j2DWUfBRXTjiDgxctBBo6eZ9qPjoH8yCaAevc0d4J7kHddN3S6kgtgkvQ0ZhC uKxETUyOUnD+OBewNBDhulQfqa4IDTiuy+Wq1TApVfgjqG+8ye3xZrds+MUnv/cXcRFM qy+A== 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=hJsZqhRBKPAhfb5Do1hHK1Ex21turn5cBuhGVM8owwA=; b=mVS0O0+GxwTCV4oz9mn9VS3sciKcIEpGfOho61t+f/KHyAxL4VJjvukyjEWDjjP2CJ yNdxVU6DG1twCPTeQ/+aeI3yPU9EpcPjB0N5QQxHowkH1ip6CMlF2eHnV2CjN29u3PUu 0X3NZXF2ovrCZr/bHZhsuh32SWgv5QfdB3ZnQKo9KtI62dUx7S6B7U1Mql3Q0iblxOQk KvrXcvVKcauF/4lhSjaFFWJK/ERBI/A8fpNeFhDnAvMHb3r9U2OxsKwtSJIB8W5pplWb Sibze4PqQaOWErBCvDiRTYhwLU8+OtzWs2quJz0vNl7z5yeufyi05s7JYDMoagvZex2h m9vQ== X-Gm-Message-State: AOAM530s30phU2cQkptM0WF3WVhePdm2YKSCNzDXXc+Pkd9eNeOFLpXH ZIVhreamDS25KcjnPLJJEku0eK78hsZ6Lg== X-Google-Smtp-Source: ABdhPJxc79Uot1d2JPTiTH0B9olKGckjMyjhjhcR1rIdMFHEgKhdBRSHLZWu6uT75E3OUujTnE3C6A== X-Received: by 2002:a1c:62c4:: with SMTP id w187mr17759684wmb.149.1603111925583; Mon, 19 Oct 2020 05:52:05 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id q6sm16634335wma.0.2020.10.19.05.52.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Oct 2020 05:52:05 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 19 Oct 2020 13:51:52 +0100 Message-Id: <20201019125156.26751-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201019125156.26751-1-david.plowman@raspberrypi.com> References: <20201019125156.26751-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 1/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 --- src/libcamera/property_ids.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml index 7261263a..022cf65d 100644 --- a/src/libcamera/property_ids.yaml +++ b/src/libcamera/property_ids.yaml @@ -663,4 +663,24 @@ controls: \todo Rename this property to ActiveAreas once we will have property categories (i.e. Properties::PixelArray::ActiveAreas) + - ScalerCropMaximum: + type: Rectangle + description: | + The size and location, in native sensor pixels, of the part of the + sensor that is rescaled to produce the output images. Note that the + units remain native sensor pixels, even if the sensor is being used in + a binning skipping or scaling mode. + + The (x,y) location of this rectangle is relative to the + PixelArrayActiveArea that is being used. The property also takes into + account any further cropping being done by the CSI-2 receiver or + elsewhere. + + 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 Mon Oct 19 12:51:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10096 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 A7E16BDB1F for ; Mon, 19 Oct 2020 12:52:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 72147613AD; Mon, 19 Oct 2020 14:52:10 +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="lcdgmQOp"; 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 D8345610C4 for ; Mon, 19 Oct 2020 14:52:06 +0200 (CEST) Received: by mail-wm1-x344.google.com with SMTP id q5so12730870wmq.0 for ; Mon, 19 Oct 2020 05:52: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=uIX91jVoGdT6Gr8iX2Oiu/Y8Rs2VGCZWrOox2SJ1UZE=; b=lcdgmQOptFl7DL6ncRlgQKj7HLxeNw7dziajMgvrSDNJ00WwrDxUpaq0V1+JU2kzZp TAFynin5IlLdvLWszDAGmVkMmRmY4kfq5tWsZpDVQQpOt7VEiTkOP8OWeCXcQvzKnIQm TO7WRlY27ErckdH3Ld9sR11BAgcFdr06dcHBT+iHXpkrqvp/sCaFiZKY2ghwVpQza5FC yL0RWo0BwlncfNa27A8HVbvuZEcxMt1yE0WdSjnZ5PfUgzb2S4hLsMFWcWU6Rq4Y0J+g yIGf4GKIne+1hpnaLtwrA7CaLN6LC2ILd0cGdxjc803Y97h15cFrsC0nmuzRkaZwc28f FaEw== 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=uIX91jVoGdT6Gr8iX2Oiu/Y8Rs2VGCZWrOox2SJ1UZE=; b=ntd8hfWV/o2oPA75xUtd0Q0KLV1PYInAHZLh94KLgBKr+7oBUV0HDTO2knFrruLdaU k3fxremYe/f6ukf+5NATrhisIquQc0zFzWEJ4506atU9cv9bBtbehPlj7pvBNr9wmcY+ wUtpT+XX00NcsQ6/zVGTOcWJx88RF1884dvcm4XsIRTjIuTN7CyGH6SgZRruQiptpaA+ sHs06umxwnkyRyzcWG6oiypAOYbuUuTLSxmfjvnkOoUWydC9KmVG3hozZGEXT+dPVwmJ 2dsH/iON0FAtHlIxJtQNqF3IZbZhk3VkEA36ev9abjyiVSfmOxHtQKnLmOEQepL7A1dK BxPA== X-Gm-Message-State: AOAM533GoQi9n1RUKvpgdUPvj9NhRprE94gIzpKyW4WC9K4D4M4n7aCQ JtaFG04g4qvX9L8YJ0sazcizz8a7TRjDng== X-Google-Smtp-Source: ABdhPJwSa9kQNuHWbtKx3DHvp+4Tpi2Vh5B4wgdaD92EyZXPr/12xoBTBDhCq6kStrrQAq1zO5bkpw== X-Received: by 2002:a1c:7707:: with SMTP id t7mr18573074wmi.54.1603111926355; Mon, 19 Oct 2020 05:52:06 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id q6sm16634335wma.0.2020.10.19.05.52.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Oct 2020 05:52:05 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 19 Oct 2020 13:51:53 +0100 Message-Id: <20201019125156.26751-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201019125156.26751-1-david.plowman@raspberrypi.com> References: <20201019125156.26751-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 2/5] libcamera: Initialise the ScalerCropMaximum 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" Add a default initialisation according to the sensor resolution, though it will need updating when the camera mode changes. Signed-off-by: David Plowman --- src/libcamera/camera_sensor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp index 78c7ceec..ae25c5c6 100644 --- a/src/libcamera/camera_sensor.cpp +++ b/src/libcamera/camera_sensor.cpp @@ -279,6 +279,12 @@ int CameraSensor::init() */ resolution_ = sizes_.back(); + /* + * Set a default value for the ScalerCropMaximum, though it will have to + * be updated when new camera modes are chosen. + */ + properties_.set(properties::ScalerCropMaximum, Rectangle{ 0, 0, resolution_ }); + return 0; } From patchwork Mon Oct 19 12:51:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10097 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 1B3F1BDB1F for ; Mon, 19 Oct 2020 12:52:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DD9C261373; Mon, 19 Oct 2020 14:52:10 +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="oJVw8URS"; dkim-atps=neutral Received: from mail-wr1-x429.google.com (mail-wr1-x429.google.com [IPv6:2a00:1450:4864:20::429]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CEF2B613B7 for ; Mon, 19 Oct 2020 14:52:07 +0200 (CEST) Received: by mail-wr1-x429.google.com with SMTP id t9so11255409wrq.11 for ; Mon, 19 Oct 2020 05:52: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=4HzMT0ebVPZrBLwm6+ejkeV+aBJ486+lHoNIiQ8cjD0=; b=oJVw8URSBASdHW0bGhKU/oEsN1jTMVxEhMfQ0G5jGdBia81h5g21UUMnURR9g7jwAg sfmyV5RfRoh3EWWhA8jaw1sBP0xf0oDodyg7mtjolgw0b9W5mn6buf93Y8GQmgPzOis5 /x2bE1/b7tWlargcmwN2huviB2byMmkEXJZeKCWlfYpnsgWDK2usH+qwZhRmCMuYA4u1 Oci2HPkAf/igdHGMD6ug2jhSifaeYsI3JcpE81dh4W9ekA9wnCYDnC0iXOr49f4cpY7G wXWI5Kg4ibw2Ja7iLuaTOFmYq5R0cp3iK3XvirxPNumSLprzlN4gsveOtxLTMbT741Gh eq2g== 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=4HzMT0ebVPZrBLwm6+ejkeV+aBJ486+lHoNIiQ8cjD0=; b=AXYIifwapx+aF9D3O/RxK1HwY867iHWlyn8wUbX+4cdFxsYMD4tgC5XoOD0FQcjmId j5T52fMt7ENE5tq4InRkibTnbcnE/SRuLC5GebX+J4PWZMN+rzNhuz+d1puujSwK8KzD PalXWU0iHvfZrcrplBQd+XUmouItw0euvWnyfIsaJlu/HBgsfktY5hJ9ExBOXpaBl1Ba MQm3uy3UV3Wals6Ck6bGVpJs+lf65KMrjYtbRqAjYvGcWRuqZqb6E8oZzMLN0+imv428 9bhS8u9XQ+wWuRQsDkMfy+y6C2JaoRpONXhBSr6T/S+fJc4D/4EjqxP2quIPqkZ/Gkxq QcgQ== X-Gm-Message-State: AOAM532AEKs0fEx27vzPfY52dpE6PabguEry/dzr7Q9wtLdFCVx2DDeL f4Cck2Q8HGPO0dPFnFsxL4+Omv7Udtl23g== X-Google-Smtp-Source: ABdhPJyc1dxeJUCjJg/LeB3jbXKxtxS8uYcRTQ+gOfTaOPu8kQCqjOCp3M4xpbO+nhbxTpQaDgjbMg== X-Received: by 2002:adf:f103:: with SMTP id r3mr21155631wro.153.1603111927238; Mon, 19 Oct 2020 05:52:07 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id q6sm16634335wma.0.2020.10.19.05.52.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Oct 2020 05:52:06 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 19 Oct 2020 13:51:54 +0100 Message-Id: <20201019125156.26751-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201019125156.26751-1-david.plowman@raspberrypi.com> References: <20201019125156.26751-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 3/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 output image. It can be used to implement digital zoom. Signed-off-by: David Plowman --- src/libcamera/control_ids.yaml | 12 ++++++++++++ src/libcamera/property_ids.yaml | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml index 4c415545..c6fbcd56 100644 --- a/src/libcamera/control_ids.yaml +++ b/src/libcamera/control_ids.yaml @@ -284,4 +284,16 @@ controls: order in an array of 9 floating point values. size: [3x3] + + - ScalerCrop: + type: Rectangle + description: | + Sets the image portion that will be scaled up to form the whole of + the final output image. The selection rectangle is expressed in the + sensor's native pixels and defined relative to the size of the frame + described by the ScalerCropMaximum property. + + This control can be used to implement digital zoom. + + \sa properties::ScalerCropMaximum ... diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml index 022cf65d..2d0fe9d3 100644 --- a/src/libcamera/property_ids.yaml +++ b/src/libcamera/property_ids.yaml @@ -678,7 +678,10 @@ controls: This property is valid only after the Camera has been successfully configured and its value changes whenever a new configuration is - applied. + applied. It can be used to implement digital zoom in conjunction with + the ScalerCrop control. + + \sa controls::ScalerCrop \todo Turn this property into a "maximum control value" for the ScalerCrop control once "dynamic" controls have been implemented. From patchwork Mon Oct 19 12:51: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: 10098 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 B1AA4BDB1F for ; Mon, 19 Oct 2020 12:52:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7DB2D610C4; Mon, 19 Oct 2020 14:52: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="cQDY6znX"; 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 D2C54613AD for ; Mon, 19 Oct 2020 14:52:08 +0200 (CEST) Received: by mail-wm1-x330.google.com with SMTP id k18so12708412wmj.5 for ; Mon, 19 Oct 2020 05:52: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=FlJbCPowPNQgdLxZw1xW4nGcr1UMbOQEpUkrtChTo/Y=; b=cQDY6znXifRUJfx8/gn21swSQSrs4eJmxuNiZUcoHTIcz5dlTM78pG6PtPh2aBGexK Te8j8+Zc4N95myGbICIAZ54L3MEF+iDNKTmAS0ibTn93dwNnMBIHzh3LBvtVN+Q2oCNy XuzVcFWUNWp4H+pEtD1DqPg2UDomhwgNr7Xji98F9Csh/+JOxy/IQxl9Qwrfjau2TI/h PNXmlv2UowWWdrWBfncMrSJvw+7Lkdqgz23bYBUdTlak6aIfy0olj25gV8DDkCF9fhtD 1QTgFMK7nMPRiNh4PakCDjdKL073btKudyhJsxGNhY8pFPBGDjY3vunxuQKUAkU5QOVB sSFA== 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=FlJbCPowPNQgdLxZw1xW4nGcr1UMbOQEpUkrtChTo/Y=; b=PQoya/HWZ5n1dXSRw8eiv2EI1DHaaDQ4t3nRMSwKVMWQJvtMYIi1fAdbp0eopqsw58 0mNSRZQclOhc42N1SmSCxMY2VzP6PCTzRJEPqR/sATNLDbp8qMPJMT9W4WIQ6qpawAmx RJj1T0yTHGXUaMGxoedkJKSRMDB/60k0xNlPmSr0X6BNcth7wN5EFCZ+mZak/BpnQ47E OTVbQYU8KBwY5Ios7lvekge1yrmoFFWiRGNvAcANBu89OYNAuPfE7I5gPZo8Q37tyoRO 8fAN4a0bhFBc/BQH2iIh/6ybERQetdJiEK1XApk71odLUyMXa9/QvCsud9oT6O1PRJx4 nEoQ== X-Gm-Message-State: AOAM530/es67v83i9sl8OWF7Q0nNiIF0gWvmO1rScLvdW0z1PIm8iMUt e4WCIOOWdopzpnXpzfBoSQcIWVn/Jq/FYQ== X-Google-Smtp-Source: ABdhPJwN22WS7uqT48E/38ccVfOMnkv41Qmow706CosCE5vDEwmXCBFP+pjRSb6W+l7j6zmDNDqrLQ== X-Received: by 2002:a1c:791a:: with SMTP id l26mr18363116wme.163.1603111928059; Mon, 19 Oct 2020 05:52:08 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id q6sm16634335wma.0.2020.10.19.05.52.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Oct 2020 05:52:07 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 19 Oct 2020 13:51:55 +0100 Message-Id: <20201019125156.26751-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201019125156.26751-1-david.plowman@raspberrypi.com> References: <20201019125156.26751-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 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 --- include/libcamera/geometry.h | 53 +++++++ src/libcamera/geometry.cpp | 296 +++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h index 02fb63c0..5935cc63 100644 --- a/include/libcamera/geometry.h +++ b/include/libcamera/geometry.h @@ -13,6 +13,25 @@ namespace libcamera { +class Point +{ +public: + constexpr Point() + : x(0), y(0) + { + } + + constexpr Point(int xpos, int ypos) + : x(xpos), y(ypos) + { + } + + int x; + int y; +}; + +class Rectangle; + class Size { public: @@ -32,6 +51,8 @@ public: bool isNull() const { return !width && !height; } const std::string toString() const; + Point center() const; + Size &alignDownTo(unsigned int hAlignment, unsigned int vAlignment) { width = width / hAlignment * hAlignment; @@ -93,8 +114,19 @@ 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*(const Size &size, float factor); +Size operator/(const Size &size, float factor); + +Size &operator*=(Size &size, float factor); +Size &operator/=(Size &size, float factor); + bool operator==(const Size &lhs, const Size &rhs); bool operator<(const Size &lhs, const Size &rhs); @@ -176,6 +208,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 +220,22 @@ public: bool isNull() const { return !width && !height; } const std::string toString() const; + + Point center() const; + + Size size() const; + + Rectangle &translateBy(int dx, int dy); + + Rectangle &rescaleTo(const Size &from, const Size &to); + + Rectangle translatedBy(int dx, int dy) const; + + Rectangle rescaledTo(const Size &from, const Size &to) const; + + Rectangle boundedTo(const Rectangle &bound) const; + + Rectangle clampedTo(const Rectangle &boundary) const; }; bool operator==(const Rectangle &lhs, const Rectangle &rhs); diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index b12e1a62..75741c1e 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -17,6 +17,36 @@ namespace libcamera { +/** + * \struct 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 x, int y) + * \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 + */ + /** * \struct Size * \brief Describe a two-dimensional size @@ -61,6 +91,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 +182,101 @@ 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 + * \return The scaled Size + */ +Size operator*(const Size &size, float factor) +{ + return Size(size.width * factor, size.height * factor); +} + +/** + * \brief Scale size down by the given factor + * \return The scaled Size + */ +Size operator/(const Size &size, float factor) +{ + return Size(size.width / factor, size.height / factor); +} + +/** + * \brief Scale size up by the given factor in place + * \return The scaled Size + */ +Size &operator*=(Size &size, float factor) +{ + size.width *= factor; + size.height *= factor; + return size; +} + +/** + * \brief Scale size down by the given factor in place + * \return The scaled Size + */ +Size &operator/=(Size &size, float factor) +{ + size.width /= factor; + size.height /= factor; + return size; +} + /** * \brief Compare sizes for equality * \return True if the two sizes are equal, false otherwise @@ -365,6 +499,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 +545,161 @@ 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 Translate this Rectangle by the given amounts in place + * \param[in] dx The amount to translate the Rectangle in the x direction + * \param[in] dy The amount to translate the Rectangle in the y direction + * + * The Rectangle is translated by the given amounts \a dx and \a dy. + * + * \return A reference to this object + */ +Rectangle &Rectangle::translateBy(int dx, int dy) +{ + x += dx; + y += dy; + + return *this; +} + +/** + * \brief Rescale this Rectangle in place from one coordinate frame to another + * \param[in] from The current coordinate frame of the Rectangle + * \param[in] to The new coordinate frame for the Rectangle + * + * The Rectangle can be regarded as being embedded within a coordinate frame + * given by \a from. It is rescaled so as to occupy the equivalent part of the + * new coordinate frame given by \a to. + * + * This is purely a scaling operation, and the Rectangle does not need to lie + * inside the coordinate frames. + * + * \return A reference to this object + */ +Rectangle &Rectangle::rescaleTo(const Size &from, const Size &to) +{ + x = static_cast(x) * to.width / from.width; + y = static_cast(y) * to.height / from.height; + width = static_cast(width) * to.width / from.width; + height = static_cast(height) * to.height / from.height; + + return *this; +} + +/** + * \brief Translate a Rectangle by the given amounts + * \param[in] dx The amount to translate the Rectangle in the x direction + * \param[in] dy The amount to translate the Rectangle in the y direction + * + * The Rectangle is translated by the given amounts \a dx and \a dy. + * + * \return The translated Rectangle + */ +Rectangle Rectangle::translatedBy(int dx, int dy) const +{ + return { x + dx, y + dy, width, height }; +} + +/** + * \brief Rescale a Rectangle from one coordinate frame to another + * \param[in] from The coordinate frame of the given Rectangle + * \param[in] to The new coordinate frame for the new Rectangle + * + * The Rectangle can be regarded as being embedded within a coordinate frame + * given by \a from. A new Rectangle, rescaled so as to occupy the equivalent + * part of the new coordinate frame given by \a to, is created and returned. + * + * This is purely a scaling operation, and the Rectangle does not need to lie + * inside the coordinate frames. + * + * \return The rescaled Rectangle + */ +Rectangle Rectangle::rescaledTo(const Size &from, const Size &to) const +{ + int scaledX = static_cast(x) * to.width / from.width; + int scaledY = static_cast(y) * to.height / from.height; + unsigned int scaledWidth = static_cast(width) * to.width / from.width; + unsigned int scaledHeight = static_cast(height) * to.height / from.height; + + return { scaledX, scaledY, scaledWidth, scaledHeight }; +} + +/** + * \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); + + int newWidth = bottomRightX - topLeftX; + int newHeight = bottomRightY - topLeftY; + + return { topLeftX, topLeftY, + static_cast(std::max(newWidth, 0)), + static_cast(std::max(newHeight, 0)) }; +} + +/** + * \brief Clamp 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. + * + * We note that this is not a conventional Rectangle intersection function + * \sa Rectangle::boundedTo + * + * \return A Rectangle that does not extend beyond a boundary Rectangle + */ +Rectangle Rectangle::clampedTo(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 Compare rectangles for equality * \return True if the two rectangles are equal, false otherwise From patchwork Mon Oct 19 12:51: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: 10099 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 4DFFCBDB1F for ; Mon, 19 Oct 2020 12:52:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F197360CE6; Mon, 19 Oct 2020 14:52: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="Ua6sUqmZ"; dkim-atps=neutral Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 728CD613AD for ; Mon, 19 Oct 2020 14:52:09 +0200 (CEST) Received: by mail-wr1-x42a.google.com with SMTP id j7so7197855wrt.9 for ; Mon, 19 Oct 2020 05:52:09 -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=Da3jfoEzqJ/NANYN3bEr5iKNEQ2R+Mn1bTNJdEXTVFk=; b=Ua6sUqmZxZ0EY/shdVdYAKjlwZo7YFiERPfEhkpRVL6T/iFOUxn51/vpNW0GULdYp+ w+JVk8xAYIVlnYjo+n4GIVjd2E9ALUeEG6OFBqPTAjsKUOGcwP9pzFt9Hb5sfZXc4eUz pdhr7iVkUQMaozVL4b3x2yB3589q62sjWhiwORAWi+/2WKDKxFCmoLgN+c1QPAzXPUA3 WWh6Kh513jW8K83AfHK0NpikK3r+0VnKmJm0XJYhesE/R+8eTWZ8BUSKQqoq/KgsdxDw opgISN1NPqU43PTEmrHI3Ad4mpgzHZ/qUsTheqHjoIty/AcBbjOOn9Z+IZB8ZD1gae3H tzgw== 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=Da3jfoEzqJ/NANYN3bEr5iKNEQ2R+Mn1bTNJdEXTVFk=; b=olp4htdYahCmyXFGqZ27FM1KIADtponD4mguQuD5Pcp6wa0sWKCZAKL54C1KBkkdWS ui2EuLDgiwqhrmYInPjXeRYU6C3LgXkfIaUFajcVQZicbLqLbep0l4ConQmwGrRkdLYb OuBpAnGuIzCUJF9C0YnfMCitxwug2pJVFx5J1UizTJxNonxOVUiXdika/M/EWPb9HHKs GZfiHKftsK12IBdKlIDDcMODZo+ZupzsjwPaqeN2J6YyeswajflRhCgVpE+XP99+he6J LgIupfQRfYvc/c8C6uJsi9MgZIDW7jZUdBypULS2pMEg/q1RdKFlM2QJaO6euNJ270DN m4zA== X-Gm-Message-State: AOAM53189c4vmqp2Tpb4DjKnTWRLnsPM/YJu5k5Dz/jQFebikqQI+Ffc eCGs+jZgrFi1Eug3W2+SDgkuX/1+IggJVQ== X-Google-Smtp-Source: ABdhPJzU4lXl9D53GGPW30F3rsVoVSnIb6R+CmUwkBDvKJZo6fBanDzOyQOtA3SL8MCpa9HlbUyENw== X-Received: by 2002:a5d:424e:: with SMTP id s14mr21392702wrr.149.1603111928827; Mon, 19 Oct 2020 05:52:08 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id q6sm16634335wma.0.2020.10.19.05.52.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Oct 2020 05:52:08 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 19 Oct 2020 13:51:56 +0100 Message-Id: <20201019125156.26751-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201019125156.26751-1-david.plowman@raspberrypi.com> References: <20201019125156.26751-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 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 (which we also note internally) 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. We also forward it to the IPA so that the IPA can return the values used in the image metadata. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- include/libcamera/ipa/raspberrypi.h | 1 + src/ipa/raspberrypi/raspberrypi.cpp | 7 ++ .../pipeline/raspberrypi/raspberrypi.cpp | 70 +++++++++++++++---- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index b3041591..bb4e1a8c 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -60,6 +60,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 48a72dd2..e777f0b4 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -699,6 +699,13 @@ void IPARPi::queueRequest(const ControlList &controls) break; } + case controls::SCALER_CROP: { + /* Just copy the information back. */ + Rectangle crop = ctrl.second.get(); + libcameraMetadata_.set(controls::ScalerCrop, crop); + 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 26dbd257..43b860ae 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,30 @@ 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. */ + Rectangle crop = sensorFormat.size.boundedToAspectRatio(maxSize).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; } @@ -1147,8 +1156,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; @@ -1157,7 +1166,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; @@ -1588,6 +1597,37 @@ 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.x, -sensorInfo_.analogCrop.y); + crop.rescaleTo(sensorInfo_.analogCrop.size(), sensorInfo_.outputSize); + + /* + * 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()).clampedTo(Rectangle(sensorInfo_.outputSize)); + + if (crop != lastIspCrop_) + isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); + lastIspCrop_ = crop; + } + + /* We must scale the actual crop used back into native pixels. */ + crop = lastIspCrop_.rescaledTo(sensorInfo_.outputSize, sensorInfo_.analogCrop.size()); + crop.translateBy(sensorInfo_.analogCrop.x, sensorInfo_.analogCrop.y); + + request->controls().set(controls::ScalerCrop, 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