/* * 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/core/SkYUVAIndex.h" /** * 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: /** * 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 * RG 0:R, 1:G * RGB 0:R, 1:G, 2:B * RGBA 0:R, 1:G, 2:B, 3:A * * 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). A fourth number is added if alpha * is present (always 4 as only full resolution alpha is supported). * * Currently this only has three-plane formats but more will be added as usage and testing of * this expands. */ enum class PlanarConfig { kUnknown, kY_U_V_444, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_U_V_422, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_U_V_420, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_V_U_420, ///< Plane 0: Y, Plane 1: V, Plane 2: U kY_U_V_440, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_U_V_411, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_U_V_410, ///< Plane 0: Y, Plane 1: U, Plane 2: V kY_U_V_A_4204, ///< Plane 0: Y, Plane 1: U, Plane 2: V, Plane 3: A kY_V_U_A_4204, ///< Plane 0: Y, Plane 1: V, Plane 2: U, Plane 3: A kY_UV_420, ///< Plane 0: Y, Plane 1: UV kY_VU_420, ///< Plane 0: Y, Plane 1: VU kY_UV_A_4204, ///< Plane 0: Y, Plane 1: UV, Plane 2: A kY_VU_A_4204, ///< Plane 0: Y, Plane 1: VU, Plane 2: A kYUV_444, ///< Plane 0: YUV kUYV_444, ///< Plane 0: UYV kYUVA_4444, ///< Plane 0: YUVA kUYVA_4444, ///< Plane 0: UYVA }; /** * 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; /** * Given image dimensions, a planar configuration, and origin, determine the expected size of * each plane. Returns the number of expected planes. planeDimensions[0] through * planeDimensons[] 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 stored in memory. */ static int PlaneDimensions(SkISize imageDimensions, PlanarConfig, SkEncodedOrigin, SkISize planeDimensions[kMaxPlanes]); /** Number of planes for a given PlanarConfig. */ static constexpr int NumPlanes(PlanarConfig); /** * Number of Y, U, V, A channels in the ith plane for a given PlanarConfig (or 0 if i is * invalid). */ static constexpr int NumChannelsInPlane(PlanarConfig, int i); /** * Given a PlanarConfig and a set of channel flags for each plane, convert to SkYUVAIndex * representation. Fails if channel flags aren't valid for the PlanarConfig (i.e. don't have * enough channels in a plane). */ static bool GetYUVAIndices(PlanarConfig, const uint32_t planeChannelFlags[kMaxPlanes], SkYUVAIndex indices[SkYUVAIndex::kIndexCount]); /** Does the PlanarConfig have alpha values? */ static bool HasAlpha(PlanarConfig); 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, PlanarConfig, SkYUVColorSpace, SkEncodedOrigin origin = kTopLeft_SkEncodedOrigin, Siting sitingX = Siting::kCentered, Siting sitingY = Siting::kCentered); SkYUVAInfo& operator=(const SkYUVAInfo& that) = default; PlanarConfig planarConfig() const { return fPlanarConfig; } /** * 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; } bool hasAlpha() const { return HasAlpha(fPlanarConfig); } /** * 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, fPlanarConfig, 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(fPlanarConfig); } int numChannelsInPlane(int i) const { return NumChannelsInPlane(fPlanarConfig, i); } /** * Given a set of channel flags for each plane, converts this->planarConfig() to SkYUVAIndex * representation. Fails if the channel flags aren't valid for the PlanarConfig (i.e. don't have * enough channels in a plane). */ bool toYUVAIndices(const uint32_t channelFlags[4], SkYUVAIndex indices[4]) const { return GetYUVAIndices(fPlanarConfig, channelFlags, indices); } bool operator==(const SkYUVAInfo& that) const; bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); } bool isValid() const { return fPlanarConfig != PlanarConfig::kUnknown; } private: SkISize fDimensions = {0, 0}; PlanarConfig fPlanarConfig = PlanarConfig::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(PlanarConfig planarConfig) { switch (planarConfig) { case PlanarConfig::kUnknown: return 0; case PlanarConfig::kY_U_V_444: return 3; case PlanarConfig::kY_U_V_422: return 3; case PlanarConfig::kY_U_V_420: return 3; case PlanarConfig::kY_V_U_420: return 3; case PlanarConfig::kY_U_V_440: return 3; case PlanarConfig::kY_U_V_411: return 3; case PlanarConfig::kY_U_V_410: return 3; case PlanarConfig::kY_U_V_A_4204: return 4; case PlanarConfig::kY_V_U_A_4204: return 4; case PlanarConfig::kY_UV_420: return 2; case PlanarConfig::kY_VU_420: return 2; case PlanarConfig::kY_UV_A_4204: return 3; case PlanarConfig::kY_VU_A_4204: return 3; case PlanarConfig::kYUV_444: return 1; case PlanarConfig::kUYV_444: return 1; case PlanarConfig::kYUVA_4444: return 1; case PlanarConfig::kUYVA_4444: return 1; } SkUNREACHABLE; } constexpr int SkYUVAInfo::NumChannelsInPlane(PlanarConfig config, int i) { switch (config) { case PlanarConfig::kUnknown: return 0; case SkYUVAInfo::PlanarConfig::kY_U_V_444: case SkYUVAInfo::PlanarConfig::kY_U_V_422: case SkYUVAInfo::PlanarConfig::kY_U_V_420: case SkYUVAInfo::PlanarConfig::kY_V_U_420: case SkYUVAInfo::PlanarConfig::kY_U_V_440: case SkYUVAInfo::PlanarConfig::kY_U_V_411: case SkYUVAInfo::PlanarConfig::kY_U_V_410: return i >= 0 && i < 3 ? 1 : 0; case SkYUVAInfo::PlanarConfig::kY_U_V_A_4204: case SkYUVAInfo::PlanarConfig::kY_V_U_A_4204: return i >= 0 && i < 4 ? 1 : 0; case SkYUVAInfo::PlanarConfig::kY_UV_420: case SkYUVAInfo::PlanarConfig::kY_VU_420: switch (i) { case 0: return 1; case 1: return 2; default: return 0; } case SkYUVAInfo::PlanarConfig::kY_UV_A_4204: case SkYUVAInfo::PlanarConfig::kY_VU_A_4204: switch (i) { case 0: return 1; case 1: return 2; case 2: return 1; default: return 0; } case SkYUVAInfo::PlanarConfig::kYUV_444: case SkYUVAInfo::PlanarConfig::kUYV_444: return i == 0 ? 3 : 0; case SkYUVAInfo::PlanarConfig::kYUVA_4444: case SkYUVAInfo::PlanarConfig::kUYVA_4444: return i == 0 ? 4 : 0; } return 0; } #endif