blob: 9a54d23713299fd98e1dffca4aa20548f35826f2 [file] [log] [blame]
Jim Van Verthf49262d2018-10-02 12:07:20 -04001/*
2 * Copyright 2018 Google Inc.
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
8#include <cstddef>
9#include <cstring>
10#include <type_traits>
11
12#include "GrClip.h"
13#include "GrContext.h"
14#include "GrContextPriv.h"
15#include "GrRenderTargetContext.h"
16#include "GrTexture.h"
Jim Van Verthf49262d2018-10-02 12:07:20 -040017#include "SkImage_Gpu.h"
18#include "SkImage_GpuYUVA.h"
Jim Van Verthe24b5872018-10-29 16:26:02 -040019#include "SkYUVASizeInfo.h"
Jim Van Verthf49262d2018-10-02 12:07:20 -040020#include "effects/GrYUVtoRGBEffect.h"
21
Jim Van Verthcea39022018-10-12 16:15:34 -040022SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
Jim Van Verthf49262d2018-10-02 12:07:20 -040023 SkYUVColorSpace colorSpace, sk_sp<GrTextureProxy> proxies[],
Jim Van Verthcea39022018-10-12 16:15:34 -040024 const SkYUVAIndex yuvaIndices[4], GrSurfaceOrigin origin,
25 sk_sp<SkColorSpace> imageColorSpace, SkBudgeted budgeted)
26 : INHERITED(std::move(context), width, height, uniqueID,
Jim Van Verth8026ccc2018-10-04 13:10:39 -040027 // If an alpha channel is present we always switch to kPremul. This is because,
28 // although the planar data is always un-premul, the final interleaved RGB image
29 // is/would-be premul.
30 -1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex ? kPremul_SkAlphaType
31 : kOpaque_SkAlphaType,
32 budgeted, imageColorSpace)
33 , fYUVColorSpace(colorSpace)
34 , fOrigin(origin) {
Jim Van Verth8bbce0e2018-10-08 14:34:52 -040035 int maxIndex = yuvaIndices[0].fIndex;
36 for (int i = 1; i < 4; ++i) {
37 if (yuvaIndices[i].fIndex > maxIndex) {
38 maxIndex = yuvaIndices[i].fIndex;
39 }
40 }
41 for (int i = 0; i <= maxIndex; ++i) {
Jim Van Verthf49262d2018-10-02 12:07:20 -040042 fProxies[i] = std::move(proxies[i]);
43 }
44 memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex));
Jim Van Verthf49262d2018-10-02 12:07:20 -040045}
46
47SkImage_GpuYUVA::~SkImage_GpuYUVA() {}
48
49SkImageInfo SkImage_GpuYUVA::onImageInfo() const {
50 // Note: this is the imageInfo for the flattened image, not the YUV planes
51 return SkImageInfo::Make(this->width(), this->height(), kRGBA_8888_SkColorType,
Jim Van Verth8026ccc2018-10-04 13:10:39 -040052 fAlphaType, fColorSpace);
Jim Van Verthf49262d2018-10-02 12:07:20 -040053}
54
55//////////////////////////////////////////////////////////////////////////////////////////////////
Jim Van Verthf49262d2018-10-02 12:07:20 -040056
57sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef() const {
58 if (!fRGBProxy) {
Jim Van Verth49fdd7a2018-10-03 10:47:05 -040059 sk_sp<GrTextureProxy> yProxy = fProxies[fYUVAIndices[SkYUVAIndex::kY_Index].fIndex];
60 sk_sp<GrTextureProxy> uProxy = fProxies[fYUVAIndices[SkYUVAIndex::kU_Index].fIndex];
61 sk_sp<GrTextureProxy> vProxy = fProxies[fYUVAIndices[SkYUVAIndex::kV_Index].fIndex];
Jim Van Verthf49262d2018-10-02 12:07:20 -040062
63 if (!yProxy || !uProxy || !vProxy) {
64 return nullptr;
65 }
66
67 GrPaint paint;
68 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Robert Phillips94ade752018-10-09 12:32:31 -040069
Jim Van Verthf49262d2018-10-02 12:07:20 -040070 // TODO: modify the YUVtoRGBEffect to do premul if fImageAlphaType is kPremul_AlphaType
Robert Phillips94ade752018-10-09 12:32:31 -040071 paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Make(fProxies, fYUVAIndices,
72 fYUVColorSpace));
Jim Van Verthf49262d2018-10-02 12:07:20 -040073
74 const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
75
76 // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
77 sk_sp<GrRenderTargetContext> renderTargetContext(
78 fContext->contextPriv().makeDeferredRenderTargetContext(
79 SkBackingFit::kExact, this->width(), this->height(), kRGBA_8888_GrPixelConfig,
Jim Van Verth8026ccc2018-10-04 13:10:39 -040080 std::move(fColorSpace), 1, GrMipMapped::kNo, fOrigin));
Jim Van Verthf49262d2018-10-02 12:07:20 -040081 if (!renderTargetContext) {
82 return nullptr;
83 }
84 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
85
86 if (!renderTargetContext->asSurfaceProxy()) {
87 return nullptr;
88 }
89
90 // DDL TODO: in the promise image version we must not flush here
91 fContext->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy());
92
Jim Van Verth21bd60d2018-10-12 15:00:20 -040093 fRGBProxy = renderTargetContext->asTextureProxyRef();
Jim Van Verthf49262d2018-10-02 12:07:20 -040094 }
95
96 return fRGBProxy;
97}
98
Jim Van Verth9bf81202018-10-30 15:53:36 -040099//////////////////////////////////////////////////////////////////////////////////////////////////
100
101sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx,
102 SkYUVColorSpace colorSpace,
103 const GrBackendTexture yuvaTextures[],
104 const SkYUVAIndex yuvaIndices[4],
105 SkISize imageSize,
106 GrSurfaceOrigin imageOrigin,
107 sk_sp<SkColorSpace> imageColorSpace) {
108 GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
109
110 // We need to make a copy of the input backend textures because we need to preserve the result
111 // of validate_backend_texture.
112 GrBackendTexture yuvaTexturesCopy[4];
113 for (int i = 0; i < 4; ++i) {
114 // Validate that the yuvaIndices refer to valid backend textures.
115 const SkYUVAIndex& yuvaIndex = yuvaIndices[i];
116 if (SkYUVAIndex::kA_Index == i && yuvaIndex.fIndex == -1) {
117 // Meaning the A plane isn't passed in.
118 continue;
119 }
120 if (yuvaIndex.fIndex == -1 || yuvaIndex.fIndex > 3) {
121 // Y plane, U plane, and V plane must refer to image sources being passed in. There are
122 // at most 4 image sources being passed in, could not have a index more than 3.
123 return nullptr;
124 }
125
126 if (!yuvaTexturesCopy[yuvaIndex.fIndex].isValid()) {
127 yuvaTexturesCopy[yuvaIndex.fIndex] = yuvaTextures[yuvaIndex.fIndex];
128
129 if (!ctx->contextPriv().caps()->getYUVAConfigFromBackendTexture(
130 yuvaTexturesCopy[yuvaIndex.fIndex],
131 &yuvaTexturesCopy[yuvaIndex.fIndex].fConfig)) {
132 return nullptr;
133 }
134 }
135
136 // TODO: Check that for each plane, the channel actually exist in the image source we are
137 // reading from.
138 }
139
140 sk_sp<GrTextureProxy> tempTextureProxies[4]; // build from yuvaTextures
141 for (int i = 0; i < 4; ++i) {
142 // Fill in tempTextureProxies to avoid duplicate texture proxies.
143 int textureIndex = yuvaIndices[i].fIndex;
144
145 // Safely ignore since this means we are missing the A plane.
146 if (textureIndex == -1) {
147 SkASSERT(SkYUVAIndex::kA_Index == i);
148 continue;
149 }
150
151 if (!tempTextureProxies[textureIndex]) {
152 SkASSERT(yuvaTexturesCopy[textureIndex].isValid());
153 tempTextureProxies[textureIndex] =
154 proxyProvider->wrapBackendTexture(yuvaTexturesCopy[textureIndex], imageOrigin);
155 if (!tempTextureProxies[textureIndex]) {
156 return nullptr;
157 }
158 }
159 }
160
161 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize.width(), imageSize.height(),
162 kNeedNewImageUniqueID, colorSpace, tempTextureProxies,
163 yuvaIndices, imageOrigin, imageColorSpace, SkBudgeted::kYes);
164}
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400165/////////////////////////////////////////////////////////////////////////////////////////////////
166sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(GrContext* context,
167 SkYUVColorSpace yuvColorSpace,
168 const GrBackendFormat yuvaFormats[],
Jim Van Verthf9f07352018-10-24 10:32:20 -0400169 const SkISize yuvaSizes[],
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400170 const SkYUVAIndex yuvaIndices[4],
Jim Van Verthcea39022018-10-12 16:15:34 -0400171 int imageWidth,
172 int imageHeight,
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400173 GrSurfaceOrigin imageOrigin,
174 sk_sp<SkColorSpace> imageColorSpace,
175 TextureFulfillProc textureFulfillProc,
176 TextureReleaseProc textureReleaseProc,
177 PromiseDoneProc promiseDoneProc,
178 TextureContext textureContexts[]) {
Jim Van Verthf00b1622018-10-10 13:03:23 -0400179 // The contract here is that if 'promiseDoneProc' is passed in it should always be called,
180 // even if creation of the SkImage fails.
Robert Phillipsef85d192018-10-09 11:24:09 -0400181 if (!promiseDoneProc) {
182 return nullptr;
183 }
184
Jim Van Verthf00b1622018-10-10 13:03:23 -0400185 int numTextures;
186 bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
187
188 // Set up promise helpers
189 SkPromiseImageHelper promiseHelpers[4];
190 for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
191 promiseHelpers[texIdx].set(textureFulfillProc, textureReleaseProc, promiseDoneProc,
Jim Van Verth21bd60d2018-10-12 15:00:20 -0400192 textureContexts[texIdx]);
Jim Van Verthf00b1622018-10-10 13:03:23 -0400193 }
194
195 if (!valid) {
196 return nullptr;
197 }
Robert Phillipsef85d192018-10-09 11:24:09 -0400198
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400199 if (!context) {
200 return nullptr;
201 }
202
Jim Van Verthcea39022018-10-12 16:15:34 -0400203 if (imageWidth <= 0 || imageWidth <= 0) {
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400204 return nullptr;
205 }
206
Robert Phillipsef85d192018-10-09 11:24:09 -0400207 if (!textureFulfillProc || !textureReleaseProc) {
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400208 return nullptr;
209 }
210
211 SkAlphaType at = (-1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex) ? kPremul_SkAlphaType
212 : kOpaque_SkAlphaType;
Jim Van Verthcea39022018-10-12 16:15:34 -0400213 SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kRGBA_8888_SkColorType,
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400214 at, imageColorSpace);
215 if (!SkImageInfoIsValid(info)) {
216 return nullptr;
217 }
218
Jim Van Verthf9f07352018-10-24 10:32:20 -0400219 // verify sizes with expected texture count
Jim Van Verth8f11e432018-10-18 14:36:59 -0400220 for (int i = 0; i < numTextures; ++i) {
Jim Van Verthf9f07352018-10-24 10:32:20 -0400221 if (yuvaSizes[i].isEmpty()) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400222 return nullptr;
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400223 }
224 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400225 for (int i = numTextures; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthf9f07352018-10-24 10:32:20 -0400226 if (!yuvaSizes[i].isEmpty()) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400227 return nullptr;
228 }
Jim Van Verthf99a6742018-10-18 16:13:18 +0000229 }
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400230
231 // Get lazy proxies
Jim Van Verthf00b1622018-10-10 13:03:23 -0400232 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400233 sk_sp<GrTextureProxy> proxies[4];
Jim Van Verthf00b1622018-10-10 13:03:23 -0400234 for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400235 // for each proxy we need to fill in
Jim Van Verthf00b1622018-10-10 13:03:23 -0400236 struct {
237 GrPixelConfig fConfig;
238 SkPromiseImageHelper fPromiseHelper;
239 } params;
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400240 bool res = context->contextPriv().caps()->getYUVAConfigFromBackendFormat(
Jim Van Verth8f11e432018-10-18 14:36:59 -0400241 yuvaFormats[texIdx],
Jim Van Verth8f11e432018-10-18 14:36:59 -0400242 &params.fConfig);
243 if (!res) {
Jim Van Verthf00b1622018-10-10 13:03:23 -0400244 return nullptr;
245 }
246 params.fPromiseHelper = promiseHelpers[texIdx];
247
248 GrProxyProvider::LazyInstantiateCallback lazyInstCallback =
249 [params](GrResourceProvider* resourceProvider) mutable {
250 if (!resourceProvider || !params.fPromiseHelper.isValid()) {
251 if (params.fPromiseHelper.isValid()) {
252 params.fPromiseHelper.reset();
253 }
254 return sk_sp<GrTexture>();
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400255 }
256
Jim Van Verthf00b1622018-10-10 13:03:23 -0400257 return params.fPromiseHelper.getTexture(resourceProvider, params.fConfig);
258 };
Jim Van Verth21bd60d2018-10-12 15:00:20 -0400259 GrSurfaceDesc desc;
260 desc.fFlags = kNone_GrSurfaceFlags;
Jim Van Verthf9f07352018-10-24 10:32:20 -0400261 desc.fWidth = yuvaSizes[texIdx].width();
262 desc.fHeight = yuvaSizes[texIdx].height();
Jim Van Verthf00b1622018-10-10 13:03:23 -0400263 desc.fConfig = params.fConfig;
Jim Van Verth21bd60d2018-10-12 15:00:20 -0400264 desc.fSampleCnt = 1;
Jim Van Verthf00b1622018-10-10 13:03:23 -0400265 proxies[texIdx] = proxyProvider->createLazyProxy(
266 std::move(lazyInstCallback), desc, imageOrigin, GrMipMapped::kNo,
267 GrTextureType::k2D, GrInternalSurfaceFlags::kNone,
268 SkBackingFit::kExact, SkBudgeted::kNo,
269 GrSurfaceProxy::LazyInstantiationType::kUninstantiate);
270 if (!proxies[texIdx]) {
271 return nullptr;
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400272 }
273 }
274
Jim Van Verthcea39022018-10-12 16:15:34 -0400275 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageWidth, imageHeight,
276 kNeedNewImageUniqueID, yuvColorSpace, proxies, yuvaIndices,
277 imageOrigin, std::move(imageColorSpace), SkBudgeted::kNo);
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400278}
279