| /* |
| * 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/base/SkTo.h" |
| |
| #include <array> |
| #include <bitset> |
| |
| class GrImageContext; |
| |
| /** |
| * 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<int>(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<kDataTypeCnt*4> 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<int, DataType> 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<size_t>(i)]; } |
| |
| /** Image info for the ith plane, or default SkImageInfo if i >= numPlanes() */ |
| const SkImageInfo& planeInfo(int i) const { return fPlaneInfos[static_cast<size_t>(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<SkImageInfo, kMaxPlanes> fPlaneInfos = {}; |
| std::array<size_t, kMaxPlanes> 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<SkData>); |
| |
| /** |
| * 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<SkPixmap, kMaxPlanes>& 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 YUVALocations representation of the planar layout. The result is guaranteed to be |
| * valid if this->isValid(). |
| */ |
| SkYUVAInfo::YUVALocations toYUVALocations() const; |
| |
| /** Does this SkPixmaps own the backing store of the planes? */ |
| bool ownsStorage() const { return SkToBool(fData); } |
| |
| private: |
| SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp<SkData>); |
| SkYUVAPixmaps(const SkYUVAInfo&, DataType, const SkPixmap[kMaxPlanes]); |
| |
| std::array<SkPixmap, kMaxPlanes> fPlanes = {}; |
| sk_sp<SkData> 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<DataType>(dt), |
| static_cast<int>(c)) != kUnknown_SkColorType) { |
| bits |= ULL(1) << (dt + static_cast<ULL>(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<size_t>(SkYUVAInfo::NumChannelsInPlane(config, i)); |
| SkASSERT(c >= 1 && c <= 4); |
| if (!fDataTypeSupport[static_cast<size_t>(type) + |
| (c - 1)*static_cast<size_t>(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 |