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 "GrContext.h" 11 #include "GrContextPriv.h" 12 #include "GrRenderTargetContext.h" 13 #include "GrTextureProxy.h" 14 #include "SkAutoMalloc.h" 15 #include "SkCachedData.h" 16 #include "SkRefCnt.h" 17 #include "SkResourceCache.h" 18 #include "SkYUVPlanesCache.h" 19 #include "effects/GrNonlinearColorSpaceXformEffect.h" 20 #include "effects/GrSRGBEffect.h" 21 #include "effects/GrYUVtoRGBEffect.h" 22 23 sk_sp<SkCachedData> init_provider(GrYUVProvider* provider, SkYUVPlanesCache::Info* yuvInfo, 24 void* planes[3]) { 25 sk_sp<SkCachedData> data; 26 data.reset(SkYUVPlanesCache::FindAndRef(provider->onGetID(), yuvInfo)); 27 28 if (data.get()) { 29 planes[0] = (void*)data->data(); 30 planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] * 31 yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); 32 planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] * 33 yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight); 34 } else { 35 // Fetch yuv plane sizes for memory allocation. 36 if (!provider->onQueryYUV8(&yuvInfo->fSizeInfo, &yuvInfo->fColorSpace)) { 37 return nullptr; 38 } 39 40 // Allocate the memory for YUV 41 size_t totalSize(0); 42 for (int i = 0; i < 3; i++) { 43 totalSize += yuvInfo->fSizeInfo.fWidthBytes[i] * yuvInfo->fSizeInfo.fSizes[i].fHeight; 44 } 45 data.reset(SkResourceCache::NewCachedData(totalSize)); 46 planes[0] = data->writable_data(); 47 planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] * 48 yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); 49 planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] * 50 yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight); 51 52 // Get the YUV planes. 53 if (!provider->onGetYUV8Planes(yuvInfo->fSizeInfo, planes)) { 54 return nullptr; 55 } 56 57 // Decoding is done, cache the resulting YUV planes 58 SkYUVPlanesCache::Add(provider->onGetID(), data.get(), yuvInfo); 59 } 60 return data; 61 } 62 63 sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc, 64 const SkColorSpace* srcColorSpace, 65 const SkColorSpace* dstColorSpace) { 66 SkYUVPlanesCache::Info yuvInfo; 67 void* planes[3]; 68 69 sk_sp<SkCachedData> dataStorage = init_provider(this, &yuvInfo, planes); 70 if (!dataStorage) { 71 return nullptr; 72 } 73 74 GrSurfaceDesc yuvDesc; 75 yuvDesc.fOrigin = kTopLeft_GrSurfaceOrigin; 76 yuvDesc.fConfig = kAlpha_8_GrPixelConfig; 77 sk_sp<GrSurfaceContext> yuvTextureContexts[3]; 78 for (int i = 0; i < 3; i++) { 79 yuvDesc.fWidth = yuvInfo.fSizeInfo.fSizes[i].fWidth; 80 yuvDesc.fHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight; 81 // TODO: why do we need this check? 82 SkBackingFit fit = 83 (yuvDesc.fWidth != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) || 84 (yuvDesc.fHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight) 85 ? SkBackingFit::kExact : SkBackingFit::kApprox; 86 87 yuvTextureContexts[i] = ctx->contextPriv().makeDeferredSurfaceContext(yuvDesc, 88 GrMipMapped::kNo, 89 fit, 90 SkBudgeted::kYes); 91 if (!yuvTextureContexts[i]) { 92 return nullptr; 93 } 94 95 const SkImageInfo ii = SkImageInfo::MakeA8(yuvDesc.fWidth, yuvDesc.fHeight); 96 if (!yuvTextureContexts[i]->writePixels(ii, planes[i], 97 yuvInfo.fSizeInfo.fWidthBytes[i], 0, 0)) { 98 return nullptr; 99 } 100 } 101 102 // We never want to perform color-space conversion during the decode 103 // TODO: investigate preallocating mip maps here 104 sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext( 105 SkBackingFit::kExact, 106 desc.fWidth, desc.fHeight, 107 desc.fConfig, nullptr, 108 desc.fSampleCnt, 109 GrMipMapped::kNo, 110 kTopLeft_GrSurfaceOrigin)); 111 if (!renderTargetContext) { 112 return nullptr; 113 } 114 115 GrPaint paint; 116 auto yuvToRgbProcessor = 117 GrYUVtoRGBEffect::Make(yuvTextureContexts[0]->asTextureProxyRef(), 118 yuvTextureContexts[1]->asTextureProxyRef(), 119 yuvTextureContexts[2]->asTextureProxyRef(), 120 yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false); 121 paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); 122 123 // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already 124 // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need 125 // to output the results of that math directly to the buffer that we will then consider sRGB. 126 // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step. 127 // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear, 128 // then let the HW convert Linear -> sRGB. 129 if (GrPixelConfigIsSRGB(desc.fConfig)) { 130 if (ctx->caps()->srgbWriteControl()) { 131 paint.setDisableOutputConversionToSRGB(true); 132 } else { 133 paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear, 134 GrSRGBEffect::Alpha::kOpaque)); 135 } 136 } 137 138 // If the caller expects the pixels in a different color space than the one from the image, 139 // apply a color conversion to do this. 140 std::unique_ptr<GrFragmentProcessor> colorConversionProcessor = 141 GrNonlinearColorSpaceXformEffect::Make(srcColorSpace, dstColorSpace); 142 if (colorConversionProcessor) { 143 paint.addColorFragmentProcessor(std::move(colorConversionProcessor)); 144 } 145 146 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 147 const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth, 148 yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); 149 150 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r); 151 152 return renderTargetContext->asTextureProxyRef(); 153 } 154