@@ -58,6 +58,7 @@ static const ControlInfoMap RPiControls = {
{ &controls::Saturation, ControlInfo(0.0f, 32.0f) },
{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
{ &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) },
+ { &controls::IspCrop, ControlInfo({}, Rectangle(65535, 65535, 65535, 65535), {}) },
};
} /* namespace libcamera */
@@ -687,6 +687,13 @@ void IPARPi::queueRequest(const ControlList &controls)
break;
}
+ case controls::ISP_CROP: {
+ /* Just copy the information back. */
+ Rectangle crop = ctrl.second.get<Rectangle>();
+ libcameraMetadata_.set(controls::IspCrop, crop);
+ break;
+ }
+
default:
LOG(IPARPI, Warning)
<< "Ctrl " << controls::controls.at(ctrl.first)->name()
@@ -182,6 +182,11 @@ public:
std::queue<FrameBuffer *> embeddedQueue_;
std::deque<Request *> requestQueue_;
+ /* For handling digital zoom. */
+ Size ispMinSize_;
+ Size sensorOutputSize_;
+ Rectangle lastIspCrop_;
+
unsigned int dropFrameCount_;
private:
@@ -502,6 +507,14 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
*/
ret = data->isp_[Isp::Input].dev()->setFormat(&sensorFormat);
+ /*
+ * Update the SensorOutputSize to the correct value for this camera mode.
+ *
+ * \todo Move this property to CameraConfiguration when that
+ * feature will be made available and set it at validate() time
+ */
+ data->properties_.set(properties::SensorOutputSize, sensorFormat.size);
+
/*
* See which streams are requested, and route the user
* StreamConfiguration appropriately.
@@ -592,6 +605,16 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
return ret;
}
+ /*
+ * Figure out the smallest selection the ISP will allow. We also store
+ * the output image size, in pixels, from the sensor. These will be
+ * used for digital zoom.
+ */
+ Rectangle testCrop(0, 0, 1, 1);
+ data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop);
+ data->ispMinSize_ = testCrop.size();
+ data->sensorOutputSize_ = sensorFormat.size;
+
/* Adjust aspect ratio by providing crops on the input image. */
Rectangle crop{ 0, 0, sensorFormat.size };
@@ -608,6 +631,8 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
crop.y = (sensorFormat.size.height - crop.height) >> 1;
data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);
+ data->lastIspCrop_ = crop;
+
ret = data->configureIPA();
if (ret)
LOG(RPI, Error) << "Failed to configure the IPA: " << ret;
@@ -1449,6 +1474,28 @@ void RPiCameraData::tryRunPipeline()
/* Take the first request from the queue and action the IPA. */
Request *request = requestQueue_.front();
+ if (request->controls().contains(controls::IspCrop)) {
+ Rectangle crop = request->controls().get<Rectangle>(controls::IspCrop);
+ if (crop.width && crop.height) {
+ /*
+ * 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_.alignedUpToAspectRatio(crop.size());
+ Size size = crop.size().expandedTo(minSize);
+ crop = size.centredTo(crop).boundedTo(Rectangle(sensorOutputSize_));
+
+ if (crop != lastIspCrop_)
+ isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);
+ lastIspCrop_ = crop;
+ }
+
+ request->controls().set(controls::IspCrop, lastIspCrop_);
+ }
+
/*
* 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