| /* | |
| * Copyright 2024 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| // This test only works with Graphite. | |
| #include "gm/gm.h" | |
| #include "src/core/SkImageInfoPriv.h" | |
| #if defined(SK_GRAPHITE) | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColorFilter.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkSurface.h" | |
| #include "include/core/SkYUVAInfo.h" | |
| #include "include/effects/SkColorMatrix.h" | |
| #include "include/gpu/graphite/Image.h" | |
| #include "include/gpu/graphite/Recorder.h" | |
| #include "include/gpu/graphite/Surface.h" | |
| #include "src/core/SkYUVAInfoLocation.h" | |
| #include "tools/DecodeUtils.h" | |
| namespace skiagm { | |
| class RenderToMipmappedYUVImagePlanes : public GM { | |
| public: | |
| RenderToMipmappedYUVImagePlanes() { this->setBGColor(0xFFFFFFFF); } | |
| protected: | |
| SkString getName() const override { return SkString("render_to_mipmapped_yuv_image_planes"); } | |
| SkISize getISize() override { return {96, 32}; } | |
| DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override { | |
| auto* recorder = canvas->recorder(); | |
| if (!recorder) { | |
| *errorMsg = "graphite-only test"; | |
| return DrawResult::kSkip; | |
| } | |
| if (!fSrcImage) { | |
| fSrcImage = ToolUtils::GetResourceAsImage("images/mandrill_512.png"); | |
| if (!fSrcImage) { | |
| *errorMsg = "Could not load src image."; | |
| return DrawResult::kFail; | |
| } | |
| } | |
| return DrawResult::kOk; | |
| } | |
| DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { | |
| auto* recorder = canvas->recorder(); | |
| if (!recorder) { | |
| *errorMsg = "direct to graphite test"; | |
| return DrawResult::kSkip; | |
| } | |
| using PlaneConfig = SkYUVAInfo::PlaneConfig; | |
| using Subsampling = SkYUVAInfo::Subsampling; | |
| struct TestCase { | |
| PlaneConfig config; | |
| Subsampling subsampling; | |
| }; | |
| for (const auto& tc : {TestCase{PlaneConfig::kY_U_V, Subsampling::k420}, | |
| TestCase{PlaneConfig::kY_UV, Subsampling::k422}, | |
| TestCase{PlaneConfig::kYUV, Subsampling::k444}}) { | |
| SkYUVAInfo yuvaInfo(fSrcImage->dimensions(), | |
| tc.config, | |
| tc.subsampling, | |
| kJPEG_Full_SkYUVColorSpace); | |
| float rgbToYuv[20]; | |
| SkColorMatrix::RGBtoYUV(yuvaInfo.yuvColorSpace()).getRowMajor(rgbToYuv); | |
| sk_sp<SkImage> planes[SkYUVAInfo::kMaxPlanes]; | |
| SkISize dimensions[SkYUVAInfo::kMaxPlanes]; | |
| int numPlanes = yuvaInfo.planeDimensions(dimensions); | |
| SkColorType colorTypes [SkYUVAInfo::kMaxPlanes]; | |
| uint32_t channelFlags[SkYUVAInfo::kMaxPlanes]; | |
| for (int i = 0; i < numPlanes; ++i) { | |
| switch (yuvaInfo.numChannelsInPlane(i)) { | |
| case 1: colorTypes[i] = kAlpha_8_SkColorType; break; | |
| case 2: colorTypes[i] = kR8G8_unorm_SkColorType; break; | |
| case 3: colorTypes[i] = kRGB_888x_SkColorType; break; | |
| case 4: colorTypes[i] = kRGBA_8888_SkColorType; break; | |
| default: SkUNREACHABLE; | |
| } | |
| channelFlags[i] = SkColorTypeChannelFlags(colorTypes[i]); | |
| } | |
| SkYUVAInfo::YUVALocations locations = yuvaInfo.toYUVALocations(channelFlags); | |
| for (int i = 0; i < numPlanes; ++i) { | |
| auto info = SkImageInfo::Make(dimensions[i], colorTypes[i], kPremul_SkAlphaType); | |
| auto surf = SkSurfaces::RenderTarget(recorder, info, skgpu::Mipmapped::kYes); | |
| if (colorTypes[i] == kRGB_888x_SkColorType && !surf) { | |
| // kRGB_888x is rarely renderable with a native texture format, so fallback to | |
| // RGBA8 and the YUV shaders will ignore the extra alpha channel. | |
| surf = SkSurfaces::RenderTarget(recorder, | |
| info.makeColorType(kRGBA_8888_SkColorType), | |
| skgpu::Mipmapped::kYes); | |
| } | |
| if (!surf) { | |
| continue; | |
| } | |
| float matrix[20] { | |
| 1, 0, 0, 0, 0, | |
| 0, 1, 0, 0, 0, | |
| 0, 0, 1, 0, 0, | |
| 0, 0, 0, 1, 0 | |
| }; | |
| for (int c = 0; c < SkYUVAInfo::kYUVAChannelCount; ++c) { | |
| if (locations[c].fPlane == i) { | |
| auto d = static_cast<int>(locations[c].fChannel); | |
| std::copy_n(rgbToYuv + 5 * c, 5, matrix + 5 * d); | |
| } | |
| } | |
| auto cf = SkColorFilters::Matrix(matrix); | |
| SkPaint paint; | |
| paint.setColorFilter(SkColorFilters::Matrix(matrix)); | |
| surf->getCanvas()->drawImageRect(fSrcImage, | |
| SkRect::Make(surf->imageInfo().dimensions()), | |
| SkFilterMode::kLinear, | |
| &paint); | |
| planes[i] = SkSurfaces::AsImage(std::move(surf)); | |
| } | |
| auto yuvaImage = SkImages::TextureFromYUVAImages(recorder, | |
| yuvaInfo, | |
| planes, | |
| /*imageColorSpace=*/nullptr); | |
| if (yuvaImage) { | |
| auto dstRect = SkRect::MakeWH(yuvaImage->width() / 16.f, | |
| yuvaImage->height() / 16.f); | |
| canvas->drawImageRect(yuvaImage.get(), | |
| dstRect, | |
| SkSamplingOptions(SkFilterMode::kLinear, | |
| SkMipmapMode::kLinear)); | |
| } | |
| canvas->translate(std::ceil(yuvaInfo.width()/16.f), 0); | |
| } | |
| return DrawResult::kOk; | |
| } | |
| private: | |
| sk_sp<SkImage> fSrcImage; | |
| }; | |
| DEF_GM(return new RenderToMipmappedYUVImagePlanes();) | |
| } // namespace skiagm | |
| #endif |