michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkLinearGradient.h" michael@0: michael@0: static inline int repeat_bits(int x, const int bits) { michael@0: return x & ((1 << bits) - 1); michael@0: } michael@0: michael@0: static inline int repeat_8bits(int x) { michael@0: return x & 0xFF; michael@0: } michael@0: michael@0: // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly. michael@0: // See http://code.google.com/p/skia/issues/detail?id=472 michael@0: #if defined(_MSC_VER) && (_MSC_VER >= 1600) michael@0: #pragma optimize("", off) michael@0: #endif michael@0: michael@0: static inline int mirror_bits(int x, const int bits) { michael@0: if (x & (1 << bits)) { michael@0: x = ~x; michael@0: } michael@0: return x & ((1 << bits) - 1); michael@0: } michael@0: michael@0: static inline int mirror_8bits(int x) { michael@0: if (x & 256) { michael@0: x = ~x; michael@0: } michael@0: return x & 255; michael@0: } michael@0: michael@0: #if defined(_MSC_VER) && (_MSC_VER >= 1600) michael@0: #pragma optimize("", on) michael@0: #endif michael@0: michael@0: static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) { michael@0: SkVector vec = pts[1] - pts[0]; michael@0: SkScalar mag = vec.length(); michael@0: SkScalar inv = mag ? SkScalarInvert(mag) : 0; michael@0: michael@0: vec.scale(inv); michael@0: matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); michael@0: matrix->postTranslate(-pts[0].fX, -pts[0].fY); michael@0: matrix->postScale(inv, inv); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc) michael@0: : SkGradientShaderBase(desc) michael@0: , fStart(pts[0]) michael@0: , fEnd(pts[1]) { michael@0: pts_to_unit_matrix(pts, &fPtsToUnit); michael@0: } michael@0: michael@0: SkLinearGradient::SkLinearGradient(SkReadBuffer& buffer) michael@0: : INHERITED(buffer) michael@0: , fStart(buffer.readPoint()) michael@0: , fEnd(buffer.readPoint()) { michael@0: } michael@0: michael@0: void SkLinearGradient::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: buffer.writePoint(fStart); michael@0: buffer.writePoint(fEnd); michael@0: } michael@0: michael@0: bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint, michael@0: const SkMatrix& matrix) { michael@0: if (!this->INHERITED::setContext(device, paint, matrix)) { michael@0: return false; michael@0: } michael@0: michael@0: unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; michael@0: if ((fDstToIndex.getType() & ~mask) == 0) { michael@0: // when we dither, we are (usually) not const-in-Y michael@0: if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) { michael@0: // only claim this if we do have a 16bit mode (i.e. none of our michael@0: // colors have alpha), and if we are not dithering (which obviously michael@0: // is not const in Y). michael@0: fFlags |= SkShader::kConstInY16_Flag; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: #define NO_CHECK_ITER \ michael@0: do { \ michael@0: unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \ michael@0: SkASSERT(fi <= 0xFF); \ michael@0: fx += dx; \ michael@0: *dstC++ = cache[toggle + fi]; \ michael@0: toggle = next_dither_toggle(toggle); \ michael@0: } while (0) michael@0: michael@0: namespace { michael@0: michael@0: typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx, michael@0: SkPMColor* dstC, const SkPMColor* cache, michael@0: int toggle, int count); michael@0: michael@0: // Linear interpolation (lerp) is unnecessary if there are no sharp michael@0: // discontinuities in the gradient - which must be true if there are michael@0: // only 2 colors - but it's cheap. michael@0: void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx, michael@0: SkPMColor* SK_RESTRICT dstC, michael@0: const SkPMColor* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: // We're a vertical gradient, so no change in a span. michael@0: // If colors change sharply across the gradient, dithering is michael@0: // insufficient (it subsamples the color space) and we need to lerp. michael@0: unsigned fullIndex = proc(fx); michael@0: unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift; michael@0: unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1); michael@0: michael@0: int index0 = fi + toggle; michael@0: int index1 = index0; michael@0: if (fi < SkGradientShaderBase::kCache32Count - 1) { michael@0: index1 += 1; michael@0: } michael@0: SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); michael@0: index0 ^= SkGradientShaderBase::kDitherStride32; michael@0: index1 ^= SkGradientShaderBase::kDitherStride32; michael@0: SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); michael@0: sk_memset32_dither(dstC, lerp, dlerp, count); michael@0: } michael@0: michael@0: void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, michael@0: SkPMColor* SK_RESTRICT dstC, michael@0: const SkPMColor* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: SkClampRange range; michael@0: range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); michael@0: michael@0: if ((count = range.fCount0) > 0) { michael@0: sk_memset32_dither(dstC, michael@0: cache[toggle + range.fV0], michael@0: cache[next_dither_toggle(toggle) + range.fV0], michael@0: count); michael@0: dstC += count; michael@0: } michael@0: if ((count = range.fCount1) > 0) { michael@0: int unroll = count >> 3; michael@0: fx = range.fFx1; michael@0: for (int i = 0; i < unroll; i++) { michael@0: NO_CHECK_ITER; NO_CHECK_ITER; michael@0: NO_CHECK_ITER; NO_CHECK_ITER; michael@0: NO_CHECK_ITER; NO_CHECK_ITER; michael@0: NO_CHECK_ITER; NO_CHECK_ITER; michael@0: } michael@0: if ((count &= 7) > 0) { michael@0: do { michael@0: NO_CHECK_ITER; michael@0: } while (--count != 0); michael@0: } michael@0: } michael@0: if ((count = range.fCount2) > 0) { michael@0: sk_memset32_dither(dstC, michael@0: cache[toggle + range.fV1], michael@0: cache[next_dither_toggle(toggle) + range.fV1], michael@0: count); michael@0: } michael@0: } michael@0: michael@0: void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, michael@0: SkPMColor* SK_RESTRICT dstC, michael@0: const SkPMColor* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: do { michael@0: unsigned fi = mirror_8bits(fx >> 8); michael@0: SkASSERT(fi <= 0xFF); michael@0: fx += dx; michael@0: *dstC++ = cache[toggle + fi]; michael@0: toggle = next_dither_toggle(toggle); michael@0: } while (--count != 0); michael@0: } michael@0: michael@0: void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, michael@0: SkPMColor* SK_RESTRICT dstC, michael@0: const SkPMColor* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: do { michael@0: unsigned fi = repeat_8bits(fx >> 8); michael@0: SkASSERT(fi <= 0xFF); michael@0: fx += dx; michael@0: *dstC++ = cache[toggle + fi]; michael@0: toggle = next_dither_toggle(toggle); michael@0: } while (--count != 0); michael@0: } michael@0: michael@0: } michael@0: michael@0: void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, michael@0: int count) { michael@0: SkASSERT(count > 0); michael@0: michael@0: SkPoint srcPt; michael@0: SkMatrix::MapXYProc dstProc = fDstToIndexProc; michael@0: TileProc proc = fTileProc; michael@0: const SkPMColor* SK_RESTRICT cache = this->getCache32(); michael@0: int toggle = init_dither_toggle(x, y); michael@0: michael@0: if (fDstToIndexClass != kPerspective_MatrixClass) { michael@0: dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, michael@0: SkIntToScalar(y) + SK_ScalarHalf, &srcPt); michael@0: SkFixed dx, fx = SkScalarToFixed(srcPt.fX); michael@0: michael@0: if (fDstToIndexClass == kFixedStepInX_MatrixClass) { michael@0: SkFixed dxStorage[1]; michael@0: (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); michael@0: dx = dxStorage[0]; michael@0: } else { michael@0: SkASSERT(fDstToIndexClass == kLinear_MatrixClass); michael@0: dx = SkScalarToFixed(fDstToIndex.getScaleX()); michael@0: } michael@0: michael@0: LinearShadeProc shadeProc = shadeSpan_linear_repeat; michael@0: if (0 == dx) { michael@0: shadeProc = shadeSpan_linear_vertical_lerp; michael@0: } else if (SkShader::kClamp_TileMode == fTileMode) { michael@0: shadeProc = shadeSpan_linear_clamp; michael@0: } else if (SkShader::kMirror_TileMode == fTileMode) { michael@0: shadeProc = shadeSpan_linear_mirror; michael@0: } else { michael@0: SkASSERT(SkShader::kRepeat_TileMode == fTileMode); michael@0: } michael@0: (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); michael@0: } else { michael@0: SkScalar dstX = SkIntToScalar(x); michael@0: SkScalar dstY = SkIntToScalar(y); michael@0: do { michael@0: dstProc(fDstToIndex, dstX, dstY, &srcPt); michael@0: unsigned fi = proc(SkScalarToFixed(srcPt.fX)); michael@0: SkASSERT(fi <= 0xFFFF); michael@0: *dstC++ = cache[toggle + (fi >> kCache32Shift)]; michael@0: toggle = next_dither_toggle(toggle); michael@0: dstX += SK_Scalar1; michael@0: } while (--count != 0); michael@0: } michael@0: } michael@0: michael@0: SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap, michael@0: SkMatrix* matrix, michael@0: TileMode xy[]) const { michael@0: if (bitmap) { michael@0: this->getGradientTableBitmap(bitmap); michael@0: } michael@0: if (matrix) { michael@0: matrix->preConcat(fPtsToUnit); michael@0: } michael@0: if (xy) { michael@0: xy[0] = fTileMode; michael@0: xy[1] = kClamp_TileMode; michael@0: } michael@0: return kLinear_BitmapType; michael@0: } michael@0: michael@0: SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { michael@0: if (info) { michael@0: commonAsAGradient(info); michael@0: info->fPoint[0] = fStart; michael@0: info->fPoint[1] = fEnd; michael@0: } michael@0: return kLinear_GradientType; michael@0: } michael@0: michael@0: static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, michael@0: int count) { michael@0: if (reinterpret_cast(dst) & 2) { michael@0: *dst++ = value; michael@0: count -= 1; michael@0: SkTSwap(value, other); michael@0: } michael@0: michael@0: sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); michael@0: michael@0: if (count & 1) { michael@0: dst[count - 1] = value; michael@0: } michael@0: } michael@0: michael@0: #define NO_CHECK_ITER_16 \ michael@0: do { \ michael@0: unsigned fi = fx >> SkGradientShaderBase::kCache16Shift; \ michael@0: SkASSERT(fi < SkGradientShaderBase::kCache16Count); \ michael@0: fx += dx; \ michael@0: *dstC++ = cache[toggle + fi]; \ michael@0: toggle = next_dither_toggle16(toggle); \ michael@0: } while (0) michael@0: michael@0: namespace { michael@0: michael@0: typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx, michael@0: uint16_t* dstC, const uint16_t* cache, michael@0: int toggle, int count); michael@0: michael@0: void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx, michael@0: uint16_t* SK_RESTRICT dstC, michael@0: const uint16_t* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: // we're a vertical gradient, so no change in a span michael@0: unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift; michael@0: SkASSERT(fi < SkGradientShaderBase::kCache16Count); michael@0: dither_memset16(dstC, cache[toggle + fi], michael@0: cache[next_dither_toggle16(toggle) + fi], count); michael@0: } michael@0: michael@0: void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, michael@0: uint16_t* SK_RESTRICT dstC, michael@0: const uint16_t* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: SkClampRange range; michael@0: range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); michael@0: michael@0: if ((count = range.fCount0) > 0) { michael@0: dither_memset16(dstC, michael@0: cache[toggle + range.fV0], michael@0: cache[next_dither_toggle16(toggle) + range.fV0], michael@0: count); michael@0: dstC += count; michael@0: } michael@0: if ((count = range.fCount1) > 0) { michael@0: int unroll = count >> 3; michael@0: fx = range.fFx1; michael@0: for (int i = 0; i < unroll; i++) { michael@0: NO_CHECK_ITER_16; NO_CHECK_ITER_16; michael@0: NO_CHECK_ITER_16; NO_CHECK_ITER_16; michael@0: NO_CHECK_ITER_16; NO_CHECK_ITER_16; michael@0: NO_CHECK_ITER_16; NO_CHECK_ITER_16; michael@0: } michael@0: if ((count &= 7) > 0) { michael@0: do { michael@0: NO_CHECK_ITER_16; michael@0: } while (--count != 0); michael@0: } michael@0: } michael@0: if ((count = range.fCount2) > 0) { michael@0: dither_memset16(dstC, michael@0: cache[toggle + range.fV1], michael@0: cache[next_dither_toggle16(toggle) + range.fV1], michael@0: count); michael@0: } michael@0: } michael@0: michael@0: void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, michael@0: uint16_t* SK_RESTRICT dstC, michael@0: const uint16_t* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: do { michael@0: unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift, michael@0: SkGradientShaderBase::kCache16Bits); michael@0: SkASSERT(fi < SkGradientShaderBase::kCache16Count); michael@0: fx += dx; michael@0: *dstC++ = cache[toggle + fi]; michael@0: toggle = next_dither_toggle16(toggle); michael@0: } while (--count != 0); michael@0: } michael@0: michael@0: void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, michael@0: uint16_t* SK_RESTRICT dstC, michael@0: const uint16_t* SK_RESTRICT cache, michael@0: int toggle, int count) { michael@0: do { michael@0: unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift, michael@0: SkGradientShaderBase::kCache16Bits); michael@0: SkASSERT(fi < SkGradientShaderBase::kCache16Count); michael@0: fx += dx; michael@0: *dstC++ = cache[toggle + fi]; michael@0: toggle = next_dither_toggle16(toggle); michael@0: } while (--count != 0); michael@0: } michael@0: } michael@0: michael@0: static bool fixed_nearly_zero(SkFixed x) { michael@0: return SkAbs32(x) < (SK_Fixed1 >> 12); michael@0: } michael@0: michael@0: void SkLinearGradient::shadeSpan16(int x, int y, michael@0: uint16_t* SK_RESTRICT dstC, int count) { michael@0: SkASSERT(count > 0); michael@0: michael@0: SkPoint srcPt; michael@0: SkMatrix::MapXYProc dstProc = fDstToIndexProc; michael@0: TileProc proc = fTileProc; michael@0: const uint16_t* SK_RESTRICT cache = this->getCache16(); michael@0: int toggle = init_dither_toggle16(x, y); michael@0: michael@0: if (fDstToIndexClass != kPerspective_MatrixClass) { michael@0: dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, michael@0: SkIntToScalar(y) + SK_ScalarHalf, &srcPt); michael@0: SkFixed dx, fx = SkScalarToFixed(srcPt.fX); michael@0: michael@0: if (fDstToIndexClass == kFixedStepInX_MatrixClass) { michael@0: SkFixed dxStorage[1]; michael@0: (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); michael@0: dx = dxStorage[0]; michael@0: } else { michael@0: SkASSERT(fDstToIndexClass == kLinear_MatrixClass); michael@0: dx = SkScalarToFixed(fDstToIndex.getScaleX()); michael@0: } michael@0: michael@0: LinearShade16Proc shadeProc = shadeSpan16_linear_repeat; michael@0: if (fixed_nearly_zero(dx)) { michael@0: shadeProc = shadeSpan16_linear_vertical; michael@0: } else if (SkShader::kClamp_TileMode == fTileMode) { michael@0: shadeProc = shadeSpan16_linear_clamp; michael@0: } else if (SkShader::kMirror_TileMode == fTileMode) { michael@0: shadeProc = shadeSpan16_linear_mirror; michael@0: } else { michael@0: SkASSERT(SkShader::kRepeat_TileMode == fTileMode); michael@0: } michael@0: (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); michael@0: } else { michael@0: SkScalar dstX = SkIntToScalar(x); michael@0: SkScalar dstY = SkIntToScalar(y); michael@0: do { michael@0: dstProc(fDstToIndex, dstX, dstY, &srcPt); michael@0: unsigned fi = proc(SkScalarToFixed(srcPt.fX)); michael@0: SkASSERT(fi <= 0xFFFF); michael@0: michael@0: int index = fi >> kCache16Shift; michael@0: *dstC++ = cache[toggle + index]; michael@0: toggle = next_dither_toggle16(toggle); michael@0: michael@0: dstX += SK_Scalar1; michael@0: } while (--count != 0); michael@0: } michael@0: } michael@0: michael@0: #if SK_SUPPORT_GPU michael@0: michael@0: #include "GrTBackendEffectFactory.h" michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: class GrGLLinearGradient : public GrGLGradientEffect { michael@0: public: michael@0: michael@0: GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&) michael@0: : INHERITED (factory) { } michael@0: michael@0: virtual ~GrGLLinearGradient() { } michael@0: michael@0: virtual void emitCode(GrGLShaderBuilder*, michael@0: const GrDrawEffect&, michael@0: EffectKey, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray&) SK_OVERRIDE; michael@0: michael@0: static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { michael@0: return GenBaseGradientKey(drawEffect); michael@0: } michael@0: michael@0: private: michael@0: michael@0: typedef GrGLGradientEffect INHERITED; michael@0: }; michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: class GrLinearGradient : public GrGradientEffect { michael@0: public: michael@0: michael@0: static GrEffectRef* Create(GrContext* ctx, michael@0: const SkLinearGradient& shader, michael@0: const SkMatrix& matrix, michael@0: SkShader::TileMode tm) { michael@0: AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm))); michael@0: return CreateEffectRef(effect); michael@0: } michael@0: michael@0: virtual ~GrLinearGradient() { } michael@0: michael@0: static const char* Name() { return "Linear Gradient"; } michael@0: const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: typedef GrGLLinearGradient GLEffect; michael@0: michael@0: private: michael@0: GrLinearGradient(GrContext* ctx, michael@0: const SkLinearGradient& shader, michael@0: const SkMatrix& matrix, michael@0: SkShader::TileMode tm) michael@0: : INHERITED(ctx, shader, matrix, tm) { } michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: typedef GrGradientEffect INHERITED; michael@0: }; michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrLinearGradient); michael@0: michael@0: GrEffectRef* GrLinearGradient::TestCreate(SkRandom* random, michael@0: GrContext* context, michael@0: const GrDrawTargetCaps&, michael@0: GrTexture**) { michael@0: SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()}, michael@0: {random->nextUScalar1(), random->nextUScalar1()}}; michael@0: michael@0: SkColor colors[kMaxRandomGradientColors]; michael@0: SkScalar stopsArray[kMaxRandomGradientColors]; michael@0: SkScalar* stops = stopsArray; michael@0: SkShader::TileMode tm; michael@0: int colorCount = RandomGradientParams(random, colors, &stops, &tm); michael@0: SkAutoTUnref shader(SkGradientShader::CreateLinear(points, michael@0: colors, stops, colorCount, michael@0: tm)); michael@0: SkPaint paint; michael@0: return shader->asNewEffect(context, paint); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect&, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray& coords, michael@0: const TextureSamplerArray& samplers) { michael@0: this->emitUniforms(builder, key); michael@0: SkString t = builder->ensureFSCoords2D(coords, 0); michael@0: t.append(".x"); michael@0: this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const { michael@0: SkASSERT(NULL != context); michael@0: SkMatrix matrix; michael@0: if (!this->getLocalMatrix().invert(&matrix)) { michael@0: return NULL; michael@0: } michael@0: matrix.postConcat(fPtsToUnit); michael@0: return GrLinearGradient::Create(context, *this, matrix, fTileMode); michael@0: } michael@0: michael@0: #else michael@0: michael@0: GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const { michael@0: SkDEBUGFAIL("Should not call in GPU-less build"); michael@0: return NULL; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #ifndef SK_IGNORE_TO_STRING michael@0: void SkLinearGradient::toString(SkString* str) const { michael@0: str->append("SkLinearGradient ("); michael@0: michael@0: str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); michael@0: str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); michael@0: michael@0: this->INHERITED::toString(str); michael@0: michael@0: str->append(")"); michael@0: } michael@0: #endif