• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 "tools/gpu/YUVUtils.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkColorPriv.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkSurface.h"
15 #include "include/gpu/GrRecordingContext.h"
16 #include "include/gpu/GrYUVABackendTextures.h"
17 #include "src/codec/SkCodecImageGenerator.h"
18 #include "src/core/SkYUVAInfoLocation.h"
19 #include "src/core/SkYUVMath.h"
20 #include "src/gpu/GrDirectContextPriv.h"
21 #include "src/gpu/GrRecordingContextPriv.h"
22 #include "tools/gpu/ManagedBackendTexture.h"
23 
24 namespace {
25 
convert_yuva_to_rgba(const float mtx[20],uint8_t yuva[4])26 static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
27     uint8_t y = yuva[0];
28     uint8_t u = yuva[1];
29     uint8_t v = yuva[2];
30     uint8_t a = yuva[3];
31 
32     uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
33     uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
34     uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
35 
36     return SkPremultiplyARGBInline(a, r, g, b);
37 }
38 
look_up(SkPoint normPt,const SkPixmap & pmap,SkColorChannel channel)39 static uint8_t look_up(SkPoint normPt, const SkPixmap& pmap, SkColorChannel channel) {
40     SkASSERT(normPt.x() > 0 && normPt.x() < 1.0f);
41     SkASSERT(normPt.y() > 0 && normPt.y() < 1.0f);
42     int x = SkScalarFloorToInt(normPt.x() * pmap.width());
43     int y = SkScalarFloorToInt(normPt.y() * pmap.height());
44 
45     auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
46     uint32_t pixel;
47     SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
48     int shift = static_cast<int>(channel) * 8;
49     return static_cast<uint8_t>((pixel >> shift) & 0xff);
50 }
51 
52 class Generator : public SkImageGenerator {
53 public:
Generator(SkYUVAPixmaps pixmaps,sk_sp<SkColorSpace> cs)54     Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs)
55             : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
56                                                  kN32_SkColorType,
57                                                  kPremul_SkAlphaType,
58                                                  std::move(cs)))
59             , fPixmaps(std::move(pixmaps)) {}
60 
61 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &)62     bool onGetPixels(const SkImageInfo& info,
63                      void* pixels,
64                      size_t rowBytes,
65                      const Options&) override {
66         if (kUnknown_SkColorType == fFlattened.colorType()) {
67             fFlattened.allocPixels(info);
68             SkASSERT(info == this->getInfo());
69 
70             float mtx[20];
71             SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
72             SkYUVAInfo::YUVALocations yuvaLocations = fPixmaps.toYUVALocations();
73             SkASSERT(SkYUVAInfo::YUVALocation::AreValidLocations(yuvaLocations));
74 
75             SkMatrix om = fPixmaps.yuvaInfo().originMatrix();
76             SkAssertResult(om.invert(&om));
77             float normX = 1.f/info.width();
78             float normY = 1.f/info.height();
79             if (SkEncodedOriginSwapsWidthHeight(fPixmaps.yuvaInfo().origin())) {
80                 using std::swap;
81                 swap(normX, normY);
82             }
83             for (int y = 0; y < info.height(); ++y) {
84                 for (int x = 0; x < info.width(); ++x) {
85                     SkPoint xy1 {(x + 0.5f),
86                                  (y + 0.5f)};
87                     xy1 = om.mapPoint(xy1);
88                     xy1.fX *= normX;
89                     xy1.fY *= normY;
90 
91                     uint8_t yuva[4] = {0, 0, 0, 255};
92 
93                     for (auto c : {SkYUVAInfo::YUVAChannels::kY,
94                                    SkYUVAInfo::YUVAChannels::kU,
95                                    SkYUVAInfo::YUVAChannels::kV}) {
96                         const auto& pmap = fPixmaps.plane(yuvaLocations[c].fPlane);
97                         yuva[c] = look_up(xy1, pmap, yuvaLocations[c].fChannel);
98                     }
99                     auto [aPlane, aChan] = yuvaLocations[SkYUVAInfo::YUVAChannels::kA];
100                     if (aPlane >= 0) {
101                         const auto& pmap = fPixmaps.plane(aPlane);
102                         yuva[3] = look_up(xy1, pmap, aChan);
103                     }
104 
105                     // Making premul here.
106                     *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
107                 }
108             }
109         }
110 
111         return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
112     }
113 
onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes & types,SkYUVAPixmapInfo * info) const114     bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
115                          SkYUVAPixmapInfo* info) const override {
116         *info = fPixmaps.pixmapsInfo();
117         return info->isValid();
118     }
119 
onGetYUVAPlanes(const SkYUVAPixmaps & pixmaps)120     bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
121         SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
122         for (int i = 0; i < pixmaps.numPlanes(); ++i) {
123             SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
124             SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
125             SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
126             fPixmaps.plane(i).readPixels(pixmaps.plane(i));
127         }
128         return true;
129     }
130 
131 private:
132     SkYUVAPixmaps fPixmaps;
133     SkBitmap      fFlattened;
134 };
135 
136 }  // anonymous namespace
137 
138 namespace sk_gpu_test {
139 
140 std::tuple<std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes>, SkYUVAInfo>
MakeYUVAPlanesAsA8(SkImage * src,SkYUVColorSpace cs,SkYUVAInfo::Subsampling ss,GrRecordingContext * rContext)141 MakeYUVAPlanesAsA8(SkImage* src,
142                    SkYUVColorSpace cs,
143                    SkYUVAInfo::Subsampling ss,
144                    GrRecordingContext* rContext) {
145     float rgbToYUV[20];
146     SkColorMatrix_RGB2YUV(cs, rgbToYUV);
147 
148     SkYUVAInfo::PlaneConfig config = src->isOpaque() ? SkYUVAInfo::PlaneConfig::kY_U_V
149                                                      : SkYUVAInfo::PlaneConfig::kY_U_V_A;
150     SkISize dims[SkYUVAInfo::kMaxPlanes];
151     int n = SkYUVAInfo::PlaneDimensions(src->dimensions(),
152                                         config,
153                                         ss,
154                                         kTopLeft_SkEncodedOrigin,
155                                         dims);
156     std::array<sk_sp<SkImage>, 4> planes;
157     for (int i = 0; i < n; ++i) {
158         SkImageInfo info = SkImageInfo::MakeA8(dims[i]);
159         sk_sp<SkSurface> surf;
160         if (rContext) {
161             surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, info, 1, nullptr);
162         } else {
163             surf = SkSurface::MakeRaster(info);
164         }
165         if (!surf) {
166             return {};
167         }
168 
169         SkPaint paint;
170         paint.setBlendMode(SkBlendMode::kSrc);
171 
172         // Make a matrix with the ith row of rgbToYUV copied to the A row since we're drawing to A8.
173         float m[20] = {};
174         std::copy_n(rgbToYUV + 5*i, 5, m + 15);
175         paint.setColorFilter(SkColorFilters::Matrix(m));
176         surf->getCanvas()->drawImageRect(src,
177                                          SkRect::Make(dims[i]),
178                                          SkSamplingOptions(SkFilterMode::kLinear),
179                                          &paint);
180         planes[i] = surf->makeImageSnapshot();
181         if (!planes[i]) {
182             return {};
183         }
184     }
185     SkYUVAInfo info(src->dimensions(), config, ss, cs);
186     return {planes, info};
187 }
188 
Make(sk_sp<SkData> data,GrMipmapped mipmapped,sk_sp<SkColorSpace> cs)189 std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
190                                                  GrMipmapped mipmapped,
191                                                  sk_sp<SkColorSpace> cs) {
192     std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
193     if (image->reset(std::move(data), mipmapped, std::move(cs))) {
194         return image;
195     } else {
196         return nullptr;
197     }
198 }
199 
Make(SkYUVAPixmaps pixmaps,GrMipmapped mipmapped,sk_sp<SkColorSpace> cs)200 std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
201                                                  GrMipmapped mipmapped,
202                                                  sk_sp<SkColorSpace> cs) {
203     std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
204     if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
205         return image;
206     } else {
207         return nullptr;
208     }
209 }
210 
refImage(GrRecordingContext * rContext,Type type)211 sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) {
212     if (this->ensureYUVImage(rContext, type)) {
213         size_t idx = static_cast<size_t>(type);
214         SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
215         return fYUVImage[idx];
216     } else {
217         return nullptr;
218     }
219 }
220 
reset(sk_sp<SkData> data,GrMipmapped mipmapped,sk_sp<SkColorSpace> cs)221 bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
222     fMipmapped = mipmapped;
223     auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
224     if (!codec) {
225         return false;
226     }
227 
228     SkYUVAPixmapInfo yuvaPixmapInfo;
229     if (!codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &yuvaPixmapInfo)) {
230         return false;
231     }
232     fPixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
233     if (!fPixmaps.isValid()) {
234         return false;
235     }
236 
237     if (!codec->getYUVAPlanes(fPixmaps)) {
238         return false;
239     }
240 
241     fColorSpace = std::move(cs);
242 
243     // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
244     return true;
245 }
246 
reset(SkYUVAPixmaps pixmaps,GrMipmapped mipmapped,sk_sp<SkColorSpace> cs)247 bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
248     if (!pixmaps.isValid()) {
249         return false;
250     }
251     fMipmapped = mipmapped;
252     if (pixmaps.ownsStorage()) {
253         fPixmaps = std::move(pixmaps);
254     } else {
255         fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
256     }
257     fColorSpace = std::move(cs);
258     // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
259     return true;
260 }
261 
ensureYUVImage(GrRecordingContext * rContext,Type type)262 bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
263     size_t idx = static_cast<size_t>(type);
264     SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
265     if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
266         return true;  // Have already made a YUV image valid for this context.
267     }
268     // Try to make a new YUV image for this context.
269     switch (type) {
270         case Type::kFromPixmaps:
271             if (!rContext || rContext->abandoned()) {
272                 return false;
273             }
274             fYUVImage[idx] = SkImage::MakeFromYUVAPixmaps(rContext,
275                                                           fPixmaps,
276                                                           fMipmapped,
277                                                           /*limit to max tex size*/ false,
278                                                           fColorSpace);
279             break;
280         case Type::kFromGenerator: {
281             // Make sure the generator has ownership of its backing planes.
282             auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
283             fYUVImage[idx] = SkImage::MakeFromGenerator(std::move(generator));
284             break;
285         }
286         case Type::kFromTextures:
287             if (!rContext || rContext->abandoned()) {
288                 return false;
289             }
290             if (fMipmapped == GrMipmapped::kYes) {
291                 // If this becomes necessary we should invoke SkMipmapBuilder here to make mip
292                 // maps from our src data (and then pass a pixmaps array to initialize the planar
293                 // textures.
294                 return false;
295             }
296             if (auto direct = rContext->asDirectContext()) {
297                 sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes];
298                 GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
299                 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
300                     mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeWithData(
301                             direct,
302                             fPixmaps.plane(i),
303                             kTopLeft_GrSurfaceOrigin,
304                             GrRenderable::kNo,
305                             GrProtected::kNo);
306                     if (mbets[i]) {
307                         textures[i] = mbets[i]->texture();
308                     } else {
309                         return false;
310                     }
311                 }
312                 GrYUVABackendTextures yuvaTextures(fPixmaps.yuvaInfo(),
313                                                    textures,
314                                                    kTopLeft_GrSurfaceOrigin);
315                 if (!yuvaTextures.isValid()) {
316                     return false;
317                 }
318                 void* planeRelContext =
319                         sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
320                 fYUVImage[idx] = SkImage::MakeFromYUVATextures(
321                         direct,
322                         yuvaTextures,
323                         fColorSpace,
324                         sk_gpu_test::ManagedBackendTexture::ReleaseProc,
325                         planeRelContext);
326             }
327     }
328     return fYUVImage[idx] != nullptr;
329 }
330 } // namespace sk_gpu_test
331