diff --git a/gm/SkLinearBitmapPipelineGM.cpp b/gm/SkLinearBitmapPipelineGM.cpp index 6b354df947..d3a143c5c1 100644 --- a/gm/SkLinearBitmapPipelineGM.cpp +++ b/gm/SkLinearBitmapPipelineGM.cpp @@ -60,7 +60,7 @@ static void draw_rect_orig(SkCanvas* canvas, const SkRect& r, SkColor c, const S SkAutoTUnref image{SkImage::NewRasterCopy( info, pmsrc.addr32(), pmsrc.rowBytes())}; SkPaint paint; - int32_t storage[200]; + int32_t storage[300]; SkShader* shader = image->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); if (useBilerp) { paint.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality); diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp index 895922c6b8..38f46d18b4 100644 --- a/src/core/SkBitmapProcShader.cpp +++ b/src/core/SkBitmapProcShader.cpp @@ -27,8 +27,7 @@ static bool only_scale_and_translate(const SkMatrix& matrix) { class BitmapProcInfoContext : public SkShader::Context { public: - // The context takes ownership of the info. It will call its destructor - // but will NOT free the memory. + // The info has been allocated elsewhere, but we are responsible for calling its destructor. BitmapProcInfoContext(const SkShader& shader, const SkShader::ContextRec& rec, SkBitmapProcInfo* info) : INHERITED(shader, rec) @@ -45,8 +44,6 @@ public: } ~BitmapProcInfoContext() override { - // The bitmap proc state has been created outside of the context on memory that will be freed - // elsewhere. Only call the destructor but leave the freeing of the memory to the caller. fInfo->~SkBitmapProcInfo(); } @@ -63,8 +60,6 @@ private: class BitmapProcShaderContext : public BitmapProcInfoContext { public: - // The context takes ownership of the state. It will call its destructor - // but will NOT free the memory. BitmapProcShaderContext(const SkShader& shader, const SkShader::ContextRec& rec, SkBitmapProcState* state) : INHERITED(shader, rec, state) @@ -116,11 +111,80 @@ private: }; /////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkLinearBitmapPipeline.h" +#include "SkPM4f.h" +#include "SkXfermode.h" -size_t SkBitmapProcShader::ContextSize(const ContextRec& rec) { - // The SkBitmapProcState is stored outside of the context object, with the context holding - // a pointer to it. - return sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState); +class LinearPipelineContext : public BitmapProcInfoContext { +public: + LinearPipelineContext(const SkShader& shader, const SkShader::ContextRec& rec, + SkBitmapProcInfo* info) + : INHERITED(shader, rec, info) + , fPipeline(info->fInvMatrix, info->fFilterQuality, info->fTileModeX, info->fTileModeY, + info->fPixmap) + { + // To implement the old shadeSpan entry-point, we need to efficiently convert our native + // floats into SkPMColor. The SkXfermode::D32Procs do exactly that. + // + sk_sp xfer(SkXfermode::Create(SkXfermode::kSrc_Mode)); + fXferProc = SkXfermode::GetD32Proc(xfer.get(), 0); + } + + void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override { + fPipeline.shadeSpan4f(x, y, dstC, count); + } + + void shadeSpan(int x, int y, SkPMColor dstC[], int count) override { + const int N = 128; + SkPM4f tmp[N]; + + while (count > 0) { + const int n = SkTMin(count, N); + fPipeline.shadeSpan4f(x, y, tmp, n); + fXferProc(nullptr, dstC, tmp, n, nullptr); + dstC += n; + x += n; + count -= n; + } + } + +private: + SkLinearBitmapPipeline fPipeline; + SkXfermode::D32Proc fXferProc; + + typedef BitmapProcInfoContext INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool choose_linear_pipeline(const SkShader::ContextRec& rec, const SkImageInfo& srcInfo) { + // These src attributes are not supported in the new 4f context (yet) + // + if (srcInfo.bytesPerPixel() < 4 || + kRGBA_F16_SkColorType == srcInfo.colorType()) { + return false; + } + +#if 0 // later we may opt-in to the new code even if the client hasn't requested it... + // These src attributes are only supported in the new 4f context + // + if (srcInfo.isSRGB() || + kUnpremul_SkAlphaType == srcInfo.alphaType() || + (4 == srcInfo.bytesPerPixel() && kN32_SkColorType != srcInfo.colorType())) + { + return true; + } +#endif + + // If we get here, we can reasonably use either context, respect the caller's preference + // + return SkShader::ContextRec::kPM4f_DstType == rec.fPreferredDstType; +} + +size_t SkBitmapProcShader::ContextSize(const ContextRec& rec, const SkImageInfo& srcInfo) { + size_t size0 = sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState); + size_t size1 = sizeof(LinearPipelineContext) + sizeof(SkBitmapProcInfo); + return SkTMax(size0, size1); } SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader, @@ -133,16 +197,32 @@ SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader, return nullptr; } - void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext); - SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy); - - SkASSERT(state); - if (!state->setup(totalInverse, *rec.fPaint)) { - state->~SkBitmapProcState(); - return nullptr; + // Decide if we can/want to use the new linear pipeine + bool useLinearPipeline = choose_linear_pipeline(rec, provider.info()); + if (SkShader::kMirror_TileMode == tmx || SkShader::kMirror_TileMode == tmy) { + useLinearPipeline = false; + } + if (totalInverse.hasPerspective()) { + useLinearPipeline = false; } - return new (storage) BitmapProcShaderContext(shader, rec, state); + if (useLinearPipeline) { + void* infoStorage = (char*)storage + sizeof(LinearPipelineContext); + SkBitmapProcInfo* info = new (infoStorage) SkBitmapProcInfo(provider, tmx, tmy); + if (!info->init(totalInverse, *rec.fPaint)) { + info->~SkBitmapProcInfo(); + return nullptr; + } + return new (storage) LinearPipelineContext(shader, rec, info); + } else { + void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext); + SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy); + if (!state->setup(totalInverse, *rec.fPaint)) { + state->~SkBitmapProcState(); + return nullptr; + } + return new (storage) BitmapProcShaderContext(shader, rec, state); + } } SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, void* storage) const { diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h index 9f4c16202c..a83fda7628 100644 --- a/src/core/SkBitmapProcShader.h +++ b/src/core/SkBitmapProcShader.h @@ -21,7 +21,9 @@ public: bool isOpaque() const override; - size_t contextSize(const ContextRec& rec) const override { return ContextSize(rec); } + size_t contextSize(const ContextRec& rec) const override { + return ContextSize(rec, fRawBitmap.info()); + } SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapProcShader) @@ -36,13 +38,13 @@ protected: Context* onCreateContext(const ContextRec&, void* storage) const override; bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override; - SkBitmap fRawBitmap; // experimental for RLE encoding + SkBitmap fRawBitmap; uint8_t fTileModeX, fTileModeY; private: friend class SkImageShader; - static size_t ContextSize(const ContextRec&); + static size_t ContextSize(const ContextRec&, const SkImageInfo& srcInfo); static Context* MakeContext(const SkShader&, TileMode tmx, TileMode tmy, const SkBitmapProvider&, const ContextRec&, void* storage); @@ -54,7 +56,7 @@ private: // an Sk3DBlitter in SkDraw.cpp // Note that some contexts may contain other contexts (e.g. for compose shaders), but we've not // yet found a situation where the size below isn't big enough. -typedef SkSmallAllocator<3, 1500> SkTBlitterAllocator; +typedef SkSmallAllocator<3, 2100> SkTBlitterAllocator; // If alloc is non-nullptr, it will be used to allocate the returned SkShader, and MUST outlive // the SkShader. diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp index 46ff4019fc..57f0989e2d 100644 --- a/src/core/SkComposeShader.cpp +++ b/src/core/SkComposeShader.cpp @@ -31,9 +31,9 @@ SkComposeShader::~SkComposeShader() { } size_t SkComposeShader::contextSize(const ContextRec& rec) const { - return sizeof(ComposeShaderContext) - + fShaderA->contextSize(rec) - + fShaderB->contextSize(rec); + return SkAlign16(sizeof(ComposeShaderContext)) + + SkAlign16(fShaderA->contextSize(rec)) + + SkAlign16(fShaderB->contextSize(rec)); } class SkAutoAlphaRestore { @@ -76,8 +76,8 @@ template void safe_call_destructor(T* obj) { } SkShader::Context* SkComposeShader::onCreateContext(const ContextRec& rec, void* storage) const { - char* aStorage = (char*) storage + sizeof(ComposeShaderContext); - char* bStorage = aStorage + fShaderA->contextSize(rec); + char* aStorage = (char*) storage + SkAlign16(sizeof(ComposeShaderContext)); + char* bStorage = aStorage + SkAlign16(fShaderA->contextSize(rec)); // we preconcat our localMatrix (if any) with the device matrix // before calling our sub-shaders diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp index e36df5b5af..5fb441a5c3 100644 --- a/src/core/SkShader.cpp +++ b/src/core/SkShader.cpp @@ -86,6 +86,9 @@ bool SkShader::asLuminanceColor(SkColor* colorPtr) const { } SkShader::Context* SkShader::createContext(const ContextRec& rec, void* storage) const { + // We currently require 16byte alignment for some of our subclasses, so assert that here. + SkASSERT(SkIsAlign16((intptr_t)storage)); + if (!this->computeTotalInverse(rec, nullptr)) { return nullptr; } diff --git a/src/image/SkImageShader.cpp b/src/image/SkImageShader.cpp index a07603c671..e8c16b8096 100644 --- a/src/image/SkImageShader.cpp +++ b/src/image/SkImageShader.cpp @@ -43,7 +43,7 @@ bool SkImageShader::isOpaque() const { } size_t SkImageShader::contextSize(const ContextRec& rec) const { - return SkBitmapProcShader::ContextSize(rec); + return SkBitmapProcShader::ContextSize(rec, SkBitmapProvider(fImage).info()); } SkShader::Context* SkImageShader::onCreateContext(const ContextRec& rec, void* storage) const { diff --git a/tests/SkColor4fTest.cpp b/tests/SkColor4fTest.cpp index c9656b9b2a..ea99c4b934 100644 --- a/tests/SkColor4fTest.cpp +++ b/tests/SkColor4fTest.cpp @@ -149,7 +149,7 @@ DEF_TEST(Color4f_shader, reporter) { SkPaint paint; for (const auto& rec : recs) { - uint32_t storage[200]; + uint32_t storage[300]; paint.setShader(rec.fFact())->unref(); // Encourage 4f context selection. At some point we may need // to instantiate two separate contexts for optimal 4b/4f selection.