yuv.glsl (8208B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include shared 6 7 #define YUV_FORMAT_NV12 0 8 #define YUV_FORMAT_P010 1 9 #define YUV_FORMAT_NV16 2 10 #define YUV_FORMAT_PLANAR 3 11 #define YUV_FORMAT_INTERLEAVED 4 12 13 //#define YUV_PRECISION mediump 14 #define YUV_PRECISION highp 15 16 #ifdef WR_VERTEX_SHADER 17 18 #ifdef WR_FEATURE_TEXTURE_RECT 19 #define TEX_SIZE_YUV(sampler) vec2(1.0) 20 #else 21 #define TEX_SIZE_YUV(sampler) vec2(TEX_SIZE(sampler).xy) 22 #endif 23 24 // `YuvRangedColorSpace` 25 #define YUV_COLOR_SPACE_REC601_NARROW 0 26 #define YUV_COLOR_SPACE_REC601_FULL 1 27 #define YUV_COLOR_SPACE_REC709_NARROW 2 28 #define YUV_COLOR_SPACE_REC709_FULL 3 29 #define YUV_COLOR_SPACE_REC2020_NARROW 4 30 #define YUV_COLOR_SPACE_REC2020_FULL 5 31 #define YUV_COLOR_SPACE_GBR_IDENTITY 6 32 33 // The constants added to the Y, U and V components are applied in the fragment shader. 34 35 // `rgbFromYuv` from https://jdashg.github.io/misc/colors/from-coeffs.html 36 // The matrix is stored in column-major. 37 const mat3 RgbFromYuv_Rec601 = mat3( 38 1.00000, 1.00000, 1.00000, 39 0.00000,-0.17207, 0.88600, 40 0.70100,-0.35707, 0.00000 41 ); 42 const mat3 RgbFromYuv_Rec709 = mat3( 43 1.00000, 1.00000, 1.00000, 44 0.00000,-0.09366, 0.92780, 45 0.78740,-0.23406, 0.00000 46 ); 47 const mat3 RgbFromYuv_Rec2020 = mat3( 48 1.00000, 1.00000, 1.00000, 49 0.00000,-0.08228, 0.94070, 50 0.73730,-0.28568, 0.00000 51 ); 52 53 // The matrix is stored in column-major. 54 // Identity is stored as GBR 55 const mat3 RgbFromYuv_GbrIdentity = mat3( 56 0.0 , 1.0, 0.0, 57 0.0 , 0.0, 1.0, 58 1.0 , 0.0, 0.0 59 ); 60 61 // - 62 63 struct YuvPrimitive { 64 int channel_bit_depth; 65 int color_space; 66 int yuv_format; 67 }; 68 69 struct YuvColorSamplingInfo { 70 mat3 rgb_from_yuv; 71 vec4 packed_zero_one_vals; 72 }; 73 74 struct YuvColorMatrixInfo { 75 vec3 ycbcr_bias; 76 mat3 rgb_from_debiased_ycbrc; 77 }; 78 79 // - 80 81 vec4 yuv_channel_zero_one_identity(int bit_depth, float channel_max) { 82 float all_ones_normalized = float((1 << bit_depth) - 1) / channel_max; 83 return vec4(0.0, 0.0, all_ones_normalized, all_ones_normalized); 84 } 85 86 vec4 yuv_channel_zero_one_narrow_range(int bit_depth, float channel_max) { 87 // Note: 512/1023 != 128/255 88 ivec4 zero_one_ints = ivec4(16, 128, 235, 240) << (bit_depth - 8); 89 return vec4(zero_one_ints) / channel_max; 90 } 91 92 vec4 yuv_channel_zero_one_full_range(int bit_depth, float channel_max) { 93 vec4 narrow = yuv_channel_zero_one_narrow_range(bit_depth, channel_max); 94 vec4 identity = yuv_channel_zero_one_identity(bit_depth, channel_max); 95 return vec4(0.0, narrow.y, identity.z, identity.w); 96 } 97 98 YuvColorSamplingInfo get_yuv_color_info(YuvPrimitive prim) { 99 float channel_max = 255.0; 100 if (prim.channel_bit_depth > 8) { 101 if (prim.yuv_format == YUV_FORMAT_P010) { 102 // This is an MSB format. 103 channel_max = float((1 << prim.channel_bit_depth) - 1); 104 } else { 105 // For >8bpc, we get the low bits, not the high bits: 106 // 10bpc(1.0): 0b0000_0011_1111_1111 107 channel_max = 65535.0; 108 } 109 } 110 if (prim.color_space == YUV_COLOR_SPACE_REC601_NARROW) { 111 return YuvColorSamplingInfo(RgbFromYuv_Rec601, 112 yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max)); 113 } else if (prim.color_space == YUV_COLOR_SPACE_REC601_FULL) { 114 return YuvColorSamplingInfo(RgbFromYuv_Rec601, 115 yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max)); 116 117 } else if (prim.color_space == YUV_COLOR_SPACE_REC709_NARROW) { 118 return YuvColorSamplingInfo(RgbFromYuv_Rec709, 119 yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max)); 120 } else if (prim.color_space == YUV_COLOR_SPACE_REC709_FULL) { 121 return YuvColorSamplingInfo(RgbFromYuv_Rec709, 122 yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max)); 123 124 } else if (prim.color_space == YUV_COLOR_SPACE_REC2020_NARROW) { 125 return YuvColorSamplingInfo(RgbFromYuv_Rec2020, 126 yuv_channel_zero_one_narrow_range(prim.channel_bit_depth, channel_max)); 127 } else if (prim.color_space == YUV_COLOR_SPACE_REC2020_FULL) { 128 return YuvColorSamplingInfo(RgbFromYuv_Rec2020, 129 yuv_channel_zero_one_full_range(prim.channel_bit_depth, channel_max)); 130 131 } else { 132 // Identity 133 return YuvColorSamplingInfo(RgbFromYuv_GbrIdentity, 134 yuv_channel_zero_one_identity(prim.channel_bit_depth, channel_max)); 135 } 136 } 137 138 YuvColorMatrixInfo get_rgb_from_ycbcr_info(YuvPrimitive prim) { 139 YuvColorSamplingInfo info = get_yuv_color_info(prim); 140 141 vec2 zero = info.packed_zero_one_vals.xy; 142 vec2 one = info.packed_zero_one_vals.zw; 143 // Such that yuv_value = (ycbcr_sample - zero) / (one - zero) 144 vec2 scale = 1.0 / (one - zero); 145 146 YuvColorMatrixInfo mat_info; 147 mat_info.ycbcr_bias = zero.xyy; 148 mat3 yuv_from_debiased_ycbcr = mat3(scale.x, 0.0, 0.0, 149 0.0, scale.y, 0.0, 150 0.0, 0.0, scale.y); 151 mat_info.rgb_from_debiased_ycbrc = info.rgb_from_yuv * yuv_from_debiased_ycbcr; 152 return mat_info; 153 } 154 155 void write_uv_rect( 156 vec2 uv0, 157 vec2 uv1, 158 vec2 f, 159 vec2 texture_size, 160 out vec2 uv, 161 out vec4 uv_bounds 162 ) { 163 uv = mix(uv0, uv1, f); 164 165 uv_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)); 166 167 #ifndef WR_FEATURE_TEXTURE_RECT 168 uv /= texture_size; 169 uv_bounds /= texture_size.xyxy; 170 #endif 171 } 172 #endif 173 174 #ifdef WR_FRAGMENT_SHADER 175 176 vec4 sample_yuv( 177 int format, 178 YUV_PRECISION vec3 ycbcr_bias, 179 YUV_PRECISION mat3 rgb_from_debiased_ycbrc, 180 vec2 in_uv_y, 181 vec2 in_uv_u, 182 vec2 in_uv_v, 183 vec4 uv_bounds_y, 184 vec4 uv_bounds_u, 185 vec4 uv_bounds_v 186 ) { 187 YUV_PRECISION vec3 ycbcr_sample; 188 189 switch (format) { 190 case YUV_FORMAT_PLANAR: 191 { 192 // The yuv_planar format should have this third texture coordinate. 193 vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw); 194 vec2 uv_u = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw); 195 vec2 uv_v = clamp(in_uv_v, uv_bounds_v.xy, uv_bounds_v.zw); 196 ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r; 197 ycbcr_sample.y = TEX_SAMPLE(sColor1, uv_u).r; 198 ycbcr_sample.z = TEX_SAMPLE(sColor2, uv_v).r; 199 } 200 break; 201 202 case YUV_FORMAT_NV12: 203 case YUV_FORMAT_P010: 204 case YUV_FORMAT_NV16: 205 { 206 vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw); 207 vec2 uv_uv = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw); 208 ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r; 209 ycbcr_sample.yz = TEX_SAMPLE(sColor1, uv_uv).rg; 210 } 211 break; 212 213 case YUV_FORMAT_INTERLEAVED: 214 { 215 // "The Y, Cb and Cr color channels within the 422 data are mapped into 216 // the existing green, blue and red color channels." 217 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt 218 vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw); 219 ycbcr_sample = TEX_SAMPLE(sColor0, uv_y).gbr; 220 } 221 break; 222 223 default: 224 ycbcr_sample = vec3(0.0); 225 break; 226 } 227 //if (true) return vec4(ycbcr_sample, 1.0); 228 229 // See the YuvColorMatrix definition for an explanation of where the constants come from. 230 YUV_PRECISION vec3 rgb = rgb_from_debiased_ycbrc * (ycbcr_sample - ycbcr_bias); 231 232 #if defined(WR_FEATURE_ALPHA_PASS) && defined(SWGL_CLIP_MASK) 233 // Avoid out-of-range RGB values that can mess with blending. These occur due to invalid 234 // YUV values outside the mappable space that never the less can be generated. 235 rgb = clamp(rgb, 0.0, 1.0); 236 #endif 237 return vec4(rgb, 1.0); 238 } 239 #endif