1 /* 2 * Copyright 2015 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 "src/gpu/GrYUVProvider.h" 9 10 #include "include/core/SkRefCnt.h" 11 #include "include/core/SkYUVAIndex.h" 12 #include "include/private/GrRecordingContext.h" 13 #include "src/core/SkAutoMalloc.h" 14 #include "src/core/SkCachedData.h" 15 #include "src/core/SkResourceCache.h" 16 #include "src/core/SkYUVPlanesCache.h" 17 #include "src/gpu/GrCaps.h" 18 #include "src/gpu/GrClip.h" 19 #include "src/gpu/GrColorSpaceXform.h" 20 #include "src/gpu/GrProxyProvider.h" 21 #include "src/gpu/GrRecordingContextPriv.h" 22 #include "src/gpu/GrRenderTargetContext.h" 23 #include "src/gpu/GrTextureProxy.h" 24 #include "src/gpu/effects/GrYUVtoRGBEffect.h" 25 getPlanes(SkYUVASizeInfo * size,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkYUVColorSpace * colorSpace,const void * constPlanes[SkYUVASizeInfo::kMaxCount])26 sk_sp<SkCachedData> GrYUVProvider::getPlanes(SkYUVASizeInfo* size, 27 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount], 28 SkYUVColorSpace* colorSpace, 29 const void* constPlanes[SkYUVASizeInfo::kMaxCount]) { 30 sk_sp<SkCachedData> data; 31 SkYUVPlanesCache::Info yuvInfo; 32 data.reset(SkYUVPlanesCache::FindAndRef(this->onGetID(), &yuvInfo)); 33 34 void* planes[SkYUVASizeInfo::kMaxCount]; 35 36 if (data.get()) { 37 planes[0] = (void*)data->data(); // we should always have at least one plane 38 39 for (int i = 1; i < SkYUVASizeInfo::kMaxCount; ++i) { 40 if (!yuvInfo.fSizeInfo.fWidthBytes[i]) { 41 SkASSERT(!yuvInfo.fSizeInfo.fWidthBytes[i] && 42 !yuvInfo.fSizeInfo.fSizes[i].fHeight); 43 planes[i] = nullptr; 44 continue; 45 } 46 47 planes[i] = (uint8_t*)planes[i-1] + (yuvInfo.fSizeInfo.fWidthBytes[i-1] * 48 yuvInfo.fSizeInfo.fSizes[i-1].fHeight); 49 } 50 } else { 51 // Fetch yuv plane sizes for memory allocation. 52 if (!this->onQueryYUVA8(&yuvInfo.fSizeInfo, yuvInfo.fYUVAIndices, &yuvInfo.fColorSpace)) { 53 return nullptr; 54 } 55 56 // Allocate the memory for YUVA 57 size_t totalSize(0); 58 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; i++) { 59 SkASSERT((yuvInfo.fSizeInfo.fWidthBytes[i] && yuvInfo.fSizeInfo.fSizes[i].fHeight) || 60 (!yuvInfo.fSizeInfo.fWidthBytes[i] && !yuvInfo.fSizeInfo.fSizes[i].fHeight)); 61 62 totalSize += yuvInfo.fSizeInfo.fWidthBytes[i] * yuvInfo.fSizeInfo.fSizes[i].fHeight; 63 } 64 65 data.reset(SkResourceCache::NewCachedData(totalSize)); 66 67 planes[0] = data->writable_data(); 68 69 for (int i = 1; i < SkYUVASizeInfo::kMaxCount; ++i) { 70 if (!yuvInfo.fSizeInfo.fWidthBytes[i]) { 71 SkASSERT(!yuvInfo.fSizeInfo.fWidthBytes[i] && 72 !yuvInfo.fSizeInfo.fSizes[i].fHeight); 73 planes[i] = nullptr; 74 continue; 75 } 76 77 planes[i] = (uint8_t*)planes[i-1] + (yuvInfo.fSizeInfo.fWidthBytes[i-1] * 78 yuvInfo.fSizeInfo.fSizes[i-1].fHeight); 79 } 80 81 // Get the YUV planes. 82 if (!this->onGetYUVA8Planes(yuvInfo.fSizeInfo, yuvInfo.fYUVAIndices, planes)) { 83 return nullptr; 84 } 85 86 // Decoding is done, cache the resulting YUV planes 87 SkYUVPlanesCache::Add(this->onGetID(), data.get(), &yuvInfo); 88 } 89 90 *size = yuvInfo.fSizeInfo; 91 memcpy(yuvaIndices, yuvInfo.fYUVAIndices, sizeof(yuvInfo.fYUVAIndices)); 92 *colorSpace = yuvInfo.fColorSpace; 93 constPlanes[0] = planes[0]; 94 constPlanes[1] = planes[1]; 95 constPlanes[2] = planes[2]; 96 constPlanes[3] = planes[3]; 97 return data; 98 } 99 YUVGen_DataReleaseProc(const void *,void * data)100 void GrYUVProvider::YUVGen_DataReleaseProc(const void*, void* data) { 101 SkCachedData* cachedData = static_cast<SkCachedData*>(data); 102 SkASSERT(cachedData); 103 cachedData->unref(); 104 } 105 refAsTextureProxy(GrRecordingContext * ctx,const GrSurfaceDesc & desc,GrColorType colorType,SkColorSpace * srcColorSpace,SkColorSpace * dstColorSpace)106 sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrRecordingContext* ctx, 107 const GrSurfaceDesc& desc, 108 GrColorType colorType, 109 SkColorSpace* srcColorSpace, 110 SkColorSpace* dstColorSpace) { 111 SkYUVASizeInfo yuvSizeInfo; 112 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount]; 113 SkYUVColorSpace yuvColorSpace; 114 const void* planes[SkYUVASizeInfo::kMaxCount]; 115 116 sk_sp<SkCachedData> dataStorage = this->getPlanes(&yuvSizeInfo, yuvaIndices, 117 &yuvColorSpace, planes); 118 if (!dataStorage) { 119 return nullptr; 120 } 121 122 sk_sp<GrTextureProxy> yuvTextureProxies[SkYUVASizeInfo::kMaxCount]; 123 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) { 124 if (yuvSizeInfo.fSizes[i].isEmpty()) { 125 SkASSERT(!yuvSizeInfo.fWidthBytes[i]); 126 continue; 127 } 128 129 int componentWidth = yuvSizeInfo.fSizes[i].fWidth; 130 int componentHeight = yuvSizeInfo.fSizes[i].fHeight; 131 // If the sizes of the components are not all the same we choose to create exact-match 132 // textures for the smaller ones rather than add a texture domain to the draw. 133 // TODO: revisit this decision to improve texture reuse? 134 SkBackingFit fit = 135 (componentWidth != yuvSizeInfo.fSizes[0].fWidth) || 136 (componentHeight != yuvSizeInfo.fSizes[0].fHeight) 137 ? SkBackingFit::kExact : SkBackingFit::kApprox; 138 139 SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight); 140 SkPixmap pixmap(imageInfo, planes[i], yuvSizeInfo.fWidthBytes[i]); 141 SkCachedData* dataStoragePtr = dataStorage.get(); 142 // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call 143 // the YUVGen_DataReleaseProc which will release this ref. 144 // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the 145 // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the 146 // life time of the proxy and not just upload. For non-DDL draws we should look into 147 // releasing this SkImage after uploads (by deleting the lambda after instantiation). 148 dataStoragePtr->ref(); 149 sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc, 150 dataStoragePtr); 151 152 auto proxyProvider = ctx->priv().proxyProvider(); 153 yuvTextureProxies[i] = 154 proxyProvider->createTextureProxy(yuvImage, 1, SkBudgeted::kYes, fit); 155 156 SkASSERT(yuvTextureProxies[i]->width() == yuvSizeInfo.fSizes[i].fWidth); 157 SkASSERT(yuvTextureProxies[i]->height() == yuvSizeInfo.fSizes[i].fHeight); 158 } 159 160 // TODO: investigate preallocating mip maps here 161 sk_sp<GrRenderTargetContext> renderTargetContext(ctx->priv().makeDeferredRenderTargetContext( 162 SkBackingFit::kExact, desc.fWidth, desc.fHeight, colorType, nullptr, 1, 163 GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin)); 164 if (!renderTargetContext) { 165 return nullptr; 166 } 167 168 GrPaint paint; 169 auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(yuvTextureProxies, yuvaIndices, yuvColorSpace, 170 GrSamplerState::Filter::kNearest); 171 paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); 172 173 // If the caller expects the pixels in a different color space than the one from the image, 174 // apply a color conversion to do this. 175 std::unique_ptr<GrFragmentProcessor> colorConversionProcessor = 176 GrColorSpaceXformEffect::Make(srcColorSpace, kOpaque_SkAlphaType, 177 dstColorSpace, kOpaque_SkAlphaType); 178 if (colorConversionProcessor) { 179 paint.addColorFragmentProcessor(std::move(colorConversionProcessor)); 180 } 181 182 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 183 const SkRect r = SkRect::MakeIWH(yuvSizeInfo.fSizes[0].fWidth, 184 yuvSizeInfo.fSizes[0].fHeight); 185 186 SkMatrix m = SkEncodedOriginToMatrix(yuvSizeInfo.fOrigin, r.width(), r.height()); 187 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, m, r); 188 189 return renderTargetContext->asTextureProxyRef(); 190 } 191