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/SkBlendMode.h" 13 #include "include/core/SkColor.h" 14 #include "include/core/SkImage.h" 15 #include "include/core/SkImageInfo.h" 16 #include "include/core/SkMatrix.h" 17 #include "include/core/SkRect.h" 18 #include "include/core/SkRefCnt.h" 19 #include "include/core/SkScalar.h" 20 #include "include/core/SkSize.h" 21 #include "include/core/SkString.h" 22 #include "include/core/SkTypes.h" 23 #include "include/core/SkYUVAIndex.h" 24 #include "include/gpu/GrContext.h" 25 #include "include/private/GrTypesPriv.h" 26 #include "src/gpu/GrClip.h" 27 #include "src/gpu/GrContextPriv.h" 28 #include "src/gpu/GrFragmentProcessor.h" 29 #include "src/gpu/GrPaint.h" 30 #include "src/gpu/GrProxyProvider.h" 31 #include "src/gpu/GrRenderTargetContext.h" 32 #include "src/gpu/GrRenderTargetContextPriv.h" 33 #include "src/gpu/GrSamplerState.h" 34 #include "src/gpu/GrTextureProxy.h" 35 #include "src/gpu/effects/GrPorterDuffXferProcessor.h" 36 #include "src/gpu/effects/GrYUVtoRGBEffect.h" 37 #include "src/gpu/ops/GrDrawOp.h" 38 #include "src/gpu/ops/GrFillRectOp.h" 39 40 #include <memory> 41 #include <utility> 42 43 class SkCanvas; 44 45 #define YSIZE 8 46 #define USIZE 4 47 #define VSIZE 4 48 49 namespace skiagm { 50 /** 51 * This GM directly exercises GrYUVtoRGBEffect. 52 */ 53 class YUVtoRGBEffect : public GpuGM { 54 public: YUVtoRGBEffect()55 YUVtoRGBEffect() { 56 this->setBGColor(0xFFFFFFFF); 57 } 58 59 protected: onShortName()60 SkString onShortName() override { 61 return SkString("yuv_to_rgb_effect"); 62 } 63 onISize()64 SkISize onISize() override { 65 int numRows = kLastEnum_SkYUVColorSpace + 1; 66 return SkISize::Make(238, kDrawPad + numRows * kColorSpaceOffset); 67 } 68 onOnceBeforeDraw()69 void onOnceBeforeDraw() override { 70 SkBitmap bmp[3]; 71 SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE); 72 bmp[0].allocPixels(yinfo); 73 SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE); 74 bmp[1].allocPixels(uinfo); 75 SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE); 76 bmp[2].allocPixels(vinfo); 77 unsigned char* pixels[3]; 78 for (int i = 0; i < 3; ++i) { 79 pixels[i] = (unsigned char*)bmp[i].getPixels(); 80 } 81 int color[] = {0, 85, 170}; 82 const int limit[] = {255, 0, 255}; 83 const int invl[] = {0, 255, 0}; 84 const int inc[] = {1, -1, 1}; 85 for (int i = 0; i < 3; ++i) { 86 const size_t nbBytes = bmp[i].rowBytes() * bmp[i].height(); 87 for (size_t j = 0; j < nbBytes; ++j) { 88 pixels[i][j] = (unsigned char)color[i]; 89 color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i]; 90 } 91 } 92 for (int i = 0; i < 3; ++i) { 93 fImage[i] = SkImage::MakeFromBitmap(bmp[i]); 94 } 95 } 96 onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)97 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext, 98 SkCanvas* canvas, SkString* errorMsg) override { 99 GrProxyProvider* proxyProvider = context->priv().proxyProvider(); 100 sk_sp<GrTextureProxy> proxies[3]; 101 102 for (int i = 0; i < 3; ++i) { 103 proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes, 104 SkBackingFit::kExact); 105 if (!proxies[i]) { 106 *errorMsg = "Failed to create proxy"; 107 return DrawResult::kFail; 108 } 109 } 110 111 for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { 112 SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()), 113 SkIntToScalar(fImage[0]->height())); 114 renderRect.outset(kDrawPad, kDrawPad); 115 116 SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset; 117 SkScalar x = kDrawPad + kTestPad; 118 119 const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, 120 {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}; 121 122 for (int i = 0; i < 6; ++i) { 123 SkYUVAIndex yuvaIndices[4] = { 124 { indices[i][0], SkColorChannel::kR }, 125 { indices[i][1], SkColorChannel::kR }, 126 { indices[i][2], SkColorChannel::kR }, 127 { -1, SkColorChannel::kA } 128 }; 129 130 std::unique_ptr<GrFragmentProcessor> fp( 131 GrYUVtoRGBEffect::Make(proxies, yuvaIndices, 132 static_cast<SkYUVColorSpace>(space), 133 GrSamplerState::Filter::kNearest)); 134 if (fp) { 135 GrPaint grPaint; 136 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 137 grPaint.addColorFragmentProcessor(std::move(fp)); 138 SkMatrix viewMatrix; 139 viewMatrix.setTranslate(x, y); 140 renderTargetContext->priv().testingOnly_addDrawOp( 141 GrFillRectOp::MakeNonAARect(context, std::move(grPaint), 142 viewMatrix, renderRect)); 143 } 144 x += renderRect.width() + kTestPad; 145 } 146 } 147 return DrawResult::kOk; 148 } 149 150 private: 151 sk_sp<SkImage> fImage[3]; 152 153 static constexpr SkScalar kDrawPad = 10.f; 154 static constexpr SkScalar kTestPad = 10.f; 155 static constexpr SkScalar kColorSpaceOffset = 36.f; 156 157 typedef GM INHERITED; 158 }; 159 160 DEF_GM(return new YUVtoRGBEffect;) 161 162 ////////////////////////////////////////////////////////////////////////////// 163 164 class YUVNV12toRGBEffect : public GpuGM { 165 public: YUVNV12toRGBEffect()166 YUVNV12toRGBEffect() { 167 this->setBGColor(0xFFFFFFFF); 168 } 169 170 protected: onShortName()171 SkString onShortName() override { 172 return SkString("yuv_nv12_to_rgb_effect"); 173 } 174 onISize()175 SkISize onISize() override { 176 int numRows = kLastEnum_SkYUVColorSpace + 1; 177 return SkISize::Make(48, kDrawPad + numRows * kColorSpaceOffset); 178 } 179 onOnceBeforeDraw()180 void onOnceBeforeDraw() override { 181 SkBitmap bmp[2]; 182 SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE); 183 bmp[0].allocPixels(yinfo); 184 SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE); 185 bmp[1].allocPixels(uvinfo); 186 int color[] = {0, 85, 170}; 187 const int limit[] = {255, 0, 255}; 188 const int invl[] = {0, 255, 0}; 189 const int inc[] = {1, -1, 1}; 190 191 { 192 unsigned char* pixels = (unsigned char*)bmp[0].getPixels(); 193 const size_t nbBytes = bmp[0].rowBytes() * bmp[0].height(); 194 for (size_t j = 0; j < nbBytes; ++j) { 195 pixels[j] = (unsigned char)color[0]; 196 color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0]; 197 } 198 } 199 200 { 201 for (int y = 0; y < bmp[1].height(); ++y) { 202 uint32_t* pixels = bmp[1].getAddr32(0, y); 203 for (int j = 0; j < bmp[1].width(); ++j) { 204 pixels[j] = SkColorSetARGB(0, color[1], color[2], 0); 205 color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1]; 206 color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2]; 207 } 208 } 209 } 210 211 for (int i = 0; i < 2; ++i) { 212 fImage[i] = SkImage::MakeFromBitmap(bmp[i]); 213 } 214 } 215 onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)216 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext, 217 SkCanvas* canvas, SkString* errorMsg) override { 218 GrProxyProvider* proxyProvider = context->priv().proxyProvider(); 219 sk_sp<GrTextureProxy> proxies[2]; 220 221 for (int i = 0; i < 2; ++i) { 222 proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes, 223 SkBackingFit::kExact); 224 if (!proxies[i]) { 225 *errorMsg = "Failed to create proxy"; 226 return DrawResult::kFail; 227 } 228 } 229 230 SkYUVAIndex yuvaIndices[4] = { 231 { 0, SkColorChannel::kR }, 232 { 1, SkColorChannel::kR }, 233 { 1, SkColorChannel::kG }, 234 { -1, SkColorChannel::kA } 235 }; 236 237 for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { 238 SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()), 239 SkIntToScalar(fImage[0]->height())); 240 renderRect.outset(kDrawPad, kDrawPad); 241 242 SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset; 243 SkScalar x = kDrawPad + kTestPad; 244 245 GrPaint grPaint; 246 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 247 auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices, 248 static_cast<SkYUVColorSpace>(space), 249 GrSamplerState::Filter::kNearest); 250 if (fp) { 251 SkMatrix viewMatrix; 252 viewMatrix.setTranslate(x, y); 253 grPaint.addColorFragmentProcessor(std::move(fp)); 254 std::unique_ptr<GrDrawOp> op(GrFillRectOp::MakeNonAARect( 255 context, std::move(grPaint), viewMatrix, renderRect)); 256 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); 257 } 258 } 259 return DrawResult::kOk; 260 } 261 262 private: 263 sk_sp<SkImage> fImage[2]; 264 265 static constexpr SkScalar kDrawPad = 10.f; 266 static constexpr SkScalar kTestPad = 10.f; 267 static constexpr SkScalar kColorSpaceOffset = 36.f; 268 269 typedef GM INHERITED; 270 }; 271 272 DEF_GM(return new YUVNV12toRGBEffect;) 273 274 ////////////////////////////////////////////////////////////////////////////// 275 276 // This GM tests domain clamping on YUV multiplanar images where the U and V 277 // planes have different resolution from Y. See skbug:8959 278 279 class YUVtoRGBDomainEffect : public GpuGM { 280 public: YUVtoRGBDomainEffect()281 YUVtoRGBDomainEffect() { 282 this->setBGColor(0xFFFFFFFF); 283 } 284 285 protected: onShortName()286 SkString onShortName() override { 287 return SkString("yuv_to_rgb_domain_effect"); 288 } 289 onISize()290 SkISize onISize() override { 291 return SkISize::Make((YSIZE + kTestPad) * 3 + kDrawPad, (YSIZE + kTestPad) * 2 + kDrawPad); 292 } 293 onOnceBeforeDraw()294 void onOnceBeforeDraw() override { 295 SkBitmap bmp[3]; 296 SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE); 297 bmp[0].allocPixels(yinfo); 298 SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE); 299 bmp[1].allocPixels(uinfo); 300 SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE); 301 bmp[2].allocPixels(vinfo); 302 303 int innerColor[] = {149, 43, 21}; 304 int outerColor[] = {128, 128, 128}; 305 for (int i = 0; i < 3; ++i) { 306 bmp[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0)); 307 SkIRect innerRect = i == 0 ? SkIRect::MakeLTRB(2, 2, 6, 6) : SkIRect::MakeLTRB(1, 1, 3, 3); 308 bmp[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect); 309 fImage[i] = SkImage::MakeFromBitmap(bmp[i]); 310 } 311 } 312 onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)313 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext, 314 SkCanvas* canvas, SkString* errorMsg) override { 315 GrProxyProvider* proxyProvider = context->priv().proxyProvider(); 316 sk_sp<GrTextureProxy> proxies[3]; 317 318 for (int i = 0; i < 3; ++i) { 319 proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes, 320 SkBackingFit::kExact); 321 if (!proxies[i]) { 322 *errorMsg = "Failed to create proxy"; 323 return DrawResult::kFail; 324 } 325 } 326 327 // Draw a 2x2 grid of the YUV images. 328 // Rows = kNearest, kBilerp, Cols = No clamp, clamp 329 static const GrSamplerState::Filter kFilters[] = { 330 GrSamplerState::Filter::kNearest, GrSamplerState::Filter::kBilerp }; 331 static const SkRect kGreenRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f); 332 333 SkYUVAIndex yuvaIndices[4] = { 334 { SkYUVAIndex::kY_Index, SkColorChannel::kR }, 335 { SkYUVAIndex::kU_Index, SkColorChannel::kR }, 336 { SkYUVAIndex::kV_Index, SkColorChannel::kR }, 337 { -1, SkColorChannel::kA } 338 }; 339 SkRect rect = SkRect::MakeWH(YSIZE, YSIZE); 340 341 SkScalar y = kDrawPad + kTestPad; 342 for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) { 343 SkScalar x = kDrawPad + kTestPad; 344 345 for (uint32_t j = 0; j < 2; ++j) { 346 SkMatrix ctm = SkMatrix::MakeTrans(x, y); 347 ctm.postScale(10.f, 10.f); 348 349 SkRect domain = kGreenRect; 350 if (kFilters[i] == GrSamplerState::Filter::kNearest) { 351 // Make a very small inset for nearest-neighbor filtering so that 0.5px 352 // centers don't round out beyond the green pixels. 353 domain.inset(0.01f, 0.01f); 354 } 355 356 const SkRect* domainPtr = j > 0 ? &domain : nullptr; 357 std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(proxies, yuvaIndices, 358 kJPEG_SkYUVColorSpace, kFilters[i], SkMatrix::I(), domainPtr)); 359 if (fp) { 360 GrPaint grPaint; 361 grPaint.addColorFragmentProcessor(std::move(fp)); 362 renderTargetContext->drawRect( 363 GrNoClip(), std::move(grPaint), GrAA::kYes, ctm, rect); 364 } 365 x += rect.width() + kTestPad; 366 } 367 368 y += rect.height() + kTestPad; 369 } 370 371 return DrawResult::kOk; 372 } 373 374 private: 375 sk_sp<SkImage> fImage[3]; 376 377 static constexpr SkScalar kDrawPad = 10.f; 378 static constexpr SkScalar kTestPad = 10.f; 379 380 typedef GM INHERITED; 381 }; 382 383 DEF_GM(return new YUVtoRGBDomainEffect;) 384 } 385