diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 0f515c5e..3335eaf9 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -295,72 +295,66 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 
 	inputConfig_.stride = inputCfg.stride;
 	inputPixelFormat_ = inputCfg.pixelFormat;
 	width_ = inputCfg.size.width;
 	height_ = inputCfg.size.height;
 
 	if (outputCfgs.size() != 1) {
 		LOG(Debayer, Error)
 			<< "Unsupported number of output streams: "
 			<< outputCfgs.size();
 		return -EINVAL;
 	}
 
 	const StreamConfiguration &outputCfg = outputCfgs[0];
 	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
 	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
 		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
 
 	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
 		LOG(Debayer, Error)
 			<< "Invalid output size/stride: "
 			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
 			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
 		return -EINVAL;
 	}
 
 	outputPixelFormat_ = outputCfg.pixelFormat;
 	outputSize_ = outputCfg.size;
 	nativeOutputSize_ = outSizeRange.max;
 
-	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
-		    ~(inputConfig_.patternSize.width - 1);
-	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
-		    ~(inputConfig_.patternSize.height - 1);
-	window_.width = outputCfg.size.width;
-	window_.height = outputCfg.size.height;
-
 	/*
-	 * Don't pass x,y from window_ since process() already adjusts for it.
-	 * But crop the window to 2/3 of its width and height for speedup.
+	 * The GPU scales the visible sensor crop to outputSize_. Statistics
+	 * consume the original input buffer, so use sensor coordinates here.
 	 */
-	stats_->setWindow(Rectangle(window_.size()));
+	const Size visibleSize = nativeOutputSize_.boundedToAspectRatio(outputSize_);
+	stats_->setWindow(visibleSize.centeredTo(Rectangle(inputCfg.size).center()));
 
 	inputBufferCount_ = inputCfg.bufferCount;
 	outputBufferCount_ = outputCfg.bufferCount;
 
 	return 0;
 }
 
 Size DebayerEGL::patternSize(PixelFormat inputFormat)
 {
 	DebayerEGL::DebayerInputConfig config;
 
 	if (getInputConfig(inputFormat, config) != 0)
 		return {};
 
 	return config.patternSize;
 }
 
 std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
 {
 	DebayerEGL::DebayerInputConfig config;
 
 	if (getInputConfig(inputFormat, config) != 0)
 		return std::vector<PixelFormat>();
 
 	return config.outputFormats;
 }
 
 std::tuple<unsigned int, unsigned int>
 DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
 {
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index 30e51a47..52b8aa07 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -84,41 +84,40 @@ private:
 
 	/* Shader parameters */
 	float firstRed_x_;
 	float firstRed_y_;
 	GLint attributeVertex_;
 	GLint attributeTexture_;
 	GLint textureUniformStep_;
 	GLint textureUniformSize_;
 	GLint textureUniformStrideFactor_;
 	GLint textureUniformBayerFirstRed_;
 	GLint textureUniformProjMatrix_;
 
 	GLint textureUniformBayerDataIn_;
 
 	/* Per-frame AWB gains */
 	GLint awbUniformDataIn_;
 
 	/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
 	GLint ccmUniformDataIn_;
 
 	/* Black Level compensation */
 	GLint blackLevelUniformDataIn_;
 
 	/* Gamma */
 	GLint gammaUniformDataIn_;
 
 	/* Contrast */
 	GLint contrastExpUniformDataIn_;
 
 	Size nativeOutputSize_;
-	Rectangle window_;
 	std::unique_ptr<SwStatsCpu> stats_;
 	eGL egl_;
 	uint32_t width_;
 	uint32_t height_;
 	GLint glFormat_;
 	unsigned int bytesPerPixel_;
 	uint32_t shaderStridePixels_;
 };
 
 } /* namespace libcamera */
