Commit f2546bef291 for woocommerce

commit f2546bef291e74a6e7cbbcb28059395ac89f4580
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Fri Jul 31 15:31:49 2026 +0200

    Improve Product Image srcset to prevent wide images from appearing blurry (#67105)

    * Improve Product Image srcset to avoid very wide images to look blurry

    * Add changelog

    * PHPStan

    * Remove filter after generating the image HTML

    * Typo

    * Add guards

    * Add test

    * Add try/finally

    * Update test

    * Add extra guards

    * PHPStan

    * Update changelog

    * Harden guards

    * Simplify code

    * Harden guards (II)

    * Harden guards (III)

diff --git a/plugins/woocommerce/changelog/fix-67103-product-image-updated-srcset b/plugins/woocommerce/changelog/fix-67103-product-image-updated-srcset
new file mode 100644
index 00000000000..2abb9834a41
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-67103-product-image-updated-srcset
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Improve Product Image srcset to prevent wide images from appearing blurry
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductImage.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductImage.php
index 3e69abad07d..d70685be959 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductImage.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductImage.php
@@ -260,7 +260,67 @@ class ProductImage extends AbstractBlock {
 			$attr['loading'] = $loading_attr;
 		}

-		return $provided_image_id_is_valid ? wp_get_attachment_image( $image_id, $image_size, false, $attr ) : $product->get_image( $image_size, $attr );
+		$adjust_srcset = function ( $sources, $size_array, $image_src, $image_meta ) use ( $aspect_ratio ) {
+			if (
+				! is_string( $aspect_ratio ) ||
+				! is_array( $sources ) ||
+				! is_array( $image_meta ) ||
+				! isset( $image_meta['width'], $image_meta['height'] ) ||
+				! is_numeric( $image_meta['width'] ) ||
+				! is_numeric( $image_meta['height'] ) ||
+				$image_meta['width'] <= 0 ||
+				$image_meta['height'] <= 0
+			) {
+				return $sources;
+			}
+
+			$aspect_ratio_parts = explode( '/', $aspect_ratio );
+
+			if (
+				count( $aspect_ratio_parts ) !== 2 ||
+				! is_numeric( $aspect_ratio_parts[0] ) ||
+				! is_numeric( $aspect_ratio_parts[1] ) ||
+				$aspect_ratio_parts[0] <= 0 ||
+				$aspect_ratio_parts[1] <= 0
+			) {
+				return $sources;
+			}
+
+			$block_aspect_ratio = $aspect_ratio_parts[0] / $aspect_ratio_parts[1];
+			$image_aspect_ratio = $image_meta['width'] / $image_meta['height'];
+
+			if ( $image_aspect_ratio > $block_aspect_ratio ) {
+				$stretch_factor = $image_aspect_ratio / $block_aspect_ratio;
+
+				foreach ( $sources as $key => $source ) {
+					if ( ! is_array( $source ) || ! isset( $source['value'] ) || ! is_numeric( $source['value'] ) ) {
+						continue;
+					}
+					$sources[ $key ]['value'] = (int) round( $source['value'] / $stretch_factor );
+				}
+			}
+
+			return $sources;
+		};
+
+		$maybe_adjust_srcset =
+			'cover' === $attributes['scale'] &&
+			is_string( $aspect_ratio ) &&
+			false !== strpos( $aspect_ratio, '/' );
+
+		if ( $maybe_adjust_srcset ) {
+			add_filter( 'wp_calculate_image_srcset', $adjust_srcset, 10, 4 );
+		}
+
+		try {
+			$image_html = $provided_image_id_is_valid ? wp_get_attachment_image( $image_id, $image_size, false, $attr ) : $product->get_image( $image_size, $attr );
+		} finally {
+			if ( $maybe_adjust_srcset ) {
+				remove_filter( 'wp_calculate_image_srcset', $adjust_srcset, 10 );
+			}
+		}
+
+		return $image_html;
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductImage.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductImage.php
index 568449b6f0d..1b4fefddd9c 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductImage.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductImage.php
@@ -349,6 +349,40 @@ class ProductImage extends \WP_UnitTestCase {
 		$product->delete( true );
 	}

+	/**
+	 * @testdox Should reduce srcset width descriptors for wide images in portrait aspect ratios.
+	 */
+	public function test_product_image_render_adjusts_srcset_for_portrait_aspect_ratio() {
+		$data = $this->create_product_with_image();
+
+		wp_update_attachment_metadata(
+			$data['image_id'],
+			array(
+				'width'  => 2000,
+				'height' => 1000,
+				'file'   => 'wide-image.jpg',
+				'sizes'  => array(
+					'large' => array(
+						'file'      => 'wide-image-1024x512.jpg',
+						'width'     => 1024,
+						'height'    => 512,
+						'mime-type' => 'image/jpeg',
+					),
+				),
+			)
+		);
+		update_post_meta( $data['image_id'], '_wp_attached_file', 'wide-image.jpg' );
+
+		$default_markup  = $this->render_product_image_block( $data['product'], '{"imageSizing":"single"}' );
+		$portrait_markup = $this->render_product_image_block( $data['product'], '{"aspectRatio":"3/4"}' );
+
+		$this->assertStringContainsString( 'wide-image-1024x512.jpg 1024w', $default_markup );
+		$this->assertStringContainsString( 'wide-image-1024x512.jpg 384w', $portrait_markup );
+
+		$data['product']->delete( true );
+		wp_delete_attachment( $data['image_id'], true );
+	}
+
 	/**
 	 * Test that the ProductImage block handles invalid product IDs correctly.
 	 */