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