143fe6185c5043247c47daa450b015e26d86728fereed/*
243fe6185c5043247c47daa450b015e26d86728fereed * Copyright 2015 Google Inc.
343fe6185c5043247c47daa450b015e26d86728fereed *
443fe6185c5043247c47daa450b015e26d86728fereed * Use of this source code is governed by a BSD-style license that can be
543fe6185c5043247c47daa450b015e26d86728fereed * found in the LICENSE file.
643fe6185c5043247c47daa450b015e26d86728fereed */
743fe6185c5043247c47daa450b015e26d86728fereed
8c65aec97619682b2c0191554f44ddf35f618a94dBrian Salomon#include "GrYUVProvider.h"
9c65aec97619682b2c0191554f44ddf35f618a94dBrian Salomon#include "GrClip.h"
1043fe6185c5043247c47daa450b015e26d86728fereed#include "GrContext.h"
11bc7a4fb06780f9829b4b21470fe6f0503d2297cdRobert Phillips#include "GrContextPriv.h"
12fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel#include "GrProxyProvider.h"
131105224f9701e57ec5ce0354d6a380b664f5c638Brian Osman#include "GrRenderTargetContext.h"
14bc7a4fb06780f9829b4b21470fe6f0503d2297cdRobert Phillips#include "GrTextureProxy.h"
1595e3c058ef633782f7549e9e1c2727d60dbc8ee5Hal Canary#include "SkAutoMalloc.h"
1643fe6185c5043247c47daa450b015e26d86728fereed#include "SkCachedData.h"
1743fe6185c5043247c47daa450b015e26d86728fereed#include "SkRefCnt.h"
1843fe6185c5043247c47daa450b015e26d86728fereed#include "SkResourceCache.h"
1943fe6185c5043247c47daa450b015e26d86728fereed#include "SkYUVPlanesCache.h"
2077e966647f6b495fe44772086c709528c711fc6eChristopher Cameron#include "effects/GrNonlinearColorSpaceXformEffect.h"
21c65aec97619682b2c0191554f44ddf35f618a94dBrian Salomon#include "effects/GrSRGBEffect.h"
227461a4abe06ffdfbb62a479a71b14895a637bba9Ethan Nicholas#include "effects/GrYUVtoRGBEffect.h"
2343fe6185c5043247c47daa450b015e26d86728fereed
241445da6c03c7ce6a3039bd63498e1179a320722fGreg Danielsk_sp<SkCachedData> init_provider(GrYUVProvider* provider, SkYUVPlanesCache::Info* yuvInfo,
251445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel                                  void* planes[3]) {
261445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel    sk_sp<SkCachedData> data;
271445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel    data.reset(SkYUVPlanesCache::FindAndRef(provider->onGetID(), yuvInfo));
2843fe6185c5043247c47daa450b015e26d86728fereed
291445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel    if (data.get()) {
301445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel        planes[0] = (void*)data->data();
314984c3c95f18eda44492a2126c9958e447f2cca8msarett        planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
324984c3c95f18eda44492a2126c9958e447f2cca8msarett                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
334984c3c95f18eda44492a2126c9958e447f2cca8msarett        planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
344984c3c95f18eda44492a2126c9958e447f2cca8msarett                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
3543fe6185c5043247c47daa450b015e26d86728fereed    } else {
364984c3c95f18eda44492a2126c9958e447f2cca8msarett        // Fetch yuv plane sizes for memory allocation.
374984c3c95f18eda44492a2126c9958e447f2cca8msarett        if (!provider->onQueryYUV8(&yuvInfo->fSizeInfo, &yuvInfo->fColorSpace)) {
381445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel            return nullptr;
3943fe6185c5043247c47daa450b015e26d86728fereed        }
4043fe6185c5043247c47daa450b015e26d86728fereed
4143fe6185c5043247c47daa450b015e26d86728fereed        // Allocate the memory for YUV
4243fe6185c5043247c47daa450b015e26d86728fereed        size_t totalSize(0);
434984c3c95f18eda44492a2126c9958e447f2cca8msarett        for (int i = 0; i < 3; i++) {
444984c3c95f18eda44492a2126c9958e447f2cca8msarett            totalSize += yuvInfo->fSizeInfo.fWidthBytes[i] * yuvInfo->fSizeInfo.fSizes[i].fHeight;
4543fe6185c5043247c47daa450b015e26d86728fereed        }
461445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel        data.reset(SkResourceCache::NewCachedData(totalSize));
471445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel        planes[0] = data->writable_data();
484984c3c95f18eda44492a2126c9958e447f2cca8msarett        planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
494984c3c95f18eda44492a2126c9958e447f2cca8msarett                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
504984c3c95f18eda44492a2126c9958e447f2cca8msarett        planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
514984c3c95f18eda44492a2126c9958e447f2cca8msarett                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
5243fe6185c5043247c47daa450b015e26d86728fereed
534984c3c95f18eda44492a2126c9958e447f2cca8msarett        // Get the YUV planes.
544984c3c95f18eda44492a2126c9958e447f2cca8msarett        if (!provider->onGetYUV8Planes(yuvInfo->fSizeInfo, planes)) {
551445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel            return nullptr;
5643fe6185c5043247c47daa450b015e26d86728fereed        }
5743fe6185c5043247c47daa450b015e26d86728fereed
581445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel        // Decoding is done, cache the resulting YUV planes
591445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel        SkYUVPlanesCache::Add(provider->onGetID(), data.get(), yuvInfo);
6043fe6185c5043247c47daa450b015e26d86728fereed    }
611445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel    return data;
6243fe6185c5043247c47daa450b015e26d86728fereed}
6343fe6185c5043247c47daa450b015e26d86728fereed
64fb3abcd8c335132b6ad8434a171516102bbf4495Greg Danielvoid GrYUVProvider::YUVGen_DataReleaseProc(const void*, void* data) {
65fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel    SkCachedData* cachedData = static_cast<SkCachedData*>(data);
66fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel    SkASSERT(cachedData);
67fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel    cachedData->unref();
68fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel}
69fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel
7077e966647f6b495fe44772086c709528c711fc6eChristopher Cameronsk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc,
7177e966647f6b495fe44772086c709528c711fc6eChristopher Cameron                                                       const SkColorSpace* srcColorSpace,
7277e966647f6b495fe44772086c709528c711fc6eChristopher Cameron                                                       const SkColorSpace* dstColorSpace) {
7343fe6185c5043247c47daa450b015e26d86728fereed    SkYUVPlanesCache::Info yuvInfo;
7443fe6185c5043247c47daa450b015e26d86728fereed    void* planes[3];
751445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel
761445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel    sk_sp<SkCachedData>  dataStorage = init_provider(this, &yuvInfo, planes);
771445da6c03c7ce6a3039bd63498e1179a320722fGreg Daniel    if (!dataStorage) {
7843fe6185c5043247c47daa450b015e26d86728fereed        return nullptr;
7943fe6185c5043247c47daa450b015e26d86728fereed    }
8043fe6185c5043247c47daa450b015e26d86728fereed
81fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel    sk_sp<GrTextureProxy> yuvTextureProxies[3];
824984c3c95f18eda44492a2126c9958e447f2cca8msarett    for (int i = 0; i < 3; i++) {
83fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        int componentWidth  = yuvInfo.fSizeInfo.fSizes[i].fWidth;
84fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        int componentHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight;
85fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // If the sizes of the components are not all the same we choose to create exact-match
86fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // textures for the smaller onces rather than add a texture domain to the draw.
87fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // TODO: revisit this decision to imporve texture reuse?
88bc7a4fb06780f9829b4b21470fe6f0503d2297cdRobert Phillips        SkBackingFit fit =
89fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel                (componentWidth  != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) ||
90fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel                (componentHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight)
91bc7a4fb06780f9829b4b21470fe6f0503d2297cdRobert Phillips                    ? SkBackingFit::kExact : SkBackingFit::kApprox;
92bc7a4fb06780f9829b4b21470fe6f0503d2297cdRobert Phillips
93fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight);
94fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        SkPixmap pixmap(imageInfo, planes[i], yuvInfo.fSizeInfo.fWidthBytes[i]);
95fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        SkCachedData* dataStoragePtr = dataStorage.get();
96fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call
97fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // the YUVGen_DataReleaseProc which will release this ref.
98fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the
99fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the
100fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // life time of the proxy and not just upload. For non-DDL draws we should look into
101fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        // releasing this SkImage after uploads (by deleting the lambda after instantiation).
102fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        dataStoragePtr->ref();
103fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc,
104fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel                                                          dataStoragePtr);
105bc7a4fb06780f9829b4b21470fe6f0503d2297cdRobert Phillips
106fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        auto proxyProvider = ctx->contextPriv().proxyProvider();
107fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel        yuvTextureProxies[i] = proxyProvider->createTextureProxy(yuvImage, kNone_GrSurfaceFlags,
108fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel                                                                 kTopLeft_GrSurfaceOrigin,
109bdecacfbe47bc7211336bb847bb33c00ef85ea3eBrian Salomon                                                                 1, SkBudgeted::kYes, fit);
11043fe6185c5043247c47daa450b015e26d86728fereed    }
11143fe6185c5043247c47daa450b015e26d86728fereed
112366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon    // We never want to perform color-space conversion during the decode. However, if the proxy
113366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon    // config is sRGB then we must use a sRGB color space.
114366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon    sk_sp<SkColorSpace> colorSpace;
115366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon    if (GrPixelConfigIsSRGB(desc.fConfig)) {
116366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon        colorSpace = SkColorSpace::MakeSRGB();
117366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon    }
118e1da1d9a7dfa6c9ebdcbd2845acebd045edd2a6fGreg Daniel    // TODO: investigate preallocating mip maps here
119dd3b3f41829d32d7eaf3eb4903570d49c2ba9ff8Robert Phillips    sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext(
120366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon            SkBackingFit::kExact, desc.fWidth, desc.fHeight, desc.fConfig, std::move(colorSpace),
121366093f2124c38fa5c590c9ed2d1811817fed8eeBrian Salomon            desc.fSampleCnt, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin));
1221105224f9701e57ec5ce0354d6a380b664f5c638Brian Osman    if (!renderTargetContext) {
12343fe6185c5043247c47daa450b015e26d86728fereed        return nullptr;
12443fe6185c5043247c47daa450b015e26d86728fereed    }
12543fe6185c5043247c47daa450b015e26d86728fereed
12643fe6185c5043247c47daa450b015e26d86728fereed    GrPaint paint;
127aff329b8e9b239bca1d93b13a914fbef45ccf7feBrian Salomon    auto yuvToRgbProcessor =
128fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel            GrYUVtoRGBEffect::Make(std::move(yuvTextureProxies[0]),
129fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel                                   std::move(yuvTextureProxies[1]),
130fb3abcd8c335132b6ad8434a171516102bbf4495Greg Daniel                                   std::move(yuvTextureProxies[2]),
1317461a4abe06ffdfbb62a479a71b14895a637bba9Ethan Nicholas                                   yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false);
13206ca8ec87cf6fab57cadd043a5ac18c4154a4129bungeman    paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
133717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman
134717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already
135717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need
136717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    // to output the results of that math directly to the buffer that we will then consider sRGB.
137717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step.
138717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear,
139717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    // then let the HW convert Linear -> sRGB.
140717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    if (GrPixelConfigIsSRGB(desc.fConfig)) {
141717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman        if (ctx->caps()->srgbWriteControl()) {
142717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman            paint.setDisableOutputConversionToSRGB(true);
143717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman        } else {
14498308fb081687970278dce2a7631005f9cb424a8Mike Reed            paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear,
14598308fb081687970278dce2a7631005f9cb424a8Mike Reed                                                               GrSRGBEffect::Alpha::kOpaque));
146717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman        }
147717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman    }
148717abfd2a9edcf57d07c42a03ed9f4c0062cd703brianosman
14977e966647f6b495fe44772086c709528c711fc6eChristopher Cameron    // If the caller expects the pixels in a different color space than the one from the image,
15077e966647f6b495fe44772086c709528c711fc6eChristopher Cameron    // apply a color conversion to do this.
151aff329b8e9b239bca1d93b13a914fbef45ccf7feBrian Salomon    std::unique_ptr<GrFragmentProcessor> colorConversionProcessor =
15277e966647f6b495fe44772086c709528c711fc6eChristopher Cameron            GrNonlinearColorSpaceXformEffect::Make(srcColorSpace, dstColorSpace);
15377e966647f6b495fe44772086c709528c711fc6eChristopher Cameron    if (colorConversionProcessor) {
154aff329b8e9b239bca1d93b13a914fbef45ccf7feBrian Salomon        paint.addColorFragmentProcessor(std::move(colorConversionProcessor));
15577e966647f6b495fe44772086c709528c711fc6eChristopher Cameron    }
15677e966647f6b495fe44772086c709528c711fc6eChristopher Cameron
157374772bd61951f01bf84fe17bf53d8867681c9aereed    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
1584984c3c95f18eda44492a2126c9958e447f2cca8msarett    const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
15967c18d6b5188a0497f6912a73d964c763d2f8f84Robert Phillips                                     yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
16043fe6185c5043247c47daa450b015e26d86728fereed
16182f44319159bb98dcacdbbec7ea643dde5ed024bBrian Salomon    renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
16243fe6185c5043247c47daa450b015e26d86728fereed
163538f1a36e3cd0327ee2639693143179ec0359151Robert Phillips    return renderTargetContext->asTextureProxyRef();
16443fe6185c5043247c47daa450b015e26d86728fereed}
165