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 "GrPaint.h"
14 #include "GrRenderTargetContext.h"
15 #include "GrResourceProvider.h"
16 
17 namespace {
18     using MakeFPProc = sk_sp<GrFragmentProcessor> (*)(sk_sp<GrFragmentProcessor>,
19                                                       SkYUVColorSpace colorSpace);
20 };
21 
convert_proxy(sk_sp<GrTextureProxy> src,GrRenderTargetContext * dst,int dstW,int dstH,SkYUVColorSpace colorSpace,MakeFPProc proc)22 static bool convert_proxy(sk_sp<GrTextureProxy> src,
23                           GrRenderTargetContext* dst, int dstW, int dstH,
24                           SkYUVColorSpace colorSpace, MakeFPProc proc) {
25 
26     SkScalar xScale = SkIntToScalar(src->width()) / dstW;
27     SkScalar yScale = SkIntToScalar(src->height()) / dstH;
28     GrSamplerParams::FilterMode filter;
29     if (dstW == src->width() && dstW == src->height()) {
30         filter = GrSamplerParams::kNone_FilterMode;
31     } else {
32         filter = GrSamplerParams::kBilerp_FilterMode;
33     }
34 
35     GrResourceProvider* resourceProvider = dst->resourceProvider();
36 
37     sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(resourceProvider, std::move(src),
38                                                               nullptr,
39                                                               SkMatrix::MakeScale(xScale, yScale),
40                                                               filter));
41     if (!fp) {
42         return false;
43     }
44     fp = proc(std::move(fp), colorSpace);
45     if (!fp) {
46         return false;
47     }
48     GrPaint paint;
49     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
50     paint.addColorFragmentProcessor(std::move(fp));
51     dst->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
52                   SkRect::MakeIWH(dstW, dstH));
53     return true;
54 }
55 
GrTextureToYUVPlanes(GrContext * context,sk_sp<GrTextureProxy> proxy,const SkISize sizes[3],void * const planes[3],const size_t rowBytes[3],SkYUVColorSpace colorSpace)56 bool GrTextureToYUVPlanes(GrContext* context, sk_sp<GrTextureProxy> proxy,
57                           const SkISize sizes[3], void* const planes[3],
58                           const size_t rowBytes[3], SkYUVColorSpace colorSpace) {
59     if (!context) {
60         return false;
61     }
62 
63     {
64         // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/
65         // readbacks.
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;
71 
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]) {
76             yuvRenderTargetContext = context->makeRenderTargetContextWithFallback(
77                                                                            SkBackingFit::kApprox,
78                                                                            sizes[0].fWidth,
79                                                                            sizes[0].fHeight,
80                                                                            kRGBA_8888_GrPixelConfig,
81                                                                            nullptr);
82             if (!yuvRenderTargetContext) {
83                 return false;
84             }
85         } else {
86             yRenderTargetContext = context->makeRenderTargetContextWithFallback(
87                                                                              SkBackingFit::kApprox,
88                                                                              sizes[0].fWidth,
89                                                                              sizes[0].fHeight,
90                                                                              kAlpha_8_GrPixelConfig,
91                                                                              nullptr);
92             if (!yRenderTargetContext) {
93                 return false;
94             }
95             if (sizes[1] == sizes[2]) {
96                 // TODO: Add support for GL_RG when available.
97                 uvRenderTargetContext = context->makeRenderTargetContextWithFallback(
98                                                                            SkBackingFit::kApprox,
99                                                                            sizes[1].fWidth,
100                                                                            sizes[1].fHeight,
101                                                                            kRGBA_8888_GrPixelConfig,
102                                                                            nullptr);
103                 if (!uvRenderTargetContext) {
104                     return false;
105                 }
106             } else {
107                 uRenderTargetContext = context->makeRenderTargetContextWithFallback(
108                                                                              SkBackingFit::kApprox,
109                                                                              sizes[1].fWidth,
110                                                                              sizes[1].fHeight,
111                                                                              kAlpha_8_GrPixelConfig,
112                                                                              nullptr);
113                 vRenderTargetContext = context->makeRenderTargetContextWithFallback(
114                                                                              SkBackingFit::kApprox,
115                                                                              sizes[2].fWidth,
116                                                                              sizes[2].fHeight,
117                                                                              kAlpha_8_GrPixelConfig,
118                                                                              nullptr);
119                 if (!uRenderTargetContext || !vRenderTargetContext) {
120                     return false;
121                 }
122             }
123         }
124 
125         // Do all the draws before any readback.
126         if (yuvRenderTargetContext) {
127             if (!convert_proxy(std::move(proxy), yuvRenderTargetContext.get(),
128                                sizes[0].fWidth, sizes[0].fHeight,
129                                colorSpace, GrYUVEffect::MakeRGBToYUV)) {
130                 return false;
131             }
132         } else {
133             SkASSERT(yRenderTargetContext);
134             if (!convert_proxy(proxy, yRenderTargetContext.get(),
135                                sizes[0].fWidth, sizes[0].fHeight,
136                                colorSpace, GrYUVEffect::MakeRGBToY)) {
137                 return false;
138             }
139             if (uvRenderTargetContext) {
140                 if (!convert_proxy(std::move(proxy), uvRenderTargetContext.get(),
141                                    sizes[1].fWidth, sizes[1].fHeight,
142                                    colorSpace,  GrYUVEffect::MakeRGBToUV)) {
143                     return false;
144                 }
145             } else {
146                 SkASSERT(uRenderTargetContext && vRenderTargetContext);
147                 if (!convert_proxy(proxy, uRenderTargetContext.get(),
148                                    sizes[1].fWidth, sizes[1].fHeight,
149                                    colorSpace, GrYUVEffect::MakeRGBToU)) {
150                     return false;
151                 }
152                 if (!convert_proxy(std::move(proxy), vRenderTargetContext.get(),
153                                    sizes[2].fWidth, sizes[2].fHeight,
154                                    colorSpace, GrYUVEffect::MakeRGBToV)) {
155                     return false;
156                 }
157             }
158         }
159 
160         if (yuvRenderTargetContext) {
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);
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)) {
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 {
194             SkASSERT(yRenderTargetContext);
195 
196             SkImageInfo ii = SkImageInfo::MakeA8(sizes[0].fWidth, sizes[0].fHeight);
197             if (!yRenderTargetContext->readPixels(ii, planes[0], rowBytes[0], 0, 0)) {
198                 return false;
199             }
200 
201             if (uvRenderTargetContext) {
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);
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)) {
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 {
232                 SkASSERT(uRenderTargetContext && vRenderTargetContext);
233 
234                 ii = SkImageInfo::MakeA8(sizes[1].fWidth, sizes[1].fHeight);
235                 if (!uRenderTargetContext->readPixels(ii, planes[1], rowBytes[1], 0, 0)) {
236                     return false;
237                 }
238 
239                 ii = SkImageInfo::MakeA8(sizes[2].fWidth, sizes[2].fHeight);
240                 if (!vRenderTargetContext->readPixels(ii, planes[2], rowBytes[2], 0, 0)) {
241                     return false;
242                 }
243 
244                 return true;
245             }
246         }
247     }
248     return false;
249 }
250