text: // Copyright 2016 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "components/viz/common/resources/resource_format_utils.h"
  
  #include <ostream>
  
  #include "base/check_op.h"
  #include "base/cxx17_backports.h"
  #include "base/notreached.h"
  #include "third_party/khronos/GLES2/gl2.h"
  #include "third_party/khronos/GLES2/gl2ext.h"
  #include "ui/gfx/buffer_types.h"
  
  namespace viz {
  
  SkColorType ResourceFormatToClosestSkColorType(bool gpu_compositing,
                                                 ResourceFormat format) {
    if (!gpu_compositing) {
      // TODO(crbug.com/986405): Remove this assumption and have clients tag
      // resources with the correct format.
      // In software compositing we lazily use RGBA_8888 throughout the system,
      // but actual pixel encodings are the native skia bit ordering, which can be
      // RGBA or BGRA.
      return kN32_SkColorType;
    }
  
    switch (format) {
      case RGBA_4444:
        return kARGB_4444_SkColorType;
      case RGBA_8888:
        return kRGBA_8888_SkColorType;
      case BGRA_8888:
        return kBGRA_8888_SkColorType;
      case ALPHA_8:
        return kAlpha_8_SkColorType;
      case RGB_565:
        return kRGB_565_SkColorType;
      case LUMINANCE_8:
        return kGray_8_SkColorType;
      case RGBX_8888:
      case ETC1:
        return kRGB_888x_SkColorType;
      case P010:
      case RGBA_1010102:
        return kRGBA_1010102_SkColorType;
      case BGRA_1010102:
        return kBGRA_1010102_SkColorType;
  
      // YUV images are sampled as RGB.
      case YVU_420:
      case YUV_420_BIPLANAR:
        return kRGB_888x_SkColorType;
  
      // Use kN32_SkColorType if there is no corresponding SkColorType.
      case RED_8:
        return kGray_8_SkColorType;
      case LUMINANCE_F16:
      case R16_EXT:
      case RG16_EXT:
      case BGR_565:
        return kN32_SkColorType;
      case RG_88:
        return kR8G8_unorm_SkColorType;
      case BGRX_8888:
        return kRGB_888x_SkColorType;
      case RGBA_F16:
        return kRGBA_F16_SkColorType;
    }
    NOTREACHED();
    return kN32_SkColorType;
  }
  
  ResourceFormat SkColorTypeToResourceFormat(SkColorType color_type) {
    switch (color_type) {
      case kARGB_4444_SkColorType:
        return RGBA_4444;
      case kBGRA_8888_SkColorType:
        return BGRA_8888;
      case kRGBA_8888_SkColorType:
        return RGBA_8888;
      case kRGBA_F16_SkColorType:
        return RGBA_F16;
      case kAlpha_8_SkColorType:
        return ALPHA_8;
      case kRGB_565_SkColorType:
        return RGB_565;
      case kGray_8_SkColorType:
        return LUMINANCE_8;
      case kRGB_888x_SkColorType:
        return RGBX_8888;
      case kRGBA_1010102_SkColorType:
        return RGBA_1010102;
      case kBGRA_1010102_SkColorType:
        return BGRA_1010102;
      // These colortypes are just for reading from - not to render to
      case kR8G8_unorm_SkColorType:
      case kA16_float_SkColorType:
      case kR16G16_float_SkColorType:
      case kA16_unorm_SkColorType:
      case kR16G16_unorm_SkColorType:
      case kR16G16B16A16_unorm_SkColorType:
      case kUnknown_SkColorType:
      // These colortypes are don't have an equivalent in ResourceFormat
      case kRGB_101010x_SkColorType:
      case kBGR_101010x_SkColorType:
      case kRGBA_F16Norm_SkColorType:
      case kRGBA_F32_SkColorType:
        break;
    }
    NOTREACHED();
    return RGBA_8888;
  }
  
  int BitsPerPixel(ResourceFormat format) {
    switch (format) {
      case RGBA_F16:
        return 64;
      case BGRA_8888:
      case RGBA_8888:
      case RGBX_8888:
      case BGRX_8888:
      case RGBA_1010102:
      case BGRA_1010102:
      case P010:
      case RG16_EXT:
        return 32;
      case RGBA_4444:
      case RGB_565:
      case LUMINANCE_F16:
      case R16_EXT:
      case BGR_565:
      case RG_88:
        return 16;
      case YVU_420:
      case YUV_420_BIPLANAR:
        return 12;
      case ALPHA_8:
      case LUMINANCE_8:
      case RED_8:
        return 8;
      case ETC1:
        return 4;
    }
    NOTREACHED();
    return 0;
  }
  
  bool HasAlpha(ResourceFormat format) {
    switch (format) {
      case RGBA_8888:
      case RGBA_4444:
      case BGRA_8888:
      case ALPHA_8:
      case RGBA_F16:
        return true;
      case LUMINANCE_8:
      case RGB_565:
      case BGR_565:
      case ETC1:
      case RED_8:
      case RG_88:
      case LUMINANCE_F16:
      case R16_EXT:
      case RG16_EXT:
      case RGBX_8888:
      case BGRX_8888:
      case RGBA_1010102:
      case BGRA_1010102:
      case YVU_420:
      case YUV_420_BIPLANAR:
      case P010:
        return false;
    }
    NOTREACHED();
    return false;
  }
  
  unsigned int GLDataType(ResourceFormat format) {
    DCHECK_LE(format, RESOURCE_FORMAT_MAX);
    static const GLenum format_gl_data_type[] = {
        GL_UNSIGNED_BYTE,                    // RGBA_8888
        GL_UNSIGNED_SHORT_4_4_4_4,           // RGBA_4444
        GL_UNSIGNED_BYTE,                    // BGRA_8888
        GL_UNSIGNED_BYTE,                    // ALPHA_8
        GL_UNSIGNED_BYTE,                    // LUMINANCE_8
        GL_UNSIGNED_SHORT_5_6_5,             // RGB_565,
        GL_ZERO,                             // BGR_565
        GL_UNSIGNED_BYTE,                    // ETC1
        GL_UNSIGNED_BYTE,                    // RED_8
        GL_UNSIGNED_BYTE,                    // RG_88
        GL_HALF_FLOAT_OES,                   // LUMINANCE_F16
        GL_HALF_FLOAT_OES,                   // RGBA_F16
        GL_UNSIGNED_SHORT,                   // R16_EXT
        GL_UNSIGNED_SHORT,                   // RG16_EXT
        GL_UNSIGNED_BYTE,                    // RGBX_8888
        GL_UNSIGNED_BYTE,                    // BGRX_8888
        GL_UNSIGNED_INT_2_10_10_10_REV_EXT,  // RGBA_1010102
        GL_UNSIGNED_INT_2_10_10_10_REV_EXT,  // BGRA_1010102
        GL_ZERO,                             // YVU_420
        GL_ZERO,                             // YUV_420_BIPLANAR
        GL_ZERO,                             // P010
    };
    static_assert(base::size(format_gl_data_type) == (RESOURCE_FORMAT_MAX + 1),
                  "format_gl_data_type does not handle all cases.");
  
    return format_gl_data_type[format];
  }
  
  unsigned int GLDataFormat(ResourceFormat format) {
    DCHECK_LE(format, RESOURCE_FORMAT_MAX);
    static const GLenum format_gl_data_format[] = {
        GL_RGBA,       // RGBA_8888
        GL_RGBA,       // RGBA_4444
        GL_BGRA_EXT,   // BGRA_8888
        GL_ALPHA,      // ALPHA_8
        GL_LUMINANCE,  // LUMINANCE_8
        GL_RGB,        // RGB_565
        GL_ZERO,       // BGR_565
        GL_RGB,        // ETC1
        GL_RED_EXT,    // RED_8
        GL_RG_EXT,     // RG_88
        GL_LUMINANCE,  // LUMINANCE_F16
        GL_RGBA,       // RGBA_F16
        GL_RED_EXT,    // R16_EXT
        GL_RG_EXT,     // RG16_EXT
        GL_RGB,        // RGBX_8888
        GL_RGB,        // BGRX_8888
        GL_RGBA,       // RGBA_1010102
        GL_RGBA,       // BGRA_1010102
        GL_ZERO,       // YVU_420
        GL_ZERO,       // YUV_420_BIPLANAR
        GL_ZERO,       // P010
    };
    static_assert(base::size(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1),
                  "format_gl_data_format does not handle all cases.");
  
    return format_gl_data_format[format];
  }
  
  unsigned int GLInternalFormat(ResourceFormat format) {
    // In GLES2, the internal format must match the texture format. (It no longer
    // is true in GLES3, however it still holds for the BGRA extension.)
    // GL_EXT_texture_norm16 follows GLES3 semantics and only exposes a sized
    // internal format (GL_R16_EXT).
    switch (format) {
      case R16_EXT:
        return GL_R16_EXT;
      case RG16_EXT:
        return GL_RG16_EXT;
      case ETC1:
        return GL_ETC1_RGB8_OES;
      case RGBA_1010102:
      case BGRA_1010102:
        return GL_RGB10_A2_EXT;
      default:
        return GLDataFormat(format);
    }
  }
  
  unsigned int GLCopyTextureInternalFormat(ResourceFormat format) {
    // In GLES2, valid formats for glCopyTexImage2D are: GL_ALPHA, GL_LUMINANCE,
    // GL_LUMINANCE_ALPHA, GL_RGB, or GL_RGBA.
    // Extensions typically used for glTexImage2D do not also work for
    // glCopyTexImage2D. For instance GL_BGRA_EXT may not be used for
    // anything but gl(Sub)TexImage2D:
    // https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt
    DCHECK_LE(format, RESOURCE_FORMAT_MAX);
    static const GLenum format_gl_data_format[] = {
        GL_RGBA,       // RGBA_8888
        GL_RGBA,       // RGBA_4444
        GL_RGBA,       // BGRA_8888
        GL_ALPHA,      // ALPHA_8
        GL_LUMINANCE,  // LUMINANCE_8
        GL_RGB,        // RGB_565
        GL_ZERO,       // BGR_565
        GL_RGB,        // ETC1
        GL_LUMINANCE,  // RED_8
        GL_RGBA,       // RG_88
        GL_LUMINANCE,  // LUMINANCE_F16
        GL_RGBA,       // RGBA_F16
        GL_LUMINANCE,  // R16_EXT
        GL_RGBA,       // RG16_EXT
        GL_RGB,        // RGBX_8888
        GL_RGB,        // BGRX_8888
        GL_ZERO,       // RGBA_1010102
        GL_ZERO,       // BGRA_1010102
        GL_ZERO,       // YVU_420
        GL_ZERO,       // YUV_420_BIPLANAR
        GL_ZERO,       // P010
    };
  
    static_assert(base::size(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1),
                  "format_gl_data_format does not handle all cases.");
  
    return format_gl_data_format[format];
  }
  
  gfx::BufferFormat BufferFormat(ResourceFormat format) {
    switch (format) {
      case BGRA_8888:
        return gfx::BufferFormat::BGRA_8888;
      case RED_8:
        return gfx::BufferFormat::R_8;
      case R16_EXT:
        return gfx::BufferFormat::R_16;
      case RGBA_4444:
        return gfx::BufferFormat::RGBA_4444;
      case RGBA_8888:
        return gfx::BufferFormat::RGBA_8888;
      case RGBA_F16:
        return gfx::BufferFormat::RGBA_F16;
      case BGR_565:
        return gfx::BufferFormat::BGR_565;
      case RG_88:
        return gfx::BufferFormat::RG_88;
      case RGBX_8888:
        return gfx::BufferFormat::RGBX_8888;
      case BGRX_8888:
        return gfx::BufferFormat::BGRX_8888;
      case RGBA_1010102:
        return gfx::BufferFormat::RGBA_1010102;
      case BGRA_1010102:
        return gfx::BufferFormat::BGRA_1010102;
      case YVU_420:
        return gfx::BufferFormat::YVU_420;
      case YUV_420_BIPLANAR:
        return gfx::BufferFormat::YUV_420_BIPLANAR;
      case P010:
        return gfx::BufferFormat::P010;
      case ETC1:
      case ALPHA_8:
      case LUMINANCE_8:
      case RGB_565:
      case LUMINANCE_F16:
      case RG16_EXT:
        // These types not allowed by IsGpuMemoryBufferFormatSupported(), so
        // give a default value that will not be used.
        break;
    }
    return gfx::BufferFormat::RGBA_8888;
  }
  
  bool IsResourceFormatCompressed(ResourceFormat format) {
    return format == ETC1;
  }
  
  unsigned int TextureStorageFormat(ResourceFormat format) {
    switch (format) {
      case RGBA_8888:
        return GL_RGBA8_OES;
      case BGRA_8888:
        return GL_BGRA8_EXT;
      case RGBA_F16:
        return GL_RGBA16F_EXT;
      case RGBA_4444:
        return GL_RGBA4;
      case ALPHA_8:
        return GL_ALPHA8_EXT;
      case LUMINANCE_8:
        return GL_LUMINANCE8_EXT;
      case RGB_565:
        return GL_RGB565;
      case RED_8:
        return GL_R8_EXT;
      case RG_88:
        return GL_RG8_EXT;
      case LUMINANCE_F16:
        return GL_LUMINANCE16F_EXT;
      case R16_EXT:
        return GL_R16_EXT;
      case RG16_EXT:
        return GL_RG16_EXT;
      case RGBX_8888:
      case ETC1:
        return GL_RGB8_OES;
      case P010:
      case RGBA_1010102:
      case BGRA_1010102:
        return GL_RGB10_A2_EXT;
      case YVU_420:
      case YUV_420_BIPLANAR:
        return GL_RGB8_OES;
      case BGRX_8888:
        return GL_RGB8_OES;
      case BGR_565:
        break;
    }
    NOTREACHED();
    return GL_RGBA8_OES;
  }
  
  bool IsGpuMemoryBufferFormatSupported(ResourceFormat format) {
    switch (format) {
      case BGRA_8888:
      case RED_8:
      case R16_EXT:
      case RGBA_4444:
      case RGBA_8888:
      case RGBA_1010102:
      case BGRA_1010102:
      case RGBA_F16:
        return true;
      // These formats have no BufferFormat equivalent or are only used
      // for external textures, or have no GL equivalent formats.
      case ETC1:
      case ALPHA_8:
      case LUMINANCE_8:
      case RGB_565:
      case LUMINANCE_F16:
      case BGR_565:
      case RG_88:
      case RG16_EXT:
      case RGBX_8888:
      case BGRX_8888:
      case YVU_420:
      case YUV_420_BIPLANAR:
      case P010:
        return false;
    }
    NOTREACHED();
    return false;
  }
  
  bool IsBitmapFormatSupported(ResourceFormat format) {
    switch (format) {
      case RGBA_8888:
        return true;
      case RGBA_4444:
      case BGRA_8888:
      case ALPHA_8:
      case LUMINANCE_8:
      case RGB_565:
      case ETC1:
      case RED_8:
      case LUMINANCE_F16:
      case RGBA_F16:
      case R16_EXT:
      case RG16_EXT:
      case BGR_565:
      case RG_88:
      case RGBX_8888:
      case BGRX_8888:
      case RGBA_1010102:
      case BGRA_1010102:
      case YVU_420:
      case YUV_420_BIPLANAR:
      case P010:
        return false;
    }
    NOTREACHED();
    return false;
  }
  
  ResourceFormat GetResourceFormat(gfx::BufferFormat format) {
    switch (format) {
      case gfx::BufferFormat::BGRA_8888:
        return BGRA_8888;
      case gfx::BufferFormat::R_8:
        return RED_8;
      case gfx::BufferFormat::R_16:
        return R16_EXT;
      case gfx::BufferFormat::RGBA_4444:
        return RGBA_4444;
      case gfx::BufferFormat::RGBA_8888:
        return RGBA_8888;
      case gfx::BufferFormat::RGBA_F16:
        return RGBA_F16;
      case gfx::BufferFormat::BGR_565:
        return BGR_565;
      case gfx::BufferFormat::RG_88:
        return RG_88;
      case gfx::BufferFormat::RGBX_8888:
        return RGBX_8888;
      case gfx::BufferFormat::BGRX_8888:
        return BGRX_8888;
      case gfx::BufferFormat::RGBA_1010102:
        return RGBA_1010102;
      case gfx::BufferFormat::BGRA_1010102:
        return BGRA_1010102;
      case gfx::BufferFormat::YVU_420:
        return YVU_420;
      case gfx::BufferFormat::YUV_420_BIPLANAR:
        return YUV_420_BIPLANAR;
      case gfx::BufferFormat::P010:
        return P010;
    }
    NOTREACHED();
    return RGBA_8888;
  }
  
  bool GLSupportsFormat(ResourceFormat format) {
    switch (format) {
      case BGR_565:
      case BGRX_8888:
      case YVU_420:
      case YUV_420_BIPLANAR:
      case P010:
        return false;
      default:
        return true;
    }
  }
  
  #if BUILDFLAG(ENABLE_VULKAN)
  namespace {
  VkFormat ToVkFormatInternal(ResourceFormat format) {
    switch (format) {
      case RGBA_8888:
        return VK_FORMAT_R8G8B8A8_UNORM;  // or VK_FORMAT_R8G8B8A8_SRGB
      case RGBA_4444:
        return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
      case BGRA_8888:
        return VK_FORMAT_B8G8R8A8_UNORM;
      case RED_8:
        return VK_FORMAT_R8_UNORM;
      case RGB_565:
        return VK_FORMAT_R5G6B5_UNORM_PACK16;
      case BGR_565:
        return VK_FORMAT_B5G6R5_UNORM_PACK16;
      case RG_88:
        return VK_FORMAT_R8G8_UNORM;
      case RGBA_F16:
        return VK_FORMAT_R16G16B16A16_SFLOAT;
      case R16_EXT:
        return VK_FORMAT_R16_UNORM;
      case RG16_EXT:
        return VK_FORMAT_R16G16_UNORM;
      case RGBX_8888:
        return VK_FORMAT_R8G8B8A8_UNORM;
      case BGRX_8888:
        return VK_FORMAT_B8G8R8A8_UNORM;
      case RGBA_1010102:
        return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
      case BGRA_1010102:
        return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
      case ALPHA_8:
        return VK_FORMAT_R8_UNORM;
      case LUMINANCE_8:
        return VK_FORMAT_R8_UNORM;
      case YVU_420:
        return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
      case YUV_420_BIPLANAR:
        return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
      case ETC1:
        return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
      case LUMINANCE_F16:
        return VK_FORMAT_R16_SFLOAT;
      case P010:
        break;
    }
    return VK_FORMAT_UNDEFINED;
  }
  }  // namespace
  
  bool HasVkFormat(ResourceFormat format) {
    return ToVkFormatInternal(format) != VK_FORMAT_UNDEFINED;
  }
  
  VkFormat ToVkFormat(ResourceFormat format) {
    auto result = ToVkFormatInternal(format);
    DCHECK_NE(result, VK_FORMAT_UNDEFINED) << "Unsupported format " << format;
    return result;
  }
  #endif
  
  wgpu::TextureFormat ToDawnFormat(ResourceFormat format) {
    switch (format) {
      case RGBA_8888:
      case RGBX_8888:
        return wgpu::TextureFormat::RGBA8Unorm;
      case BGRA_8888:
      case BGRX_8888:
        return wgpu::TextureFormat::BGRA8Unorm;
      case RED_8:
      case ALPHA_8:
      case LUMINANCE_8:
        return wgpu::TextureFormat::R8Unorm;
      case RG_88:
        return wgpu::TextureFormat::RG8Unorm;
      case RGBA_F16:
        return wgpu::TextureFormat::RGBA16Float;
      case RGBA_1010102:
        return wgpu::TextureFormat::RGB10A2Unorm;
      case RGBA_4444:
      case RGB_565:
      case BGR_565:
      case R16_EXT:
      case RG16_EXT:
      case BGRA_1010102:
      case YVU_420:
      case YUV_420_BIPLANAR:
      case ETC1:
      case LUMINANCE_F16:
      case P010:
        break;
    }
    return wgpu::TextureFormat::Undefined;
  }
  
  WGPUTextureFormat ToWGPUFormat(ResourceFormat format) {
    return static_cast<WGPUTextureFormat>(ToDawnFormat(format));
  }
  
  }  // namespace viz
  