Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2016 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 "GrTextureToYUVPlanes.h"
      9 #include "effects/GrSimpleTextureEffect.h"
     10 #include "effects/GrYUVEffect.h"
     11 #include "GrClip.h"
     12 #include "GrContext.h"
     13 #include "GrDrawContext.h"
     14 #include "GrPaint.h"
     15 #include "GrTextureProvider.h"
     16 
     17 namespace {
     18     using CreateFPProc = const GrFragmentProcessor* (*)(const GrFragmentProcessor*,
     19                                                         SkYUVColorSpace colorSpace);
     20 };
     21 
     22 static bool convert_texture(GrTexture* src, GrDrawContext* dst, int dstW, int dstH,
     23                             SkYUVColorSpace colorSpace, CreateFPProc proc) {
     24 
     25     SkScalar xScale = SkIntToScalar(src->width()) / dstW / src->width();
     26     SkScalar yScale = SkIntToScalar(src->height()) / dstH / src->height();
     27     GrTextureParams::FilterMode filter;
     28     if (dstW == src->width() && dstW == src->height()) {
     29         filter = GrTextureParams::kNone_FilterMode;
     30     } else {
     31         filter = GrTextureParams::kBilerp_FilterMode;
     32     }
     33 
     34     SkAutoTUnref<const GrFragmentProcessor> fp(
     35             GrSimpleTextureEffect::Create(src, SkMatrix::MakeScale(xScale, yScale), filter));
     36     if (!fp) {
     37         return false;
     38     }
     39     fp.reset(proc(fp, colorSpace));
     40     if (!fp) {
     41         return false;
     42     }
     43     GrPaint paint;
     44     paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     45     paint.addColorFragmentProcessor(fp);
     46     dst->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), SkRect::MakeIWH(dstW, dstH));
     47     return true;
     48 }
     49 
     50 bool GrTextureToYUVPlanes(GrTexture* texture, const SkISize sizes[3], void* const planes[3],
     51                           const size_t rowBytes[3], SkYUVColorSpace colorSpace) {
     52     if (GrContext* context = texture->getContext()) {
     53         // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/
     54         // readbacks.
     55         SkAutoTUnref<GrTexture> yuvTex;
     56         SkAutoTUnref<GrTexture> yTex;
     57         SkAutoTUnref<GrTexture> uvTex;
     58         SkAutoTUnref<GrTexture> uTex;
     59         SkAutoTUnref<GrTexture> vTex;
     60 
     61         GrPixelConfig singleChannelPixelConfig;
     62         if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
     63             singleChannelPixelConfig = kAlpha_8_GrPixelConfig;
     64         } else {
     65             singleChannelPixelConfig = kRGBA_8888_GrPixelConfig;
     66         }
     67 
     68         // We issue draw(s) to convert from RGBA to Y, U, and V. All three planes may have different
     69         // sizes however we optimize for two other cases - all planes are the same (1 draw to YUV),
     70         // and U and V are the same but Y differs (2 draws, one for Y, one for UV).
     71         if (sizes[0] == sizes[1] && sizes[1] == sizes[2]) {
     72             GrSurfaceDesc yuvDesc;
     73             yuvDesc.fConfig = kRGBA_8888_GrPixelConfig;
     74             yuvDesc.fFlags = kRenderTarget_GrSurfaceFlag;
     75             yuvDesc.fWidth = sizes[0].fWidth;
     76             yuvDesc.fHeight = sizes[0].fHeight;
     77             yuvTex.reset(context->textureProvider()->createApproxTexture(yuvDesc));
     78             if (!yuvTex) {
     79                 return false;
     80             }
     81         } else {
     82             GrSurfaceDesc yDesc;
     83             yDesc.fConfig = singleChannelPixelConfig;
     84             yDesc.fFlags = kRenderTarget_GrSurfaceFlag;
     85             yDesc.fWidth = sizes[0].fWidth;
     86             yDesc.fHeight = sizes[0].fHeight;
     87             yTex.reset(context->textureProvider()->createApproxTexture(yDesc));
     88             if (!yTex) {
     89                 return false;
     90             }
     91             if (sizes[1] == sizes[2]) {
     92                 GrSurfaceDesc uvDesc;
     93                 // TODO: Add support for GL_RG when available.
     94                 uvDesc.fConfig = kRGBA_8888_GrPixelConfig;
     95                 uvDesc.fFlags = kRenderTarget_GrSurfaceFlag;
     96                 uvDesc.fWidth = sizes[1].fWidth;
     97                 uvDesc.fHeight = sizes[1].fHeight;
     98                 uvTex.reset(context->textureProvider()->createApproxTexture(uvDesc));
     99                 if (!uvTex) {
    100                     return false;
    101                 }
    102             } else {
    103                 GrSurfaceDesc uvDesc;
    104                 uvDesc.fConfig = singleChannelPixelConfig;
    105                 uvDesc.fFlags = kRenderTarget_GrSurfaceFlag;
    106                 uvDesc.fWidth = sizes[1].fWidth;
    107                 uvDesc.fHeight = sizes[1].fHeight;
    108                 uTex.reset(context->textureProvider()->createApproxTexture(uvDesc));
    109                 uvDesc.fWidth = sizes[2].fWidth;
    110                 uvDesc.fHeight = sizes[2].fHeight;
    111                 vTex.reset(context->textureProvider()->createApproxTexture(uvDesc));
    112                 if (!uTex || !vTex) {
    113                     return false;
    114                 }
    115             }
    116         }
    117 
    118         // Do all the draws before any readback.
    119         if (yuvTex) {
    120             SkAutoTUnref<GrDrawContext> dc(context->drawContext(yuvTex->asRenderTarget()));
    121             if (!dc) {
    122                 return false;
    123             }
    124             if (!convert_texture(texture, dc, sizes[0].fWidth, sizes[0].fHeight, colorSpace,
    125                                  GrYUVEffect::CreateRGBToYUV)) {
    126                 return false;
    127             }
    128 
    129         } else {
    130             SkASSERT(yTex);
    131             SkAutoTUnref<GrDrawContext> dc(context->drawContext(yTex->asRenderTarget()));
    132             if (!dc) {
    133                 return false;
    134             }
    135             if (!convert_texture(texture, dc, sizes[0].fWidth, sizes[0].fHeight, colorSpace,
    136                                  GrYUVEffect::CreateRGBToY)) {
    137                 return false;
    138             }
    139             if (uvTex) {
    140                 dc.reset(context->drawContext(uvTex->asRenderTarget()));
    141                 if (!dc) {
    142                     return false;
    143                 }
    144                 if (!convert_texture(texture, dc, sizes[1].fWidth, sizes[1].fHeight,
    145                                      colorSpace,  GrYUVEffect::CreateRGBToUV)) {
    146                     return false;
    147                 }
    148             } else {
    149                 SkASSERT(uTex && vTex);
    150                 dc.reset(context->drawContext(uTex->asRenderTarget()));
    151                 if (!dc) {
    152                     return false;
    153                 }
    154                 if (!convert_texture(texture, dc, sizes[1].fWidth, sizes[1].fHeight,
    155                                      colorSpace, GrYUVEffect::CreateRGBToU)) {
    156                     return false;
    157                 }
    158                 dc.reset(context->drawContext(vTex->asRenderTarget()));
    159                 if (!dc) {
    160                     return false;
    161                 }
    162                 if (!convert_texture(texture, dc, sizes[2].fWidth, sizes[2].fHeight,
    163                                      colorSpace, GrYUVEffect::CreateRGBToV)) {
    164                     return false;
    165                 }
    166             }
    167         }
    168 
    169         if (yuvTex) {
    170             SkASSERT(sizes[0] == sizes[1] && sizes[1] == sizes[2]);
    171             SkISize yuvSize = sizes[0];
    172             // We have no kRGB_888 pixel format, so readback rgba and then copy three channels.
    173             SkAutoSTMalloc<128 * 128, uint32_t> tempYUV(yuvSize.fWidth * yuvSize.fHeight);
    174             if (!yuvTex->readPixels(0, 0, yuvSize.fWidth, yuvSize.fHeight,
    175                                     kRGBA_8888_GrPixelConfig, tempYUV.get(), 0)) {
    176                 return false;
    177             }
    178             size_t yRowBytes = rowBytes[0] ? rowBytes[0] : yuvSize.fWidth;
    179             size_t uRowBytes = rowBytes[1] ? rowBytes[1] : yuvSize.fWidth;
    180             size_t vRowBytes = rowBytes[2] ? rowBytes[2] : yuvSize.fWidth;
    181             if (yRowBytes < (size_t)yuvSize.fWidth || uRowBytes < (size_t)yuvSize.fWidth ||
    182                 vRowBytes < (size_t)yuvSize.fWidth) {
    183                 return false;
    184             }
    185             for (int j = 0; j < yuvSize.fHeight; ++j) {
    186                 for (int i = 0; i < yuvSize.fWidth; ++i) {
    187                     // These writes could surely be made more efficient.
    188                     uint32_t y = GrColorUnpackR(tempYUV.get()[j * yuvSize.fWidth + i]);
    189                     uint32_t u = GrColorUnpackG(tempYUV.get()[j * yuvSize.fWidth + i]);
    190                     uint32_t v = GrColorUnpackB(tempYUV.get()[j * yuvSize.fWidth + i]);
    191                     uint8_t* yLoc = ((uint8_t*)planes[0]) + j * yRowBytes + i;
    192                     uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
    193                     uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
    194                     *yLoc = y;
    195                     *uLoc = u;
    196                     *vLoc = v;
    197                 }
    198             }
    199             return true;
    200         } else {
    201             SkASSERT(yTex);
    202             if (!yTex->readPixels(0, 0, sizes[0].fWidth, sizes[0].fHeight,
    203                                   kAlpha_8_GrPixelConfig, planes[0], rowBytes[0])) {
    204                 return false;
    205             }
    206             if (uvTex) {
    207                 SkASSERT(sizes[1].fWidth == sizes[2].fWidth);
    208                 SkISize uvSize = sizes[1];
    209                 // We have no kRG_88 pixel format, so readback rgba and then copy two channels.
    210                 SkAutoSTMalloc<128 * 128, uint32_t> tempUV(uvSize.fWidth * uvSize.fHeight);
    211                 if (!uvTex->readPixels(0, 0, uvSize.fWidth, uvSize.fHeight,
    212                                        kRGBA_8888_GrPixelConfig, tempUV.get(), 0)) {
    213                     return false;
    214                 }
    215 
    216                 size_t uRowBytes = rowBytes[1] ? rowBytes[1] : uvSize.fWidth;
    217                 size_t vRowBytes = rowBytes[2] ? rowBytes[2] : uvSize.fWidth;
    218                 if (uRowBytes < (size_t)uvSize.fWidth || vRowBytes < (size_t)uvSize.fWidth) {
    219                     return false;
    220                 }
    221                 for (int j = 0; j < uvSize.fHeight; ++j) {
    222                     for (int i = 0; i < uvSize.fWidth; ++i) {
    223                         // These writes could surely be made more efficient.
    224                         uint32_t u = GrColorUnpackR(tempUV.get()[j * uvSize.fWidth + i]);
    225                         uint32_t v = GrColorUnpackG(tempUV.get()[j * uvSize.fWidth + i]);
    226                         uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
    227                         uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
    228                         *uLoc = u;
    229                         *vLoc = v;
    230                     }
    231                 }
    232                 return true;
    233             } else {
    234                 SkASSERT(uTex && vTex);
    235                 if (!uTex->readPixels(0, 0, sizes[1].fWidth, sizes[1].fHeight,
    236                                       kAlpha_8_GrPixelConfig, planes[1], rowBytes[1])) {
    237                     return false;
    238                 }
    239                 if (!vTex->readPixels(0, 0, sizes[2].fWidth, sizes[2].fHeight,
    240                                       kAlpha_8_GrPixelConfig, planes[2], rowBytes[2])) {
    241                     return false;
    242                 }
    243                 return true;
    244             }
    245         }
    246     }
    247     return false;
    248 }
    249