Index: /trunk/src/wp-includes/media.php
===================================================================
--- /trunk/src/wp-includes/media.php	(revision 35411)
+++ /trunk/src/wp-includes/media.php	(revision 35412)
@@ -814,11 +814,12 @@
 		// Generate srcset and sizes if not already present.
 		if ( empty( $attr['srcset'] ) ) {
-			$srcset = wp_get_attachment_image_srcset( $attachment_id, $size );
-			$sizes  = wp_get_attachment_image_sizes( $attachment_id, $size, $width );
-
-			if ( $srcset && $sizes ) {
-				$attr['srcset'] = $srcset;
-
-				if ( empty( $attr['sizes'] ) ) {
+			$image_meta = wp_get_attachment_metadata( $attachment_id );
+			$size_array = array( absint( $width ), absint( $height ) );
+			$sources = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id );
+
+			if ( count( $sources ) > 1 ) {
+				$attr['srcset'] = wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id );
+
+				if ( empty( $attr['sizes'] ) && ( $sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id ) ) ) {
 					$attr['sizes'] = $sizes;
 				}
@@ -865,13 +866,116 @@
 
 /**
- * Retrieves an array of URLs and pixel widths representing sizes of an image.
- *
- * The purpose is to populate a source set when creating responsive image markup.
+ * Private, do not use
+ */
+function _wp_upload_dir_baseurl() {
+	static $baseurl = null;
+
+	if ( ! $baseurl ) {
+		$uploads_dir = wp_upload_dir();
+		$baseurl = $uploads_dir['baseurl'];
+	}
+
+	return $baseurl;
+}
+
+/**
+ * Private, do not use
+ */
+function _wp_get_image_size_from_meta( $size, $image_meta ) {
+	if ( $size === 'full' ) {
+		return array(
+			absint( $image_meta['width'] ),
+			absint( $image_meta['height'] ),
+		);
+	} elseif ( ! empty( $image_meta['sizes'][$size] ) ) {
+		return array(
+			absint( $image_meta['sizes'][$size]['width'] ),
+			absint( $image_meta['sizes'][$size]['height'] ),
+		);
+	}
+
+	return false;
+}
+
+/**
+ * Retrieves the value for an image attachment's 'srcset' attribute.
  *
  * @since 4.4.0
  *
- * @param int          $attachment_id Image attachment ID.
+
+ * @param int          $attachment_id Optional. Image attachment ID.
  * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
  *                                    values in pixels (in that order). Default 'medium'.
+ * @param array        $image_meta    Optional. The image meta data.
+ * @return string|bool A 'srcset' value string or false.
+ */
+function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) {
+	if ( ! $image = wp_get_attachment_image_src( $attachment_id, $size ) ) {
+		return false;
+	}
+
+	if ( ! is_array( $image_meta ) ) {
+		$image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+	}
+
+	$image_url = $image[0];
+	$size_array = array(
+		absint( $image[1] ),
+		absint( $image[2] )
+	);
+
+	// Calculate the sources for the srcset.
+	$sources = wp_calculate_image_srcset( $image_url, $size_array, $image_meta, $attachment_id );
+
+	// Only return a srcset value if there is more than one source.
+	if ( count( $sources ) < 2 ) {
+		return false;
+	}
+
+	return wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id );
+}
+
+
+/**
+ * A helper function to concatenate and filter the srcset attribute value.
+ *
+ * @since 4.4.0
+ *
+ * @param array   $sources       The array containing image sizes data as returned by wp_calculate_image_srcset().
+ * @param array   $size_array    Array of width and height values in pixels (in that order).
+ * @param array   $image_meta    The image meta data.
+ * @param int     $attachment_id The image attachment post id to pass to the filter.
+ * @return string The srcset attribute value.
+ */
+function wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id ) {
+	$srcset = '';
+
+	foreach ( $sources as $source ) {
+		$srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
+	}
+
+	/**
+	 * Filter the output of wp_get_attachment_image_srcset().
+	 *
+	 * @since 4.4.0
+	 *
+	 * @param string       $srcset        A source set formatted for a `srcset` attribute.
+	 * @param int          $attachment_id Image attachment ID.
+	 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
+	 *                                    values in pixels (in that order). Default 'medium'.
+	 * @param array        $image_meta    The image meta data.
+	 */
+	return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size_array, $image_meta );
+}
+
+/**
+ * A helper function to caclulate the image sources to include in a srcset attribute.
+ *
+ * @since 4.4.0
+ *
+ * @param string $image_name    The file name, path, URL or partial path or URL of the image being matched.
+ * @param array  $size_array    Array of width and height values in pixels (in that order).
+ * @param array  $image_meta    The image meta data.
+ * @param int    $attachment_id Optional. The image attachment post id to pass to the filter.
  * @return array|bool $sources {
  *     Array image candidate values containing a URL, descriptor type, and
@@ -887,43 +991,36 @@
  *
  */
-function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' ) {
-	// Get the intermediate size.
-	$image = image_get_intermediate_size( $attachment_id, $size );
-
-	// Get the post meta.
-	$img_meta = wp_get_attachment_metadata( $attachment_id );
-	if ( ! is_array( $img_meta ) ) {
+function wp_calculate_image_srcset( $image_name, $size_array, $image_meta, $attachment_id = 0 ) {
+	if ( empty( $image_meta['sizes'] ) ) {
 		return false;
 	}
 
-	// Extract the height and width from the intermediate or the full size.
-	$img_width  = ( $image ) ? $image['width']  : $img_meta['width'];
-	$img_height = ( $image ) ? $image['height'] : $img_meta['height'];
-
-	// Bail early if the width isn't greater than zero.
-	if ( ! $img_width > 0 ) {
+	$image_sizes = $image_meta['sizes'];
+
+	// Get the height and width for the image.
+	$image_width = (int) $size_array[0];
+	$image_height = (int) $size_array[1];
+
+	// Bail early if error/no width.
+	if ( $image_width < 1 ) {
 		return false;
 	}
 
-	// Use the URL from the intermediate size or build the url from the metadata.
-	if ( ! empty( $image['url'] ) ) {
-		$img_url = $image['url'];
-	} else {
-		$uploads_dir = wp_upload_dir();
-		$img_file = ( $image ) ? path_join( dirname( $img_meta['file'] ) , $image['file'] ) : $img_meta['file'];
-		$img_url = $uploads_dir['baseurl'] . '/' . $img_file;
-	}
-
-	$img_sizes = $img_meta['sizes'];
-
 	// Add full size to the img_sizes array.
-	$img_sizes['full'] = array(
-		'width'  => $img_meta['width'],
-		'height' => $img_meta['height'],
-		'file'   => wp_basename( $img_meta['file'] )
+	$image_sizes['full'] = array(
+		'width'  => $image_meta['width'],
+		'height' => $image_meta['height'],
+		'file'   => wp_basename( $image_meta['file'] ),
 	);
 
+	$image_baseurl = _wp_upload_dir_baseurl();
+	$dirname = dirname( $image_meta['file'] );
+
+	if ( $dirname !== '.' ) {
+		$image_baseurl = path_join( $image_baseurl, $dirname );
+	}
+
 	// Calculate the image aspect ratio.
-	$img_ratio = $img_height / $img_width;
+	$image_ratio = $image_height / $image_width;
 
 	/*
@@ -932,56 +1029,51 @@
 	 * out images that are leftovers from previous versions.
 	 */
-	$img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash );
-
-	/**
-	 * Filter the maximum width included in a srcset attribute.
+	$image_edited = preg_match( '/-e[0-9]{13}/', $image_name, $image_edit_hash );
+
+	/**
+	 * Filter the maximum width included in a 'srcset' attribute.
 	 *
 	 * @since 4.4.0
 	 *
-	 * @param array|string $size Size of image, either array or string.
-	 */
-	$max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size );
+	 * @param int          $max_width  The maximum width to include in the 'srcset'. Default '1600'.
+	 * @param array|string $size_array Array of width and height values in pixels (in that order).
+	 */
+	$max_srcset_width = apply_filters( 'max_srcset_image_width', 1600, $size_array );
+
+	// Array to hold URL candidates.
+	$sources = array();
 
 	/*
-	 * Set up arrays to hold url candidates and matched image sources so
-	 * we can avoid duplicates without looping through the full sources array
-	 */
-	$candidates = $sources = array();
-
-	/*
-	 * Loop through available images and only use images that are resized
-	 * versions of the same rendition.
-	 */
-	foreach ( $img_sizes as $img ) {
-
-		// Filter out images that are leftovers from previous renditions.
-		if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) {
+	 * Loop through available images. Only use images that are resized
+	 * versions of the same edit.
+	 */
+	foreach ( $image_sizes as $image ) {
+
+		// Filter out images that are from previous edits.
+		if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
 			continue;
 		}
 
 		// Filter out images that are wider than $max_srcset_width.
-		if ( $max_srcset_width && $img['width'] > $max_srcset_width ) {
+		if ( $max_srcset_width && $image['width'] > $max_srcset_width ) {
 			continue;
 		}
 
-		$candidate_url = path_join( dirname( $img_url ), $img['file'] );
+		$candidate_url = $image['file'];
 
 		// Calculate the new image ratio.
-		if ( $img['width'] ) {
-			$img_ratio_compare = $img['height'] / $img['width'];
+		if ( $image['width'] ) {
+			$image_ratio_compare = $image['height'] / $image['width'];
 		} else {
-			$img_ratio_compare = 0;
+			$image_ratio_compare = 0;
 		}
 
 		// If the new ratio differs by less than 0.01, use it.
-		if ( abs( $img_ratio - $img_ratio_compare ) < 0.01 && ! in_array( $candidate_url, $candidates ) ) {
-			// Add the URL to our list of candidates.
-			$candidates[] = $candidate_url;
-
-			// Add the url, descriptor, and value to the sources array to be returned.
-			$sources[] = array(
-				'url'        => $candidate_url,
+		if ( abs( $image_ratio - $image_ratio_compare ) < 0.01 && ! array_key_exists( $candidate_url, $sources ) ) {
+			// Add the URL, descriptor, and value to the sources array to be returned.
+			$sources[ $image['width'] ] = array(
+				'url'        => path_join( $image_baseurl, $candidate_url ),
 				'descriptor' => 'w',
-				'value'      => $img['width'],
+				'value'      => $image['width'],
 			);
 		}
@@ -993,100 +1085,74 @@
 	 * @since 4.4.0
 	 *
-	 * @param array        $sources       An array of image urls and widths.
+	 * @param array        $sources       An array of image URLs and widths.
 	 * @param int          $attachment_id Attachment ID for image.
 	 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
 	 *                                    values in pixels (in that order). Default 'medium'.
-	 */
-	return apply_filters( 'wp_get_attachment_image_srcset_array', $sources, $attachment_id, $size );
-}
-
-/**
- * Retrieves the value for an image attachment's 'srcset' attribute.
+	 * @param array        $image_meta    The image meta data.
+
+	 */
+	return apply_filters( 'wp_get_attachment_image_srcset_array', array_values( $sources ), $attachment_id, $size_array, $image_meta );
+}
+
+/**
+ * Create `sizes` attribute value for an image.
  *
  * @since 4.4.0
  *
- * @param int          $attachment_id Image attachment ID.
- * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
- *                                    values in pixels (in that order). Default 'medium'.
- * @return string|bool A 'srcset' value string or false.
- */
-function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium' ) {
-	$srcset_array = wp_get_attachment_image_srcset_array( $attachment_id, $size );
-
-	// Only return a srcset value if there is more than one source.
-	if ( count( $srcset_array ) <= 1 ) {
+ * @param array|string $size          Image size. Accepts any valid image size name (thumbnail, medium, etc.),
+ *                                    or an array of width and height values in pixels (in that order).
+ * @param array        $image_meta    Optional. The image meta data.
+ * @param int          $attachment_id Optional. Image attachment ID. Either $image_meta or $attachment_id is needed
+ *                                    when using image size name.
+ *
+ * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
+ */
+function wp_get_attachment_image_sizes( $size, $image_meta = null, $attachment_id = 0 ) {
+	$width = 0;
+
+	if ( is_numeric( $size ) ) {
+		$width = absint( $size );
+	} elseif ( is_array( $size ) ) {
+		$width = absint( $size[0] );
+	} elseif ( is_string( $size ) ) {
+		if ( ! $image_meta && $attachment_id ) {
+			$image_meta = wp_get_attachment_metadata( $attachment_id );
+		}
+
+		if ( $image_meta ) {
+			$width = _wp_get_image_size_from_meta( $size, $image_meta );
+			if ( $width ) {
+				$width = $width[0];
+			}
+		}
+	}
+
+	if ( ! $width ) {
 		return false;
 	}
 
-	$srcset = '';
-	foreach ( $srcset_array as $source ) {
-		$srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', ';
-	}
-
-	/**
-	 * Filter the output of wp_get_attachment_image_srcset().
+	// Setup the default sizes attribute.
+	$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', (int) $width );
+
+	/**
+	 * Filter the output of wp_get_attachment_image_sizes().
 	 *
 	 * @since 4.4.0
 	 *
-	 * @param string       $srcset        A source set formated for a `srcset` attribute.
-	 * @param int          $attachment_id Attachment ID for image.
+	 * @param string       $sizes         A source size value for use in a 'sizes' attribute.
 	 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
 	 *                                    values in pixels (in that order). Default 'medium'.
-	 */
-	return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size );
-}
-
-/**
- * Retrieves a source size attribute for an image from an array of values.
+	 * @param array        $image_meta    The image meta data.
+	 * @param int          $attachment_id Post ID of the original image.
+	 */
+	return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $size, $image_meta, $attachment_id );
+}
+
+/**
+ * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes.
  *
  * @since 4.4.0
  *
- * @param int          $attachment_id Image attachment ID.
- * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
- *                                    values in pixels (in that order). Default 'medium'.
- * @param int          $width         Optional. Display width of the image.
- * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
- */
-function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $width = null ) {
-	// Try to get the image width from the $width parameter.
-	if ( is_numeric( $width ) ) {
-		$img_width = (int) $width;
-	// Next, see if a width value was passed in the $size parameter.
-	} elseif ( is_array( $size ) ) {
-		$img_width = $size[0];
-	// Finally, use the $size name to return the width of the image.
-	} else {
-		$image = image_get_intermediate_size( $attachment_id, $size );
-		$img_width = $image ? $image['width'] : false;
-	}
-
-	// Bail early if $img_width isn't set.
-	if ( ! $img_width ) {
-		return false;
-	}
-
-	// Setup the default sizes attribute.
-	$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $img_width );
-
-	/**
-	 * Filter the output of wp_get_attachment_image_sizes().
-	 *
-	 * @since 4.4.0
-	 *
-	 * @param string       $sizes         A source size value for use in a 'sizes' attribute.
-	 * @param int          $attachment_id Post ID of the original image.
-	 * @param array|string $size          Image size. Accepts any valid image size, or an array of width and height
-	 *                                    values in pixels (in that order). Default 'medium'.
-	 * @param int          $width         Display width of the image.
-	 */
-	return apply_filters( 'wp_get_attachment_image_sizes', $sizes, $attachment_id, $size, $width );
-}
-
-/**
- * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes.
- *
- * @since 4.4.0
- *
- * @see wp_img_add_srcset_and_sizes()
+ * @see wp_image_add_srcset_and_sizes()
  *
  * @param string $content The raw post content to be filtered.
@@ -1096,27 +1162,31 @@
 	$images = get_media_embedded_in_content( $content, 'img' );
 
-	$attachment_ids = array();
+	$selected_images = $attachment_ids = array();
 
 	foreach( $images as $image ) {
-		if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) {
-			$attachment_id = (int) $class_id[1];
-			if ( $attachment_id ) {
-				$attachment_ids[] = $attachment_id;
-			}
-		}
-	}
-
-	if ( 0 < count( $attachment_ids ) ) {
+		if ( false === strpos( $image, ' srcset="' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) &&
+			( $attachment_id = absint( $class_id[1] ) ) ) {
+
+			// If exactly the same image tag is used more than once, overwrite it.
+			// All identical tags will be replaced later with str_replace().
+			$selected_images[ $image ] = $attachment_id;
+			// Overwrite the ID when the same image is included more than once.
+			$attachment_ids[ $attachment_id ] = true;
+		}
+	}
+
+	if ( count( $attachment_ids ) > 1 ) {
 		/*
-		 * Warm object caches for use with wp_get_attachment_metadata.
+		 * Warm object cache for use with get_post_meta().
 		 *
 		 * To avoid making a database call for each image, a single query
 		 * warms the object cache with the meta information for all images.
 		 */
-		_prime_post_caches( $attachment_ids, false, true );
-	}
-
-	foreach( $images as $image ) {
-		$content = str_replace( $image, wp_img_add_srcset_and_sizes( $image ), $content );
+		update_meta_cache( 'post', array_keys( $attachment_ids ) );
+	}
+
+	foreach ( $selected_images as $image => $attachment_id ) {
+		$image_meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
+		$content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content );
 	}
 
@@ -1132,72 +1202,74 @@
  * @see wp_get_attachment_image_sizes()
  *
- * @param string $image An HTML 'img' element to be filtered.
+ * @param string $image         An HTML 'img' element to be filtered.
+ * @param array  $image_meta    The image meta data.
+ * @param int    $attachment_id Image attachment ID.
  * @return string Converted 'img' element with `srcset` and `sizes` attributes added.
  */
-function wp_img_add_srcset_and_sizes( $image ) {
-	// Return early if a 'srcset' attribute already exists.
-	if ( false !== strpos( $image, ' srcset="' ) ) {
+function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
+	// Ensure the image meta exists
+	if ( empty( $image_meta['sizes'] ) ) {
 		return $image;
 	}
 
-	// Parse id, size, width, and height from the `img` element.
-	$id     = preg_match( '/wp-image-([0-9]+)/i', $image, $match_id     ) ? (int) $match_id[1]     : false;
-	$size   = preg_match( '/size-([^\s|"]+)/i',   $image, $match_size   ) ? $match_size[1]         : false;
-	$width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : false;
-
-	if ( $id && ! $size ) {
-		$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : false;
-
-		if ( $width && $height ) {
-			$size = array( $width, $height );
-		}
-	}
-
-	/*
-	 * If attempts to parse the size value failed, attempt to use the image
-	 * metadata to match the 'src' against the available sizes for an attachment.
-	 */
-	if ( $id && ! $size ) {
-		$meta = wp_get_attachment_metadata( $id );
-
-		// Parse the image src value from the img element.
-		$src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false;
-
-		// Return early if the metadata does not exist or the src value is empty.
-		if ( ! $meta || ! $src ) {
-			return $image;
-		}
-
+	$src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : '';
+	list( $src ) = explode( '?', $src );
+
+	// Return early if we coudn't get the image source.
+	if ( ! $src ) {
+		return $image;
+	}
+
+	// Bail early when an image has been inserted and later edited.
+	if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) &&
+		strpos( wp_basename( $src ), $img_edit_hash[0] ) === false ) {
+
+		return $image;
+	}
+
+	$width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : 0;
+	$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
+
+	if ( ! $width || ! $height ) {
 		/*
-		 * First, see if the file is the full size image. If not, loop through
-		 * the intermediate sizes until we find a file that matches.
+		 * If attempts to parse the size value failed, attempt to use the image
+		 * metadata to match the image file name from 'src' against the available sizes for an attachment.
 		 */
 		$image_filename = wp_basename( $src );
 
-		if ( $image_filename === basename( $meta['file'] ) ) {
-			$size = 'full';
+		if ( $image_filename === wp_basename( $image_meta['file'] ) ) {
+			$width = (int) $image_meta['width'];
+			$height = (int) $image_meta['height'];
 		} else {
-			foreach( $meta['sizes'] as $image_size => $image_size_data ) {
+			foreach( $image_meta['sizes'] as $image_size_data ) {
 				if ( $image_filename === $image_size_data['file'] ) {
-					$size = $image_size;
+					$width = (int) $image_size_data['width'];
+					$height = (int) $image_size_data['height'];
 					break;
 				}
 			}
 		}
-
-	}
-
-	// If ID and size exist, try for 'srcset' and 'sizes' and update the markup.
-	if ( $id && $size ) {
-		$srcset = wp_get_attachment_image_srcset( $id, $size );
-		$sizes  = wp_get_attachment_image_sizes( $id, $size, $width );
-
-		if ( $srcset && $sizes ) {
-			// Format the srcset and sizes string and escape attributes.
-			$srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes) );
-
-			// Add srcset and sizes attributes to the image markup.
-			$image = preg_replace( '/<img ([^>]+)[\s?][\/?]>/', '<img $1' . $srcset_and_sizes . ' />', $image );
-		}
+	}
+
+	if ( ! $width || ! $height ) {
+		return $image;
+	}
+
+	$size_array = array( $width, $height );
+	$sources = wp_calculate_image_srcset( $src, $size_array, $image_meta, $attachment_id );
+
+	$srcset = $sizes = '';
+	// Only calculate srcset and sizes values if there is more than one source.
+	if ( count( $sources ) > 1 ) {
+		$srcset = wp_image_srcset_attr( $sources, $size_array, $image_meta, $attachment_id );
+		$sizes = wp_get_attachment_image_sizes( $size_array, $image_meta, $attachment_id );
+	}
+
+	if ( $srcset && $sizes ) {
+		// Format the srcset and sizes string and escape attributes.
+		$srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes ) );
+
+		// Add srcset and sizes attributes to the image markup.
+		$image = preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $srcset_and_sizes . ' />', $image );
 	}
 
Index: /trunk/tests/phpunit/tests/media.php
===================================================================
--- /trunk/tests/phpunit/tests/media.php	(revision 35411)
+++ /trunk/tests/phpunit/tests/media.php	(revision 35412)
@@ -717,25 +717,43 @@
 
 	/**
+	 * Helper function to get image size array from size "name"
+	 */
+	function _get_image_size_array_from_name( $size_name ) {
+		switch ( $size_name ) {
+			case 'thumbnail':
+				return array( 150, 150 );
+			case 'medium':
+				return array( 300, 225 );
+			case 'large':
+				return array( 1024, 768 );
+			case 'full':
+				return array( 1600, 1200 ); // actual size of ../data/images/test-image-large.png
+			default:
+				return array( 800, 600 ); // soft-resized image
+		}
+	}
+
+	/**
 	 * @ticket 33641
 	 */
-	function test_wp_get_attachment_image_srcset_array() {
+	function test_wp_calculate_image_srcset() {
 		$year_month = date('Y/m');
-		$image = wp_get_attachment_metadata( self::$large_id );
+		$image_meta = wp_get_attachment_metadata( self::$large_id );
 
 		$expected = array(
 			array(
-				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image['sizes']['medium']['file'],
+				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image_meta['sizes']['medium']['file'],
 				'descriptor' => 'w',
-				'value'      => $image['sizes']['medium']['width'],
+				'value'      => $image_meta['sizes']['medium']['width'],
 			),
 			array(
-				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image['sizes']['large']['file'],
+				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month . '/' . $image_meta['sizes']['large']['file'],
 				'descriptor' => 'w',
-				'value'      => $image['sizes']['large']['width'],
+				'value'      => $image_meta['sizes']['large']['width'],
 			),
 			array(
-				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'],
+				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'],
 				'descriptor' => 'w',
-				'value'      => $image['width'],
+				'value'      => $image_meta['width'],
 			),
 		);
@@ -745,5 +763,7 @@
 
 		foreach ( $sizes as $size ) {
-			$this->assertSame( $expected, wp_get_attachment_image_srcset_array( self::$large_id, $size ) );
+			$image_url = wp_get_attachment_image_url( self::$large_id, $size );
+			$size_array = $this->_get_image_size_array_from_name( $size );
+			$this->assertSame( $expected, wp_calculate_image_srcset( $image_url, $size_array, $image_meta ) );
 		}
 	}
@@ -752,5 +772,5 @@
 	 * @ticket 33641
 	 */
-	function test_wp_get_attachment_image_srcset_array_no_date_uploads() {
+	function test_wp_calculate_image_srcset_no_date_uploads() {
 		// Save the current setting for uploads folders
 		$uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' );
@@ -763,21 +783,21 @@
 		$id = self::factory()->attachment->create_upload_object( $filename );
 
-		$image = wp_get_attachment_metadata( $id );
+		$image_meta = wp_get_attachment_metadata( $id );
 
 		$expected = array(
 			array(
-				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['sizes']['medium']['file'],
+				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['sizes']['medium']['file'],
 				'descriptor' => 'w',
-				'value'      => $image['sizes']['medium']['width'],
+				'value'      => $image_meta['sizes']['medium']['width'],
 			),
 			array(
-				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['sizes']['large']['file'],
+				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['sizes']['large']['file'],
 				'descriptor' => 'w',
-				'value'      => $image['sizes']['large']['width'],
+				'value'      => $image_meta['sizes']['large']['width'],
 			),
 			array(
-				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'],
+				'url'        => 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'],
 				'descriptor' => 'w',
-				'value'      => $image['width'],
+				'value'      => $image_meta['width'],
 			),
 		);
@@ -787,5 +807,7 @@
 
 		foreach ( $sizes as $size ) {
-			$this->assertSame( $expected, wp_get_attachment_image_srcset_array( $id, $size ) );
+			$size_array = $this->_get_image_size_array_from_name( $size );
+			$image_url = wp_get_attachment_image_url( self::$large_id, $size );
+			$this->assertSame( $expected, wp_calculate_image_srcset( $image_url, $size_array, $image_meta ) );
 		}
 
@@ -797,8 +819,10 @@
 	 * @ticket 33641
 	 */
-	function test_wp_get_attachment_image_srcset_array_with_edits() {
+	function test_wp_calculate_image_srcset_with_edits() {
 		// For this test we're going to mock metadata changes from an edit.
 		// Start by getting the attachment metadata.
-		$meta = wp_get_attachment_metadata( self::$large_id );
+		$image_meta = wp_get_attachment_metadata( self::$large_id );
+		$image_url = wp_get_attachment_image_url( self::$large_id );
+		$size_array = $this->_get_image_size_array_from_name( 'medium' );
 
 		// Copy hash generation method used in wp_save_image().
@@ -806,13 +830,11 @@
 
 		// Replace file paths for full and medium sizes with hashed versions.
-		$filename_base = basename( $meta['file'], '.png' );
-		$meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['file'] );
-		$meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['sizes']['medium']['file'] );
-
-		// Save edited metadata.
-		wp_update_attachment_metadata( self::$large_id, $meta );
+		$filename_base = basename( $image_meta['file'], '.png' );
+		$image_meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['file'] );
+		$image_meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['sizes']['medium']['file'] );
+		$image_meta['sizes']['large']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $image_meta['sizes']['large']['file'] );
 
 		// Calculate a srcset array.
-		$sizes = wp_get_attachment_image_srcset_array( self::$large_id, 'medium' );
+		$sizes = wp_calculate_image_srcset( $image_url, $size_array, $image_meta );
 
 		// Test to confirm all sources in the array include the same edit hash.
@@ -825,6 +847,6 @@
 	 * @ticket 33641
 	 */
-	function test_wp_get_attachment_image_srcset_array_false() {
-		$sizes = wp_get_attachment_image_srcset_array( 99999, 'foo' );
+	function test_wp_calculate_image_srcset_false() {
+		$sizes = wp_calculate_image_srcset( 'file.png', array( 400, 300 ), array() );
 
 		// For canola.jpg we should return
@@ -835,17 +857,12 @@
 	 * @ticket 33641
 	 */
-	function test_wp_get_attachment_image_srcset_array_no_width() {
-		// Filter image_downsize() output.
-		add_filter( 'wp_generate_attachment_metadata', array( $this, '_test_wp_get_attachment_image_srcset_array_no_width_filter' ) );
-
-		$old_meta = get_post_meta( self::$large_id, '_wp_attachment_metadata', true );
+	function test_wp_calculate_image_srcset_no_width() {
 		$file = get_attached_file( self::$large_id );
-
-		$data = wp_generate_attachment_metadata( self::$large_id, $file );
-		wp_update_attachment_metadata( self::$large_id, $data );
-
-		$srcset = wp_get_attachment_image_srcset_array( self::$large_id, 'medium' );
-
-		update_post_meta( self::$large_id, '_wp_attachment_metadata', $old_meta );
+		$image_url = wp_get_attachment_image_url( self::$large_id, 'medium' );
+		$image_meta = wp_generate_attachment_metadata( self::$large_id, $file );
+
+		$size_array = array(0, 0);
+
+		$srcset = wp_calculate_image_srcset( $image_url, $size_array, $image_meta );
 
 		// The srcset should be false.
@@ -854,28 +871,19 @@
 
 	/**
-	 * Helper function to filter image_downsize and return zero values for width and height.
-	 */
-	public function _test_wp_get_attachment_image_srcset_array_no_width_filter( $meta ) {
-		remove_filter( 'wp_generate_attachment_metadata', array( $this, __FUNCTION__ ) );
-
-		$meta['sizes']['medium']['width'] = 0;
-		$meta['sizes']['medium']['height'] = 0;
-		return $meta;
-	}
-
-	/**
 	 * @ticket 33641
 	 */
 	function test_wp_get_attachment_image_srcset() {
-		$sizes = wp_get_attachment_image_srcset( self::$large_id, 'full-size' );
-
-		$image = wp_get_attachment_metadata( self::$large_id );
+		$image_meta = wp_get_attachment_metadata( self::$large_id );
+		$size_array = array( 1600, 1200 ); // full size
+
+		$sizes = wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta );
+
 		$year_month = date('Y/m');
 
 		$expected = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month = date('Y/m') . '/'
-			. $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w, ';
+			. $image_meta['sizes']['medium']['file'] . ' ' . $image_meta['sizes']['medium']['width'] . 'w, ';
 		$expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $year_month = date('Y/m') . '/'
-			. $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w, ';
-		$expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w';
+			. $image_meta['sizes']['large']['file'] . ' ' . $image_meta['sizes']['large']['width'] . 'w, ';
+		$expected .= 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w';
 
 		$this->assertSame( $expected, $sizes );
@@ -886,9 +894,11 @@
 	 */
 	function test_wp_get_attachment_image_srcset_single_srcset() {
+		$image_meta = wp_get_attachment_metadata( self::$large_id );
+		$size_array = array( 150, 150 );
 		/*
 		 * In our tests, thumbnails will only return a single srcset candidate,
 		 * so we shouldn't return a srcset value in order to avoid unneeded markup.
 		 */
-		$sizes = wp_get_attachment_image_srcset( self::$large_id, 'thumbnail' );
+		$sizes = wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta );
 
 		$this->assertFalse( $sizes );
@@ -900,13 +910,15 @@
 	function test_wp_get_attachment_image_sizes() {
 		// Test sizes against the default WP sizes.
-		$intermediates = array('thumbnail', 'medium', 'large');
-
-		foreach( $intermediates as $int ) {
-			$width = get_option( $int . '_size_w' );
+		$intermediates = array( 'thumbnail', 'medium', 'large' );
+		$image_meta = wp_get_attachment_metadata( self::$large_id );
+
+		foreach( $intermediates as $int_size ) {
+			$size_array = $this->_get_image_size_array_from_name( $int_size );
+			list( $width, $height ) = $size_array;
 
 			$expected = '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
-			$sizes = wp_get_attachment_image_sizes( self::$large_id, $int );
-
-			$this->assertSame($expected, $sizes);
+			$sizes = wp_get_attachment_image_sizes( $size_array, $image_meta );
+
+			$this->assertSame( $expected, $sizes );
 		}
 	}
@@ -915,59 +927,38 @@
 	 * @ticket 33641
 	 */
-	function test_wp_get_attachment_image_sizes_with_width() {
-		$width = 350;
-
-		$expected = '(max-width: 350px) 100vw, 350px';
-		$sizes = wp_get_attachment_image_sizes( self::$large_id, 'medium', $width );
-
-		$this->assertSame( $expected, $sizes );
-	}
-
-	/**
-	 * @ticket 33641
-	 */
 	function test_wp_make_content_images_responsive() {
-		$srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, 'medium' ) );
-		$sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( self::$large_id, 'medium' ) );
+		$image_meta = wp_get_attachment_metadata( self::$large_id );
+		$size_array = $this->_get_image_size_array_from_name( 'medium' );
+
+		$srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta ) );
+		$sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( $size_array, $image_meta, self::$large_id ) );
 
 		// Function used to build HTML for the editor.
 		$img = get_image_tag( self::$large_id, '', '', '', 'medium' );
-		$img_no_size = str_replace( 'size-', '', $img );
-		$img_no_size_id = str_replace( 'wp-image-', 'id-', $img_no_size );
+		$img_no_size_in_class = str_replace( 'size-', '', $img );
+		$img_no_width_height = str_replace( ' width="' . $size_array[0] . '"', '', $img );
+		$img_no_width_height = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height );
+		$img_no_size_id = str_replace( 'wp-image-', 'id-', $img );
 
 		// Manually add srcset and sizes to the markup from get_image_tag();
-		$respimg = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img);
-		$respimg_no_size = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size);
-
-		$content = '<p>Welcome to WordPress!  This post contains important information.  After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.</p>
-			<p>First things first:</p>
-
+		$respimg = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img );
+		$respimg_no_size_in_class = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size_in_class );
+		$respimg_no_width_height = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_width_height );
+
+		$content = '
+			<p>Image, standard. Should have srcset and sizes.</p>
 			%1$s
 
-			<ul>
-			<li><a href="http://wordpress.org" title="Subscribe to the WordPress mailing list for Release Notifications">Subscribe to the WordPress mailing list for release notifications</a></li>
-			</ul>
-
+			<p>Image, no size class. Should have srcset and sizes.</p>
 			%2$s
 
-			<p>As a subscriber, you will receive an email every time an update is available (and only then).  This will make it easier to keep your site up to date, and secure from evildoers.<br />
-			When a new version is released, <a href="http://wordpress.org" title="If you are already logged in, this will take you directly to the Dashboard">log in to the Dashboard</a> and follow the instructions.<br />
-			Upgrading is a couple of clicks!</p>
-
+			<p>Image, no width and height attributes. Should have srcset and sizes (from matching the file name).</p>
 			%3$s
 
-			<p>Then you can start enjoying the WordPress experience:</p>
-			<ul>
-			<li>Edit your personal information at <a href="http://wordpress.org" title="Edit settings like your password, your display name and your contact information">Users &#8250; Your Profile</a></li>
-			<li>Start publishing at <a href="http://wordpress.org" title="Create a new post">Posts &#8250; Add New</a> and at <a href="http://wordpress.org" title="Create a new page">Pages &#8250; Add New</a></li>
-			<li>Browse and install plugins at <a href="http://wordpress.org" title="Browse and install plugins at the official WordPress repository directly from your Dashboard">Plugins &#8250; Add New</a></li>
-			<li>Browse and install themes at <a href="http://wordpress.org" title="Browse and install themes at the official WordPress repository directly from your Dashboard">Appearance &#8250; Add New Themes</a></li>
-			<li>Modify and prettify your website&#8217;s links at <a href="http://wordpress.org" title="For example, select a link structure like: http://example.com/1999/12/post-name">Settings &#8250; Permalinks</a></li>
-			<li>Import content from another system or WordPress site at <a href="http://wordpress.org" title="WordPress comes with importers for the most common publishing systems">Tools &#8250; Import</a></li>
-			<li>Find answers to your questions at the <a href="http://wordpress.orgs" title="The official WordPress documentation, maintained by the WordPress community">WordPress Codex</a></li>
-			</ul>';
-
-		$content_unfiltered = sprintf( $content, $img, $img_no_size, $img_no_size_id );
-		$content_filtered = sprintf( $content, $respimg, $respimg_no_size, $img_no_size_id );
+			<p>Image, no attachment ID class. Should NOT have srcset and sizes.</p>
+			%4$s';
+
+		$content_unfiltered = sprintf( $content, $img, $img_no_size_in_class, $img_no_width_height, $img_no_size_id );
+		$content_filtered = sprintf( $content, $respimg, $respimg_no_size_in_class, $respimg_no_width_height, $img_no_size_id );
 
 		$this->assertSame( $content_filtered, wp_make_content_images_responsive( $content_unfiltered ) );
