/* * Copyright 2020 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkYUVAInfo_DEFINED #define SkYUVAInfo_DEFINED #include "include/codec/SkEncodedOrigin.h" #include "include/core/SkImageInfo.h" #include "include/core/SkSize.h" #include #include /** * Specifies the structure of planes for a YUV image with optional alpha. The actual planar data * is not part of this structure and depending on usage is in external textures or pixmaps. */ class SK_API SkYUVAInfo { public: enum YUVAChannels { kY, kU, kV, kA, kLast = kA }; static constexpr int kYUVAChannelCount = static_cast(YUVAChannels::kLast + 1); struct YUVALocation; // For internal use. using YUVALocations = std::array; /** * Specifies how YUV (and optionally A) are divided among planes. Planes are separated by * underscores in the enum value names. Within each plane the pixmap/texture channels are * mapped to the YUVA channels in the order specified, e.g. for kY_UV Y is in channel 0 of plane * 0, U is in channel 0 of plane 1, and V is in channel 1 of plane 1. Channel ordering * within a pixmap/texture given the channels it contains: * A: 0:A * Luminance/Gray: 0:Gray * Luminance/Gray + Alpha: 0:Gray, 1:A * RG 0:R, 1:G * RGB 0:R, 1:G, 2:B * RGBA 0:R, 1:G, 2:B, 3:A */ enum class PlaneConfig { kUnknown, kY_U_V, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_V_U, ///< Plane 0: Y, Plane 1: V, Plane 2: U kY_UV, ///< Plane 0: Y, Plane 1: UV kY_VU, ///< Plane 0: Y, Plane 1: VU kYUV, ///< Plane 0: YUV kUYV, ///< Plane 0: UYV kY_U_V_A, ///< Plane 0: Y, Plane 1: U, Plane 2: V, Plane 3: A kY_V_U_A, ///< Plane 0: Y, Plane 1: V, Plane 2: U, Plane 3: A kY_UV_A, ///< Plane 0: Y, Plane 1: UV, Plane 2: A kY_VU_A, ///< Plane 0: Y, Plane 1: VU, Plane 2: A kYUVA, ///< Plane 0: YUVA kUYVA, ///< Plane 0: UYVA kLast = kUYVA }; /** * UV subsampling is also specified in the enum value names using J:a:b notation (e.g. 4:2:0 is * 1/2 horizontal and 1/2 vertical resolution for U and V). If alpha is present it is not sub- * sampled. Note that Subsampling values other than k444 are only valid with PlaneConfig values * that have U and V in different planes than Y (and A, if present). */ enum class Subsampling { kUnknown, k444, ///< No subsampling. UV values for each Y. k422, ///< 1 set of UV values for each 2x1 block of Y values. k420, ///< 1 set of UV values for each 2x2 block of Y values. k440, ///< 1 set of UV values for each 1x2 block of Y values. k411, ///< 1 set of UV values for each 4x1 block of Y values. k410, ///< 1 set of UV values for each 4x2 block of Y values. kLast = k410 }; /** * Describes how subsampled chroma values are sited relative to luma values. * * Currently only centered siting is supported but will expand to support additional sitings. */ enum class Siting { /** * Subsampled chroma value is sited at the center of the block of corresponding luma values. */ kCentered, }; static constexpr int kMaxPlanes = 4; /** ratio of Y/A values to U/V values in x and y. */ static std::tuple SubsamplingFactors(Subsampling); /** * SubsamplingFactors(Subsampling) if planedIdx refers to a U/V plane and otherwise {1, 1} if * inputs are valid. Invalid inputs consist of incompatible PlaneConfig/Subsampling/planeIdx * combinations. {0, 0} is returned for invalid inputs. */ static std::tuple PlaneSubsamplingFactors(PlaneConfig, Subsampling, int planeIdx); /** * Given image dimensions, a planer configuration, subsampling, and origin, determine the * expected size of each plane. Returns the number of expected planes. planeDimensions[0] * through planeDimensions[] are written. The input image dimensions are as displayed * (after the planes have been transformed to the intended display orientation). The plane * dimensions are output as the planes are stored in memory (may be rotated from image * dimensions). */ static int PlaneDimensions(SkISize imageDimensions, PlaneConfig, Subsampling, SkEncodedOrigin, SkISize planeDimensions[kMaxPlanes]); /** Number of planes for a given PlaneConfig. */ static constexpr int NumPlanes(PlaneConfig); /** * Number of Y, U, V, A channels in the ith plane for a given PlaneConfig (or 0 if i is * invalid). */ static constexpr int NumChannelsInPlane(PlaneConfig, int i); /** * Given a PlaneConfig and a set of channel flags for each plane, convert to YUVALocations * representation. Fails if channel flags aren't valid for the PlaneConfig (i.e. don't have * enough channels in a plane) by returning an invalid set of locations (plane indices are -1). */ static YUVALocations GetYUVALocations(PlaneConfig, const uint32_t* planeChannelFlags); /** Does the PlaneConfig have alpha values? */ static bool HasAlpha(PlaneConfig); SkYUVAInfo() = default; SkYUVAInfo(const SkYUVAInfo&) = default; /** * 'dimensions' should specify the size of the full resolution image (after planes have been * oriented to how the image is displayed as indicated by 'origin'). */ SkYUVAInfo(SkISize dimensions, PlaneConfig, Subsampling, SkYUVColorSpace, SkEncodedOrigin origin = kTopLeft_SkEncodedOrigin, Siting sitingX = Siting::kCentered, Siting sitingY = Siting::kCentered); SkYUVAInfo& operator=(const SkYUVAInfo& that) = default; PlaneConfig planeConfig() const { return fPlaneConfig; } Subsampling subsampling() const { return fSubsampling; } std::tuple planeSubsamplingFactors(int planeIdx) const { return PlaneSubsamplingFactors(fPlaneConfig, fSubsampling, planeIdx); } /** * Dimensions of the full resolution image (after planes have been oriented to how the image * is displayed as indicated by fOrigin). */ SkISize dimensions() const { return fDimensions; } int width() const { return fDimensions.width(); } int height() const { return fDimensions.height(); } SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; } Siting sitingX() const { return fSitingX; } Siting sitingY() const { return fSitingY; } SkEncodedOrigin origin() const { return fOrigin; } SkMatrix originMatrix() const { return SkEncodedOriginToMatrix(fOrigin, this->width(), this->height()); } bool hasAlpha() const { return HasAlpha(fPlaneConfig); } /** * Returns the number of planes and initializes planeDimensions[0]..planeDimensions[] to * the expected dimensions for each plane. Dimensions are as stored in memory, before * transformation to image display space as indicated by origin(). */ int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const { return PlaneDimensions(fDimensions, fPlaneConfig, fSubsampling, fOrigin, planeDimensions); } /** * Given a per-plane row bytes, determine size to allocate for all planes. Optionally retrieves * the per-plane byte sizes in planeSizes if not null. If total size overflows will return * SIZE_MAX and set all planeSizes to SIZE_MAX. */ size_t computeTotalBytes(const size_t rowBytes[kMaxPlanes], size_t planeSizes[kMaxPlanes] = nullptr) const; int numPlanes() const { return NumPlanes(fPlaneConfig); } int numChannelsInPlane(int i) const { return NumChannelsInPlane(fPlaneConfig, i); } /** * Given a set of channel flags for each plane, converts this->planeConfig() to YUVALocations * representation. Fails if the channel flags aren't valid for the PlaneConfig (i.e. don't have * enough channels in a plane) by returning default initialized locations (all plane indices are * -1). */ YUVALocations toYUVALocations(const uint32_t* channelFlags) const; /** * Makes a SkYUVAInfo that is identical to this one but with the passed Subsampling. If the * passed Subsampling is not k444 and this info's PlaneConfig is not compatible with chroma * subsampling (because Y is in the same plane as UV) then the result will be an invalid * SkYUVAInfo. */ SkYUVAInfo makeSubsampling(SkYUVAInfo::Subsampling) const; /** * Makes a SkYUVAInfo that is identical to this one but with the passed dimensions. If the * passed dimensions is empty then the result will be an invalid SkYUVAInfo. */ SkYUVAInfo makeDimensions(SkISize) const; bool operator==(const SkYUVAInfo& that) const; bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); } bool isValid() const { return fPlaneConfig != PlaneConfig::kUnknown; } private: SkISize fDimensions = {0, 0}; PlaneConfig fPlaneConfig = PlaneConfig::kUnknown; Subsampling fSubsampling = Subsampling::kUnknown; SkYUVColorSpace fYUVColorSpace = SkYUVColorSpace::kIdentity_SkYUVColorSpace; /** * YUVA data often comes from formats like JPEG that support EXIF orientation. * Code that operates on the raw YUV data often needs to know that orientation. */ SkEncodedOrigin fOrigin = kTopLeft_SkEncodedOrigin; Siting fSitingX = Siting::kCentered; Siting fSitingY = Siting::kCentered; }; constexpr int SkYUVAInfo::NumPlanes(PlaneConfig planeConfig) { switch (planeConfig) { case PlaneConfig::kUnknown: return 0; case PlaneConfig::kY_U_V: return 3; case PlaneConfig::kY_V_U: return 3; case PlaneConfig::kY_UV: return 2; case PlaneConfig::kY_VU: return 2; case PlaneConfig::kYUV: return 1; case PlaneConfig::kUYV: return 1; case PlaneConfig::kY_U_V_A: return 4; case PlaneConfig::kY_V_U_A: return 4; case PlaneConfig::kY_UV_A: return 3; case PlaneConfig::kY_VU_A: return 3; case PlaneConfig::kYUVA: return 1; case PlaneConfig::kUYVA: return 1; } SkUNREACHABLE; } constexpr int SkYUVAInfo::NumChannelsInPlane(PlaneConfig config, int i) { switch (config) { case PlaneConfig::kUnknown: return 0; case SkYUVAInfo::PlaneConfig::kY_U_V: case SkYUVAInfo::PlaneConfig::kY_V_U: return i >= 0 && i < 3 ? 1 : 0; case SkYUVAInfo::PlaneConfig::kY_UV: case SkYUVAInfo::PlaneConfig::kY_VU: switch (i) { case 0: return 1; case 1: return 2; default: return 0; } case SkYUVAInfo::PlaneConfig::kYUV: case SkYUVAInfo::PlaneConfig::kUYV: return i == 0 ? 3 : 0; case SkYUVAInfo::PlaneConfig::kY_U_V_A: case SkYUVAInfo::PlaneConfig::kY_V_U_A: return i >= 0 && i < 4 ? 1 : 0; case SkYUVAInfo::PlaneConfig::kY_UV_A: case SkYUVAInfo::PlaneConfig::kY_VU_A: switch (i) { case 0: return 1; case 1: return 2; case 2: return 1; default: return 0; } case SkYUVAInfo::PlaneConfig::kYUVA: case SkYUVAInfo::PlaneConfig::kUYVA: return i == 0 ? 4 : 0; } return 0; } #endif