1 /* 2 * Copyright 2014 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 // This test only works with the GPU backend. 9 10 #include "gm/gm.h" 11 #include "include/core/SkBitmap.h" 12 #include "include/core/SkColor.h" 13 #include "include/core/SkImageInfo.h" 14 #include "include/core/SkMatrix.h" 15 #include "include/core/SkRect.h" 16 #include "include/core/SkScalar.h" 17 #include "include/core/SkSize.h" 18 #include "include/core/SkString.h" 19 #include "include/core/SkYUVAInfo.h" 20 #include "include/core/SkYUVAPixmaps.h" 21 #include "src/core/SkCanvasPriv.h" 22 #include "src/gpu/GrSamplerState.h" 23 #include "src/gpu/GrTextureProxy.h" 24 #include "src/gpu/GrYUVATextureProxies.h" 25 #include "src/gpu/SkGr.h" 26 #include "src/gpu/effects/GrYUVtoRGBEffect.h" 27 #include "src/gpu/v1/SurfaceDrawContext_v1.h" 28 29 #include <memory> 30 #include <utility> 31 32 class SkCanvas; 33 34 namespace skiagm { 35 36 ////////////////////////////////////////////////////////////////////////////// 37 38 // This GM tests subsetting YUV multiplanar images where the U and V 39 // planes have different resolution from Y. See skbug:8959 40 41 class YUVtoRGBSubsetEffect : public GpuGM { 42 public: YUVtoRGBSubsetEffect()43 YUVtoRGBSubsetEffect() { 44 this->setBGColor(0xFFFFFFFF); 45 } 46 47 protected: onShortName()48 SkString onShortName() override { 49 return SkString("yuv_to_rgb_subset_effect"); 50 } 51 onISize()52 SkISize onISize() override { return {1310, 540}; } 53 makePixmaps()54 void makePixmaps() { 55 SkYUVAInfo yuvaInfo = SkYUVAInfo({8, 8}, 56 SkYUVAInfo::PlaneConfig::kY_U_V, 57 SkYUVAInfo::Subsampling::k420, 58 kJPEG_Full_SkYUVColorSpace); 59 SkColorType colorTypes[] = {kAlpha_8_SkColorType, 60 kAlpha_8_SkColorType, 61 kAlpha_8_SkColorType}; 62 SkYUVAPixmapInfo pmapInfo(yuvaInfo, colorTypes, nullptr); 63 fPixmaps = SkYUVAPixmaps::Allocate(pmapInfo); 64 65 unsigned char innerY[16] = {149, 160, 130, 105, 66 160, 130, 105, 149, 67 130, 105, 149, 160, 68 105, 149, 160, 130}; 69 unsigned char innerU[4] = {43, 75, 145, 200}; 70 unsigned char innerV[4] = {88, 180, 200, 43}; 71 int outerYUV[] = {128, 128, 128}; 72 SkBitmap bitmaps[3]; 73 for (int i = 0; i < 3; ++i) { 74 bitmaps[i].installPixels(fPixmaps.plane(i)); 75 bitmaps[i].eraseColor(SkColorSetARGB(outerYUV[i], 0, 0, 0)); 76 } 77 SkPixmap innerYPM(SkImageInfo::MakeA8(4, 4), innerY, 4); 78 SkPixmap innerUPM(SkImageInfo::MakeA8(2, 2), innerU, 2); 79 SkPixmap innerVPM(SkImageInfo::MakeA8(2, 2), innerV, 2); 80 bitmaps[0].writePixels(innerYPM, 2, 2); 81 bitmaps[1].writePixels(innerUPM, 1, 1); 82 bitmaps[2].writePixels(innerVPM, 1, 1); 83 } 84 onGpuSetup(GrDirectContext * context,SkString * errorMsg)85 DrawResult onGpuSetup(GrDirectContext* context, SkString* errorMsg) override { 86 if (!context) { 87 return DrawResult::kSkip; 88 } 89 if (!fPixmaps.isValid()) { 90 this->makePixmaps(); 91 } 92 GrSurfaceProxyView views[SkYUVAInfo::kMaxPlanes]; 93 GrColorType colorTypes[SkYUVAInfo::kMaxPlanes]; 94 for (int i = 0; i < fPixmaps.numPlanes(); ++i) { 95 SkBitmap bitmap; 96 bitmap.installPixels(fPixmaps.plane(i)); 97 bitmap.setImmutable(); 98 views[i] = std::get<0>(GrMakeCachedBitmapProxyView(context, bitmap, GrMipmapped::kNo)); 99 if (!views[i]) { 100 *errorMsg = "Failed to create proxy"; 101 return context->abandoned() ? DrawResult::kSkip : DrawResult::kFail; 102 } 103 colorTypes[i] = SkColorTypeToGrColorType(bitmap.colorType()); 104 } 105 fProxies = GrYUVATextureProxies(fPixmaps.yuvaInfo(), views, colorTypes); 106 if (!fProxies.isValid()) { 107 *errorMsg = "Failed to create GrYUVATextureProxies"; 108 return DrawResult::kFail; 109 } 110 return DrawResult::kOk; 111 } 112 onGpuTeardown()113 void onGpuTeardown() override { fProxies = {}; } 114 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)115 DrawResult onDraw(GrRecordingContext* rContext, 116 SkCanvas* canvas, 117 SkString* errorMsg) override { 118 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas); 119 if (!sdc) { 120 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 121 return DrawResult::kSkip; 122 } 123 124 static const GrSamplerState::Filter kFilters[] = {GrSamplerState::Filter::kNearest, 125 GrSamplerState::Filter::kLinear}; 126 static const SkRect kColorRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f); 127 128 // Outset to visualize wrap modes. 129 SkRect rect = SkRect::Make(fProxies.yuvaInfo().dimensions()); 130 rect = rect.makeOutset(fProxies.yuvaInfo().width()/2.f, fProxies.yuvaInfo().height()/2.f); 131 132 SkScalar y = kTestPad; 133 // Rows are filter modes. 134 for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) { 135 SkScalar x = kTestPad; 136 // Columns are non-subsetted followed by subsetted with each WrapMode in a row 137 for (uint32_t j = 0; j < GrSamplerState::kWrapModeCount + 1; ++j) { 138 SkMatrix ctm = SkMatrix::Translate(x, y); 139 ctm.postScale(10.f, 10.f); 140 141 const SkRect* subset = j > 0 ? &kColorRect : nullptr; 142 143 GrSamplerState samplerState; 144 samplerState.setFilterMode(kFilters[i]); 145 if (j > 0) { 146 auto wm = static_cast<GrSamplerState::WrapMode>(j - 1); 147 samplerState.setWrapModeX(wm); 148 samplerState.setWrapModeY(wm); 149 } 150 const auto& caps = *rContext->priv().caps(); 151 std::unique_ptr<GrFragmentProcessor> fp = 152 GrYUVtoRGBEffect::Make(fProxies, samplerState, caps, SkMatrix::I(), subset); 153 if (fp) { 154 GrPaint grPaint; 155 grPaint.setColorFragmentProcessor(std::move(fp)); 156 sdc->drawRect(nullptr, std::move(grPaint), GrAA::kYes, ctm, rect); 157 } 158 x += rect.width() + kTestPad; 159 } 160 161 y += rect.height() + kTestPad; 162 } 163 164 return DrawResult::kOk; 165 } 166 167 private: 168 SkYUVAPixmaps fPixmaps; 169 GrYUVATextureProxies fProxies; 170 171 inline static constexpr SkScalar kTestPad = 10.f; 172 173 using INHERITED = GM; 174 }; 175 176 DEF_GM(return new YUVtoRGBSubsetEffect;) 177 } // namespace skiagm 178