layers: Add Blocked Image helper
diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp
index 0d56b2d..bf2bb61 100644
--- a/layers/buffer_validation.cpp
+++ b/layers/buffer_validation.cpp
@@ -2483,7 +2483,7 @@
     auto pool = cb_node->command_pool.get();
     if (pool) {
         granularity = GetPhysicalDeviceState()->queue_family_properties[pool->queueFamilyIndex].minImageTransferGranularity;
-        if (FormatIsCompressed(img->createInfo.format) || FormatIsSinglePlane_422(img->createInfo.format)) {
+        if (FormatIsBlockedImage(img->createInfo.format)) {
             auto block_size = FormatTexelBlockExtent(img->createInfo.format);
             granularity.width *= block_size.width;
             granularity.height *= block_size.height;
@@ -2718,10 +2718,8 @@
                              func_name, i, region.srcOffset.z);
         }
 
-        // Source checks that apply only to compressed images (or to _422 images if ycbcr enabled)
-        bool ext_ycbcr = IsExtEnabled(device_extensions.vk_khr_sampler_ycbcr_conversion);
-        if (FormatIsCompressed(src_state->createInfo.format) ||
-            (ext_ycbcr && FormatIsSinglePlane_422(src_state->createInfo.format))) {
+        // Source checks that apply only to "blocked images"
+        if (FormatIsBlockedImage(src_state->createInfo.format)) {
             const VkExtent3D block_size = FormatTexelBlockExtent(src_state->createInfo.format);
             //  image offsets must be multiples of block dimensions
             if ((SafeModulo(region.srcOffset.x, block_size.width) != 0) ||
@@ -2729,7 +2727,7 @@
                 (SafeModulo(region.srcOffset.z, block_size.depth) != 0)) {
                 vuid = is_2khr ? "VUID-VkCopyImageInfo2KHR-srcImage-01727" : "VUID-vkCmdCopyImage-srcImage-01727";
                 skip |= LogError(src_state->image(), vuid,
-                                 "%s: pRegion[%d] srcOffset (%d, %d) must be multiples of the compressed image's "
+                                 "%s: pRegion[%d] srcOffset (%d, %d) must be multiples of the blocked image's "
                                  "texel width & height (%d, %d).",
                                  func_name, i, region.srcOffset.x, region.srcOffset.y, block_size.width, block_size.height);
             }
@@ -2739,7 +2737,7 @@
                 (src_copy_extent.width + region.srcOffset.x != mip_extent.width)) {
                 vuid = is_2khr ? "VUID-VkCopyImageInfo2KHR-srcImage-01728" : "VUID-vkCmdCopyImage-srcImage-01728";
                 skip |= LogError(src_state->image(), vuid,
-                                 "%s: pRegion[%d] extent width (%d) must be a multiple of the compressed texture block "
+                                 "%s: pRegion[%d] extent width (%d) must be a multiple of the blocked texture block "
                                  "width (%d), or when added to srcOffset.x (%d) must equal the image subresource width (%d).",
                                  func_name, i, src_copy_extent.width, block_size.width, region.srcOffset.x, mip_extent.width);
             }
@@ -2831,9 +2829,8 @@
             }
         }
 
-        // Dest checks that apply only to compressed images (or to _422 images if ycbcr enabled)
-        if (FormatIsCompressed(dst_state->createInfo.format) ||
-            (ext_ycbcr && FormatIsSinglePlane_422(dst_state->createInfo.format))) {
+        // Dest checks that apply only to "blocked images"
+        if (FormatIsBlockedImage(dst_state->createInfo.format)) {
             const VkExtent3D block_size = FormatTexelBlockExtent(dst_state->createInfo.format);
 
             //  image offsets must be multiples of block dimensions
@@ -2842,7 +2839,7 @@
                 (SafeModulo(region.dstOffset.z, block_size.depth) != 0)) {
                 vuid = is_2khr ? "VUID-VkCopyImageInfo2KHR-dstImage-01731" : "VUID-vkCmdCopyImage-dstImage-01731";
                 skip |= LogError(dst_state->image(), vuid,
-                                 "%s: pRegion[%d] dstOffset (%d, %d) must be multiples of the compressed image's "
+                                 "%s: pRegion[%d] dstOffset (%d, %d) must be multiples of the blocked image's "
                                  "texel width & height (%d, %d).",
                                  func_name, i, region.dstOffset.x, region.dstOffset.y, block_size.width, block_size.height);
             }
@@ -2852,7 +2849,7 @@
                 (dst_copy_extent.width + region.dstOffset.x != mip_extent.width)) {
                 vuid = is_2khr ? "VUID-VkCopyImageInfo2KHR-dstImage-01732" : "VUID-vkCmdCopyImage-dstImage-01732";
                 skip |= LogError(dst_state->image(), vuid,
-                                 "%s: pRegion[%d] dst_copy_extent width (%d) must be a multiple of the compressed texture "
+                                 "%s: pRegion[%d] dst_copy_extent width (%d) must be a multiple of the blocked texture "
                                  "block width (%d), or when added to dstOffset.x (%d) must equal the image subresource width (%d).",
                                  func_name, i, dst_copy_extent.width, block_size.width, region.dstOffset.x, mip_extent.width);
             }
@@ -6313,23 +6310,22 @@
         }
 
         // Checks that apply only to compressed images
-        if (FormatIsCompressed(image_format) || FormatIsSinglePlane_422(image_format)) {
+        if (FormatIsBlockedImage(image_format)) {
             auto block_size = FormatTexelBlockExtent(image_format);
 
             //  BufferRowLength must be a multiple of block width
             if (SafeModulo(pRegions[i].bufferRowLength, block_size.width) != 0) {
-                skip |=
-                    LogError(image_state->image(), GetBufferImageCopyCommandVUID("00203", image_to_buffer, is_2khr),
-                             "%s: pRegion[%d] bufferRowLength (%d) must be a multiple of the compressed image's texel width (%d).",
-                             function, i, pRegions[i].bufferRowLength, block_size.width);
+                skip |= LogError(image_state->image(), GetBufferImageCopyCommandVUID("00203", image_to_buffer, is_2khr),
+                                 "%s: pRegion[%d] bufferRowLength (%d) must be a multiple of the blocked image's texel width (%d).",
+                                 function, i, pRegions[i].bufferRowLength, block_size.width);
             }
 
             //  BufferRowHeight must be a multiple of block height
             if (SafeModulo(pRegions[i].bufferImageHeight, block_size.height) != 0) {
-                skip |= LogError(
-                    image_state->image(), GetBufferImageCopyCommandVUID("00204", image_to_buffer, is_2khr),
-                    "%s: pRegion[%d] bufferImageHeight (%d) must be a multiple of the compressed image's texel height (%d).",
-                    function, i, pRegions[i].bufferImageHeight, block_size.height);
+                skip |=
+                    LogError(image_state->image(), GetBufferImageCopyCommandVUID("00204", image_to_buffer, is_2khr),
+                             "%s: pRegion[%d] bufferImageHeight (%d) must be a multiple of the blocked image's texel height (%d).",
+                             function, i, pRegions[i].bufferImageHeight, block_size.height);
             }
 
             //  image offsets must be multiples of block dimensions
@@ -6337,7 +6333,7 @@
                 (SafeModulo(pRegions[i].imageOffset.y, block_size.height) != 0) ||
                 (SafeModulo(pRegions[i].imageOffset.z, block_size.depth) != 0)) {
                 skip |= LogError(image_state->image(), GetBufferImageCopyCommandVUID("00205", image_to_buffer, is_2khr),
-                                 "%s: pRegion[%d] imageOffset(x,y) (%d, %d) must be multiples of the compressed image's texel "
+                                 "%s: pRegion[%d] imageOffset(x,y) (%d, %d) must be multiples of the blocked image's texel "
                                  "width & height (%d, %d).",
                                  function, i, pRegions[i].imageOffset.x, pRegions[i].imageOffset.y, block_size.width,
                                  block_size.height);
@@ -6347,7 +6343,7 @@
             if (SafeModulo(bufferOffset, element_size) != 0) {
                 skip |= LogError(image_state->image(), GetBufferImageCopyCommandVUID("00206", image_to_buffer, is_2khr),
                                  "%s: pRegion[%d] bufferOffset (0x%" PRIxLEAST64
-                                 ") must be a multiple of the compressed image's texel block size (%" PRIu32 ").",
+                                 ") must be a multiple of the blocked image's texel block size (%" PRIu32 ").",
                                  function, i, bufferOffset, element_size);
             }
 
@@ -6356,7 +6352,7 @@
             if ((SafeModulo(pRegions[i].imageExtent.width, block_size.width) != 0) &&
                 (pRegions[i].imageExtent.width + pRegions[i].imageOffset.x != mip_extent.width)) {
                 skip |= LogError(image_state->image(), GetBufferImageCopyCommandVUID("00207", image_to_buffer, is_2khr),
-                                 "%s: pRegion[%d] extent width (%d) must be a multiple of the compressed texture block width "
+                                 "%s: pRegion[%d] extent width (%d) must be a multiple of the blocked texture block width "
                                  "(%d), or when added to offset.x (%d) must equal the image subresource width (%d).",
                                  function, i, pRegions[i].imageExtent.width, block_size.width, pRegions[i].imageOffset.x,
                                  mip_extent.width);
@@ -6366,7 +6362,7 @@
             if ((SafeModulo(pRegions[i].imageExtent.height, block_size.height) != 0) &&
                 (pRegions[i].imageExtent.height + pRegions[i].imageOffset.y != mip_extent.height)) {
                 skip |= LogError(image_state->image(), GetBufferImageCopyCommandVUID("00208", image_to_buffer, is_2khr),
-                                 "%s: pRegion[%d] extent height (%d) must be a multiple of the compressed texture block height "
+                                 "%s: pRegion[%d] extent height (%d) must be a multiple of the blocked texture block height "
                                  "(%d), or when added to offset.y (%d) must equal the image subresource height (%d).",
                                  function, i, pRegions[i].imageExtent.height, block_size.height, pRegions[i].imageOffset.y,
                                  mip_extent.height);
@@ -6376,7 +6372,7 @@
             if ((SafeModulo(pRegions[i].imageExtent.depth, block_size.depth) != 0) &&
                 (pRegions[i].imageExtent.depth + pRegions[i].imageOffset.z != mip_extent.depth)) {
                 skip |= LogError(image_state->image(), GetBufferImageCopyCommandVUID("00209", image_to_buffer, is_2khr),
-                                 "%s: pRegion[%d] extent width (%d) must be a multiple of the compressed texture block depth "
+                                 "%s: pRegion[%d] extent width (%d) must be a multiple of the blocked texture block depth "
                                  "(%d), or when added to offset.z (%d) must equal the image subresource depth (%d).",
                                  function, i, pRegions[i].imageExtent.depth, block_size.depth, pRegions[i].imageOffset.z,
                                  mip_extent.depth);
@@ -6455,8 +6451,9 @@
 
         VkExtent3D image_extent = image_state->GetSubresourceExtent(pRegions[i].imageSubresource);
 
-        // If we're using a compressed format, valid extent is rounded up to multiple of block size (per 18.1)
-        if (FormatIsCompressed(image_info->format) || FormatIsSinglePlane_422(image_state->createInfo.format)) {
+        // If we're using a blocked image format, valid extent is rounded up to multiple of block size (per
+        // vkspec.html#_common_operation)
+        if (FormatIsBlockedImage(image_info->format)) {
             auto block_extent = FormatTexelBlockExtent(image_info->format);
             if (image_extent.width % block_extent.width) {
                 image_extent.width += (block_extent.width - (image_extent.width % block_extent.width));
diff --git a/layers/state_tracker.h b/layers/state_tracker.h
index e436eac..973f22c 100644
--- a/layers/state_tracker.h
+++ b/layers/state_tracker.h
@@ -145,14 +145,12 @@
 // Destination image texel extents must be adjusted by block size for the dest validation checks
 static inline VkExtent3D GetAdjustedDestImageExtent(VkFormat src_format, VkFormat dst_format, VkExtent3D extent) {
     VkExtent3D adjusted_extent = extent;
-    if ((FormatIsCompressed(src_format) || FormatIsSinglePlane_422(src_format)) &&
-        !(FormatIsCompressed(dst_format) || FormatIsSinglePlane_422(dst_format))) {
+    if (FormatIsBlockedImage(src_format) && !FormatIsBlockedImage(dst_format)) {
         VkExtent3D block_size = FormatTexelBlockExtent(src_format);
         adjusted_extent.width /= block_size.width;
         adjusted_extent.height /= block_size.height;
         adjusted_extent.depth /= block_size.depth;
-    } else if (!(FormatIsCompressed(src_format) || FormatIsSinglePlane_422(src_format)) &&
-               (FormatIsCompressed(dst_format) || FormatIsSinglePlane_422(dst_format))) {
+    } else if (!FormatIsBlockedImage(src_format) && FormatIsBlockedImage(dst_format)) {
         VkExtent3D block_size = FormatTexelBlockExtent(dst_format);
         adjusted_extent.width *= block_size.width;
         adjusted_extent.height *= block_size.height;
@@ -203,7 +201,7 @@
         unit_size = FormatElementSize(image_format, region.imageSubresource.aspectMask);
     }
 
-    if (FormatIsCompressed(image_format) || FormatIsSinglePlane_422(image_format)) {
+    if (FormatIsBlockedImage(image_format)) {
         // Switch to texel block units, rounding up for any partially-used blocks
         auto block_dim = FormatTexelBlockExtent(image_format);
         buffer_width = (buffer_width + block_dim.width - 1) / block_dim.width;
diff --git a/layers/vk_format_utils.cpp b/layers/vk_format_utils.cpp
index 7d98e22..d9d356d 100644
--- a/layers/vk_format_utils.cpp
+++ b/layers/vk_format_utils.cpp
@@ -472,6 +472,12 @@
     return (FormatIsCompressed_ASTC(format) || FormatIsCompressed_BC(format) || FormatIsCompressed_ETC2_EAC(format) ||
             FormatIsCompressed_PVRTC(format));
 }
+
+// "blocked image" are defined in the spec (vkspec.html#blocked-image)
+VK_LAYER_EXPORT bool FormatIsBlockedImage(VkFormat format) {
+    return (FormatIsCompressed(format) || FormatIsSinglePlane_422(format));
+}
+
 // Return true if format is packed
 VK_LAYER_EXPORT bool FormatIsPacked(VkFormat format) {
     bool found = false;
diff --git a/layers/vk_format_utils.h b/layers/vk_format_utils.h
index a0edcde..1876938 100644
--- a/layers/vk_format_utils.h
+++ b/layers/vk_format_utils.h
@@ -152,6 +152,7 @@
 VK_LAYER_EXPORT bool FormatIsCompressed_BC(VkFormat format);
 VK_LAYER_EXPORT bool FormatIsCompressed_PVRTC(VkFormat format);
 VK_LAYER_EXPORT bool FormatIsSinglePlane_422(VkFormat format);
+VK_LAYER_EXPORT bool FormatIsBlockedImage(VkFormat format);
 VK_LAYER_EXPORT bool FormatIsNorm(VkFormat format);
 VK_LAYER_EXPORT bool FormatIsUNorm(VkFormat format);
 VK_LAYER_EXPORT bool FormatIsSNorm(VkFormat format);