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