From 97c98f95962aa6d053bbbd69b97944ea158acb35 Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Tue, 24 Mar 2020 18:59:25 +0000 Subject: [PATCH] Revert "Reland x2 "Drawing YUVA images does not flatten for bicubic."" This reverts commit 9b22838da3854b161b0363c321f342d886e38d81. Reason for revert: bad GM results. Original change's description: > Reland x2 "Drawing YUVA images does not flatten for bicubic." > > With fix for dumb coord mistake. > > This reverts commit 6cb8168c2b9b757110883a6974e45c7f76db228c. > > Change-Id: If5385d16339c2b2b03eeff4ddd65c601e672d3dc > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/278783 > Reviewed-by: Brian Salomon > Commit-Queue: Brian Salomon TBR=bsalomon@google.com Change-Id: I1143413f841182d39f5dc3eb4593fa98edf1bed4 No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://skia-review.googlesource.com/c/skia/+/278858 Reviewed-by: Brian Salomon Commit-Queue: Brian Salomon --- gm/imagefromyuvtextures.cpp | 257 ++++++++---------- src/gpu/GrImageTextureMaker.cpp | 17 +- src/gpu/effects/GrBicubicEffect.cpp | 154 +++++------ src/gpu/effects/GrBicubicEffect.h | 15 +- src/gpu/effects/GrDeviceSpaceEffect.fp | 4 +- .../effects/generated/GrDeviceSpaceEffect.cpp | 4 +- 6 files changed, 208 insertions(+), 243 deletions(-) diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp index e19e2dfed6..e54b266b07 100644 --- a/gm/imagefromyuvtextures.cpp +++ b/gm/imagefromyuvtextures.cpp @@ -12,6 +12,7 @@ #include "include/core/SkCanvas.h" #include "include/core/SkColor.h" #include "include/core/SkColorFilter.h" +#include "include/core/SkColorPriv.h" #include "include/core/SkImage.h" #include "include/core/SkImageInfo.h" #include "include/core/SkPaint.h" @@ -19,19 +20,27 @@ #include "include/core/SkPoint.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" +#include "include/core/SkShader.h" #include "include/core/SkSize.h" #include "include/core/SkString.h" +#include "include/core/SkTileMode.h" #include "include/core/SkTypes.h" -#include "include/core/SkYUVAIndex.h" +#include "include/effects/SkGradientShader.h" #include "include/gpu/GrBackendSurface.h" #include "include/gpu/GrContext.h" #include "include/gpu/GrTypes.h" +#include "include/private/GrTypesPriv.h" #include "include/private/SkTo.h" #include "src/core/SkYUVMath.h" -#include "tools/Resources.h" class GrRenderTargetContext; +static sk_sp yuv_to_rgb_colorfilter() { + float m[20]; + SkColorMatrix_YUV2RGB(kJPEG_SkYUVColorSpace, m); + return SkColorFilters::Matrix(m); +} + namespace skiagm { class ImageFromYUVTextures : public GpuGM { public: @@ -44,93 +53,93 @@ protected: return SkString("image_from_yuv_textures"); } - SkISize onISize() override { return {1420, 610}; } - - void onOnceBeforeDraw() override { - fRGBABmp = this->createBmpAndPlanes("images/mandrill_32.png", fYUVABmps); + SkISize onISize() override { + // Original image, plus each color space drawn twice + int numBitmaps = 2 * (kLastEnum_SkYUVColorSpace + 1) + 1; + return SkISize::Make(kBmpSize + 2 * kPad, numBitmaps * (kBmpSize + kPad) + kPad); } - SkBitmap createBmpAndPlanes(const char* name, SkBitmap yuvaBmps[4]) { - SkBitmap bmp; - if (!GetResourceAsBitmap(name, &bmp)) { - return {}; - } - auto ii = SkImageInfo::Make(bmp.dimensions(), kRGBA_8888_SkColorType, kPremul_SkAlphaType); + void onOnceBeforeDraw() override { + // We create an RGB bitmap and then extract YUV bmps where the U and V bitmaps are + // subsampled by 2 in both dimensions. + SkPaint paint; + constexpr SkColor kColors[] = + { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorWHITE }; + paint.setShader(SkGradientShader::MakeRadial(SkPoint::Make(0,0), kBmpSize / 2.f, kColors, + nullptr, SK_ARRAY_COUNT(kColors), + SkTileMode::kMirror)); + SkBitmap rgbBmp; + auto ii = + SkImageInfo::Make(kBmpSize, kBmpSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + rgbBmp.allocPixels(ii); + SkCanvas canvas(rgbBmp); + canvas.drawPaint(paint); + SkPMColor* rgbColors = static_cast(rgbBmp.getPixels()); - SkBitmap rgbaBmp; - rgbaBmp.allocPixels(ii); - bmp.readPixels(rgbaBmp.pixmap(), 0, 0); - - SkImageInfo yaInfo = SkImageInfo::Make(rgbaBmp.dimensions(), kAlpha_8_SkColorType, - kUnpremul_SkAlphaType); - yuvaBmps[0].allocPixels(yaInfo); - SkISize uvSize = {rgbaBmp.width()/2, rgbaBmp.height()/2}; - SkImageInfo uvInfo = SkImageInfo::Make(uvSize, kAlpha_8_SkColorType, kUnpremul_SkAlphaType); - yuvaBmps[1].allocPixels(uvInfo); - yuvaBmps[2].allocPixels(uvInfo); - yuvaBmps[3].allocPixels(yaInfo); - - unsigned char* yuvPixels[] = { - static_cast(yuvaBmps[0].getPixels()), - static_cast(yuvaBmps[1].getPixels()), - static_cast(yuvaBmps[2].getPixels()), - static_cast(yuvaBmps[3].getPixels()), - }; + SkImageInfo yinfo = SkImageInfo::Make(kBmpSize, kBmpSize, kGray_8_SkColorType, + kUnpremul_SkAlphaType); + fYUVBmps[0].allocPixels(yinfo); + SkImageInfo uinfo = SkImageInfo::Make(kBmpSize / 2, kBmpSize / 2, kGray_8_SkColorType, + kUnpremul_SkAlphaType); + fYUVBmps[1].allocPixels(uinfo); + SkImageInfo vinfo = SkImageInfo::Make(kBmpSize / 2, kBmpSize / 2, kGray_8_SkColorType, + kUnpremul_SkAlphaType); + fYUVBmps[2].allocPixels(vinfo); + unsigned char* yPixels; + signed char* uvPixels[2]; + yPixels = static_cast(fYUVBmps[0].getPixels()); + uvPixels[0] = static_cast(fYUVBmps[1].getPixels()); + uvPixels[1] = static_cast(fYUVBmps[2].getPixels()); float m[20]; SkColorMatrix_RGB2YUV(kJPEG_SkYUVColorSpace, m); // Here we encode using the kJPEG_SkYUVColorSpace (i.e., full-swing Rec 601) even though // we will draw it with all the supported yuv color spaces when converted back to RGB - for (int j = 0; j < yaInfo.height(); ++j) { - for (int i = 0; i < yaInfo.width(); ++i) { - auto rgba = *rgbaBmp.getAddr32(i, j); - auto r = (rgba & 0x000000ff) >> 0; - auto g = (rgba & 0x0000ff00) >> 8; - auto b = (rgba & 0x00ff0000) >> 16; - auto a = (rgba & 0xff000000) >> 24; - yuvPixels[0][j*yaInfo.width() + i] = SkToU8( - sk_float_round2int(m[0]*r + m[1]*g + m[2]*b + m[3]*a + 255*m[4])); - yuvPixels[3][j*yaInfo.width() + i] = SkToU8(sk_float_round2int( - m[15]*r + m[16]*g + m[17]*b + m[18]*a + 255*m[19])); - } + for (int i = 0; i < kBmpSize * kBmpSize; ++i) { + auto r = (rgbColors[i] & 0x000000ff) >> 0; + auto g = (rgbColors[i] & 0x0000ff00) >> 8; + auto b = (rgbColors[i] & 0x00ff0000) >> 16; + auto a = (rgbColors[i] & 0xff000000) >> 24; + yPixels[i] = SkToU8(sk_float_round2int(m[0]*r + m[1]*g + m[2]*b + m[3]*a + 255*m[4])); } - for (int j = 0; j < uvInfo.height(); ++j) { - for (int i = 0; i < uvInfo.width(); ++i) { + for (int j = 0; j < kBmpSize / 2; ++j) { + for (int i = 0; i < kBmpSize / 2; ++i) { // Average together 4 pixels of RGB. int rgba[] = {0, 0, 0, 0}; for (int y = 0; y < 2; ++y) { for (int x = 0; x < 2; ++x) { - auto src = *rgbaBmp.getAddr32(2 * i + x, 2 * j + y); - rgba[0] += (src & 0x000000ff) >> 0; - rgba[1] += (src & 0x0000ff00) >> 8; - rgba[2] += (src & 0x00ff0000) >> 16; - rgba[3] += (src & 0xff000000) >> 24; + int rgbIndex = (2 * j + y) * kBmpSize + 2 * i + x; + rgba[0] += (rgbColors[rgbIndex] & 0x000000ff) >> 0; + rgba[1] += (rgbColors[rgbIndex] & 0x0000ff00) >> 8; + rgba[2] += (rgbColors[rgbIndex] & 0x00ff0000) >> 16; + rgba[3] += (rgbColors[rgbIndex] & 0xff000000) >> 24; } } for (int c = 0; c < 4; ++c) { rgba[c] /= 4; } - int uvIndex = j*uvInfo.width() + i; - yuvPixels[1][uvIndex] = SkToU8(sk_float_round2int( + int uvIndex = j * kBmpSize / 2 + i; + uvPixels[0][uvIndex] = SkToU8(sk_float_round2int( m[5]*rgba[0] + m[6]*rgba[1] + m[7]*rgba[2] + m[8]*rgba[3] + 255*m[9])); - yuvPixels[2][uvIndex] = SkToU8(sk_float_round2int( + uvPixels[1][uvIndex] = SkToU8(sk_float_round2int( m[10]*rgba[0] + m[11]*rgba[1] + m[12]*rgba[2] + m[13]*rgba[3] + 255*m[14])); } } - return rgbaBmp; + fRGBImage = SkImage::MakeRasterCopy(SkPixmap(rgbBmp.info(), rgbColors, rgbBmp.rowBytes())); } - void createYUVTextures(SkBitmap bmps[4], GrContext* context, GrBackendTexture textures[4]) { - for (int i = 0; i < 4; ++i) { - SkASSERT(bmps[i].width() == SkToInt(bmps[i].rowBytes())); - textures[i] = context->createBackendTexture(&bmps[i].pixmap(), 1, GrRenderable::kNo, - GrProtected::kNo); + void createYUVTextures(GrContext* context, GrBackendTexture yuvTextures[3]) { + for (int i = 0; i < 3; ++i) { + SkASSERT(fYUVBmps[i].width() == SkToInt(fYUVBmps[i].rowBytes())); + yuvTextures[i] = context->createBackendTexture(&fYUVBmps[i].pixmap(), 1, + GrRenderable::kNo, GrProtected::kNo); } } - void createResultTexture(GrContext* context, SkISize size, GrBackendTexture* resultTexture) { + void createResultTexture(GrContext* context, int width, int height, + GrBackendTexture* resultTexture) { *resultTexture = context->createBackendTexture( - size.width(), size.height(), kRGBA_8888_SkColorType, SkColors::kTransparent, + width, height, kRGBA_8888_SkColorType, SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes, GrProtected::kNo); } @@ -149,93 +158,67 @@ protected: } void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override { - GrBackendTexture yuvaTextures[4]; - static constexpr SkYUVAIndex kIndices[] = { - {0, SkColorChannel::kR}, - {1, SkColorChannel::kR}, - {2, SkColorChannel::kR}, - {3, SkColorChannel::kR}, - }; - this->createYUVTextures(fYUVABmps, context, yuvaTextures); - auto image1 = SkImage::MakeFromYUVATextures(context, - kJPEG_SkYUVColorSpace, - yuvaTextures, - kIndices, - fRGBABmp.dimensions(), - kTopLeft_GrSurfaceOrigin); + // draw the original + SkScalar yOffset = kPad; + canvas->drawImage(fRGBImage.get(), kPad, yOffset); + yOffset += kBmpSize + kPad; - GrBackendTexture resultTexture; - this->createResultTexture(context, fRGBABmp.dimensions(), &resultTexture); - auto image2 = SkImage::MakeFromYUVTexturesCopyWithExternalBackend(context, - kJPEG_SkYUVColorSpace, - yuvaTextures, - kTopLeft_GrSurfaceOrigin, - resultTexture); + for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { + GrBackendTexture yuvTextures[3]; + this->createYUVTextures(context, yuvTextures); + auto image = SkImage::MakeFromYUVTexturesCopy(context, + static_cast(space), + yuvTextures, + kTopLeft_GrSurfaceOrigin); + this->deleteBackendTextures(context, yuvTextures, 3); - auto draw_image = [canvas](SkImage* image, SkFilterQuality fq) -> SkSize { SkPaint paint; - paint.setFilterQuality(fq); - canvas->drawImage(image, 0, 0, &paint); - return {SkIntToScalar(image->width()), SkIntToScalar(image->height())}; - }; - - auto draw_image_rect = [canvas](SkImage* image, SkFilterQuality fq) -> SkSize { - SkPaint paint; - paint.setFilterQuality(fq); - auto subset = SkRect::Make(image->dimensions()); - subset.inset(subset.width() * .05f, subset.height() * .1f); - auto dst = SkRect::MakeWH(subset.width(), subset.height()); - canvas->drawImageRect(image, subset, dst, &paint); - return {dst.width(), dst.height()}; - }; - - auto draw_image_shader = [canvas](SkImage* image, SkFilterQuality fq) -> SkSize { - SkMatrix m; - m.setRotate(45, image->width()/2.f, image->height()/2.f); - auto shader = image->makeShader(SkTileMode::kMirror, SkTileMode::kDecal, m); - SkPaint paint; - paint.setFilterQuality(fq); - paint.setShader(std::move(shader)); - auto rect = SkRect::MakeWH(image->width() * 1.3f, image->height()); - canvas->drawRect(rect, paint); - return {rect.width(), rect.height()}; - }; - - canvas->translate(kPad, kPad); - using DrawSig = SkSize(SkImage* image, SkFilterQuality fq); - using DF = std::function; - for (const auto& draw : {DF(draw_image), DF(draw_image_rect), DF(draw_image_shader)}) { - for (auto scale : {1.f, 4.f, 0.75f}) { - SkScalar h = 0; - canvas->save(); - for (auto fq : {kNone_SkFilterQuality, kLow_SkFilterQuality, - kMedium_SkFilterQuality, kHigh_SkFilterQuality}) { - canvas->save(); - canvas->scale(scale, scale); - auto s1 = draw(image1.get(), fq); - canvas->restore(); - canvas->translate(kPad + SkScalarCeilToScalar(scale*s1.width()), 0); - canvas->save(); - canvas->scale(scale, scale); - auto s2 = draw(image2.get(), fq); - canvas->restore(); - canvas->translate(kPad + SkScalarCeilToScalar(scale*s2.width()), 0); - h = std::max({h, s1.height(), s2.height()}); - } - canvas->restore(); - canvas->translate(0, kPad + SkScalarCeilToScalar(scale*h)); + if (kIdentity_SkYUVColorSpace == space) { + // The identity color space needs post-processing to appear correct + paint.setColorFilter(yuv_to_rgb_colorfilter()); } + + canvas->drawImage(image.get(), kPad, yOffset, &paint); + yOffset += kBmpSize + kPad; } - this->deleteBackendTextures(context, &resultTexture, 1); - this->deleteBackendTextures(context, yuvaTextures, 4); + for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { + GrBackendTexture yuvTextures[3]; + GrBackendTexture resultTexture; + this->createYUVTextures(context, yuvTextures); + this->createResultTexture( + context, yuvTextures[0].width(), yuvTextures[0].height(), &resultTexture); + auto image = SkImage::MakeFromYUVTexturesCopyWithExternalBackend( + context, + static_cast(space), + yuvTextures, + kTopLeft_GrSurfaceOrigin, + resultTexture); + + SkPaint paint; + if (kIdentity_SkYUVColorSpace == space) { + // The identity color space needs post-processing to appear correct + paint.setColorFilter(yuv_to_rgb_colorfilter()); + } + canvas->drawImage(image.get(), kPad, yOffset, &paint); + yOffset += kBmpSize + kPad; + + GrBackendTexture texturesToDelete[4]{ + yuvTextures[0], + yuvTextures[1], + yuvTextures[2], + resultTexture, + }; + this->deleteBackendTextures(context, texturesToDelete, 4); + } } private: - SkBitmap fRGBABmp; - SkBitmap fYUVABmps[4]; + sk_sp fRGBImage; + SkBitmap fYUVBmps[3]; static constexpr SkScalar kPad = 10.0f; + static constexpr int kBmpSize = 32; typedef GM INHERITED; }; diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp index 71c2d263f7..5a4f7e7a0c 100644 --- a/src/gpu/GrImageTextureMaker.cpp +++ b/src/gpu/GrImageTextureMaker.cpp @@ -11,7 +11,6 @@ #include "src/gpu/GrContextPriv.h" #include "src/gpu/GrRecordingContextPriv.h" #include "src/gpu/SkGr.h" -#include "src/gpu/effects/GrBicubicEffect.h" #include "src/gpu/effects/GrYUVtoRGBEffect.h" #include "src/image/SkImage_GpuYUVA.h" #include "src/image/SkImage_Lazy.h" @@ -66,18 +65,17 @@ std::unique_ptr GrYUVAImageTextureMaker::createFragmentProc GrSamplerState::WrapMode wrapX, GrSamplerState::WrapMode wrapY, const GrSamplerState::Filter* filterOrNullForBicubic) { - // Check whether it's already been flattened. - if (fImage->fRGBView.proxy()) { + // Check simple cases to see if we need to fall back to flattening the image (or whether it's + // already been flattened.) + if (!filterOrNullForBicubic || fImage->fRGBView.proxy()) { return this->INHERITED::createFragmentProcessor( textureMatrix, constraintRect, filterConstraint, coordsLimitedToConstraintRect, wrapX, wrapY, filterOrNullForBicubic); } - GrSamplerState::Filter filter = - filterOrNullForBicubic ? *filterOrNullForBicubic : GrSamplerState::Filter::kNearest; - // Check to see if the client has given us pre-mipped textures or we can generate them // If not, fall back to bilerp. Also fall back to bilerp when a domain is requested + GrSamplerState::Filter filter = *filterOrNullForBicubic; if (GrSamplerState::Filter::kMipMap == filter && (filterConstraint == GrTextureProducer::kYes_FilterConstraint || !fImage->setupMipmapsForPlanes(this->context()))) { @@ -93,13 +91,8 @@ std::unique_ptr GrYUVAImageTextureMaker::createFragmentProc } const auto& caps = *fImage->context()->priv().caps(); - const SkMatrix& m = filterOrNullForBicubic ? textureMatrix : SkMatrix::I(); auto fp = GrYUVtoRGBEffect::Make(fImage->fViews, fImage->fYUVAIndices, fImage->fYUVColorSpace, - filter, caps, m, domain); - if (!filterOrNullForBicubic) { - fp = GrBicubicEffect::Make(std::move(fp), fImage->alphaType(), textureMatrix, - GrBicubicEffect::Direction::kXY); - } + filter, caps, textureMatrix, domain); if (fImage->fFromColorSpace) { fp = GrColorSpaceXformEffect::Make(std::move(fp), fImage->fFromColorSpace.get(), fImage->alphaType(), fImage->colorSpace()); diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp index d1639a22ea..d7f3933956 100644 --- a/src/gpu/effects/GrBicubicEffect.cpp +++ b/src/gpu/effects/GrBicubicEffect.cpp @@ -19,12 +19,22 @@ public: void emitCode(EmitArgs&) override; private: + void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; + + UniformHandle fDimensions; + GrTextureDomain::GLDomain fDomain; + typedef GrGLSLFragmentProcessor INHERITED; }; void GrBicubicEffect::Impl::emitCode(EmitArgs& args) { const GrBicubicEffect& bicubicEffect = args.fFp.cast(); + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fDimensions = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "Dimensions"); + + const char* dims = uniformHandler->getUniformCStr(fDimensions); + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint); @@ -51,15 +61,14 @@ void GrBicubicEffect::Impl::emitCode(EmitArgs& args) { "-9.0 / 18.0, 0.0 / 18.0, 9.0 / 18.0, 0.0 / 18.0," "15.0 / 18.0, -36.0 / 18.0, 27.0 / 18.0, -6.0 / 18.0," "-7.0 / 18.0, 21.0 / 18.0, -21.0 / 18.0, 7.0 / 18.0);"); - fragBuilder->codeAppendf("float2 coord = %s - float2(0.5);", coords2D.c_str()); - // We determine our fractional offset (f) within the texel. We then snap coord to a texel - // center. The snap prevents cases where the starting coords are near a texel boundary and - // offsets with imperfect precision would cause us to skip/double hit a texel. - // The use of "texel" above is somewhat abstract as we're sampling a child processor. It is - // assumed the child processor represents something akin to a nearest neighbor sampled texture. + fragBuilder->codeAppendf("float2 coord = %s - %s.xy * float2(0.5);", coords2D.c_str(), dims); + // We unnormalize the coord in order to determine our fractional offset (f) within the texel + // We then snap coord to a texel center and renormalize. The snap prevents cases where the + // starting coords are near a texel boundary and accumulations of dims would cause us to skip/ + // double hit a texel. + fragBuilder->codeAppendf("half2 f = half2(fract(coord * %s.zw));", dims); + fragBuilder->codeAppendf("coord = coord + (half2(0.5) - f) * %s.xy;", dims); if (bicubicEffect.fDirection == GrBicubicEffect::Direction::kXY) { - fragBuilder->codeAppend("half2 f = half2(fract(coord));"); - fragBuilder->codeAppend("coord = coord + (half2(0.5) - f);"); fragBuilder->codeAppend( "half4 wx = kMitchellCoefficients * half4(1.0, f.x, f.x * f.x, f.x * f.x * f.x);"); fragBuilder->codeAppend( @@ -68,7 +77,7 @@ void GrBicubicEffect::Impl::emitCode(EmitArgs& args) { for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { SkString coord; - coord.printf("coord + float2(%d, %d)", x - 1, y - 1); + coord.printf("coord + %s.xy * float2(%d, %d)", dims, x - 1, y - 1); auto childStr = this->invokeChild(0, args, SkSL::String(coord.c_str(), coord.size())); fragBuilder->codeAppendf("rowColors[%d] = %s;", x, childStr.c_str()); @@ -81,19 +90,14 @@ void GrBicubicEffect::Impl::emitCode(EmitArgs& args) { fragBuilder->codeAppend( "half4 bicubicColor = wy.x * s0 + wy.y * s1 + wy.z * s2 + wy.w * s3;"); } else { - const char* d = bicubicEffect.fDirection == Direction::kX ? "x" : "y"; - fragBuilder->codeAppendf("half f = half(fract(coord.%s));", d); - fragBuilder->codeAppendf("coord.%s = coord.%s + (0.5 - f);", d, d); - fragBuilder->codeAppend("half f2 = f * f;"); - fragBuilder->codeAppend("half4 w = kMitchellCoefficients * half4(1.0, f, f2, f2 * f);"); + // One of the dims.xy values will be zero. So v here selects the nonzero value of f. + fragBuilder->codeAppend("half v = f.x + f.y;"); + fragBuilder->codeAppend("half v2 = v * v;"); + fragBuilder->codeAppend("half4 w = kMitchellCoefficients * half4(1.0, v, v2, v2 * v);"); fragBuilder->codeAppend("half4 c[4];"); for (int i = 0; i < 4; ++i) { SkString coord; - if (bicubicEffect.fDirection == Direction::kX) { - coord.printf("float2(coord.x + %d, coord.y)", i - 1); - } else { - coord.printf("float2(coord.x, coord.y + %d)", i - 1); - } + coord.printf("coord + %s.xy * half(%d)", dims, i - 1); auto childStr = this->invokeChild(0, args, SkSL::String(coord.c_str(), coord.size())); fragBuilder->codeAppendf("c[%d] = %s;", i, childStr.c_str()); } @@ -114,14 +118,42 @@ void GrBicubicEffect::Impl::emitCode(EmitArgs& args) { fragBuilder->codeAppendf("%s = bicubicColor * %s;", args.fOutputColor, args.fInputColor); } +void GrBicubicEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdman, + const GrFragmentProcessor& processor) { + const GrBicubicEffect& bicubicEffect = processor.cast(); + // Currently we only ever construct with GrTextureEffect and always take its + // coord transform as our own. + SkASSERT(bicubicEffect.fCoordTransform.peekTexture()); + SkISize textureDims = bicubicEffect.fCoordTransform.peekTexture()->dimensions(); + + float dims[4] = {0, 0, 0, 0}; + if (bicubicEffect.fDirection != GrBicubicEffect::Direction::kY) { + if (bicubicEffect.fCoordTransform.normalize()) { + dims[0] = 1.f / textureDims.width(); + dims[2] = textureDims.width(); + } else { + dims[0] = dims[2] = 1.f; + } + } + if (bicubicEffect.fDirection != GrBicubicEffect::Direction::kX) { + if (bicubicEffect.fCoordTransform.normalize()) { + dims[1] = 1.f / textureDims.height(); + dims[3] = textureDims.height(); + } else { + dims[1] = dims[3] = 1.f; + } + } + pdman.set4fv(fDimensions, 1, dims); +} + std::unique_ptr GrBicubicEffect::Make(GrSurfaceProxyView view, SkAlphaType alphaType, const SkMatrix& matrix, Direction direction) { - auto fp = GrTextureEffect::Make(std::move(view), alphaType, SkMatrix::I()); + auto fp = GrTextureEffect::Make(std::move(view), alphaType, matrix); auto clamp = kPremul_SkAlphaType == alphaType ? Clamp::kPremul : Clamp::kUnpremul; return std::unique_ptr( - new GrBicubicEffect(std::move(fp), matrix, direction, clamp)); + new GrBicubicEffect(std::move(fp), direction, clamp)); } std::unique_ptr GrBicubicEffect::Make(GrSurfaceProxyView view, @@ -133,10 +165,10 @@ std::unique_ptr GrBicubicEffect::Make(GrSurfaceProxyView vi const GrCaps& caps) { GrSamplerState sampler(wrapX, wrapY, GrSamplerState::Filter::kNearest); std::unique_ptr fp; - fp = GrTextureEffect::Make(std::move(view), alphaType, SkMatrix::I(), sampler, caps); + fp = GrTextureEffect::Make(std::move(view), alphaType, matrix, sampler, caps); auto clamp = kPremul_SkAlphaType == alphaType ? Clamp::kPremul : Clamp::kUnpremul; return std::unique_ptr( - new GrBicubicEffect(std::move(fp), matrix, direction, clamp)); + new GrBicubicEffect(std::move(fp), direction, clamp)); } std::unique_ptr GrBicubicEffect::MakeSubset( @@ -150,32 +182,23 @@ std::unique_ptr GrBicubicEffect::MakeSubset( const GrCaps& caps) { GrSamplerState sampler(wrapX, wrapY, GrSamplerState::Filter::kNearest); std::unique_ptr fp; - fp = GrTextureEffect::MakeSubset( - std::move(view), alphaType, SkMatrix::I(), sampler, subset, caps); + fp = GrTextureEffect::MakeSubset(std::move(view), alphaType, matrix, sampler, subset, caps); auto clamp = kPremul_SkAlphaType == alphaType ? Clamp::kPremul : Clamp::kUnpremul; return std::unique_ptr( - new GrBicubicEffect(std::move(fp), matrix, direction, clamp)); -} - -std::unique_ptr GrBicubicEffect::Make(std::unique_ptr fp, - SkAlphaType alphaType, - const SkMatrix& matrix, - Direction direction) { - auto clamp = kPremul_SkAlphaType == alphaType ? Clamp::kPremul : Clamp::kUnpremul; - return std::unique_ptr( - new GrBicubicEffect(std::move(fp), matrix, direction, clamp)); + new GrBicubicEffect(std::move(fp), direction, clamp)); } GrBicubicEffect::GrBicubicEffect(std::unique_ptr fp, - const SkMatrix& matrix, Direction direction, Clamp clamp) : INHERITED(kGrBicubicEffect_ClassID, ProcessorOptimizationFlags(fp.get())) - , fCoordTransform(matrix) , fDirection(direction) , fClamp(clamp) { - fp->setSampledWithExplicitCoords(true); + SkASSERT(fp->numCoordTransforms() == 1); + fCoordTransform = fp->coordTransform(0); this->addCoordTransform(&fCoordTransform); + fp->coordTransform(0) = {}; + fp->setSampledWithExplicitCoords(true); this->registerChildProcessor(std::move(fp)); } @@ -192,7 +215,8 @@ GrBicubicEffect::GrBicubicEffect(const GrBicubicEffect& that) void GrBicubicEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { - uint32_t key = static_cast(fDirection) | (static_cast(fClamp) << 2); + uint32_t key = (fDirection == GrBicubicEffect::Direction::kXY) + | (static_cast(fClamp) << 1); b->add32(key); } @@ -203,10 +227,6 @@ bool GrBicubicEffect::onIsEqual(const GrFragmentProcessor& other) const { return fDirection == that.fDirection && fClamp == that.fClamp; } -SkPMColor4f GrBicubicEffect::constantOutputForConstantInput(const SkPMColor4f& input) const { - return GrFragmentProcessor::ConstantOutputForConstantInput(this->childProcessor(0), input); -} - GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrBicubicEffect); #if GR_TEST_UTILS @@ -223,42 +243,24 @@ std::unique_ptr GrBicubicEffect::TestCreate(GrProcessorTest direction = Direction::kXY; break; } + auto [view, ct, at] = d->randomView(); auto m = GrTest::TestMatrix(d->fRandom); - switch (d->fRandom->nextULessThan(3)) { - case 0: { - auto [view, ct, at] = d->randomView(); - GrSamplerState::WrapMode wm[2]; - GrTest::TestWrapModes(d->fRandom, wm); + if (d->fRandom->nextBool()) { + GrSamplerState::WrapMode wm[2]; + GrTest::TestWrapModes(d->fRandom, wm); - if (d->fRandom->nextBool()) { - SkRect subset; - subset.fLeft = d->fRandom->nextSScalar1() * view.width(); - subset.fTop = d->fRandom->nextSScalar1() * view.height(); - subset.fRight = d->fRandom->nextSScalar1() * view.width(); - subset.fBottom = d->fRandom->nextSScalar1() * view.height(); - subset.sort(); - return MakeSubset( - std::move(view), at, m, wm[0], wm[1], subset, direction, *d->caps()); - } - return Make(std::move(view), at, m, wm[0], wm[1], direction, *d->caps()); - } - case 1: { - auto [view, ct, at] = d->randomView(); - return Make(std::move(view), at, m, direction); - } - default: { - SkAlphaType at; - do { - at = static_cast(d->fRandom->nextULessThan(kLastEnum_SkAlphaType + 1)); - } while (at != kUnknown_SkAlphaType); - std::unique_ptr fp; - // We have a restriction that explicit coords only work for FPs with zero or one - // coord transform. - do { - fp = GrProcessorUnitTest::MakeChildFP(d); - } while (fp->numCoordTransforms() > 1); - return Make(std::move(fp), at, m, direction); + if (d->fRandom->nextBool()) { + SkRect subset; + subset.fLeft = d->fRandom->nextSScalar1() * view.width(); + subset.fTop = d->fRandom->nextSScalar1() * view.height(); + subset.fRight = d->fRandom->nextSScalar1() * view.width(); + subset.fBottom = d->fRandom->nextSScalar1() * view.height(); + subset.sort(); + return MakeSubset(std::move(view), at, m, wm[0], wm[1], subset, direction, *d->caps()); } + return Make(std::move(view), at, m, wm[0], wm[1], direction, *d->caps()); + } else { + return Make(std::move(view), at, m, direction); } } #endif diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h index 51b43f6c9e..1ad5688f59 100644 --- a/src/gpu/effects/GrBicubicEffect.h +++ b/src/gpu/effects/GrBicubicEffect.h @@ -66,16 +66,6 @@ public: const SkRect& subset, Direction, const GrCaps&); - - /** - * Make a Mitchell filter of a another fragment processor. The bicubic filter assumes that the - * discrete samples of the provided processor are at half-integer coords. - */ - static std::unique_ptr Make(std::unique_ptr, - SkAlphaType, - const SkMatrix&, - Direction); - /** * Determines whether the bicubic effect should be used based on the transformation from the * local coords to the device. Returns true if the bicubic effect should be used. filterMode @@ -94,8 +84,7 @@ private: kPremul, // clamps a to 0..1 and rgb to 0..a }; - GrBicubicEffect(std::unique_ptr, const SkMatrix&, Direction, Clamp); - + GrBicubicEffect(std::unique_ptr fp, Direction direction, Clamp clamp); explicit GrBicubicEffect(const GrBicubicEffect&); GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; @@ -104,8 +93,6 @@ private: bool onIsEqual(const GrFragmentProcessor&) const override; - SkPMColor4f constantOutputForConstantInput(const SkPMColor4f&) const override; - GrCoordTransform fCoordTransform; Direction fDirection; Clamp fClamp; diff --git a/src/gpu/effects/GrDeviceSpaceEffect.fp b/src/gpu/effects/GrDeviceSpaceEffect.fp index 879c7c4d7c..d817ec2b19 100644 --- a/src/gpu/effects/GrDeviceSpaceEffect.fp +++ b/src/gpu/effects/GrDeviceSpaceEffect.fp @@ -13,10 +13,10 @@ void main() { @test(d) { std::unique_ptr fp; - // We have a restriction that explicit coords only work for FPs with zero or one + // We have a restriction that explicit coords only work for FPs with exactly one // coord transform. do { fp = GrProcessorUnitTest::MakeChildFP(d); - } while (fp->numCoordTransforms() > 1); + } while (fp->numCoordTransforms() != 1); return GrDeviceSpaceEffect::Make(std::move(fp)); } diff --git a/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp b/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp index fbab6e19f7..daf7eb9628 100644 --- a/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp +++ b/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp @@ -61,11 +61,11 @@ GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceEffect); #if GR_TEST_UTILS std::unique_ptr GrDeviceSpaceEffect::TestCreate(GrProcessorTestData* d) { std::unique_ptr fp; - // We have a restriction that explicit coords only work for FPs with zero or one + // We have a restriction that explicit coords only work for FPs with exactly one // coord transform. do { fp = GrProcessorUnitTest::MakeChildFP(d); - } while (fp->numCoordTransforms() > 1); + } while (fp->numCoordTransforms() != 1); return GrDeviceSpaceEffect::Make(std::move(fp)); } #endif