/* * 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 SkYUVAPixmaps_DEFINED #define SkYUVAPixmaps_DEFINED #include "include/core/SkData.h" #include "include/core/SkImageInfo.h" #include "include/core/SkPixmap.h" #include "include/core/SkYUVAInfo.h" #include "include/private/SkTo.h" #include #include class GrImageContext; struct SkYUVASizeInfo; struct SkYUVAIndex; /** * SkYUVAInfo combined with per-plane SkColorTypes and row bytes. Fully specifies the SkPixmaps * for a YUVA image without the actual pixel memory and data. */ class SK_API SkYUVAPixmapInfo { public: static constexpr auto kMaxPlanes = SkYUVAInfo::kMaxPlanes; using PlaneConfig = SkYUVAInfo::PlaneConfig; using Subsampling = SkYUVAInfo::Subsampling; /** * Data type for Y, U, V, and possibly A channels independent of how values are packed into * planes. **/ enum class DataType { kUnorm8, ///< 8 bit unsigned normalized kUnorm16, ///< 16 bit unsigned normalized kFloat16, ///< 16 bit (half) floating point kUnorm10_Unorm2, ///< 10 bit unorm for Y, U, and V. 2 bit unorm for alpha (if present). kLast = kUnorm10_Unorm2 }; static constexpr int kDataTypeCnt = static_cast(DataType::kLast) + 1; class SK_API SupportedDataTypes { public: /** Defaults to nothing supported. */ constexpr SupportedDataTypes() = default; /** Init based on texture formats supported by the context. */ SupportedDataTypes(const GrImageContext&); /** All legal combinations of PlaneConfig and DataType are supported. */ static constexpr SupportedDataTypes All(); /** * Checks whether there is a supported combination of color types for planes structured * as indicated by PlaneConfig with channel data types as indicated by DataType. */ constexpr bool supported(PlaneConfig, DataType) const; /** * Update to add support for pixmaps with numChannel channels where each channel is * represented as DataType. */ void enableDataType(DataType, int numChannels); private: // The bit for DataType dt with n channels is at index kDataTypeCnt*(n-1) + dt. std::bitset fDataTypeSupport = {}; }; /** * Gets the default SkColorType to use with numChannels channels, each represented as DataType. * Returns kUnknown_SkColorType if no such color type. */ static constexpr SkColorType DefaultColorTypeForDataType(DataType dataType, int numChannels); /** * If the SkColorType is supported for YUVA pixmaps this will return the number of YUVA channels * that can be stored in a plane of this color type and what the DataType is of those channels. * If the SkColorType is not supported as a YUVA plane the number of channels is reported as 0 * and the DataType returned should be ignored. */ static std::tuple NumChannelsAndDataType(SkColorType); /** Default SkYUVAPixmapInfo is invalid. */ SkYUVAPixmapInfo() = default; /** * Initializes the SkYUVAPixmapInfo from a SkYUVAInfo with per-plane color types and row bytes. * This will be invalid if the colorTypes aren't compatible with the SkYUVAInfo or if a * rowBytes entry is not valid for the plane dimensions and color type. Color type and * row byte values beyond the number of planes in SkYUVAInfo are ignored. All SkColorTypes * must have the same DataType or this will be invalid. * * If rowBytes is nullptr then bpp*width is assumed for each plane. */ SkYUVAPixmapInfo(const SkYUVAInfo&, const SkColorType[kMaxPlanes], const size_t rowBytes[kMaxPlanes]); /** * Like above but uses DefaultColorTypeForDataType to determine each plane's SkColorType. If * rowBytes is nullptr then bpp*width is assumed for each plane. */ SkYUVAPixmapInfo(const SkYUVAInfo&, DataType, const size_t rowBytes[kMaxPlanes]); SkYUVAPixmapInfo(const SkYUVAPixmapInfo&) = default; SkYUVAPixmapInfo& operator=(const SkYUVAPixmapInfo&) = default; bool operator==(const SkYUVAPixmapInfo&) const; bool operator!=(const SkYUVAPixmapInfo& that) const { return !(*this == that); } const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; } SkYUVColorSpace yuvColorSpace() const { return fYUVAInfo.yuvColorSpace(); } /** The number of SkPixmap planes, 0 if this SkYUVAPixmapInfo is invalid. */ int numPlanes() const { return fYUVAInfo.numPlanes(); } /** The per-YUV[A] channel data type. */ DataType dataType() const { return fDataType; } /** * Row bytes for the ith plane. Returns zero if i >= numPlanes() or this SkYUVAPixmapInfo is * invalid. */ size_t rowBytes(int i) const { return fRowBytes[static_cast(i)]; } /** Image info for the ith plane, or default SkImageInfo if i >= numPlanes() */ const SkImageInfo& planeInfo(int i) const { return fPlaneInfos[static_cast(i)]; } /** * Determine size to allocate for all planes. Optionally retrieves the per-plane sizes in * planeSizes if not null. If total size overflows will return SIZE_MAX and set all planeSizes * to SIZE_MAX. Returns 0 and fills planesSizes with 0 if this SkYUVAPixmapInfo is not valid. */ size_t computeTotalBytes(size_t planeSizes[kMaxPlanes] = nullptr) const; /** * Takes an allocation that is assumed to be at least computeTotalBytes() in size and configures * the first numPlanes() entries in pixmaps array to point into that memory. The remaining * entries of pixmaps are default initialized. Fails if this SkYUVAPixmapInfo not valid. */ bool initPixmapsFromSingleAllocation(void* memory, SkPixmap pixmaps[kMaxPlanes]) const; /** * Returns true if this has been configured with a non-empty dimensioned SkYUVAInfo with * compatible color types and row bytes. */ bool isValid() const { return fYUVAInfo.isValid(); } /** Is this valid and does it use color types allowed by the passed SupportedDataTypes? */ bool isSupported(const SupportedDataTypes&) const; private: SkYUVAInfo fYUVAInfo; std::array fPlaneInfos = {}; std::array fRowBytes = {}; DataType fDataType = DataType::kUnorm8; static_assert(kUnknown_SkColorType == 0, "default init isn't kUnknown"); }; /** * Helper to store SkPixmap planes as described by a SkYUVAPixmapInfo. Can be responsible for * allocating/freeing memory for pixmaps or use external memory. */ class SK_API SkYUVAPixmaps { public: using DataType = SkYUVAPixmapInfo::DataType; static constexpr auto kMaxPlanes = SkYUVAPixmapInfo::kMaxPlanes; static SkColorType RecommendedRGBAColorType(DataType); /** Allocate space for pixmaps' pixels in the SkYUVAPixmaps. */ static SkYUVAPixmaps Allocate(const SkYUVAPixmapInfo& yuvaPixmapInfo); /** * Use storage in SkData as backing store for pixmaps' pixels. SkData is retained by the * SkYUVAPixmaps. */ static SkYUVAPixmaps FromData(const SkYUVAPixmapInfo&, sk_sp); /** * Makes a deep copy of the src SkYUVAPixmaps. The returned SkYUVAPixmaps owns its planes' * backing stores. */ static SkYUVAPixmaps MakeCopy(const SkYUVAPixmaps& src); /** * Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains * allocated while pixmaps are in use. There must be at least * SkYUVAPixmapInfo::computeTotalBytes() allocated starting at memory. */ static SkYUVAPixmaps FromExternalMemory(const SkYUVAPixmapInfo&, void* memory); /** * Wraps existing SkPixmaps. The SkYUVAPixmaps will have no ownership of the SkPixmaps' pixel * memory so the caller must ensure it remains valid. Will return an invalid SkYUVAPixmaps if * the SkYUVAInfo isn't compatible with the SkPixmap array (number of planes, plane dimensions, * sufficient color channels in planes, ...). */ static SkYUVAPixmaps FromExternalPixmaps(const SkYUVAInfo&, const SkPixmap[kMaxPlanes]); /** Default SkYUVAPixmaps is invalid. */ SkYUVAPixmaps() = default; ~SkYUVAPixmaps() = default; SkYUVAPixmaps(SkYUVAPixmaps&& that) = default; SkYUVAPixmaps& operator=(SkYUVAPixmaps&& that) = default; SkYUVAPixmaps(const SkYUVAPixmaps&) = default; SkYUVAPixmaps& operator=(const SkYUVAPixmaps& that) = default; /** Does have initialized pixmaps compatible with its SkYUVAInfo. */ bool isValid() const { return !fYUVAInfo.dimensions().isEmpty(); } const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; } DataType dataType() const { return fDataType; } SkYUVAPixmapInfo pixmapsInfo() const; /** Number of pixmap planes or 0 if this SkYUVAPixmaps is invalid. */ int numPlanes() const { return this->isValid() ? fYUVAInfo.numPlanes() : 0; } /** * Access the SkPixmap planes. They are default initialized if this is not a valid * SkYUVAPixmaps. */ const std::array& planes() const { return fPlanes; } /** * Get the ith SkPixmap plane. SkPixmap will be default initialized if i >= numPlanes or this * SkYUVAPixmaps is invalid. */ const SkPixmap& plane(int i) const { return fPlanes[SkToSizeT(i)]; } /** * Computes a SkYUVAIndex representation of the planar layout. Returns true on success and * false on failure. Will succeed whenever this->isValid() is true. */ bool toYUVAIndices(SkYUVAIndex[SkYUVAIndex::kIndexCount]) const; /** Does this SkPixmaps own the backing store of the planes? */ bool ownsStorage() const { return SkToBool(fData); } /** * Conversion to legacy SkYUVA data structures. */ bool toLegacy(SkYUVASizeInfo*, SkYUVAIndex[SkYUVAIndex::kIndexCount]) const; private: SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp); SkYUVAPixmaps(const SkYUVAInfo&, DataType, const SkPixmap[kMaxPlanes]); std::array fPlanes = {}; sk_sp fData; SkYUVAInfo fYUVAInfo; DataType fDataType; }; ////////////////////////////////////////////////////////////////////////////// constexpr SkYUVAPixmapInfo::SupportedDataTypes SkYUVAPixmapInfo::SupportedDataTypes::All() { using ULL = unsigned long long; // bitset cons. takes this. ULL bits = 0; for (ULL c = 1; c <= 4; ++c) { for (ULL dt = 0; dt <= ULL(kDataTypeCnt); ++dt) { if (DefaultColorTypeForDataType(static_cast(dt), static_cast(c)) != kUnknown_SkColorType) { bits |= ULL(1) << (dt + static_cast(kDataTypeCnt)*(c - 1)); } } } SupportedDataTypes combinations; combinations.fDataTypeSupport = bits; return combinations; } constexpr bool SkYUVAPixmapInfo::SupportedDataTypes::supported(PlaneConfig config, DataType type) const { int n = SkYUVAInfo::NumPlanes(config); for (int i = 0; i < n; ++i) { auto c = static_cast(SkYUVAInfo::NumChannelsInPlane(config, i)); SkASSERT(c >= 1 && c <= 4); if (!fDataTypeSupport[static_cast(type) + (c - 1)*static_cast(kDataTypeCnt)]) { return false; } } return true; } constexpr SkColorType SkYUVAPixmapInfo::DefaultColorTypeForDataType(DataType dataType, int numChannels) { switch (numChannels) { case 1: switch (dataType) { case DataType::kUnorm8: return kGray_8_SkColorType; case DataType::kUnorm16: return kA16_unorm_SkColorType; case DataType::kFloat16: return kA16_float_SkColorType; case DataType::kUnorm10_Unorm2: return kUnknown_SkColorType; } break; case 2: switch (dataType) { case DataType::kUnorm8: return kR8G8_unorm_SkColorType; case DataType::kUnorm16: return kR16G16_unorm_SkColorType; case DataType::kFloat16: return kR16G16_float_SkColorType; case DataType::kUnorm10_Unorm2: return kUnknown_SkColorType; } break; case 3: // None of these are tightly packed. The intended use case is for interleaved YUVA // planes where we're forcing opaqueness by ignoring the alpha values. // There are "x" rather than "A" variants for Unorm8 and Unorm10_Unorm2 but we don't // choose them because 1) there is no inherent advantage and 2) there is better support // in the GPU backend for the "A" versions. switch (dataType) { case DataType::kUnorm8: return kRGBA_8888_SkColorType; case DataType::kUnorm16: return kR16G16B16A16_unorm_SkColorType; case DataType::kFloat16: return kRGBA_F16_SkColorType; case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType; } break; case 4: switch (dataType) { case DataType::kUnorm8: return kRGBA_8888_SkColorType; case DataType::kUnorm16: return kR16G16B16A16_unorm_SkColorType; case DataType::kFloat16: return kRGBA_F16_SkColorType; case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType; } break; } return kUnknown_SkColorType; } #endif