new file mode 100644
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Red Hat Inc.
+ *
+ * Geometry for scaling and cropping debayered images
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+
+#include <libcamera/geometry.h>
+
+namespace libcamera {
+
+/*
+ * Return a column-major projection matrix for an input-sized viewport and an
+ * output-sized framebuffer. Scale to fill the output, centering the crop on
+ * the input. All sizes must be nonzero, with nativeSize contained in inputSize.
+ */
+inline std::array<float, 16> debayerProjection(const Size &inputSize,
+ const Size &nativeSize,
+ const Size &outputSize)
+{
+ float scale = std::max(static_cast<float>(outputSize.width) / nativeSize.width,
+ static_cast<float>(outputSize.height) / nativeSize.height);
+
+ /* The framebuffer center is output/viewport - 1 in normalized coordinates. */
+ float transX = static_cast<float>(outputSize.width) / inputSize.width - 1.0f;
+ float transY = static_cast<float>(outputSize.height) / inputSize.height - 1.0f;
+
+ return {
+ scale, 0, 0, 0,
+ 0, scale, 0, 0,
+ 0, 0, 1, 0,
+ transX, transY, 0, 1,
+ };
+}
+
+} /* namespace libcamera */
@@ -2,6 +2,7 @@
libcamera_internal_headers += files([
'benchmark.h',
+ 'debayer_geometry.h',
'debayer_params.h',
'software_isp.h',
'swisp_stats.h',
@@ -9,7 +9,7 @@
#include "debayer_egl.h"
-#include <algorithm>
+#include <array>
#include <assert.h>
#include <memory>
#include <span>
@@ -24,6 +24,7 @@
#include "libcamera/internal/formats.h"
#include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/software_isp/debayer_geometry.h"
#include "../shaders/glsl_shaders.h"
@@ -388,20 +389,8 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara
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.
- */
- GLfloat scale = std::max((GLfloat)outputSize_.width / nativeOutputSize_.width,
- (GLfloat)outputSize_.height / nativeOutputSize_.height);
- GLfloat trans = -(1.0f - scale);
- GLfloat projMatrix[] = {
- scale, 0, 0, 0,
- 0, scale, 0, 0,
- 0, 0, 1, 0,
- trans, trans, 0, 1
- };
+ const std::array<float, 16> projMatrix =
+ debayerProjection({ width_, height_ }, nativeOutputSize_, outputSize_);
/* Static const coordinates */
static const GLfloat vcoordinates[4][2] = {
{ -1.0f, -1.0f },
@@ -445,7 +434,7 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara
glUniform2fv(textureUniformSize_, 1, imgSize);
glUniform2fv(textureUniformStep_, 1, Step);
glUniform1f(textureUniformStrideFactor_, Stride);
- glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix);
+ glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix.data());
LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
<< " tex_y " << textureUniformBayerDataIn_