| /* | |
| * Copyright 2014 Google Inc. | |
| * | |
| * 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 the GPU backend. | |
| #include "gm/gm.h" | |
| #include "include/core/SkBitmap.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkShader.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkYUVAInfo.h" | |
| #include "include/core/SkYUVAPixmaps.h" | |
| #include "src/core/SkCanvasPriv.h" | |
| #include "tools/gpu/YUVUtils.h" | |
| #if defined(SK_GANESH) | |
| #include "include/gpu/ganesh/GrDirectContext.h" | |
| #include "src/gpu/ganesh/SkGr.h" | |
| #endif | |
| #include <array> | |
| #include <memory> | |
| #include <utility> | |
| class SkCanvas; | |
| namespace skiagm { | |
| ////////////////////////////////////////////////////////////////////////////// | |
| // This GM tests subsetting YUV multiplanar images where the U and V | |
| // planes have different resolution from Y. See skbug.com/40040241 | |
| class YUVtoRGBSubsetEffect : public GM { | |
| public: | |
| YUVtoRGBSubsetEffect() { | |
| this->setBGColor(0xFFFFFFFF); | |
| } | |
| protected: | |
| SkString getName() const override { return SkString("yuv_to_rgb_subset_effect"); } | |
| SkISize getISize() override { return {1310, 540}; } | |
| void makePixmaps() { | |
| SkYUVAInfo yuvaInfo = SkYUVAInfo({8, 8}, | |
| SkYUVAInfo::PlaneConfig::kY_U_V, | |
| SkYUVAInfo::Subsampling::k420, | |
| kJPEG_Full_SkYUVColorSpace); | |
| SkColorType colorTypes[] = {kAlpha_8_SkColorType, | |
| kAlpha_8_SkColorType, | |
| kAlpha_8_SkColorType}; | |
| SkYUVAPixmapInfo pmapInfo(yuvaInfo, colorTypes, nullptr); | |
| fPixmaps = SkYUVAPixmaps::Allocate(pmapInfo); | |
| unsigned char innerY[16] = {149, 160, 130, 105, | |
| 160, 130, 105, 149, | |
| 130, 105, 149, 160, | |
| 105, 149, 160, 130}; | |
| unsigned char innerU[4] = {43, 75, 145, 200}; | |
| unsigned char innerV[4] = {88, 180, 200, 43}; | |
| auto outerYUV = std::to_array<int>({128, 128, 128}); | |
| std::array<SkBitmap, 3> bitmaps; | |
| for (int i = 0; i < 3; ++i) { | |
| bitmaps[i].installPixels(fPixmaps.plane(i)); | |
| bitmaps[i].eraseColor(SkColorSetARGB(outerYUV[i], 0, 0, 0)); | |
| } | |
| SkPixmap innerYPM(SkImageInfo::MakeA8(4, 4), innerY, 4); | |
| SkPixmap innerUPM(SkImageInfo::MakeA8(2, 2), innerU, 2); | |
| SkPixmap innerVPM(SkImageInfo::MakeA8(2, 2), innerV, 2); | |
| bitmaps[0].writePixels(innerYPM, 2, 2); | |
| bitmaps[1].writePixels(innerUPM, 1, 1); | |
| bitmaps[2].writePixels(innerVPM, 1, 1); | |
| } | |
| DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override { | |
| skgpu::graphite::Recorder* recorder = nullptr; | |
| GrDirectContext* context = nullptr; | |
| #if defined(SK_GRAPHITE) | |
| recorder = canvas->recorder(); | |
| #endif | |
| #if defined(SK_GANESH) | |
| context = GrAsDirectContext(canvas->recordingContext()); | |
| #endif | |
| if (!context && !recorder) { | |
| return DrawResult::kSkip; | |
| } | |
| if (!fPixmaps.isValid()) { | |
| this->makePixmaps(); | |
| } | |
| auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(fPixmaps); | |
| #if defined(SK_GRAPHITE) | |
| if (recorder) { | |
| fYUVImage = lazyYUV->refImage(recorder, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps); | |
| } | |
| #endif | |
| #if defined(SK_GANESH) | |
| if (context) { | |
| fYUVImage = lazyYUV->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps); | |
| } | |
| #endif | |
| return DrawResult::kOk; | |
| } | |
| void onGpuTeardown() override { fYUVImage.reset(); } | |
| DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { | |
| SkRecorder* recorder = canvas->baseRecorder(); | |
| if (!recorder) { | |
| *errorMsg = kErrorMsg_DrawSkippedGpuOnly; | |
| return DrawResult::kSkip; | |
| } | |
| if (!fYUVImage) { | |
| *errorMsg = "No valid YUV image generated -- skipping"; | |
| return DrawResult::kSkip; | |
| } | |
| static constexpr auto kFilters = | |
| std::to_array<SkFilterMode>({SkFilterMode::kNearest, SkFilterMode::kLinear}); | |
| static const SkIRect kColorRect = SkIRect::MakeLTRB(2, 2, 6, 6); | |
| // Outset to visualize wrap modes. | |
| SkRect rect = SkRect::Make(fYUVImage->dimensions()); | |
| rect = rect.makeOutset(fYUVImage->width()/2.f, fYUVImage->height()/2.f); | |
| SkScalar y = kTestPad; | |
| // Rows are filter modes. | |
| for (uint32_t i = 0; i < std::size(kFilters); ++i) { | |
| SkScalar x = kTestPad; | |
| // Columns are non-subsetted followed by subsetted with each TileMode in a row | |
| for (uint32_t j = 0; j < kSkTileModeCount + 1; ++j) { | |
| SkMatrix ctm = SkMatrix::Translate(x, y); | |
| ctm.postScale(10.f, 10.f); | |
| const SkIRect* subset = j > 0 ? &kColorRect : nullptr; | |
| auto tm = SkTileMode::kClamp; | |
| if (j > 0) { | |
| tm = static_cast<SkTileMode>(j - 1); | |
| } | |
| canvas->save(); | |
| canvas->concat(ctm); | |
| SkSamplingOptions sampling(kFilters[i]); | |
| SkPaint paint; | |
| // Draw black rectangle in background so rendering with Decal tilemode matches | |
| // the previously used ClampToBorder wrapmode. | |
| paint.setColor(SK_ColorBLACK); | |
| canvas->drawRect(rect, paint); | |
| if (subset) { | |
| sk_sp<SkImage> subsetImg = fYUVImage->makeSubset(recorder, *subset, {false}); | |
| SkASSERT(subsetImg); | |
| paint.setShader(subsetImg->makeShader(tm, tm, | |
| sampling, SkMatrix::Translate(2, 2))); | |
| } else { | |
| paint.setShader(fYUVImage->makeShader(tm, tm, | |
| sampling, SkMatrix::I())); | |
| } | |
| canvas->drawRect(rect, paint); | |
| canvas->restore(); | |
| x += rect.width() + kTestPad; | |
| } | |
| y += rect.height() + kTestPad; | |
| } | |
| return DrawResult::kOk; | |
| } | |
| private: | |
| SkYUVAPixmaps fPixmaps; | |
| sk_sp<SkImage> fYUVImage; | |
| inline static constexpr SkScalar kTestPad = 10.f; | |
| using INHERITED = GM; | |
| }; | |
| DEF_GM(return new YUVtoRGBSubsetEffect;) | |
| } // namespace skiagm |