Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Image_YUVA_Graphite.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
30
31
32namespace skgpu::graphite {
33
34namespace {
35
37
38static constexpr int kY = static_cast<int>(SkYUVAInfo::kY);
39static constexpr int kU = static_cast<int>(SkYUVAInfo::kU);
40static constexpr int kV = static_cast<int>(SkYUVAInfo::kV);
41static constexpr int kA = static_cast<int>(SkYUVAInfo::kA);
42
43static SkAlphaType yuva_alpha_type(const SkYUVAInfo& yuvaInfo) {
44 // If an alpha channel is present we always use kPremul. This is because, although the planar
45 // data is always un-premul, the final interleaved RGBA sample produced in the shader is premul
46 // (and similar if flattened).
48}
49
50} // anonymous
51
52Image_YUVA::Image_YUVA(const YUVAProxies& proxies,
53 const SkYUVAInfo& yuvaInfo,
54 sk_sp<SkColorSpace> imageColorSpace)
55 : Image_Base(SkImageInfo::Make(yuvaInfo.dimensions(),
57 yuva_alpha_type(yuvaInfo),
58 std::move(imageColorSpace)),
60 , fProxies(std::move(proxies))
61 , fYUVAInfo(yuvaInfo)
62 , fUVSubsampleFactors(SkYUVAInfo::SubsamplingFactors(yuvaInfo.subsampling())) {
63 // The caller should have checked this, just verifying.
64 SkASSERT(fYUVAInfo.isValid());
65 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
66 if (!fProxies[i]) {
67 SkASSERT(i == kA);
68 continue;
69 }
70 if (fProxies[i].proxy()->mipmapped() == Mipmapped::kNo) {
71 fMipmapped = Mipmapped::kNo;
72 }
73 if (fProxies[i].proxy()->isProtected()) {
74 fProtected = Protected::kYes;
75 }
76 }
77}
78
79Image_YUVA::~Image_YUVA() = default;
80
81sk_sp<Image_YUVA> Image_YUVA::Make(const Caps* caps,
82 const SkYUVAInfo& yuvaInfo,
84 sk_sp<SkColorSpace> imageColorSpace) {
85 if (!yuvaInfo.isValid()) {
86 return nullptr;
87 }
89 yuvaInfo.dimensions(), kAssumedColorType, yuva_alpha_type(yuvaInfo), imageColorSpace);
91 return nullptr;
92 }
93
94 // Invoke the PlaneProxyFactoryFn for each plane and validate it against the plane config
95 const int numPlanes = yuvaInfo.numPlanes();
96 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
97 if (numPlanes != yuvaInfo.planeDimensions(planeDimensions)) {
98 return nullptr;
99 }
100 uint32_t pixmapChannelmasks[SkYUVAInfo::kMaxPlanes];
101 for (int i = 0; i < numPlanes; ++i) {
102 if (!planes[i] || !caps->isTexturable(planes[i].proxy()->textureInfo())) {
103 return nullptr;
104 }
105 if (planes[i].dimensions() != planeDimensions[i]) {
106 return nullptr;
107 }
108 pixmapChannelmasks[i] = caps->channelMask(planes[i].proxy()->textureInfo());
109 }
110
111 // Re-arrange the proxies from planes to channels
112 SkYUVAInfo::YUVALocations locations = yuvaInfo.toYUVALocations(pixmapChannelmasks);
113 int expectedPlanes;
114 if (!SkYUVAInfo::YUVALocation::AreValidLocations(locations, &expectedPlanes) ||
115 expectedPlanes != numPlanes) {
116 return nullptr;
117 }
118 // Y channel should match the YUVAInfo dimensions
119 if (planes[locations[kY].fPlane].dimensions() != yuvaInfo.dimensions()) {
120 return nullptr;
121 }
122 // UV channels should have planes with the same dimensions and subsampling factor.
123 if (planes[locations[kU].fPlane].dimensions() != planes[locations[kV].fPlane].dimensions()) {
124 return nullptr;
125 }
126 // If A channel is present, it should match the Y channel
127 if (locations[kA].fPlane >= 0 &&
128 planes[locations[kA].fPlane].dimensions() != yuvaInfo.dimensions()) {
129 return nullptr;
130 }
131
132 if (yuvaInfo.planeSubsamplingFactors(locations[kU].fPlane) !=
133 yuvaInfo.planeSubsamplingFactors(locations[kV].fPlane)) {
134 return nullptr;
135 }
136
137 // Re-arrange into YUVA channel order and apply the location to the swizzle
138 YUVAProxies channelProxies;
139 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
140 auto [plane, channel] = locations[i];
141 if (plane >= 0) {
142 // Compose the YUVA location with the data swizzle. replaceSwizzle() is used since
143 // selectChannelInR() effectively does the composition (vs. Swizzle::Concat).
144 Swizzle channelSwizzle = planes[plane].swizzle().selectChannelInR((int) channel);
145 channelProxies[i] = planes[plane].replaceSwizzle(channelSwizzle);
146 } else if (i == kA) {
147 // The alpha channel is allowed to be not provided, set it to an empty view
148 channelProxies[i] = {};
149 } else {
150 SKGPU_LOG_W("YUVA channel %d does not have a valid location", i);
151 return nullptr;
152 }
153 }
154
155 return sk_sp<Image_YUVA>(new Image_YUVA(std::move(channelProxies),
156 yuvaInfo,
157 std::move(imageColorSpace)));
158}
159
160sk_sp<Image_YUVA> Image_YUVA::WrapImages(const Caps* caps,
161 const SkYUVAInfo& yuvaInfo,
162 SkSpan<const sk_sp<SkImage>> images,
163 sk_sp<SkColorSpace> imageColorSpace) {
164 if (SkTo<int>(images.size()) < yuvaInfo.numPlanes()) {
165 return nullptr;
166 }
167
169 for (int i = 0; i < yuvaInfo.numPlanes(); ++i) {
170 planes[i] = AsView(images[i]);
171 if (!planes[i]) {
172 // A null image, or not graphite-backed, or not backed by a single texture.
173 return nullptr;
174 }
175 // The YUVA shader expects to sample from the red channel for single-channel textures, so
176 // reset the swizzle for alpha-only textures to compensate for that
177 if (images[i]->isAlphaOnly()) {
178 planes[i] = planes[i].makeSwizzle(Swizzle("aaaa"));
179 }
180 }
181
182 sk_sp<Image_YUVA> image = Make(caps, yuvaInfo, SkSpan(planes), std::move(imageColorSpace));
183 if (image) {
184 // Unlike the other factories, this YUVA image shares the texture proxies with each plane
185 // Image, so if those are linked to Devices, it must inherit those same links.
186 for (int plane = 0; plane < yuvaInfo.numPlanes(); ++plane) {
187 SkASSERT(as_IB(images[plane])->isGraphiteBacked());
188 image->linkDevices(static_cast<Image_Base*>(images[plane].get()));
189 }
190 }
191 return image;
192}
193
194size_t Image_YUVA::textureSize() const {
195 // We could look at the plane config and plane count to determine how many different textures
196 // to expect, but it's theoretically possible for an Image_YUVA to be constructed where the
197 // same TextureProxy is aliased to both the U and the V planes (and similarly for the Y and A)
198 // even when the plane config specifies that those channels are not packed into the same texture
199 //
200 // Given that it's simpler to just sum the total gpu memory of non-duplicate textures.
201 size_t size = 0;
202 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
203 if (!fProxies[i]) {
204 continue; // Null channels (A) have no size.
205 }
206 bool repeat = false;
207 for (int j = 0; j < i - 1; ++j) {
208 if (fProxies[i].proxy() == fProxies[j].proxy()) {
209 repeat = true;
210 break;
211 }
212 }
213 if (!repeat) {
214 if (fProxies[i].proxy()->isInstantiated()) {
215 size += fProxies[i].proxy()->texture()->gpuMemorySize();
216 } else {
217 size += fProxies[i].proxy()->uninstantiatedGpuMemorySize();
218 }
219 }
220 }
221
222 return size;
223}
224
225sk_sp<SkImage> Image_YUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
226 sk_sp<Image_YUVA> view{new Image_YUVA(fProxies,
227 fYUVAInfo,
228 std::move(newCS))};
229 // The new Image object shares the same texture planes, so it should also share linked Devices
230 view->linkDevices(this);
231 return view;
232}
233
234} // namespace skgpu::graphite
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define SKGPU_LOG_W(fmt,...)
Definition Log.h:40
SkAlphaType
Definition SkAlphaType.h:26
@ kOpaque_SkAlphaType
pixel is opaque
Definition SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkASSERT(cond)
Definition SkAssert.h:116
static unsigned repeat(SkFixed fx, int max)
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
static bool SkImageInfoIsValid(const SkImageInfo &info)
@ kNeedNewImageUniqueID
static SkImage_Base * as_IB(SkImage *image)
static constexpr auto kAssumedColorType
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
int numPlanes() const
Definition SkYUVAInfo.h:204
static constexpr int kMaxPlanes
Definition SkYUVAInfo.h:98
bool hasAlpha() const
Definition SkYUVAInfo.h:185
YUVALocations toYUVALocations(const uint32_t *channelFlags) const
std::tuple< int, int > planeSubsamplingFactors(int planeIdx) const
Definition SkYUVAInfo.h:163
int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const
Definition SkYUVAInfo.h:192
std::array< YUVALocation, kYUVAChannelCount > YUVALocations
Definition SkYUVAInfo.h:32
static constexpr int kYUVAChannelCount
Definition SkYUVAInfo.h:29
bool isValid() const
Definition SkYUVAInfo.h:233
SkISize dimensions() const
Definition SkYUVAInfo.h:171
virtual uint32_t channelMask(const TextureInfo &) const =0
bool isTexturable(const TextureInfo &) const
Definition Caps.cpp:65
void linkDevices(const Image_Base *)
TextureProxyView makeSwizzle(Swizzle swizzle) const &
sk_sp< SkImage > image
Definition examples.cpp:29
std::array< MockImage, 3 > images
Definition ref_ptr.h:256
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static bool AreValidLocations(const SkYUVAInfo::YUVALocations &locations, int *numPlanes=nullptr)