[1/4] software-isp: Center the GPU image crop
diff mbox series

Message ID 20261015194335.287960-2-halfline@gmail.com
State New
Headers show
Series
  • software-isp: Align GPU cropping and statistics
Related show

Commit Message

Ray Strode Oct. 15, 2026, 7:43 p.m. UTC
From: Ray Strode <rstrode@redhat.com>

The GPU debayer path scales the image to fill a requested output size
and crops any excess image area.

Its projection positions the scaled image against one side of the
output, so aspect-ratio changes and Bayer border removal shift the
visible scene.

This commit centers the projection on the output region in sensor
coordinates, distributing the crop across both sides.

Signed-off-by: Ray Strode <rstrode@redhat.com>
---
 src/libcamera/software_isp/debayer_egl.cpp | 23 +++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 300822f9..0f515c5e 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -364,70 +364,87 @@  std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
 std::tuple<unsigned int, unsigned int>
 DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
 {
 	/* Align stride to 256 bytes as a generic GPU memory access alignment */
 	const PixelFormatInfo &info = PixelFormatInfo::info(outputFormat);
 	return std::make_tuple(info.stride(size.width, 0, 256), info.frameSize(size, 256));
 }
 
 uint32_t DebayerEGL::preferredInputStride(const PixelFormat &inputFormat, const Size &size)
 {
 	const PixelFormatInfo &info = PixelFormatInfo::info(inputFormat);
 	return info.stride(size.width, 0, 256);
 }
 
 void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerParams &params)
 {
 	/*
 	 * Raw Bayer 8-bit, and packed raw Bayer 10-bit/12-bit formats
 	 * are stored in a GL_LUMINANCE texture. The texture width is
 	 * equal to the stride.
 	 */
 	GLfloat firstRed[] = { firstRed_x_, firstRed_y_ };
 	GLfloat imgSize[] = { (GLfloat)width_,
 			      (GLfloat)height_ };
 	GLfloat Step[] = { static_cast<float>(bytesPerPixel_) / (inputConfig_.stride - 1),
 			   1.0f / (height_ - 1) };
 	GLfloat Stride = (GLfloat)width_ / (shaderStridePixels_ / bytesPerPixel_);
 	/*
 	 * Scale the output size from the native size the algorithm produces for
 	 * the input size. Keep the aspect ratio and prefer cropping over black
-	 * bars.
+	 * bars - but crop from both sides, not from one.
+	 *
+	 * The viewport is the sensor frame while the buffer drawn into is the
+	 * requested size, so the buffer covers only the corner of the viewport
+	 * that starts at NDC -1 and ends where its own width reaches. Placing
+	 * the quad at -(1 - scale) puts the texture's left edge exactly at
+	 * that corner, so everything cropped is taken off one side: metered
+	 * by registering a 640x480 stream against a 1920x1080 one of the same
+	 * scene, the frame shown ran from x=48 rather than from the x=240
+	 * that centring the 4:3 window in a 16:9 sensor calls for.
+	 *
+	 * Centre it on the region the buffer actually covers instead. That
+	 * region spans NDC -1 to 2*out/viewport - 1, whose midpoint is
+	 * out/viewport - 1, and putting the quad's centre there splits the
+	 * crop evenly between the two edges at every size, the native one
+	 * included - where it shares the eight columns of Bayer border
+	 * instead of taking them all off the right.
 	 */
 	GLfloat scale = std::max((GLfloat)outputSize_.width / nativeOutputSize_.width,
 				 (GLfloat)outputSize_.height / nativeOutputSize_.height);
-	GLfloat trans = -(1.0f - scale);
+	GLfloat transX = (GLfloat)outputSize_.width / width_ - 1.0f;
+	GLfloat transY = (GLfloat)outputSize_.height / height_ - 1.0f;
 	GLfloat projMatrix[] = {
 		scale, 0, 0, 0,
 		0, scale, 0, 0,
 		0, 0, 1, 0,
-		trans, trans, 0, 1
+		transX, transY, 0, 1
 	};
 	/* Static const coordinates */
 	static const GLfloat vcoordinates[4][2] = {
 		{ -1.0f, -1.0f },
 		{ -1.0f, +1.0f },
 		{ +1.0f, +1.0f },
 		{ +1.0f, -1.0f },
 	};
 	static const GLfloat tcoordinates[4][2] = {
 		{ 0.0f, 0.0f },
 		{ 0.0f, 1.0f },
 		{ 1.0f, 1.0f },
 		{ 1.0f, 0.0f },
 	};
 
 	/* vertexIn - bayer_8.vert */
 	glEnableVertexAttribArray(attributeVertex_);
 	glVertexAttribPointer(attributeVertex_, 2, GL_FLOAT, GL_TRUE,
 			      2 * sizeof(GLfloat), vcoordinates);
 
 	/* textureIn - bayer_8.vert */
 	glEnableVertexAttribArray(attributeTexture_);
 	glVertexAttribPointer(attributeTexture_, 2, GL_FLOAT, GL_TRUE,
 			      2 * sizeof(GLfloat), tcoordinates);
 
 	/*
 	 * Set the sampler2D to the respective texture unit for each texutre
 	 * To simultaneously sample multiple textures we need to use multiple
 	 * texture units
 	 */