diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 28cdbf3f0..dc68fe062 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -56,3 +56,15 @@ image-surface aaclip composeshader peekpixels + +# egdaniel https://codereview.chromium.org/227623004/ +# This change will cause change two point conical gradients to change pixels values +# but without any real perceptual differences. +gradients +gradients_view_perspective +gradients_local_perspective +gradients_no_texture +twopointconical +lightingcolorfilter +radial_gradient2 +gradient_dirty_laundry diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 1f588a42b..bf9ec4f8b 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -323,13 +323,14 @@ protected: // V21: add pushCull, popCull // V22: SK_PICT_FACTORY_TAG's size is now the chunk size in bytes // V23: SkPaint::FilterLevel became a real enum + // V24: SkTwoPointConicalGradient now has fFlipped flag for gradient flipping // Note: If the picture version needs to be increased then please follow the // steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw // Only SKPs within the min/current picture version range (inclusive) can be read. static const uint32_t MIN_PICTURE_VERSION = 19; - static const uint32_t CURRENT_PICTURE_VERSION = 23; + static const uint32_t CURRENT_PICTURE_VERSION = 24; mutable uint32_t fUniqueID; diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp index f155cc5eb..7f0840470 100644 --- a/src/effects/gradients/SkGradientShader.cpp +++ b/src/effects/gradients/SkGradientShader.cpp @@ -118,12 +118,13 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc) { SkFixed dp = SK_Fixed1 / (desc.fCount - 1); SkFixed p = dp; SkFixed scale = (desc.fCount - 1) << 8; // (1 << 24) / dp - for (int i = 1; i < desc.fCount; i++) { + for (int i = 1; i < desc.fCount - 1; i++) { recs->fPos = p; recs->fScale = scale; recs += 1; p += dp; } + recs->fPos = SK_Fixed1; } } this->initCommon(); @@ -237,6 +238,30 @@ SkGradientShaderBase::GpuColorType SkGradientShaderBase::getGpuColorType(SkColor return kTexture_GpuColorType; } +void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst, + SkColor* colorSrc, Rec* recSrc, + int count) { + SkAutoSTArray<8, SkColor> colorsTemp(count); + for (int i = 0; i < count; ++i) { + int offset = count - i - 1; + colorsTemp[i] = colorSrc[offset]; + } + if (count > 2) { + SkAutoSTArray<8, Rec> recsTemp(count); + for (int i = 0; i < count; ++i) { + int offset = count - i - 1; + recsTemp[i].fPos = SK_Fixed1 - recSrc[offset].fPos; + recsTemp[i].fScale = recSrc[offset].fScale; + } + memcpy(recDst, recsTemp.get(), count * sizeof(Rec)); + } + memcpy(colorDst, colorsTemp.get(), count * sizeof(SkColor)); +} + +void SkGradientShaderBase::flipGradientColors() { + FlipGradientColors(fOrigColors, fRecs, fOrigColors, fRecs, fColorCount); +} + bool SkGradientShaderBase::isOpaque() const { return fColorsAreOpaque; } @@ -643,11 +668,23 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const { } } -void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const { +void SkGradientShaderBase::commonAsAGradient(GradientInfo* info, bool flipGrad) const { if (info) { if (info->fColorCount >= fColorCount) { + SkColor* colorLoc; + Rec* recLoc; + if (flipGrad && (info->fColors || info->fColorOffsets)) { + SkAutoSTArray<8, SkColor> colorStorage(fColorCount); + SkAutoSTArray<8, Rec> recStorage(fColorCount); + colorLoc = colorStorage.get(); + recLoc = recStorage.get(); + FlipGradientColors(colorLoc, recLoc, fOrigColors, fRecs, fColorCount); + } else { + colorLoc = fOrigColors; + recLoc = fRecs; + } if (info->fColors) { - memcpy(info->fColors, fOrigColors, fColorCount * sizeof(SkColor)); + memcpy(info->fColors, colorLoc, fColorCount * sizeof(SkColor)); } if (info->fColorOffsets) { if (fColorCount == 2) { @@ -655,7 +692,7 @@ void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const { info->fColorOffsets[1] = SK_Scalar1; } else if (fColorCount > 2) { for (int i = 0; i < fColorCount; ++i) { - info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos); + info->fColorOffsets[i] = SkFixedToScalar(recLoc[i].fPos); } } } @@ -801,12 +838,36 @@ SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start, if (start == end && startRadius == endRadius) { return SkNEW(SkEmptyShader); } + EXPAND_1_COLOR(colorCount); + bool flipGradient = startRadius > endRadius; + SkGradientShaderBase::Descriptor desc; - desc_init(&desc, colors, pos, colorCount, mode, mapper, flags); - return SkNEW_ARGS(SkTwoPointConicalGradient, - (start, startRadius, end, endRadius, desc)); + + if (!flipGradient) { + desc_init(&desc, colors, pos, colorCount, mode, mapper, flags); + return SkNEW_ARGS(SkTwoPointConicalGradient, + (start, startRadius, end, endRadius, flipGradient, desc)); + } else { + SkAutoSTArray<8, SkColor> colorsNew(colorCount); + SkAutoSTArray<8, SkScalar> posNew(colorCount); + for (int i = 0; i < colorCount; ++i) { + colorsNew[i] = colors[colorCount - i - 1]; + } + + if (pos) { + for (int i = 0; i < colorCount; ++i) { + posNew[i] = 1 - pos[colorCount - i - 1]; + } + desc_init(&desc, colorsNew.get(), posNew.get(), colorCount, mode, mapper, flags); + } else { + desc_init(&desc, colorsNew.get(), NULL, colorCount, mode, mapper, flags); + } + + return SkNEW_ARGS(SkTwoPointConicalGradient, + (end, endRadius, start, startRadius, flipGradient, desc)); + } } SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy, diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h index 6a24d0f15..84fe49880 100644 --- a/src/effects/gradients/SkGradientShaderPriv.h +++ b/src/effects/gradients/SkGradientShaderPriv.h @@ -166,7 +166,23 @@ protected: const uint16_t* getCache16() const; const SkPMColor* getCache32() const; - void commonAsAGradient(GradientInfo*) const; + void commonAsAGradient(GradientInfo*, bool flipGrad = false) const; + + /* + * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively. + * Count is the number of colors in the gradient + * It will then flip all the color and rec information and return in their respective Dst + * pointers. It is assumed that space has already been allocated for the Dst pointers. + * The rec src and dst are only assumed to be valid if count > 2 + */ + static void FlipGradientColors(SkColor* colorDst, Rec* recDst, + SkColor* colorSrc, Rec* recSrc, + int count); + + // V23_COMPATIBILITY_CODE + // Used for 2-pt conical gradients since we sort start/end cirlces by radius + // Assumes space has already been allocated for fOrigColors + void flipGradientColors(); private: enum { diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp index de8c2364c..1e6a0d812 100644 --- a/src/effects/gradients/SkTwoPointConicalGradient.cpp +++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp @@ -20,7 +20,7 @@ static int valid_divide(float numer, float denom, float* ratio) { // Return the number of distinct real roots, and write them into roots[] in // ascending order -static int find_quad_roots(float A, float B, float C, float roots[2]) { +static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) { SkASSERT(roots); if (A == 0) { @@ -54,6 +54,9 @@ static int find_quad_roots(float A, float B, float C, float roots[2]) { float r1 = C / Q; roots[0] = r0 < r1 ? r0 : r1; roots[1] = r0 > r1 ? r0 : r1; + if (descendingOrder) { + SkTSwap(roots[0], roots[1]); + } return 2; } @@ -64,7 +67,8 @@ static float lerp(float x, float dx, float t) { static float sqr(float x) { return x * x; } void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, - const SkPoint& center1, SkScalar rad1) { + const SkPoint& center1, SkScalar rad1, + bool flipped) { fCenterX = SkScalarToFloat(center0.fX); fCenterY = SkScalarToFloat(center0.fY); fDCenterX = SkScalarToFloat(center1.fX) - fCenterX; @@ -75,6 +79,8 @@ void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius); fRadius2 = sqr(fRadius); fRDR = fRadius * fDRadius; + + fFlipped = flipped; } void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) { @@ -90,7 +96,7 @@ SkFixed TwoPtRadial::nextT() { float roots[2]; float C = sqr(fRelX) + sqr(fRelY) - fRadius2; - int countRoots = find_quad_roots(fA, fB, C, roots); + int countRoots = find_quad_roots(fA, fB, C, roots, fFlipped); fRelX += fIncX; fRelY += fIncY; @@ -169,7 +175,7 @@ static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, } void SkTwoPointConicalGradient::init() { - fRec.init(fCenter1, fRadius1, fCenter2, fRadius2); + fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad); fPtsToUnit.reset(); } @@ -178,12 +184,13 @@ void SkTwoPointConicalGradient::init() { SkTwoPointConicalGradient::SkTwoPointConicalGradient( const SkPoint& start, SkScalar startRadius, const SkPoint& end, SkScalar endRadius, - const Descriptor& desc) + bool flippedGrad, const Descriptor& desc) : SkGradientShaderBase(desc), fCenter1(start), fCenter2(end), fRadius1(startRadius), - fRadius2(endRadius) { + fRadius2(endRadius), + fFlippedGrad(flippedGrad) { // this is degenerate, and should be caught by our caller SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); this->init(); @@ -299,14 +306,19 @@ SkShader::BitmapType SkTwoPointConicalGradient::asABitmap( return kTwoPointConical_BitmapType; } +// Returns the original non-sorted version of the gradient SkShader::GradientType SkTwoPointConicalGradient::asAGradient( GradientInfo* info) const { if (info) { - commonAsAGradient(info); + commonAsAGradient(info, fFlippedGrad); info->fPoint[0] = fCenter1; info->fPoint[1] = fCenter2; info->fRadius[0] = fRadius1; info->fRadius[1] = fRadius2; + if (fFlippedGrad) { + SkTSwap(info->fPoint[0], info->fPoint[1]); + SkTSwap(info->fRadius[0], info->fRadius[1]); + } } return kConical_GradientType; } @@ -318,6 +330,20 @@ SkTwoPointConicalGradient::SkTwoPointConicalGradient( fCenter2(buffer.readPoint()), fRadius1(buffer.readScalar()), fRadius2(buffer.readScalar()) { + if (buffer.pictureVersion() >= 24 || 0 == buffer.pictureVersion()) { + fFlippedGrad = buffer.readBool(); + } else { + // V23_COMPATIBILITY_CODE + // Sort gradient by radius size for old pictures + if (fRadius2 < fRadius1) { + SkTSwap(fCenter1, fCenter2); + SkTSwap(fRadius1, fRadius2); + this->flipGradientColors(); + fFlippedGrad = true; + } else { + fFlippedGrad = false; + } + } this->init(); }; @@ -328,6 +354,7 @@ void SkTwoPointConicalGradient::flatten( buffer.writePoint(fCenter2); buffer.writeScalar(fRadius1); buffer.writeScalar(fRadius2); + buffer.writeBool(fFlippedGrad); } #if SK_SUPPORT_GPU diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h index 041f8b8be..5548b1798 100644 --- a/src/effects/gradients/SkTwoPointConicalGradient.h +++ b/src/effects/gradients/SkTwoPointConicalGradient.h @@ -23,9 +23,11 @@ struct TwoPtRadial { float fA; float fRadius2; float fRDR; + bool fFlipped; void init(const SkPoint& center0, SkScalar rad0, - const SkPoint& center1, SkScalar rad1); + const SkPoint& center1, SkScalar rad1, + bool flipped); // used by setup and nextT float fRelX, fRelY, fIncX, fIncY; @@ -47,7 +49,7 @@ class SkTwoPointConicalGradient : public SkGradientShaderBase { public: SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius, const SkPoint& end, SkScalar endRadius, - const Descriptor&); + bool flippedGrad, const Descriptor&); virtual void shadeSpan(int x, int y, SkPMColor* dstCParam, int count) SK_OVERRIDE; @@ -68,6 +70,7 @@ public: const SkPoint& getStartCenter() const { return fCenter1; } const SkPoint& getEndCenter() const { return fCenter2; } SkScalar getEndRadius() const { return fRadius2; } + bool isFlippedGrad() const { return fFlippedGrad; } SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient) @@ -77,11 +80,13 @@ protected: virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE; private: + SkPoint fCenter1; + SkPoint fCenter2; + SkScalar fRadius1; + SkScalar fRadius2; + bool fFlippedGrad; + typedef SkGradientShaderBase INHERITED; - const SkPoint fCenter1; - const SkPoint fCenter2; - const SkScalar fRadius1; - const SkScalar fRadius2; }; #endif diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp index d7b038401..7cdb62dc4 100644 --- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp +++ b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp @@ -56,6 +56,7 @@ public: // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); } + bool isFlipped() const { return fIsFlipped; } SkScalar center() const { return fCenterX1; } SkScalar diffRadius() const { return fDiffRadius; } SkScalar radius() const { return fRadius0; } @@ -68,7 +69,8 @@ private: return (INHERITED::onIsEqual(sBase) && this->fCenterX1 == s.fCenterX1 && this->fRadius0 == s.fRadius0 && - this->fDiffRadius == s.fDiffRadius); + this->fDiffRadius == s.fDiffRadius && + this->fIsFlipped == s.fIsFlipped); } Default2PtConicalEffect(GrContext* ctx, @@ -78,7 +80,8 @@ private: : INHERITED(ctx, shader, matrix, tm), fCenterX1(shader.getCenterX1()), fRadius0(shader.getStartRadius()), - fDiffRadius(shader.getDiffRadius()) { + fDiffRadius(shader.getDiffRadius()), + fIsFlipped(shader.isFlippedGrad()) { // We pass the linear part of the quadratic as a varying. // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) fBTransform = this->getCoordTransform(); @@ -103,6 +106,7 @@ private: SkScalar fCenterX1; SkScalar fRadius0; SkScalar fDiffRadius; + bool fIsFlipped; // @} @@ -132,6 +136,7 @@ protected: const char* fFSVaryingName; bool fIsDegenerate; + bool fIsFlipped; // @{ /// Values last uploaded as uniforms @@ -194,6 +199,7 @@ GLDefault2PtConicalEffect::GLDefault2PtConicalEffect(const GrBackendEffectFactor const Default2PtConicalEffect& data = drawEffect.castEffect(); fIsDegenerate = data.isDegenerate(); + fIsFlipped = data.isFlipped(); } void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, @@ -281,9 +287,14 @@ void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, // Note: If there are two roots that both generate radius(t) > 0, the // Canvas spec says to choose the larger t. - // so we'll look at the larger one first: - builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), - r0Name.c_str(), r1Name.c_str()); + // so we'll look at the larger one first (or smaller if flipped): + if (!fIsFlipped) { + builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), + r0Name.c_str(), r1Name.c_str()); + } else { + builder->fsCodeAppendf("\t\tfloat %s = min(%s, %s);\n", tName.c_str(), + r0Name.c_str(), r1Name.c_str()); + } // if r(t) > 0, then we're done; t will be our x coordinate builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), @@ -294,8 +305,13 @@ void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, // otherwise, if r(t) for the larger root was <= 0, try the other root builder->fsCodeAppend("\t\t} else {\n"); - builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), - r0Name.c_str(), r1Name.c_str()); + if (!fIsFlipped) { + builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), + r0Name.c_str(), r1Name.c_str()); + } else { + builder->fsCodeAppendf("\t\t\t%s = max(%s, %s);\n", tName.c_str(), + r0Name.c_str(), r1Name.c_str()); + } // if r(t) > 0 for the smaller root, then t will be our x coordinate builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n", @@ -330,6 +346,7 @@ void GLDefault2PtConicalEffect::setData(const GrGLUniformManager& uman, INHERITED::setData(uman, drawEffect); const Default2PtConicalEffect& data = drawEffect.castEffect(); SkASSERT(data.isDegenerate() == fIsDegenerate); + SkASSERT(data.isFlipped() == fIsFlipped); SkScalar centerX1 = data.center(); SkScalar radius0 = data.radius(); SkScalar diffRadius = data.diffRadius(); @@ -365,12 +382,16 @@ GrGLEffect::EffectKey GLDefault2PtConicalEffect::GenKey(const GrDrawEffect& draw const GrGLCaps&) { enum { kIsDegenerate = 1 << kBaseKeyBitCnt, + kIsFlipped = 1 << (kBaseKeyBitCnt + 1), }; EffectKey key = GenBaseGradientKey(drawEffect); if (drawEffect.castEffect().isDegenerate()) { key |= kIsDegenerate; } + if (drawEffect.castEffect().isFlipped()) { + key |= kIsFlipped; + } return key; }