e4387382c2
Sometimes it's helpful to think about subsampling separately from how the channels are spread across planes. Bug: skia:10632 Change-Id: Ib03f71195f9706ef6def418b1f2125c29e0cf738 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/334102 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
354 lines
14 KiB
C++
354 lines
14 KiB
C++
/*
|
|
* 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 <array>
|
|
#include <bitset>
|
|
|
|
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;
|
|
using PlanarConfig = SkYUVAInfo::PlanarConfig;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/** Deprecated. Use PlaneConfig version. */
|
|
constexpr bool supported(PlanarConfig, 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 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<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 bool SkYUVAPixmapInfo::SupportedDataTypes::supported(PlanarConfig planarConfig,
|
|
DataType type) const {
|
|
auto pc = std::get<0>(SkYUVAInfo::PlanarConfigToPlaneConfigAndSubsampling(planarConfig));
|
|
return this->supported(pc, type);
|
|
}
|
|
|
|
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
|