Index: wp-includes/theme.php
===================================================================
--- wp-includes/theme.php	(revision 19814)
+++ wp-includes/theme.php	(working copy)
@@ -1351,7 +1351,10 @@
 	if ( isset( $mods[ $name ] ) )
 		return apply_filters( "theme_mod_$name", $mods[ $name ] );
 
-	return apply_filters( "theme_mod_$name", sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() ) );
+	if ( is_string( $default ) )
+		$default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
+
+	return apply_filters( "theme_mod_$name", $default );
 }
 
 /**
@@ -1457,40 +1460,62 @@
 }
 
 /**
- * Get random header image from registered images in theme.
+ * Get random header image data from registered images in theme.
  *
- * @since 3.2.0
+ * @since 3.4.0
  *
+ * @access private
+ *
  * @return string Path to header image
  */
-function get_random_header_image() {
-	global $_wp_default_headers;
 
-	$header_image_mod = get_theme_mod( 'header_image', '' );
-	$headers = array();
+function _get_random_header_data() {
+	static $_wp_random_header;
 
-	if ( 'random-uploaded-image' == $header_image_mod )
-		$headers = get_uploaded_header_images();
-	elseif ( ! empty( $_wp_default_headers ) ) {
-		if ( 'random-default-image' == $header_image_mod ) {
-			$headers = $_wp_default_headers;
-		} else {
-			$is_random = get_theme_support( 'custom-header' );
-			if ( isset( $is_random[ 0 ] ) && !empty( $is_random[ 0 ][ 'random-default' ] ) )
+	if ( empty( $_wp_random_header ) ) {
+		global $_wp_default_headers;
+		$header_image_mod = get_theme_mod( 'header_image', '' );
+		$headers = array();
+
+		if ( 'random-uploaded-image' == $header_image_mod )
+			$headers = get_uploaded_header_images();
+		elseif ( ! empty( $_wp_default_headers ) ) {
+			if ( 'random-default-image' == $header_image_mod ) {
 				$headers = $_wp_default_headers;
+			} else {
+				$is_random = get_theme_support( 'custom-header' );
+				if ( isset( $is_random[ 0 ] ) && !empty( $is_random[ 0 ][ 'random-default' ] ) )
+					$headers = $_wp_default_headers;
+			}
 		}
-	}
 
-	if ( empty( $headers ) )
-		return '';
+		if ( empty( $headers ) )
+			return stdClass();
 
-	$random_image = array_rand( $headers );
-	$header_url = sprintf( $headers[$random_image]['url'], get_template_directory_uri(), get_stylesheet_directory_uri() );
+		$_wp_random_header = (object) $headers[ array_rand( $headers ) ];
 
-	return $header_url;
+		$_wp_random_header->url =  sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
+		$_wp_random_header->thumbnail_url =  sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
+	}
+	return $_wp_random_header;
 }
 
 /**
+ * Get random header image url from registered images in theme.
+ *
+ * @since 3.2.0
+ *
+ * @return string Path to header image
+ */
+
+function get_random_header_image() {
+	$random_image = _get_random_header_data();
+	if ( empty( $random_image->url ) )
+		return '';
+	return $random_image->url;
+}
+
+/**
  * Check if random header image is in use.
  *
  * Always true if user expressly chooses the option in Appearance > Header.
@@ -1547,17 +1572,60 @@
 
 	foreach ( (array) $headers as $header ) {
 		$url = esc_url_raw( $header->guid );
-		$header = basename($url);
-		$header_images[$header] = array();
-		$header_images[$header]['url'] =  $url;
-		$header_images[$header]['thumbnail_url'] =  $url;
-		$header_images[$header]['uploaded'] = true;
+		$header_data = wp_get_attachment_metadata( $header->ID );
+		$header_index = basename($url);
+		$header_images[$header_index] = array();
+		$header_images[$header_index]['attachment_id'] =  $header->ID;
+		$header_images[$header_index]['url'] =  $url;
+		$header_images[$header_index]['thumbnail_url'] =  $url;
+		$header_images[$header_index]['width'] = $header_data['width'];
+		$header_images[$header_index]['height'] = $header_data['height'];
 	}
 
 	return $header_images;
 }
 
 /**
+ * Get the header image data.
+ *
+ * @since 3.4.0
+ *
+ * @return object
+ */
+function get_current_header_data() {
+	$data = is_random_header_image()? _get_random_header_data() : get_theme_mod( 'header_image_data' );
+	$default = array(
+		'url'           => '',
+		'thumbnail_url' => '',
+		'width'         => '',
+		'height'        => '',
+	);
+	return (object) wp_parse_args( $data, $default );
+}
+
+/**
+ * Get the header image width.
+ *
+ * @since 3.4.0
+ *
+ * @return int
+ */
+function get_header_image_width() {
+	return empty( get_current_header_data()->width )? HEADER_IMAGE_WIDTH : get_current_header_data()->width;
+}
+
+/**
+ * Get the header image height.
+ *
+ * @since 3.4.0
+ *
+ * @return int
+ */
+function get_header_image_height() {
+	return empty( get_current_header_data()->height )? HEADER_IMAGE_HEIGHT : get_current_header_data()->height;
+}
+
+/**
  * Add callbacks for image header display.
  *
  * The parameter $header_callback callback will be required to display the
@@ -1955,6 +2023,13 @@
 			$post_format = $args[0];
 			return in_array( $post_format, $_wp_theme_features[$feature][0] );
 			break;
+
+		case 'custom-header':
+			// specific custom header capabilities can be registered by passing
+			// an array to add_theme_support()
+			$header_support = $args[0];
+			return ( isset( $_wp_theme_features[$feature][0][$header_support] ) && $_wp_theme_features[$feature][0][$header_support] );
+			break;
 	}
 
 	return apply_filters('current_theme_supports-' . $feature, true, $args, $_wp_theme_features[$feature]);
Index: wp-admin/css/wp-admin.dev.css
===================================================================
--- wp-admin/css/wp-admin.dev.css	(revision 19814)
+++ wp-admin/css/wp-admin.dev.css	(working copy)
@@ -4494,7 +4494,6 @@
 
 .appearance_page_custom-header #headimg {
 	border: 1px solid #DFDFDF;
-	min-height: 100px;
 	width: 100%;
 }
 
Index: wp-admin/custom-header.php
===================================================================
--- wp-admin/custom-header.php	(revision 19814)
+++ wp-admin/custom-header.php	(working copy)
@@ -188,7 +188,21 @@
 
 		if ( isset( $_POST['resetheader'] ) ) {
 			check_admin_referer( 'custom-header-options', '_wpnonce-custom-header-options' );
-			remove_theme_mod( 'header_image' );
+			$this->process_default_headers();
+			$default = defined( 'HEADER_IMAGE' ) ? HEADER_IMAGE : '';
+			$default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
+			foreach ( $this->default_headers as $header => $details ) {
+				if ( $details['url'] == $default ) {
+					$default_data = $details;
+					break;
+				}
+			}
+			set_theme_mod( 'header_image', $default );
+			if ( empty( $default_data['width'] ) )
+				$default_data['width'] = HEADER_IMAGE_WIDTH;
+			if ( empty( $default_data['height'] ) )
+				$default_data['height'] = HEADER_IMAGE_HEIGHT;
+			set_theme_mod( 'header_image_data', object( $default_data ) );
 			return;
 		}
 
@@ -225,10 +239,17 @@
 			} else {
 				$this->process_default_headers();
 				$uploaded = get_uploaded_header_images();
-				if ( isset( $uploaded[$_POST['default-header']] ) )
+				if ( isset( $uploaded[$_POST['default-header']] ) ) {
 					set_theme_mod( 'header_image', esc_url( $uploaded[$_POST['default-header']]['url'] ) );
-				elseif ( isset( $this->default_headers[$_POST['default-header']] ) )
+					set_theme_mod( 'header_image_data', (object) $uploaded[$_POST['default-header']] );
+				} elseif ( isset( $this->default_headers[$_POST['default-header']] ) ) {
 					set_theme_mod( 'header_image', esc_url( $this->default_headers[$_POST['default-header']]['url'] ) );
+					if ( empty( $this->default_headers[$_POST['default-header']]['width'] ) )
+						$this->default_headers[$_POST['default-header']]['width'] = HEADER_IMAGE_WIDTH;
+					if ( empty( $this->default_headers[$_POST['default-header']]['height'] ) )
+						$this->default_headers[$_POST['default-header']]['height'] = HEADER_IMAGE_HEIGHT;
+					set_theme_mod( 'header_image_data', (object) $this->default_headers[$_POST['default-header']] );
+				}
 			}
 		}
 	}
@@ -289,7 +310,7 @@
 			echo '<div class="default-header">';
 			echo '<label><input name="default-header" type="radio" value="' . esc_attr( $header_key ) . '" ' . checked( $header_url, get_theme_mod( 'header_image' ), false ) . ' />';
 			$width = '';
-			if ( !empty( $header['uploaded'] ) )
+			if ( !empty( $header['attachment_id'] ) )
 				$width = ' width="230"';
 			echo '<img src="' . $header_thumbnail . '" alt="' . esc_attr( $header_desc ) .'" title="' . esc_attr( $header_desc ) . '"' . $width . ' /></label>';
 			echo '</div>';
@@ -419,8 +440,17 @@
 	}
 
 	jQuery(document).ready(function() {
-		var xinit = <?php echo HEADER_IMAGE_WIDTH; ?>;
-		var yinit = <?php echo HEADER_IMAGE_HEIGHT; ?>;
+		<?php
+		$xinit = HEADER_IMAGE_WIDTH;
+		$yinit = HEADER_IMAGE_HEIGHT;
+		$header_support = get_theme_support( 'custom-header' );
+		if ( !empty( $header_support[ 0 ][ 'suggested-width' ] ) )
+			$xinit = $header_support[ 0 ][ 'suggested-width' ];
+		if ( !empty( $header_support[ 0 ][ 'suggested-height' ] ) )
+			$yinit = $header_support[ 0 ][ 'suggested-height' ];
+		?>
+		var xinit = <?php echo absint( $xinit ); ?>;
+		var yinit = <?php echo absint( $yinit ); ?>;
 		var ratio = xinit / yinit;
 		var ximg = jQuery('img#upload').width();
 		var yimg = jQuery('img#upload').height();
@@ -438,14 +468,28 @@
 		jQuery('img#upload').imgAreaSelect({
 			handles: true,
 			keys: true,
-			aspectRatio: xinit + ':' + yinit,
 			show: true,
 			x1: 0,
 			y1: 0,
 			x2: xinit,
 			y2: yinit,
+			<?php
+			if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
+			?>
+			aspectRatio: xinit + ':' + yinit,
+			<?php
+			}
+			if ( ! current_theme_supports( 'custom-header', 'flex-height' ) ) {
+			?>
 			maxHeight: <?php echo HEADER_IMAGE_HEIGHT; ?>,
+			<?php
+			}
+			if ( ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
+			?>
 			maxWidth: <?php echo HEADER_IMAGE_WIDTH; ?>,
+			<?php
+			}
+			?>
 			onInit: function () {
 				jQuery('#width').val(xinit);
 				jQuery('#height').val(yinit);
@@ -492,7 +536,7 @@
 	  call_user_func( $this->admin_image_div_callback );
 	} else {
 	?>
-	<div id="headimg" style="max-width:<?php echo HEADER_IMAGE_WIDTH; ?>px;height:<?php echo HEADER_IMAGE_HEIGHT; ?>px;background-image:url(<?php esc_url ( header_image() ) ?>);">
+	<div id="headimg" style="background-image:url(<?php esc_url ( header_image() ) ?>);max-width:<?php echo get_header_image_width(); ?>px;height:<?php echo get_header_image_height(); ?>px;">
 		<?php
 		if ( 'blank' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || '' == get_theme_mod('header_textcolor', HEADER_TEXTCOLOR) || ! $this->header_text() )
 			$style = ' style="display:none;"';
@@ -510,7 +554,24 @@
 <th scope="row"><?php _e( 'Upload Image' ); ?></th>
 <td>
 	<p><?php _e( 'You can upload a custom header image to be shown at the top of your site instead of the default one. On the next screen you will be able to crop the image.' ); ?><br />
-	<?php printf( __( 'Images of exactly <strong>%1$d &times; %2$d pixels</strong> will be used as-is.' ), HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT ); ?></p>
+	<?php
+	if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) {
+		printf( __( 'Images of exactly <strong>%1$d &times; %2$d pixels</strong> will be used as-is.' ) . '<br />', HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT );
+	} elseif ( current_theme_supports( 'custom-header', 'flex-height' ) ) {
+		if ( ! current_theme_supports( 'custom-header', 'flex-width' ) )
+			printf( __( 'Images should be at least <strong>%1$d pixels</strong> wide.' ) . '<br />', HEADER_IMAGE_WIDTH );
+	} elseif ( current_theme_supports( 'custom-header', 'flex-width' ) ) {
+		if ( ! current_theme_supports( 'custom-header', 'flex-height' ) )
+			printf( __( 'Images should be at least <strong>%1$d pixels</strong> tall.' ) . '<br />', HEADER_IMAGE_HEIGHT );
+	}
+	if ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) {
+		$header_support = get_theme_support( 'custom-header' );
+		if ( !empty( $header_support[ 0 ][ 'suggested-width' ] ) )
+			printf( __( 'Suggested width is <strong>%1$d pixels</strong>.' ) . '<br />', absint( $header_support[ 0 ][ 'suggested-width' ] ) );
+		if ( !empty( $header_support[ 0 ][ 'suggested-height' ] ) )
+			printf( __( 'Suggested height is <strong>%1$d pixels</strong>.' ) . '<br />', absint( $header_support[ 0 ][ 'suggested-height' ] ) );
+	}
+	?></p>
 	<form enctype="multipart/form-data" id="upload-form" method="post" action="<?php echo esc_attr( add_query_arg( 'step', 2 ) ) ?>">
 	<p>
 		<label for="upload"><?php _e( 'Choose an image from your computer:' ); ?></label><br />
@@ -662,7 +723,19 @@
 
 		list($width, $height, $type, $attr) = getimagesize( $file );
 
-		if ( $width == HEADER_IMAGE_WIDTH && $height == HEADER_IMAGE_HEIGHT ) {
+		$header_support = get_theme_support( 'custom-header' );
+		$max_width = 0;
+		// For flex, limit size of image displayed to 1500px unless theme says otherwise
+		if ( current_theme_supports( 'custom-header', 'flex-width' ) )
+			$max_width = 1500;
+
+		if ( !empty( $header_support[ 0 ][ 'max-width' ] ) )
+			$max_width = max( $max_width, absint( $header_support[ 0 ][ 'max-width' ] ) );
+
+		if ( defined( 'HEADER_IMAGE_WIDTH' ) )
+			$max_width = max( $max_width, HEADER_IMAGE_WIDTH );
+		// If flexible height isn't supported and the image is the exact right size
+		if ( ! current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) && $width == HEADER_IMAGE_WIDTH && $height == HEADER_IMAGE_HEIGHT ) {
 			// Add the meta-data
 			wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
 			update_post_meta( $id, '_wp_attachment_is_custom_header', get_option('stylesheet' ) );
@@ -670,9 +743,9 @@
 			set_theme_mod('header_image', esc_url($url));
 			do_action('wp_create_file_in_uploads', $file, $id); // For replication
 			return $this->finished();
-		} elseif ( $width > HEADER_IMAGE_WIDTH ) {
-			$oitar = $width / HEADER_IMAGE_WIDTH;
-			$image = wp_crop_image($file, 0, 0, $width, $height, HEADER_IMAGE_WIDTH, $height / $oitar, false, str_replace(basename($file), 'midsize-'.basename($file), $file));
+		} elseif ( $width > $max_width ) {
+			$oitar = $width / $max_width;
+			$image = wp_crop_image($file, 0, 0, $width, $height, $max_width, $height / $oitar, false, str_replace(basename($file), 'midsize-'.basename($file), $file));
 			if ( is_wp_error( $image ) )
 				wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
 
@@ -733,7 +806,33 @@
 		$attachment_id = absint( $_POST['attachment_id'] );
 		$original = get_attached_file($attachment_id);
 
-		$cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], HEADER_IMAGE_WIDTH, HEADER_IMAGE_HEIGHT );
+		$header_support = get_theme_support( 'custom-header' );
+		$max_width = 0;
+		// For flex, limit size of image displayed to 1500px unless theme says otherwise
+		if ( current_theme_supports( 'custom-header', 'flex-width' ) )
+			$max_width = 1500;
+
+		if ( !empty( $header_support[ 0 ][ 'max-width' ] ) )
+			$max_width = max( $max_width, absint( $header_support[ 0 ][ 'max-width' ] ) );
+
+		if ( defined( 'HEADER_IMAGE_WIDTH' ) )
+			$max_width = max( $max_width, HEADER_IMAGE_WIDTH );
+
+		if ( ( current_theme_supports( 'custom-header', 'flex-height' ) && ! current_theme_supports( 'custom-header', 'flex-width' ) ) || $_POST['width'] > $max_width )
+			$dst_height = absint( $_POST['height'] * ( $max_width / $_POST['width'] ) );
+		elseif ( current_theme_supports( 'custom-header', 'flex-height' ) && current_theme_supports( 'custom-header', 'flex-width' ) )
+			$dst_height = absint( $_POST['height'] );
+		else
+			$dst_height = HEADER_IMAGE_HEIGHT;
+
+		if ( ( current_theme_supports( 'custom-header', 'flex-width' ) && ! current_theme_supports( 'custom-header', 'flex-height' ) ) || $_POST['width'] > $max_width )
+			$dst_width = absint( $_POST['width'] * ( $max_width / $_POST['width'] ) );
+		elseif ( current_theme_supports( 'custom-header', 'flex-width' ) && current_theme_supports( 'custom-header', 'flex-height' ) )
+			$dst_width = absint( $_POST['width'] );
+		else
+			$dst_width = HEADER_IMAGE_WIDTH;
+
+		$cropped = wp_crop_image( $attachment_id, (int) $_POST['x1'], (int) $_POST['y1'], (int) $_POST['width'], (int) $_POST['height'], $dst_width, $dst_height );
 		if ( is_wp_error( $cropped ) )
 			wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
 
@@ -760,6 +859,15 @@
 
 		set_theme_mod('header_image', $url);
 
+		$header_data                = new stdClass();
+		$header_data->attachment_id = $attachment_id;
+		$header_data->url           = $url;
+		$header_data->thumbnail_url = $url;
+		$header_data->width         = $dst_width;
+		$header_data->height        = $dst_height;
+
+		set_theme_mod( 'header_image_data', $header_data );
+
 		// cleanup
 		$medium = str_replace(basename($original), 'midsize-'.basename($original), $original);
 		@unlink( apply_filters( 'wp_delete_file', $medium ) );
