/* * 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 "include/gpu/ganesh/GrDirectContext.h" #include "src/core/SkCanvasPriv.h" #include "src/gpu/ganesh/SkGr.h" #include "tools/gpu/YUVUtils.h" #include #include class SkCanvas; namespace skiagm { ////////////////////////////////////////////////////////////////////////////// // This GM tests subsetting YUV multiplanar images where the U and V // planes have different resolution from Y. See skbug:8959 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}; int outerYUV[] = {128, 128, 128}; SkBitmap bitmaps[3]; 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 { auto context = GrAsDirectContext(canvas->recordingContext()); skgpu::graphite::Recorder* recorder = nullptr; #if defined(SK_GRAPHITE) recorder = canvas->recorder(); #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); } else #endif { fYUVImage = lazyYUV->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps); } return DrawResult::kOk; } void onGpuTeardown() override { fYUVImage.reset(); } DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { auto context = GrAsDirectContext(canvas->recordingContext()); skgpu::graphite::Recorder* recorder = nullptr; #if defined(SK_GRAPHITE) recorder = canvas->recorder(); #endif if (!context && !recorder) { *errorMsg = kErrorMsg_DrawSkippedGpuOnly; return DrawResult::kSkip; } if (!fYUVImage) { *errorMsg = "No valid YUV image generated -- skipping"; return DrawResult::kSkip; } static const SkFilterMode kFilters[] = {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(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 subsetImg; #if defined(SK_GRAPHITE) if (recorder) { subsetImg = fYUVImage->makeSubset(recorder, *subset, {false}); } else #endif { subsetImg = fYUVImage->makeSubset(context, *subset); } 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 fYUVImage; inline static constexpr SkScalar kTestPad = 10.f; using INHERITED = GM; }; DEF_GM(return new YUVtoRGBSubsetEffect;) } // namespace skiagm