1/* 2 * Copyright 2020 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/core/SkYUVAInfo.h" 9#include "src/core/SkSafeMath.h" 10#include "src/core/SkYUVAInfoLocation.h" 11 12#include <algorithm> 13 14static bool is_plane_config_compatible_with_subsampling(SkYUVAInfo::PlaneConfig config, 15 SkYUVAInfo::Subsampling subsampling) { 16 if (config == SkYUVAInfo::PlaneConfig::kUnknown || 17 subsampling == SkYUVAInfo::Subsampling::kUnknown) { 18 return false; 19 } 20 return subsampling == SkYUVAInfo::Subsampling::k444 || 21 (config != SkYUVAInfo::PlaneConfig::kYUV && 22 config != SkYUVAInfo::PlaneConfig::kYUVA && 23 config != SkYUVAInfo::PlaneConfig::kUYV && 24 config != SkYUVAInfo::PlaneConfig::kUYVA); 25} 26 27std::tuple<int, int> SkYUVAInfo::SubsamplingFactors(Subsampling subsampling) { 28 switch (subsampling) { 29 case Subsampling::kUnknown: return {0, 0}; 30 case Subsampling::k444: return {1, 1}; 31 case Subsampling::k422: return {2, 1}; 32 case Subsampling::k420: return {2, 2}; 33 case Subsampling::k440: return {1, 2}; 34 case Subsampling::k411: return {4, 1}; 35 case Subsampling::k410: return {4, 2}; 36 } 37 SkUNREACHABLE; 38} 39 40std::tuple<int, int> SkYUVAInfo::PlaneSubsamplingFactors(PlaneConfig planeConfig, 41 Subsampling subsampling, 42 int planeIdx) { 43 if (!is_plane_config_compatible_with_subsampling(planeConfig, subsampling) || 44 planeIdx < 0 || 45 planeIdx > NumPlanes(planeConfig)) { 46 return {0, 0}; 47 } 48 bool isSubsampledPlane = false; 49 switch (planeConfig) { 50 case PlaneConfig::kUnknown: SkUNREACHABLE; 51 52 case PlaneConfig::kY_U_V: 53 case PlaneConfig::kY_V_U: 54 case PlaneConfig::kY_U_V_A: 55 case PlaneConfig::kY_V_U_A: 56 isSubsampledPlane = planeIdx == 1 || planeIdx == 2; 57 break; 58 59 case PlaneConfig::kY_UV: 60 case PlaneConfig::kY_VU: 61 case PlaneConfig::kY_UV_A: 62 case PlaneConfig::kY_VU_A: 63 isSubsampledPlane = planeIdx == 1; 64 break; 65 66 case PlaneConfig::kYUV: 67 case PlaneConfig::kUYV: 68 case PlaneConfig::kYUVA: 69 case PlaneConfig::kUYVA: 70 break; 71 } 72 return isSubsampledPlane ? SubsamplingFactors(subsampling) : std::make_tuple(1, 1); 73} 74 75int SkYUVAInfo::PlaneDimensions(SkISize imageDimensions, 76 PlaneConfig planeConfig, 77 Subsampling subsampling, 78 SkEncodedOrigin origin, 79 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes]) { 80 std::fill_n(planeDimensions, SkYUVAInfo::kMaxPlanes, SkISize{0, 0}); 81 if (!is_plane_config_compatible_with_subsampling(planeConfig, subsampling)) { 82 return 0; 83 } 84 85 int w = imageDimensions.width(); 86 int h = imageDimensions.height(); 87 if (origin >= kLeftTop_SkEncodedOrigin) { 88 using std::swap; 89 swap(w, h); 90 } 91 auto down2 = [](int x) { return (x + 1)/2; }; 92 auto down4 = [](int x) { return (x + 3)/4; }; 93 SkISize uvSize; 94 switch (subsampling) { 95 case Subsampling::kUnknown: SkUNREACHABLE; 96 97 case Subsampling::k444: uvSize = { w , h }; break; 98 case Subsampling::k422: uvSize = {down2(w), h }; break; 99 case Subsampling::k420: uvSize = {down2(w), down2(h)}; break; 100 case Subsampling::k440: uvSize = { w , down2(h)}; break; 101 case Subsampling::k411: uvSize = {down4(w), h }; break; 102 case Subsampling::k410: uvSize = {down4(w), down2(h)}; break; 103 } 104 switch (planeConfig) { 105 case PlaneConfig::kUnknown: SkUNREACHABLE; 106 107 case PlaneConfig::kY_U_V: 108 case PlaneConfig::kY_V_U: 109 planeDimensions[0] = {w, h}; 110 planeDimensions[1] = planeDimensions[2] = uvSize; 111 return 3; 112 113 case PlaneConfig::kY_UV: 114 case PlaneConfig::kY_VU: 115 planeDimensions[0] = {w, h}; 116 planeDimensions[1] = uvSize; 117 return 2; 118 119 case PlaneConfig::kY_U_V_A: 120 case PlaneConfig::kY_V_U_A: 121 planeDimensions[0] = planeDimensions[3] = {w, h}; 122 planeDimensions[1] = planeDimensions[2] = uvSize; 123 return 4; 124 125 case PlaneConfig::kY_UV_A: 126 case PlaneConfig::kY_VU_A: 127 planeDimensions[0] = planeDimensions[2] = {w, h}; 128 planeDimensions[1] = uvSize; 129 return 3; 130 131 case PlaneConfig::kYUV: 132 case PlaneConfig::kUYV: 133 case PlaneConfig::kYUVA: 134 case PlaneConfig::kUYVA: 135 planeDimensions[0] = {w, h}; 136 SkASSERT(planeDimensions[0] == uvSize); 137 return 1; 138 } 139 SkUNREACHABLE; 140} 141 142static bool channel_index_to_channel(uint32_t channelFlags, 143 int channelIdx, 144 SkColorChannel* channel) { 145 switch (channelFlags) { 146 case kGray_SkColorChannelFlag: // For gray returning any of R, G, or B for index 0 is ok. 147 case kRed_SkColorChannelFlag: 148 if (channelIdx == 0) { 149 *channel = SkColorChannel::kR; 150 return true; 151 } 152 return false; 153 case kGrayAlpha_SkColorChannelFlags: 154 switch (channelIdx) { 155 case 0: *channel = SkColorChannel::kR; return true; 156 case 1: *channel = SkColorChannel::kA; return true; 157 158 default: return false; 159 } 160 case kAlpha_SkColorChannelFlag: 161 if (channelIdx == 0) { 162 *channel = SkColorChannel::kA; 163 return true; 164 } 165 return false; 166 case kRG_SkColorChannelFlags: 167 if (channelIdx == 0 || channelIdx == 1) { 168 *channel = static_cast<SkColorChannel>(channelIdx); 169 return true; 170 } 171 return false; 172 case kRGB_SkColorChannelFlags: 173 if (channelIdx >= 0 && channelIdx <= 2) { 174 *channel = static_cast<SkColorChannel>(channelIdx); 175 return true; 176 } 177 return false; 178 case kRGBA_SkColorChannelFlags: 179 if (channelIdx >= 0 && channelIdx <= 3) { 180 *channel = static_cast<SkColorChannel>(channelIdx); 181 return true; 182 } 183 return false; 184 default: 185 return false; 186 } 187} 188 189SkYUVAInfo::YUVALocations SkYUVAInfo::GetYUVALocations(PlaneConfig config, 190 const uint32_t* planeChannelFlags) { 191 // Like YUVALocation but chanIdx refers to channels by index rather than absolute channel, e.g. 192 // A is the 0th channel of an alpha-only texture. We'll use this plus planeChannelFlags to get 193 // the actual channel. 194 struct PlaneAndIndex {int plane, chanIdx;}; 195 const PlaneAndIndex* planesAndIndices = nullptr; 196 switch (config) { 197 case PlaneConfig::kUnknown: 198 return {}; 199 200 case PlaneConfig::kY_U_V: { 201 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {2, 0}, {-1, -1}}; 202 planesAndIndices = kPlanesAndIndices; 203 break; 204 } 205 case PlaneConfig::kY_V_U: { 206 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {2, 0}, {1, 0}, {-1, -1}}; 207 planesAndIndices = kPlanesAndIndices; 208 break; 209 } 210 case PlaneConfig::kY_UV: { 211 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {1, 1}, {-1, -1}}; 212 planesAndIndices = kPlanesAndIndices; 213 break; 214 } 215 case PlaneConfig::kY_VU: { 216 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 1}, {1, 0}, {-1, -1}}; 217 planesAndIndices = kPlanesAndIndices; 218 break; 219 } 220 case PlaneConfig::kYUV: { 221 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {0, 1}, {0, 2}, {-1, -1}}; 222 planesAndIndices = kPlanesAndIndices; 223 break; 224 } 225 case PlaneConfig::kUYV: { 226 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 1}, {0, 0}, {0, 2}, {-1, -1}}; 227 planesAndIndices = kPlanesAndIndices; 228 break; 229 } 230 case PlaneConfig::kY_U_V_A: { 231 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}}; 232 planesAndIndices = kPlanesAndIndices; 233 break; 234 } 235 case PlaneConfig::kY_V_U_A: { 236 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {2, 0}, {1, 0}, {3, 0}}; 237 planesAndIndices = kPlanesAndIndices; 238 break; 239 } 240 case PlaneConfig::kY_UV_A: { 241 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; 242 planesAndIndices = kPlanesAndIndices; 243 break; 244 } 245 case PlaneConfig::kY_VU_A: { 246 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 1}, {1, 0}, {2, 0}}; 247 planesAndIndices = kPlanesAndIndices; 248 break; 249 } 250 case PlaneConfig::kYUVA: { 251 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}}; 252 planesAndIndices = kPlanesAndIndices; 253 break; 254 } 255 case PlaneConfig::kUYVA: { 256 static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 1}, {0, 0}, {0, 2}, {0, 3}}; 257 planesAndIndices = kPlanesAndIndices; 258 break; 259 } 260 } 261 SkASSERT(planesAndIndices); 262 YUVALocations yuvaLocations; 263 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) { 264 auto [plane, chanIdx] = planesAndIndices[i]; 265 SkColorChannel channel; 266 if (plane >= 0) { 267 if (!channel_index_to_channel(planeChannelFlags[plane], chanIdx, &channel)) { 268 return {}; 269 } 270 yuvaLocations[i] = {plane, channel}; 271 } else { 272 SkASSERT(i == 3); 273 yuvaLocations[i] = {-1, SkColorChannel::kR}; 274 } 275 } 276 return yuvaLocations; 277} 278 279bool SkYUVAInfo::HasAlpha(PlaneConfig planeConfig) { 280 switch (planeConfig) { 281 case PlaneConfig::kUnknown: return false; 282 283 case PlaneConfig::kY_U_V: return false; 284 case PlaneConfig::kY_V_U: return false; 285 case PlaneConfig::kY_UV: return false; 286 case PlaneConfig::kY_VU: return false; 287 case PlaneConfig::kYUV: return false; 288 case PlaneConfig::kUYV: return false; 289 290 case PlaneConfig::kY_U_V_A: return true; 291 case PlaneConfig::kY_V_U_A: return true; 292 case PlaneConfig::kY_UV_A: return true; 293 case PlaneConfig::kY_VU_A: return true; 294 case PlaneConfig::kYUVA: return true; 295 case PlaneConfig::kUYVA: return true; 296 } 297 SkUNREACHABLE; 298} 299 300SkYUVAInfo::SkYUVAInfo(SkISize dimensions, 301 PlaneConfig planeConfig, 302 Subsampling subsampling, 303 SkYUVColorSpace yuvColorSpace, 304 SkEncodedOrigin origin, 305 Siting sitingX, 306 Siting sitingY) 307 : fDimensions(dimensions) 308 , fPlaneConfig(planeConfig) 309 , fSubsampling(subsampling) 310 , fYUVColorSpace(yuvColorSpace) 311 , fOrigin(origin) 312 , fSitingX(sitingX) 313 , fSitingY(sitingY) { 314 if (fDimensions.isEmpty() || 315 !is_plane_config_compatible_with_subsampling(planeConfig, subsampling)) { 316 *this = {}; 317 SkASSERT(!this->isValid()); 318 return; 319 } 320 SkASSERT(this->isValid()); 321} 322 323size_t SkYUVAInfo::computeTotalBytes(const size_t rowBytes[kMaxPlanes], 324 size_t planeSizes[kMaxPlanes]) const { 325 if (!this->isValid()) { 326 return 0; 327 } 328 SkSafeMath safe; 329 size_t totalBytes = 0; 330 SkISize planeDimensions[kMaxPlanes]; 331 int n = this->planeDimensions(planeDimensions); 332 for (int i = 0; i < n; ++i) { 333 SkASSERT(!planeDimensions[i].isEmpty()); 334 SkASSERT(rowBytes[i]); 335 size_t size = safe.mul(rowBytes[i], planeDimensions[i].height()); 336 if (planeSizes) { 337 planeSizes[i] = size; 338 } 339 totalBytes = safe.add(totalBytes, size); 340 } 341 if (planeSizes) { 342 if (safe.ok()) { 343 for (int i = n; i < kMaxPlanes; ++i) { 344 planeSizes[i] = 0; 345 } 346 } else { 347 for (int i = 0; n < kMaxPlanes; ++i) { 348 planeSizes[i] = SIZE_MAX; 349 } 350 } 351 } 352 353 return safe.ok() ? totalBytes : SIZE_MAX; 354} 355 356SkYUVAInfo::YUVALocations SkYUVAInfo::toYUVALocations(const uint32_t* channelFlags) const { 357 return GetYUVALocations(fPlaneConfig, channelFlags); 358} 359 360SkYUVAInfo SkYUVAInfo::makeSubsampling(SkYUVAInfo::Subsampling subsampling) const { 361 return {fDimensions, fPlaneConfig, subsampling, fYUVColorSpace, fOrigin, fSitingX, fSitingY}; 362} 363 364SkYUVAInfo SkYUVAInfo::makeDimensions(SkISize dimensions) const { 365 return {dimensions, fPlaneConfig, fSubsampling, fYUVColorSpace, fOrigin, fSitingX, fSitingY}; 366} 367 368bool SkYUVAInfo::operator==(const SkYUVAInfo& that) const { 369 return fPlaneConfig == that.fPlaneConfig && 370 fSubsampling == that.fSubsampling && 371 fYUVColorSpace == that.fYUVColorSpace && 372 fDimensions == that.fDimensions && 373 fSitingX == that.fSitingX && 374 fSitingY == that.fSitingY && 375 fOrigin == that.fOrigin; 376} 377