PluginProbe ʕ •ᴥ•ʔ
Modern Image Formats / 2.6.1
Modern Image Formats v2.6.1
2.7.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 2.0.0 2.0.1 2.0.2 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.5.1 2.6.0 2.6.1
webp-uploads / picture-element.php
webp-uploads Last commit date
deprecated.php 1 year ago helper.php 10 months ago hooks.php 5 months ago image-edit.php 5 months ago load.php 5 months ago picture-element.php 5 months ago readme.txt 1 month ago rest-api.php 1 year ago settings.php 5 months ago uninstall.php 5 months ago
picture-element.php
179 lines
1 <?php
2 /**
3 * Add `picture` element support
4 *
5 * @package webp-uploads
6 *
7 * @since 2.0.0
8 */
9
10 // @codeCoverageIgnoreStart
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14 // @codeCoverageIgnoreEnd
15
16 /**
17 * Potentially wrap an image tag in a picture element.
18 *
19 * @since 2.0.0
20 *
21 * @param string $image The image tag.
22 * @param string $context The context for the image tag.
23 * @param int $attachment_id The attachment id.
24 *
25 * @return string The new image tag.
26 */
27 function webp_uploads_wrap_image_in_picture( string $image, string $context, int $attachment_id ): string {
28 if ( ! in_array( $context, array( 'the_content', 'post_thumbnail_html', 'widget_block_content' ), true ) ) {
29 return $image;
30 }
31
32 $original_file_mime_type = webp_uploads_get_attachment_file_mime_type( $attachment_id );
33 if ( '' === $original_file_mime_type ) {
34 return $image;
35 }
36
37 $image_meta = wp_get_attachment_metadata( $attachment_id );
38
39 if ( ! isset( $image_meta['sizes'] ) ) {
40 return $image;
41 }
42
43 $image_sizes = $image_meta['sizes'];
44
45 // Append missing full size image in $image_sizes array for srcset.
46 if ( isset( $image_meta['sources'], $image_meta['width'], $image_meta['height'] ) ) {
47 array_unshift( $image_sizes, $image_meta );
48 }
49
50 // Extract sizes using regex to parse image tag, then use to retrieve tag.
51 $width = 0;
52 $height = 0;
53 $processor = new WP_HTML_Tag_Processor( $image );
54 if ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
55 $width = (int) $processor->get_attribute( 'width' );
56 $height = (int) $processor->get_attribute( 'height' );
57 }
58 $size_to_use = ( $width > 0 && $height > 0 ) ? array( $width, $height ) : 'full';
59
60 $image_src = wp_get_attachment_image_src( $attachment_id, $size_to_use );
61 if ( false === $image_src ) {
62 return $image;
63 }
64 list( $src, $width, $height ) = $image_src;
65 $size_array = array( absint( $width ), absint( $height ) );
66
67 // Collect all the sub size image mime types.
68 $mime_type_data = array();
69 foreach ( $image_sizes as $size ) {
70 if ( isset( $size['sources'] ) && isset( $size['width'] ) && isset( $size['height'] ) ) {
71 foreach ( $size['sources'] as $mime_type => $data ) {
72 if ( wp_image_matches_ratio( $size_array[0], $size_array[1], $size['width'], $size['height'] ) ) {
73 $mime_type_data[ $mime_type ] = $mime_type_data[ $mime_type ] ?? array();
74 $mime_type_data[ $mime_type ]['w'][ $size['width'] ] = $data;
75 $mime_type_data[ $mime_type ]['h'][ $size['height'] ] = $data;
76 }
77 }
78 }
79 }
80 $sub_size_mime_types = array_keys( $mime_type_data );
81
82 // If original image type fallback is not available, don't wrap in picture element.
83 if ( ! in_array( $original_file_mime_type, $sub_size_mime_types, true ) ) {
84 return $image;
85 }
86
87 /**
88 * Filter the image mime types that can be used for the <picture> element.
89 *
90 * Default is: ['image/avif', 'image/webp']. Returning an empty array will skip using the `picture` element.
91 *
92 * The mime types will output in the picture element in the order they are provided.
93 * The original image will be used as the fallback image for browsers that don't support the picture element.
94 *
95 * @since 2.0.0
96 * @since 2.1.0 The default value was updated, removing 'image/jpeg'.
97 *
98 * @param string[] $mime_types Mime types than can be used.
99 * @param int $attachment_id The id of the image being evaluated.
100 */
101 $enabled_mime_types = (array) apply_filters(
102 'webp_uploads_picture_element_mime_types',
103 array(
104 'image/avif',
105 'image/webp',
106 ),
107 $attachment_id
108 );
109
110 $mime_types = array_intersect( $enabled_mime_types, $sub_size_mime_types );
111
112 // No eligible mime types.
113 if ( count( $mime_types ) === 0 ) {
114 return $image;
115 }
116
117 // If the original mime types is the only one available, no picture element is needed.
118 if ( 1 === count( $mime_types ) && current( $mime_types ) === $original_file_mime_type ) {
119 return $image;
120 }
121
122 // Add each mime type to the picture's sources.
123 $picture_sources = '';
124
125 // Gets the srcset and sizes from the IMG tag.
126 $sizes = $processor->get_attribute( 'sizes' );
127 $srcset = $processor->get_attribute( 'srcset' );
128
129 if ( null !== $srcset && null !== $sizes ) {
130 foreach ( $mime_types as $image_mime_type ) {
131 // Filter core's wp_get_attachment_image_srcset to return the sources for the current mime type.
132 $filter = static function ( $sources ) use ( $mime_type_data, $image_mime_type ): array {
133 $filtered_sources = array();
134 foreach ( $sources as $source ) {
135 // Swap the URL for the current mime type.
136 if ( isset( $mime_type_data[ $image_mime_type ][ $source['descriptor'] ][ $source['value'] ] ) ) {
137 $filename = $mime_type_data[ $image_mime_type ][ $source['descriptor'] ][ $source['value'] ]['file'];
138 $filtered_sources[] = array(
139 'url' => dirname( $source['url'] ) . '/' . $filename,
140 'descriptor' => $source['descriptor'],
141 'value' => $source['value'],
142 );
143 }
144 }
145 return $filtered_sources;
146 };
147 add_filter( 'wp_calculate_image_srcset', $filter );
148 $image_srcset = wp_get_attachment_image_srcset( $attachment_id, $size_array, $image_meta );
149 remove_filter( 'wp_calculate_image_srcset', $filter );
150 if ( is_string( $image_srcset ) ) {
151 $picture_sources .= sprintf(
152 '<source type="%s" srcset="%s"%s>',
153 esc_attr( $image_mime_type ),
154 esc_attr( $image_srcset ),
155 is_string( $sizes ) ? sprintf( ' sizes="%s"', esc_attr( $sizes ) ) : ''
156 );
157 }
158 }
159 } else {
160 foreach ( $mime_types as $image_mime_type ) {
161 $image_srcset = webp_uploads_get_mime_type_image( $attachment_id, $src, $image_mime_type );
162 if ( is_string( $image_srcset ) ) {
163 $picture_sources .= sprintf(
164 '<source type="%s" srcset="%s">',
165 esc_attr( $image_mime_type ),
166 esc_attr( $image_srcset )
167 );
168 }
169 }
170 }
171
172 return sprintf(
173 '<picture class="%s" style="display: contents;">%s%s</picture>',
174 esc_attr( 'wp-picture-' . $attachment_id ),
175 $picture_sources,
176 $image
177 );
178 }
179