bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 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" |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 13 | #include "GrPaint.h" |
Brian Osman | 32342f0 | 2017-03-04 08:12:46 -0500 | [diff] [blame] | 14 | #include "GrRenderTargetContext.h" |
| 15 | #include "GrResourceProvider.h" |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 16 | |
| 17 | namespace { |
bungeman | 06ca8ec | 2016-06-09 08:01:03 -0700 | [diff] [blame] | 18 | using MakeFPProc = sk_sp<GrFragmentProcessor> (*)(sk_sp<GrFragmentProcessor>, |
| 19 | SkYUVColorSpace colorSpace); |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 20 | }; |
| 21 | |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 22 | static bool convert_proxy(sk_sp<GrTextureProxy> src, |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 23 | GrRenderTargetContext* dst, int dstW, int dstH, |
| 24 | SkYUVColorSpace colorSpace, MakeFPProc proc) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 25 | |
Robert Phillips | 67c18d6 | 2017-01-20 12:44:06 -0500 | [diff] [blame] | 26 | SkScalar xScale = SkIntToScalar(src->width()) / dstW; |
| 27 | SkScalar yScale = SkIntToScalar(src->height()) / dstH; |
Brian Salomon | 514baff | 2016-11-17 15:17:07 -0500 | [diff] [blame] | 28 | GrSamplerParams::FilterMode filter; |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 29 | if (dstW == src->width() && dstW == src->height()) { |
Brian Salomon | 514baff | 2016-11-17 15:17:07 -0500 | [diff] [blame] | 30 | filter = GrSamplerParams::kNone_FilterMode; |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 31 | } else { |
Brian Salomon | 514baff | 2016-11-17 15:17:07 -0500 | [diff] [blame] | 32 | filter = GrSamplerParams::kBilerp_FilterMode; |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 33 | } |
| 34 | |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 35 | GrResourceProvider* resourceProvider = dst->resourceProvider(); |
| 36 | |
| 37 | sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(resourceProvider, std::move(src), |
| 38 | nullptr, |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 39 | SkMatrix::MakeScale(xScale, yScale), |
| 40 | filter)); |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 41 | if (!fp) { |
| 42 | return false; |
| 43 | } |
bungeman | 06ca8ec | 2016-06-09 08:01:03 -0700 | [diff] [blame] | 44 | fp = proc(std::move(fp), colorSpace); |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 45 | if (!fp) { |
| 46 | return false; |
| 47 | } |
| 48 | GrPaint paint; |
reed | 374772b | 2016-10-05 17:33:02 -0700 | [diff] [blame] | 49 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
bungeman | 06ca8ec | 2016-06-09 08:01:03 -0700 | [diff] [blame] | 50 | paint.addColorFragmentProcessor(std::move(fp)); |
Brian Salomon | 82f4431 | 2017-01-11 13:42:54 -0500 | [diff] [blame] | 51 | dst->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), |
| 52 | SkRect::MakeIWH(dstW, dstH)); |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 53 | return true; |
| 54 | } |
| 55 | |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 56 | bool GrTextureToYUVPlanes(GrContext* context, sk_sp<GrTextureProxy> proxy, |
| 57 | const SkISize sizes[3], void* const planes[3], |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 58 | const size_t rowBytes[3], SkYUVColorSpace colorSpace) { |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 59 | if (!context) { |
| 60 | return false; |
| 61 | } |
| 62 | |
| 63 | { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 64 | // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/ |
| 65 | // readbacks. |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 66 | sk_sp<GrRenderTargetContext> yuvRenderTargetContext; |
| 67 | sk_sp<GrRenderTargetContext> yRenderTargetContext; |
| 68 | sk_sp<GrRenderTargetContext> uvRenderTargetContext; |
| 69 | sk_sp<GrRenderTargetContext> uRenderTargetContext; |
| 70 | sk_sp<GrRenderTargetContext> vRenderTargetContext; |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 71 | |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 72 | // We issue draw(s) to convert from RGBA to Y, U, and V. All three planes may have different |
| 73 | // sizes however we optimize for two other cases - all planes are the same (1 draw to YUV), |
| 74 | // and U and V are the same but Y differs (2 draws, one for Y, one for UV). |
| 75 | if (sizes[0] == sizes[1] && sizes[1] == sizes[2]) { |
Robert Phillips | d83ec04 | 2017-04-10 11:10:22 -0400 | [diff] [blame^] | 76 | yuvRenderTargetContext = context->makeDeferredRenderTargetContextWithFallback( |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 77 | SkBackingFit::kApprox, |
| 78 | sizes[0].fWidth, |
| 79 | sizes[0].fHeight, |
| 80 | kRGBA_8888_GrPixelConfig, |
| 81 | nullptr); |
| 82 | if (!yuvRenderTargetContext) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 83 | return false; |
| 84 | } |
| 85 | } else { |
Robert Phillips | d83ec04 | 2017-04-10 11:10:22 -0400 | [diff] [blame^] | 86 | yRenderTargetContext = context->makeDeferredRenderTargetContextWithFallback( |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 87 | SkBackingFit::kApprox, |
| 88 | sizes[0].fWidth, |
| 89 | sizes[0].fHeight, |
| 90 | kAlpha_8_GrPixelConfig, |
| 91 | nullptr); |
| 92 | if (!yRenderTargetContext) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 93 | return false; |
| 94 | } |
| 95 | if (sizes[1] == sizes[2]) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 96 | // TODO: Add support for GL_RG when available. |
Robert Phillips | d83ec04 | 2017-04-10 11:10:22 -0400 | [diff] [blame^] | 97 | uvRenderTargetContext = context->makeDeferredRenderTargetContextWithFallback( |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 98 | SkBackingFit::kApprox, |
| 99 | sizes[1].fWidth, |
| 100 | sizes[1].fHeight, |
| 101 | kRGBA_8888_GrPixelConfig, |
| 102 | nullptr); |
| 103 | if (!uvRenderTargetContext) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 104 | return false; |
| 105 | } |
| 106 | } else { |
Robert Phillips | d83ec04 | 2017-04-10 11:10:22 -0400 | [diff] [blame^] | 107 | uRenderTargetContext = context->makeDeferredRenderTargetContextWithFallback( |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 108 | SkBackingFit::kApprox, |
| 109 | sizes[1].fWidth, |
| 110 | sizes[1].fHeight, |
| 111 | kAlpha_8_GrPixelConfig, |
| 112 | nullptr); |
Robert Phillips | d83ec04 | 2017-04-10 11:10:22 -0400 | [diff] [blame^] | 113 | vRenderTargetContext = context->makeDeferredRenderTargetContextWithFallback( |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 114 | SkBackingFit::kApprox, |
| 115 | sizes[2].fWidth, |
| 116 | sizes[2].fHeight, |
| 117 | kAlpha_8_GrPixelConfig, |
| 118 | nullptr); |
| 119 | if (!uRenderTargetContext || !vRenderTargetContext) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 120 | return false; |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // Do all the draws before any readback. |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 126 | if (yuvRenderTargetContext) { |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 127 | if (!convert_proxy(std::move(proxy), yuvRenderTargetContext.get(), |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 128 | sizes[0].fWidth, sizes[0].fHeight, |
| 129 | colorSpace, GrYUVEffect::MakeRGBToYUV)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 130 | return false; |
| 131 | } |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 132 | } else { |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 133 | SkASSERT(yRenderTargetContext); |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 134 | if (!convert_proxy(proxy, yRenderTargetContext.get(), |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 135 | sizes[0].fWidth, sizes[0].fHeight, |
| 136 | colorSpace, GrYUVEffect::MakeRGBToY)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 137 | return false; |
| 138 | } |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 139 | if (uvRenderTargetContext) { |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 140 | if (!convert_proxy(std::move(proxy), uvRenderTargetContext.get(), |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 141 | sizes[1].fWidth, sizes[1].fHeight, |
| 142 | colorSpace, GrYUVEffect::MakeRGBToUV)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 143 | return false; |
| 144 | } |
| 145 | } else { |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 146 | SkASSERT(uRenderTargetContext && vRenderTargetContext); |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 147 | if (!convert_proxy(proxy, uRenderTargetContext.get(), |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 148 | sizes[1].fWidth, sizes[1].fHeight, |
| 149 | colorSpace, GrYUVEffect::MakeRGBToU)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 150 | return false; |
| 151 | } |
Robert Phillips | 296b1cc | 2017-03-15 10:42:12 -0400 | [diff] [blame] | 152 | if (!convert_proxy(std::move(proxy), vRenderTargetContext.get(), |
Robert Phillips | 538f1a3 | 2017-03-08 14:32:55 -0500 | [diff] [blame] | 153 | sizes[2].fWidth, sizes[2].fHeight, |
| 154 | colorSpace, GrYUVEffect::MakeRGBToV)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 155 | return false; |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 160 | if (yuvRenderTargetContext) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 161 | SkASSERT(sizes[0] == sizes[1] && sizes[1] == sizes[2]); |
| 162 | SkISize yuvSize = sizes[0]; |
| 163 | // We have no kRGB_888 pixel format, so readback rgba and then copy three channels. |
| 164 | SkAutoSTMalloc<128 * 128, uint32_t> tempYUV(yuvSize.fWidth * yuvSize.fHeight); |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 165 | |
| 166 | const SkImageInfo ii = SkImageInfo::Make(yuvSize.fWidth, yuvSize.fHeight, |
| 167 | kRGBA_8888_SkColorType, kOpaque_SkAlphaType); |
| 168 | if (!yuvRenderTargetContext->readPixels(ii, tempYUV.get(), 0, 0, 0)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 169 | return false; |
| 170 | } |
| 171 | size_t yRowBytes = rowBytes[0] ? rowBytes[0] : yuvSize.fWidth; |
| 172 | size_t uRowBytes = rowBytes[1] ? rowBytes[1] : yuvSize.fWidth; |
| 173 | size_t vRowBytes = rowBytes[2] ? rowBytes[2] : yuvSize.fWidth; |
| 174 | if (yRowBytes < (size_t)yuvSize.fWidth || uRowBytes < (size_t)yuvSize.fWidth || |
| 175 | vRowBytes < (size_t)yuvSize.fWidth) { |
| 176 | return false; |
| 177 | } |
| 178 | for (int j = 0; j < yuvSize.fHeight; ++j) { |
| 179 | for (int i = 0; i < yuvSize.fWidth; ++i) { |
| 180 | // These writes could surely be made more efficient. |
| 181 | uint32_t y = GrColorUnpackR(tempYUV.get()[j * yuvSize.fWidth + i]); |
| 182 | uint32_t u = GrColorUnpackG(tempYUV.get()[j * yuvSize.fWidth + i]); |
| 183 | uint32_t v = GrColorUnpackB(tempYUV.get()[j * yuvSize.fWidth + i]); |
| 184 | uint8_t* yLoc = ((uint8_t*)planes[0]) + j * yRowBytes + i; |
| 185 | uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i; |
| 186 | uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i; |
| 187 | *yLoc = y; |
| 188 | *uLoc = u; |
| 189 | *vLoc = v; |
| 190 | } |
| 191 | } |
| 192 | return true; |
| 193 | } else { |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 194 | SkASSERT(yRenderTargetContext); |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 195 | |
| 196 | SkImageInfo ii = SkImageInfo::MakeA8(sizes[0].fWidth, sizes[0].fHeight); |
| 197 | if (!yRenderTargetContext->readPixels(ii, planes[0], rowBytes[0], 0, 0)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 198 | return false; |
| 199 | } |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 200 | |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 201 | if (uvRenderTargetContext) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 202 | SkASSERT(sizes[1].fWidth == sizes[2].fWidth); |
| 203 | SkISize uvSize = sizes[1]; |
| 204 | // We have no kRG_88 pixel format, so readback rgba and then copy two channels. |
| 205 | SkAutoSTMalloc<128 * 128, uint32_t> tempUV(uvSize.fWidth * uvSize.fHeight); |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 206 | |
| 207 | ii = SkImageInfo::Make(uvSize.fWidth, uvSize.fHeight, |
| 208 | kRGBA_8888_SkColorType, kOpaque_SkAlphaType); |
| 209 | |
| 210 | if (!uvRenderTargetContext->readPixels(ii, tempUV.get(), 0, 0, 0)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 211 | return false; |
| 212 | } |
| 213 | |
| 214 | size_t uRowBytes = rowBytes[1] ? rowBytes[1] : uvSize.fWidth; |
| 215 | size_t vRowBytes = rowBytes[2] ? rowBytes[2] : uvSize.fWidth; |
| 216 | if (uRowBytes < (size_t)uvSize.fWidth || vRowBytes < (size_t)uvSize.fWidth) { |
| 217 | return false; |
| 218 | } |
| 219 | for (int j = 0; j < uvSize.fHeight; ++j) { |
| 220 | for (int i = 0; i < uvSize.fWidth; ++i) { |
| 221 | // These writes could surely be made more efficient. |
| 222 | uint32_t u = GrColorUnpackR(tempUV.get()[j * uvSize.fWidth + i]); |
| 223 | uint32_t v = GrColorUnpackG(tempUV.get()[j * uvSize.fWidth + i]); |
| 224 | uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i; |
| 225 | uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i; |
| 226 | *uLoc = u; |
| 227 | *vLoc = v; |
| 228 | } |
| 229 | } |
| 230 | return true; |
| 231 | } else { |
Brian Osman | 1105224 | 2016-10-27 14:47:55 -0400 | [diff] [blame] | 232 | SkASSERT(uRenderTargetContext && vRenderTargetContext); |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 233 | |
| 234 | ii = SkImageInfo::MakeA8(sizes[1].fWidth, sizes[1].fHeight); |
| 235 | if (!uRenderTargetContext->readPixels(ii, planes[1], rowBytes[1], 0, 0)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 236 | return false; |
| 237 | } |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 238 | |
| 239 | ii = SkImageInfo::MakeA8(sizes[2].fWidth, sizes[2].fHeight); |
| 240 | if (!vRenderTargetContext->readPixels(ii, planes[2], rowBytes[2], 0, 0)) { |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 241 | return false; |
| 242 | } |
Robert Phillips | 276be05 | 2017-01-13 14:55:50 -0500 | [diff] [blame] | 243 | |
bsalomon | f267c1e | 2016-02-01 13:16:14 -0800 | [diff] [blame] | 244 | return true; |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | return false; |
| 249 | } |