From 5119bdb952025a30f115b9c6a187173956e55097 Mon Sep 17 00:00:00 2001 From: "reed@android.com" Date: Fri, 12 Jun 2009 21:27:03 +0000 Subject: [PATCH] add shader flag kConstInY_Flag this signals blitters that the shader will return the same results for a given x value, independent of y. Useful inside blitRect(), where it can cache the first call to shadeSpan() and reuse it on all subsequent scans. Works with (non-rotated) linear-gradients, and Nx1 bitmaps. git-svn-id: http://skia.googlecode.com/svn/trunk@214 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkColorShader.h | 8 ++++-- include/core/SkShader.h | 10 ++++++- samplecode/SampleTestGL.cpp | 16 +++++++++++ src/core/SkBitmapProcShader.cpp | 11 ++++++++ src/core/SkBlitter.cpp | 7 ++--- src/core/SkBlitter_RGB16.cpp | 28 +++++++++++++++++-- src/core/SkCoreBlitters.h | 6 ++-- src/core/SkShader.cpp | 11 ++++---- src/effects/SkGradientShader.cpp | 14 ++++++++++ .../SampleApp.xcodeproj/project.pbxproj | 4 +-- 10 files changed, 96 insertions(+), 19 deletions(-) diff --git a/include/core/SkColorShader.h b/include/core/SkColorShader.h index f9c3dc33d2..7c5f941c0a 100644 --- a/include/core/SkColorShader.h +++ b/include/core/SkColorShader.h @@ -29,14 +29,15 @@ public: /** Create a ColorShader that will inherit its color from the Paint at draw time. */ - SkColorShader() : fInheritColor(true) {} + SkColorShader() : fFlags(0), fInheritColor(true) {} + /** Create a ColorShader that ignores the color in the paint, and uses the specified color. Note: like all shaders, at draw time the paint's alpha will be respected, and is applied to the specified color. */ - SkColorShader(SkColor c) : fColor(c), fInheritColor(false) {} + SkColorShader(SkColor c) : fColor(c), fFlags(0), fInheritColor(false) {} - virtual uint32_t getFlags(); + virtual uint32_t getFlags() { return fFlags; } virtual uint8_t getSpan16Alpha() const; virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix); @@ -54,6 +55,7 @@ private: } SkColor fColor; // ignored if fInheritColor is true SkPMColor fPMColor; // cached after setContext() + uint32_t fFlags; // cached after setContext() uint16_t fColor16; // cached after setContext() SkBool8 fInheritColor; diff --git a/include/core/SkShader.h b/include/core/SkShader.h index 7c13e3d3e8..8fac8cbb08 100644 --- a/include/core/SkShader.h +++ b/include/core/SkShader.h @@ -63,14 +63,22 @@ public: enum Flags { //!< set if all of the colors will be opaque kOpaqueAlpha_Flag = 0x01, + //! set if this shader's shadeSpan16() method can be called kHasSpan16_Flag = 0x02, + /** Set this bit if the shader's native data type is instrinsically 16 bit, meaning that calling the 32bit shadeSpan() entry point will mean the the impl has to up-sample 16bit data into 32bit. Used as a a means of clearing a dither request if the it will have no effect */ - kIntrinsicly16_Flag = 0x04 + kIntrinsicly16_Flag = 0x04, + + /** set (after setContext) if the spans only vary in X (const in Y). + e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient + that varies from left-to-right + */ + kConstInY_Flag = 0x08 }; /** Called sometimes before drawing with this shader. diff --git a/samplecode/SampleTestGL.cpp b/samplecode/SampleTestGL.cpp index c12eb7ccac..210d7799e1 100644 --- a/samplecode/SampleTestGL.cpp +++ b/samplecode/SampleTestGL.cpp @@ -9,6 +9,19 @@ #include "SkShader.h" #include "SkUtils.h" +static void show_ramp(SkCanvas* canvas, const SkRect& r) { + SkPoint pts[] = { r.fLeft, 0, r.fRight, 0 }; + SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; + SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kRepeat_TileMode); + SkPaint p; + p.setShader(s)->unref(); + canvas->drawRect(r, p); + canvas->translate(r.width() + SkIntToScalar(8), 0); + p.setDither(true); + canvas->drawRect(r, p); +} + class TestGLView : public SkView { public: TestGLView() { @@ -49,6 +62,9 @@ protected: canvas->translate(r.width() + SkIntToScalar(20), 0); paint.setStrokeWidth(SkIntToScalar(5)); canvas->drawRect(r, paint); + + canvas->translate(r.width() * 10/9, 0); + show_ramp(canvas, r); } private: diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp index 8ba57c4ae3..2302e0f048 100644 --- a/src/core/SkBitmapProcShader.cpp +++ b/src/core/SkBitmapProcShader.cpp @@ -67,6 +67,11 @@ void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) { buffer.write8(fState.fTileModeY); } +static bool only_scale_and_translate(const SkMatrix& matrix) { + unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; + return (matrix.getType() & ~mask) == 0; +} + bool SkBitmapProcShader::setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) { @@ -117,6 +122,12 @@ bool SkBitmapProcShader::setContext(const SkBitmap& device, default: break; } + + // if we're only 1-pixel heigh, and we don't rotate, then we can claim this + if (1 == fState.fBitmap->height() && + only_scale_and_translate(this->getTotalInverse())) { + fFlags |= kConstInY_Flag; + } return true; } diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp index 25303ac6b8..a38b46e99a 100644 --- a/src/core/SkBlitter.cpp +++ b/src/core/SkBlitter.cpp @@ -977,17 +977,16 @@ const uint32_t gMask_00FF00FF = 0xFF00FF; ////////////////////////////////////////////////////////////////////////////////////////////////////// SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint) - : INHERITED(device) -{ + : INHERITED(device) { fShader = paint.getShader(); SkASSERT(fShader); fShader->ref(); fShader->beginSession(); + fShaderFlags = fShader->getFlags(); } -SkShaderBlitter::~SkShaderBlitter() -{ +SkShaderBlitter::~SkShaderBlitter() { fShader->endSession(); fShader->unref(); } diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp index b253662da3..40eceeca5c 100644 --- a/src/core/SkBlitter_RGB16.cpp +++ b/src/core/SkBlitter_RGB16.cpp @@ -483,7 +483,7 @@ void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) { SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkBitmap& device, const SkPaint& paint) : SkRGB16_Shader_Blitter(device, paint) { - SkASSERT(SkShader::CanCallShadeSpan16(fShader->getFlags())); + SkASSERT(SkShader::CanCallShadeSpan16(fShaderFlags)); } void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) SK_RESTRICT { @@ -569,7 +569,7 @@ SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device, // compute SkBlitRow::Procs unsigned flags = 0; - uint32_t shaderFlags = fShader->getFlags(); + uint32_t shaderFlags = fShaderFlags; // shaders take care of global alpha, so we never set it in SkBlitRow if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) { flags |= SkBlitRow::kSrcPixelAlpha_Flag; @@ -597,6 +597,30 @@ void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) { fOpaqueProc(fDevice.getAddr16(x, y), fBuffer, width, 0xFF, x, y); } +void SkRGB16_Shader_Blitter::blitRect(int x, int y, int width, int height) { + SkShader* shader = fShader; + SkBlitRow::Proc proc = fOpaqueProc; + SkPMColor* buffer = fBuffer; + uint16_t* dst = fDevice.getAddr16(x, y); + size_t dstRB = fDevice.rowBytes(); + + if (fShaderFlags & SkShader::kConstInY_Flag) { + shader->shadeSpan(x, y, buffer, width); + do { + proc(dst, buffer, width, 0xFF, x, y); + y += 1; + dst = (uint16_t*)((char*)dst + dstRB); + } while (--height); + } else { + do { + shader->shadeSpan(x, y, buffer, width); + proc(dst, buffer, width, 0xFF, x, y); + y += 1; + dst = (uint16_t*)((char*)dst + dstRB); + } while (--height); + } +} + static inline int count_nonzero_span(const int16_t runs[], const SkAlpha aa[]) { int count = 0; for (;;) { diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h index 5b3497e089..8f9cfc36f9 100644 --- a/src/core/SkCoreBlitters.h +++ b/src/core/SkCoreBlitters.h @@ -38,7 +38,8 @@ public: virtual ~SkShaderBlitter(); protected: - SkShader* fShader; + uint32_t fShaderFlags; + SkShader* fShader; private: // illegal @@ -192,7 +193,8 @@ public: virtual ~SkRGB16_Shader_Blitter(); virtual void blitH(int x, int y, int width); virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]); - + virtual void blitRect(int x, int y, int width, int height); + protected: SkPMColor* fBuffer; SkBlitRow::Proc fOpaqueProc; diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp index dd9c8590b8..5567914530 100644 --- a/src/core/SkShader.cpp +++ b/src/core/SkShader.cpp @@ -210,6 +210,7 @@ SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, #include "SkUtils.h" SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) { + fFlags = 0; // computed in setContext fInheritColor = b.readU8(); if (fInheritColor) { return; @@ -226,11 +227,6 @@ void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) { buffer.write32(fColor); } -uint32_t SkColorShader::getFlags() { - return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : 0) | - kHasSpan16_Flag; -} - uint8_t SkColorShader::getSpan16Alpha() const { return SkGetPackedA32(fPMColor); } @@ -267,6 +263,11 @@ bool SkColorShader::setContext(const SkBitmap& device, const SkPaint& paint, } fPMColor = SkPackARGB32(a, r, g, b); + fFlags = kHasSpan16_Flag | kConstInY_Flag; + if (SkGetPackedA32(fPMColor) == 255) { + fFlags |= kOpaqueAlpha_Flag; + } + return true; } diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp index 87d70c23b0..e1a92ba3bf 100644 --- a/src/effects/SkGradientShader.cpp +++ b/src/effects/SkGradientShader.cpp @@ -620,6 +620,7 @@ public: } } + virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&); virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*); @@ -646,6 +647,19 @@ private: typedef Gradient_Shader INHERITED; }; +bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint, + const SkMatrix& matrix) { + if (!this->INHERITED::setContext(device, paint, matrix)) { + return false; + } + + unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; + if ((fDstToIndex.getType() & ~mask) == 0) { + fFlags |= SkShader::kConstInY_Flag; + } + return true; +} + // Return true if fx, fx+dx, fx+2*dx, ... is always in range static inline bool no_need_for_clamp(int fx, int dx, int count) { diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj index d3679c4824..c67031436d 100644 --- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj +++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj @@ -71,7 +71,7 @@ 00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6760FCCCB01002BD8B4 /* SampleMovie.cpp */; }; 00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A7282D0FD43D3700D5051F /* SkMovie.cpp */; }; 00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; }; - 00A729650FD93ED600D5051F /* SampleTestGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A729630FD93ED600D5051F /* SampleTestGL.cpp */; }; + 00AF77B00FE2EA2D007F9650 /* SampleTestGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A729630FD93ED600D5051F /* SampleTestGL.cpp */; }; 00C55DA10F8552DC000CAC09 /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */; }; 00FF39140FC6ED2C00915187 /* SampleEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00FF39130FC6ED2C00915187 /* SampleEffects.cpp */; }; 0156F80407C56A3000C6122B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0156F80307C56A3000C6122B /* Foundation.framework */; }; @@ -569,8 +569,8 @@ 00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */, 00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */, 00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */, - 00A729650FD93ED600D5051F /* SampleTestGL.cpp in Sources */, 000A99820FD97526007E45BD /* SampleArc.cpp in Sources */, + 00AF77B00FE2EA2D007F9650 /* SampleTestGL.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -- 2.34.1