Home | History | Annotate | Download | only in gpu
      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