1 /* 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 "GrGpu.h" 16 #include "GrRenderTargetContext.h" 17 #include "GrTexture.h" 18 #include "GrTextureProducer.h" 19 #include "SkAutoPixmapStorage.h" 20 #include "SkGr.h" 21 #include "SkImage_Gpu.h" 22 #include "SkImage_GpuYUVA.h" 23 #include "SkMipMap.h" 24 #include "SkScopeExit.h" 25 #include "SkYUVASizeInfo.h" 26 #include "effects/GrYUVtoRGBEffect.h" 27 28 SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID, 29 SkYUVColorSpace colorSpace, sk_sp<GrTextureProxy> proxies[], 30 int numProxies, const SkYUVAIndex yuvaIndices[4], 31 GrSurfaceOrigin origin, sk_sp<SkColorSpace> imageColorSpace) 32 : INHERITED(std::move(context), width, height, uniqueID, 33 // If an alpha channel is present we always switch to kPremul. This is because, 34 // although the planar data is always un-premul, the final interleaved RGB image 35 // is/would-be premul. 36 GetAlphaTypeFromYUVAIndices(yuvaIndices), imageColorSpace) 37 , fNumProxies(numProxies) 38 , fYUVColorSpace(colorSpace) 39 , fOrigin(origin) { 40 // The caller should have done this work, just verifying 41 SkDEBUGCODE(int textureCount;) 42 SkASSERT(SkYUVAIndex::AreValidIndices(yuvaIndices, &textureCount)); 43 SkASSERT(textureCount == fNumProxies); 44 45 for (int i = 0; i < numProxies; ++i) { 46 fProxies[i] = std::move(proxies[i]); 47 } 48 memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex)); 49 } 50 51 // For onMakeColorSpace() 52 SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace> targetCS) 53 : INHERITED(image->fContext, image->width(), image->height(), kNeedNewImageUniqueID, 54 // If an alpha channel is present we always switch to kPremul. This is because, 55 // although the planar data is always un-premul, the final interleaved RGB image 56 // is/would-be premul. 57 GetAlphaTypeFromYUVAIndices(image->fYUVAIndices), image->fColorSpace) 58 , fNumProxies(image->fNumProxies) 59 , fYUVColorSpace(image->fYUVColorSpace) 60 , fOrigin(image->fOrigin) 61 , fTargetColorSpace(targetCS) { 62 // The caller should have done this work, just verifying 63 SkDEBUGCODE(int textureCount;) 64 SkASSERT(SkYUVAIndex::AreValidIndices(image->fYUVAIndices, &textureCount)); 65 SkASSERT(textureCount == fNumProxies); 66 67 for (int i = 0; i < fNumProxies; ++i) { 68 fProxies[i] = image->fProxies[i]; // we ref in this case, not move 69 } 70 memcpy(fYUVAIndices, image->fYUVAIndices, 4 * sizeof(SkYUVAIndex)); 71 } 72 73 SkImage_GpuYUVA::~SkImage_GpuYUVA() {} 74 75 SkImageInfo SkImage_GpuYUVA::onImageInfo() const { 76 // Note: this is the imageInfo for the flattened image, not the YUV planes 77 return SkImageInfo::Make(this->width(), this->height(), kRGBA_8888_SkColorType, 78 fAlphaType, fColorSpace); 79 } 80 81 bool SkImage_GpuYUVA::setupMipmapsForPlanes() const { 82 for (int i = 0; i < fNumProxies; ++i) { 83 GrTextureProducer::CopyParams copyParams; 84 int mipCount = SkMipMap::ComputeLevelCount(fProxies[i]->width(), fProxies[i]->height()); 85 if (mipCount && GrGpu::IsACopyNeededForMips(fContext->contextPriv().caps(), 86 fProxies[i].get(), 87 GrSamplerState::Filter::kMipMap, 88 ©Params)) { 89 auto mippedProxy = GrCopyBaseMipMapToTextureProxy(fContext.get(), fProxies[i].get()); 90 if (!mippedProxy) { 91 return false; 92 } 93 fProxies[i] = mippedProxy; 94 } 95 } 96 return true; 97 } 98 99 ////////////////////////////////////////////////////////////////////////////////////////////////// 100 101 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef() const { 102 if (!fRGBProxy) { 103 const GrBackendFormat format = 104 fContext->contextPriv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType); 105 106 // Needs to create a render target in order to draw to it for the yuv->rgb conversion. 107 sk_sp<GrRenderTargetContext> renderTargetContext( 108 fContext->contextPriv().makeDeferredRenderTargetContext( 109 format, SkBackingFit::kExact, this->width(), this->height(), 110 kRGBA_8888_GrPixelConfig, fColorSpace, 1, GrMipMapped::kNo, fOrigin)); 111 if (!renderTargetContext) { 112 return nullptr; 113 } 114 115 const SkRect rect = SkRect::MakeIWH(this->width(), this->height()); 116 if (!RenderYUVAToRGBA(fContext.get(), renderTargetContext.get(), rect, fYUVColorSpace, 117 fProxies, fYUVAIndices)) { 118 return nullptr; 119 } 120 121 fRGBProxy = renderTargetContext->asTextureProxyRef(); 122 } 123 124 return fRGBProxy; 125 } 126 127 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asMippedTextureProxyRef() const { 128 // if invalid or already has miplevels 129 auto proxy = this->asTextureProxyRef(); 130 if (!proxy || GrMipMapped::kYes == fRGBProxy->mipMapped()) { 131 return proxy; 132 } 133 134 // need to generate mips for the proxy 135 if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(fContext.get(), proxy.get())) { 136 fRGBProxy = mippedProxy; 137 return mippedProxy; 138 } 139 140 // failed to generate mips 141 return nullptr; 142 } 143 144 ////////////////////////////////////////////////////////////////////////////////////////////////// 145 146 sk_sp<SkImage> SkImage_GpuYUVA::onMakeColorTypeAndColorSpace(SkColorType, 147 sk_sp<SkColorSpace> targetCS) const { 148 // We explicitly ignore color type changes, for now. 149 150 // we may need a mutex here but for now we expect usage to be in a single thread 151 if (fOnMakeColorSpaceTarget && 152 SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) { 153 return fOnMakeColorSpaceResult; 154 } 155 sk_sp<SkImage> result = sk_sp<SkImage>(new SkImage_GpuYUVA(this, targetCS)); 156 if (result) { 157 fOnMakeColorSpaceTarget = targetCS; 158 fOnMakeColorSpaceResult = result; 159 } 160 return result; 161 } 162 163 ////////////////////////////////////////////////////////////////////////////////////////////////// 164 165 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx, 166 SkYUVColorSpace colorSpace, 167 const GrBackendTexture yuvaTextures[], 168 const SkYUVAIndex yuvaIndices[4], 169 SkISize imageSize, 170 GrSurfaceOrigin imageOrigin, 171 sk_sp<SkColorSpace> imageColorSpace) { 172 int numTextures; 173 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) { 174 return nullptr; 175 } 176 177 sk_sp<GrTextureProxy> tempTextureProxies[4]; 178 if (!SkImage_GpuBase::MakeTempTextureProxies(ctx, yuvaTextures, numTextures, yuvaIndices, 179 imageOrigin, tempTextureProxies)) { 180 return nullptr; 181 } 182 183 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize.width(), imageSize.height(), 184 kNeedNewImageUniqueID, colorSpace, tempTextureProxies, 185 numTextures, yuvaIndices, imageOrigin, imageColorSpace); 186 } 187 188 sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps( 189 GrContext* context, SkYUVColorSpace yuvColorSpace, const SkPixmap yuvaPixmaps[], 190 const SkYUVAIndex yuvaIndices[4], SkISize imageSize, GrSurfaceOrigin imageOrigin, 191 bool buildMips, bool limitToMaxTextureSize, sk_sp<SkColorSpace> imageColorSpace) { 192 int numPixmaps; 193 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) { 194 return nullptr; 195 } 196 197 // Make proxies 198 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 199 sk_sp<GrTextureProxy> tempTextureProxies[4]; 200 for (int i = 0; i < numPixmaps; ++i) { 201 const SkPixmap* pixmap = &yuvaPixmaps[i]; 202 SkAutoPixmapStorage resized; 203 int maxTextureSize = context->contextPriv().caps()->maxTextureSize(); 204 int maxDim = SkTMax(yuvaPixmaps[i].width(), yuvaPixmaps[i].height()); 205 if (limitToMaxTextureSize && maxDim > maxTextureSize) { 206 float scale = static_cast<float>(maxTextureSize) / maxDim; 207 int newWidth = SkTMin(static_cast<int>(yuvaPixmaps[i].width() * scale), 208 maxTextureSize); 209 int newHeight = SkTMin(static_cast<int>(yuvaPixmaps[i].height() * scale), 210 maxTextureSize); 211 SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight); 212 if (!resized.tryAlloc(info) || 213 !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) { 214 return nullptr; 215 } 216 pixmap = &resized; 217 } 218 // Turn the pixmap into a GrTextureProxy 219 if (buildMips) { 220 SkBitmap bmp; 221 bmp.installPixels(*pixmap); 222 tempTextureProxies[i] = proxyProvider->createMipMapProxyFromBitmap(bmp); 223 } 224 if (!tempTextureProxies[i]) { 225 if (SkImageInfoIsValid(pixmap->info())) { 226 ATRACE_ANDROID_FRAMEWORK("Upload Texture [%ux%u]", 227 pixmap->width(), pixmap->height()); 228 // We don't need a release proc on the data in pixmap since we know we are in a 229 // GrContext that has a resource provider. Thus the createTextureProxy call will 230 // immediately upload the data. 231 sk_sp<SkImage> image = SkImage::MakeFromRaster(*pixmap, nullptr, nullptr); 232 tempTextureProxies[i] = 233 proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, 1, 234 SkBudgeted::kYes, SkBackingFit::kExact); 235 } 236 } 237 238 if (!tempTextureProxies[i]) { 239 return nullptr; 240 } 241 } 242 243 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize.width(), imageSize.height(), 244 kNeedNewImageUniqueID, yuvColorSpace, tempTextureProxies, 245 numPixmaps, yuvaIndices, imageOrigin, imageColorSpace); 246 } 247 248 249 ///////////////////////////////////////////////////////////////////////////////////////////////// 250 sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture( 251 GrContext* context, 252 SkYUVColorSpace yuvColorSpace, 253 const GrBackendFormat yuvaFormats[], 254 const SkISize yuvaSizes[], 255 const SkYUVAIndex yuvaIndices[4], 256 int imageWidth, 257 int imageHeight, 258 GrSurfaceOrigin imageOrigin, 259 sk_sp<SkColorSpace> imageColorSpace, 260 PromiseImageTextureFulfillProc textureFulfillProc, 261 PromiseImageTextureReleaseProc textureReleaseProc, 262 PromiseImageTextureDoneProc promiseDoneProc, 263 PromiseImageTextureContext textureContexts[], 264 DelayReleaseCallback delayReleaseCallback) { 265 int numTextures; 266 bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures); 267 268 // The contract here is that if 'promiseDoneProc' is passed in it should always be called, 269 // even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes 270 // responsibility for calling the done proc. 271 if (!promiseDoneProc) { 272 return nullptr; 273 } 274 int proxiesCreated = 0; 275 SkScopeExit callDone([promiseDoneProc, textureContexts, numTextures, &proxiesCreated]() { 276 for (int i = proxiesCreated; i < numTextures; ++i) { 277 promiseDoneProc(textureContexts[i]); 278 } 279 }); 280 281 if (!valid) { 282 return nullptr; 283 } 284 285 if (!context) { 286 return nullptr; 287 } 288 289 if (imageWidth <= 0 || imageWidth <= 0) { 290 return nullptr; 291 } 292 293 SkAlphaType at = (-1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex) ? kPremul_SkAlphaType 294 : kOpaque_SkAlphaType; 295 SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kRGBA_8888_SkColorType, 296 at, imageColorSpace); 297 if (!SkImageInfoIsValid(info)) { 298 return nullptr; 299 } 300 301 // verify sizes with expected texture count 302 for (int i = 0; i < numTextures; ++i) { 303 if (yuvaSizes[i].isEmpty()) { 304 return nullptr; 305 } 306 } 307 for (int i = numTextures; i < SkYUVASizeInfo::kMaxCount; ++i) { 308 if (!yuvaSizes[i].isEmpty()) { 309 return nullptr; 310 } 311 } 312 313 // Get lazy proxies 314 sk_sp<GrTextureProxy> proxies[4]; 315 for (int texIdx = 0; texIdx < numTextures; ++texIdx) { 316 GrPixelConfig config = 317 context->contextPriv().caps()->getYUVAConfigFromBackendFormat(yuvaFormats[texIdx]); 318 if (config == kUnknown_GrPixelConfig) { 319 return nullptr; 320 } 321 proxies[texIdx] = MakePromiseImageLazyProxy( 322 context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(), imageOrigin, config, 323 yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc, textureReleaseProc, 324 promiseDoneProc, textureContexts[texIdx], delayReleaseCallback); 325 ++proxiesCreated; 326 if (!proxies[texIdx]) { 327 return nullptr; 328 } 329 } 330 331 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageWidth, imageHeight, 332 kNeedNewImageUniqueID, yuvColorSpace, proxies, numTextures, 333 yuvaIndices, imageOrigin, std::move(imageColorSpace)); 334 } 335