| /* | |
| * Copyright 2025 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/graphite/TextureFormat.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkColorType.h" | |
| #include "src/core/SkImageInfoPriv.h" | |
| namespace skgpu::graphite { | |
| using TF = TextureFormat; // for brevity in this file | |
| const char* TextureFormatName(TextureFormat format) { | |
| switch (format) { | |
| case TF::kUnsupported: return "Unsupported"; | |
| case TF::kR8: return "R8"; | |
| case TF::kR16: return "R16"; | |
| case TF::kR16F: return "R16F"; | |
| case TF::kR32F: return "R32F"; | |
| case TF::kA8: return "A8"; | |
| case TF::kRG8: return "RG8"; | |
| case TF::kRG16: return "RG16"; | |
| case TF::kRG16F: return "RG16F"; | |
| case TF::kRG32F: return "RG32F"; | |
| case TF::kRGB8: return "RGB8"; | |
| case TF::kBGR8: return "BGR8"; | |
| case TF::kB5_G6_R5: return "B5_G6_R5"; | |
| case TF::kR5_G6_B5: return "R5_G6_B5"; | |
| case TF::kRGB16: return "RGB16"; | |
| case TF::kRGB16F: return "RGB16F"; | |
| case TF::kRGB32F: return "RGB32F"; | |
| case TF::kRGB8_sRGB: return "RGB8_sRGB"; | |
| case TF::kBGR10_XR: return "BGR10_XR"; | |
| case TF::kRGBA8: return "RGBA8"; | |
| case TF::kRGBA16: return "RBGA16"; | |
| case TF::kRGBA16F: return "RGBA16F"; | |
| case TF::kRGBA32F: return "RGBA32F"; | |
| case TF::kRGB10_A2: return "RGB10_A2"; | |
| case TF::kRGBA10x6: return "RGBA10x6"; | |
| case TF::kRGBA8_sRGB: return "RGBA8_sRGB"; | |
| case TF::kBGRA8: return "BGRA8"; | |
| case TF::kBGR10_A2: return "BGR10_A2"; | |
| case TF::kBGRA8_sRGB: return "BGRA8_sRGB"; | |
| case TF::kABGR4: return "ABGR4"; | |
| case TF::kARGB4: return "ARGB4"; | |
| case TF::kBGRA10x6_XR: return "BGRA10x6_XR"; | |
| case TF::kRGB8_ETC2: return "RGB8_ETC2"; | |
| case TF::kRGB8_ETC2_sRGB: return "RGB8_ETC2_sRGB"; | |
| case TF::kRGB8_BC1: return "RGB8_BC1"; | |
| case TF::kRGBA8_BC1: return "RGBA8_BC1"; | |
| case TF::kRGBA8_BC1_sRGB: return "RGBA8_BC1_sRGB"; | |
| case TF::kYUV8_P2_420: return "YUV8_P2_420"; | |
| case TF::kYUV8_P3_420: return "YUV8_P3_420"; | |
| case TF::kYUV10x6_P2_420: return "YUV10x6_P2_420"; | |
| case TF::kYUV8_P2_422: return "YUV8_P2_422"; | |
| case TF::kYUV8_P3_422: return "YUV8_P3_422"; | |
| case TF::kYUV10x6_P2_422: return "YUV10x6_P2_422"; | |
| case TF::kYUV8_P2_444: return "YUV8_P2_444"; | |
| case TF::kYUV8_P3_444: return "YUV8_P3_444"; | |
| case TF::kYUV10x6_P2_444: return "YUV10x6_P2_444"; | |
| case TF::kExternal: return "External"; | |
| case TF::kS8: return "S8"; | |
| case TF::kD16: return "D16"; | |
| case TF::kD32F: return "D32F"; | |
| case TF::kD24_S8: return "D24_S8"; | |
| case TF::kD32F_S8: return "D32F_S8"; | |
| } | |
| SkUNREACHABLE; | |
| } | |
| SkTextureCompressionType TextureFormatCompressionType(TextureFormat format) { | |
| switch (format) { | |
| case TF::kRGB8_ETC2: [[fallthrough]]; | |
| case TF::kRGB8_ETC2_sRGB: return SkTextureCompressionType::kETC2_RGB8_UNORM; | |
| case TF::kRGB8_BC1: return SkTextureCompressionType::kBC1_RGB8_UNORM; | |
| case TF::kRGBA8_BC1: [[fallthrough]]; | |
| case TF::kRGBA8_BC1_sRGB: return SkTextureCompressionType::kBC1_RGBA8_UNORM; | |
| default: return SkTextureCompressionType::kNone; | |
| } | |
| } | |
| TextureFormat CompressionTypeToTextureFormat(SkTextureCompressionType type) { | |
| switch (type) { | |
| case SkTextureCompressionType::kBC1_RGB8_UNORM: return TF::kRGB8_BC1; | |
| case SkTextureCompressionType::kBC1_RGBA8_UNORM: return TF::kRGBA8_BC1; | |
| case SkTextureCompressionType::kETC2_RGB8_UNORM: return TF::kRGB8_ETC2; | |
| default: return TF::kUnsupported; | |
| } | |
| } | |
| int TextureFormatBytesPerBlock(TextureFormat format) { | |
| switch (format) { | |
| case TF::kUnsupported: return 0; | |
| case TF::kR8: return 1; | |
| case TF::kR16: return 2; | |
| case TF::kR16F: return 2; | |
| case TF::kR32F: return 4; | |
| case TF::kA8: return 1; | |
| case TF::kRG8: return 2; | |
| case TF::kRG16: return 4; | |
| case TF::kRG16F: return 4; | |
| case TF::kRG32F: return 8; | |
| case TF::kRGB8: return 3; | |
| case TF::kBGR8: return 3; | |
| case TF::kB5_G6_R5: return 2; | |
| case TF::kR5_G6_B5: return 2; | |
| case TF::kRGB16: return 6; | |
| case TF::kRGB16F: return 6; | |
| case TF::kRGB32F: return 12; | |
| case TF::kRGB8_sRGB: return 3; | |
| case TF::kBGR10_XR: return 4; | |
| case TF::kRGBA8: return 4; | |
| case TF::kRGBA16: return 8; | |
| case TF::kRGBA16F: return 8; | |
| case TF::kRGBA32F: return 16; | |
| case TF::kRGB10_A2: return 4; | |
| case TF::kRGBA10x6: return 8; | |
| case TF::kRGBA8_sRGB: return 4; | |
| case TF::kBGRA8: return 4; | |
| case TF::kBGR10_A2: return 4; | |
| case TF::kBGRA8_sRGB: return 4; | |
| case TF::kABGR4: return 2; | |
| case TF::kARGB4: return 2; | |
| case TF::kBGRA10x6_XR: return 8; | |
| case TF::kS8: return 1; | |
| case TF::kD16: return 2; | |
| case TF::kD32F: return 4; | |
| case TF::kD24_S8: return 4; | |
| case TF::kD32F_S8: return 5; // Assuming it's multiplanar | |
| // NOTE: For compressed formats, the block size refers to an actual compressed block of | |
| // multiple texels, whereas with other formats the block size represents a single pixel. | |
| case TF::kRGB8_ETC2: | |
| case TF::kRGB8_ETC2_sRGB: | |
| case TF::kRGB8_BC1: | |
| case TF::kRGBA8_BC1: | |
| case TF::kRGBA8_BC1_sRGB: | |
| return 8; | |
| // NOTE: We don't actually know the size of external formats, so this is an arbitrary value. | |
| // We will see external formats only in wrapped SkImages, so this won't impact Skia's | |
| // internal budgeting. | |
| case TF::kExternal: | |
| return 4; | |
| // TODO(b/401016699): We are just over estimating this value to be used in gpu size | |
| // calculations even though the actually size is probably less. We should instead treat | |
| // planar formats similar to compressed textures that go through their own special query for | |
| // calculating size. | |
| case TF::kYUV8_P2_420: | |
| case TF::kYUV8_P3_420: | |
| case TF::kYUV8_P2_422: | |
| case TF::kYUV8_P3_422: | |
| case TF::kYUV8_P2_444: | |
| case TF::kYUV8_P3_444: | |
| return 3; | |
| case TF::kYUV10x6_P2_420: | |
| case TF::kYUV10x6_P2_422: | |
| case TF::kYUV10x6_P2_444: | |
| return 6; | |
| } | |
| SkUNREACHABLE; | |
| } | |
| uint32_t TextureFormatChannelMask(TextureFormat format) { | |
| switch (format) { | |
| case TF::kA8: return kAlpha_SkColorChannelFlag; | |
| case TF::kR8: [[fallthrough]]; | |
| case TF::kR16: | |
| case TF::kR16F: | |
| case TF::kR32F: return kRed_SkColorChannelFlag; | |
| case TF::kRG8: [[fallthrough]]; | |
| case TF::kRG16: | |
| case TF::kRG16F: | |
| case TF::kRG32F: return kRG_SkColorChannelFlags; | |
| case TF::kRGB8: [[fallthrough]]; | |
| case TF::kBGR8: | |
| case TF::kB5_G6_R5: | |
| case TF::kR5_G6_B5: | |
| case TF::kRGB16: | |
| case TF::kRGB16F: | |
| case TF::kRGB32F: | |
| case TF::kRGB8_sRGB: | |
| case TF::kBGR10_XR: | |
| case TF::kRGB8_ETC2: | |
| case TF::kRGB8_ETC2_sRGB: | |
| case TF::kRGB8_BC1: | |
| case TF::kYUV8_P2_420: | |
| case TF::kYUV8_P3_420: | |
| case TF::kYUV10x6_P2_420: | |
| case TF::kYUV8_P2_422: | |
| case TF::kYUV8_P3_422: | |
| case TF::kYUV10x6_P2_422: | |
| case TF::kYUV8_P2_444: | |
| case TF::kYUV8_P3_444: | |
| case TF::kYUV10x6_P2_444: return kRGB_SkColorChannelFlags; | |
| case TF::kRGBA8: [[fallthrough]]; | |
| case TF::kRGBA16: | |
| case TF::kRGBA16F: | |
| case TF::kRGBA32F: | |
| case TF::kRGB10_A2: | |
| case TF::kRGBA10x6: | |
| case TF::kRGBA8_sRGB: | |
| case TF::kBGRA8: | |
| case TF::kBGR10_A2: | |
| case TF::kBGRA8_sRGB: | |
| case TF::kABGR4: | |
| case TF::kARGB4: | |
| case TF::kBGRA10x6_XR: | |
| case TF::kRGBA8_BC1: | |
| case TF::kRGBA8_BC1_sRGB: | |
| case TF::kExternal: return kRGBA_SkColorChannelFlags; | |
| case TF::kS8: [[fallthrough]]; | |
| case TF::kD16: | |
| case TF::kD32F: | |
| case TF::kD24_S8: | |
| case TF::kD32F_S8: | |
| case TF::kUnsupported: return 0; | |
| } | |
| SkUNREACHABLE; | |
| } | |
| bool TextureFormatAutoClamps(TextureFormat format) { | |
| // Floating point formats, extended range formats, and non-normalized integer formats do not | |
| // auto-clamp. Everything behaves like an unsigned normalized number. | |
| return !(TextureFormatIsFloatingPoint(format) || | |
| format == TF::kBGR10_XR || | |
| format == TF::kBGRA10x6_XR || | |
| format == TF::kS8); | |
| } | |
| bool TextureFormatIsFloatingPoint(TextureFormat format) { | |
| switch (format) { | |
| // Floating point formats | |
| case TF::kR16F: [[fallthrough]]; | |
| case TF::kR32F: | |
| case TF::kRG16F: | |
| case TF::kRG32F: | |
| case TF::kRGB16F: | |
| case TF::kRGB32F: | |
| case TF::kRGBA16F: | |
| case TF::kRGBA32F: | |
| case TF::kD32F: | |
| case TF::kD32F_S8: return true; | |
| // Everything else is unorm, unorm-srgb, fixed point, or integral | |
| case TF::kUnsupported: [[fallthrough]]; | |
| case TF::kR8: | |
| case TF::kR16: | |
| case TF::kA8: | |
| case TF::kRG8: | |
| case TF::kRG16: | |
| case TF::kRGB8: | |
| case TF::kBGR8: | |
| case TF::kB5_G6_R5: | |
| case TF::kR5_G6_B5: | |
| case TF::kRGB16: | |
| case TF::kRGB8_sRGB: | |
| case TF::kBGR10_XR: | |
| case TF::kRGBA8: | |
| case TF::kRGBA16: | |
| case TF::kRGB10_A2: | |
| case TF::kRGBA10x6: | |
| case TF::kRGBA8_sRGB: | |
| case TF::kBGRA8: | |
| case TF::kBGR10_A2: | |
| case TF::kBGRA8_sRGB: | |
| case TF::kABGR4: | |
| case TF::kARGB4: | |
| case TF::kBGRA10x6_XR: | |
| case TF::kRGB8_ETC2: | |
| case TF::kRGB8_ETC2_sRGB: | |
| case TF::kRGB8_BC1: | |
| case TF::kRGBA8_BC1: | |
| case TF::kRGBA8_BC1_sRGB: | |
| case TF::kYUV8_P2_420: | |
| case TF::kYUV8_P3_420: | |
| case TF::kYUV10x6_P2_420: | |
| case TF::kYUV8_P2_422: | |
| case TF::kYUV8_P3_422: | |
| case TF::kYUV10x6_P2_422: | |
| case TF::kYUV8_P2_444: | |
| case TF::kYUV8_P3_444: | |
| case TF::kYUV10x6_P2_444: | |
| case TF::kExternal: | |
| case TF::kS8: | |
| case TF::kD16: | |
| case TF::kD24_S8: return false; | |
| } | |
| SkUNREACHABLE; | |
| } | |
| bool TextureFormatIsDepthOrStencil(TextureFormat format) { | |
| switch (format) { | |
| case TF::kS8: [[fallthrough]]; | |
| case TF::kD16: | |
| case TF::kD32F: | |
| case TF::kD24_S8: | |
| case TF::kD32F_S8: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool TextureFormatHasDepth(TextureFormat format) { | |
| switch (format) { | |
| case TF::kD16: [[fallthrough]]; | |
| case TF::kD32F: | |
| case TF::kD24_S8: | |
| case TF::kD32F_S8: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool TextureFormatHasStencil(TextureFormat format) { | |
| switch (format) { | |
| case TF::kS8: [[fallthrough]]; | |
| case TF::kD24_S8: | |
| case TF::kD32F_S8: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool TextureFormatIsMultiplanar(TextureFormat format) { | |
| switch (format) { | |
| case TF::kYUV8_P2_420: [[fallthrough]]; | |
| case TF::kYUV8_P3_420: | |
| case TF::kYUV10x6_P2_420: | |
| case TF::kYUV8_P2_422: | |
| case TF::kYUV8_P3_422: | |
| case TF::kYUV10x6_P2_422: | |
| case TF::kYUV8_P2_444: | |
| case TF::kYUV8_P3_444: | |
| case TF::kYUV10x6_P2_444: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| // Supporting implementation details for TextureFormat and SkColorType conversions | |
| // ------------------------------------------------------------------------------------------------ | |
| Swizzle ReadSwizzleForColorType(SkColorType ct, TextureFormat format) { | |
| uint32_t colorChannels = SkColorTypeChannelFlags(ct); | |
| uint32_t formatChannels = TextureFormatChannelMask(format); | |
| // Read swizzles only have to handle a few semantics around the sampled values, as any sort of | |
| // channel ordering for RGB vs BGR is handled by hardware. All we have to handle is mapping to | |
| // "gray", red-vs-alpha, and forcing to opaque. | |
| if (SkColorTypeIsAlphaOnly(ct)) { | |
| // If the format isn't just an alpha channel (e.g. TF::kA8), we need to adjust | |
| if (formatChannels != kAlpha_SkColorChannelFlag) { | |
| // If the format has an alpha channel, mask every other channel to 0 | |
| if (formatChannels & kAlpha_SkColorChannelFlag) { | |
| return Swizzle("000a"); | |
| } else { | |
| // Otherwise move the red channel to alpha | |
| SkASSERT(formatChannels & kRed_SkColorChannelFlag); | |
| return Swizzle("000r"); | |
| } | |
| } else { | |
| // otherwise leave as "rgba" and let hardware do the right thing | |
| return Swizzle::RGBA(); | |
| } | |
| } else { | |
| // First map gray to rrra; if this is just gray and not gray+alpha, it will also be forced | |
| // to opaque below and become rrr1. | |
| Swizzle swizzle; | |
| if (colorChannels & kGray_SkColorChannelFlag) { | |
| SkASSERT(formatChannels & kRed_SkColorChannelFlag); | |
| swizzle = Swizzle::RRRA(); | |
| } else { | |
| swizzle = Swizzle::RGBA(); | |
| } | |
| // Last, force the alpha to opaque if the color type masks it off but is present in the | |
| // texture format. | |
| if (!(colorChannels & kAlpha_SkColorChannelFlag) && | |
| (formatChannels & kAlpha_SkColorChannelFlag)) { | |
| swizzle = Swizzle::Concat(swizzle, Swizzle::RGB1()); | |
| } | |
| return swizzle; | |
| } | |
| } | |
| std::optional<skgpu::Swizzle> WriteSwizzleForColorType(SkColorType ct, TextureFormat format) { | |
| // D/S, compressed, external, and multiplanar formats aren't renderable with a color type. | |
| // Format support would mean we never really try to get here in practice, but keep consistent. | |
| if (format == TextureFormat::kExternal || | |
| TextureFormatIsDepthOrStencil(format) || | |
| TextureFormatIsMultiplanar(format) || | |
| TextureFormatCompressionType(format) != SkTextureCompressionType::kNone) { | |
| return std::nullopt; | |
| } | |
| uint32_t colorChannels = SkColorTypeChannelFlags(ct); | |
| uint32_t formatChannels = TextureFormatChannelMask(format); | |
| // Write swizzles only have to handle a few semantics around the sampled values, as any sort of | |
| // channel ordering for RGB vs BGR is handled by hardware. This reduces to just handling red | |
| // vs alpha. The other cases for read swizzles do not apply: | |
| // - We disallow gray since computing luminance is beyond a swizzle. | |
| // - We disallow forcing to opaque since in all cases where we'd do that we have no guarantee | |
| // of what the dst pixel's alpha was. In the future, we could support forced-opaque | |
| // rendering by always using shader-based blending or by guaranteeing a one-time initialize | |
| // draw that forced any alpha channel to 1 (b/489785214). | |
| if (SkColorTypeIsAlphaOnly(ct)) { | |
| // If the format isn't just an alpha channel (e.g. TF::kA8), we need to adjust | |
| if (formatChannels != kAlpha_SkColorChannelFlag) { | |
| // If the format has an alpha channel, mask every other channel to 0 | |
| if (formatChannels & kAlpha_SkColorChannelFlag) { | |
| return Swizzle("000a"); | |
| } else { | |
| // Otherwise move the alpha channel to red | |
| SkASSERT(formatChannels & kRed_SkColorChannelFlag); | |
| return Swizzle("a000"); | |
| } | |
| } else { | |
| // otherwise leave as "rgba" and let hardware do the right thing | |
| return Swizzle::RGBA(); | |
| } | |
| } else { | |
| if (((colorChannels & formatChannels) != formatChannels) || | |
| (colorChannels & kGray_SkColorChannelFlag)) { | |
| return std::nullopt; | |
| } | |
| return Swizzle::RGBA(); | |
| } | |
| } | |
| SkSpan<const TextureFormat> PreferredTextureFormats(SkColorType ct) { | |
| #define N(...) std::size({__VA_ARGS__}) | |
| #define CASE(C, ...) case C: { \ | |
| static const std::array<TextureFormat, N(__VA_ARGS__)> kFormats{{__VA_ARGS__}}; \ | |
| return SkSpan(kFormats); } | |
| switch (ct) { | |
| case kUnknown_SkColorType: return {}; | |
| // NOTE: Not all backends support all TextureFormats. Some of the more advanced formats | |
| // may not be supported at all and have no viable fallback. For color types that have | |
| // equivalent texture formats differing only in RGB vs. BGR swizzle, we allow both | |
| // format variations to maximize color types that have some format. For alpha-only color | |
| // types, we only match to red-channel formats as they have the broadest support. | |
| // SkColorType | TextureFormat(s)... | |
| CASE(kAlpha_8_SkColorType, TF::kR8) | |
| // NOTE: kRGB_565_SkColorType is misnamed and natively matches B5_G6_R5 | |
| CASE(kRGB_565_SkColorType, TF::kB5_G6_R5, TF::kR5_G6_B5) | |
| // NOTE: kARGB_4444_SkColorType is misnamed and natively matches ABGR4 | |
| CASE(kARGB_4444_SkColorType, TF::kABGR4, TF::kARGB4) | |
| CASE(kRGBA_8888_SkColorType, TF::kRGBA8, TF::kBGRA8) | |
| CASE(kRGB_888x_SkColorType, TF::kRGB8, TF::kRGBA8, TF::kBGRA8) | |
| CASE(kBGRA_8888_SkColorType, TF::kBGRA8, TF::kRGBA8) | |
| CASE(kRGBA_1010102_SkColorType, TF::kRGB10_A2, TF::kBGR10_A2) | |
| CASE(kBGRA_1010102_SkColorType, TF::kBGR10_A2, TF::kRGB10_A2) | |
| CASE(kRGB_101010x_SkColorType, TF::kRGB10_A2, TF::kBGR10_A2) | |
| CASE(kBGR_101010x_SkColorType, TF::kBGR10_A2, TF::kRGB10_A2) | |
| CASE(kBGR_101010x_XR_SkColorType, TF::kBGR10_XR) | |
| CASE(kBGRA_10101010_XR_SkColorType, TF::kBGRA10x6_XR) | |
| CASE(kRGBA_10x6_SkColorType, TF::kRGBA10x6) | |
| CASE(kGray_8_SkColorType, TF::kR8) | |
| CASE(kRGBA_F16Norm_SkColorType, TF::kRGBA16F) | |
| CASE(kRGBA_F16_SkColorType, TF::kRGBA16F) | |
| CASE(kRGB_F16F16F16x_SkColorType, TF::kRGBA16F) | |
| CASE(kRGBA_F32_SkColorType, TF::kRGBA32F) | |
| CASE(kR8G8_unorm_SkColorType, TF::kRG8) | |
| CASE(kA16_float_SkColorType, TF::kR16F) | |
| CASE(kR16_float_SkColorType, TF::kR16F) | |
| CASE(kR16G16_float_SkColorType, TF::kRG16F) | |
| CASE(kA16_unorm_SkColorType, TF::kR16) | |
| CASE(kR16_unorm_SkColorType, TF::kR16) | |
| CASE(kR16G16_unorm_SkColorType, TF::kRG16) | |
| CASE(kR16G16B16A16_unorm_SkColorType, TF::kRGBA16) | |
| CASE(kSRGBA_8888_SkColorType, TF::kRGBA8_sRGB, | |
| TF::kBGRA8_sRGB) | |
| CASE(kR8_unorm_SkColorType, TF::kR8) | |
| } | |
| SkUNREACHABLE; | |
| #undef CASE | |
| #undef N | |
| } | |
| std::pair<SkColorType, SkEnumBitMask<FormatXferOp>> | |
| TextureFormatColorTypeInfo(TextureFormat format) { | |
| #define CASE(TF, CT, Ops) case TF: return {CT, Ops}; | |
| using X = FormatXferOp; | |
| switch (format) { | |
| // TextureFormat | SkColorType | FormatXferOp(s) | |
| CASE(TF::kUnsupported, kUnknown_SkColorType, X::kDisabled) | |
| CASE(TF::kR8, kR8_unorm_SkColorType, X::kIdentity) | |
| CASE(TF::kR16, kR16_unorm_SkColorType, X::kIdentity) | |
| CASE(TF::kR16F, kR16_float_SkColorType, X::kIdentity) | |
| CASE(TF::kR32F, kR16_float_SkColorType, X::kDisabled) | |
| CASE(TF::kA8, kAlpha_8_SkColorType, X::kIdentity) | |
| CASE(TF::kRG8, kR8G8_unorm_SkColorType, X::kIdentity) | |
| CASE(TF::kRG16, kR16G16_unorm_SkColorType, X::kIdentity) | |
| CASE(TF::kRG16F, kR16G16_float_SkColorType, X::kIdentity) | |
| CASE(TF::kRG32F, kR16G16_float_SkColorType, X::kDisabled) | |
| CASE(TF::kRGB8, kRGB_888x_SkColorType, X::kDropAlpha) | |
| CASE(TF::kBGR8, kRGB_888x_SkColorType, X::kSwapRB | X::kDropAlpha) | |
| // NOTE: kRGB_565_SkColorType is misnamed and natively matches TextureFormat::kB5_G6_R5 | |
| CASE(TF::kB5_G6_R5, kRGB_565_SkColorType, X::kIdentity) | |
| CASE(TF::kR5_G6_B5, kRGB_565_SkColorType, X::kSwapRB) | |
| CASE(TF::kRGB16, kR16G16B16A16_unorm_SkColorType, X::kDropAlpha) | |
| CASE(TF::kRGB16F, kRGB_F16F16F16x_SkColorType, X::kDropAlpha) | |
| CASE(TF::kRGB32F, kRGBA_F32_SkColorType, X::kDropAlpha) | |
| CASE(TF::kRGB8_sRGB, kSRGBA_8888_SkColorType, X::kDropAlpha) | |
| CASE(TF::kBGR10_XR, kBGR_101010x_XR_SkColorType, X::kIdentity) | |
| CASE(TF::kRGBA8, kRGBA_8888_SkColorType, X::kIdentity) | |
| CASE(TF::kRGBA16, kR16G16B16A16_unorm_SkColorType, X::kIdentity) | |
| CASE(TF::kRGBA16F, kRGBA_F16_SkColorType, X::kIdentity) | |
| CASE(TF::kRGBA32F, kRGBA_F32_SkColorType, X::kIdentity) | |
| CASE(TF::kRGB10_A2, kRGBA_1010102_SkColorType, X::kIdentity) | |
| CASE(TF::kRGBA10x6, kRGBA_10x6_SkColorType, X::kIdentity) | |
| CASE(TF::kRGBA8_sRGB, kSRGBA_8888_SkColorType, X::kIdentity) | |
| CASE(TF::kBGRA8, kBGRA_8888_SkColorType, X::kIdentity) | |
| CASE(TF::kBGR10_A2, kBGRA_1010102_SkColorType, X::kIdentity) | |
| CASE(TF::kBGRA8_sRGB, kSRGBA_8888_SkColorType, X::kSwapRB) | |
| // NOTE: kARGB_4444_SkColorType is misnamed and natively matches TextureFormat::kABGR4 | |
| CASE(TF::kABGR4, kARGB_4444_SkColorType, X::kIdentity) | |
| CASE(TF::kARGB4, kARGB_4444_SkColorType, X::kSwapRB) | |
| CASE(TF::kBGRA10x6_XR, kBGRA_10101010_XR_SkColorType, X::kIdentity) | |
| // Compressed, multi-planar, and external formats can't exactly describe their data as | |
| // an SkColorType (although transfers with specialized data could be allowed). | |
| CASE(TF::kRGB8_ETC2, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kRGB8_ETC2_sRGB, kSRGBA_8888_SkColorType, X::kDisabled) | |
| CASE(TF::kRGB8_BC1, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kRGBA8_BC1, kRGBA_8888_SkColorType, X::kDisabled) | |
| CASE(TF::kRGBA8_BC1_sRGB, kSRGBA_8888_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV8_P2_420, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV8_P3_420, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV10x6_P2_420, kRGBA_10x6_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV8_P2_422, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV8_P3_422, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV10x6_P2_422, kRGBA_10x6_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV8_P2_444, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV8_P3_444, kRGB_888x_SkColorType, X::kDisabled) | |
| CASE(TF::kYUV10x6_P2_444, kRGBA_10x6_SkColorType, X::kDisabled) | |
| CASE(TF::kExternal, kRGBA_8888_SkColorType, X::kDisabled) | |
| // Non color texture formats can't be used with SkColorType | |
| CASE(TF::kS8, kUnknown_SkColorType, X::kDisabled) | |
| CASE(TF::kD16, kUnknown_SkColorType, X::kDisabled) | |
| CASE(TF::kD32F, kUnknown_SkColorType, X::kDisabled) | |
| CASE(TF::kD24_S8, kUnknown_SkColorType, X::kDisabled) | |
| CASE(TF::kD32F_S8, kUnknown_SkColorType, X::kDisabled) | |
| } | |
| SkUNREACHABLE; | |
| #undef CASE | |
| } | |
| bool AreColorTypeAndFormatCompatible(SkColorType targetColorType, TextureFormat format) { | |
| // If the format maps to the color type, they are compatible | |
| auto [baseColorType, _] = TextureFormatColorTypeInfo(format); | |
| if (baseColorType != kUnknown_SkColorType && baseColorType == targetColorType) { | |
| return true; // shortcut | |
| } | |
| // If the color type could map to the format, they are compatible | |
| for (TextureFormat preferred : PreferredTextureFormats(targetColorType)) { | |
| if (preferred == format) { | |
| return true; | |
| } | |
| } | |
| // Also allow kRGB_888x if kRGBA_8888 is compatible since RGBx is just a swizzle. This is almost | |
| // always handled by the combination of base color type and preferred formats, but for external | |
| // and compressed formats those two functions aren't quite descriptive enough. | |
| if (targetColorType == kRGB_888x_SkColorType && | |
| AreColorTypeAndFormatCompatible(kRGBA_8888_SkColorType, format)) { | |
| return true; | |
| } | |
| // Otherwise consider them incompatible | |
| return false; | |
| } | |
| } // namespace skgpu::graphite |