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 17namespace { 18 using CreateFPProc = const GrFragmentProcessor* (*)(const GrFragmentProcessor*, 19 SkYUVColorSpace colorSpace); 20}; 21 22static 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 50bool 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