diff options
author | Stan Iliev <stani@google.com> | 2017-05-25 22:07:16 +0000 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-05-25 22:30:19 +0000 |
commit | d8872be505c5c9c48072fe62c244e53e9b7334f1 (patch) | |
tree | ef6ae54d394f1bf0794393c42129811b93d0f6be /src/shaders | |
parent | 5b474d36238e38642add8d4b54cb9ac80935262b (diff) |
Revert "Relocate shaders to own dir"
This reverts commit fabe0b26d05624ce7374f6ca89bd66df6142534e.
Reason for revert: Last android roll failed with "external/skia/src/effects/SkGaussianEdgeShader.h:11:10: fatal error: 'SkShaderBase.h' file not found"
Original change's description:
> Relocate shaders to own dir
>
> Consolidate all shader impls under src/shaders/.
>
> Change-Id: I450e37541214704c1ad9e379d9d753b7cc62fac3
> Reviewed-on: https://skia-review.googlesource.com/17927
> Commit-Queue: Florin Malita <fmalita@chromium.org>
> Reviewed-by: Herb Derby <herb@google.com>
>
TBR=mtklein@google.com,herb@google.com,fmalita@chromium.org,reed@google.com
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Change-Id: Idbb2b75053969df1dad9d8ce0217cd39189b9ddb
Reviewed-on: https://skia-review.googlesource.com/18020
Reviewed-by: Stan Iliev <stani@google.com>
Commit-Queue: Stan Iliev <stani@google.com>
Diffstat (limited to 'src/shaders')
41 files changed, 0 insertions, 12445 deletions
diff --git a/src/shaders/SkBitmapProcShader.cpp b/src/shaders/SkBitmapProcShader.cpp deleted file mode 100644 index e52982354d..0000000000 --- a/src/shaders/SkBitmapProcShader.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBitmapProcShader.h" - -#include "SkArenaAlloc.h" -#include "SkBitmapProcState.h" -#include "SkBitmapProvider.h" -#include "SkXfermodePriv.h" - -static bool only_scale_and_translate(const SkMatrix& matrix) { - unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; - return (matrix.getType() & ~mask) == 0; -} - -class BitmapProcInfoContext : public SkShaderBase::Context { -public: - // The info has been allocated elsewhere, but we are responsible for calling its destructor. - BitmapProcInfoContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec, - SkBitmapProcInfo* info) - : INHERITED(shader, rec) - , fInfo(info) - { - fFlags = 0; - if (fInfo->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) { - fFlags |= SkShaderBase::kOpaqueAlpha_Flag; - } - - if (1 == fInfo->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) { - fFlags |= SkShaderBase::kConstInY32_Flag; - } - } - - uint32_t getFlags() const override { return fFlags; } - -private: - SkBitmapProcInfo* fInfo; - uint32_t fFlags; - - typedef SkShaderBase::Context INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -class BitmapProcShaderContext : public BitmapProcInfoContext { -public: - BitmapProcShaderContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec, - SkBitmapProcState* state) - : INHERITED(shader, rec, state) - , fState(state) - {} - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override { - const SkBitmapProcState& state = *fState; - if (state.getShaderProc32()) { - state.getShaderProc32()(&state, x, y, dstC, count); - return; - } - - const int BUF_MAX = 128; - uint32_t buffer[BUF_MAX]; - SkBitmapProcState::MatrixProc mproc = state.getMatrixProc(); - SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32(); - const int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX); - - SkASSERT(state.fPixmap.addr()); - - for (;;) { - int n = SkTMin(count, max); - SkASSERT(n > 0 && n < BUF_MAX*2); - mproc(state, buffer, n, x, y); - sproc(state, buffer, n, dstC); - - if ((count -= n) == 0) { - break; - } - SkASSERT(count > 0); - x += n; - dstC += n; - } - } - - ShadeProc asAShadeProc(void** ctx) override { - if (fState->getShaderProc32()) { - *ctx = fState; - return (ShadeProc)fState->getShaderProc32(); - } - return nullptr; - } - -private: - SkBitmapProcState* fState; - - typedef BitmapProcInfoContext INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#include "SkLinearBitmapPipeline.h" -#include "SkPM4f.h" - -class LinearPipelineContext : public BitmapProcInfoContext { -public: - LinearPipelineContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec, - SkBitmapProcInfo* info, SkArenaAlloc* alloc) - : INHERITED(shader, rec, info), fAllocator{alloc} - { - // Save things off in case we need to build a blitter pipeline. - fSrcPixmap = info->fPixmap; - fAlpha = SkColorGetA(info->fPaintColor) / 255.0f; - fFilterQuality = info->fFilterQuality; - fMatrixTypeMask = info->fRealInvMatrix.getType(); - - fShaderPipeline = alloc->make<SkLinearBitmapPipeline>( - info->fRealInvMatrix, info->fFilterQuality, - info->fTileModeX, info->fTileModeY, - info->fPaintColor, - info->fPixmap, - fAllocator); - - // To implement the old shadeSpan entry-point, we need to efficiently convert our native - // floats into SkPMColor. The SkXfermode::D32Procs do exactly that. - // - fSrcModeProc = SkXfermode::GetD32Proc(SkBlendMode::kSrc, 0); - } - - void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override { - fShaderPipeline->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); - fShaderPipeline->shadeSpan4f(x, y, tmp, n); - fSrcModeProc(SkBlendMode::kSrc, dstC, tmp, n, nullptr); - dstC += n; - x += n; - count -= n; - } - } - - bool onChooseBlitProcs(const SkImageInfo& dstInfo, BlitState* state) override { - if ((fBlitterPipeline = SkLinearBitmapPipeline::ClonePipelineForBlitting( - *fShaderPipeline, - fMatrixTypeMask, - fFilterQuality, fSrcPixmap, - fAlpha, state->fMode, dstInfo, fAllocator))) - { - state->fStorage[0] = fBlitterPipeline; - state->fBlitBW = &LinearPipelineContext::ForwardToPipeline; - - return true; - } - - return false; - } - - static void ForwardToPipeline(BlitState* state, int x, int y, const SkPixmap& dst, int count) { - SkLinearBitmapPipeline* pipeline = static_cast<SkLinearBitmapPipeline*>(state->fStorage[0]); - void* addr = dst.writable_addr32(x, y); - pipeline->blitSpan(x, y, addr, count); - } - -private: - // Store the allocator from the context creation incase we are asked to build a blitter. - SkArenaAlloc* fAllocator; - SkLinearBitmapPipeline* fShaderPipeline; - SkLinearBitmapPipeline* fBlitterPipeline; - SkXfermode::D32Proc fSrcModeProc; - SkPixmap fSrcPixmap; - float fAlpha; - SkMatrix::TypeMask fMatrixTypeMask; - SkFilterQuality fFilterQuality; - - typedef BitmapProcInfoContext INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static bool choose_linear_pipeline(const SkShaderBase::ContextRec& rec, const SkImageInfo& srcInfo) { - // If we get here, we can reasonably use either context, respect the caller's preference - // - bool needsPremul = srcInfo.alphaType() == kUnpremul_SkAlphaType; - bool needsSwizzle = srcInfo.bytesPerPixel() == 4 && srcInfo.colorType() != kN32_SkColorType; - return SkShaderBase::ContextRec::kPM4f_DstType == rec.fPreferredDstType - || needsPremul || needsSwizzle; -} - -size_t SkBitmapProcLegacyShader::ContextSize(const ContextRec& rec, const SkImageInfo& srcInfo) { - size_t size0 = sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState); - size_t size1 = sizeof(LinearPipelineContext) + sizeof(SkBitmapProcInfo); - size_t s = SkTMax(size0, size1); - return s; -} - -SkShaderBase::Context* SkBitmapProcLegacyShader::MakeContext( - const SkShaderBase& shader, TileMode tmx, TileMode tmy, - const SkBitmapProvider& provider, const ContextRec& rec, SkArenaAlloc* alloc) -{ - SkMatrix totalInverse; - // Do this first, so we know the matrix can be inverted. - if (!shader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &totalInverse)) { - return nullptr; - } - - // Decide if we can/want to use the new linear pipeline - bool useLinearPipeline = choose_linear_pipeline(rec, provider.info()); - - if (useLinearPipeline) { - SkBitmapProcInfo* info = alloc->make<SkBitmapProcInfo>(provider, tmx, tmy); - if (!info->init(totalInverse, *rec.fPaint)) { - return nullptr; - } - - return alloc->make<LinearPipelineContext>(shader, rec, info, alloc); - } else { - SkBitmapProcState* state = alloc->make<SkBitmapProcState>(provider, tmx, tmy); - if (!state->setup(totalInverse, *rec.fPaint)) { - return nullptr; - } - return alloc->make<BitmapProcShaderContext>(shader, rec, state); - } -} diff --git a/src/shaders/SkBitmapProcShader.h b/src/shaders/SkBitmapProcShader.h deleted file mode 100644 index 2a2599cb1d..0000000000 --- a/src/shaders/SkBitmapProcShader.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkBitmapProcShader_DEFINED -#define SkBitmapProcShader_DEFINED - -#include "SkImagePriv.h" -#include "SkShaderBase.h" - -class SkBitmapProvider; - -class SkBitmapProcLegacyShader : public SkShaderBase { -private: - friend class SkImageShader; - - static size_t ContextSize(const ContextRec&, const SkImageInfo& srcInfo); - static Context* MakeContext(const SkShaderBase&, TileMode tmx, TileMode tmy, - const SkBitmapProvider&, const ContextRec&, SkArenaAlloc* alloc); - - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkColorFilterShader.cpp b/src/shaders/SkColorFilterShader.cpp deleted file mode 100644 index 4798422dfa..0000000000 --- a/src/shaders/SkColorFilterShader.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArenaAlloc.h" -#include "SkColorFilterShader.h" -#include "SkColorSpaceXformer.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -#include "SkShader.h" -#include "SkString.h" - -#if SK_SUPPORT_GPU -#include "GrFragmentProcessor.h" -#endif - -SkColorFilterShader::SkColorFilterShader(sk_sp<SkShader> shader, sk_sp<SkColorFilter> filter) - : fShader(std::move(shader)) - , fFilter(std::move(filter)) -{ - SkASSERT(fShader); - SkASSERT(fFilter); -} - -sk_sp<SkFlattenable> SkColorFilterShader::CreateProc(SkReadBuffer& buffer) { - auto shader = buffer.readShader(); - auto filter = buffer.readColorFilter(); - if (!shader || !filter) { - return nullptr; - } - return sk_make_sp<SkColorFilterShader>(shader, filter); -} - -void SkColorFilterShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeFlattenable(fShader.get()); - buffer.writeFlattenable(fFilter.get()); -} - -uint32_t SkColorFilterShader::FilterShaderContext::getFlags() const { - const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader); - - uint32_t shaderF = fShaderContext->getFlags(); - uint32_t filterF = filterShader.fFilter->getFlags(); - - // If the filter does not support a given feature, but sure to clear the corresponding flag - // in the shader flags. - // - if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) { - shaderF &= ~kOpaqueAlpha_Flag; - } - return shaderF; -} - -SkShaderBase::Context* SkColorFilterShader::onMakeContext(const ContextRec& rec, - SkArenaAlloc* alloc) const { - auto* shaderContext = as_SB(fShader)->makeContext(rec, alloc); - if (nullptr == shaderContext) { - return nullptr; - } - return alloc->make<FilterShaderContext>(*this, shaderContext, rec); -} - -sk_sp<SkShader> SkColorFilterShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - return xformer->apply(fShader.get())->makeWithColorFilter(xformer->apply(fFilter.get())); -} - -SkColorFilterShader::FilterShaderContext::FilterShaderContext( - const SkColorFilterShader& filterShader, - SkShaderBase::Context* shaderContext, - const ContextRec& rec) - : INHERITED(filterShader, rec) - , fShaderContext(shaderContext) -{} - -void SkColorFilterShader::FilterShaderContext::shadeSpan(int x, int y, SkPMColor result[], - int count) { - const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader); - - fShaderContext->shadeSpan(x, y, result, count); - filterShader.fFilter->filterSpan(result, count, result); -} - -void SkColorFilterShader::FilterShaderContext::shadeSpan4f(int x, int y, SkPM4f result[], - int count) { - const SkColorFilterShader& filterShader = static_cast<const SkColorFilterShader&>(fShader); - - fShaderContext->shadeSpan4f(x, y, result, count); - filterShader.fFilter->filterSpan4f(result, count, result); -} - -#if SK_SUPPORT_GPU -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkColorFilterShader::asFragmentProcessor(const AsFPArgs& args) const { - - sk_sp<GrFragmentProcessor> fp1(as_SB(fShader)->asFragmentProcessor(args)); - if (!fp1) { - return nullptr; - } - - sk_sp<GrFragmentProcessor> fp2(fFilter->asFragmentProcessor(args.fContext, - args.fDstColorSpace)); - if (!fp2) { - return fp1; - } - - sk_sp<GrFragmentProcessor> fpSeries[] = { std::move(fp1), std::move(fp2) }; - return GrFragmentProcessor::RunInSeries(fpSeries, 2); -} -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkColorFilterShader::toString(SkString* str) const { - str->append("SkColorFilterShader: ("); - - str->append("Shader: "); - as_SB(fShader)->toString(str); - str->append(" Filter: "); - // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkShader> SkShader::makeWithColorFilter(sk_sp<SkColorFilter> filter) const { - SkShader* base = const_cast<SkShader*>(this); - if (!filter) { - return sk_ref_sp(base); - } - return sk_make_sp<SkColorFilterShader>(sk_ref_sp(base), filter); -} diff --git a/src/shaders/SkColorFilterShader.h b/src/shaders/SkColorFilterShader.h deleted file mode 100644 index 7f4202158a..0000000000 --- a/src/shaders/SkColorFilterShader.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkColorFilterShader_DEFINED -#define SkColorFilterShader_DEFINED - -#include "SkColorFilter.h" -#include "SkShaderBase.h" - -class SkArenaAlloc; - -class SkColorFilterShader : public SkShaderBase { -public: - SkColorFilterShader(sk_sp<SkShader> shader, sk_sp<SkColorFilter> filter); - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - class FilterShaderContext : public Context { - public: - // Takes ownership of shaderContext and calls its destructor. - FilterShaderContext(const SkColorFilterShader&, SkShaderBase::Context*, const ContextRec&); - - uint32_t getFlags() const override; - - void shadeSpan(int x, int y, SkPMColor[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f[], int count) override; - - void set3DMask(const SkMask* mask) override { - // forward to our proxy - fShaderContext->set3DMask(mask); - } - - private: - SkShaderBase::Context* fShaderContext; - - typedef Context INHERITED; - }; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterShader) - -protected: - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc* alloc) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - sk_sp<SkShader> fShader; - sk_sp<SkColorFilter> fFilter; - - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkColorShader.cpp b/src/shaders/SkColorShader.cpp deleted file mode 100644 index 32b2c54d71..0000000000 --- a/src/shaders/SkColorShader.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArenaAlloc.h" -#include "SkColorShader.h" -#include "SkColorSpace.h" -#include "SkPM4fPriv.h" -#include "SkRasterPipeline.h" -#include "SkReadBuffer.h" -#include "SkUtils.h" - -SkColorShader::SkColorShader(SkColor c) : fColor(c) {} - -bool SkColorShader::isOpaque() const { - return SkColorGetA(fColor) == 255; -} - -sk_sp<SkFlattenable> SkColorShader::CreateProc(SkReadBuffer& buffer) { - return sk_make_sp<SkColorShader>(buffer.readColor()); -} - -void SkColorShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeColor(fColor); -} - -uint32_t SkColorShader::ColorShaderContext::getFlags() const { - return fFlags; -} - -SkShaderBase::Context* SkColorShader::onMakeContext(const ContextRec& rec, - SkArenaAlloc* alloc) const { - return alloc->make<ColorShaderContext>(*this, rec); -} - -SkColorShader::ColorShaderContext::ColorShaderContext(const SkColorShader& shader, - const ContextRec& rec) - : INHERITED(shader, rec) -{ - SkColor color = shader.fColor; - unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha())); - - unsigned r = SkColorGetR(color); - unsigned g = SkColorGetG(color); - unsigned b = SkColorGetB(color); - - if (a != 255) { - r = SkMulDiv255Round(r, a); - g = SkMulDiv255Round(g, a); - b = SkMulDiv255Round(b, a); - } - fPMColor = SkPackARGB32(a, r, g, b); - - SkColor4f c4 = SkColor4f::FromColor(shader.fColor); - c4.fA *= rec.fPaint->getAlpha() / 255.0f; - fPM4f = c4.premul(); - - fFlags = kConstInY32_Flag; - if (255 == a) { - fFlags |= kOpaqueAlpha_Flag; - } -} - -void SkColorShader::ColorShaderContext::shadeSpan(int x, int y, SkPMColor span[], int count) { - sk_memset32(span, fPMColor, count); -} - -void SkColorShader::ColorShaderContext::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { - memset(alpha, SkGetPackedA32(fPMColor), count); -} - -void SkColorShader::ColorShaderContext::shadeSpan4f(int x, int y, SkPM4f span[], int count) { - for (int i = 0; i < count; ++i) { - span[i] = fPM4f; - } -} - -SkShader::GradientType SkColorShader::asAGradient(GradientInfo* info) const { - if (info) { - if (info->fColors && info->fColorCount >= 1) { - info->fColors[0] = fColor; - } - info->fColorCount = 1; - info->fTileMode = SkShader::kRepeat_TileMode; - } - return kColor_GradientType; -} - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "effects/GrConstColorProcessor.h" -sk_sp<GrFragmentProcessor> SkColorShader::asFragmentProcessor(const AsFPArgs& args) const { - GrColor4f color = SkColorToPremulGrColor4f(fColor, args.fDstColorSpace); - return GrConstColorProcessor::Make(color, GrConstColorProcessor::kModulateA_InputMode); -} - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkColorShader::toString(SkString* str) const { - str->append("SkColorShader: ("); - - str->append("Color: "); - str->appendHex(fColor); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static unsigned unit_to_byte(float unit) { - SkASSERT(unit >= 0 && unit <= 1); - return (unsigned)(unit * 255 + 0.5); -} - -static SkColor unit_to_skcolor(const SkColor4f& unit, SkColorSpace* cs) { - return SkColorSetARGB(unit_to_byte(unit.fA), unit_to_byte(unit.fR), - unit_to_byte(unit.fG), unit_to_byte(unit.fB)); -} - -SkColor4Shader::SkColor4Shader(const SkColor4f& color, sk_sp<SkColorSpace> space) - : fColorSpace(std::move(space)) - , fColor4(color) - , fCachedByteColor(unit_to_skcolor(color.pin(), space.get())) -{} - -sk_sp<SkFlattenable> SkColor4Shader::CreateProc(SkReadBuffer& buffer) { - SkColor4f color; - buffer.readColor4f(&color); - if (buffer.readBool()) { - // TODO how do we unflatten colorspaces - } - return SkShader::MakeColorShader(color, nullptr); -} - -void SkColor4Shader::flatten(SkWriteBuffer& buffer) const { - buffer.writeColor4f(fColor4); - buffer.writeBool(false); // TODO how do we flatten colorspaces? -} - -uint32_t SkColor4Shader::Color4Context::getFlags() const { - return fFlags; -} - -SkShaderBase::Context* SkColor4Shader::onMakeContext(const ContextRec& rec, - SkArenaAlloc* alloc) const { - return alloc->make<Color4Context>(*this, rec); -} - -SkColor4Shader::Color4Context::Color4Context(const SkColor4Shader& shader, - const ContextRec& rec) -: INHERITED(shader, rec) -{ - SkColor color = shader.fCachedByteColor; - unsigned a = SkAlphaMul(SkColorGetA(color), SkAlpha255To256(rec.fPaint->getAlpha())); - - unsigned r = SkColorGetR(color); - unsigned g = SkColorGetG(color); - unsigned b = SkColorGetB(color); - - if (a != 255) { - r = SkMulDiv255Round(r, a); - g = SkMulDiv255Round(g, a); - b = SkMulDiv255Round(b, a); - } - fPMColor = SkPackARGB32(a, r, g, b); - - SkColor4f c4 = shader.fColor4; - c4.fA *= rec.fPaint->getAlpha() * (1 / 255.0f); - fPM4f = c4.premul(); - - fFlags = kConstInY32_Flag; - if (255 == a) { - fFlags |= kOpaqueAlpha_Flag; - } -} - -void SkColor4Shader::Color4Context::shadeSpan(int x, int y, SkPMColor span[], int count) { - sk_memset32(span, fPMColor, count); -} - -void SkColor4Shader::Color4Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { - memset(alpha, SkGetPackedA32(fPMColor), count); -} - -void SkColor4Shader::Color4Context::shadeSpan4f(int x, int y, SkPM4f span[], int count) { - for (int i = 0; i < count; ++i) { - span[i] = fPM4f; - } -} - -// TODO: do we need an updated version of this method for color4+colorspace? -SkShader::GradientType SkColor4Shader::asAGradient(GradientInfo* info) const { - if (info) { - if (info->fColors && info->fColorCount >= 1) { - info->fColors[0] = fCachedByteColor; - } - info->fColorCount = 1; - info->fTileMode = SkShader::kRepeat_TileMode; - } - return kColor_GradientType; -} - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "effects/GrConstColorProcessor.h" -#include "GrColorSpaceXform.h" -sk_sp<GrFragmentProcessor> SkColor4Shader::asFragmentProcessor(const AsFPArgs& args) const { - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - GrColor4f color = GrColor4f::FromSkColor4f(fColor4); - if (colorSpaceXform) { - color = colorSpaceXform->apply(color); - } - return GrConstColorProcessor::Make(color.premul(), GrConstColorProcessor::kModulateA_InputMode); -} - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkColor4Shader::toString(SkString* str) const { - str->append("SkColor4Shader: ("); - - str->append("RGBA:"); - for (int i = 0; i < 4; ++i) { - str->appendf(" %g", fColor4.vec()[i]); - } - str->append(" )"); -} -#endif - -sk_sp<SkShader> SkColor4Shader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - return SkShader::MakeColorShader(xformer->apply(fCachedByteColor)); -} - -sk_sp<SkShader> SkShader::MakeColorShader(const SkColor4f& color, sk_sp<SkColorSpace> space) { - if (!SkScalarsAreFinite(color.vec(), 4)) { - return nullptr; - } - return sk_make_sp<SkColor4Shader>(color, std::move(space)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static void D32_BlitBW(SkShaderBase::Context::BlitState* state, int x, int y, const SkPixmap& dst, - int count) { - SkXfermode::D32Proc proc = (SkXfermode::D32Proc)state->fStorage[0]; - const SkPM4f* src = (const SkPM4f*)state->fStorage[1]; - proc(state->fMode, dst.writable_addr32(x, y), src, count, nullptr); -} - -static void D32_BlitAA(SkShaderBase::Context::BlitState* state, int x, int y, const SkPixmap& dst, - int count, const SkAlpha aa[]) { - SkXfermode::D32Proc proc = (SkXfermode::D32Proc)state->fStorage[0]; - const SkPM4f* src = (const SkPM4f*)state->fStorage[1]; - proc(state->fMode, dst.writable_addr32(x, y), src, count, aa); -} - -static void F16_BlitBW(SkShaderBase::Context::BlitState* state, int x, int y, const SkPixmap& dst, - int count) { - SkXfermode::F16Proc proc = (SkXfermode::F16Proc)state->fStorage[0]; - const SkPM4f* src = (const SkPM4f*)state->fStorage[1]; - proc(state->fMode, dst.writable_addr64(x, y), src, count, nullptr); -} - -static void F16_BlitAA(SkShaderBase::Context::BlitState* state, int x, int y, const SkPixmap& dst, - int count, const SkAlpha aa[]) { - SkXfermode::F16Proc proc = (SkXfermode::F16Proc)state->fStorage[0]; - const SkPM4f* src = (const SkPM4f*)state->fStorage[1]; - proc(state->fMode, dst.writable_addr64(x, y), src, count, aa); -} - -static bool choose_blitprocs(const SkPM4f* pm4, const SkImageInfo& info, - SkShaderBase::Context::BlitState* state) { - uint32_t flags = SkXfermode::kSrcIsSingle_D32Flag; - if (pm4->a() == 1) { - flags |= SkXfermode::kSrcIsOpaque_D32Flag; - } - switch (info.colorType()) { - case kN32_SkColorType: - if (info.gammaCloseToSRGB()) { - flags |= SkXfermode::kDstIsSRGB_D32Flag; - } - state->fStorage[0] = (void*)SkXfermode::GetD32Proc(state->fMode, flags); - state->fStorage[1] = (void*)pm4; - state->fBlitBW = D32_BlitBW; - state->fBlitAA = D32_BlitAA; - return true; - case kRGBA_F16_SkColorType: - state->fStorage[0] = (void*)SkXfermode::GetF16Proc(state->fMode, flags); - state->fStorage[1] = (void*)pm4; - state->fBlitBW = F16_BlitBW; - state->fBlitAA = F16_BlitAA; - return true; - default: - return false; - } -} - -bool SkColorShader::ColorShaderContext::onChooseBlitProcs(const SkImageInfo& info, - BlitState* state) { - return choose_blitprocs(&fPM4f, info, state); -} - -bool SkColor4Shader::Color4Context::onChooseBlitProcs(const SkImageInfo& info, BlitState* state) { - return choose_blitprocs(&fPM4f, info, state); -} - -bool SkColorShader::onAppendStages(SkRasterPipeline* p, - SkColorSpace* dst, - SkArenaAlloc* scratch, - const SkMatrix&, - const SkPaint&, - const SkMatrix*) const { - auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst)); - p->append(SkRasterPipeline::constant_color, color); - return true; -} - -bool SkColor4Shader::onAppendStages(SkRasterPipeline* p, - SkColorSpace* dst, - SkArenaAlloc* scratch, - const SkMatrix&, - const SkPaint&, - const SkMatrix*) const { - auto color = scratch->make<SkPM4f>(to_colorspace(fColor4, fColorSpace.get(), dst).premul()); - p->append(SkRasterPipeline::constant_color, color); - return true; -} diff --git a/src/shaders/SkColorShader.h b/src/shaders/SkColorShader.h deleted file mode 100644 index 9af83c1163..0000000000 --- a/src/shaders/SkColorShader.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2007 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkColorShader_DEFINED -#define SkColorShader_DEFINED - -#include "SkColorSpaceXformer.h" -#include "SkShaderBase.h" -#include "SkPM4f.h" - -/** \class SkColorShader - A Shader that represents a single color. In general, this effect can be - accomplished by just using the color field on the paint, but if an - actual shader object is needed, this provides that feature. -*/ -class SK_API SkColorShader : public SkShaderBase { -public: - /** 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. - */ - explicit SkColorShader(SkColor c); - - bool isOpaque() const override; - bool isConstant() const override { return true; } - - class ColorShaderContext : public Context { - public: - ColorShaderContext(const SkColorShader& shader, const ContextRec&); - - uint32_t getFlags() const override; - void shadeSpan(int x, int y, SkPMColor span[], int count) override; - void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f[], int count) override; - - protected: - bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override; - - private: - SkPM4f fPM4f; - SkPMColor fPMColor; - uint32_t fFlags; - - typedef Context INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader) - -protected: - SkColorShader(SkReadBuffer&); - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override; - - bool onAsLuminanceColor(SkColor* lum) const override { - *lum = fColor; - return true; - } - - bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, - const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override; - - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override { - return SkShader::MakeColorShader(xformer->apply(fColor)); - } - -private: - SkColor fColor; - - typedef SkShaderBase INHERITED; -}; - -class SkColor4Shader : public SkShaderBase { -public: - SkColor4Shader(const SkColor4f&, sk_sp<SkColorSpace>); - - bool isOpaque() const override { - return SkColorGetA(fCachedByteColor) == 255; - } - bool isConstant() const override { return true; } - - class Color4Context : public Context { - public: - Color4Context(const SkColor4Shader& shader, const ContextRec&); - - uint32_t getFlags() const override; - void shadeSpan(int x, int y, SkPMColor span[], int count) override; - void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f[], int count) override; - - protected: - bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override; - - private: - SkPM4f fPM4f; - SkPMColor fPMColor; - uint32_t fFlags; - - typedef Context INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader) - -protected: - SkColor4Shader(SkReadBuffer&); - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - bool onAsLuminanceColor(SkColor* lum) const override { - *lum = fCachedByteColor; - return true; - } - bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, - const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override; - - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - sk_sp<SkColorSpace> fColorSpace; - const SkColor4f fColor4; - const SkColor fCachedByteColor; - - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp deleted file mode 100644 index 7735494291..0000000000 --- a/src/shaders/SkComposeShader.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArenaAlloc.h" -#include "SkBlendModePriv.h" -#include "SkComposeShader.h" -#include "SkColorFilter.h" -#include "SkColorPriv.h" -#include "SkColorShader.h" -#include "SkRasterPipeline.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -#include "SkString.h" -#include "../jumper/SkJumper.h" - -sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src, - SkBlendMode mode) { - if (!src || !dst) { - return nullptr; - } - if (SkBlendMode::kSrc == mode) { - return src; - } - if (SkBlendMode::kDst == mode) { - return dst; - } - return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode)); -} - -/////////////////////////////////////////////////////////////////////////////// - -class SkAutoAlphaRestore { -public: - SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { - fAlpha = paint->getAlpha(); - fPaint = paint; - paint->setAlpha(newAlpha); - } - - ~SkAutoAlphaRestore() { - fPaint->setAlpha(fAlpha); - } -private: - SkPaint* fPaint; - uint8_t fAlpha; -}; -#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore) - -sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) { - sk_sp<SkShader> shaderA(buffer.readShader()); - sk_sp<SkShader> shaderB(buffer.readShader()); - SkBlendMode mode; - if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) { - sk_sp<SkXfermode> xfer = buffer.readXfermode(); - mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver; - } else { - mode = (SkBlendMode)buffer.read32(); - } - if (!shaderA || !shaderB) { - return nullptr; - } - return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode); -} - -void SkComposeShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeFlattenable(fShaderA.get()); - buffer.writeFlattenable(fShaderB.get()); - buffer.write32((int)fMode); -} - -SkShaderBase::Context* SkComposeShader::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - // we preconcat our localMatrix (if any) with the device matrix - // before calling our sub-shaders - SkMatrix tmpM; - tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); - - // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the - // result. ComposeShader itself will respect the alpha, and post-apply it after calling the - // sub-shaders. - SkPaint opaquePaint(*rec.fPaint); - opaquePaint.setAlpha(0xFF); - - ContextRec newRec(rec); - newRec.fMatrix = &tmpM; - newRec.fPaint = &opaquePaint; - - SkShaderBase::Context* contextA = as_SB(fShaderA)->makeContext(newRec, alloc); - SkShaderBase::Context* contextB = as_SB(fShaderB)->makeContext(newRec, alloc); - if (!contextA || !contextB) { - return nullptr; - } - - return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB); -} - -sk_sp<SkShader> SkComposeShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - return SkShader::MakeComposeShader(xformer->apply(fShaderA.get()), - xformer->apply(fShaderB.get()), fMode); -} - -SkComposeShader::ComposeShaderContext::ComposeShaderContext( - const SkComposeShader& shader, const ContextRec& rec, - SkShaderBase::Context* contextA, SkShaderBase::Context* contextB) - : INHERITED(shader, rec) - , fShaderContextA(contextA) - , fShaderContextB(contextB) {} - -bool SkComposeShader::asACompose(ComposeRec* rec) const { - if (rec) { - rec->fShaderA = fShaderA.get(); - rec->fShaderB = fShaderB.get(); - rec->fBlendMode = fMode; - } - return true; -} - -bool SkComposeShader::isRasterPipelineOnly() const { - return as_SB(fShaderA)->isRasterPipelineOnly() || as_SB(fShaderB)->isRasterPipelineOnly(); -} - -bool SkComposeShader::onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, - SkArenaAlloc* alloc, const SkMatrix& ctm, - const SkPaint& paint, const SkMatrix* localM) const { - struct Storage { - float fXY[4 * SkJumper_kMaxStride]; - float fRGBA[4 * SkJumper_kMaxStride]; - float fAlpha; - }; - auto storage = alloc->make<Storage>(); - - // We need to save off device x,y (inputs to shader), since after calling fShaderA they - // will be smashed, and I'll need them again for fShaderB. store_rgba saves off 4 registers - // even though we only need to save r,g. - pipeline->append(SkRasterPipeline::store_rgba, storage->fXY); - if (!as_SB(fShaderB)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // SRC - return false; - } - // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now - // since fShaderB will overwrite them. - pipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA); - // Now we restore the device x,y for the next shader - pipeline->append(SkRasterPipeline::load_rgba, storage->fXY); - if (!as_SB(fShaderA)->appendStages(pipeline, dstCS, alloc, ctm, paint, localM)) { // DST - return false; - } - // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode - // so we have to shuttle them. If we had a stage the would load_into_dst, then we could - // reverse the two shader invocations, and avoid this move... - pipeline->append(SkRasterPipeline::move_src_dst); - pipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA); - - // Idea: should time this, and see if it helps to have custom versions of the overflow modes - // that do their own clamping, avoiding the overhead of an extra stage. - SkBlendMode_AppendStages(fMode, pipeline); - if (SkBlendMode_CanOverflow(fMode)) { - pipeline->append(SkRasterPipeline::clamp_a); - } - return true; -} - -// larger is better (fewer times we have to loop), but we shouldn't -// take up too much stack-space (each element is 4 bytes) -#define TMP_COLOR_COUNT 64 - -void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { - auto* shaderContextA = fShaderContextA; - auto* shaderContextB = fShaderContextB; - SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode; - unsigned scale = SkAlpha255To256(this->getPaintAlpha()); - - SkPMColor tmp[TMP_COLOR_COUNT]; - - SkXfermode* xfer = SkXfermode::Peek(mode); - if (nullptr == xfer) { // implied SRC_OVER - // TODO: when we have a good test-case, should use SkBlitRow::Proc32 - // for these loops - do { - int n = count; - if (n > TMP_COLOR_COUNT) { - n = TMP_COLOR_COUNT; - } - - shaderContextA->shadeSpan(x, y, result, n); - shaderContextB->shadeSpan(x, y, tmp, n); - - if (256 == scale) { - for (int i = 0; i < n; i++) { - result[i] = SkPMSrcOver(tmp[i], result[i]); - } - } else { - for (int i = 0; i < n; i++) { - result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), - scale); - } - } - - result += n; - x += n; - count -= n; - } while (count > 0); - } else { // use mode for the composition - do { - int n = count; - if (n > TMP_COLOR_COUNT) { - n = TMP_COLOR_COUNT; - } - - shaderContextA->shadeSpan(x, y, result, n); - shaderContextB->shadeSpan(x, y, tmp, n); - xfer->xfer32(result, tmp, n, nullptr); - - if (256 != scale) { - for (int i = 0; i < n; i++) { - result[i] = SkAlphaMulQ(result[i], scale); - } - } - - result += n; - x += n; - count -= n; - } while (count > 0); - } -} - -void SkComposeShader::ComposeShaderContext::shadeSpan4f(int x, int y, SkPM4f result[], int count) { - auto* shaderContextA = fShaderContextA; - auto* shaderContextB = fShaderContextB; - SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode; - unsigned alpha = this->getPaintAlpha(); - Sk4f scale(alpha * (1.0f / 255)); - - SkPM4f tmp[TMP_COLOR_COUNT]; - - SkXfermodeProc4f xfer = SkXfermode::GetProc4f(mode); - do { - int n = SkTMin(count, TMP_COLOR_COUNT); - - shaderContextA->shadeSpan4f(x, y, result, n); - shaderContextB->shadeSpan4f(x, y, tmp, n); - if (255 == alpha) { - for (int i = 0; i < n; ++i) { - result[i] = xfer(tmp[i], result[i]); - } - } else { - for (int i = 0; i < n; ++i) { - (xfer(tmp[i], result[i]).to4f() * scale).store(result + i); - } - } - result += n; - x += n; - count -= n; - } while (count > 0); -} - -#if SK_SUPPORT_GPU - -#include "effects/GrConstColorProcessor.h" -#include "effects/GrXfermodeFragmentProcessor.h" - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const { - switch (fMode) { - case SkBlendMode::kClear: - return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), - GrConstColorProcessor::kIgnore_InputMode); - break; - case SkBlendMode::kSrc: - return as_SB(fShaderB)->asFragmentProcessor(args); - break; - case SkBlendMode::kDst: - return as_SB(fShaderA)->asFragmentProcessor(args); - break; - default: - sk_sp<GrFragmentProcessor> fpA(as_SB(fShaderA)->asFragmentProcessor(args)); - if (!fpA) { - return nullptr; - } - sk_sp<GrFragmentProcessor> fpB(as_SB(fShaderB)->asFragmentProcessor(args)); - if (!fpB) { - return nullptr; - } - return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB), - std::move(fpA), fMode); - } -} -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkComposeShader::toString(SkString* str) const { - str->append("SkComposeShader: ("); - - str->append("ShaderA: "); - as_SB(fShaderA)->toString(str); - str->append(" ShaderB: "); - as_SB(fShaderB)->toString(str); - if (SkBlendMode::kSrcOver != fMode) { - str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode)); - } - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/src/shaders/SkComposeShader.h b/src/shaders/SkComposeShader.h deleted file mode 100644 index 8592f3a8ae..0000000000 --- a/src/shaders/SkComposeShader.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkComposeShader_DEFINED -#define SkComposeShader_DEFINED - -#include "SkShaderBase.h" -#include "SkBlendMode.h" - -class SkColorSpacXformer; - -/////////////////////////////////////////////////////////////////////////////////////////// - -/** \class SkComposeShader - This subclass of shader returns the composition of two other shaders, combined by - a xfermode. -*/ -class SK_API SkComposeShader : public SkShaderBase { -public: - /** Create a new compose shader, given shaders A, B, and a combining xfermode mode. - When the xfermode is called, it will be given the result from shader A as its - "dst", and the result from shader B as its "src". - mode->xfer32(sA_result, sB_result, ...) - @param shaderA The colors from this shader are seen as the "dst" by the xfermode - @param shaderB The colors from this shader are seen as the "src" by the xfermode - @param mode The xfermode that combines the colors from the two shaders. If mode - is null, then SRC_OVER is assumed. - */ - SkComposeShader(sk_sp<SkShader> sA, sk_sp<SkShader> sB, SkBlendMode mode) - : fShaderA(std::move(sA)) - , fShaderB(std::move(sB)) - , fMode(mode) - {} - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - class ComposeShaderContext : public Context { - public: - // When this object gets destroyed, it will call contextA and contextB's destructor - // but it will NOT free the memory. - ComposeShaderContext(const SkComposeShader&, const ContextRec&, - SkShaderBase::Context* contextA, SkShaderBase::Context* contextB); - - void shadeSpan(int x, int y, SkPMColor[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f[], int count) override; - - private: - SkShaderBase::Context* fShaderContextA; - SkShaderBase::Context* fShaderContextB; - - typedef Context INHERITED; - }; - -#ifdef SK_DEBUG - SkShader* getShaderA() { return fShaderA.get(); } - SkShader* getShaderB() { return fShaderB.get(); } -#endif - - bool asACompose(ComposeRec* rec) const override; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader) - -protected: - SkComposeShader(SkReadBuffer&); - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - bool onAppendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*, - const SkMatrix&, const SkPaint&, const SkMatrix* localM) const override; - - bool isRasterPipelineOnly() const final; - -private: - sk_sp<SkShader> fShaderA; - sk_sp<SkShader> fShaderB; - SkBlendMode fMode; - - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkEmptyShader.h b/src/shaders/SkEmptyShader.h deleted file mode 100644 index c1bcfe0957..0000000000 --- a/src/shaders/SkEmptyShader.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkEmptyShader_DEFINED -#define SkEmptyShader_DEFINED - -#include "SkShaderBase.h" - -// TODO: move this to private, as there is a public factory on SkShader - -/** - * \class SkEmptyShader - * A Shader that always draws nothing. Its createContext always returns nullptr. - */ -class SK_API SkEmptyShader : public SkShaderBase { -public: - SkEmptyShader() {} - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader) - -protected: - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override { - return nullptr; - } - - void flatten(SkWriteBuffer& buffer) const override { - // Do nothing. - // We just don't want to fall through to SkShader::flatten(), - // which will write data we don't care to serialize or decode. - } - -private: - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp deleted file mode 100644 index 751300e951..0000000000 --- a/src/shaders/SkImageShader.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArenaAlloc.h" -#include "SkBitmapController.h" -#include "SkBitmapProcShader.h" -#include "SkBitmapProvider.h" -#include "SkColorTable.h" -#include "SkEmptyShader.h" -#include "SkImage_Base.h" -#include "SkImageShader.h" -#include "SkPM4fPriv.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -#include "../jumper/SkJumper.h" - -SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix) - : INHERITED(matrix) - , fImage(std::move(img)) - , fTileModeX(tmx) - , fTileModeY(tmy) -{} - -sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) { - const TileMode tx = (TileMode)buffer.readUInt(); - const TileMode ty = (TileMode)buffer.readUInt(); - SkMatrix matrix; - buffer.readMatrix(&matrix); - sk_sp<SkImage> img = buffer.readImage(); - if (!img) { - return nullptr; - } - return SkImageShader::Make(std::move(img), tx, ty, &matrix); -} - -void SkImageShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeUInt(fTileModeX); - buffer.writeUInt(fTileModeY); - buffer.writeMatrix(this->getLocalMatrix()); - buffer.writeImage(fImage.get()); -} - -bool SkImageShader::isOpaque() const { - return fImage->isOpaque(); -} - -SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec, - SkArenaAlloc* alloc) const { - return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, - SkBitmapProvider(fImage.get(), rec.fDstColorSpace), - rec, alloc); -} - -SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const { - if (texM) { - *texM = this->getLocalMatrix(); - } - if (xy) { - xy[0] = (TileMode)fTileModeX; - xy[1] = (TileMode)fTileModeY; - } - return const_cast<SkImage*>(fImage.get()); -} - -#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP -bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const { - const SkBitmap* bm = as_IB(fImage)->onPeekBitmap(); - if (!bm) { - return false; - } - - if (texture) { - *texture = *bm; - } - if (texM) { - *texM = this->getLocalMatrix(); - } - if (xy) { - xy[0] = (TileMode)fTileModeX; - xy[1] = (TileMode)fTileModeY; - } - return true; -} -#endif - -static bool bitmap_is_too_big(int w, int h) { - // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it - // communicates between its matrix-proc and its sampler-proc. Until we can - // widen that, we have to reject bitmaps that are larger. - // - static const int kMaxSize = 65535; - - return w > kMaxSize || h > kMaxSize; -} - -sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty, - const SkMatrix* localMatrix) { - if (!image || bitmap_is_too_big(image->width(), image->height())) { - return sk_make_sp<SkEmptyShader>(); - } else { - return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix); - } -} - -#ifndef SK_IGNORE_TO_STRING -void SkImageShader::toString(SkString* str) const { - const char* gTileModeName[SkShader::kTileModeCount] = { - "clamp", "repeat", "mirror" - }; - - str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]); - fImage->toString(str); - this->INHERITED::toString(str); - str->append(")"); -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "GrContext.h" -#include "effects/GrSimpleTextureEffect.h" -#include "effects/GrBicubicEffect.h" -#include "effects/GrSimpleTextureEffect.h" - -sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const { - - SkMatrix lmInverse; - if (!this->getLocalMatrix().invert(&lmInverse)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - lmInverse.postConcat(inv); - } - - SkShader::TileMode tm[] = { fTileModeX, fTileModeY }; - - // Must set wrap and filter on the sampler before requesting a texture. In two places below - // we check the matrix scale factors to determine how to interpret the filter quality setting. - // This completely ignores the complexity of the drawVertices case where explicit local coords - // are provided by the caller. - bool doBicubic; - GrSamplerParams::FilterMode textureFilterMode = - GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(), - &doBicubic); - GrSamplerParams params(tm, textureFilterMode); - sk_sp<SkColorSpace> texColorSpace; - SkScalar scaleAdjust[2] = { 1.0f, 1.0f }; - sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params, - args.fDstColorSpace, - &texColorSpace, scaleAdjust)); - if (!proxy) { - return nullptr; - } - - bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config()); - - lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]); - - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner; - if (doBicubic) { - inner = GrBicubicEffect::Make(args.fContext->resourceProvider(), std::move(proxy), - std::move(colorSpaceXform), lmInverse, tm); - } else { - inner = GrSimpleTextureEffect::Make(args.fContext->resourceProvider(), std::move(proxy), - std::move(colorSpaceXform), lmInverse, params); - } - - if (isAlphaOnly) { - return inner; - } - return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner))); -} - -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#include "SkImagePriv.h" - -sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx, - SkShader::TileMode tmy, const SkMatrix* localMatrix, - SkCopyPixelsMode cpm) { - return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm), - tmx, tmy, localMatrix); -} - -static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) { - SkMatrix lm; - buffer.readMatrix(&lm); - sk_sp<SkImage> image = buffer.readBitmapAsImage(); - SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt(); - SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt(); - return image ? image->makeShader(mx, my, &lm) : nullptr; -} - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase) -SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader) -SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShaderBase_Type); -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END - - -bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dstCS, SkArenaAlloc* alloc, - const SkMatrix& ctm, const SkPaint& paint, - const SkMatrix* localM) const { - auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix()); - if (localM) { - matrix.preConcat(*localM); - } - - if (!matrix.invert(&matrix)) { - return false; - } - auto quality = paint.getFilterQuality(); - - SkBitmapProvider provider(fImage.get(), dstCS); - SkDefaultBitmapController controller(SkDefaultBitmapController::CanShadeHQ::kYes); - std::unique_ptr<SkBitmapController::State> state { - controller.requestBitmap(provider, matrix, quality) - }; - if (!state) { - return false; - } - - const SkPixmap& pm = state->pixmap(); - matrix = state->invMatrix(); - quality = state->quality(); - auto info = pm.info(); - - // When the matrix is just an integer translate, bilerp == nearest neighbor. - if (quality == kLow_SkFilterQuality && - matrix.getType() <= SkMatrix::kTranslate_Mask && - matrix.getTranslateX() == (int)matrix.getTranslateX() && - matrix.getTranslateY() == (int)matrix.getTranslateY()) { - quality = kNone_SkFilterQuality; - } - - // See skia:4649 and the GM image_scale_aligned. - if (quality == kNone_SkFilterQuality) { - if (matrix.getScaleX() >= 0) { - matrix.setTranslateX(nextafterf(matrix.getTranslateX(), - floorf(matrix.getTranslateX()))); - } - if (matrix.getScaleY() >= 0) { - matrix.setTranslateY(nextafterf(matrix.getTranslateY(), - floorf(matrix.getTranslateY()))); - } - } - - - struct MiscCtx { - std::unique_ptr<SkBitmapController::State> state; - SkColor4f paint_color; - float matrix[9]; - }; - auto misc = alloc->make<MiscCtx>(); - misc->state = std::move(state); // Extend lifetime to match the pipeline's. - misc->paint_color = SkColor4f_from_SkColor(paint.getColor(), dstCS); - if (matrix.asAffine(misc->matrix)) { - p->append(SkRasterPipeline::matrix_2x3, misc->matrix); - } else { - matrix.get9(misc->matrix); - p->append(SkRasterPipeline::matrix_perspective, misc->matrix); - } - - auto gather = alloc->make<SkJumper_GatherCtx>(); - gather->pixels = pm.addr(); - gather->ctable = pm.ctable() ? pm.ctable()->readColors() : nullptr; - gather->stride = pm.rowBytesAsPixels(); - - // Tiling stages (clamp_x, mirror_y, etc.) are inclusive of their limit, - // so we tick down our width and height by one float to make them exclusive. - auto ulp_before = [](float f) { - uint32_t bits; - memcpy(&bits, &f, 4); - bits--; - memcpy(&f, &bits, 4); - return f; - }; - auto limit_x = alloc->make<float>(ulp_before((float)pm. width())), - limit_y = alloc->make<float>(ulp_before((float)pm.height())); - - auto append_tiling_and_gather = [&] { - switch (fTileModeX) { - case kClamp_TileMode: p->append(SkRasterPipeline::clamp_x, limit_x); break; - case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x); break; - case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x); break; - } - switch (fTileModeY) { - case kClamp_TileMode: p->append(SkRasterPipeline::clamp_y, limit_y); break; - case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y); break; - case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y); break; - } - switch (info.colorType()) { - case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, gather); break; - case kIndex_8_SkColorType: p->append(SkRasterPipeline::gather_i8, gather); break; - case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_g8, gather); break; - case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, gather); break; - case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, gather); break; - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, gather); break; - case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, gather); break; - default: SkASSERT(false); - } - if (info.gammaCloseToSRGB() && dstCS != nullptr) { - p->append_from_srgb(info.alphaType()); - } - }; - - SkJumper_SamplerCtx* sampler = nullptr; - if (quality != kNone_SkFilterQuality) { - sampler = alloc->make<SkJumper_SamplerCtx>(); - } - - auto sample = [&](SkRasterPipeline::StockStage setup_x, - SkRasterPipeline::StockStage setup_y) { - p->append(setup_x, sampler); - p->append(setup_y, sampler); - append_tiling_and_gather(); - p->append(SkRasterPipeline::accumulate, sampler); - }; - - if (quality == kNone_SkFilterQuality) { - append_tiling_and_gather(); - } else if (quality == kLow_SkFilterQuality) { - p->append(SkRasterPipeline::save_xy, sampler); - - sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny); - sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny); - sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py); - sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py); - - p->append(SkRasterPipeline::move_dst_src); - } else { - p->append(SkRasterPipeline::save_xy, sampler); - - sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y); - sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y); - sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y); - sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y); - - sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y); - sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y); - sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y); - sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y); - - sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y); - sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y); - sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y); - sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y); - - sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y); - sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y); - sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y); - sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y); - - p->append(SkRasterPipeline::move_dst_src); - } - - auto effective_color_type = [](SkColorType ct) { - return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct; - }; - - if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) { - p->append(SkRasterPipeline::swap_rb); - } - if (info.colorType() == kAlpha_8_SkColorType) { - p->append(SkRasterPipeline::set_rgb, &misc->paint_color); - } - if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) { - p->append(SkRasterPipeline::premul); - } - if (quality > kLow_SkFilterQuality) { - // Bicubic filtering naturally produces out of range values on both sides. - p->append(SkRasterPipeline::clamp_0); - p->append(SkRasterPipeline::clamp_a); - } - append_gamut_transform(p, alloc, info.colorSpace(), dstCS, kPremul_SkAlphaType); - return true; -} diff --git a/src/shaders/SkImageShader.h b/src/shaders/SkImageShader.h deleted file mode 100644 index 7be982c5c6..0000000000 --- a/src/shaders/SkImageShader.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkImageShader_DEFINED -#define SkImageShader_DEFINED - -#include "SkBitmapProcShader.h" -#include "SkColorSpaceXformer.h" -#include "SkImage.h" -#include "SkShaderBase.h" - -class SkImageShader : public SkShaderBase { -public: - static sk_sp<SkShader> Make(sk_sp<SkImage>, TileMode tx, TileMode ty, - const SkMatrix* localMatrix); - - bool isOpaque() const override; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageShader) - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SkImageShader(sk_sp<SkImage>, TileMode tx, TileMode ty, const SkMatrix* localMatrix); - -protected: - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override; -#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP - bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override; -#endif - SkImage* onIsAImage(SkMatrix*, TileMode*) const override; - - bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, - const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override; - - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override { - return xformer->apply(fImage.get())->makeShader(fTileModeX, fTileModeY, - &this->getLocalMatrix()); - } - - sk_sp<SkImage> fImage; - const TileMode fTileModeX; - const TileMode fTileModeY; - -private: - friend class SkShaderBase; - - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkLightingShader.cpp b/src/shaders/SkLightingShader.cpp deleted file mode 100644 index cdfa528e1e..0000000000 --- a/src/shaders/SkLightingShader.cpp +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArenaAlloc.h" -#include "SkBitmapProcShader.h" -#include "SkBitmapProcState.h" -#include "SkColor.h" -#include "SkColorSpaceXformer.h" -#include "SkEmptyShader.h" -#include "SkLightingShader.h" -#include "SkMathPriv.h" -#include "SkNormalSource.h" -#include "SkPoint3.h" -#include "SkReadBuffer.h" -#include "SkShaderBase.h" -#include "SkWriteBuffer.h" - -//////////////////////////////////////////////////////////////////////////// - -/* - SkLightingShader TODOs: - support different light types - support multiple lights - fix non-opaque diffuse textures - - To Test: - A8 diffuse textures - down & upsampled draws -*/ - - - -/** \class SkLightingShaderImpl - This subclass of shader applies lighting. -*/ -class SkLightingShaderImpl : public SkShaderBase { -public: - /** Create a new lighting shader that uses the provided normal map and - lights to light the diffuse bitmap. - @param diffuseShader the shader that provides the diffuse colors - @param normalSource the source of normals for lighting computation - @param lights the lights applied to the geometry - */ - SkLightingShaderImpl(sk_sp<SkShader> diffuseShader, - sk_sp<SkNormalSource> normalSource, - sk_sp<SkLights> lights) - : fDiffuseShader(std::move(diffuseShader)) - , fNormalSource(std::move(normalSource)) - , fLights(std::move(lights)) {} - - bool isOpaque() const override; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - class LightingShaderContext : public Context { - public: - // The context takes ownership of the context and provider. It will call their destructors - // and then indirectly free their memory by calling free() on heapAllocated - LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&, - SkShaderBase::Context* diffuseContext, SkNormalSource::Provider*, - void* heapAllocated); - - void shadeSpan(int x, int y, SkPMColor[], int count) override; - - uint32_t getFlags() const override { return fFlags; } - - private: - SkShaderBase::Context* fDiffuseContext; - SkNormalSource::Provider* fNormalProvider; - SkColor fPaintColor; - uint32_t fFlags; - - typedef Context INHERITED; - }; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingShaderImpl) - -protected: - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - sk_sp<SkShader> fDiffuseShader; - sk_sp<SkNormalSource> fNormalSource; - sk_sp<SkLights> fLights; - - friend class SkLightingShader; - - typedef SkShaderBase INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "SkGr.h" - -// This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is -// handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it -// premul'd. -class LightingFP : public GrFragmentProcessor { -public: - LightingFP(sk_sp<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights) - : INHERITED(kPreservesOpaqueInput_OptimizationFlag) { - // fuse all ambient lights into a single one - fAmbientColor = lights->ambientLightColor(); - for (int i = 0; i < lights->numLights(); ++i) { - if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) { - fDirectionalLights.push_back(lights->light(i)); - // TODO get the handle to the shadow map if there is one - } else { - SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP"); - } - } - - this->registerChildProcessor(std::move(normalFP)); - this->initClassID<LightingFP>(); - } - - class GLSLLightingFP : public GrGLSLFragmentProcessor { - public: - GLSLLightingFP() { - fAmbientColor.fX = 0.0f; - } - - void emitCode(EmitArgs& args) override { - - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - const LightingFP& lightingFP = args.fFp.cast<LightingFP>(); - - const char *lightDirsUniName = nullptr; - const char *lightColorsUniName = nullptr; - if (lightingFP.fDirectionalLights.count() != 0) { - fLightDirsUni = uniformHandler->addUniformArray( - kFragment_GrShaderFlag, - kVec3f_GrSLType, - kDefault_GrSLPrecision, - "LightDir", - lightingFP.fDirectionalLights.count(), - &lightDirsUniName); - fLightColorsUni = uniformHandler->addUniformArray( - kFragment_GrShaderFlag, - kVec3f_GrSLType, - kDefault_GrSLPrecision, - "LightColor", - lightingFP.fDirectionalLights.count(), - &lightColorsUniName); - } - - const char* ambientColorUniName = nullptr; - fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "AmbientColor", &ambientColorUniName); - - fragBuilder->codeAppendf("vec4 diffuseColor = %s;", args.fInputColor); - - SkString dstNormalName("dstNormal"); - this->emitChild(0, &dstNormalName, args); - - fragBuilder->codeAppendf("vec3 normal = %s.xyz;", dstNormalName.c_str()); - - fragBuilder->codeAppend( "vec3 result = vec3(0.0);"); - - // diffuse light - if (lightingFP.fDirectionalLights.count() != 0) { - fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {", - lightingFP.fDirectionalLights.count()); - // TODO: modulate the contribution from each light based on the shadow map - fragBuilder->codeAppendf(" float NdotL = clamp(dot(normal, %s[i]), 0.0, 1.0);", - lightDirsUniName); - fragBuilder->codeAppendf(" result += %s[i]*diffuseColor.rgb*NdotL;", - lightColorsUniName); - fragBuilder->codeAppend("}"); - } - - // ambient light - fragBuilder->codeAppendf("result += %s * diffuseColor.rgb;", ambientColorUniName); - - // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0) - fragBuilder->codeAppendf("%s = vec4(clamp(result.rgb, 0.0, diffuseColor.a), " - "diffuseColor.a);", args.fOutputColor); - } - - static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - const LightingFP& lightingFP = proc.cast<LightingFP>(); - b->add32(lightingFP.fDirectionalLights.count()); - } - - protected: - void onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& proc) override { - const LightingFP& lightingFP = proc.cast<LightingFP>(); - - const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights(); - if (directionalLights != fDirectionalLights) { - SkTArray<SkColor3f> lightDirs(directionalLights.count()); - SkTArray<SkVector3> lightColors(directionalLights.count()); - for (const SkLights::Light& light : directionalLights) { - lightDirs.push_back(light.dir()); - lightColors.push_back(light.color()); - } - - pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX)); - pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX)); - - fDirectionalLights = directionalLights; - } - - const SkColor3f& ambientColor = lightingFP.ambientColor(); - if (ambientColor != fAmbientColor) { - pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX); - fAmbientColor = ambientColor; - } - } - - private: - SkTArray<SkLights::Light> fDirectionalLights; - GrGLSLProgramDataManager::UniformHandle fLightDirsUni; - GrGLSLProgramDataManager::UniformHandle fLightColorsUni; - - SkColor3f fAmbientColor; - GrGLSLProgramDataManager::UniformHandle fAmbientColorUni; - }; - - void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { - GLSLLightingFP::GenKey(*this, caps, b); - } - - const char* name() const override { return "LightingFP"; } - - const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; } - const SkColor3f& ambientColor() const { return fAmbientColor; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; } - - bool onIsEqual(const GrFragmentProcessor& proc) const override { - const LightingFP& lightingFP = proc.cast<LightingFP>(); - return fDirectionalLights == lightingFP.fDirectionalLights && - fAmbientColor == lightingFP.fAmbientColor; - } - - SkTArray<SkLights::Light> fDirectionalLights; - SkColor3f fAmbientColor; - - typedef GrFragmentProcessor INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const AsFPArgs& args) const { - sk_sp<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args)); - if (!normalFP) { - return nullptr; - } - - if (fDiffuseShader) { - sk_sp<GrFragmentProcessor> fpPipeline[] = { - as_SB(fDiffuseShader)->asFragmentProcessor(args), - sk_make_sp<LightingFP>(std::move(normalFP), fLights) - }; - if(!fpPipeline[0]) { - return nullptr; - } - - sk_sp<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2); - // FP is wrapped because paint's alpha needs to be applied to output - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(innerLightFP)); - } else { - // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP - // expects premul'd color. - return GrFragmentProcessor::PremulInput(sk_make_sp<LightingFP>(std::move(normalFP), - fLights)); - } -} - -#endif - -//////////////////////////////////////////////////////////////////////////// - -bool SkLightingShaderImpl::isOpaque() const { - return (fDiffuseShader ? fDiffuseShader->isOpaque() : false); -} - -SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( - const SkLightingShaderImpl& shader, const ContextRec& rec, - SkShaderBase::Context* diffuseContext, SkNormalSource::Provider* normalProvider, - void* heapAllocated) - : INHERITED(shader, rec) - , fDiffuseContext(diffuseContext) - , fNormalProvider(normalProvider) { - bool isOpaque = shader.isOpaque(); - - // update fFlags - uint32_t flags = 0; - if (isOpaque && (255 == this->getPaintAlpha())) { - flags |= kOpaqueAlpha_Flag; - } - - fPaintColor = rec.fPaint->getColor(); - fFlags = flags; -} - -static inline SkPMColor convert(SkColor3f color, U8CPU a) { - if (color.fX <= 0.0f) { - color.fX = 0.0f; - } else if (color.fX >= 255.0f) { - color.fX = 255.0f; - } - - if (color.fY <= 0.0f) { - color.fY = 0.0f; - } else if (color.fY >= 255.0f) { - color.fY = 255.0f; - } - - if (color.fZ <= 0.0f) { - color.fZ = 0.0f; - } else if (color.fZ >= 255.0f) { - color.fZ = 255.0f; - } - - return SkPreMultiplyARGB(a, (int) color.fX, (int) color.fY, (int) color.fZ); -} - -// larger is better (fewer times we have to loop), but we shouldn't -// take up too much stack-space (each one here costs 16 bytes) -#define BUFFER_MAX 16 -void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, - SkPMColor result[], int count) { - const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader); - - SkPMColor diffuse[BUFFER_MAX]; - SkPoint3 normals[BUFFER_MAX]; - - SkColor diffColor = fPaintColor; - - do { - int n = SkTMin(count, BUFFER_MAX); - - fNormalProvider->fillScanLine(x, y, normals, n); - - if (fDiffuseContext) { - fDiffuseContext->shadeSpan(x, y, diffuse, n); - } - - for (int i = 0; i < n; ++i) { - if (fDiffuseContext) { - diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); - } - - SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f); - - // Adding ambient light - accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor); - accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor); - accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor); - - // This is all done in linear unpremul color space (each component 0..255.0f though) - for (int l = 0; l < lightShader.fLights->numLights(); ++l) { - const SkLights::Light& light = lightShader.fLights->light(l); - - SkScalar illuminanceScalingFactor = 1.0f; - - if (SkLights::Light::kDirectional_LightType == light.type()) { - illuminanceScalingFactor = normals[i].dot(light.dir()); - if (illuminanceScalingFactor < 0.0f) { - illuminanceScalingFactor = 0.0f; - } - } - - accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor; - accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor; - accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor; - } - - // convert() premultiplies the accumulate color with alpha - result[i] = convert(accum, SkColorGetA(diffColor)); - } - - result += n; - x += n; - count -= n; - } while (count > 0); -} - -//////////////////////////////////////////////////////////////////////////// - -#ifndef SK_IGNORE_TO_STRING -void SkLightingShaderImpl::toString(SkString* str) const { - str->appendf("LightingShader: ()"); -} -#endif - -sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { - - // Discarding SkShader flattenable params - bool hasLocalMatrix = buf.readBool(); - SkAssertResult(!hasLocalMatrix); - - sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf); - - sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>()); - - bool hasDiffuse = buf.readBool(); - sk_sp<SkShader> diffuseShader = nullptr; - if (hasDiffuse) { - diffuseShader = buf.readFlattenable<SkShaderBase>(); - } - - return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), - std::move(lights)); -} - -void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { - this->INHERITED::flatten(buf); - - fLights->flatten(buf); - - buf.writeFlattenable(fNormalSource.get()); - buf.writeBool(fDiffuseShader); - if (fDiffuseShader) { - buf.writeFlattenable(fDiffuseShader.get()); - } -} - -SkShaderBase::Context* SkLightingShaderImpl::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - SkShaderBase::Context *diffuseContext = nullptr; - if (fDiffuseShader) { - diffuseContext = as_SB(fDiffuseShader)->makeContext(rec, alloc); - if (!diffuseContext) { - return nullptr; - } - } - - SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc); - if (!normalProvider) { - return nullptr; - } - - return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr); -} - -sk_sp<SkShader> SkLightingShaderImpl::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - sk_sp<SkShader> xformedDiffuseShader = - fDiffuseShader ? xformer->apply(fDiffuseShader.get()) : nullptr; - return SkLightingShader::Make(std::move(xformedDiffuseShader), fNormalSource, - fLights->makeColorSpace(xformer)); -} - -/////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader, - sk_sp<SkNormalSource> normalSource, - sk_sp<SkLights> lights) { - SkASSERT(lights); - if (!normalSource) { - normalSource = SkNormalSource::MakeFlat(); - } - - return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), - std::move(lights)); -} - -/////////////////////////////////////////////////////////////////////////////// - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingShaderImpl) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END - -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/shaders/SkLightingShader.h b/src/shaders/SkLightingShader.h deleted file mode 100644 index aa90710aa4..0000000000 --- a/src/shaders/SkLightingShader.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLightingShader_DEFINED -#define SkLightingShader_DEFINED - -#include "SkLights.h" -#include "SkShader.h" - -class SkBitmap; -class SkMatrix; -class SkNormalSource; - -class SK_API SkLightingShader { -public: - /** Returns a shader that lights the shape, colored by the diffuseShader, using the - normals from normalSource, with the set of lights provided. - - @param diffuseShader the shader that provides the colors. If nullptr, uses the paint's - color. - @param normalSource the source for the shape's normals. If nullptr, assumes straight - up normals (<0,0,1>). - @param lights the lights applied to the normals - - The lighting equation is currently: - result = (LightColor * dot(Normal, LightDir) + AmbientColor) * DiffuseColor - - */ - static sk_sp<SkShader> Make(sk_sp<SkShader> diffuseShader, sk_sp<SkNormalSource> normalSource, - sk_sp<SkLights> lights); - - SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() -}; - -#endif diff --git a/src/shaders/SkLocalMatrixShader.cpp b/src/shaders/SkLocalMatrixShader.cpp deleted file mode 100644 index e21e4a84b7..0000000000 --- a/src/shaders/SkLocalMatrixShader.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkLocalMatrixShader.h" - -#if SK_SUPPORT_GPU -#include "GrFragmentProcessor.h" -#endif - -#if SK_SUPPORT_GPU -sk_sp<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(const AsFPArgs& args) const { - SkMatrix tmp = this->getLocalMatrix(); - if (args.fLocalMatrix) { - tmp.preConcat(*args.fLocalMatrix); - } - return as_SB(fProxyShader)->asFragmentProcessor(AsFPArgs( - args.fContext, args.fViewMatrix, &tmp, args.fFilterQuality, args.fDstColorSpace)); -} -#endif - -sk_sp<SkFlattenable> SkLocalMatrixShader::CreateProc(SkReadBuffer& buffer) { - SkMatrix lm; - buffer.readMatrix(&lm); - auto baseShader(buffer.readShader()); - if (!baseShader) { - return nullptr; - } - return baseShader->makeWithLocalMatrix(lm); -} - -void SkLocalMatrixShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeMatrix(this->getLocalMatrix()); - buffer.writeFlattenable(fProxyShader.get()); -} - -SkShaderBase::Context* SkLocalMatrixShader::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - ContextRec newRec(rec); - SkMatrix tmp; - if (rec.fLocalMatrix) { - tmp.setConcat(*rec.fLocalMatrix, this->getLocalMatrix()); - newRec.fLocalMatrix = &tmp; - } else { - newRec.fLocalMatrix = &this->getLocalMatrix(); - } - return as_SB(fProxyShader)->makeContext(newRec, alloc); -} - -SkImage* SkLocalMatrixShader::onIsAImage(SkMatrix* outMatrix, enum TileMode* mode) const { - SkMatrix imageMatrix; - SkImage* image = fProxyShader->isAImage(&imageMatrix, mode); - if (image && outMatrix) { - // Local matrix must be applied first so it is on the right side of the concat. - *outMatrix = SkMatrix::Concat(imageMatrix, this->getLocalMatrix()); - } - - return image; -} - -bool SkLocalMatrixShader::onAppendStages(SkRasterPipeline* p, - SkColorSpace* dst, - SkArenaAlloc* scratch, - const SkMatrix& ctm, - const SkPaint& paint, - const SkMatrix* localM) const { - SkMatrix tmp; - if (localM) { - tmp.setConcat(*localM, this->getLocalMatrix()); - } - return as_SB(fProxyShader)->appendStages(p, dst, scratch, ctm, paint, - localM ? &tmp : &this->getLocalMatrix()); -} - -#ifndef SK_IGNORE_TO_STRING -void SkLocalMatrixShader::toString(SkString* str) const { - str->append("SkLocalMatrixShader: ("); - - as_SB(fProxyShader)->toString(str); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const { - if (localMatrix.isIdentity()) { - return sk_ref_sp(const_cast<SkShader*>(this)); - } - - const SkMatrix* lm = &localMatrix; - - sk_sp<SkShader> baseShader; - SkMatrix otherLocalMatrix; - sk_sp<SkShader> proxy(as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix)); - if (proxy) { - otherLocalMatrix.preConcat(localMatrix); - lm = &otherLocalMatrix; - baseShader = proxy; - } else { - baseShader = sk_ref_sp(const_cast<SkShader*>(this)); - } - - return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm); -} diff --git a/src/shaders/SkLocalMatrixShader.h b/src/shaders/SkLocalMatrixShader.h deleted file mode 100644 index 4572e9fe2e..0000000000 --- a/src/shaders/SkLocalMatrixShader.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLocalMatrixShader_DEFINED -#define SkLocalMatrixShader_DEFINED - -#include "SkShaderBase.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" - -class GrFragmentProcessor; -class SkArenaAlloc; -class SkColorSpaceXformer; - -class SkLocalMatrixShader : public SkShaderBase { -public: - SkLocalMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix) - : INHERITED(&localMatrix) - , fProxyShader(std::move(proxy)) - {} - - GradientType asAGradient(GradientInfo* info) const override { - return fProxyShader->asAGradient(info); - } - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - sk_sp<SkShader> makeAsALocalMatrixShader(SkMatrix* localMatrix) const override { - if (localMatrix) { - *localMatrix = this->getLocalMatrix(); - } - return fProxyShader; - } - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLocalMatrixShader) - -protected: - void flatten(SkWriteBuffer&) const override; - - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - - SkImage* onIsAImage(SkMatrix* matrix, TileMode* mode) const override; - - bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, - const SkMatrix&, const SkPaint&, const SkMatrix*) const override; - - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override { - return as_SB(fProxyShader)->makeColorSpace(xformer)->makeWithLocalMatrix( - this->getLocalMatrix()); - } - -#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP - bool onIsABitmap(SkBitmap* bitmap, SkMatrix* matrix, TileMode* mode) const override { - return fProxyShader->isABitmap(bitmap, matrix, mode); - } -#endif - - bool isRasterPipelineOnly() const final { - return as_SB(fProxyShader)->isRasterPipelineOnly(); - } - -private: - sk_sp<SkShader> fProxyShader; - - typedef SkShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/SkPerlinNoiseShader.cpp b/src/shaders/SkPerlinNoiseShader.cpp deleted file mode 100644 index 87f8967242..0000000000 --- a/src/shaders/SkPerlinNoiseShader.cpp +++ /dev/null @@ -1,1056 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkPerlinNoiseShader.h" - -#include "SkArenaAlloc.h" -#include "SkColorFilter.h" -#include "SkReadBuffer.h" -#include "SkShaderBase.h" -#include "SkString.h" -#include "SkUnPreMultiply.h" -#include "SkWriteBuffer.h" - -#if SK_SUPPORT_GPU -#include "GrContext.h" -#include "GrCoordTransform.h" -#include "SkGr.h" -#include "effects/GrConstColorProcessor.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#endif - -static const int kBlockSize = 256; -static const int kBlockMask = kBlockSize - 1; -static const int kPerlinNoise = 4096; -static const int kRandMaximum = SK_MaxS32; // 2**31 - 1 - -namespace { - -// noiseValue is the color component's value (or color) -// limitValue is the maximum perlin noise array index value allowed -// newValue is the current noise dimension (either width or height) -inline int checkNoise(int noiseValue, int limitValue, int newValue) { - // If the noise value would bring us out of bounds of the current noise array while we are - // stiching noise tiles together, wrap the noise around the current dimension of the noise to - // stay within the array bounds in a continuous fashion (so that tiling lines are not visible) - if (noiseValue >= limitValue) { - noiseValue -= newValue; - } - return noiseValue; -} - -inline SkScalar smoothCurve(SkScalar t) { - return t * t * (3 - 2 * t); -} - -class SkPerlinNoiseShaderImpl final : public SkShaderBase { -public: - /** - * About the noise types : the difference between the 2 is just minor tweaks to the algorithm, - * they're not 2 entirely different noises. The output looks different, but once the noise is - * generated in the [1, -1] range, the output is brought back in the [0, 1] range by doing : - * kFractalNoise_Type : noise * 0.5 + 0.5 - * kTurbulence_Type : abs(noise) - * Very little differences between the 2 types, although you can tell the difference visually. - */ - enum Type { - kFractalNoise_Type, - kTurbulence_Type, - kFirstType = kFractalNoise_Type, - kLastType = kTurbulence_Type - }; - - SkPerlinNoiseShaderImpl(Type type, SkScalar baseFrequencyX, - SkScalar baseFrequencyY, int numOctaves, SkScalar seed, - const SkISize* tileSize); - ~SkPerlinNoiseShaderImpl() override = default; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPerlinNoiseShaderImpl) - - struct StitchData; - struct PaintingData; - -protected: - void flatten(SkWriteBuffer&) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override; - -private: - class PerlinNoiseShaderContext final : public Context { - public: - PerlinNoiseShaderContext(const SkPerlinNoiseShaderImpl& shader, const ContextRec&); - ~PerlinNoiseShaderContext() override; - - void shadeSpan(int x, int y, SkPMColor[], int count) override; - - private: - SkPMColor shade(const SkPoint& point, StitchData& stitchData) const; - SkScalar calculateTurbulenceValueForPoint( - int channel, - StitchData& stitchData, const SkPoint& point) const; - SkScalar noise2D(int channel, - const StitchData& stitchData, const SkPoint& noiseVector) const; - - SkMatrix fMatrix; - PaintingData* fPaintingData; - - typedef Context INHERITED; - }; - - const Type fType; - const SkScalar fBaseFrequencyX; - const SkScalar fBaseFrequencyY; - const int fNumOctaves; - const SkScalar fSeed; - const SkISize fTileSize; - const bool fStitchTiles; - - friend class ::SkPerlinNoiseShader; - - typedef SkShaderBase INHERITED; -}; - -} // end namespace - -struct SkPerlinNoiseShaderImpl::StitchData { - StitchData() - : fWidth(0) - , fWrapX(0) - , fHeight(0) - , fWrapY(0) - {} - - bool operator==(const StitchData& other) const { - return fWidth == other.fWidth && - fWrapX == other.fWrapX && - fHeight == other.fHeight && - fWrapY == other.fWrapY; - } - - int fWidth; // How much to subtract to wrap for stitching. - int fWrapX; // Minimum value to wrap. - int fHeight; - int fWrapY; -}; - -struct SkPerlinNoiseShaderImpl::PaintingData { - PaintingData(const SkISize& tileSize, SkScalar seed, - SkScalar baseFrequencyX, SkScalar baseFrequencyY, - const SkMatrix& matrix) - { - SkVector vec[2] = { - { SkScalarInvert(baseFrequencyX), SkScalarInvert(baseFrequencyY) }, - { SkIntToScalar(tileSize.fWidth), SkIntToScalar(tileSize.fHeight) }, - }; - matrix.mapVectors(vec, 2); - - fBaseFrequency.set(SkScalarInvert(vec[0].fX), SkScalarInvert(vec[0].fY)); - fTileSize.set(SkScalarRoundToInt(vec[1].fX), SkScalarRoundToInt(vec[1].fY)); - this->init(seed); - if (!fTileSize.isEmpty()) { - this->stitch(); - } - -#if SK_SUPPORT_GPU - fPermutationsBitmap.setInfo(SkImageInfo::MakeA8(kBlockSize, 1)); - fPermutationsBitmap.setPixels(fLatticeSelector); - - fNoiseBitmap.setInfo(SkImageInfo::MakeN32Premul(kBlockSize, 4)); - fNoiseBitmap.setPixels(fNoise[0][0]); -#endif - } - - int fSeed; - uint8_t fLatticeSelector[kBlockSize]; - uint16_t fNoise[4][kBlockSize][2]; - SkPoint fGradient[4][kBlockSize]; - SkISize fTileSize; - SkVector fBaseFrequency; - StitchData fStitchDataInit; - -private: - -#if SK_SUPPORT_GPU - SkBitmap fPermutationsBitmap; - SkBitmap fNoiseBitmap; -#endif - - inline int random() { - static const int gRandAmplitude = 16807; // 7**5; primitive root of m - static const int gRandQ = 127773; // m / a - static const int gRandR = 2836; // m % a - - int result = gRandAmplitude * (fSeed % gRandQ) - gRandR * (fSeed / gRandQ); - if (result <= 0) - result += kRandMaximum; - fSeed = result; - return result; - } - - // Only called once. Could be part of the constructor. - void init(SkScalar seed) - { - static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize)); - - // According to the SVG spec, we must truncate (not round) the seed value. - fSeed = SkScalarTruncToInt(seed); - // The seed value clamp to the range [1, kRandMaximum - 1]. - if (fSeed <= 0) { - fSeed = -(fSeed % (kRandMaximum - 1)) + 1; - } - if (fSeed > kRandMaximum - 1) { - fSeed = kRandMaximum - 1; - } - for (int channel = 0; channel < 4; ++channel) { - for (int i = 0; i < kBlockSize; ++i) { - fLatticeSelector[i] = i; - fNoise[channel][i][0] = (random() % (2 * kBlockSize)); - fNoise[channel][i][1] = (random() % (2 * kBlockSize)); - } - } - for (int i = kBlockSize - 1; i > 0; --i) { - int k = fLatticeSelector[i]; - int j = random() % kBlockSize; - SkASSERT(j >= 0); - SkASSERT(j < kBlockSize); - fLatticeSelector[i] = fLatticeSelector[j]; - fLatticeSelector[j] = k; - } - - // Perform the permutations now - { - // Copy noise data - uint16_t noise[4][kBlockSize][2]; - for (int i = 0; i < kBlockSize; ++i) { - for (int channel = 0; channel < 4; ++channel) { - for (int j = 0; j < 2; ++j) { - noise[channel][i][j] = fNoise[channel][i][j]; - } - } - } - // Do permutations on noise data - for (int i = 0; i < kBlockSize; ++i) { - for (int channel = 0; channel < 4; ++channel) { - for (int j = 0; j < 2; ++j) { - fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j]; - } - } - } - } - - // Half of the largest possible value for 16 bit unsigned int - static const SkScalar gHalfMax16bits = 32767.5f; - - // Compute gradients from permutated noise data - for (int channel = 0; channel < 4; ++channel) { - for (int i = 0; i < kBlockSize; ++i) { - fGradient[channel][i] = SkPoint::Make( - (fNoise[channel][i][0] - kBlockSize) * gInvBlockSizef, - (fNoise[channel][i][1] - kBlockSize) * gInvBlockSizef); - fGradient[channel][i].normalize(); - // Put the normalized gradient back into the noise data - fNoise[channel][i][0] = SkScalarRoundToInt( - (fGradient[channel][i].fX + 1) * gHalfMax16bits); - fNoise[channel][i][1] = SkScalarRoundToInt( - (fGradient[channel][i].fY + 1) * gHalfMax16bits); - } - } - } - - // Only called once. Could be part of the constructor. - void stitch() { - SkScalar tileWidth = SkIntToScalar(fTileSize.width()); - SkScalar tileHeight = SkIntToScalar(fTileSize.height()); - SkASSERT(tileWidth > 0 && tileHeight > 0); - // When stitching tiled turbulence, the frequencies must be adjusted - // so that the tile borders will be continuous. - if (fBaseFrequency.fX) { - SkScalar lowFrequencx = - SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; - SkScalar highFrequencx = - SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; - // BaseFrequency should be non-negative according to the standard. - if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) { - fBaseFrequency.fX = lowFrequencx; - } else { - fBaseFrequency.fX = highFrequencx; - } - } - if (fBaseFrequency.fY) { - SkScalar lowFrequency = - SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; - SkScalar highFrequency = - SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; - if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) { - fBaseFrequency.fY = lowFrequency; - } else { - fBaseFrequency.fY = highFrequency; - } - } - // Set up TurbulenceInitial stitch values. - fStitchDataInit.fWidth = - SkScalarRoundToInt(tileWidth * fBaseFrequency.fX); - fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth; - fStitchDataInit.fHeight = - SkScalarRoundToInt(tileHeight * fBaseFrequency.fY); - fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight; - } - -public: - -#if SK_SUPPORT_GPU - const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; } - - const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; } -#endif -}; - -sk_sp<SkShader> SkPerlinNoiseShader::MakeFractalNoise(SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, SkScalar seed, - const SkISize* tileSize) { - return sk_make_sp<SkPerlinNoiseShaderImpl>(SkPerlinNoiseShaderImpl::kFractalNoise_Type, - baseFrequencyX, baseFrequencyY, numOctaves, - seed, tileSize); -} - -sk_sp<SkShader> SkPerlinNoiseShader::MakeTurbulence(SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, SkScalar seed, - const SkISize* tileSize) { - return sk_make_sp<SkPerlinNoiseShaderImpl>(SkPerlinNoiseShaderImpl::kTurbulence_Type, - baseFrequencyX, baseFrequencyY, - numOctaves, seed, tileSize); -} - -SkPerlinNoiseShaderImpl::SkPerlinNoiseShaderImpl(Type type, - SkScalar baseFrequencyX, - SkScalar baseFrequencyY, - int numOctaves, - SkScalar seed, - const SkISize* tileSize) - : fType(type) - , fBaseFrequencyX(baseFrequencyX) - , fBaseFrequencyY(baseFrequencyY) - , fNumOctaves(SkTPin<int>(numOctaves, 0, 255)) // [0,255] octaves allowed - , fSeed(seed) - , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize) - , fStitchTiles(!fTileSize.isEmpty()) -{ - SkASSERT(fNumOctaves >= 0 && fNumOctaves < 256); -} - -sk_sp<SkFlattenable> SkPerlinNoiseShaderImpl::CreateProc(SkReadBuffer& buffer) { - Type type = (Type)buffer.readInt(); - SkScalar freqX = buffer.readScalar(); - SkScalar freqY = buffer.readScalar(); - int octaves = buffer.readInt(); - SkScalar seed = buffer.readScalar(); - SkISize tileSize; - tileSize.fWidth = buffer.readInt(); - tileSize.fHeight = buffer.readInt(); - - switch (type) { - case kFractalNoise_Type: - return SkPerlinNoiseShader::MakeFractalNoise(freqX, freqY, octaves, seed, - &tileSize); - case kTurbulence_Type: - return SkPerlinNoiseShader::MakeTurbulence(freqX, freqY, octaves, seed, - &tileSize); - default: - return nullptr; - } -} - -void SkPerlinNoiseShaderImpl::flatten(SkWriteBuffer& buffer) const { - buffer.writeInt((int) fType); - buffer.writeScalar(fBaseFrequencyX); - buffer.writeScalar(fBaseFrequencyY); - buffer.writeInt(fNumOctaves); - buffer.writeScalar(fSeed); - buffer.writeInt(fTileSize.fWidth); - buffer.writeInt(fTileSize.fHeight); -} - -SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::noise2D( - int channel, const StitchData& stitchData, const SkPoint& noiseVector) const { - struct Noise { - int noisePositionIntegerValue; - int nextNoisePositionIntegerValue; - SkScalar noisePositionFractionValue; - Noise(SkScalar component) - { - SkScalar position = component + kPerlinNoise; - noisePositionIntegerValue = SkScalarFloorToInt(position); - noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue); - nextNoisePositionIntegerValue = noisePositionIntegerValue + 1; - } - }; - Noise noiseX(noiseVector.x()); - Noise noiseY(noiseVector.y()); - SkScalar u, v; - const SkPerlinNoiseShaderImpl& perlinNoiseShader = - static_cast<const SkPerlinNoiseShaderImpl&>(fShader); - // If stitching, adjust lattice points accordingly. - if (perlinNoiseShader.fStitchTiles) { - noiseX.noisePositionIntegerValue = - checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth); - noiseY.noisePositionIntegerValue = - checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight); - noiseX.nextNoisePositionIntegerValue = - checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth); - noiseY.nextNoisePositionIntegerValue = - checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight); - } - noiseX.noisePositionIntegerValue &= kBlockMask; - noiseY.noisePositionIntegerValue &= kBlockMask; - noiseX.nextNoisePositionIntegerValue &= kBlockMask; - noiseY.nextNoisePositionIntegerValue &= kBlockMask; - int i = - fPaintingData->fLatticeSelector[noiseX.noisePositionIntegerValue]; - int j = - fPaintingData->fLatticeSelector[noiseX.nextNoisePositionIntegerValue]; - int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask; - int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask; - int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask; - int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask; - SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue); - SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue); - if (sx < 0 || sy < 0 || sx > 1 || sy > 1) { - return 0; // Check for pathological inputs. - } - // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement - SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue, - noiseY.noisePositionFractionValue); // Offset (0,0) - u = fPaintingData->fGradient[channel][b00].dot(fractionValue); - fractionValue.fX -= SK_Scalar1; // Offset (-1,0) - v = fPaintingData->fGradient[channel][b10].dot(fractionValue); - SkScalar a = SkScalarInterp(u, v, sx); - fractionValue.fY -= SK_Scalar1; // Offset (-1,-1) - v = fPaintingData->fGradient[channel][b11].dot(fractionValue); - fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1) - u = fPaintingData->fGradient[channel][b01].dot(fractionValue); - SkScalar b = SkScalarInterp(u, v, sx); - return SkScalarInterp(a, b, sy); -} - -SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint( - int channel, StitchData& stitchData, const SkPoint& point) const { - const SkPerlinNoiseShaderImpl& perlinNoiseShader = - static_cast<const SkPerlinNoiseShaderImpl&>(fShader); - if (perlinNoiseShader.fStitchTiles) { - // Set up TurbulenceInitial stitch values. - stitchData = fPaintingData->fStitchDataInit; - } - SkScalar turbulenceFunctionResult = 0; - SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData->fBaseFrequency.fX, - point.y() * fPaintingData->fBaseFrequency.fY)); - SkScalar ratio = SK_Scalar1; - for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) { - SkScalar noise = noise2D(channel, stitchData, noiseVector); - SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ? - noise : SkScalarAbs(noise); - turbulenceFunctionResult += numer / ratio; - noiseVector.fX *= 2; - noiseVector.fY *= 2; - ratio *= 2; - if (perlinNoiseShader.fStitchTiles) { - // Update stitch values - stitchData.fWidth *= 2; - stitchData.fWrapX = stitchData.fWidth + kPerlinNoise; - stitchData.fHeight *= 2; - stitchData.fWrapY = stitchData.fHeight + kPerlinNoise; - } - } - - // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 - // by fractalNoise and (turbulenceFunctionResult) by turbulence. - if (perlinNoiseShader.fType == kFractalNoise_Type) { - turbulenceFunctionResult = turbulenceFunctionResult * SK_ScalarHalf + SK_ScalarHalf; - } - - if (channel == 3) { // Scale alpha by paint value - turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255; - } - - // Clamp result - return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1); -} - -SkPMColor SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shade( - const SkPoint& point, StitchData& stitchData) const { - SkPoint newPoint; - fMatrix.mapPoints(&newPoint, &point, 1); - newPoint.fX = SkScalarRoundToScalar(newPoint.fX); - newPoint.fY = SkScalarRoundToScalar(newPoint.fY); - - U8CPU rgba[4]; - for (int channel = 3; channel >= 0; --channel) { - rgba[channel] = SkScalarFloorToInt(255 * - calculateTurbulenceValueForPoint(channel, stitchData, newPoint)); - } - return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); -} - -SkShaderBase::Context* SkPerlinNoiseShaderImpl::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const { - return alloc->make<PerlinNoiseShaderContext>(*this, rec); -} - -SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::PerlinNoiseShaderContext( - const SkPerlinNoiseShaderImpl& shader, const ContextRec& rec) - : INHERITED(shader, rec) -{ - SkMatrix newMatrix = SkMatrix::Concat(*rec.fMatrix, shader.getLocalMatrix()); - if (rec.fLocalMatrix) { - newMatrix.preConcat(*rec.fLocalMatrix); - } - // This (1,1) translation is due to WebKit's 1 based coordinates for the noise - // (as opposed to 0 based, usually). The same adjustment is in the setData() function. - fMatrix.setTranslate(-newMatrix.getTranslateX() + SK_Scalar1, -newMatrix.getTranslateY() + SK_Scalar1); - fPaintingData = new PaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX, - shader.fBaseFrequencyY, newMatrix); -} - -SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::~PerlinNoiseShaderContext() { delete fPaintingData; } - -void SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shadeSpan( - int x, int y, SkPMColor result[], int count) { - SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y)); - StitchData stitchData; - for (int i = 0; i < count; ++i) { - result[i] = shade(point, stitchData); - point.fX += SK_Scalar1; - } -} - -///////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -class GrGLPerlinNoise : public GrGLSLFragmentProcessor { -public: - void emitCode(EmitArgs&) override; - - static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - -private: - GrGLSLProgramDataManager::UniformHandle fStitchDataUni; - GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrPerlinNoiseEffect : public GrFragmentProcessor { -public: - static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider, - SkPerlinNoiseShaderImpl::Type type, - int numOctaves, bool stitchTiles, - SkPerlinNoiseShaderImpl::PaintingData* paintingData, - sk_sp<GrTextureProxy> permutationsProxy, - sk_sp<GrTextureProxy> noiseProxy, - const SkMatrix& matrix) { - return sk_sp<GrFragmentProcessor>( - new GrPerlinNoiseEffect(resourceProvider, type, numOctaves, stitchTiles, paintingData, - std::move(permutationsProxy), std::move(noiseProxy), matrix)); - } - - ~GrPerlinNoiseEffect() override { delete fPaintingData; } - - const char* name() const override { return "PerlinNoise"; } - - const SkPerlinNoiseShaderImpl::StitchData& stitchData() const { - return fPaintingData->fStitchDataInit; - } - - SkPerlinNoiseShaderImpl::Type type() const { return fType; } - bool stitchTiles() const { return fStitchTiles; } - const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; } - int numOctaves() const { return fNumOctaves; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { - return new GrGLPerlinNoise; - } - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override { - GrGLPerlinNoise::GenKey(*this, caps, b); - } - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const GrPerlinNoiseEffect& s = sBase.cast<GrPerlinNoiseEffect>(); - return fType == s.fType && - fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency && - fNumOctaves == s.fNumOctaves && - fStitchTiles == s.fStitchTiles && - fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit; - } - - GrPerlinNoiseEffect(GrResourceProvider* resourceProvider, - SkPerlinNoiseShaderImpl::Type type, int numOctaves, bool stitchTiles, - SkPerlinNoiseShaderImpl::PaintingData* paintingData, - sk_sp<GrTextureProxy> permutationsProxy, sk_sp<GrTextureProxy> noiseProxy, - const SkMatrix& matrix) - : INHERITED(kNone_OptimizationFlags) - , fType(type) - , fCoordTransform(matrix) - , fNumOctaves(numOctaves) - , fStitchTiles(stitchTiles) - , fPermutationsSampler(resourceProvider, std::move(permutationsProxy)) - , fNoiseSampler(resourceProvider, std::move(noiseProxy)) - , fPaintingData(paintingData) { - this->initClassID<GrPerlinNoiseEffect>(); - this->addTextureSampler(&fPermutationsSampler); - this->addTextureSampler(&fNoiseSampler); - this->addCoordTransform(&fCoordTransform); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - SkPerlinNoiseShaderImpl::Type fType; - GrCoordTransform fCoordTransform; - int fNumOctaves; - bool fStitchTiles; - TextureSampler fPermutationsSampler; - TextureSampler fNoiseSampler; - SkPerlinNoiseShaderImpl::PaintingData *fPaintingData; - -private: - typedef GrFragmentProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoiseEffect); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrPerlinNoiseEffect::TestCreate(GrProcessorTestData* d) { - int numOctaves = d->fRandom->nextRangeU(2, 10); - bool stitchTiles = d->fRandom->nextBool(); - SkScalar seed = SkIntToScalar(d->fRandom->nextU()); - SkISize tileSize = SkISize::Make(d->fRandom->nextRangeU(4, 4096), - d->fRandom->nextRangeU(4, 4096)); - SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, - 0.99f); - SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, - 0.99f); - - sk_sp<SkShader> shader(d->fRandom->nextBool() ? - SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed, - stitchTiles ? &tileSize : nullptr) : - SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, - stitchTiles ? &tileSize : nullptr)); - - GrTest::TestAsFPArgs asFPArgs(d); - return as_SB(shader)->asFragmentProcessor(asFPArgs.args()); -} -#endif - -void GrGLPerlinNoise::emitCode(EmitArgs& args) { - const GrPerlinNoiseEffect& pne = args.fFp.cast<GrPerlinNoiseEffect>(); - - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - - fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "baseFrequency"); - const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni); - - const char* stitchDataUni = nullptr; - if (pne.stitchTiles()) { - fStitchDataUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "stitchData"); - stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni); - } - - // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8 - const char* chanCoordR = "0.125"; - const char* chanCoordG = "0.375"; - const char* chanCoordB = "0.625"; - const char* chanCoordA = "0.875"; - const char* chanCoord = "chanCoord"; - const char* stitchData = "stitchData"; - const char* ratio = "ratio"; - const char* noiseVec = "noiseVec"; - const char* noiseSmooth = "noiseSmooth"; - const char* floorVal = "floorVal"; - const char* fractVal = "fractVal"; - const char* uv = "uv"; - const char* ab = "ab"; - const char* latticeIdx = "latticeIdx"; - const char* bcoords = "bcoords"; - const char* lattice = "lattice"; - const char* inc8bit = "0.00390625"; // 1.0 / 256.0 - // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a - // [-1,1] vector and perform a dot product between that vector and the provided vector. - const char* dotLattice = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);"; - - // Add noise function - static const GrShaderVar gPerlinNoiseArgs[] = { - GrShaderVar(chanCoord, kFloat_GrSLType), - GrShaderVar(noiseVec, kVec2f_GrSLType) - }; - - static const GrShaderVar gPerlinNoiseStitchArgs[] = { - GrShaderVar(chanCoord, kFloat_GrSLType), - GrShaderVar(noiseVec, kVec2f_GrSLType), - GrShaderVar(stitchData, kVec2f_GrSLType) - }; - - SkString noiseCode; - - noiseCode.appendf("\tvec4 %s;\n", floorVal); - noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec); - noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal); - noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec); - - // smooth curve : t * t * (3 - 2 * t) - noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);", - noiseSmooth, fractVal, fractVal, fractVal); - - // Adjust frequencies if we're stitching tiles - if (pne.stitchTiles()) { - noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }", - floorVal, stitchData, floorVal, stitchData); - noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }", - floorVal, stitchData, floorVal, stitchData); - noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }", - floorVal, stitchData, floorVal, stitchData); - noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }", - floorVal, stitchData, floorVal, stitchData); - } - - // Get texture coordinates and normalize - noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n", - floorVal, floorVal); - - // Get permutation for x - { - SkString xCoords(""); - xCoords.appendf("vec2(%s.x, 0.5)", floorVal); - - noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(), - kVec2f_GrSLType); - noiseCode.append(".r;"); - } - - // Get permutation for x + 1 - { - SkString xCoords(""); - xCoords.appendf("vec2(%s.z, 0.5)", floorVal); - - noiseCode.appendf("\n\t%s.y = ", latticeIdx); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[0], xCoords.c_str(), - kVec2f_GrSLType); - noiseCode.append(".r;"); - } - -#if defined(SK_BUILD_FOR_ANDROID) - // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3). - // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit - // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725 - // (or 0.484368 here). The following rounding operation prevents these precision issues from - // affecting the result of the noise by making sure that we only have multiples of 1/255. - // (Note that 1/255 is about 0.003921569, which is the value used here). - noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);", - latticeIdx, latticeIdx); -#endif - - // Get (x,y) coordinates with the permutated x - noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal); - - noiseCode.appendf("\n\n\tvec2 %s;", uv); - // Compute u, at offset (0,0) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord); - noiseCode.appendf("\n\tvec4 %s = ", lattice); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.x = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal); - // Compute v, at offset (-1,0) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord); - noiseCode.append("\n\tlattice = "); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.y = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - // Compute 'a' as a linear interpolation of 'u' and 'v' - noiseCode.appendf("\n\tvec2 %s;", ab); - noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth); - - noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal); - // Compute v, at offset (-1,-1) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord); - noiseCode.append("\n\tlattice = "); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.y = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - noiseCode.appendf("\n\t%s.x += 1.0;", fractVal); - // Compute u, at offset (0,-1) - { - SkString latticeCoords(""); - latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord); - noiseCode.append("\n\tlattice = "); - fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[1], latticeCoords.c_str(), - kVec2f_GrSLType); - noiseCode.appendf(".bgra;\n\t%s.x = ", uv); - noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); - } - - // Compute 'b' as a linear interpolation of 'u' and 'v' - noiseCode.appendf("\n\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth); - // Compute the noise as a linear interpolation of 'a' and 'b' - noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth); - - SkString noiseFuncName; - if (pne.stitchTiles()) { - fragBuilder->emitFunction(kFloat_GrSLType, - "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs), - gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName); - } else { - fragBuilder->emitFunction(kFloat_GrSLType, - "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs), - gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName); - } - - // There are rounding errors if the floor operation is not performed here - fragBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;", - noiseVec, vCoords.c_str(), baseFrequencyUni); - - // Clear the color accumulator - fragBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", args.fOutputColor); - - if (pne.stitchTiles()) { - // Set up TurbulenceInitial stitch values. - fragBuilder->codeAppendf("vec2 %s = %s;", stitchData, stitchDataUni); - } - - fragBuilder->codeAppendf("float %s = 1.0;", ratio); - - // Loop over all octaves - fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves()); - - fragBuilder->codeAppendf("%s += ", args.fOutputColor); - if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) { - fragBuilder->codeAppend("abs("); - } - if (pne.stitchTiles()) { - fragBuilder->codeAppendf( - "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s)," - "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))", - noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData, - noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData, - noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData, - noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData); - } else { - fragBuilder->codeAppendf( - "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s)," - "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))", - noiseFuncName.c_str(), chanCoordR, noiseVec, - noiseFuncName.c_str(), chanCoordG, noiseVec, - noiseFuncName.c_str(), chanCoordB, noiseVec, - noiseFuncName.c_str(), chanCoordA, noiseVec); - } - if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) { - fragBuilder->codeAppendf(")"); // end of "abs(" - } - fragBuilder->codeAppendf(" * %s;", ratio); - - fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec); - fragBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio); - - if (pne.stitchTiles()) { - fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData); - } - fragBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves - - if (pne.type() == SkPerlinNoiseShaderImpl::kFractalNoise_Type) { - // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 - // by fractalNoise and (turbulenceFunctionResult) by turbulence. - fragBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);", - args.fOutputColor,args.fOutputColor); - } - - // Clamp values - fragBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor); - - // Pre-multiply the result - fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n", - args.fOutputColor, args.fOutputColor, - args.fOutputColor, args.fOutputColor); -} - -void GrGLPerlinNoise::GenKey(const GrProcessor& processor, const GrShaderCaps&, - GrProcessorKeyBuilder* b) { - const GrPerlinNoiseEffect& turbulence = processor.cast<GrPerlinNoiseEffect>(); - - uint32_t key = turbulence.numOctaves(); - - key = key << 3; // Make room for next 3 bits - - switch (turbulence.type()) { - case SkPerlinNoiseShaderImpl::kFractalNoise_Type: - key |= 0x1; - break; - case SkPerlinNoiseShaderImpl::kTurbulence_Type: - key |= 0x2; - break; - default: - // leave key at 0 - break; - } - - if (turbulence.stitchTiles()) { - key |= 0x4; // Flip the 3rd bit if tile stitching is on - } - - b->add32(key); -} - -void GrGLPerlinNoise::onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - - const GrPerlinNoiseEffect& turbulence = processor.cast<GrPerlinNoiseEffect>(); - - const SkVector& baseFrequency = turbulence.baseFrequency(); - pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY); - - if (turbulence.stitchTiles()) { - const SkPerlinNoiseShaderImpl::StitchData& stitchData = turbulence.stitchData(); - pdman.set2f(fStitchDataUni, SkIntToScalar(stitchData.fWidth), - SkIntToScalar(stitchData.fHeight)); - } -} - -///////////////////////////////////////////////////////////////////// -sk_sp<GrFragmentProcessor> SkPerlinNoiseShaderImpl::asFragmentProcessor( - const AsFPArgs& args) const { - SkASSERT(args.fContext); - - SkMatrix localMatrix = this->getLocalMatrix(); - if (args.fLocalMatrix) { - localMatrix.preConcat(*args.fLocalMatrix); - } - - SkMatrix matrix = *args.fViewMatrix; - matrix.preConcat(localMatrix); - - if (0 == fNumOctaves) { - if (kFractalNoise_Type == fType) { - // Extract the incoming alpha and emit rgba = (a/4, a/4, a/4, a/2) - // TODO: Either treat the output of this shader as sRGB or allow client to specify a - // color space of the noise. Either way, this case (and the GLSL) need to convert to - // the destination. - sk_sp<GrFragmentProcessor> inner( - GrConstColorProcessor::Make(GrColor4f::FromGrColor(0x80404040), - GrConstColorProcessor::kModulateRGBA_InputMode)); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); - } - // Emit zero. - return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), - GrConstColorProcessor::kIgnore_InputMode); - } - - // Either we don't stitch tiles, either we have a valid tile size - SkASSERT(!fStitchTiles || !fTileSize.isEmpty()); - - SkPerlinNoiseShaderImpl::PaintingData* paintingData = - new PaintingData(fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix); - sk_sp<GrTextureProxy> permutationsProxy(GrMakeCachedBitmapProxy( - args.fContext->resourceProvider(), - paintingData->getPermutationsBitmap())); - sk_sp<GrTextureProxy> noiseProxy(GrMakeCachedBitmapProxy(args.fContext->resourceProvider(), - paintingData->getNoiseBitmap())); - - SkMatrix m = *args.fViewMatrix; - m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1); - m.setTranslateY(-localMatrix.getTranslateY() + SK_Scalar1); - if (permutationsProxy && noiseProxy) { - sk_sp<GrFragmentProcessor> inner( - GrPerlinNoiseEffect::Make(args.fContext->resourceProvider(), - fType, - fNumOctaves, - fStitchTiles, - paintingData, - std::move(permutationsProxy), - std::move(noiseProxy), - m)); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); - } - delete paintingData; - return nullptr; -} - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkPerlinNoiseShaderImpl::toString(SkString* str) const { - str->append("SkPerlinNoiseShader: ("); - - str->append("type: "); - switch (fType) { - case kFractalNoise_Type: - str->append("\"fractal noise\""); - break; - case kTurbulence_Type: - str->append("\"turbulence\""); - break; - default: - str->append("\"unknown\""); - break; - } - str->append(" base frequency: ("); - str->appendScalar(fBaseFrequencyX); - str->append(", "); - str->appendScalar(fBaseFrequencyY); - str->append(") number of octaves: "); - str->appendS32(fNumOctaves); - str->append(" seed: "); - str->appendScalar(fSeed); - str->append(" stitch tiles: "); - str->append(fStitchTiles ? "true " : "false "); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkPerlinNoiseShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPerlinNoiseShaderImpl) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp deleted file mode 100644 index d6ee941251..0000000000 --- a/src/shaders/SkPictureShader.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkPictureShader.h" - -#include "SkArenaAlloc.h" -#include "SkBitmap.h" -#include "SkBitmapProcShader.h" -#include "SkCanvas.h" -#include "SkColorSpaceXformCanvas.h" -#include "SkImage.h" -#include "SkImageShader.h" -#include "SkMatrixUtils.h" -#include "SkPicture.h" -#include "SkPictureImageGenerator.h" -#include "SkReadBuffer.h" -#include "SkResourceCache.h" - -#if SK_SUPPORT_GPU -#include "GrContext.h" -#include "GrCaps.h" -#include "GrFragmentProcessor.h" -#endif - -namespace { -static unsigned gBitmapSkaderKeyNamespaceLabel; - -struct BitmapShaderKey : public SkResourceCache::Key { -public: - BitmapShaderKey(sk_sp<SkColorSpace> colorSpace, - uint32_t pictureID, - const SkRect& tile, - SkShader::TileMode tmx, - SkShader::TileMode tmy, - const SkSize& scale, - const SkMatrix& localMatrix, - SkTransferFunctionBehavior blendBehavior) - : fColorSpace(std::move(colorSpace)) - , fPictureID(pictureID) - , fTile(tile) - , fTmx(tmx) - , fTmy(tmy) - , fScale(scale) - , fBlendBehavior(blendBehavior) { - - for (int i = 0; i < 9; ++i) { - fLocalMatrixStorage[i] = localMatrix[i]; - } - - static const size_t keySize = sizeof(fColorSpace) + - sizeof(fPictureID) + - sizeof(fTile) + - sizeof(fTmx) + sizeof(fTmy) + - sizeof(fScale) + - sizeof(fLocalMatrixStorage) + - sizeof(fBlendBehavior); - // This better be packed. - SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - (uint32_t*)&fColorSpace) == keySize); - this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize); - } - -private: - sk_sp<SkColorSpace> fColorSpace; - uint32_t fPictureID; - SkRect fTile; - SkShader::TileMode fTmx, fTmy; - SkSize fScale; - SkScalar fLocalMatrixStorage[9]; - SkTransferFunctionBehavior fBlendBehavior; - - SkDEBUGCODE(uint32_t fEndOfStruct;) -}; - -struct BitmapShaderRec : public SkResourceCache::Rec { - BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader) - : fKey(key) - , fShader(SkRef(tileShader)) {} - - BitmapShaderKey fKey; - sk_sp<SkShader> fShader; - size_t fBitmapBytes; - - const Key& getKey() const override { return fKey; } - size_t bytesUsed() const override { - // Just the record overhead -- the actual pixels are accounted by SkImageCacherator. - return sizeof(fKey) + sizeof(SkImageShader); - } - const char* getCategory() const override { return "bitmap-shader"; } - SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; } - - static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) { - const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec); - sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader); - - *result = rec.fShader; - - // The bitmap shader is backed by an image generator, thus it can always re-generate its - // pixels if discarded. - return true; - } -}; - -} // namespace - -SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix, const SkRect* tile, - sk_sp<SkColorSpace> colorSpace) - : INHERITED(localMatrix) - , fPicture(std::move(picture)) - , fTile(tile ? *tile : fPicture->cullRect()) - , fTmx(tmx) - , fTmy(tmy) - , fColorSpace(std::move(colorSpace)) -{} - -sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix, const SkRect* tile) { - if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) { - return SkShader::MakeEmptyShader(); - } - return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile, - nullptr)); -} - -sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) { - SkMatrix lm; - buffer.readMatrix(&lm); - TileMode mx = (TileMode)buffer.read32(); - TileMode my = (TileMode)buffer.read32(); - SkRect tile; - buffer.readRect(&tile); - - sk_sp<SkPicture> picture; - - if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) { - if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) { - // Older code blindly serialized pictures. We don't trust them. - buffer.validate(false); - return nullptr; - } - // Newer code won't serialize pictures in disallow-cross-process-picture mode. - // Assert that they didn't serialize anything except a false here. - buffer.validate(!buffer.readBool()); - } else { - // Old code always serialized the picture. New code writes a 'true' first if it did. - if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) || - buffer.readBool()) { - picture = SkPicture::MakeFromBuffer(buffer); - } - } - return SkPictureShader::Make(picture, mx, my, &lm, &tile); -} - -void SkPictureShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeMatrix(this->getLocalMatrix()); - buffer.write32(fTmx); - buffer.write32(fTmy); - buffer.writeRect(fTile); - - // The deserialization code won't trust that our serialized picture is safe to deserialize. - // So write a 'false' telling it that we're not serializing a picture. - if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) { - buffer.writeBool(false); - } else { - buffer.writeBool(true); - fPicture->flatten(buffer); - } -} - -sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM, - SkColorSpace* dstColorSpace, - const int maxTextureSize) const { - SkASSERT(fPicture && !fPicture->cullRect().isEmpty()); - - SkMatrix m; - m.setConcat(viewMatrix, this->getLocalMatrix()); - if (localM) { - m.preConcat(*localM); - } - - // Use a rotation-invariant scale - SkPoint scale; - // - // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines! - // - if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) { - // Decomposition failed, use an approximation. - scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), - SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); - } - SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()), - SkScalarAbs(scale.y() * fTile.height())); - - // Clamp the tile size to about 4M pixels - static const SkScalar kMaxTileArea = 2048 * 2048; - SkScalar tileArea = scaledSize.width() * scaledSize.height(); - if (tileArea > kMaxTileArea) { - SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea); - scaledSize.set(scaledSize.width() * clampScale, - scaledSize.height() * clampScale); - } -#if SK_SUPPORT_GPU - // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture - if (maxTextureSize) { - if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { - SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height()); - scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale), - SkScalarFloorToScalar(scaledSize.height() * downScale)); - } - } -#endif - -#ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING - const SkISize tileSize = scaledSize.toRound(); -#else - const SkISize tileSize = scaledSize.toCeil(); -#endif - if (tileSize.isEmpty()) { - return SkShader::MakeEmptyShader(); - } - - // The actual scale, compensating for rounding & clamping. - const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(), - SkIntToScalar(tileSize.height()) / fTile.height()); - - // |fColorSpace| will only be set when using an SkColorSpaceXformCanvas to do pre-draw xforms. - // This canvas is strictly for legacy mode. A non-null |dstColorSpace| indicates that we - // should perform color correct rendering and xform at draw time. - SkASSERT(!fColorSpace || !dstColorSpace); - sk_sp<SkColorSpace> keyCS = dstColorSpace ? sk_ref_sp(dstColorSpace) : fColorSpace; - SkTransferFunctionBehavior blendBehavior = dstColorSpace ? SkTransferFunctionBehavior::kRespect - : SkTransferFunctionBehavior::kIgnore; - - sk_sp<SkShader> tileShader; - BitmapShaderKey key(std::move(keyCS), - fPicture->uniqueID(), - fTile, - fTmx, - fTmy, - tileScale, - this->getLocalMatrix(), - blendBehavior); - - if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) { - SkMatrix tileMatrix; - tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()), - SkMatrix::kFill_ScaleToFit); - - sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator( - SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr, - SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace))); - if (!tileImage) { - return nullptr; - } - - if (fColorSpace) { - tileImage = tileImage->makeColorSpace(fColorSpace, SkTransferFunctionBehavior::kIgnore); - } - - SkMatrix shaderMatrix = this->getLocalMatrix(); - shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height()); - tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix); - - SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get())); - } - - return tileShader; -} - -bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc, - const SkMatrix& ctm, const SkPaint& paint, - const SkMatrix* localMatrix) const { - // Keep bitmapShader alive by using alloc instead of stack memory - auto& bitmapShader = *alloc->make<sk_sp<SkShader>>(); - bitmapShader = this->refBitmapShader(ctm, localMatrix, cs); - return bitmapShader && as_SB(bitmapShader)->appendStages(p, cs, alloc, ctm, paint); -} - -///////////////////////////////////////////////////////////////////////////////////////// -SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) -const { - sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix, - rec.fDstColorSpace)); - if (!bitmapShader) { - return nullptr; - } - - PictureShaderContext* ctx = - alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc); - if (nullptr == ctx->fBitmapShaderContext) { - ctx = nullptr; - } - return ctx; -} - -sk_sp<SkShader> SkPictureShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - return sk_sp<SkPictureShader>(new SkPictureShader(fPicture, fTmx, fTmy, &this->getLocalMatrix(), - &fTile, xformer->dst())); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -SkPictureShader::PictureShaderContext::PictureShaderContext( - const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader, - SkArenaAlloc* alloc) - : INHERITED(shader, rec) - , fBitmapShader(std::move(bitmapShader)) -{ - fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc); - //if fBitmapShaderContext is null, we are invalid -} - -uint32_t SkPictureShader::PictureShaderContext::getFlags() const { - SkASSERT(fBitmapShaderContext); - return fBitmapShaderContext->getFlags(); -} - -SkShaderBase::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) { - SkASSERT(fBitmapShaderContext); - return fBitmapShaderContext->asAShadeProc(ctx); -} - -void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { - SkASSERT(fBitmapShaderContext); - fBitmapShaderContext->shadeSpan(x, y, dstC, count); -} - -#ifndef SK_IGNORE_TO_STRING -void SkPictureShader::toString(SkString* str) const { - static const char* gTileModeName[SkShader::kTileModeCount] = { - "clamp", "repeat", "mirror" - }; - - str->appendf("PictureShader: [%f:%f:%f:%f] ", - fPicture->cullRect().fLeft, - fPicture->cullRect().fTop, - fPicture->cullRect().fRight, - fPicture->cullRect().fBottom); - - str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]); - - this->INHERITED::toString(str); -} -#endif - -#if SK_SUPPORT_GPU -sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const { - int maxTextureSize = 0; - if (args.fContext) { - maxTextureSize = args.fContext->caps()->maxTextureSize(); - } - sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix, - args.fDstColorSpace, maxTextureSize)); - if (!bitmapShader) { - return nullptr; - } - return as_SB(bitmapShader)->asFragmentProcessor(SkShaderBase::AsFPArgs( - args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace)); -} -#endif diff --git a/src/shaders/SkPictureShader.h b/src/shaders/SkPictureShader.h deleted file mode 100644 index f7a509f181..0000000000 --- a/src/shaders/SkPictureShader.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPictureShader_DEFINED -#define SkPictureShader_DEFINED - -#include "SkShaderBase.h" - -class SkArenaAlloc; -class SkBitmap; -class SkPicture; - -/* - * An SkPictureShader can be used to draw SkPicture-based patterns. - * - * The SkPicture is first rendered into a tile, which is then used to shade the area according - * to specified tiling rules. - */ -class SkPictureShader : public SkShaderBase { -public: - static sk_sp<SkShader> Make(sk_sp<SkPicture>, TileMode, TileMode, const SkMatrix*, - const SkRect*); - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureShader) - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - -protected: - SkPictureShader(SkReadBuffer&); - void flatten(SkWriteBuffer&) const override; - bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*, - const SkMatrix&, const SkPaint&, const SkMatrix*) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - SkPictureShader(sk_sp<SkPicture>, TileMode, TileMode, const SkMatrix*, const SkRect*, - sk_sp<SkColorSpace>); - - sk_sp<SkShader> refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix, - SkColorSpace* dstColorSpace, - const int maxTextureSize = 0) const; - - sk_sp<SkPicture> fPicture; - SkRect fTile; - TileMode fTmx, fTmy; - - class PictureShaderContext : public Context { - public: - PictureShaderContext( - const SkPictureShader&, const ContextRec&, sk_sp<SkShader> bitmapShader, SkArenaAlloc*); - - uint32_t getFlags() const override; - - ShadeProc asAShadeProc(void** ctx) override; - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - sk_sp<SkShader> fBitmapShader; - SkShaderBase::Context* fBitmapShaderContext; - void* fBitmapShaderContextStorage; - - typedef Context INHERITED; - }; - - // Should never be set by a public constructor. This is only used when onMakeColorSpace() - // forces a deferred color space xform. - sk_sp<SkColorSpace> fColorSpace; - - typedef SkShaderBase INHERITED; -}; - -#endif // SkPictureShader_DEFINED diff --git a/src/shaders/SkShader.cpp b/src/shaders/SkShader.cpp deleted file mode 100644 index d04fbfe4df..0000000000 --- a/src/shaders/SkShader.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkArenaAlloc.h" -#include "SkAtomics.h" -#include "SkBitmapProcShader.h" -#include "SkColorShader.h" -#include "SkEmptyShader.h" -#include "SkMallocPixelRef.h" -#include "SkPaint.h" -#include "SkPicture.h" -#include "SkPictureShader.h" -#include "SkPM4fPriv.h" -#include "SkRasterPipeline.h" -#include "SkReadBuffer.h" -#include "SkScalar.h" -#include "SkShaderBase.h" -#include "SkTLazy.h" -#include "SkWriteBuffer.h" -#include "../jumper/SkJumper.h" - -#if SK_SUPPORT_GPU -#include "GrFragmentProcessor.h" -#endif - -//#define SK_TRACK_SHADER_LIFETIME - -#ifdef SK_TRACK_SHADER_LIFETIME - static int32_t gShaderCounter; -#endif - -static inline void inc_shader_counter() { -#ifdef SK_TRACK_SHADER_LIFETIME - int32_t prev = sk_atomic_inc(&gShaderCounter); - SkDebugf("+++ shader counter %d\n", prev + 1); -#endif -} -static inline void dec_shader_counter() { -#ifdef SK_TRACK_SHADER_LIFETIME - int32_t prev = sk_atomic_dec(&gShaderCounter); - SkDebugf("--- shader counter %d\n", prev - 1); -#endif -} - -SkShaderBase::SkShaderBase(const SkMatrix* localMatrix) - : fLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I()) { - inc_shader_counter(); - // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe. - (void)fLocalMatrix.getType(); -} - -SkShaderBase::~SkShaderBase() { - dec_shader_counter(); -} - -void SkShaderBase::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - bool hasLocalM = !fLocalMatrix.isIdentity(); - buffer.writeBool(hasLocalM); - if (hasLocalM) { - buffer.writeMatrix(fLocalMatrix); - } -} - -bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm, - const SkMatrix* outerLocalMatrix, - SkMatrix* totalInverse) const { - SkMatrix total = SkMatrix::Concat(ctm, fLocalMatrix); - if (outerLocalMatrix) { - total.preConcat(*outerLocalMatrix); - } - - return total.invert(totalInverse); -} - -bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const { - SkColor storage; - if (nullptr == colorPtr) { - colorPtr = &storage; - } - if (this->onAsLuminanceColor(colorPtr)) { - *colorPtr = SkColorSetA(*colorPtr, 0xFF); // we only return opaque - return true; - } - return false; -} - -SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const { - if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) { - return nullptr; - } - return this->onMakeContext(rec, alloc); -} - -SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec) - : fShader(shader), fCTM(*rec.fMatrix) -{ - // We should never use a context for RP-only shaders. - SkASSERT(!shader.isRasterPipelineOnly()); - - // Because the context parameters must be valid at this point, we know that the matrix is - // invertible. - SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse)); - fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse); - - fPaintAlpha = rec.fPaint->getAlpha(); -} - -SkShaderBase::Context::~Context() {} - -SkShaderBase::Context::ShadeProc SkShaderBase::Context::asAShadeProc(void** ctx) { - return nullptr; -} - -void SkShaderBase::Context::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { - const int N = 128; - SkPMColor tmp[N]; - while (count > 0) { - int n = SkTMin(count, N); - this->shadeSpan(x, y, tmp, n); - for (int i = 0; i < n; ++i) { - dst[i] = SkPM4f::FromPMColor(tmp[i]); - } - dst += n; - x += n; - count -= n; - } -} - -#include "SkColorPriv.h" - -#define kTempColorQuadCount 6 // balance between speed (larger) and saving stack-space -#define kTempColorCount (kTempColorQuadCount << 2) - -#ifdef SK_CPU_BENDIAN - #define SkU32BitShiftToByteOffset(shift) (3 - ((shift) >> 3)) -#else - #define SkU32BitShiftToByteOffset(shift) ((shift) >> 3) -#endif - -void SkShaderBase::Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { - SkASSERT(count > 0); - - SkPMColor colors[kTempColorCount]; - - while ((count -= kTempColorCount) >= 0) { - this->shadeSpan(x, y, colors, kTempColorCount); - x += kTempColorCount; - - const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); - int quads = kTempColorQuadCount; - do { - U8CPU a0 = srcA[0]; - U8CPU a1 = srcA[4]; - U8CPU a2 = srcA[8]; - U8CPU a3 = srcA[12]; - srcA += 4*4; - *alpha++ = SkToU8(a0); - *alpha++ = SkToU8(a1); - *alpha++ = SkToU8(a2); - *alpha++ = SkToU8(a3); - } while (--quads != 0); - } - SkASSERT(count < 0); - SkASSERT(count + kTempColorCount >= 0); - if (count += kTempColorCount) { - this->shadeSpan(x, y, colors, count); - - const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); - do { - *alpha++ = *srcA; - srcA += 4; - } while (--count != 0); - } -#if 0 - do { - int n = count; - if (n > kTempColorCount) - n = kTempColorCount; - SkASSERT(n > 0); - - this->shadeSpan(x, y, colors, n); - x += n; - count -= n; - - const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); - do { - *alpha++ = *srcA; - srcA += 4; - } while (--n != 0); - } while (count > 0); -#endif -} - -SkShaderBase::Context::MatrixClass SkShaderBase::Context::ComputeMatrixClass(const SkMatrix& mat) { - MatrixClass mc = kLinear_MatrixClass; - - if (mat.hasPerspective()) { - if (mat.isFixedStepInX()) { - mc = kFixedStepInX_MatrixClass; - } else { - mc = kPerspective_MatrixClass; - } - } - return mc; -} - -////////////////////////////////////////////////////////////////////////////// - -const SkMatrix& SkShader::getLocalMatrix() const { - return as_SB(this)->getLocalMatrix(); -} - -#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP -bool SkShader::isABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, TileMode xy[2]) const { - return as_SB(this)->onIsABitmap(outTexture, outMatrix, xy); -} -#endif - -SkImage* SkShader::isAImage(SkMatrix* localMatrix, TileMode xy[2]) const { - return as_SB(this)->onIsAImage(localMatrix, xy); -} - -SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const { - return kNone_GradientType; -} - -#if SK_SUPPORT_GPU -sk_sp<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const AsFPArgs&) const { - return nullptr; -} -#endif - -sk_sp<SkShader> SkShader::makeAsALocalMatrixShader(SkMatrix*) const { - return nullptr; -} - -sk_sp<SkShader> SkShader::MakeEmptyShader() { return sk_make_sp<SkEmptyShader>(); } - -sk_sp<SkShader> SkShader::MakeColorShader(SkColor color) { return sk_make_sp<SkColorShader>(color); } - -sk_sp<SkShader> SkShader::MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix) { - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - return SkMakeBitmapShader(src, tmx, tmy, localMatrix, kIfMutable_SkCopyPixelsMode); -} - -sk_sp<SkShader> SkShader::MakePictureShader(sk_sp<SkPicture> src, TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix, const SkRect* tile) { - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - return SkPictureShader::Make(std::move(src), tmx, tmy, localMatrix, tile); -} - -#ifndef SK_IGNORE_TO_STRING -void SkShaderBase::toString(SkString* str) const { - if (!fLocalMatrix.isIdentity()) { - str->append(" "); - fLocalMatrix.toString(str); - } -} -#endif - -bool SkShaderBase::appendStages(SkRasterPipeline* p, - SkColorSpace* dstCS, - SkArenaAlloc* alloc, - const SkMatrix& ctm, - const SkPaint& paint, - const SkMatrix* localM) const { - return this->onAppendStages(p, dstCS, alloc, ctm, paint, localM); -} - -bool SkShaderBase::onAppendStages(SkRasterPipeline* p, - SkColorSpace* dstCS, - SkArenaAlloc* alloc, - const SkMatrix& ctm, - const SkPaint& paint, - const SkMatrix* localM) const { - return false; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) { - return SkShader::MakeEmptyShader(); -} - -#ifndef SK_IGNORE_TO_STRING -#include "SkEmptyShader.h" - -void SkEmptyShader::toString(SkString* str) const { - str->append("SkEmptyShader: ("); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/src/shaders/SkShaderBase.h b/src/shaders/SkShaderBase.h deleted file mode 100644 index 895fc6cf05..0000000000 --- a/src/shaders/SkShaderBase.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkShaderBase_DEFINED -#define SkShaderBase_DEFINED - -#include "SkFilterQuality.h" -#include "SkMatrix.h" -#include "SkShader.h" - -class GrContext; -class GrFragmentProcessor; -class SkArenaAlloc; -class SkColorSpace; -class SkColorSpaceXformer; -class SkImage; -struct SkImageInfo; -class SkPaint; -class SkRasterPipeline; - -class SkShaderBase : public SkShader { -public: - SkShaderBase(const SkMatrix* localMatrix = nullptr); - - ~SkShaderBase() override; - - /** - * Returns true if the shader is guaranteed to produce only a single color. - * Subclasses can override this to allow loop-hoisting optimization. - */ - virtual bool isConstant() const { return false; } - - const SkMatrix& getLocalMatrix() const { return fLocalMatrix; } - - enum Flags { - //!< set if all of the colors will be opaque - kOpaqueAlpha_Flag = 1 << 0, - - /** set 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. This flag specifies this for - shadeSpan(). - */ - kConstInY32_Flag = 1 << 1, - - /** hint for the blitter that 4f is the preferred shading mode. - */ - kPrefers4f_Flag = 1 << 2, - }; - - /** - * ContextRec acts as a parameter bundle for creating Contexts. - */ - struct ContextRec { - enum DstType { - kPMColor_DstType, // clients prefer shading into PMColor dest - kPM4f_DstType, // clients prefer shading into PM4f dest - }; - - ContextRec(const SkPaint& paint, const SkMatrix& matrix, const SkMatrix* localM, - DstType dstType, SkColorSpace* dstColorSpace) - : fPaint(&paint) - , fMatrix(&matrix) - , fLocalMatrix(localM) - , fPreferredDstType(dstType) - , fDstColorSpace(dstColorSpace) {} - - const SkPaint* fPaint; // the current paint associated with the draw - const SkMatrix* fMatrix; // the current matrix in the canvas - const SkMatrix* fLocalMatrix; // optional local matrix - const DstType fPreferredDstType; // the "natural" client dest type - SkColorSpace* fDstColorSpace; // the color space of the dest surface (if any) - }; - - class Context : public ::SkNoncopyable { - public: - Context(const SkShaderBase& shader, const ContextRec&); - - virtual ~Context(); - - /** - * Called sometimes before drawing with this shader. Return the type of - * alpha your shader will return. The default implementation returns 0. - * Your subclass should override if it can (even sometimes) report a - * non-zero value, since that will enable various blitters to perform - * faster. - */ - virtual uint32_t getFlags() const { return 0; } - - /** - * Called for each span of the object being drawn. Your subclass should - * set the appropriate colors (with premultiplied alpha) that correspond - * to the specified device coordinates. - */ - virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0; - - virtual void shadeSpan4f(int x, int y, SkPM4f[], int count); - - struct BlitState; - typedef void (*BlitBW)(BlitState*, - int x, int y, const SkPixmap&, int count); - typedef void (*BlitAA)(BlitState*, - int x, int y, const SkPixmap&, int count, const SkAlpha[]); - - struct BlitState { - // inputs - Context* fCtx; - SkBlendMode fMode; - - // outputs - enum { N = 2 }; - void* fStorage[N]; - BlitBW fBlitBW; - BlitAA fBlitAA; - }; - - // Returns true if one or more of the blitprocs are set in the BlitState - bool chooseBlitProcs(const SkImageInfo& info, BlitState* state) { - state->fBlitBW = nullptr; - state->fBlitAA = nullptr; - if (this->onChooseBlitProcs(info, state)) { - SkASSERT(state->fBlitBW || state->fBlitAA); - return true; - } - return false; - } - - /** - * The const void* ctx is only const because all the implementations are const. - * This can be changed to non-const if a new shade proc needs to change the ctx. - */ - typedef void (*ShadeProc)(const void* ctx, int x, int y, SkPMColor[], int count); - virtual ShadeProc asAShadeProc(void** ctx); - - /** - * Similar to shadeSpan, but only returns the alpha-channel for a span. - * The default implementation calls shadeSpan() and then extracts the alpha - * values from the returned colors. - */ - virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count); - - // Notification from blitter::blitMask in case we need to see the non-alpha channels - virtual void set3DMask(const SkMask*) {} - - protected: - // Reference to shader, so we don't have to dupe information. - const SkShaderBase& fShader; - - enum MatrixClass { - kLinear_MatrixClass, // no perspective - kFixedStepInX_MatrixClass, // fast perspective, need to call fixedStepInX() each - // scanline - kPerspective_MatrixClass // slow perspective, need to mappoints each pixel - }; - static MatrixClass ComputeMatrixClass(const SkMatrix&); - - uint8_t getPaintAlpha() const { return fPaintAlpha; } - const SkMatrix& getTotalInverse() const { return fTotalInverse; } - MatrixClass getInverseClass() const { return (MatrixClass)fTotalInverseClass; } - const SkMatrix& getCTM() const { return fCTM; } - - virtual bool onChooseBlitProcs(const SkImageInfo&, BlitState*) { return false; } - - private: - SkMatrix fCTM; - SkMatrix fTotalInverse; - uint8_t fPaintAlpha; - uint8_t fTotalInverseClass; - - typedef SkNoncopyable INHERITED; - }; - - /** - * Make a context using the memory provided by the arena. - * - * @return pointer to context or nullptr if can't be created - */ - Context* makeContext(const ContextRec&, SkArenaAlloc*) const; - -#if SK_SUPPORT_GPU - struct AsFPArgs { - AsFPArgs() {} - AsFPArgs(GrContext* context, - const SkMatrix* viewMatrix, - const SkMatrix* localMatrix, - SkFilterQuality filterQuality, - SkColorSpace* dstColorSpace) - : fContext(context) - , fViewMatrix(viewMatrix) - , fLocalMatrix(localMatrix) - , fFilterQuality(filterQuality) - , fDstColorSpace(dstColorSpace) {} - - GrContext* fContext; - const SkMatrix* fViewMatrix; - const SkMatrix* fLocalMatrix; - SkFilterQuality fFilterQuality; - SkColorSpace* fDstColorSpace; - }; - - /** - * Returns a GrFragmentProcessor that implements the shader for the GPU backend. NULL is - * returned if there is no GPU implementation. - * - * The GPU device does not call SkShader::createContext(), instead we pass the view matrix, - * local matrix, and filter quality directly. - * - * The GrContext may be used by the to create textures that are required by the returned - * processor. - * - * The returned GrFragmentProcessor should expect an unpremultiplied input color and - * produce a premultiplied output. - */ - virtual sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const; -#endif - - /** - * If the shader can represent its "average" luminance in a single color, return true and - * if color is not NULL, return that color. If it cannot, return false and ignore the color - * parameter. - * - * Note: if this returns true, the returned color will always be opaque, as only the RGB - * components are used to compute luminance. - */ - bool asLuminanceColor(SkColor*) const; - - /** - * Returns a shader transformed into a new color space via the |xformer|. - */ - sk_sp<SkShader> makeColorSpace(SkColorSpaceXformer* xformer) const { - return this->onMakeColorSpace(xformer); - } - - virtual bool isRasterPipelineOnly() const { return false; } - - bool appendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*, - const SkMatrix& ctm, const SkPaint&, const SkMatrix* localM=nullptr) const; - - bool computeTotalInverse(const SkMatrix& ctm, - const SkMatrix* outerLocalMatrix, - SkMatrix* totalInverse) const; - -#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP - virtual bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode[2]) const { - return false; - } -#endif - - virtual SkImage* onIsAImage(SkMatrix*, TileMode[2]) const { - return nullptr; - } - - SK_TO_STRING_VIRT() - - SK_DEFINE_FLATTENABLE_TYPE(SkShaderBase) - SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() - -protected: - void flatten(SkWriteBuffer&) const override; - - /** - * Specialize creating a SkShader context using the supplied allocator. - * @return pointer to context owned by the arena allocator. - */ - virtual Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const { - return nullptr; - } - - virtual bool onAsLuminanceColor(SkColor*) const { - return false; - } - - virtual sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer*) const { - return sk_ref_sp(const_cast<SkShaderBase*>(this)); - } - - virtual bool onAppendStages(SkRasterPipeline*, SkColorSpace* dstCS, SkArenaAlloc*, - const SkMatrix&, const SkPaint&, const SkMatrix* localM) const; - -private: - // This is essentially const, but not officially so it can be modified in constructors. - SkMatrix fLocalMatrix; - - typedef SkShader INHERITED; -}; - -inline SkShaderBase* as_SB(SkShader* shader) { - return static_cast<SkShaderBase*>(shader); -} - -inline const SkShaderBase* as_SB(const SkShader* shader) { - return static_cast<const SkShaderBase*>(shader); -} - -inline const SkShaderBase* as_SB(const sk_sp<SkShader>& shader) { - return static_cast<SkShaderBase*>(shader.get()); -} - -#endif // SkShaderBase_DEFINED diff --git a/src/shaders/gradients/Sk4fGradientBase.cpp b/src/shaders/gradients/Sk4fGradientBase.cpp deleted file mode 100644 index e20f5f4702..0000000000 --- a/src/shaders/gradients/Sk4fGradientBase.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk4fGradientBase.h" - -#include <functional> - -namespace { - -Sk4f pack_color(SkColor c, bool premul, const Sk4f& component_scale) { - const SkColor4f c4f = SkColor4f::FromColor(c); - const Sk4f pm4f = premul - ? c4f.premul().to4f() - : Sk4f{c4f.fR, c4f.fG, c4f.fB, c4f.fA}; - - return pm4f * component_scale; -} - -class IntervalIterator { -public: - IntervalIterator(const SkColor* colors, const SkScalar* pos, int count, bool reverse) - : fColors(colors) - , fPos(pos) - , fCount(count) - , fFirstPos(reverse ? SK_Scalar1 : 0) - , fBegin(reverse ? count - 1 : 0) - , fAdvance(reverse ? -1 : 1) { - SkASSERT(colors); - SkASSERT(count > 0); - } - - void iterate(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const { - if (!fPos) { - this->iterateImplicitPos(func); - return; - } - - const int end = fBegin + fAdvance * (fCount - 1); - const SkScalar lastPos = 1 - fFirstPos; - int prev = fBegin; - SkScalar prevPos = fFirstPos; - - do { - const int curr = prev + fAdvance; - SkASSERT(curr >= 0 && curr < fCount); - - // TODO: this sanitization should be done in SkGradientShaderBase - const SkScalar currPos = (fAdvance > 0) - ? SkTPin(fPos[curr], prevPos, lastPos) - : SkTPin(fPos[curr], lastPos, prevPos); - - if (currPos != prevPos) { - SkASSERT((currPos - prevPos > 0) == (fAdvance > 0)); - func(fColors[prev], fColors[curr], prevPos, currPos); - } - - prev = curr; - prevPos = currPos; - } while (prev != end); - } - -private: - void iterateImplicitPos(std::function<void(SkColor, SkColor, SkScalar, SkScalar)> func) const { - // When clients don't provide explicit color stop positions (fPos == nullptr), - // the color stops are distributed evenly across the unit interval - // (implicit positioning). - const SkScalar dt = fAdvance * SK_Scalar1 / (fCount - 1); - const int end = fBegin + fAdvance * (fCount - 2); - int prev = fBegin; - SkScalar prevPos = fFirstPos; - - while (prev != end) { - const int curr = prev + fAdvance; - SkASSERT(curr >= 0 && curr < fCount); - - const SkScalar currPos = prevPos + dt; - func(fColors[prev], fColors[curr], prevPos, currPos); - prev = curr; - prevPos = currPos; - } - - // emit the last interval with a pinned end position, to avoid precision issues - func(fColors[prev], fColors[prev + fAdvance], prevPos, 1 - fFirstPos); - } - - const SkColor* fColors; - const SkScalar* fPos; - const int fCount; - const SkScalar fFirstPos; - const int fBegin; - const int fAdvance; -}; - -void addMirrorIntervals(const SkColor colors[], - const SkScalar pos[], int count, - const Sk4f& componentScale, - bool premulColors, bool reverse, - Sk4fGradientIntervalBuffer::BufferType* buffer) { - const IntervalIterator iter(colors, pos, count, reverse); - iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) { - SkASSERT(buffer->empty() || buffer->back().fT1 == 2 - t0); - - const auto mirror_t0 = 2 - t0; - const auto mirror_t1 = 2 - t1; - // mirror_p1 & mirror_p1 may collapse for very small values - recheck to avoid - // triggering Interval asserts. - if (mirror_t0 != mirror_t1) { - buffer->emplace_back(pack_color(c0, premulColors, componentScale), mirror_t0, - pack_color(c1, premulColors, componentScale), mirror_t1); - } - }); -} - -} // anonymous namespace - -Sk4fGradientInterval::Sk4fGradientInterval(const Sk4f& c0, SkScalar t0, - const Sk4f& c1, SkScalar t1) - : fT0(t0) - , fT1(t1) { - SkASSERT(t0 != t1); - // Either p0 or p1 can be (-)inf for synthetic clamp edge intervals. - SkASSERT(SkScalarIsFinite(t0) || SkScalarIsFinite(t1)); - - const auto dt = t1 - t0; - - // Clamp edge intervals are always zero-ramp. - SkASSERT(SkScalarIsFinite(dt) || (c0 == c1).allTrue()); - SkASSERT(SkScalarIsFinite(t0) || (c0 == c1).allTrue()); - const Sk4f dc = SkScalarIsFinite(dt) ? (c1 - c0) / dt : 0; - const Sk4f bias = c0 - (SkScalarIsFinite(t0) ? t0 * dc : 0); - - bias.store(&fCb.fVec); - dc.store(&fCg.fVec); -} - -void Sk4fGradientIntervalBuffer::init(const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode tileMode, bool premulColors, - SkScalar alpha, bool reverse) { - // The main job here is to build a specialized interval list: a different - // representation of the color stops data, optimized for efficient scan line - // access during shading. - // - // [{P0,C0} , {P1,C1}) [{P1,C2} , {P2,c3}) ... [{Pn,C2n} , {Pn+1,C2n+1}) - // - // The list may be inverted when requested (such that e.g. points are sorted - // in increasing x order when dx < 0). - // - // Note: the current representation duplicates pos data; we could refactor to - // avoid this if interval storage size becomes a concern. - // - // Aside from reordering, we also perform two more pre-processing steps at - // this stage: - // - // 1) scale the color components depending on paint alpha and the requested - // interpolation space (note: the interval color storage is SkPM4f, but - // that doesn't necessarily mean the colors are premultiplied; that - // property is tracked in fColorsArePremul) - // - // 2) inject synthetic intervals to support tiling. - // - // * for kRepeat, no extra intervals are needed - the iterator just - // wraps around at the end: - // - // ->[P0,P1)->..[Pn-1,Pn)-> - // - // * for kClamp, we add two "infinite" intervals before/after: - // - // [-/+inf , P0)->[P0 , P1)->..[Pn-1 , Pn)->[Pn , +/-inf) - // - // (the iterator should never run off the end in this mode) - // - // * for kMirror, we extend the range to [0..2] and add a flipped - // interval series - then the iterator operates just as in the - // kRepeat case: - // - // ->[P0,P1)->..[Pn-1,Pn)->[2 - Pn,2 - Pn-1)->..[2 - P1,2 - P0)-> - // - // TODO: investigate collapsing intervals << 1px. - - SkASSERT(count > 0); - SkASSERT(colors); - - fIntervals.reset(); - - const Sk4f componentScale = premulColors - ? Sk4f(alpha) - : Sk4f(1.0f, 1.0f, 1.0f, alpha); - const int first_index = reverse ? count - 1 : 0; - const int last_index = count - 1 - first_index; - const SkScalar first_pos = reverse ? SK_Scalar1 : 0; - const SkScalar last_pos = SK_Scalar1 - first_pos; - - if (tileMode == SkShader::kClamp_TileMode) { - // synthetic edge interval: -/+inf .. P0 - const Sk4f clamp_color = pack_color(colors[first_index], - premulColors, componentScale); - const SkScalar clamp_pos = reverse ? SK_ScalarInfinity : SK_ScalarNegativeInfinity; - fIntervals.emplace_back(clamp_color, clamp_pos, - clamp_color, first_pos); - } else if (tileMode == SkShader::kMirror_TileMode && reverse) { - // synthetic mirror intervals injected before main intervals: (2 .. 1] - addMirrorIntervals(colors, pos, count, componentScale, premulColors, false, &fIntervals); - } - - const IntervalIterator iter(colors, pos, count, reverse); - iter.iterate([&] (SkColor c0, SkColor c1, SkScalar t0, SkScalar t1) { - SkASSERT(fIntervals.empty() || fIntervals.back().fT1 == t0); - - fIntervals.emplace_back(pack_color(c0, premulColors, componentScale), t0, - pack_color(c1, premulColors, componentScale), t1); - }); - - if (tileMode == SkShader::kClamp_TileMode) { - // synthetic edge interval: Pn .. +/-inf - const Sk4f clamp_color = pack_color(colors[last_index], premulColors, componentScale); - const SkScalar clamp_pos = reverse ? SK_ScalarNegativeInfinity : SK_ScalarInfinity; - fIntervals.emplace_back(clamp_color, last_pos, - clamp_color, clamp_pos); - } else if (tileMode == SkShader::kMirror_TileMode && !reverse) { - // synthetic mirror intervals injected after main intervals: [1 .. 2) - addMirrorIntervals(colors, pos, count, componentScale, premulColors, true, &fIntervals); - } -} - -const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::find(SkScalar t) const { - // Binary search. - const auto* i0 = fIntervals.begin(); - const auto* i1 = fIntervals.end() - 1; - - while (i0 != i1) { - SkASSERT(i0 < i1); - SkASSERT(t >= i0->fT0 && t <= i1->fT1); - - const auto* i = i0 + ((i1 - i0) >> 1); - - if (t > i->fT1) { - i0 = i + 1; - } else { - i1 = i; - } - } - - SkASSERT(i0->contains(t)); - return i0; -} - -const Sk4fGradientInterval* Sk4fGradientIntervalBuffer::findNext( - SkScalar t, const Sk4fGradientInterval* prev, bool increasing) const { - - SkASSERT(!prev->contains(t)); - SkASSERT(prev >= fIntervals.begin() && prev < fIntervals.end()); - SkASSERT(t >= fIntervals.front().fT0 && t <= fIntervals.back().fT1); - - const auto* i = prev; - - // Use the |increasing| signal to figure which direction we should search for - // the next interval, then perform a linear search. - if (increasing) { - do { - i += 1; - if (i >= fIntervals.end()) { - i = fIntervals.begin(); - } - } while (!i->contains(t)); - } else { - do { - i -= 1; - if (i < fIntervals.begin()) { - i = fIntervals.end() - 1; - } - } while (!i->contains(t)); - } - - return i; -} - -SkGradientShaderBase:: -GradientShaderBase4fContext::GradientShaderBase4fContext(const SkGradientShaderBase& shader, - const ContextRec& rec) - : INHERITED(shader, rec) - , fFlags(this->INHERITED::getFlags()) -#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING - , fDither(true) -#else - , fDither(rec.fPaint->isDither()) -#endif -{ - const SkMatrix& inverse = this->getTotalInverse(); - fDstToPos.setConcat(shader.fPtsToUnit, inverse); - fDstToPosProc = fDstToPos.getMapXYProc(); - fDstToPosClass = static_cast<uint8_t>(INHERITED::ComputeMatrixClass(fDstToPos)); - - if (shader.fColorsAreOpaque && this->getPaintAlpha() == SK_AlphaOPAQUE) { - fFlags |= kOpaqueAlpha_Flag; - } - - fColorsArePremul = - (shader.fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) - || shader.fColorsAreOpaque; -} - -bool SkGradientShaderBase:: -GradientShaderBase4fContext::isValid() const { - return fDstToPos.isFinite(); -} - -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { - if (fColorsArePremul) { - this->shadePremulSpan<DstType::L32, ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::L32, ApplyPremul::True>(x, y, dst, count); - } -} - -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { - if (fColorsArePremul) { - this->shadePremulSpan<DstType::F32, ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::F32, ApplyPremul::True>(x, y, dst, count); - } -} - -template<DstType dstType, ApplyPremul premul> -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadePremulSpan(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - const SkGradientShaderBase& shader = - static_cast<const SkGradientShaderBase&>(fShader); - - switch (shader.fTileMode) { - case kClamp_TileMode: - this->shadeSpanInternal<dstType, - premul, - kClamp_TileMode>(x, y, dst, count); - break; - case kRepeat_TileMode: - this->shadeSpanInternal<dstType, - premul, - kRepeat_TileMode>(x, y, dst, count); - break; - case kMirror_TileMode: - this->shadeSpanInternal<dstType, - premul, - kMirror_TileMode>(x, y, dst, count); - break; - } -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -void SkGradientShaderBase:: -GradientShaderBase4fContext::shadeSpanInternal(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - static const int kBufSize = 128; - SkScalar ts[kBufSize]; - TSampler<dstType, premul, tileMode> sampler(*this); - - SkASSERT(count > 0); - do { - const int n = SkTMin(kBufSize, count); - this->mapTs(x, y, ts, n); - for (int i = 0; i < n; ++i) { - const Sk4f c = sampler.sample(ts[i]); - DstTraits<dstType, premul>::store(c, dst++); - } - x += n; - count -= n; - } while (count > 0); -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -class SkGradientShaderBase::GradientShaderBase4fContext::TSampler { -public: - TSampler(const GradientShaderBase4fContext& ctx) - : fCtx(ctx) - , fInterval(nullptr) { - switch (tileMode) { - case kClamp_TileMode: - fLargestIntervalValue = SK_ScalarInfinity; - break; - case kRepeat_TileMode: - fLargestIntervalValue = nextafterf(1, 0); - break; - case kMirror_TileMode: - fLargestIntervalValue = nextafterf(2.0f, 0); - break; - } - } - - Sk4f sample(SkScalar t) { - const auto tiled_t = tileProc(t); - - if (!fInterval) { - // Very first sample => locate the initial interval. - // TODO: maybe do this in ctor to remove a branch? - fInterval = fCtx.fIntervals.find(tiled_t); - this->loadIntervalData(fInterval); - } else if (!fInterval->contains(tiled_t)) { - fInterval = fCtx.fIntervals.findNext(tiled_t, fInterval, t >= fPrevT); - this->loadIntervalData(fInterval); - } - - fPrevT = t; - return lerp(tiled_t); - } - -private: - SkScalar tileProc(SkScalar t) const { - switch (tileMode) { - case kClamp_TileMode: - // synthetic clamp-mode edge intervals allow for a free-floating t: - // [-inf..0)[0..1)[1..+inf) - return t; - case kRepeat_TileMode: - // t % 1 (intervals range: [0..1)) - // Due to the extra arithmetic, we must clamp to ensure the value remains less than 1. - return SkTMin(t - SkScalarFloorToScalar(t), fLargestIntervalValue); - case kMirror_TileMode: - // t % 2 (synthetic mirror intervals expand the range to [0..2) - // Due to the extra arithmetic, we must clamp to ensure the value remains less than 2. - return SkTMin(t - SkScalarFloorToScalar(t / 2) * 2, fLargestIntervalValue); - } - - SK_ABORT("Unhandled tile mode."); - return 0; - } - - Sk4f lerp(SkScalar t) { - SkASSERT(fInterval->contains(t)); - return fCb + fCg * t; - } - - void loadIntervalData(const Sk4fGradientInterval* i) { - fCb = DstTraits<dstType, premul>::load(i->fCb); - fCg = DstTraits<dstType, premul>::load(i->fCg); - } - - const GradientShaderBase4fContext& fCtx; - const Sk4fGradientInterval* fInterval; - SkScalar fPrevT; - SkScalar fLargestIntervalValue; - Sk4f fCb; - Sk4f fCg; -}; diff --git a/src/shaders/gradients/Sk4fGradientBase.h b/src/shaders/gradients/Sk4fGradientBase.h deleted file mode 100644 index a660d6bde5..0000000000 --- a/src/shaders/gradients/Sk4fGradientBase.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef Sk4fGradientBase_DEFINED -#define Sk4fGradientBase_DEFINED - -#include "Sk4fGradientPriv.h" -#include "SkColor.h" -#include "SkGradientShaderPriv.h" -#include "SkMatrix.h" -#include "SkNx.h" -#include "SkPM4f.h" -#include "SkShaderBase.h" -#include "SkTArray.h" - -struct Sk4fGradientInterval { - Sk4fGradientInterval(const Sk4f& c0, SkScalar t0, - const Sk4f& c1, SkScalar t1); - - bool contains(SkScalar t) const { - // True if t is in [p0,p1]. Note: this helper assumes a - // natural/increasing interval - so it's not usable in Sk4fLinearGradient. - SkASSERT(fT0 < fT1); - return t >= fT0 && t <= fT1; - } - - // Color bias and color gradient, such that for a t in this interval - // - // C = fCb + t * fCg; - SkPM4f fCb, fCg; - SkScalar fT0, fT1; -}; - -class Sk4fGradientIntervalBuffer { -public: - void init(const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode tileMode, bool premulColors, SkScalar alpha, bool reverse); - - const Sk4fGradientInterval* find(SkScalar t) const; - const Sk4fGradientInterval* findNext(SkScalar t, const Sk4fGradientInterval* prev, - bool increasing) const; - - using BufferType = SkSTArray<8, Sk4fGradientInterval, true>; - - const BufferType* operator->() const { return &fIntervals; } - -private: - BufferType fIntervals; -}; - -class SkGradientShaderBase:: -GradientShaderBase4fContext : public Context { -public: - GradientShaderBase4fContext(const SkGradientShaderBase&, - const ContextRec&); - - uint32_t getFlags() const override { return fFlags; } - - void shadeSpan(int x, int y, SkPMColor dst[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override; - - bool isValid() const; - -protected: - virtual void mapTs(int x, int y, SkScalar ts[], int count) const = 0; - - Sk4fGradientIntervalBuffer fIntervals; - SkMatrix fDstToPos; - SkMatrix::MapXYProc fDstToPosProc; - uint8_t fDstToPosClass; - uint8_t fFlags; - bool fDither; - bool fColorsArePremul; - -private: - using INHERITED = Context; - - void addMirrorIntervals(const SkGradientShaderBase&, - const Sk4f& componentScale, bool reverse); - - template<DstType, ApplyPremul, SkShader::TileMode tileMode> - class TSampler; - - template <DstType dstType, ApplyPremul premul> - void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; - - template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> - void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; -}; - -#endif // Sk4fGradientBase_DEFINED diff --git a/src/shaders/gradients/Sk4fGradientPriv.h b/src/shaders/gradients/Sk4fGradientPriv.h deleted file mode 100644 index b8f4bcaee1..0000000000 --- a/src/shaders/gradients/Sk4fGradientPriv.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef Sk4fGradientPriv_DEFINED -#define Sk4fGradientPriv_DEFINED - -#include "SkColor.h" -#include "SkHalf.h" -#include "SkImageInfo.h" -#include "SkNx.h" -#include "SkPM4f.h" -#include "SkPM4fPriv.h" -#include "SkUtils.h" - -// Templates shared by various 4f gradient flavors. - -namespace { - -enum class ApplyPremul { True, False }; - -enum class DstType { - L32, // Linear 32bit. Used for both shader/blitter paths. - S32, // SRGB 32bit. Used for the blitter path only. - F16, // Linear half-float. Used for blitters only. - F32, // Linear float. Used for shaders only. -}; - -template <ApplyPremul> -struct PremulTraits; - -template <> -struct PremulTraits<ApplyPremul::False> { - static Sk4f apply(const Sk4f& c) { return c; } -}; - -template <> -struct PremulTraits<ApplyPremul::True> { - static Sk4f apply(const Sk4f& c) { - const float alpha = c[SkPM4f::A]; - // FIXME: portable swizzle? - return c * Sk4f(alpha, alpha, alpha, 1); - } -}; - -// Struct encapsulating various dest-dependent ops: -// -// - load() Load a SkPM4f value into Sk4f. Normally called once per interval -// advance. Also applies a scale and swizzle suitable for DstType. -// -// - store() Store one Sk4f to dest. Optionally handles premul, color space -// conversion, etc. -// -// - store(count) Store the Sk4f value repeatedly to dest, count times. -// -// - store4x() Store 4 Sk4f values to dest (opportunistic optimization). -// -template <DstType, ApplyPremul premul> -struct DstTraits; - -template <ApplyPremul premul> -struct DstTraits<DstType::L32, premul> { - using PM = PremulTraits<premul>; - using Type = SkPMColor; - - // For L32, prescaling by 255 saves a per-pixel multiplication when premul is not needed. - static Sk4f load(const SkPM4f& c) { - return premul == ApplyPremul::False - ? c.to4f_pmorder() * Sk4f(255) - : c.to4f_pmorder(); - } - - static void store(const Sk4f& c, Type* dst) { - if (premul == ApplyPremul::False) { - // c is prescaled by 255, just store. - SkNx_cast<uint8_t>(c).store(dst); - } else { - *dst = Sk4f_toL32(PM::apply(c)); - } - } - - static void store(const Sk4f& c, Type* dst, int n) { - Type pmc; - store(c, &pmc); - sk_memset32(dst, pmc, n); - } - - static void store4x(const Sk4f& c0, const Sk4f& c1, - const Sk4f& c2, const Sk4f& c3, - Type* dst) { - if (premul == ApplyPremul::False) { - Sk4f_ToBytes((uint8_t*)dst, c0, c1, c2, c3); - } else { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); - } - } -}; - -template <ApplyPremul premul> -struct DstTraits<DstType::S32, premul> { - using PM = PremulTraits<premul>; - using Type = SkPMColor; - - static Sk4f load(const SkPM4f& c) { - return c.to4f_pmorder(); - } - - static void store(const Sk4f& c, Type* dst) { - // FIXME: this assumes opaque colors. Handle unpremultiplication. - *dst = Sk4f_toS32(PM::apply(c)); - } - - static void store(const Sk4f& c, Type* dst, int n) { - sk_memset32(dst, Sk4f_toS32(PM::apply(c)), n); - } - - static void store4x(const Sk4f& c0, const Sk4f& c1, - const Sk4f& c2, const Sk4f& c3, - Type* dst) { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); - } -}; - -template <ApplyPremul premul> -struct DstTraits<DstType::F16, premul> { - using PM = PremulTraits<premul>; - using Type = uint64_t; - - static Sk4f load(const SkPM4f& c) { - return c.to4f(); - } - - static void store(const Sk4f& c, Type* dst) { - SkFloatToHalf_finite_ftz(PM::apply(c)).store(dst); - } - - static void store(const Sk4f& c, Type* dst, int n) { - uint64_t color; - SkFloatToHalf_finite_ftz(PM::apply(c)).store(&color); - sk_memset64(dst, color, n); - } - - static void store4x(const Sk4f& c0, const Sk4f& c1, - const Sk4f& c2, const Sk4f& c3, - Type* dst) { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); - } -}; - -template <ApplyPremul premul> -struct DstTraits<DstType::F32, premul> { - using PM = PremulTraits<premul>; - using Type = SkPM4f; - - static Sk4f load(const SkPM4f& c) { - return c.to4f(); - } - - static void store(const Sk4f& c, Type* dst) { - PM::apply(c).store(dst->fVec); - } - - static void store(const Sk4f& c, Type* dst, int n) { - const Sk4f pmc = PM::apply(c); - for (int i = 0; i < n; ++i) { - pmc.store(dst[i].fVec); - } - } - - static void store4x(const Sk4f& c0, const Sk4f& c1, - const Sk4f& c2, const Sk4f& c3, - Type* dst) { - store(c0, dst + 0); - store(c1, dst + 1); - store(c2, dst + 2); - store(c3, dst + 3); - } -}; - -} // anonymous namespace - -#endif // Sk4fGradientPriv_DEFINED diff --git a/src/shaders/gradients/Sk4fLinearGradient.cpp b/src/shaders/gradients/Sk4fLinearGradient.cpp deleted file mode 100644 index 49b98fd86e..0000000000 --- a/src/shaders/gradients/Sk4fLinearGradient.cpp +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk4fLinearGradient.h" -#include "Sk4x4f.h" - -#include <cmath> - -namespace { - -template<DstType dstType, ApplyPremul premul> -void ramp(const Sk4f& c, const Sk4f& dc, typename DstTraits<dstType, premul>::Type dst[], int n) { - SkASSERT(n > 0); - - const Sk4f dc2 = dc + dc; - const Sk4f dc4 = dc2 + dc2; - - Sk4f c0 = c ; - Sk4f c1 = c + dc; - Sk4f c2 = c0 + dc2; - Sk4f c3 = c1 + dc2; - - while (n >= 4) { - DstTraits<dstType, premul>::store4x(c0, c1, c2, c3, dst); - dst += 4; - - c0 = c0 + dc4; - c1 = c1 + dc4; - c2 = c2 + dc4; - c3 = c3 + dc4; - n -= 4; - } - if (n & 2) { - DstTraits<dstType, premul>::store(c0, dst++); - DstTraits<dstType, premul>::store(c1, dst++); - c0 = c0 + dc2; - } - if (n & 1) { - DstTraits<dstType, premul>::store(c0, dst); - } -} - -// Planar version of ramp (S32 no-premul only). -template<> -void ramp<DstType::S32, ApplyPremul::False>(const Sk4f& c, const Sk4f& dc, SkPMColor dst[], int n) { - SkASSERT(n > 0); - - const Sk4f dc4 = dc * 4; - const Sk4x4f dc4x = { Sk4f(dc4[0]), Sk4f(dc4[1]), Sk4f(dc4[2]), Sk4f(dc4[3]) }; - Sk4x4f c4x = Sk4x4f::Transpose(c, c + dc, c + dc * 2, c + dc * 3); - - while (n >= 4) { - ( sk_linear_to_srgb(c4x.r) << 0 - | sk_linear_to_srgb(c4x.g) << 8 - | sk_linear_to_srgb(c4x.b) << 16 - | Sk4f_round(255.0f*c4x.a) << 24).store(dst); - - c4x.r += dc4x.r; - c4x.g += dc4x.g; - c4x.b += dc4x.b; - c4x.a += dc4x.a; - - dst += 4; - n -= 4; - } - - if (n & 2) { - DstTraits<DstType::S32, ApplyPremul::False> - ::store(Sk4f(c4x.r[0], c4x.g[0], c4x.b[0], c4x.a[0]), dst++); - DstTraits<DstType::S32, ApplyPremul::False> - ::store(Sk4f(c4x.r[1], c4x.g[1], c4x.b[1], c4x.a[1]), dst++); - } - - if (n & 1) { - DstTraits<DstType::S32, ApplyPremul::False> - ::store(Sk4f(c4x.r[n & 2], c4x.g[n & 2], c4x.b[n & 2], c4x.a[n & 2]), dst); - } -} - -template<SkShader::TileMode> -SkScalar pinFx(SkScalar); - -template<> -SkScalar pinFx<SkShader::kClamp_TileMode>(SkScalar fx) { - return fx; -} - -template<> -SkScalar pinFx<SkShader::kRepeat_TileMode>(SkScalar fx) { - SkScalar f = SkScalarFraction(fx); - if (f < 0) { - f = SkTMin(f + 1, nextafterf(1, 0)); - } - SkASSERT(f >= 0); - SkASSERT(f < 1.0f); - return f; -} - -template<> -SkScalar pinFx<SkShader::kMirror_TileMode>(SkScalar fx) { - SkScalar f = SkScalarMod(fx, 2.0f); - if (f < 0) { - f = SkTMin(f + 2, nextafterf(2, 0)); - } - SkASSERT(f >= 0); - SkASSERT(f < 2.0f); - return f; -} - -// true when x is in [k1,k2], or [k2, k1] when the interval is reversed. -// TODO(fmalita): hoist the reversed interval check out of this helper. -bool in_range(SkScalar x, SkScalar k1, SkScalar k2) { - SkASSERT(k1 != k2); - return (k1 < k2) - ? (x >= k1 && x <= k2) - : (x >= k2 && x <= k1); -} - -} // anonymous namespace - -SkLinearGradient:: -LinearGradient4fContext::LinearGradient4fContext(const SkLinearGradient& shader, - const ContextRec& rec) - : INHERITED(shader, rec) { - - // Our fast path expects interval points to be monotonically increasing in x. - const bool reverseIntervals = this->isFast() && std::signbit(fDstToPos.getScaleX()); - fIntervals.init(shader.fOrigColors, shader.fOrigPos, shader.fColorCount, shader.fTileMode, - fColorsArePremul, rec.fPaint->getAlpha() * (1.0f / 255), reverseIntervals); - - SkASSERT(fIntervals->count() > 0); - fCachedInterval = fIntervals->begin(); -} - -const Sk4fGradientInterval* -SkLinearGradient::LinearGradient4fContext::findInterval(SkScalar fx) const { - SkASSERT(in_range(fx, fIntervals->front().fT0, fIntervals->back().fT1)); - - if (1) { - // Linear search, using the last scanline interval as a starting point. - SkASSERT(fCachedInterval >= fIntervals->begin()); - SkASSERT(fCachedInterval < fIntervals->end()); - const int search_dir = fDstToPos.getScaleX() >= 0 ? 1 : -1; - while (!in_range(fx, fCachedInterval->fT0, fCachedInterval->fT1)) { - fCachedInterval += search_dir; - if (fCachedInterval >= fIntervals->end()) { - fCachedInterval = fIntervals->begin(); - } else if (fCachedInterval < fIntervals->begin()) { - fCachedInterval = fIntervals->end() - 1; - } - } - return fCachedInterval; - } else { - // Binary search. Seems less effective than linear + caching. - const auto* i0 = fIntervals->begin(); - const auto* i1 = fIntervals->end() - 1; - - while (i0 != i1) { - SkASSERT(i0 < i1); - SkASSERT(in_range(fx, i0->fT0, i1->fT1)); - - const auto* i = i0 + ((i1 - i0) >> 1); - - if (in_range(fx, i0->fT0, i->fT1)) { - i1 = i; - } else { - SkASSERT(in_range(fx, i->fT1, i1->fT1)); - i0 = i + 1; - } - } - - SkASSERT(in_range(fx, i0->fT0, i0->fT1)); - return i0; - } -} - -void SkLinearGradient:: -LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { - if (!this->isFast()) { - this->INHERITED::shadeSpan(x, y, dst, count); - return; - } - - // TODO: plumb dithering - SkASSERT(count > 0); - if (fColorsArePremul) { - this->shadePremulSpan<DstType::L32, - ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::L32, - ApplyPremul::True>(x, y, dst, count); - } -} - -void SkLinearGradient:: -LinearGradient4fContext::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { - if (!this->isFast()) { - this->INHERITED::shadeSpan4f(x, y, dst, count); - return; - } - - // TONOTDO: plumb dithering - SkASSERT(count > 0); - if (fColorsArePremul) { - this->shadePremulSpan<DstType::F32, - ApplyPremul::False>(x, y, dst, count); - } else { - this->shadePremulSpan<DstType::F32, - ApplyPremul::True>(x, y, dst, count); - } -} - -template<DstType dstType, ApplyPremul premul> -void SkLinearGradient:: -LinearGradient4fContext::shadePremulSpan(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - const SkLinearGradient& shader = - static_cast<const SkLinearGradient&>(fShader); - switch (shader.fTileMode) { - case kClamp_TileMode: - this->shadeSpanInternal<dstType, - premul, - kClamp_TileMode>(x, y, dst, count); - break; - case kRepeat_TileMode: - this->shadeSpanInternal<dstType, - premul, - kRepeat_TileMode>(x, y, dst, count); - break; - case kMirror_TileMode: - this->shadeSpanInternal<dstType, - premul, - kMirror_TileMode>(x, y, dst, count); - break; - } -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -void SkLinearGradient:: -LinearGradient4fContext::shadeSpanInternal(int x, int y, - typename DstTraits<dstType, premul>::Type dst[], - int count) const { - SkPoint pt; - fDstToPosProc(fDstToPos, - x + SK_ScalarHalf, - y + SK_ScalarHalf, - &pt); - const SkScalar fx = pinFx<tileMode>(pt.x()); - const SkScalar dx = fDstToPos.getScaleX(); - LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(), - fIntervals->end() - 1, - this->findInterval(fx), - fx, - dx, - SkScalarNearlyZero(dx * count)); - while (count > 0) { - // What we really want here is SkTPin(advance, 1, count) - // but that's a significant perf hit for >> stops; investigate. - const int n = SkScalarTruncToInt( - SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count))); - - // The current interval advance can be +inf (e.g. when reaching - // the clamp mode end intervals) - when that happens, we expect to - // a) consume all remaining count in one swoop - // b) return a zero color gradient - SkASSERT(SkScalarIsFinite(proc.currentAdvance()) - || (n == count && proc.currentRampIsZero())); - - if (proc.currentRampIsZero()) { - DstTraits<dstType, premul>::store(proc.currentColor(), - dst, n); - } else { - ramp<dstType, premul>(proc.currentColor(), - proc.currentColorGrad(), - dst, n); - } - - proc.advance(SkIntToScalar(n)); - count -= n; - dst += n; - } -} - -template<DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> -class SkLinearGradient:: -LinearGradient4fContext::LinearIntervalProcessor { -public: - LinearIntervalProcessor(const Sk4fGradientInterval* firstInterval, - const Sk4fGradientInterval* lastInterval, - const Sk4fGradientInterval* i, - SkScalar fx, - SkScalar dx, - bool is_vertical) - : fAdvX(is_vertical ? SK_ScalarInfinity : (i->fT1 - fx) / dx) - , fFirstInterval(firstInterval) - , fLastInterval(lastInterval) - , fInterval(i) - , fDx(dx) - , fIsVertical(is_vertical) - { - SkASSERT(fAdvX >= 0); - SkASSERT(firstInterval <= lastInterval); - - if (tileMode != kClamp_TileMode && !is_vertical) { - const auto spanX = (lastInterval->fT1 - firstInterval->fT0) / dx; - SkASSERT(spanX >= 0); - - // If we're in a repeating tile mode and the whole gradient is compressed into a - // fraction of a pixel, we just use the average color in zero-ramp mode. - // This also avoids cases where we make no progress due to interval advances being - // close to zero. - static constexpr SkScalar kMinSpanX = .25f; - if (spanX < kMinSpanX) { - this->init_average_props(); - return; - } - } - - this->compute_interval_props(fx); - } - - SkScalar currentAdvance() const { - SkASSERT(fAdvX >= 0); - SkASSERT(fAdvX <= (fInterval->fT1 - fInterval->fT0) / fDx || !std::isfinite(fAdvX)); - return fAdvX; - } - - bool currentRampIsZero() const { return fZeroRamp; } - const Sk4f& currentColor() const { return fCc; } - const Sk4f& currentColorGrad() const { return fDcDx; } - - void advance(SkScalar advX) { - SkASSERT(advX > 0); - SkASSERT(fAdvX >= 0); - - if (advX >= fAdvX) { - advX = this->advance_interval(advX); - } - SkASSERT(advX < fAdvX); - - fCc = fCc + fDcDx * Sk4f(advX); - fAdvX -= advX; - } - -private: - void compute_interval_props(SkScalar t) { - SkASSERT(in_range(t, fInterval->fT0, fInterval->fT1)); - - const Sk4f dc = DstTraits<dstType, premul>::load(fInterval->fCg); - fCc = DstTraits<dstType, premul>::load(fInterval->fCb) + dc * Sk4f(t); - fDcDx = dc * fDx; - fZeroRamp = fIsVertical || (dc == 0).allTrue(); - } - - void init_average_props() { - fAdvX = SK_ScalarInfinity; - fZeroRamp = true; - fDcDx = 0; - fCc = Sk4f(0); - - // TODO: precompute the average at interval setup time? - for (const auto* i = fFirstInterval; i <= fLastInterval; ++i) { - // Each interval contributes its average color to the total/weighted average: - // - // C = (c0 + c1) / 2 = (Cb + Cg * t0 + Cb + Cg * t1) / 2 = Cb + Cg *(t0 + t1) / 2 - // - // Avg += C * (t1 - t0) - // - const auto c = DstTraits<dstType, premul>::load(i->fCb) - + DstTraits<dstType, premul>::load(i->fCg) * (i->fT0 + i->fT1) * 0.5f; - fCc = fCc + c * (i->fT1 - i->fT0); - } - } - - const Sk4fGradientInterval* next_interval(const Sk4fGradientInterval* i) const { - SkASSERT(i >= fFirstInterval); - SkASSERT(i <= fLastInterval); - i++; - - if (tileMode == kClamp_TileMode) { - SkASSERT(i <= fLastInterval); - return i; - } - - return (i <= fLastInterval) ? i : fFirstInterval; - } - - SkScalar advance_interval(SkScalar advX) { - SkASSERT(advX >= fAdvX); - - do { - advX -= fAdvX; - fInterval = this->next_interval(fInterval); - fAdvX = (fInterval->fT1 - fInterval->fT0) / fDx; - SkASSERT(fAdvX > 0); - } while (advX >= fAdvX); - - compute_interval_props(fInterval->fT0); - - SkASSERT(advX >= 0); - return advX; - } - - // Current interval properties. - Sk4f fDcDx; // dst color gradient (dc/dx) - Sk4f fCc; // current color, interpolated in dst - SkScalar fAdvX; // remaining interval advance in dst - bool fZeroRamp; // current interval color grad is 0 - - const Sk4fGradientInterval* fFirstInterval; - const Sk4fGradientInterval* fLastInterval; - const Sk4fGradientInterval* fInterval; // current interval - const SkScalar fDx; // 'dx' for consistency with other impls; actually dt/dx - const bool fIsVertical; -}; - -void SkLinearGradient:: -LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const { - SkASSERT(count > 0); - SkASSERT(fDstToPosClass != kLinear_MatrixClass); - - SkScalar sx = x + SK_ScalarHalf; - const SkScalar sy = y + SK_ScalarHalf; - SkPoint pt; - - if (fDstToPosClass != kPerspective_MatrixClass) { - // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline - const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x(); - fDstToPosProc(fDstToPos, sx, sy, &pt); - - const Sk4f dtdx4 = Sk4f(4 * dtdx); - Sk4f t4 = Sk4f(pt.x() + 0 * dtdx, - pt.x() + 1 * dtdx, - pt.x() + 2 * dtdx, - pt.x() + 3 * dtdx); - - while (count >= 4) { - t4.store(ts); - t4 = t4 + dtdx4; - ts += 4; - count -= 4; - } - - if (count & 2) { - *ts++ = t4[0]; - *ts++ = t4[1]; - t4 = SkNx_shuffle<2, 0, 1, 3>(t4); - } - - if (count & 1) { - *ts++ = t4[0]; - } - } else { - for (int i = 0; i < count; ++i) { - fDstToPosProc(fDstToPos, sx, sy, &pt); - // Perspective may yield NaN values. - // Short of a better idea, drop to 0. - ts[i] = SkScalarIsNaN(pt.x()) ? 0 : pt.x(); - sx += SK_Scalar1; - } - } -} - -bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info, - BlitState* state) { - if (state->fMode != SkBlendMode::kSrc && - !(state->fMode == SkBlendMode::kSrcOver && (fFlags & kOpaqueAlpha_Flag))) { - return false; - } - - switch (info.colorType()) { - case kN32_SkColorType: - state->fBlitBW = D32_BlitBW; - return true; - case kRGBA_F16_SkColorType: - state->fBlitBW = D64_BlitBW; - return true; - default: - return false; - } -} - -void SkLinearGradient:: -LinearGradient4fContext::D32_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst, - int count) { - // FIXME: ignoring coverage for now - const LinearGradient4fContext* ctx = - static_cast<const LinearGradient4fContext*>(state->fCtx); - - if (!dst.info().gammaCloseToSRGB()) { - if (ctx->fColorsArePremul) { - ctx->shadePremulSpan<DstType::L32, ApplyPremul::False>( - x, y, dst.writable_addr32(x, y), count); - } else { - ctx->shadePremulSpan<DstType::L32, ApplyPremul::True>( - x, y, dst.writable_addr32(x, y), count); - } - } else { - if (ctx->fColorsArePremul) { - ctx->shadePremulSpan<DstType::S32, ApplyPremul::False>( - x, y, dst.writable_addr32(x, y), count); - } else { - ctx->shadePremulSpan<DstType::S32, ApplyPremul::True>( - x, y, dst.writable_addr32(x, y), count); - } - } -} - -void SkLinearGradient:: -LinearGradient4fContext::D64_BlitBW(BlitState* state, int x, int y, const SkPixmap& dst, - int count) { - // FIXME: ignoring coverage for now - const LinearGradient4fContext* ctx = - static_cast<const LinearGradient4fContext*>(state->fCtx); - - if (ctx->fColorsArePremul) { - ctx->shadePremulSpan<DstType::F16, ApplyPremul::False>( - x, y, dst.writable_addr64(x, y), count); - } else { - ctx->shadePremulSpan<DstType::F16, ApplyPremul::True>( - x, y, dst.writable_addr64(x, y), count); - } -} diff --git a/src/shaders/gradients/Sk4fLinearGradient.h b/src/shaders/gradients/Sk4fLinearGradient.h deleted file mode 100644 index eebd30fbf5..0000000000 --- a/src/shaders/gradients/Sk4fLinearGradient.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef Sk4fLinearGradient_DEFINED -#define Sk4fLinearGradient_DEFINED - -#include "Sk4fGradientBase.h" -#include "SkLinearGradient.h" - -class SkLinearGradient:: -LinearGradient4fContext final : public GradientShaderBase4fContext { -public: - LinearGradient4fContext(const SkLinearGradient&, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dst[], int count) override; - void shadeSpan4f(int x, int y, SkPM4f dst[], int count) override; - -protected: - void mapTs(int x, int y, SkScalar ts[], int count) const override; - - bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override; - -private: - using INHERITED = GradientShaderBase4fContext; - - template<DstType, ApplyPremul, TileMode> - class LinearIntervalProcessor; - - template <DstType dstType, ApplyPremul premul> - void shadePremulSpan(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; - - template <DstType dstType, ApplyPremul premul, SkShader::TileMode tileMode> - void shadeSpanInternal(int x, int y, typename DstTraits<dstType, premul>::Type[], - int count) const; - - const Sk4fGradientInterval* findInterval(SkScalar fx) const; - - bool isFast() const { return fDstToPosClass == kLinear_MatrixClass; } - - static void D32_BlitBW(BlitState*, int x, int y, const SkPixmap& dst, int count); - static void D64_BlitBW(BlitState*, int x, int y, const SkPixmap& dst, int count); - - mutable const Sk4fGradientInterval* fCachedInterval; -}; - -#endif // Sk4fLinearGradient_DEFINED diff --git a/src/shaders/gradients/SkClampRange.cpp b/src/shaders/gradients/SkClampRange.cpp deleted file mode 100644 index efc93959d1..0000000000 --- a/src/shaders/gradients/SkClampRange.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkClampRange.h" -#include "SkMathPriv.h" - -static int SkCLZ64(uint64_t value) { - int count = 0; - if (value >> 32) { - value >>= 32; - } else { - count += 32; - } - return count + SkCLZ(SkToU32(value)); -} - -static bool sk_64_smul_check(int64_t count, int64_t dx, int64_t* result) { - // Do it the slow way until we have some assembly. - if (dx == std::numeric_limits<int64_t>::min()) { - return false; // SkTAbs overflow - } - - SkASSERT(count >= 0); - uint64_t ucount = static_cast<uint64_t>(count); - uint64_t udx = static_cast<uint64_t>(SkTAbs(dx)); - int zeros = SkCLZ64(ucount) + SkCLZ64(udx); - // this is a conservative check: it may return false when in fact it would not have overflowed. - // Hackers Delight uses 34 as its convervative check, but that is for 32x32 multiplies. - // Since we are looking at 64x64 muls, we add 32 to the check. - if (zeros < (32 + 34)) { - return false; - } - *result = count * dx; - return true; -} - -static bool sk_64_sadd_check(int64_t a, int64_t b, int64_t* result) { - if (a > 0) { - if (b > std::numeric_limits<int64_t>::max() - a) { - return false; - } - } else { - if (b < std::numeric_limits<int64_t>::min() - a) { - return false; - } - } - - *result = a + b; - return true; -} - - -/* - * returns [0..count] for the number of steps (<= count) for which x0 <= edge - * given each step is followed by x0 += dx - */ -static int chop(int64_t x0, SkGradFixed edge, int64_t x1, int64_t dx, int count) { - SkASSERT(dx > 0); - SkASSERT(count >= 0); - - if (x0 >= edge) { - return 0; - } - if (x1 <= edge) { - return count; - } - int64_t n = (edge - x0 + dx - 1) / dx; - SkASSERT(n >= 0); - SkASSERT(n <= count); - return (int)n; -} - -void SkClampRange::initFor1(SkGradFixed fx) { - fCount0 = fCount1 = fCount2 = 0; - if (fx <= 0) { - fCount0 = 1; - } else if (fx < kFracMax_SkGradFixed) { - fCount1 = 1; - fFx1 = fx; - } else { - fCount2 = 1; - } -} - -void SkClampRange::init(SkGradFixed fx0, SkGradFixed dx0, int count, int v0, int v1) { - SkASSERT(count > 0); - - fV0 = v0; - fV1 = v1; - - // special case 1 == count, as it is slightly common for skia - // and avoids us ever calling divide or 64bit multiply - if (1 == count) { - this->initFor1(fx0); - return; - } - - int64_t fx = fx0; - int64_t dx = dx0; - - // start with ex equal to the last computed value - int64_t count_times_dx, ex; - if (!sk_64_smul_check(count - 1, dx, &count_times_dx) || - !sk_64_sadd_check(fx, count_times_dx, &ex)) { - // we can't represent the computed end in 32.32, so just draw something (first color) - fCount1 = fCount2 = 0; - fCount0 = count; - return; - } - - if ((uint64_t)(fx | ex) <= kFracMax_SkGradFixed) { - fCount0 = fCount2 = 0; - fCount1 = count; - fFx1 = fx0; - return; - } - if (fx <= 0 && ex <= 0) { - fCount1 = fCount2 = 0; - fCount0 = count; - return; - } - if (fx >= kFracMax_SkGradFixed && ex >= kFracMax_SkGradFixed) { - fCount0 = fCount1 = 0; - fCount2 = count; - return; - } - - // now make ex be 1 past the last computed value - ex += dx; - - bool doSwap = dx < 0; - - if (doSwap) { - ex -= dx; - fx -= dx; - SkTSwap(fx, ex); - dx = -dx; - } - - - fCount0 = chop(fx, 0, ex, dx, count); - SkASSERT(fCount0 >= 0); - SkASSERT(fCount0 <= count); - count -= fCount0; - fx += fCount0 * dx; - SkASSERT(fx >= 0); - SkASSERT(fCount0 == 0 || (fx - dx) < 0); - fCount1 = chop(fx, kFracMax_SkGradFixed, ex, dx, count); - SkASSERT(fCount1 >= 0); - SkASSERT(fCount1 <= count); - count -= fCount1; - fCount2 = count; - -#ifdef SK_DEBUG - fx += fCount1 * dx; - SkASSERT(fx <= ex); - if (fCount2 > 0) { - SkASSERT(fx >= kFracMax_SkGradFixed); - if (fCount1 > 0) { - SkASSERT(fx - dx < kFracMax_SkGradFixed); - } - } -#endif - - if (doSwap) { - SkTSwap(fCount0, fCount2); - SkTSwap(fV0, fV1); - dx = -dx; - } - - if (fCount1 > 0) { - fFx1 = fx0 + fCount0 * dx; - } -} diff --git a/src/shaders/gradients/SkClampRange.h b/src/shaders/gradients/SkClampRange.h deleted file mode 100644 index 8a22e72d38..0000000000 --- a/src/shaders/gradients/SkClampRange.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkClampRange_DEFINED -#define SkClampRange_DEFINED - -#include "SkFixed.h" -#include "SkScalar.h" - -#define SkGradFixed SkFixed3232 - -// We want the largest 32.32 value representable as a float. (float)0x7FFFFFFF -// becomes too big, due to limited mantissa on the float and its rounding rules, so -// we have to manually compute the next smaller value (aka nextafter). - -// #define SkGradFixedMaxScalar nextafterf(SkFixed3232ToFloat(SkFixed3232Max), 0) -// #define SkGradFixedMinScalar nextafterf(SkFixed3232ToFloat(SkFixed3232Min), 0) -#define SkGradFixedMaxScalar ( 2147483520.0f) -#define SkGradFixedMinScalar (-2147483520.0f) -#define SkScalarPinToGradFixed(x) SkScalarToFixed3232(SkTPin(x, \ - SkGradFixedMinScalar,\ - SkGradFixedMaxScalar)) -#define SkFixedToGradFixed(x) SkFixedToFixed3232(x) -#define SkGradFixedToFixed(x) (SkFixed)((x) >> 16) -#define kFracMax_SkGradFixed 0xFFFFFFFFLL - -/** - * Iteration fixed fx by dx, clamping as you go to [0..kFracMax_SkGradFixed], this class - * computes the (up to) 3 spans there are: - * - * range0: use constant value V0 - * range1: iterate as usual fx += dx - * range2: use constant value V1 - */ -struct SkClampRange { - int fCount0; // count for fV0 - int fCount1; // count for interpolating (fV0...fV1) - int fCount2; // count for fV1 - SkGradFixed fFx1; // initial fx value for the fCount1 range. - // only valid if fCount1 > 0 - int fV0, fV1; - - void init(SkGradFixed fx, SkGradFixed dx, int count, int v0, int v1); - - void validate(int count) const { -#ifdef SK_DEBUG - SkASSERT(fCount0 >= 0); - SkASSERT(fCount1 >= 0); - SkASSERT(fCount2 >= 0); - SkASSERT(fCount0 + fCount1 + fCount2 == count); -#endif - } - -private: - void initFor1(SkGradFixed fx); -}; - -#endif diff --git a/src/shaders/gradients/SkGradientBitmapCache.cpp b/src/shaders/gradients/SkGradientBitmapCache.cpp deleted file mode 100644 index 06b2d8c3fe..0000000000 --- a/src/shaders/gradients/SkGradientBitmapCache.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkGradientBitmapCache.h" - -#include "SkMalloc.h" - -struct SkGradientBitmapCache::Entry { - Entry* fPrev; - Entry* fNext; - - void* fBuffer; - size_t fSize; - SkBitmap fBitmap; - - Entry(const void* buffer, size_t size, const SkBitmap& bm) - : fPrev(nullptr), - fNext(nullptr), - fBitmap(bm) { - fBuffer = sk_malloc_throw(size); - fSize = size; - memcpy(fBuffer, buffer, size); - } - - ~Entry() { sk_free(fBuffer); } - - bool equals(const void* buffer, size_t size) const { - return (fSize == size) && !memcmp(fBuffer, buffer, size); - } -}; - -SkGradientBitmapCache::SkGradientBitmapCache(int max) : fMaxEntries(max) { - fEntryCount = 0; - fHead = fTail = nullptr; - - this->validate(); -} - -SkGradientBitmapCache::~SkGradientBitmapCache() { - this->validate(); - - Entry* entry = fHead; - while (entry) { - Entry* next = entry->fNext; - delete entry; - entry = next; - } -} - -SkGradientBitmapCache::Entry* SkGradientBitmapCache::release(Entry* entry) const { - if (entry->fPrev) { - SkASSERT(fHead != entry); - entry->fPrev->fNext = entry->fNext; - } else { - SkASSERT(fHead == entry); - fHead = entry->fNext; - } - if (entry->fNext) { - SkASSERT(fTail != entry); - entry->fNext->fPrev = entry->fPrev; - } else { - SkASSERT(fTail == entry); - fTail = entry->fPrev; - } - return entry; -} - -void SkGradientBitmapCache::attachToHead(Entry* entry) const { - entry->fPrev = nullptr; - entry->fNext = fHead; - if (fHead) { - fHead->fPrev = entry; - } else { - fTail = entry; - } - fHead = entry; -} - -bool SkGradientBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const { - AutoValidate av(this); - - Entry* entry = fHead; - while (entry) { - if (entry->equals(buffer, size)) { - if (bm) { - *bm = entry->fBitmap; - } - // move to the head of our list, so we purge it last - this->release(entry); - this->attachToHead(entry); - return true; - } - entry = entry->fNext; - } - return false; -} - -void SkGradientBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) { - AutoValidate av(this); - - if (fEntryCount == fMaxEntries) { - SkASSERT(fTail); - delete this->release(fTail); - fEntryCount -= 1; - } - - Entry* entry = new Entry(buffer, len, bm); - this->attachToHead(entry); - fEntryCount += 1; -} - -/////////////////////////////////////////////////////////////////////////////// - -#ifdef SK_DEBUG - -void SkGradientBitmapCache::validate() const { - SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries); - - if (fEntryCount > 0) { - SkASSERT(nullptr == fHead->fPrev); - SkASSERT(nullptr == fTail->fNext); - - if (fEntryCount == 1) { - SkASSERT(fHead == fTail); - } else { - SkASSERT(fHead != fTail); - } - - Entry* entry = fHead; - int count = 0; - while (entry) { - count += 1; - entry = entry->fNext; - } - SkASSERT(count == fEntryCount); - - entry = fTail; - while (entry) { - count -= 1; - entry = entry->fPrev; - } - SkASSERT(0 == count); - } else { - SkASSERT(nullptr == fHead); - SkASSERT(nullptr == fTail); - } -} - -#endif diff --git a/src/shaders/gradients/SkGradientBitmapCache.h b/src/shaders/gradients/SkGradientBitmapCache.h deleted file mode 100644 index 0dcd32272e..0000000000 --- a/src/shaders/gradients/SkGradientBitmapCache.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkGradientBitmapCache_DEFINED -#define SkGradientBitmapCache_DEFINED - -#include "SkBitmap.h" - -class SkGradientBitmapCache : SkNoncopyable { -public: - SkGradientBitmapCache(int maxEntries); - ~SkGradientBitmapCache(); - - bool find(const void* buffer, size_t len, SkBitmap*) const; - void add(const void* buffer, size_t len, const SkBitmap&); - -private: - int fEntryCount; - const int fMaxEntries; - - struct Entry; - mutable Entry* fHead; - mutable Entry* fTail; - - inline Entry* release(Entry*) const; - inline void attachToHead(Entry*) const; - -#ifdef SK_DEBUG - void validate() const; -#else - void validate() const {} -#endif - - class AutoValidate : SkNoncopyable { - public: - AutoValidate(const SkGradientBitmapCache* bc) : fBC(bc) { bc->validate(); } - ~AutoValidate() { fBC->validate(); } - private: - const SkGradientBitmapCache* fBC; - }; -}; - -#endif diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp deleted file mode 100644 index 137da84d0c..0000000000 --- a/src/shaders/gradients/SkGradientShader.cpp +++ /dev/null @@ -1,2004 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <algorithm> -#include "Sk4fLinearGradient.h" -#include "SkColorSpace_XYZ.h" -#include "SkGradientShaderPriv.h" -#include "SkHalf.h" -#include "SkLinearGradient.h" -#include "SkMallocPixelRef.h" -#include "SkRadialGradient.h" -#include "SkSweepGradient.h" -#include "SkTwoPointConicalGradient.h" -#include "../../jumper/SkJumper.h" - - -enum GradientSerializationFlags { - // Bits 29:31 used for various boolean flags - kHasPosition_GSF = 0x80000000, - kHasLocalMatrix_GSF = 0x40000000, - kHasColorSpace_GSF = 0x20000000, - - // Bits 12:28 unused - - // Bits 8:11 for fTileMode - kTileModeShift_GSF = 8, - kTileModeMask_GSF = 0xF, - - // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80) - kGradFlagsShift_GSF = 0, - kGradFlagsMask_GSF = 0xFF, -}; - -void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const { - uint32_t flags = 0; - if (fPos) { - flags |= kHasPosition_GSF; - } - if (fLocalMatrix) { - flags |= kHasLocalMatrix_GSF; - } - sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr; - if (colorSpaceData) { - flags |= kHasColorSpace_GSF; - } - SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF); - flags |= (fTileMode << kTileModeShift_GSF); - SkASSERT(fGradFlags <= kGradFlagsMask_GSF); - flags |= (fGradFlags << kGradFlagsShift_GSF); - - buffer.writeUInt(flags); - - buffer.writeColor4fArray(fColors, fCount); - if (colorSpaceData) { - buffer.writeDataAsByteArray(colorSpaceData.get()); - } - if (fPos) { - buffer.writeScalarArray(fPos, fCount); - } - if (fLocalMatrix) { - buffer.writeMatrix(*fLocalMatrix); - } -} - -bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) { - if (buffer.isVersionLT(SkReadBuffer::kGradientShaderFloatColor_Version)) { - fCount = buffer.getArrayCount(); - if (fCount > kStorageCount) { - size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount; - fDynamicStorage.reset(allocSize); - fColors = (SkColor4f*)fDynamicStorage.get(); - fPos = (SkScalar*)(fColors + fCount); - } else { - fColors = fColorStorage; - fPos = fPosStorage; - } - - // Old gradients serialized SkColor. Read that to a temporary location, then convert. - SkSTArray<2, SkColor, true> colors; - colors.resize_back(fCount); - if (!buffer.readColorArray(colors.begin(), fCount)) { - return false; - } - for (int i = 0; i < fCount; ++i) { - mutableColors()[i] = SkColor4f::FromColor(colors[i]); - } - - if (buffer.readBool()) { - if (!buffer.readScalarArray(const_cast<SkScalar*>(fPos), fCount)) { - return false; - } - } else { - fPos = nullptr; - } - - fColorSpace = nullptr; - fTileMode = (SkShader::TileMode)buffer.read32(); - fGradFlags = buffer.read32(); - - if (buffer.readBool()) { - fLocalMatrix = &fLocalMatrixStorage; - buffer.readMatrix(&fLocalMatrixStorage); - } else { - fLocalMatrix = nullptr; - } - } else { - // New gradient format. Includes floating point color, color space, densely packed flags - uint32_t flags = buffer.readUInt(); - - fTileMode = (SkShader::TileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF); - fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF; - - fCount = buffer.getArrayCount(); - if (fCount > kStorageCount) { - size_t allocSize = (sizeof(SkColor4f) + sizeof(SkScalar)) * fCount; - fDynamicStorage.reset(allocSize); - fColors = (SkColor4f*)fDynamicStorage.get(); - fPos = (SkScalar*)(fColors + fCount); - } else { - fColors = fColorStorage; - fPos = fPosStorage; - } - if (!buffer.readColor4fArray(mutableColors(), fCount)) { - return false; - } - if (SkToBool(flags & kHasColorSpace_GSF)) { - sk_sp<SkData> data = buffer.readByteArrayAsData(); - fColorSpace = SkColorSpace::Deserialize(data->data(), data->size()); - } else { - fColorSpace = nullptr; - } - if (SkToBool(flags & kHasPosition_GSF)) { - if (!buffer.readScalarArray(mutablePos(), fCount)) { - return false; - } - } else { - fPos = nullptr; - } - if (SkToBool(flags & kHasLocalMatrix_GSF)) { - fLocalMatrix = &fLocalMatrixStorage; - buffer.readMatrix(&fLocalMatrixStorage); - } else { - fLocalMatrix = nullptr; - } - } - return buffer.isValid(); -} - -//////////////////////////////////////////////////////////////////////////////////////////// - -SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit) - : INHERITED(desc.fLocalMatrix) - , fPtsToUnit(ptsToUnit) -{ - fPtsToUnit.getType(); // Precache so reads are threadsafe. - SkASSERT(desc.fCount > 1); - - fGradFlags = static_cast<uint8_t>(desc.fGradFlags); - - SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount); - SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs)); - fTileMode = desc.fTileMode; - fTileProc = gTileProcs[desc.fTileMode]; - - /* Note: we let the caller skip the first and/or last position. - i.e. pos[0] = 0.3, pos[1] = 0.7 - In these cases, we insert dummy entries to ensure that the final data - will be bracketed by [0, 1]. - i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1 - - Thus colorCount (the caller's value, and fColorCount (our value) may - differ by up to 2. In the above example: - colorCount = 2 - fColorCount = 4 - */ - fColorCount = desc.fCount; - // check if we need to add in dummy start and/or end position/colors - bool dummyFirst = false; - bool dummyLast = false; - if (desc.fPos) { - dummyFirst = desc.fPos[0] != 0; - dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1; - fColorCount += dummyFirst + dummyLast; - } - - if (fColorCount > kColorStorageCount) { - size_t size = sizeof(SkColor) + sizeof(SkColor4f) + sizeof(Rec); - if (desc.fPos) { - size += sizeof(SkScalar); - } - fOrigColors = reinterpret_cast<SkColor*>(sk_malloc_throw(size * fColorCount)); - } - else { - fOrigColors = fStorage; - } - - fOrigColors4f = (SkColor4f*)(fOrigColors + fColorCount); - - // Now copy over the colors, adding the dummies as needed - SkColor4f* origColors = fOrigColors4f; - if (dummyFirst) { - *origColors++ = desc.fColors[0]; - } - memcpy(origColors, desc.fColors, desc.fCount * sizeof(SkColor4f)); - if (dummyLast) { - origColors += desc.fCount; - *origColors = desc.fColors[desc.fCount - 1]; - } - - // Convert our SkColor4f colors to SkColor as well. Note that this is incorrect if the - // source colors are not in sRGB gamut. We would need to do a gamut transformation, but - // SkColorSpaceXform can't do that (yet). GrColorSpaceXform can, but we may not have GPU - // support compiled in here. For the common case (sRGB colors), this does the right thing. - for (int i = 0; i < fColorCount; ++i) { - fOrigColors[i] = fOrigColors4f[i].toSkColor(); - } - - if (!desc.fColorSpace) { - // This happens if we were constructed from SkColors, so our colors are really sRGB - fColorSpace = SkColorSpace::MakeSRGBLinear(); - } else { - // The color space refers to the float colors, so it must be linear gamma - SkASSERT(desc.fColorSpace->gammaIsLinear()); - fColorSpace = desc.fColorSpace; - } - - if (desc.fPos && fColorCount) { - fOrigPos = (SkScalar*)(fOrigColors4f + fColorCount); - fRecs = (Rec*)(fOrigPos + fColorCount); - } else { - fOrigPos = nullptr; - fRecs = (Rec*)(fOrigColors4f + fColorCount); - } - - if (fColorCount > 2) { - Rec* recs = fRecs; - recs->fPos = 0; - // recs->fScale = 0; // unused; - recs += 1; - if (desc.fPos) { - SkScalar* origPosPtr = fOrigPos; - *origPosPtr++ = 0; - - /* We need to convert the user's array of relative positions into - fixed-point positions and scale factors. We need these results - to be strictly monotonic (no two values equal or out of order). - Hence this complex loop that just jams a zero for the scale - value if it sees a segment out of order, and it assures that - we start at 0 and end at 1.0 - */ - SkScalar prev = 0; - int startIndex = dummyFirst ? 0 : 1; - int count = desc.fCount + dummyLast; - for (int i = startIndex; i < count; i++) { - // force the last value to be 1.0 - SkScalar curr; - if (i == desc.fCount) { // we're really at the dummyLast - curr = 1; - } else { - curr = SkScalarPin(desc.fPos[i], 0, 1); - } - *origPosPtr++ = curr; - - recs->fPos = SkScalarToFixed(curr); - SkFixed diff = SkScalarToFixed(curr - prev); - if (diff > 0) { - recs->fScale = (1 << 24) / diff; - } else { - recs->fScale = 0; // ignore this segment - } - // get ready for the next value - prev = curr; - recs += 1; - } - } else { // assume even distribution - fOrigPos = nullptr; - - 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 - 1; i++) { - recs->fPos = p; - recs->fScale = scale; - recs += 1; - p += dp; - } - recs->fPos = SK_Fixed1; - recs->fScale = scale; - } - } else if (desc.fPos) { - SkASSERT(2 == fColorCount); - fOrigPos[0] = SkScalarPin(desc.fPos[0], 0, 1); - fOrigPos[1] = SkScalarPin(desc.fPos[1], fOrigPos[0], 1); - if (0 == fOrigPos[0] && 1 == fOrigPos[1]) { - fOrigPos = nullptr; - } - } - this->initCommon(); -} - -SkGradientShaderBase::~SkGradientShaderBase() { - if (fOrigColors != fStorage) { - sk_free(fOrigColors); - } -} - -void SkGradientShaderBase::initCommon() { - unsigned colorAlpha = 0xFF; - for (int i = 0; i < fColorCount; i++) { - colorAlpha &= SkColorGetA(fOrigColors[i]); - } - fColorsAreOpaque = colorAlpha == 0xFF; -} - -void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const { - Descriptor desc; - desc.fColors = fOrigColors4f; - desc.fColorSpace = fColorSpace; - desc.fPos = fOrigPos; - desc.fCount = fColorCount; - desc.fTileMode = fTileMode; - desc.fGradFlags = fGradFlags; - - const SkMatrix& m = this->getLocalMatrix(); - desc.fLocalMatrix = m.isIdentity() ? nullptr : &m; - desc.flatten(buffer); -} - -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)); -} - -static void add_stop_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f Fs, SkPM4f Bs) { - (ctx->fs[0])[stop] = Fs.r(); - (ctx->fs[1])[stop] = Fs.g(); - (ctx->fs[2])[stop] = Fs.b(); - (ctx->fs[3])[stop] = Fs.a(); - (ctx->bs[0])[stop] = Bs.r(); - (ctx->bs[1])[stop] = Bs.g(); - (ctx->bs[2])[stop] = Bs.b(); - (ctx->bs[3])[stop] = Bs.a(); -} - -static void add_const_color(SkJumper_GradientCtx* ctx, size_t stop, SkPM4f color) { - add_stop_color(ctx, stop, SkPM4f::FromPremulRGBA(0,0,0,0), color); -} - -// Calculate a factor F and a bias B so that color = F*t + B when t is in range of -// the stop. Assume that the distance between stops is 1/gapCount. -static void init_stop_evenly( - SkJumper_GradientCtx* ctx, float gapCount, size_t stop, SkPM4f c_l, SkPM4f c_r) { - // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar... - SkPM4f Fs = {{ - (c_r.r() - c_l.r()) * gapCount, - (c_r.g() - c_l.g()) * gapCount, - (c_r.b() - c_l.b()) * gapCount, - (c_r.a() - c_l.a()) * gapCount, - }}; - SkPM4f Bs = {{ - c_l.r() - Fs.r()*(stop/gapCount), - c_l.g() - Fs.g()*(stop/gapCount), - c_l.b() - Fs.b()*(stop/gapCount), - c_l.a() - Fs.a()*(stop/gapCount), - }}; - add_stop_color(ctx, stop, Fs, Bs); -} - -// For each stop we calculate a bias B and a scale factor F, such that -// for any t between stops n and n+1, the color we want is B[n] + F[n]*t. -static void init_stop_pos( - SkJumper_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPM4f c_l, SkPM4f c_r) { - // See note about Clankium's old compiler in init_stop_evenly(). - SkPM4f Fs = {{ - (c_r.r() - c_l.r()) / (t_r - t_l), - (c_r.g() - c_l.g()) / (t_r - t_l), - (c_r.b() - c_l.b()) / (t_r - t_l), - (c_r.a() - c_l.a()) / (t_r - t_l), - }}; - SkPM4f Bs = {{ - c_l.r() - Fs.r()*t_l, - c_l.g() - Fs.g()*t_l, - c_l.b() - Fs.b()*t_l, - c_l.a() - Fs.a()*t_l, - }}; - ctx->ts[stop] = t_l; - add_stop_color(ctx, stop, Fs, Bs); -} - -bool SkGradientShaderBase::onAppendStages(SkRasterPipeline* p, - SkColorSpace* dstCS, - SkArenaAlloc* alloc, - const SkMatrix& ctm, - const SkPaint& paint, - const SkMatrix* localM) const { - SkMatrix matrix; - if (!this->computeTotalInverse(ctm, localM, &matrix)) { - return false; - } - - SkRasterPipeline_<256> subclass; - if (!this->adjustMatrixAndAppendStages(alloc, &matrix, &subclass)) { - return false; - } - - auto* m = alloc->makeArrayDefault<float>(9); - if (matrix.asAffine(m)) { - p->append(SkRasterPipeline::matrix_2x3, m); - } else { - matrix.get9(m); - p->append(SkRasterPipeline::matrix_perspective, m); - } - - p->extend(subclass); - - switch(fTileMode) { - case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x_1); break; - case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x_1); break; - case kClamp_TileMode: - if (!fOrigPos) { - // We clamp only when the stops are evenly spaced. - // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1. - // In that case, we must make sure we're using the general "gradient" stage, - // which is the only stage that will correctly handle unclamped t. - p->append(SkRasterPipeline::clamp_x_1); - } - } - - const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag; - auto prepareColor = [premulGrad, dstCS, this](int i) { - SkColor4f c = dstCS ? to_colorspace(fOrigColors4f[i], fColorSpace.get(), dstCS) - : SkColor4f_from_SkColor(fOrigColors[i], nullptr); - return premulGrad ? c.premul() - : SkPM4f::From4f(Sk4f::Load(&c)); - }; - - // The two-stop case with stops at 0 and 1. - if (fColorCount == 2 && fOrigPos == nullptr) { - const SkPM4f c_l = prepareColor(0), - c_r = prepareColor(1); - - // See F and B below. - auto* f_and_b = alloc->makeArrayDefault<SkPM4f>(2); - f_and_b[0] = SkPM4f::From4f(c_r.to4f() - c_l.to4f()); - f_and_b[1] = c_l; - - p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, f_and_b); - } else { - auto* ctx = alloc->make<SkJumper_GradientCtx>(); - - // Note: In order to handle clamps in search, the search assumes a stop conceptully placed - // at -inf. Therefore, the max number of stops is fColorCount+1. - for (int i = 0; i < 4; i++) { - // Allocate at least at for the AVX2 gather from a YMM register. - ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); - ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); - } - - if (fOrigPos == nullptr) { - // Handle evenly distributed stops. - - size_t stopCount = fColorCount; - float gapCount = stopCount - 1; - - SkPM4f c_l = prepareColor(0); - for (size_t i = 0; i < stopCount - 1; i++) { - SkPM4f c_r = prepareColor(i + 1); - init_stop_evenly(ctx, gapCount, i, c_l, c_r); - c_l = c_r; - } - add_const_color(ctx, stopCount - 1, c_l); - - ctx->stopCount = stopCount; - p->append(SkRasterPipeline::evenly_spaced_gradient, ctx); - } else { - // Handle arbitrary stops. - - ctx->ts = alloc->makeArray<float>(fColorCount+1); - - // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase - // because they are naturally handled by the search method. - int firstStop; - int lastStop; - if (fColorCount > 2) { - firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1; - lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1] - ? fColorCount - 1 : fColorCount - 2; - } else { - firstStop = 0; - lastStop = 1; - } - - size_t stopCount = 0; - float t_l = fOrigPos[firstStop]; - SkPM4f c_l = prepareColor(firstStop); - add_const_color(ctx, stopCount++, c_l); - // N.B. lastStop is the index of the last stop, not one after. - for (int i = firstStop; i < lastStop; i++) { - float t_r = fOrigPos[i + 1]; - SkPM4f c_r = prepareColor(i + 1); - if (t_l < t_r) { - init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r); - stopCount += 1; - } - t_l = t_r; - c_l = c_r; - } - - ctx->ts[stopCount] = t_l; - add_const_color(ctx, stopCount++, c_l); - - ctx->stopCount = stopCount; - p->append(SkRasterPipeline::gradient, ctx); - } - } - - if (!premulGrad && !this->colorsAreOpaque()) { - p->append(SkRasterPipeline::premul); - } - - return true; -} - - -bool SkGradientShaderBase::isOpaque() const { - return fColorsAreOpaque; -} - -static unsigned rounded_divide(unsigned numer, unsigned denom) { - return (numer + (denom >> 1)) / denom; -} - -bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const { - // we just compute an average color. - // possibly we could weight this based on the proportional width for each color - // assuming they are not evenly distributed in the fPos array. - int r = 0; - int g = 0; - int b = 0; - const int n = fColorCount; - for (int i = 0; i < n; ++i) { - SkColor c = fOrigColors[i]; - r += SkColorGetR(c); - g += SkColorGetG(c); - b += SkColorGetB(c); - } - *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n)); - return true; -} - -SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext( - const SkGradientShaderBase& shader, const ContextRec& rec) - : INHERITED(shader, rec) -#ifdef SK_SUPPORT_LEGACY_GRADIENT_DITHERING - , fDither(true) -#else - , fDither(rec.fPaint->isDither()) -#endif - , fCache(shader.refCache(getPaintAlpha(), fDither)) -{ - const SkMatrix& inverse = this->getTotalInverse(); - - fDstToIndex.setConcat(shader.fPtsToUnit, inverse); - - fDstToIndexProc = fDstToIndex.getMapXYProc(); - fDstToIndexClass = (uint8_t)SkShaderBase::Context::ComputeMatrixClass(fDstToIndex); - - // now convert our colors in to PMColors - unsigned paintAlpha = this->getPaintAlpha(); - - fFlags = this->INHERITED::getFlags(); - if (shader.fColorsAreOpaque && paintAlpha == 0xFF) { - fFlags |= kOpaqueAlpha_Flag; - } -} - -bool SkGradientShaderBase::GradientShaderBaseContext::isValid() const { - return fDstToIndex.isFinite(); -} - -SkGradientShaderBase::GradientShaderCache::GradientShaderCache( - U8CPU alpha, bool dither, const SkGradientShaderBase& shader) - : fCacheAlpha(alpha) - , fCacheDither(dither) - , fShader(shader) -{ - // Only initialize the cache in getCache32. - fCache32 = nullptr; -} - -SkGradientShaderBase::GradientShaderCache::~GradientShaderCache() {} - -/* - * r,g,b used to be SkFixed, but on gcc (4.2.1 mac and 4.6.3 goobuntu) in - * release builds, we saw a compiler error where the 0xFF parameter in - * SkPackARGB32() was being totally ignored whenever it was called with - * a non-zero add (e.g. 0x8000). - * - * We found two work-arounds: - * 1. change r,g,b to unsigned (or just one of them) - * 2. change SkPackARGB32 to + its (a << SK_A32_SHIFT) value instead - * of using | - * - * We chose #1 just because it was more localized. - * See http://code.google.com/p/skia/issues/detail?id=1113 - * - * The type SkUFixed encapsulate this need for unsigned, but logically Fixed. - */ -typedef uint32_t SkUFixed; - -void SkGradientShaderBase::GradientShaderCache::Build32bitCache( - SkPMColor cache[], SkColor c0, SkColor c1, - int count, U8CPU paintAlpha, uint32_t gradFlags, bool dither) { - SkASSERT(count > 1); - - // need to apply paintAlpha to our two endpoints - uint32_t a0 = SkMulDiv255Round(SkColorGetA(c0), paintAlpha); - uint32_t a1 = SkMulDiv255Round(SkColorGetA(c1), paintAlpha); - - - const bool interpInPremul = SkToBool(gradFlags & - SkGradientShader::kInterpolateColorsInPremul_Flag); - - uint32_t r0 = SkColorGetR(c0); - uint32_t g0 = SkColorGetG(c0); - uint32_t b0 = SkColorGetB(c0); - - uint32_t r1 = SkColorGetR(c1); - uint32_t g1 = SkColorGetG(c1); - uint32_t b1 = SkColorGetB(c1); - - if (interpInPremul) { - r0 = SkMulDiv255Round(r0, a0); - g0 = SkMulDiv255Round(g0, a0); - b0 = SkMulDiv255Round(b0, a0); - - r1 = SkMulDiv255Round(r1, a1); - g1 = SkMulDiv255Round(g1, a1); - b1 = SkMulDiv255Round(b1, a1); - } - - SkFixed da = SkIntToFixed(a1 - a0) / (count - 1); - SkFixed dr = SkIntToFixed(r1 - r0) / (count - 1); - SkFixed dg = SkIntToFixed(g1 - g0) / (count - 1); - SkFixed db = SkIntToFixed(b1 - b0) / (count - 1); - - /* We pre-add 1/8 to avoid having to add this to our [0] value each time - in the loop. Without this, the bias for each would be - 0x2000 0xA000 0xE000 0x6000 - With this trick, we can add 0 for the first (no-op) and just adjust the - others. - */ - const SkUFixed bias0 = dither ? 0x2000 : 0x8000; - const SkUFixed bias1 = dither ? 0x8000 : 0; - const SkUFixed bias2 = dither ? 0xC000 : 0; - const SkUFixed bias3 = dither ? 0x4000 : 0; - - SkUFixed a = SkIntToFixed(a0) + bias0; - SkUFixed r = SkIntToFixed(r0) + bias0; - SkUFixed g = SkIntToFixed(g0) + bias0; - SkUFixed b = SkIntToFixed(b0) + bias0; - - /* - * Our dither-cell (spatially) is - * 0 2 - * 3 1 - * Where - * [0] -> [-1/8 ... 1/8 ) values near 0 - * [1] -> [ 1/8 ... 3/8 ) values near 1/4 - * [2] -> [ 3/8 ... 5/8 ) values near 1/2 - * [3] -> [ 5/8 ... 7/8 ) values near 3/4 - */ - - if (0xFF == a0 && 0 == da) { - do { - cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0 ) >> 16, - (g + 0 ) >> 16, - (b + 0 ) >> 16); - cache[kCache32Count*1] = SkPackARGB32(0xFF, (r + bias1) >> 16, - (g + bias1) >> 16, - (b + bias1) >> 16); - cache[kCache32Count*2] = SkPackARGB32(0xFF, (r + bias2) >> 16, - (g + bias2) >> 16, - (b + bias2) >> 16); - cache[kCache32Count*3] = SkPackARGB32(0xFF, (r + bias3) >> 16, - (g + bias3) >> 16, - (b + bias3) >> 16); - cache += 1; - r += dr; - g += dg; - b += db; - } while (--count != 0); - } else if (interpInPremul) { - do { - cache[kCache32Count*0] = SkPackARGB32((a + 0 ) >> 16, - (r + 0 ) >> 16, - (g + 0 ) >> 16, - (b + 0 ) >> 16); - cache[kCache32Count*1] = SkPackARGB32((a + bias1) >> 16, - (r + bias1) >> 16, - (g + bias1) >> 16, - (b + bias1) >> 16); - cache[kCache32Count*2] = SkPackARGB32((a + bias2) >> 16, - (r + bias2) >> 16, - (g + bias2) >> 16, - (b + bias2) >> 16); - cache[kCache32Count*3] = SkPackARGB32((a + bias3) >> 16, - (r + bias3) >> 16, - (g + bias3) >> 16, - (b + bias3) >> 16); - cache += 1; - a += da; - r += dr; - g += dg; - b += db; - } while (--count != 0); - } else { // interpolate in unpreml space - do { - cache[kCache32Count*0] = SkPremultiplyARGBInline((a + 0 ) >> 16, - (r + 0 ) >> 16, - (g + 0 ) >> 16, - (b + 0 ) >> 16); - cache[kCache32Count*1] = SkPremultiplyARGBInline((a + bias1) >> 16, - (r + bias1) >> 16, - (g + bias1) >> 16, - (b + bias1) >> 16); - cache[kCache32Count*2] = SkPremultiplyARGBInline((a + bias2) >> 16, - (r + bias2) >> 16, - (g + bias2) >> 16, - (b + bias2) >> 16); - cache[kCache32Count*3] = SkPremultiplyARGBInline((a + bias3) >> 16, - (r + bias3) >> 16, - (g + bias3) >> 16, - (b + bias3) >> 16); - cache += 1; - a += da; - r += dr; - g += dg; - b += db; - } while (--count != 0); - } -} - -static inline int SkFixedToFFFF(SkFixed x) { - SkASSERT((unsigned)x <= SK_Fixed1); - return x - (x >> 16); -} - -const SkPMColor* SkGradientShaderBase::GradientShaderCache::getCache32() { - fCache32InitOnce(SkGradientShaderBase::GradientShaderCache::initCache32, this); - SkASSERT(fCache32); - return fCache32; -} - -void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache* cache) { - const int kNumberOfDitherRows = 4; - const SkImageInfo info = SkImageInfo::MakeN32Premul(kCache32Count, kNumberOfDitherRows); - - SkASSERT(nullptr == cache->fCache32PixelRef); - cache->fCache32PixelRef = SkMallocPixelRef::MakeAllocate(info, 0, nullptr); - cache->fCache32 = (SkPMColor*)cache->fCache32PixelRef->pixels(); - if (cache->fShader.fColorCount == 2) { - Build32bitCache(cache->fCache32, cache->fShader.fOrigColors[0], - cache->fShader.fOrigColors[1], kCache32Count, cache->fCacheAlpha, - cache->fShader.fGradFlags, cache->fCacheDither); - } else { - Rec* rec = cache->fShader.fRecs; - int prevIndex = 0; - for (int i = 1; i < cache->fShader.fColorCount; i++) { - int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift; - SkASSERT(nextIndex < kCache32Count); - - if (nextIndex > prevIndex) - Build32bitCache(cache->fCache32 + prevIndex, cache->fShader.fOrigColors[i-1], - cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1, - cache->fCacheAlpha, cache->fShader.fGradFlags, cache->fCacheDither); - prevIndex = nextIndex; - } - } -} - -void SkGradientShaderBase::initLinearBitmap(SkBitmap* bitmap) const { - const bool interpInPremul = SkToBool(fGradFlags & - SkGradientShader::kInterpolateColorsInPremul_Flag); - SkHalf* pixelsF16 = reinterpret_cast<SkHalf*>(bitmap->getPixels()); - uint32_t* pixelsS32 = reinterpret_cast<uint32_t*>(bitmap->getPixels()); - - typedef std::function<void(const Sk4f&, int)> pixelWriteFn_t; - - pixelWriteFn_t writeF16Pixel = [&](const Sk4f& x, int index) { - Sk4h c = SkFloatToHalf_finite_ftz(x); - pixelsF16[4*index+0] = c[0]; - pixelsF16[4*index+1] = c[1]; - pixelsF16[4*index+2] = c[2]; - pixelsF16[4*index+3] = c[3]; - }; - pixelWriteFn_t writeS32Pixel = [&](const Sk4f& c, int index) { - pixelsS32[index] = Sk4f_toS32(c); - }; - - pixelWriteFn_t writeSizedPixel = - (kRGBA_F16_SkColorType == bitmap->colorType()) ? writeF16Pixel : writeS32Pixel; - pixelWriteFn_t writeUnpremulPixel = [&](const Sk4f& c, int index) { - writeSizedPixel(c * Sk4f(c[3], c[3], c[3], 1.0f), index); - }; - - pixelWriteFn_t writePixel = interpInPremul ? writeSizedPixel : writeUnpremulPixel; - - int prevIndex = 0; - for (int i = 1; i < fColorCount; i++) { - int nextIndex = (fColorCount == 2) ? (kCache32Count - 1) - : SkFixedToFFFF(fRecs[i].fPos) >> kCache32Shift; - SkASSERT(nextIndex < kCache32Count); - - if (nextIndex > prevIndex) { - Sk4f c0 = Sk4f::Load(fOrigColors4f[i - 1].vec()); - Sk4f c1 = Sk4f::Load(fOrigColors4f[i].vec()); - if (interpInPremul) { - c0 = c0 * Sk4f(c0[3], c0[3], c0[3], 1.0f); - c1 = c1 * Sk4f(c1[3], c1[3], c1[3], 1.0f); - } - - Sk4f step = Sk4f(1.0f / static_cast<float>(nextIndex - prevIndex)); - Sk4f delta = (c1 - c0) * step; - - for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) { - writePixel(c0, curIndex); - c0 += delta; - } - } - prevIndex = nextIndex; - } - SkASSERT(prevIndex == kCache32Count - 1); -} - -/* - * The gradient holds a cache for the most recent value of alpha. Successive - * callers with the same alpha value will share the same cache. - */ -sk_sp<SkGradientShaderBase::GradientShaderCache> SkGradientShaderBase::refCache(U8CPU alpha, - bool dither) const { - SkAutoMutexAcquire ama(fCacheMutex); - if (!fCache || fCache->getAlpha() != alpha || fCache->getDither() != dither) { - fCache.reset(new GradientShaderCache(alpha, dither, *this)); - } - // Increment the ref counter inside the mutex to ensure the returned pointer is still valid. - // Otherwise, the pointer may have been overwritten on a different thread before the object's - // ref count was incremented. - return fCache; -} - -SK_DECLARE_STATIC_MUTEX(gGradientCacheMutex); -/* - * Because our caller might rebuild the same (logically the same) gradient - * over and over, we'd like to return exactly the same "bitmap" if possible, - * allowing the client to utilize a cache of our bitmap (e.g. with a GPU). - * To do that, we maintain a private cache of built-bitmaps, based on our - * colors and positions. Note: we don't try to flatten the fMapper, so if one - * is present, we skip the cache for now. - */ -void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap, - GradientBitmapType bitmapType) const { - // our caller assumes no external alpha, so we ensure that our cache is built with 0xFF - sk_sp<GradientShaderCache> cache(this->refCache(0xFF, true)); - - // build our key: [numColors + colors[] + {positions[]} + flags + colorType ] - int count = 1 + fColorCount + 1 + 1; - if (fColorCount > 2) { - count += fColorCount - 1; // fRecs[].fPos - } - - SkAutoSTMalloc<16, int32_t> storage(count); - int32_t* buffer = storage.get(); - - *buffer++ = fColorCount; - memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor)); - buffer += fColorCount; - if (fColorCount > 2) { - for (int i = 1; i < fColorCount; i++) { - *buffer++ = fRecs[i].fPos; - } - } - *buffer++ = fGradFlags; - *buffer++ = static_cast<int32_t>(bitmapType); - SkASSERT(buffer - storage.get() == count); - - /////////////////////////////////// - - static SkGradientBitmapCache* gCache; - // each cache cost 1K or 2K of RAM, since each bitmap will be 1x256 at either 32bpp or 64bpp - static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32; - SkAutoMutexAcquire ama(gGradientCacheMutex); - - if (nullptr == gCache) { - gCache = new SkGradientBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS); - } - size_t size = count * sizeof(int32_t); - - if (!gCache->find(storage.get(), size, bitmap)) { - if (GradientBitmapType::kLegacy == bitmapType) { - // force our cache32pixelref to be built - (void)cache->getCache32(); - bitmap->setInfo(SkImageInfo::MakeN32Premul(kCache32Count, 1)); - bitmap->setPixelRef(sk_ref_sp(cache->getCache32PixelRef()), 0, 0); - } else { - // For these cases we use the bitmap cache, but not the GradientShaderCache. So just - // allocate and populate the bitmap's data directly. - - SkImageInfo info; - switch (bitmapType) { - case GradientBitmapType::kSRGB: - info = SkImageInfo::Make(kCache32Count, 1, kRGBA_8888_SkColorType, - kPremul_SkAlphaType, - SkColorSpace::MakeSRGB()); - break; - case GradientBitmapType::kHalfFloat: - info = SkImageInfo::Make( - kCache32Count, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType, - SkColorSpace::MakeSRGBLinear()); - break; - default: - SkFAIL("Unexpected bitmap type"); - return; - } - bitmap->allocPixels(info); - this->initLinearBitmap(bitmap); - } - gCache->add(storage.get(), size, *bitmap); - } -} - -void SkGradientShaderBase::commonAsAGradient(GradientInfo* info, bool flipGrad) const { - if (info) { - if (info->fColorCount >= fColorCount) { - SkColor* colorLoc; - Rec* recLoc; - SkAutoSTArray<8, SkColor> colorStorage; - SkAutoSTArray<8, Rec> recStorage; - if (flipGrad && (info->fColors || info->fColorOffsets)) { - colorStorage.reset(fColorCount); - recStorage.reset(fColorCount); - colorLoc = colorStorage.get(); - recLoc = recStorage.get(); - FlipGradientColors(colorLoc, recLoc, fOrigColors, fRecs, fColorCount); - } else { - colorLoc = fOrigColors; - recLoc = fRecs; - } - if (info->fColors) { - memcpy(info->fColors, colorLoc, fColorCount * sizeof(SkColor)); - } - if (info->fColorOffsets) { - if (fColorCount == 2) { - info->fColorOffsets[0] = 0; - info->fColorOffsets[1] = SK_Scalar1; - } else if (fColorCount > 2) { - for (int i = 0; i < fColorCount; ++i) { - info->fColorOffsets[i] = SkFixedToScalar(recLoc[i].fPos); - } - } - } - } - info->fColorCount = fColorCount; - info->fTileMode = fTileMode; - info->fGradientFlags = fGradFlags; - } -} - -#ifndef SK_IGNORE_TO_STRING -void SkGradientShaderBase::toString(SkString* str) const { - - str->appendf("%d colors: ", fColorCount); - - for (int i = 0; i < fColorCount; ++i) { - str->appendHex(fOrigColors[i], 8); - if (i < fColorCount-1) { - str->append(", "); - } - } - - if (fColorCount > 2) { - str->append(" points: ("); - for (int i = 0; i < fColorCount; ++i) { - str->appendScalar(SkFixedToScalar(fRecs[i].fPos)); - if (i < fColorCount-1) { - str->append(", "); - } - } - str->append(")"); - } - - static const char* gTileModeName[SkShader::kTileModeCount] = { - "clamp", "repeat", "mirror" - }; - - str->append(" "); - str->append(gTileModeName[fTileMode]); - - this->INHERITED::toString(str); -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -// Return true if these parameters are valid/legal/safe to construct a gradient -// -static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count, - unsigned tileMode) { - return nullptr != colors && count >= 1 && tileMode < (unsigned)SkShader::kTileModeCount; -} - -static void desc_init(SkGradientShaderBase::Descriptor* desc, - const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, uint32_t flags, const SkMatrix* localMatrix) { - SkASSERT(colorCount > 1); - - desc->fColors = colors; - desc->fColorSpace = std::move(colorSpace); - desc->fPos = pos; - desc->fCount = colorCount; - desc->fTileMode = mode; - desc->fGradFlags = flags; - desc->fLocalMatrix = localMatrix; -} - -// assumes colors is SkColor4f* and pos is SkScalar* -#define EXPAND_1_COLOR(count) \ - SkColor4f tmp[2]; \ - do { \ - if (1 == count) { \ - tmp[0] = tmp[1] = colors[0]; \ - colors = tmp; \ - pos = nullptr; \ - count = 2; \ - } \ - } while (0) - -struct ColorStopOptimizer { - ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, - int count, SkShader::TileMode mode) - : fColors(colors) - , fPos(pos) - , fCount(count) { - - if (!pos || count != 3) { - return; - } - - if (SkScalarNearlyEqual(pos[0], 0.0f) && - SkScalarNearlyEqual(pos[1], 0.0f) && - SkScalarNearlyEqual(pos[2], 1.0f)) { - - if (SkShader::kRepeat_TileMode == mode || - SkShader::kMirror_TileMode == mode || - colors[0] == colors[1]) { - - // Ignore the leftmost color/pos. - fColors += 1; - fPos += 1; - fCount = 2; - } - } else if (SkScalarNearlyEqual(pos[0], 0.0f) && - SkScalarNearlyEqual(pos[1], 1.0f) && - SkScalarNearlyEqual(pos[2], 1.0f)) { - - if (SkShader::kRepeat_TileMode == mode || - SkShader::kMirror_TileMode == mode || - colors[1] == colors[2]) { - - // Ignore the rightmost color/pos. - fCount = 2; - } - } - } - - const SkColor4f* fColors; - const SkScalar* fPos; - int fCount; -}; - -struct ColorConverter { - ColorConverter(const SkColor* colors, int count) { - for (int i = 0; i < count; ++i) { - fColors4f.push_back(SkColor4f::FromColor(colors[i])); - } - } - - SkSTArray<2, SkColor4f, true> fColors4f; -}; - -sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], - const SkColor colors[], - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags, - localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) { - return nullptr; - } - if (!valid_grad(colors, pos, colorCount, mode)) { - return nullptr; - } - if (1 == colorCount) { - return SkShader::MakeColorShader(colors[0], std::move(colorSpace)); - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - SkGradientShaderBase::Descriptor desc; - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkLinearGradient>(pts, desc); -} - -sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, - const SkColor colors[], - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, - flags, localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - if (radius <= 0) { - return nullptr; - } - if (!valid_grad(colors, pos, colorCount, mode)) { - return nullptr; - } - if (1 == colorCount) { - return SkShader::MakeColorShader(colors[0], std::move(colorSpace)); - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - SkGradientShaderBase::Descriptor desc; - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkRadialGradient>(center, radius, desc); -} - -sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, - SkScalar startRadius, - const SkPoint& end, - SkScalar endRadius, - const SkColor colors[], - const SkScalar pos[], - int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(), - nullptr, pos, colorCount, mode, flags, localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, - SkScalar startRadius, - const SkPoint& end, - SkScalar endRadius, - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], - int colorCount, - SkShader::TileMode mode, - uint32_t flags, - const SkMatrix* localMatrix) { - if (startRadius < 0 || endRadius < 0) { - return nullptr; - } - if (!valid_grad(colors, pos, colorCount, mode)) { - return nullptr; - } - if (startRadius == endRadius) { - if (start == end || startRadius == 0) { - return SkShader::MakeEmptyShader(); - } - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - EXPAND_1_COLOR(colorCount); - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - bool flipGradient = startRadius > endRadius; - - SkGradientShaderBase::Descriptor desc; - - if (!flipGradient) { - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkTwoPointConicalGradient>(start, startRadius, end, endRadius, - flipGradient, desc); - } else { - SkAutoSTArray<8, SkColor4f> colorsNew(opt.fCount); - SkAutoSTArray<8, SkScalar> posNew(opt.fCount); - for (int i = 0; i < opt.fCount; ++i) { - colorsNew[i] = opt.fColors[opt.fCount - i - 1]; - } - - if (pos) { - for (int i = 0; i < opt.fCount; ++i) { - posNew[i] = 1 - opt.fPos[opt.fCount - i - 1]; - } - desc_init(&desc, colorsNew.get(), std::move(colorSpace), posNew.get(), opt.fCount, mode, - flags, localMatrix); - } else { - desc_init(&desc, colorsNew.get(), std::move(colorSpace), nullptr, opt.fCount, mode, - flags, localMatrix); - } - - return sk_make_sp<SkTwoPointConicalGradient>(end, endRadius, start, startRadius, - flipGradient, desc); - } -} - -sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, - const SkColor colors[], - const SkScalar pos[], - int colorCount, - uint32_t flags, - const SkMatrix* localMatrix) { - ColorConverter converter(colors, colorCount); - return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, flags, - localMatrix); -} - -sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, - const SkColor4f colors[], - sk_sp<SkColorSpace> colorSpace, - const SkScalar pos[], - int colorCount, - uint32_t flags, - const SkMatrix* localMatrix) { - if (!valid_grad(colors, pos, colorCount, SkShader::kClamp_TileMode)) { - return nullptr; - } - if (1 == colorCount) { - return SkShader::MakeColorShader(colors[0], std::move(colorSpace)); - } - if (localMatrix && !localMatrix->invert(nullptr)) { - return nullptr; - } - - auto mode = SkShader::kClamp_TileMode; - - ColorStopOptimizer opt(colors, pos, colorCount, mode); - - SkGradientShaderBase::Descriptor desc; - desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, - localMatrix); - return sk_make_sp<SkSweepGradient>(cx, cy, desc); -} - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END - -/////////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrContext.h" -#include "GrShaderCaps.h" -#include "GrTextureStripAtlas.h" -#include "gl/GrGLContext.h" -#include "glsl/GrGLSLColorSpaceXformHelper.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "SkGr.h" - -static inline bool close_to_one_half(const SkFixed& val) { - return SkScalarNearlyEqual(SkFixedToScalar(val), SK_ScalarHalf); -} - -static inline int color_type_to_color_count(GrGradientEffect::ColorType colorType) { - switch (colorType) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case GrGradientEffect::kSingleHardStop_ColorType: - return 4; - case GrGradientEffect::kHardStopLeftEdged_ColorType: - case GrGradientEffect::kHardStopRightEdged_ColorType: - return 3; -#endif - case GrGradientEffect::kTwo_ColorType: - return 2; - case GrGradientEffect::kThree_ColorType: - return 3; - case GrGradientEffect::kTexture_ColorType: - return 0; - } - - SkDEBUGFAIL("Unhandled ColorType in color_type_to_color_count()"); - return -1; -} - -GrGradientEffect::ColorType GrGradientEffect::determineColorType( - const SkGradientShaderBase& shader) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - if (shader.fOrigPos) { - if (4 == shader.fColorCount) { - if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[1], shader.fOrigPos[2]) && - SkScalarNearlyEqual(shader.fOrigPos[3], 1.0f)) { - - return kSingleHardStop_ColorType; - } - } else if (3 == shader.fColorCount) { - if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[1], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) { - - return kHardStopLeftEdged_ColorType; - } else if (SkScalarNearlyEqual(shader.fOrigPos[0], 0.0f) && - SkScalarNearlyEqual(shader.fOrigPos[1], 1.0f) && - SkScalarNearlyEqual(shader.fOrigPos[2], 1.0f)) { - - return kHardStopRightEdged_ColorType; - } - } - } -#endif - - if (SkShader::kClamp_TileMode == shader.getTileMode()) { - if (2 == shader.fColorCount) { - return kTwo_ColorType; - } else if (3 == shader.fColorCount && - close_to_one_half(shader.getRecs()[1].fPos)) { - return kThree_ColorType; - } - } - - return kTexture_ColorType; -} - -void GrGradientEffect::GLSLProcessor::emitUniforms(GrGLSLUniformHandler* uniformHandler, - const GrGradientEffect& ge) { - if (int colorCount = color_type_to_color_count(ge.getColorType())) { - fColorsUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, - kVec4f_GrSLType, - kDefault_GrSLPrecision, - "Colors", - colorCount); - if (ge.fColorType == kSingleHardStop_ColorType) { - fHardStopT = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, - kDefault_GrSLPrecision, "HardStopT"); - } - } else { - fFSYUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kFloat_GrSLType, kDefault_GrSLPrecision, - "GradientYCoordFS"); - } -} - -static inline void set_after_interp_color_uni_array( - const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor4f>& colors, - const GrColorSpaceXform* colorSpaceXform) { - int count = colors.count(); - if (colorSpaceXform) { - constexpr int kSmallCount = 10; - SkAutoSTArray<4 * kSmallCount, float> vals(4 * count); - - for (int i = 0; i < count; i++) { - colorSpaceXform->srcToDst().mapScalars(colors[i].vec(), &vals[4 * i]); - } - - pdman.set4fv(uni, count, vals.get()); - } else { - pdman.set4fv(uni, count, (float*)&colors[0]); - } -} - -static inline void set_before_interp_color_uni_array( - const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor4f>& colors, - const GrColorSpaceXform* colorSpaceXform) { - int count = colors.count(); - constexpr int kSmallCount = 10; - SkAutoSTArray<4 * kSmallCount, float> vals(4 * count); - - for (int i = 0; i < count; i++) { - float a = colors[i].fA; - vals[4 * i + 0] = colors[i].fR * a; - vals[4 * i + 1] = colors[i].fG * a; - vals[4 * i + 2] = colors[i].fB * a; - vals[4 * i + 3] = a; - } - - if (colorSpaceXform) { - for (int i = 0; i < count; i++) { - colorSpaceXform->srcToDst().mapScalars(&vals[4 * i]); - } - } - - pdman.set4fv(uni, count, vals.get()); -} - -static inline void set_after_interp_color_uni_array(const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor>& colors) { - int count = colors.count(); - constexpr int kSmallCount = 10; - - SkAutoSTArray<4*kSmallCount, float> vals(4*count); - - for (int i = 0; i < colors.count(); i++) { - // RGBA - vals[4*i + 0] = SkColorGetR(colors[i]) / 255.f; - vals[4*i + 1] = SkColorGetG(colors[i]) / 255.f; - vals[4*i + 2] = SkColorGetB(colors[i]) / 255.f; - vals[4*i + 3] = SkColorGetA(colors[i]) / 255.f; - } - - pdman.set4fv(uni, colors.count(), vals.get()); -} - -static inline void set_before_interp_color_uni_array(const GrGLSLProgramDataManager& pdman, - const GrGLSLProgramDataManager::UniformHandle uni, - const SkTDArray<SkColor>& colors) { - int count = colors.count(); - constexpr int kSmallCount = 10; - - SkAutoSTArray<4*kSmallCount, float> vals(4*count); - - for (int i = 0; i < count; i++) { - float a = SkColorGetA(colors[i]) / 255.f; - float aDiv255 = a / 255.f; - - // RGBA - vals[4*i + 0] = SkColorGetR(colors[i]) * aDiv255; - vals[4*i + 1] = SkColorGetG(colors[i]) * aDiv255; - vals[4*i + 2] = SkColorGetB(colors[i]) * aDiv255; - vals[4*i + 3] = a; - } - - pdman.set4fv(uni, count, vals.get()); -} - -void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& processor) { - const GrGradientEffect& e = processor.cast<GrGradientEffect>(); - - switch (e.getColorType()) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case GrGradientEffect::kSingleHardStop_ColorType: - pdman.set1f(fHardStopT, e.fPositions[1]); - // fall through - case GrGradientEffect::kHardStopLeftEdged_ColorType: - case GrGradientEffect::kHardStopRightEdged_ColorType: -#endif - case GrGradientEffect::kTwo_ColorType: - case GrGradientEffect::kThree_ColorType: { - if (e.fColors4f.count() > 0) { - // Gamma-correct / color-space aware - if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { - set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors4f, - e.fColorSpaceXform.get()); - } else { - set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors4f, - e.fColorSpaceXform.get()); - } - } else { - // Legacy mode. Would be nice if we had converted the 8-bit colors to float earlier - if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { - set_before_interp_color_uni_array(pdman, fColorsUni, e.fColors); - } else { - set_after_interp_color_uni_array(pdman, fColorsUni, e.fColors); - } - } - - break; - } - - case GrGradientEffect::kTexture_ColorType: { - SkScalar yCoord = e.getYCoord(); - if (yCoord != fCachedYCoord) { - pdman.set1f(fFSYUni, yCoord); - fCachedYCoord = yCoord; - } - if (SkToBool(e.fColorSpaceXform)) { - fColorSpaceHelper.setData(pdman, e.fColorSpaceXform.get()); - } - break; - } - } -} - -uint32_t GrGradientEffect::GLSLProcessor::GenBaseGradientKey(const GrProcessor& processor) { - const GrGradientEffect& e = processor.cast<GrGradientEffect>(); - - uint32_t key = 0; - - if (GrGradientEffect::kBeforeInterp_PremulType == e.getPremulType()) { - key |= kPremulBeforeInterpKey; - } - - if (GrGradientEffect::kTwo_ColorType == e.getColorType()) { - key |= kTwoColorKey; - } else if (GrGradientEffect::kThree_ColorType == e.getColorType()) { - key |= kThreeColorKey; - } -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - else if (GrGradientEffect::kSingleHardStop_ColorType == e.getColorType()) { - key |= kHardStopCenteredKey; - } else if (GrGradientEffect::kHardStopLeftEdged_ColorType == e.getColorType()) { - key |= kHardStopZeroZeroOneKey; - } else if (GrGradientEffect::kHardStopRightEdged_ColorType == e.getColorType()) { - key |= kHardStopZeroOneOneKey; - } - - if (SkShader::TileMode::kClamp_TileMode == e.fTileMode) { - key |= kClampTileMode; - } else if (SkShader::TileMode::kRepeat_TileMode == e.fTileMode) { - key |= kRepeatTileMode; - } else { - key |= kMirrorTileMode; - } -#endif - - key |= GrColorSpaceXform::XformKey(e.fColorSpaceXform.get()) << kReservedBits; - - return key; -} - -void GrGradientEffect::GLSLProcessor::emitColor(GrGLSLFPFragmentBuilder* fragBuilder, - GrGLSLUniformHandler* uniformHandler, - const GrShaderCaps* shaderCaps, - const GrGradientEffect& ge, - const char* gradientTValue, - const char* outputColor, - const char* inputColor, - const TextureSamplers& texSamplers) { - switch (ge.getColorType()) { -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case kSingleHardStop_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - const char* stopT = uniformHandler->getUniformCStr(fHardStopT); - - fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t); - - // Account for tile mode - if (SkShader::kRepeat_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("clamp_t = fract(%s);", t); - } else if (SkShader::kMirror_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t); - fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t); - fragBuilder->codeAppendf(" clamp_t = fract(%s);", t); - fragBuilder->codeAppendf(" } else {"); - fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t); - fragBuilder->codeAppendf(" }"); - fragBuilder->codeAppendf("}"); - } - - // Calculate color - fragBuilder->codeAppend ("vec4 start, end;"); - fragBuilder->codeAppend ("float relative_t;"); - fragBuilder->codeAppendf("if (clamp_t < %s) {", stopT); - fragBuilder->codeAppendf(" start = %s[0];", colors); - fragBuilder->codeAppendf(" end = %s[1];", colors); - fragBuilder->codeAppendf(" relative_t = clamp_t / %s;", stopT); - fragBuilder->codeAppend ("} else {"); - fragBuilder->codeAppendf(" start = %s[2];", colors); - fragBuilder->codeAppendf(" end = %s[3];", colors); - fragBuilder->codeAppendf(" relative_t = (clamp_t - %s) / (1 - %s);", stopT, stopT); - fragBuilder->codeAppend ("}"); - fragBuilder->codeAppend ("vec4 colorTemp = mix(start, end, relative_t);"); - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kHardStopLeftEdged_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t); - - // Account for tile mode - if (SkShader::kRepeat_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("clamp_t = fract(%s);", t); - } else if (SkShader::kMirror_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t); - fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t); - fragBuilder->codeAppendf(" clamp_t = fract(%s);", t); - fragBuilder->codeAppendf(" } else {"); - fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t); - fragBuilder->codeAppendf(" }"); - fragBuilder->codeAppendf("}"); - } - - fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[1], %s[2], clamp_t);", colors, - colors); - if (SkShader::kClamp_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0) {", t); - fragBuilder->codeAppendf(" colorTemp = %s[0];", colors); - fragBuilder->codeAppendf("}"); - } - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kHardStopRightEdged_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("float clamp_t = clamp(%s, 0.0, 1.0);", t); - - // Account for tile mode - if (SkShader::kRepeat_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("clamp_t = fract(%s);", t); - } else if (SkShader::kMirror_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s < 0.0 || %s > 1.0) {", t, t); - fragBuilder->codeAppendf(" if (mod(floor(%s), 2.0) == 0.0) {", t); - fragBuilder->codeAppendf(" clamp_t = fract(%s);", t); - fragBuilder->codeAppendf(" } else {"); - fragBuilder->codeAppendf(" clamp_t = 1.0 - fract(%s);", t); - fragBuilder->codeAppendf(" }"); - fragBuilder->codeAppendf("}"); - } - - fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp_t);", colors, - colors); - if (SkShader::kClamp_TileMode == ge.fTileMode) { - fragBuilder->codeAppendf("if (%s > 1.0) {", t); - fragBuilder->codeAppendf(" colorTemp = %s[2];", colors); - fragBuilder->codeAppendf("}"); - } - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } -#endif - - case kTwo_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("vec4 colorTemp = mix(%s[0], %s[1], clamp(%s, 0.0, 1.0));", - colors, colors, t); - - // We could skip this step if both colors are known to be opaque. Two - // considerations: - // The gradient SkShader reporting opaque is more restrictive than necessary in the two - // pt case. Make sure the key reflects this optimization (and note that it can use the - // same shader as thekBeforeIterp case). This same optimization applies to the 3 color - // case below. - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kThree_ColorType: { - const char* t = gradientTValue; - const char* colors = uniformHandler->getUniformCStr(fColorsUni); - - fragBuilder->codeAppendf("float oneMinus2t = 1.0 - (2.0 * %s);", t); - fragBuilder->codeAppendf("vec4 colorTemp = clamp(oneMinus2t, 0.0, 1.0) * %s[0];", - colors); - if (!shaderCaps->canUseMinAndAbsTogether()) { - // The Tegra3 compiler will sometimes never return if we have - // min(abs(oneMinus2t), 1.0), or do the abs first in a separate expression. - fragBuilder->codeAppendf("float minAbs = abs(oneMinus2t);"); - fragBuilder->codeAppendf("minAbs = minAbs > 1.0 ? 1.0 : minAbs;"); - fragBuilder->codeAppendf("colorTemp += (1.0 - minAbs) * %s[1];", colors); - } else { - fragBuilder->codeAppendf("colorTemp += (1.0 - min(abs(oneMinus2t), 1.0)) * %s[1];", - colors); - } - fragBuilder->codeAppendf("colorTemp += clamp(-oneMinus2t, 0.0, 1.0) * %s[2];", colors); - - if (GrGradientEffect::kAfterInterp_PremulType == ge.getPremulType()) { - fragBuilder->codeAppend("colorTemp.rgb *= colorTemp.a;"); - } - if (ge.fColorSpaceXform) { - fragBuilder->codeAppend("colorTemp.rgb = clamp(colorTemp.rgb, 0, colorTemp.a);"); - } - - fragBuilder->codeAppendf("%s = %s * colorTemp;", outputColor, inputColor); - - break; - } - - case kTexture_ColorType: { - fColorSpaceHelper.emitCode(uniformHandler, ge.fColorSpaceXform.get()); - - const char* fsyuni = uniformHandler->getUniformCStr(fFSYUni); - - fragBuilder->codeAppendf("vec2 coord = vec2(%s, %s);", gradientTValue, fsyuni); - fragBuilder->codeAppendf("%s = ", outputColor); - fragBuilder->appendTextureLookupAndModulate(inputColor, texSamplers[0], "coord", - kVec2f_GrSLType, &fColorSpaceHelper); - fragBuilder->codeAppend(";"); - - break; - } - } -} - -///////////////////////////////////////////////////////////////////// - -inline GrFragmentProcessor::OptimizationFlags GrGradientEffect::OptFlags(bool isOpaque) { - return isOpaque - ? kPreservesOpaqueInput_OptimizationFlag | - kCompatibleWithCoverageAsAlpha_OptimizationFlag - : kCompatibleWithCoverageAsAlpha_OptimizationFlag; -} - -GrGradientEffect::GrGradientEffect(const CreateArgs& args, bool isOpaque) - : INHERITED(OptFlags(isOpaque)) { - const SkGradientShaderBase& shader(*args.fShader); - - fIsOpaque = shader.isOpaque(); - - fColorType = this->determineColorType(shader); - fColorSpaceXform = std::move(args.fColorSpaceXform); - - if (kTexture_ColorType != fColorType) { - SkASSERT(shader.fOrigColors && shader.fOrigColors4f); - if (args.fGammaCorrect) { - fColors4f = SkTDArray<SkColor4f>(shader.fOrigColors4f, shader.fColorCount); - } else { - fColors = SkTDArray<SkColor>(shader.fOrigColors, shader.fColorCount); - } - -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - if (shader.fOrigPos) { - fPositions = SkTDArray<SkScalar>(shader.fOrigPos, shader.fColorCount); - } -#endif - } - -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - fTileMode = args.fTileMode; -#endif - - switch (fColorType) { - // The two and three color specializations do not currently support tiling. - case kTwo_ColorType: - case kThree_ColorType: -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - case kHardStopLeftEdged_ColorType: - case kHardStopRightEdged_ColorType: - case kSingleHardStop_ColorType: -#endif - fRow = -1; - - if (SkGradientShader::kInterpolateColorsInPremul_Flag & shader.getGradFlags()) { - fPremulType = kBeforeInterp_PremulType; - } else { - fPremulType = kAfterInterp_PremulType; - } - - fCoordTransform.reset(*args.fMatrix); - - break; - case kTexture_ColorType: - // doesn't matter how this is set, just be consistent because it is part of the - // effect key. - fPremulType = kBeforeInterp_PremulType; - - SkGradientShaderBase::GradientBitmapType bitmapType = - SkGradientShaderBase::GradientBitmapType::kLegacy; - if (args.fGammaCorrect) { - // Try to use F16 if we can - if (args.fContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) { - bitmapType = SkGradientShaderBase::GradientBitmapType::kHalfFloat; - } else if (args.fContext->caps()->isConfigTexturable(kSRGBA_8888_GrPixelConfig)) { - bitmapType = SkGradientShaderBase::GradientBitmapType::kSRGB; - } else { - // This can happen, but only if someone explicitly creates an unsupported - // (eg sRGB) surface. Just fall back to legacy behavior. - } - } - - SkBitmap bitmap; - shader.getGradientTableBitmap(&bitmap, bitmapType); - SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width())); - - - GrTextureStripAtlas::Desc desc; - desc.fWidth = bitmap.width(); - desc.fHeight = 32; - desc.fRowHeight = bitmap.height(); - desc.fContext = args.fContext; - desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info(), *args.fContext->caps()); - fAtlas = GrTextureStripAtlas::GetAtlas(desc); - SkASSERT(fAtlas); - - // We always filter the gradient table. Each table is one row of a texture, always - // y-clamp. - GrSamplerParams params; - params.setFilterMode(GrSamplerParams::kBilerp_FilterMode); - params.setTileModeX(args.fTileMode); - - fRow = fAtlas->lockRow(bitmap); - if (-1 != fRow) { - fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight(); - // This is 1/2 places where auto-normalization is disabled - fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix, - fAtlas->asTextureProxyRef().get(), false); - fTextureSampler.reset(args.fContext->resourceProvider(), - fAtlas->asTextureProxyRef(), params); - } else { - // In this instance we know the params are: - // clampY, bilerp - // and the proxy is: - // exact fit, power of two in both dimensions - // Only the x-tileMode is unknown. However, given all the other knowns we know - // that GrMakeCachedBitmapProxy is sufficient (i.e., it won't need to be - // extracted to a subset or mipmapped). - sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy( - args.fContext->resourceProvider(), - bitmap); - if (!proxy) { - return; - } - // This is 2/2 places where auto-normalization is disabled - fCoordTransform.reset(args.fContext->resourceProvider(), *args.fMatrix, - proxy.get(), false); - fTextureSampler.reset(args.fContext->resourceProvider(), - std::move(proxy), params); - fYCoord = SK_ScalarHalf; - } - - this->addTextureSampler(&fTextureSampler); - - break; - } - - this->addCoordTransform(&fCoordTransform); -} - -GrGradientEffect::~GrGradientEffect() { - if (this->useAtlas()) { - fAtlas->unlockRow(fRow); - } -} - -bool GrGradientEffect::onIsEqual(const GrFragmentProcessor& processor) const { - const GrGradientEffect& ge = processor.cast<GrGradientEffect>(); - - if (this->fColorType != ge.getColorType()) { - return false; - } - SkASSERT(this->useAtlas() == ge.useAtlas()); - if (kTexture_ColorType == fColorType) { - if (fYCoord != ge.getYCoord()) { - return false; - } - } else { - if (kSingleHardStop_ColorType == fColorType) { - if (!SkScalarNearlyEqual(ge.fPositions[1], fPositions[1])) { - return false; - } - } - if (this->getPremulType() != ge.getPremulType() || - this->fColors.count() != ge.fColors.count() || - this->fColors4f.count() != ge.fColors4f.count()) { - return false; - } - - for (int i = 0; i < this->fColors.count(); i++) { - if (*this->getColors(i) != *ge.getColors(i)) { - return false; - } - } - for (int i = 0; i < this->fColors4f.count(); i++) { - if (*this->getColors4f(i) != *ge.getColors4f(i)) { - return false; - } - } - } - return GrColorSpaceXform::Equals(this->fColorSpaceXform.get(), ge.fColorSpaceXform.get()); -} - -#if GR_TEST_UTILS -GrGradientEffect::RandomGradientParams::RandomGradientParams(SkRandom* random) { - // Set color count to min of 2 so that we don't trigger the const color optimization and make - // a non-gradient processor. - fColorCount = random->nextRangeU(2, kMaxRandomGradientColors); - fUseColors4f = random->nextBool(); - - // if one color, omit stops, otherwise randomly decide whether or not to - if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) { - fStops = nullptr; - } else { - fStops = fStopStorage; - } - - // if using SkColor4f, attach a random (possibly null) color space (with linear gamma) - if (fUseColors4f) { - fColorSpace = GrTest::TestColorSpace(random); - if (fColorSpace) { - SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(fColorSpace)->type()); - fColorSpace = static_cast<SkColorSpace_XYZ*>(fColorSpace.get())->makeLinearGamma(); - } - } - - SkScalar stop = 0.f; - for (int i = 0; i < fColorCount; ++i) { - if (fUseColors4f) { - fColors4f[i].fR = random->nextUScalar1(); - fColors4f[i].fG = random->nextUScalar1(); - fColors4f[i].fB = random->nextUScalar1(); - fColors4f[i].fA = random->nextUScalar1(); - } else { - fColors[i] = random->nextU(); - } - if (fStops) { - fStops[i] = stop; - stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f; - } - } - fTileMode = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount)); -} -#endif - -#endif diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h deleted file mode 100644 index 7a66edaffc..0000000000 --- a/src/shaders/gradients/SkGradientShaderPriv.h +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkGradientShaderPriv_DEFINED -#define SkGradientShaderPriv_DEFINED - -#include "SkGradientBitmapCache.h" -#include "SkGradientShader.h" - -#include "SkArenaAlloc.h" -#include "SkAutoMalloc.h" -#include "SkClampRange.h" -#include "SkColorPriv.h" -#include "SkColorSpace.h" -#include "SkOnce.h" -#include "SkPM4fPriv.h" -#include "SkRasterPipeline.h" -#include "SkReadBuffer.h" -#include "SkShaderBase.h" -#include "SkUtils.h" -#include "SkWriteBuffer.h" - -#if SK_SUPPORT_GPU - #define GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS 1 -#endif - -static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1, - int count) { - if (count > 0) { - if (v0 == v1) { - sk_memset32(dst, v0, count); - } else { - int pairs = count >> 1; - for (int i = 0; i < pairs; i++) { - *dst++ = v0; - *dst++ = v1; - } - if (count & 1) { - *dst = v0; - } - } - } -} - -// Clamp - -static inline SkFixed clamp_tileproc(SkFixed x) { - return SkClampMax(x, 0xFFFF); -} - -// Repeat - -static inline SkFixed repeat_tileproc(SkFixed x) { - return x & 0xFFFF; -} - -// Mirror - -static inline SkFixed mirror_tileproc(SkFixed x) { - int s = SkLeftShift(x, 15) >> 31; - return (x ^ s) & 0xFFFF; -} - -/////////////////////////////////////////////////////////////////////////////// - -typedef SkFixed (*TileProc)(SkFixed); - -/////////////////////////////////////////////////////////////////////////////// - -static const TileProc gTileProcs[] = { - clamp_tileproc, - repeat_tileproc, - mirror_tileproc -}; - -/////////////////////////////////////////////////////////////////////////////// - -class SkGradientShaderBase : public SkShaderBase { -public: - struct Descriptor { - Descriptor() { - sk_bzero(this, sizeof(*this)); - fTileMode = SkShader::kClamp_TileMode; - } - - const SkMatrix* fLocalMatrix; - const SkColor4f* fColors; - sk_sp<SkColorSpace> fColorSpace; - const SkScalar* fPos; - int fCount; - SkShader::TileMode fTileMode; - uint32_t fGradFlags; - - void flatten(SkWriteBuffer&) const; - }; - - class DescriptorScope : public Descriptor { - public: - DescriptorScope() {} - - bool unflatten(SkReadBuffer&); - - // fColors and fPos always point into local memory, so they can be safely mutated - // - SkColor4f* mutableColors() { return const_cast<SkColor4f*>(fColors); } - SkScalar* mutablePos() { return const_cast<SkScalar*>(fPos); } - - private: - enum { - kStorageCount = 16 - }; - SkColor4f fColorStorage[kStorageCount]; - SkScalar fPosStorage[kStorageCount]; - SkMatrix fLocalMatrixStorage; - SkAutoMalloc fDynamicStorage; - }; - - SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit); - ~SkGradientShaderBase() override; - - // The cache is initialized on-demand when getCache32 is called. - class GradientShaderCache : public SkRefCnt { - public: - GradientShaderCache(U8CPU alpha, bool dither, const SkGradientShaderBase& shader); - ~GradientShaderCache(); - - const SkPMColor* getCache32(); - - SkPixelRef* getCache32PixelRef() const { return fCache32PixelRef.get(); } - - unsigned getAlpha() const { return fCacheAlpha; } - bool getDither() const { return fCacheDither; } - - private: - // Working pointer. If it's nullptr, we need to recompute the cache values. - SkPMColor* fCache32; - - sk_sp<SkPixelRef> fCache32PixelRef; - const unsigned fCacheAlpha; // The alpha value we used when we computed the cache. - // Larger than 8bits so we can store uninitialized - // value. - const bool fCacheDither; // The dither flag used when we computed the cache. - - const SkGradientShaderBase& fShader; - - // Make sure we only initialize the cache once. - SkOnce fCache32InitOnce; - - static void initCache32(GradientShaderCache* cache); - - static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count, - U8CPU alpha, uint32_t gradFlags, bool dither); - }; - - class GradientShaderBaseContext : public Context { - public: - GradientShaderBaseContext(const SkGradientShaderBase& shader, const ContextRec&); - - uint32_t getFlags() const override { return fFlags; } - - bool isValid() const; - - protected: - SkMatrix fDstToIndex; - SkMatrix::MapXYProc fDstToIndexProc; - uint8_t fDstToIndexClass; - uint8_t fFlags; - bool fDither; - - sk_sp<GradientShaderCache> fCache; - - private: - typedef Context INHERITED; - }; - - bool isOpaque() const override; - - enum class GradientBitmapType : uint8_t { - kLegacy, - kSRGB, - kHalfFloat, - }; - - void getGradientTableBitmap(SkBitmap*, GradientBitmapType bitmapType) const; - - enum { - /// Seems like enough for visual accuracy. TODO: if pos[] deserves - /// it, use a larger cache. - kCache32Bits = 8, - kCache32Count = (1 << kCache32Bits), - kCache32Shift = 16 - kCache32Bits, - kSqrt32Shift = 8 - kCache32Bits, - - /// This value is used to *read* the dither cache; it may be 0 - /// if dithering is disabled. - kDitherStride32 = kCache32Count, - }; - - uint32_t getGradFlags() const { return fGradFlags; } - -protected: - struct Rec { - SkFixed fPos; // 0...1 - uint32_t fScale; // (1 << 24) / range - }; - - class GradientShaderBase4fContext; - - SkGradientShaderBase(SkReadBuffer& ); - void flatten(SkWriteBuffer&) const override; - SK_TO_STRING_OVERRIDE() - - void commonAsAGradient(GradientInfo*, bool flipGrad = false) const; - - bool onAsLuminanceColor(SkColor*) const override; - - void initLinearBitmap(SkBitmap* bitmap) 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); - - bool onAppendStages(SkRasterPipeline* pipeline, SkColorSpace* dstCS, SkArenaAlloc* alloc, - const SkMatrix& ctm, const SkPaint& paint, - const SkMatrix* localM) const override; - - virtual bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { return false; } - - template <typename T, typename... Args> - static Context* CheckedMakeContext(SkArenaAlloc* alloc, Args&&... args) { - auto* ctx = alloc->make<T>(std::forward<Args>(args)...); - if (!ctx->isValid()) { - return nullptr; - } - return ctx; - } - - const SkMatrix fPtsToUnit; - TileMode fTileMode; - TileProc fTileProc; - uint8_t fGradFlags; - Rec* fRecs; - -private: - enum { - kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space - - kStorageSize = kColorStorageCount * - (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec) + sizeof(SkColor4f)) - }; - SkColor fStorage[(kStorageSize + 3) >> 2]; -public: - SkColor* fOrigColors; // original colors, before modulation by paint in context. - SkColor4f* fOrigColors4f; // original colors, as linear floats - SkScalar* fOrigPos; // original positions - int fColorCount; - sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops - - bool colorsAreOpaque() const { return fColorsAreOpaque; } - - TileMode getTileMode() const { return fTileMode; } - Rec* getRecs() const { return fRecs; } - -private: - bool fColorsAreOpaque; - - sk_sp<GradientShaderCache> refCache(U8CPU alpha, bool dither) const; - mutable SkMutex fCacheMutex; - mutable sk_sp<GradientShaderCache> fCache; - - void initCommon(); - - typedef SkShaderBase INHERITED; -}; - - -static inline int init_dither_toggle(int x, int y) { - x &= 1; - y = (y & 1) << 1; - return (x | y) * SkGradientShaderBase::kDitherStride32; -} - -static inline int next_dither_toggle(int toggle) { - return toggle ^ SkGradientShaderBase::kDitherStride32; -} - -/////////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrColorSpaceXform.h" -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "glsl/GrGLSLColorSpaceXformHelper.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLProgramDataManager.h" - -class GrInvariantOutput; - -/* - * The interpretation of the texture matrix depends on the sample mode. The - * texture matrix is applied both when the texture coordinates are explicit - * and when vertex positions are used as texture coordinates. In the latter - * case the texture matrix is applied to the pre-view-matrix position - * values. - * - * Normal SampleMode - * The post-matrix texture coordinates are in normalize space with (0,0) at - * the top-left and (1,1) at the bottom right. - * RadialGradient - * The matrix specifies the radial gradient parameters. - * (0,0) in the post-matrix space is center of the radial gradient. - * Radial2Gradient - * Matrix transforms to space where first circle is centered at the - * origin. The second circle will be centered (x, 0) where x may be - * 0 and is provided by setRadial2Params. The post-matrix space is - * normalized such that 1 is the second radius - first radius. - * SweepGradient - * The angle from the origin of texture coordinates in post-matrix space - * determines the gradient value. - */ - - class GrTextureStripAtlas; - -// Base class for Gr gradient effects -class GrGradientEffect : public GrFragmentProcessor { -public: - struct CreateArgs { - CreateArgs(GrContext* context, - const SkGradientShaderBase* shader, - const SkMatrix* matrix, - SkShader::TileMode tileMode, - sk_sp<GrColorSpaceXform> colorSpaceXform, - bool gammaCorrect) - : fContext(context) - , fShader(shader) - , fMatrix(matrix) - , fTileMode(tileMode) - , fColorSpaceXform(std::move(colorSpaceXform)) - , fGammaCorrect(gammaCorrect) {} - - GrContext* fContext; - const SkGradientShaderBase* fShader; - const SkMatrix* fMatrix; - SkShader::TileMode fTileMode; - sk_sp<GrColorSpaceXform> fColorSpaceXform; - bool fGammaCorrect; - }; - - class GLSLProcessor; - - ~GrGradientEffect() override; - - bool useAtlas() const { return SkToBool(-1 != fRow); } - SkScalar getYCoord() const { return fYCoord; } - - enum ColorType { - kTwo_ColorType, - kThree_ColorType, // Symmetric three color - kTexture_ColorType, - -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - kSingleHardStop_ColorType, // 0, t, t, 1 - kHardStopLeftEdged_ColorType, // 0, 0, 1 - kHardStopRightEdged_ColorType, // 0, 1, 1 -#endif - }; - - ColorType getColorType() const { return fColorType; } - - // Determines the type of gradient, one of: - // - Two-color - // - Symmetric three-color - // - Texture - // - Centered hard stop - // - Left-edged hard stop - // - Right-edged hard stop - ColorType determineColorType(const SkGradientShaderBase& shader); - - enum PremulType { - kBeforeInterp_PremulType, - kAfterInterp_PremulType, - }; - - PremulType getPremulType() const { return fPremulType; } - - const SkColor* getColors(int pos) const { - SkASSERT(fColorType != kTexture_ColorType); - SkASSERT(pos < fColors.count()); - return &fColors[pos]; - } - - const SkColor4f* getColors4f(int pos) const { - SkASSERT(fColorType != kTexture_ColorType); - SkASSERT(pos < fColors4f.count()); - return &fColors4f[pos]; - } - -protected: - GrGradientEffect(const CreateArgs&, bool isOpaque); - - #if GR_TEST_UTILS - /** Helper struct that stores (and populates) parameters to construct a random gradient. - If fUseColors4f is true, then the SkColor4f factory should be called, with fColors4f and - fColorSpace. Otherwise, the SkColor factory should be called, with fColors. fColorCount - will be the number of color stops in either case, and fColors and fStops can be passed to - the gradient factory. (The constructor may decide not to use stops, in which case fStops - will be nullptr). */ - struct RandomGradientParams { - static const int kMaxRandomGradientColors = 5; - - RandomGradientParams(SkRandom* r); - - bool fUseColors4f; - SkColor fColors[kMaxRandomGradientColors]; - SkColor4f fColors4f[kMaxRandomGradientColors]; - sk_sp<SkColorSpace> fColorSpace; - SkScalar fStopStorage[kMaxRandomGradientColors]; - SkShader::TileMode fTileMode; - int fColorCount; - SkScalar* fStops; - }; - #endif - - bool onIsEqual(const GrFragmentProcessor&) const override; - - const GrCoordTransform& getCoordTransform() const { return fCoordTransform; } - -private: - static OptimizationFlags OptFlags(bool isOpaque); - - // If we're in legacy mode, then fColors will be populated. If we're gamma-correct, then - // fColors4f and fColorSpaceXform will be populated. - SkTDArray<SkColor> fColors; - - SkTDArray<SkColor4f> fColors4f; - sk_sp<GrColorSpaceXform> fColorSpaceXform; - - SkTDArray<SkScalar> fPositions; - SkShader::TileMode fTileMode; - - GrCoordTransform fCoordTransform; - TextureSampler fTextureSampler; - SkScalar fYCoord; - GrTextureStripAtlas* fAtlas; - int fRow; - bool fIsOpaque; - ColorType fColorType; - PremulType fPremulType; // This is already baked into the table for texture gradients, and - // only changes behavior for gradients that don't use a texture. - typedef GrFragmentProcessor INHERITED; - -}; - -/////////////////////////////////////////////////////////////////////////////// - -// Base class for GL gradient effects -class GrGradientEffect::GLSLProcessor : public GrGLSLFragmentProcessor { -public: - GLSLProcessor() { - fCachedYCoord = SK_ScalarMax; - } - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - -protected: - /** - * Subclasses must call this. It will return a key for the part of the shader code controlled - * by the base class. The subclasses must stick it in their key and then pass it to the below - * emit* functions from their emitCode function. - */ - static uint32_t GenBaseGradientKey(const GrProcessor&); - - // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses - // should call this method from their emitCode(). - void emitUniforms(GrGLSLUniformHandler*, const GrGradientEffect&); - - // Emit code that gets a fragment's color from an expression for t; has branches for - // several control flows inside -- 2-color gradients, 3-color symmetric gradients, 4+ - // color gradients that use the traditional texture lookup, as well as several varieties - // of hard stop gradients - void emitColor(GrGLSLFPFragmentBuilder* fragBuilder, - GrGLSLUniformHandler* uniformHandler, - const GrShaderCaps* shaderCaps, - const GrGradientEffect&, - const char* gradientTValue, - const char* outputColor, - const char* inputColor, - const TextureSamplers&); - -private: - enum { - // First bit for premul before/after interp - kPremulBeforeInterpKey = 1, - - // Next three bits for 2/3 color type or different special - // hard stop cases (neither means using texture atlas) - kTwoColorKey = 2, - kThreeColorKey = 4, -#if GR_GL_USE_ACCURATE_HARD_STOP_GRADIENTS - kHardStopCenteredKey = 6, - kHardStopZeroZeroOneKey = 8, - kHardStopZeroOneOneKey = 10, - - // Next two bits for tile mode - kClampTileMode = 16, - kRepeatTileMode = 32, - kMirrorTileMode = 48, - - // Lower six bits for premul, 2/3 color type, and tile mode - kReservedBits = 6, -#endif - }; - - SkScalar fCachedYCoord; - GrGLSLProgramDataManager::UniformHandle fColorsUni; - GrGLSLProgramDataManager::UniformHandle fHardStopT; - GrGLSLProgramDataManager::UniformHandle fFSYUni; - GrGLSLColorSpaceXformHelper fColorSpaceHelper; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -#endif - -#endif diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp deleted file mode 100644 index 17c4fd36a4..0000000000 --- a/src/shaders/gradients/SkLinearGradient.cpp +++ /dev/null @@ -1,804 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Sk4fLinearGradient.h" -#include "SkColorSpaceXformer.h" -#include "SkLinearGradient.h" -#include "SkRefCnt.h" - -// define to test the 4f gradient path -// #define FORCE_4F_CONTEXT - -static const float kInv255Float = 1.0f / 255; - -static inline int repeat_8bits(int x) { - return x & 0xFF; -} - -static inline int mirror_8bits(int x) { - if (x & 256) { - x = ~x; - } - return x & 255; -} - -static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) { - SkVector vec = pts[1] - pts[0]; - SkScalar mag = vec.length(); - SkScalar inv = mag ? SkScalarInvert(mag) : 0; - - vec.scale(inv); - SkMatrix matrix; - matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); - matrix.postTranslate(-pts[0].fX, -pts[0].fY); - matrix.postScale(inv, inv); - return matrix; -} - -static bool use_4f_context(const SkShaderBase::ContextRec& rec, uint32_t flags) { -#ifdef FORCE_4F_CONTEXT - return true; -#else - return rec.fPreferredDstType == SkShaderBase::ContextRec::kPM4f_DstType - || SkToBool(flags & SkLinearGradient::kForce4fContext_PrivateFlag); -#endif -} - -/////////////////////////////////////////////////////////////////////////////// - -SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc) - : SkGradientShaderBase(desc, pts_to_unit_matrix(pts)) - , fStart(pts[0]) - , fEnd(pts[1]) { -} - -sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - SkPoint pts[2]; - pts[0] = buffer.readPoint(); - pts[1] = buffer.readPoint(); - return SkGradientShader::MakeLinear(pts, desc.fColors, std::move(desc.fColorSpace), desc.fPos, - desc.fCount, desc.fTileMode, desc.fGradFlags, - desc.fLocalMatrix); -} - -void SkLinearGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fStart); - buffer.writePoint(fEnd); -} - -SkShaderBase::Context* SkLinearGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - return use_4f_context(rec, fGradFlags) - ? CheckedMakeContext<LinearGradient4fContext>(alloc, *this, rec) - : CheckedMakeContext< LinearGradientContext>(alloc, *this, rec); -} - -bool SkLinearGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { - *matrix = SkMatrix::Concat(fPtsToUnit, *matrix); - // If the gradient is less than a quarter of a pixel, this falls into the - // subpixel gradient code handled on a different path. - SkVector dx = matrix->mapVector(1, 0); - if (dx.fX >= 4) { - return false; - } - return true; -} - -sk_sp<SkShader> SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkPoint pts[2] = { fStart, fEnd }; - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeLinear(pts, xformedColors.begin(), fOrigPos, fColorCount, - fTileMode, fGradFlags, &this->getLocalMatrix()); -} - -// This swizzles SkColor into the same component order as SkPMColor, but does not actually -// "pre" multiply the color components. -// -// This allows us to map directly to Sk4f, and eventually scale down to bytes to output a -// SkPMColor from the floats, without having to swizzle each time. -// -static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) { - return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); -} - -SkLinearGradient::LinearGradientContext::LinearGradientContext( - const SkLinearGradient& shader, const ContextRec& ctx) - : INHERITED(shader, ctx) -{ - // setup for Sk4f - const int count = shader.fColorCount; - SkASSERT(count > 1); - - fRecs.setCount(count); - Rec* rec = fRecs.begin(); - if (shader.fOrigPos) { - rec[0].fPos = 0; - SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used - for (int i = 1; i < count; ++i) { - rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f); - float diff = rec[i].fPos - rec[i - 1].fPos; - if (diff > 0) { - rec[i].fPosScale = 1.0f / diff; - } else { - rec[i].fPosScale = 0; - } - } - } else { - // no pos specified, so we compute evenly spaced values - const float scale = float(count - 1); - const float invScale = 1.0f / scale; - for (int i = 0; i < count; ++i) { - rec[i].fPos = i * invScale; - rec[i].fPosScale = scale; - } - } - rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0 - - fApplyAlphaAfterInterp = true; - if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag) || - shader.colorsAreOpaque()) - { - fApplyAlphaAfterInterp = false; - } - - if (fApplyAlphaAfterInterp) { - // Our fColor values are in PMColor order, but are still unpremultiplied, allowing us to - // interpolate in unpremultiplied space first, and then scale by alpha right before we - // convert to SkPMColor bytes. - const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float; - const Sk4f scale(1, 1, 1, paintAlpha); - for (int i = 0; i < count; ++i) { - uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]); - rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&c)) * scale; - if (i > 0) { - SkASSERT(rec[i - 1].fPos <= rec[i].fPos); - } - } - } else { - // Our fColor values are premultiplied, so converting to SkPMColor is just a matter - // of converting the floats down to bytes. - unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >> 7); - for (int i = 0; i < count; ++i) { - SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]); - pmc = SkAlphaMulQ(pmc, alphaScale); - rec[i].fColor = SkNx_cast<float>(Sk4b::Load(&pmc)); - if (i > 0) { - SkASSERT(rec[i - 1].fPos <= rec[i].fPos); - } - } - } -} - -#define NO_CHECK_ITER \ - do { \ - unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \ - SkASSERT(fi <= 0xFF); \ - fx += dx; \ - *dstC++ = cache[toggle + fi]; \ - toggle = next_dither_toggle(toggle); \ - } while (0) - -namespace { - -typedef void (*LinearShadeProc)(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* dstC, const SkPMColor* cache, - int toggle, int count); - -// Linear interpolation (lerp) is unnecessary if there are no sharp -// discontinuities in the gradient - which must be true if there are -// only 2 colors - but it's cheap. -void shadeSpan_linear_vertical_lerp(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - // We're a vertical gradient, so no change in a span. - // If colors change sharply across the gradient, dithering is - // insufficient (it subsamples the color space) and we need to lerp. - unsigned fullIndex = proc(SkGradFixedToFixed(fx)); - unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift; - unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1); - - int index0 = fi + toggle; - int index1 = index0; - if (fi < SkGradientShaderBase::kCache32Count - 1) { - index1 += 1; - } - SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); - index0 ^= SkGradientShaderBase::kDitherStride32; - index1 ^= SkGradientShaderBase::kDitherStride32; - SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); - sk_memset32_dither(dstC, lerp, dlerp, count); -} - -void shadeSpan_linear_clamp(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - SkClampRange range; - range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); - range.validate(count); - - if ((count = range.fCount0) > 0) { - sk_memset32_dither(dstC, - cache[toggle + range.fV0], - cache[next_dither_toggle(toggle) + range.fV0], - count); - dstC += count; - } - if ((count = range.fCount1) > 0) { - int unroll = count >> 3; - fx = range.fFx1; - for (int i = 0; i < unroll; i++) { - NO_CHECK_ITER; NO_CHECK_ITER; - NO_CHECK_ITER; NO_CHECK_ITER; - NO_CHECK_ITER; NO_CHECK_ITER; - NO_CHECK_ITER; NO_CHECK_ITER; - } - if ((count &= 7) > 0) { - do { - NO_CHECK_ITER; - } while (--count != 0); - } - } - if ((count = range.fCount2) > 0) { - sk_memset32_dither(dstC, - cache[toggle + range.fV1], - cache[next_dither_toggle(toggle) + range.fV1], - count); - } -} - -void shadeSpan_linear_mirror(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - do { - unsigned fi = mirror_8bits(SkGradFixedToFixed(fx) >> 8); - SkASSERT(fi <= 0xFF); - fx += dx; - *dstC++ = cache[toggle + fi]; - toggle = next_dither_toggle(toggle); - } while (--count != 0); -} - -void shadeSpan_linear_repeat(TileProc proc, SkGradFixed dx, SkGradFixed fx, - SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, - int toggle, int count) { - do { - unsigned fi = repeat_8bits(SkGradFixedToFixed(fx) >> 8); - SkASSERT(fi <= 0xFF); - fx += dx; - *dstC++ = cache[toggle + fi]; - toggle = next_dither_toggle(toggle); - } while (--count != 0); -} - -} - -void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, - int count) { - SkASSERT(count > 0); - const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader); - - if (SkShader::kClamp_TileMode == linearGradient.fTileMode && - kLinear_MatrixClass == fDstToIndexClass) - { - this->shade4_clamp(x, y, dstC, count); - return; - } - - SkPoint srcPt; - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - TileProc proc = linearGradient.fTileProc; - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - int toggle = init_dither_toggle(x, y); - - if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkGradFixed dx, fx = SkScalarPinToGradFixed(srcPt.fX); - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y)); - // todo: do we need a real/high-precision value for dx here? - dx = SkScalarPinToGradFixed(step.fX); - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = SkScalarPinToGradFixed(fDstToIndex.getScaleX()); - } - - LinearShadeProc shadeProc = shadeSpan_linear_repeat; - if (0 == dx) { - shadeProc = shadeSpan_linear_vertical_lerp; - } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) { - shadeProc = shadeSpan_linear_clamp; - } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) { - shadeProc = shadeSpan_linear_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode); - } - (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); - } else { - SkScalar dstX = SkIntToScalar(x); - SkScalar dstY = SkIntToScalar(y); - do { - dstProc(fDstToIndex, dstX, dstY, &srcPt); - unsigned fi = proc(SkScalarToFixed(srcPt.fX)); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[toggle + (fi >> kCache32Shift)]; - toggle = next_dither_toggle(toggle); - dstX += SK_Scalar1; - } while (--count != 0); - } -} - -SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { - if (info) { - commonAsAGradient(info); - info->fPoint[0] = fStart; - info->fPoint[1] = fEnd; - } - return kLinear_GradientType; -} - -#if SK_SUPPORT_GPU - -#include "GrColorSpaceXform.h" -#include "GrShaderCaps.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "SkGr.h" - -///////////////////////////////////////////////////////////////////// - -class GrLinearGradient : public GrGradientEffect { -public: - class GLSLLinearProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new GrLinearGradient(args)); - } - - ~GrLinearGradient() override {} - - const char* name() const override { return "Linear Gradient"; } - -private: - GrLinearGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { - this->initClassID<GrLinearGradient>(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - typedef GrGradientEffect INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrLinearGradient::GLSLLinearProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLLinearProcessor(const GrProcessor&) {} - - ~GLSLLinearProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); - } - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -GrGLSLFragmentProcessor* GrLinearGradient::onCreateGLSLInstance() const { - return new GrLinearGradient::GLSLLinearProcessor(*this); -} - -void GrLinearGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrLinearGradient::GLSLLinearProcessor::GenKey(*this, caps, b); -} - -///////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradient); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrLinearGradient::TestCreate(GrProcessorTestData* d) { - SkPoint points[] = {{d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}, - {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}}; - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeLinear(points, params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeLinear(points, params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -///////////////////////////////////////////////////////////////////// - -void GrLinearGradient::GLSLLinearProcessor::emitCode(EmitArgs& args) { - const GrLinearGradient& ge = args.fFp.cast<GrLinearGradient>(); - this->emitUniforms(args.fUniformHandler, ge); - SkString t = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - t.append(".x"); - this->emitColor(args.fFragBuilder, - args.fUniformHandler, - args.fShaderCaps, - ge, - t.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(const AsFPArgs& args) const { - SkASSERT(args.fContext); - - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - matrix.postConcat(fPtsToUnit); - - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(GrLinearGradient::Make( - GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - - -#endif - -#ifndef SK_IGNORE_TO_STRING -void SkLinearGradient::toString(SkString* str) const { - str->append("SkLinearGradient ("); - - str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); - str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#include "SkNx.h" - -static const SkLinearGradient::LinearGradientContext::Rec* -find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) { - SkASSERT(tiledX >= 0 && tiledX <= 1); - - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - rec += 1; - while (rec->fPos < tiledX || rec->fPosScale == 0) { - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - rec += 1; - } - return rec - 1; -} - -static const SkLinearGradient::LinearGradientContext::Rec* -find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float tiledX) { - SkASSERT(tiledX >= 0 && tiledX <= 1); - - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - while (tiledX < rec->fPos || rec[1].fPosScale == 0) { - rec -= 1; - SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); - SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); - SkASSERT(rec[0].fPos <= rec[1].fPos); - } - return rec; -} - -// As an optimization, we can apply the dither bias before interpolation -- but only when -// operating in premul space (apply_alpha == false). When apply_alpha == true, we must -// defer the bias application until after premul. -// -// The following two helpers encapsulate this logic: pre_bias is called before interpolation, -// and effects the bias when apply_alpha == false, while post_bias is called after premul and -// effects the bias for the apply_alpha == true case. - -template <bool apply_alpha> -Sk4f pre_bias(const Sk4f& x, const Sk4f& bias) { - return apply_alpha ? x : x + bias; -} - -template <bool apply_alpha> -Sk4f post_bias(const Sk4f& x, const Sk4f& bias) { - return apply_alpha ? x + bias : x; -} - -template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x, const Sk4f& bias) { - SkPMColor c; - Sk4f c4f255 = x; - if (apply_alpha) { - const float scale = x[SkPM4f::A] * (1 / 255.f); - c4f255 *= Sk4f(scale, scale, scale, 1); - } - SkNx_cast<uint8_t>(post_bias<apply_alpha>(c4f255, bias)).store(&c); - - return c; -} - -template <bool apply_alpha> void fill(SkPMColor dst[], int count, - const Sk4f& c4, const Sk4f& bias0, const Sk4f& bias1) { - const SkPMColor c0 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias0), bias0); - const SkPMColor c1 = trunc_from_255<apply_alpha>(pre_bias<apply_alpha>(c4, bias1), bias1); - sk_memset32_dither(dst, c0, c1, count); -} - -template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4) { - // Assumes that c4 does not need to be dithered. - sk_memset32(dst, trunc_from_255<apply_alpha>(c4, 0), count); -} - -/* - * TODOs - * - * - tilemodes - * - interp before or after premul - * - perspective - * - optimizations - * - use fixed (32bit or 16bit) instead of floats? - */ - -static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::Rec* rec) { - SkASSERT(fx >= rec[0].fPos); - SkASSERT(fx <= rec[1].fPos); - - const float p0 = rec[0].fPos; - const Sk4f c0 = rec[0].fColor; - const Sk4f c1 = rec[1].fColor; - const Sk4f diffc = c1 - c0; - const float scale = rec[1].fPosScale; - const float t = (fx - p0) * scale; - return c0 + Sk4f(t) * diffc; -} - -template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, const Sk4f& dc, - const Sk4f& dither0, const Sk4f& dither1) { - Sk4f dc2 = dc + dc; - Sk4f dc4 = dc2 + dc2; - Sk4f cd0 = pre_bias<apply_alpha>(c , dither0); - Sk4f cd1 = pre_bias<apply_alpha>(c + dc, dither1); - Sk4f cd2 = cd0 + dc2; - Sk4f cd3 = cd1 + dc2; - while (n >= 4) { - if (!apply_alpha) { - Sk4f_ToBytes((uint8_t*)dstC, cd0, cd1, cd2, cd3); - dstC += 4; - } else { - *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0); - *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1); - *dstC++ = trunc_from_255<apply_alpha>(cd2, dither0); - *dstC++ = trunc_from_255<apply_alpha>(cd3, dither1); - } - cd0 = cd0 + dc4; - cd1 = cd1 + dc4; - cd2 = cd2 + dc4; - cd3 = cd3 + dc4; - n -= 4; - } - if (n & 2) { - *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0); - *dstC++ = trunc_from_255<apply_alpha>(cd1, dither1); - cd0 = cd0 + dc2; - } - if (n & 1) { - *dstC++ = trunc_from_255<apply_alpha>(cd0, dither0); - } -} - -template <bool apply_alpha, bool dx_is_pos> -void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[], int count, - float fx, float dx, float invDx, - const float dither[2]) { - Sk4f dither0(dither[0]); - Sk4f dither1(dither[1]); - const Rec* rec = fRecs.begin(); - - const Sk4f dx4 = Sk4f(dx); - SkDEBUGCODE(SkPMColor* endDstC = dstC + count;) - - if (dx_is_pos) { - if (fx < 0) { - // count is guaranteed to be positive, but the first arg may overflow int32 after - // increment => casting to uint32 ensures correct clamping. - int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor(-fx * invDx)) + 1, - count); - SkASSERT(n > 0); - fill<apply_alpha>(dstC, n, rec[0].fColor); - count -= n; - dstC += n; - fx += n * dx; - SkASSERT(0 == count || fx >= 0); - if (n & 1) { - SkTSwap(dither0, dither1); - } - } - } else { // dx < 0 - if (fx > 1) { - // count is guaranteed to be positive, but the first arg may overflow int32 after - // increment => casting to uint32 ensures correct clamping. - int n = SkTMin<uint32_t>(static_cast<uint32_t>(SkFloatToIntFloor((1 - fx) * invDx)) + 1, - count); - SkASSERT(n > 0); - fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor); - count -= n; - dstC += n; - fx += n * dx; - SkASSERT(0 == count || fx <= 1); - if (n & 1) { - SkTSwap(dither0, dither1); - } - } - } - SkASSERT(count >= 0); - - const Rec* r; - if (dx_is_pos) { - r = fRecs.begin(); // start at the beginning - } else { - r = fRecs.begin() + fRecs.count() - 2; // start at the end - } - - while (count > 0) { - if (dx_is_pos) { - if (fx >= 1) { - fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor); - return; - } - } else { // dx < 0 - if (fx <= 0) { - fill<apply_alpha>(dstC, count, rec[0].fColor); - return; - } - } - - if (dx_is_pos) { - r = find_forward(r, fx); - } else { - r = find_backward(r, fx); - } - SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1); - - const float p0 = r[0].fPos; - const Sk4f c0 = r[0].fColor; - const float p1 = r[1].fPos; - const Sk4f diffc = Sk4f(r[1].fColor) - c0; - const float scale = r[1].fPosScale; - const float t = (fx - p0) * scale; - const Sk4f c = c0 + Sk4f(t) * diffc; - const Sk4f dc = diffc * dx4 * Sk4f(scale); - - int n; - if (dx_is_pos) { - n = SkTMin((int)((p1 - fx) * invDx) + 1, count); - } else { - n = SkTMin((int)((p0 - fx) * invDx) + 1, count); - } - - fx += n * dx; - // fx should now outside of the p0..p1 interval. However, due to float precision loss, - // its possible that fx is slightly too small/large, so we clamp it. - if (dx_is_pos) { - fx = SkTMax(fx, p1); - } else { - fx = SkTMin(fx, p0); - } - - ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1); - dstC += n; - SkASSERT(dstC <= endDstC); - - if (n & 1) { - SkTSwap(dither0, dither1); - } - - count -= n; - SkASSERT(count >= 0); - } -} - -void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMColor dstC[], - int count) { - SkASSERT(count > 0); - SkASSERT(kLinear_MatrixClass == fDstToIndexClass); - - SkPoint srcPt; - fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt); - float fx = srcPt.x(); - const float dx = fDstToIndex.getScaleX(); - - // Default our dither bias values to 1/2, (rounding), which is no dithering - float dither0 = 0.5f; - float dither1 = 0.5f; - if (fDither) { - const float ditherCell[] = { - 1/8.0f, 5/8.0f, - 7/8.0f, 3/8.0f, - }; - const int rowIndex = (y & 1) << 1; - dither0 = ditherCell[rowIndex]; - dither1 = ditherCell[rowIndex + 1]; - if (x & 1) { - SkTSwap(dither0, dither1); - } - } - const float dither[2] = { dither0, dither1 }; - - if (SkScalarNearlyZero(dx * count)) { // gradient is vertical - const float pinFx = SkTPin(fx, 0.0f, 1.0f); - Sk4f c = lerp_color(pinFx, find_forward(fRecs.begin(), pinFx)); - if (fApplyAlphaAfterInterp) { - fill<true>(dstC, count, c, dither0, dither1); - } else { - fill<false>(dstC, count, c, dither0, dither1); - } - return; - } - - SkASSERT(0.f != dx); - const float invDx = 1 / dx; - if (dx > 0) { - if (fApplyAlphaAfterInterp) { - this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither); - } else { - this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dither); - } - } else { - if (fApplyAlphaAfterInterp) { - this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dither); - } else { - this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dither); - } - } -} diff --git a/src/shaders/gradients/SkLinearGradient.h b/src/shaders/gradients/SkLinearGradient.h deleted file mode 100644 index 19a965c7bb..0000000000 --- a/src/shaders/gradients/SkLinearGradient.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLinearGradient_DEFINED -#define SkLinearGradient_DEFINED - -#include "SkGradientShaderPriv.h" -#include "SkNx.h" - -struct Sk4fStorage { - float fArray[4]; - - operator Sk4f() const { - return Sk4f::Load(fArray); - } - - Sk4fStorage& operator=(const Sk4f& src) { - src.store(fArray); - return *this; - } -}; - -class SkLinearGradient : public SkGradientShaderBase { -public: - enum { - // Temp flag for testing the 4f impl. - kForce4fContext_PrivateFlag = 1 << 7, - }; - - SkLinearGradient(const SkPoint pts[2], const Descriptor&); - - class LinearGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - LinearGradientContext(const SkLinearGradient&, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - struct Rec { - Sk4fStorage fColor; - float fPos; - float fPosScale; - }; - private: - SkTDArray<Rec> fRecs; - bool fApplyAlphaAfterInterp; - - void shade4_clamp(int x, int y, SkPMColor dstC[], int count); - template <bool, bool> void shade4_dx_clamp(SkPMColor dstC[], int count, float fx, float dx, - float invDx, const float dither[2]); - - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient) - -protected: - SkLinearGradient(SkReadBuffer& buffer); - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - - bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const final; - - - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - class LinearGradient4fContext; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; - const SkPoint fStart; - const SkPoint fEnd; -}; - -#endif diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp deleted file mode 100644 index d49b3dd8e1..0000000000 --- a/src/shaders/gradients/SkRadialGradient.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColorSpaceXformer.h" -#include "SkRadialGradient.h" -#include "SkNx.h" - -namespace { - -// GCC doesn't like using static functions as template arguments. So force these to be non-static. -inline SkFixed mirror_tileproc_nonstatic(SkFixed x) { - return mirror_tileproc(x); -} - -inline SkFixed repeat_tileproc_nonstatic(SkFixed x) { - return repeat_tileproc(x); -} - -SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) { - SkScalar inv = SkScalarInvert(radius); - - SkMatrix matrix; - matrix.setTranslate(-center.fX, -center.fY); - matrix.postScale(inv, inv); - return matrix; -} - - -} // namespace - -///////////////////////////////////////////////////////////////////// - -SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc) - : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius)) - , fCenter(center) - , fRadius(radius) { -} - -SkShaderBase::Context* SkRadialGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const -{ - return CheckedMakeContext<RadialGradientContext>(alloc, *this, rec); -} - -SkRadialGradient::RadialGradientContext::RadialGradientContext( - const SkRadialGradient& shader, const ContextRec& rec) - : INHERITED(shader, rec) {} - -SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { - if (info) { - commonAsAGradient(info); - info->fPoint[0] = fCenter; - info->fRadius[0] = fRadius; - } - return kRadial_GradientType; -} - -sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - const SkPoint center = buffer.readPoint(); - const SkScalar radius = buffer.readScalar(); - return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace), - desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags, - desc.fLocalMatrix); -} - -void SkRadialGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fCenter); - buffer.writeScalar(fRadius); -} - -namespace { - -inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) { - // fast, overly-conservative test: checks unit square instead of unit circle - bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0); - bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0); - return xClamped || yClamped; -} - -typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, - SkScalar sfy, SkScalar sdy, - SkPMColor* dstC, const SkPMColor* cache, - int count, int toggle); - -static inline Sk4f fast_sqrt(const Sk4f& R) { - return R * R.rsqrt(); -} - -static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) { - return a * a + b * b; -} - -void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - if (radial_completely_pinned(sfx, sdx, sfy, sdy)) { - unsigned fi = SkGradientShaderBase::kCache32Count - 1; - sk_memset32_dither(dstC, - cache[toggle + fi], - cache[next_dither_toggle(toggle) + fi], - count); - } else { - const Sk4f min(SK_ScalarNearlyZero); - const Sk4f max(255); - const float scale = 255; - sfx *= scale; - sfy *= scale; - sdx *= scale; - sdy *= scale; - const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx); - const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy); - const Sk4f dx4(sdx * 4); - const Sk4f dy4(sdy * 4); - - Sk4f tmpxy = fx4 * dx4 + fy4 * dy4; - Sk4f tmpdxdy = sum_squares(dx4, dy4); - Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min); - Sk4f dR = tmpxy + tmpxy + tmpdxdy; - const Sk4f ddR = tmpdxdy + tmpdxdy; - - for (int i = 0; i < (count >> 2); ++i) { - Sk4f dist = Sk4f::Min(fast_sqrt(R), max); - R = Sk4f::Max(R + dR, min); - dR = dR + ddR; - - uint8_t fi[4]; - SkNx_cast<uint8_t>(dist).store(fi); - - for (int i = 0; i < 4; i++) { - *dstC++ = cache[toggle + fi[i]]; - toggle = next_dither_toggle(toggle); - } - } - count &= 3; - if (count) { - Sk4f dist = Sk4f::Min(fast_sqrt(R), max); - - uint8_t fi[4]; - SkNx_cast<uint8_t>(dist).store(fi); - for (int i = 0; i < count; i++) { - *dstC++ = cache[toggle + fi[i]]; - toggle = next_dither_toggle(toggle); - } - } - } -} - -// Unrolling this loop doesn't seem to help (when float); we're stalling to -// get the results of the sqrt (?), and don't have enough extra registers to -// have many in flight. -template <SkFixed (*TileProc)(SkFixed)> -void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - do { - const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); - const unsigned fi = TileProc(dist); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; - toggle = next_dither_toggle(toggle); - fx += dx; - fy += dy; - } while (--count != 0); -} - -void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); -} - -void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, - SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, - int count, int toggle) { - shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); -} - -} // namespace - -void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y, - SkPMColor* SK_RESTRICT dstC, int count) { - SkASSERT(count > 0); - - const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); - - SkPoint srcPt; - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - TileProc proc = radialGradient.fTileProc; - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - int toggle = init_dither_toggle(x, y); - - if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkScalar sdx = fDstToIndex.getScaleX(); - SkScalar sdy = fDstToIndex.getSkewY(); - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y)); - sdx = step.fX; - sdy = step.fY; - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - } - - RadialShadeProc shadeProc = shadeSpan_radial_repeat; - if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { - shadeProc = shadeSpan_radial_clamp2; - } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { - shadeProc = shadeSpan_radial_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); - } - (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); - } else { // perspective case - SkScalar dstX = SkIntToScalar(x); - SkScalar dstY = SkIntToScalar(y); - do { - dstProc(fDstToIndex, dstX, dstY, &srcPt); - unsigned fi = proc(SkScalarToFixed(srcPt.length())); - SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift]; - dstX += SK_Scalar1; - } while (--count != 0); - } -} - -///////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "GrShaderCaps.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" - -class GrRadialGradient : public GrGradientEffect { -public: - class GLSLRadialProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new GrRadialGradient(args)); - } - - ~GrRadialGradient() override {} - - const char* name() const override { return "Radial Gradient"; } - -private: - GrRadialGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { - this->initClassID<GrRadialGradient>(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - typedef GrGradientEffect INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLRadialProcessor(const GrProcessor&) {} - ~GLSLRadialProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); - } - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -///////////////////////////////////////////////////////////////////// - -GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const { - return new GrRadialGradient::GLSLRadialProcessor(*this); -} - -void GrRadialGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b); -} - -///////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) { - sk_sp<SkShader> shader; - do { - RandomGradientParams params(d->fRandom); - SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius = d->fRandom->nextUScalar1(); - shader = params.fUseColors4f - ? SkGradientShader::MakeRadial(center, radius, params.fColors4f, - params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) - : SkGradientShader::MakeRadial(center, radius, params.fColors, - params.fStops, params.fColorCount, - params.fTileMode); - } while (!shader); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -///////////////////////////////////////////////////////////////////// - -void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) { - const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>(); - this->emitUniforms(args.fUniformHandler, ge); - SkString t("length("); - t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0])); - t.append(")"); - this->emitColor(args.fFragBuilder, - args.fUniformHandler, - args.fShaderCaps, - ge, t.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const { - SkASSERT(args.fContext); - - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - matrix.postConcat(fPtsToUnit); - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make( - GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - -#endif - -sk_sp<SkShader> SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.begin(), fOrigPos, - fColorCount, fTileMode, fGradFlags, - &this->getLocalMatrix()); -} - -bool SkRadialGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { - matrix->postTranslate(-fCenter.fX, -fCenter.fY); - matrix->postScale(1/fRadius, 1/fRadius); - - p->append(SkRasterPipeline::xy_to_radius); - return true; -} - -#ifndef SK_IGNORE_TO_STRING -void SkRadialGradient::toString(SkString* str) const { - str->append("SkRadialGradient: ("); - - str->append("center: ("); - str->appendScalar(fCenter.fX); - str->append(", "); - str->appendScalar(fCenter.fY); - str->append(") radius: "); - str->appendScalar(fRadius); - str->append(" "); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/src/shaders/gradients/SkRadialGradient.h b/src/shaders/gradients/SkRadialGradient.h deleted file mode 100644 index 69ec4b1285..0000000000 --- a/src/shaders/gradients/SkRadialGradient.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkRadialGradient_DEFINED -#define SkRadialGradient_DEFINED - -#include "SkGradientShaderPriv.h" - -class SkRadialGradient : public SkGradientShaderBase { -public: - SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&); - - class RadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - RadialGradientContext(const SkRadialGradient&, const ContextRec&); - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - private: - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - GradientType asAGradient(GradientInfo* info) const override; -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient) - -protected: - SkRadialGradient(SkReadBuffer& buffer); - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - - bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const final; - -private: - const SkPoint fCenter; - const SkScalar fRadius; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp deleted file mode 100644 index a138a8e213..0000000000 --- a/src/shaders/gradients/SkSweepGradient.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColorSpaceXformer.h" -#include "SkSweepGradient.h" - -#include "SkPM4fPriv.h" -#include "SkRasterPipeline.h" - -static SkMatrix translate(SkScalar dx, SkScalar dy) { - SkMatrix matrix; - matrix.setTranslate(dx, dy); - return matrix; -} - -SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor& desc) - : SkGradientShaderBase(desc, translate(-cx, -cy)) - , fCenter(SkPoint::Make(cx, cy)) -{ - // overwrite the tilemode to a canonical value (since sweep ignores it) - fTileMode = SkShader::kClamp_TileMode; -} - -SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const { - if (info) { - commonAsAGradient(info); - info->fPoint[0] = fCenter; - } - return kSweep_GradientType; -} - -sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - const SkPoint center = buffer.readPoint(); - return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors, - std::move(desc.fColorSpace), desc.fPos, desc.fCount, - desc.fGradFlags, desc.fLocalMatrix); -} - -void SkSweepGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fCenter); -} - -///////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "GrShaderCaps.h" -#include "gl/GrGLContext.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" - -class GrSweepGradient : public GrGradientEffect { -public: - class GLSLSweepProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new GrSweepGradient(args)); - } - ~GrSweepGradient() override {} - - const char* name() const override { return "Sweep Gradient"; } - -private: - GrSweepGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { - this->initClassID<GrSweepGradient>(); - } - - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - typedef GrGradientEffect INHERITED; -}; - -///////////////////////////////////////////////////////////////////// - -class GrSweepGradient::GLSLSweepProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLSweepProcessor(const GrProcessor&) {} - ~GLSLSweepProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); - } - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -///////////////////////////////////////////////////////////////////// - -GrGLSLFragmentProcessor* GrSweepGradient::onCreateGLSLInstance() const { - return new GrSweepGradient::GLSLSweepProcessor(*this); -} - -void GrSweepGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrSweepGradient::GLSLSweepProcessor::GenKey(*this, caps, b); -} - - -///////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSweepGradient); - -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> GrSweepGradient::TestCreate(GrProcessorTestData* d) { - SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors4f, params.fColorSpace, - params.fStops, params.fColorCount) : - SkGradientShader::MakeSweep(center.fX, center.fY, params.fColors, - params.fStops, params.fColorCount); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -///////////////////////////////////////////////////////////////////// - -void GrSweepGradient::GLSLSweepProcessor::emitCode(EmitArgs& args) { - const GrSweepGradient& ge = args.fFp.cast<GrSweepGradient>(); - this->emitUniforms(args.fUniformHandler, ge); - SkString coords2D = args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - SkString t; - // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi] - if (args.fShaderCaps->atan2ImplementedAsAtanYOverX()) { - // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is - // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in - // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device - // handle the undefined behavior of the second paramenter being 0 instead of doing the - // divide ourselves and using atan instead. - t.printf("(2.0 * atan(- %s.y, length(%s) - %s.x) * 0.1591549430918 + 0.5)", - coords2D.c_str(), coords2D.c_str(), coords2D.c_str()); - } else { - t.printf("(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5)", - coords2D.c_str(), coords2D.c_str()); - } - this->emitColor(args.fFragBuilder, - args.fUniformHandler, - args.fShaderCaps, - ge, t.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -///////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs& args) const { - - SkMatrix matrix; - if (!this->getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fLocalMatrix) { - SkMatrix inv; - if (!args.fLocalMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - matrix.postConcat(fPtsToUnit); - - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(GrSweepGradient::Make( - GrGradientEffect::CreateArgs(args.fContext, this, &matrix, SkShader::kClamp_TileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - -#endif - -sk_sp<SkShader> SkSweepGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> xformedColors(fColorCount); - xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); - return SkGradientShader::MakeSweep(fCenter.fX, fCenter.fY, xformedColors.begin(), fOrigPos, - fColorCount, fGradFlags, &this->getLocalMatrix()); -} - -#ifndef SK_IGNORE_TO_STRING -void SkSweepGradient::toString(SkString* str) const { - str->append("SkSweepGradient: ("); - - str->append("center: ("); - str->appendScalar(fCenter.fX); - str->append(", "); - str->appendScalar(fCenter.fY); - str->append(") "); - - this->INHERITED::toString(str); - - str->append(")"); -} - -bool SkSweepGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const { - matrix->postTranslate(-fCenter.fX, -fCenter.fY); - p->append(SkRasterPipeline::xy_to_unit_angle); - - return true; -} - -#endif diff --git a/src/shaders/gradients/SkSweepGradient.h b/src/shaders/gradients/SkSweepGradient.h deleted file mode 100644 index c30ca4d916..0000000000 --- a/src/shaders/gradients/SkSweepGradient.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkSweepGradient_DEFINED -#define SkSweepGradient_DEFINED - -#include "SkGradientShaderPriv.h" - -class SkSweepGradient final : public SkGradientShaderBase { -public: - SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&); - - GradientType asAGradient(GradientInfo* info) const override; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient) - -protected: - void flatten(SkWriteBuffer& buffer) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - - bool adjustMatrixAndAppendStages(SkArenaAlloc* alloc, - SkMatrix* matrix, - SkRasterPipeline* p) const override; - - bool isRasterPipelineOnly() const override { return true; } - -private: - const SkPoint fCenter; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp deleted file mode 100644 index 4549527d51..0000000000 --- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTwoPointConicalGradient.h" - -struct TwoPtRadialContext { - const TwoPtRadial& fRec; - float fRelX, fRelY; - const float fIncX, fIncY; - float fB; - const float fDB; - - TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy, - SkScalar dfx, SkScalar dfy); - SkFixed nextT(); -}; - -static int valid_divide(float numer, float denom, float* ratio) { - SkASSERT(ratio); - if (0 == denom) { - return 0; - } - *ratio = numer / denom; - return 1; -} - -// 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], bool descendingOrder = false) { - SkASSERT(roots); - - if (A == 0) { - return valid_divide(-C, B, roots); - } - - float R = B*B - 4*A*C; - if (R < 0) { - return 0; - } - R = sk_float_sqrt(R); - -#if 1 - float Q = B; - if (Q < 0) { - Q -= R; - } else { - Q += R; - } -#else - // on 10.6 this was much slower than the above branch :( - float Q = B + copysignf(R, B); -#endif - Q *= -0.5f; - if (0 == Q) { - roots[0] = 0; - return 1; - } - - float r0 = Q / A; - 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; -} - -static float lerp(float x, float dx, float t) { - return x + t * dx; -} - -static float sqr(float x) { return x * x; } - -void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, - const SkPoint& center1, SkScalar rad1, - bool flipped) { - fCenterX = SkScalarToFloat(center0.fX); - fCenterY = SkScalarToFloat(center0.fY); - fDCenterX = SkScalarToFloat(center1.fX) - fCenterX; - fDCenterY = SkScalarToFloat(center1.fY) - fCenterY; - fRadius = SkScalarToFloat(rad0); - fDRadius = SkScalarToFloat(rad1) - fRadius; - - fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius); - fRadius2 = sqr(fRadius); - fRDR = fRadius * fDRadius; - - fFlipped = flipped; -} - -TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy, - SkScalar dfx, SkScalar dfy) - : fRec(rec) - , fRelX(SkScalarToFloat(fx) - rec.fCenterX) - , fRelY(SkScalarToFloat(fy) - rec.fCenterY) - , fIncX(SkScalarToFloat(dfx)) - , fIncY(SkScalarToFloat(dfy)) - , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR)) - , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {} - -SkFixed TwoPtRadialContext::nextT() { - float roots[2]; - - float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2; - int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped); - - fRelX += fIncX; - fRelY += fIncY; - fB += fDB; - - if (0 == countRoots) { - return TwoPtRadial::kDontDrawT; - } - - // Prefer the bigger t value if both give a radius(t) > 0 - // find_quad_roots returns the values sorted, so we start with the last - float t = roots[countRoots - 1]; - float r = lerp(fRec.fRadius, fRec.fDRadius, t); - if (r < 0) { - t = roots[0]; // might be the same as roots[countRoots-1] - r = lerp(fRec.fRadius, fRec.fDRadius, t); - if (r < 0) { - return TwoPtRadial::kDontDrawT; - } - } - return SkFloatToFixed(t); -} - -typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC, - const SkPMColor* cache, int toggle, int count); - -static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, int toggle, - int count) { - for (; count > 0; --count) { - SkFixed t = rec->nextT(); - if (TwoPtRadial::DontDrawT(t)) { - *dstC++ = 0; - } else { - SkFixed index = SkClampMax(t, 0xFFFF); - SkASSERT(index <= 0xFFFF); - *dstC++ = cache[toggle + - (index >> SkGradientShaderBase::kCache32Shift)]; - } - toggle = next_dither_toggle(toggle); - } -} - -static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, int toggle, - int count) { - for (; count > 0; --count) { - SkFixed t = rec->nextT(); - if (TwoPtRadial::DontDrawT(t)) { - *dstC++ = 0; - } else { - SkFixed index = repeat_tileproc(t); - SkASSERT(index <= 0xFFFF); - *dstC++ = cache[toggle + - (index >> SkGradientShaderBase::kCache32Shift)]; - } - toggle = next_dither_toggle(toggle); - } -} - -static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, - const SkPMColor* SK_RESTRICT cache, int toggle, - int count) { - for (; count > 0; --count) { - SkFixed t = rec->nextT(); - if (TwoPtRadial::DontDrawT(t)) { - *dstC++ = 0; - } else { - SkFixed index = mirror_tileproc(t); - SkASSERT(index <= 0xFFFF); - *dstC++ = cache[toggle + - (index >> SkGradientShaderBase::kCache32Shift)]; - } - toggle = next_dither_toggle(toggle); - } -} - -///////////////////////////////////////////////////////////////////// - -SkTwoPointConicalGradient::SkTwoPointConicalGradient( - const SkPoint& start, SkScalar startRadius, - const SkPoint& end, SkScalar endRadius, - bool flippedGrad, const Descriptor& desc) - : SkGradientShaderBase(desc, SkMatrix::I()) - , fCenter1(start) - , fCenter2(end) - , fRadius1(startRadius) - , fRadius2(endRadius) - , fFlippedGrad(flippedGrad) -{ - // this is degenerate, and should be caught by our caller - SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); - fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad); -} - -bool SkTwoPointConicalGradient::isOpaque() const { - // Because areas outside the cone are left untouched, we cannot treat the - // shader as opaque even if the gradient itself is opaque. - // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 - return false; -} - -SkShaderBase::Context* SkTwoPointConicalGradient::onMakeContext( - const ContextRec& rec, SkArenaAlloc* alloc) const { - return CheckedMakeContext<TwoPointConicalGradientContext>(alloc, *this, rec); -} - -SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext( - const SkTwoPointConicalGradient& shader, const ContextRec& rec) - : INHERITED(shader, rec) -{ - // in general, we might discard based on computed-radius, so clear - // this flag (todo: sometimes we can detect that we never discard...) - fFlags &= ~kOpaqueAlpha_Flag; -} - -void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan( - int x, int y, SkPMColor* dstCParam, int count) { - const SkTwoPointConicalGradient& twoPointConicalGradient = - static_cast<const SkTwoPointConicalGradient&>(fShader); - - int toggle = init_dither_toggle(x, y); - - SkASSERT(count > 0); - - SkPMColor* SK_RESTRICT dstC = dstCParam; - - SkMatrix::MapXYProc dstProc = fDstToIndexProc; - - const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); - - TwoPointConicalProc shadeProc = twopoint_repeat; - if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) { - shadeProc = twopoint_clamp; - } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) { - shadeProc = twopoint_mirror; - } else { - SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode); - } - - if (fDstToIndexClass != kPerspective_MatrixClass) { - SkPoint srcPt; - dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - SkScalar dx, fx = srcPt.fX; - SkScalar dy, fy = srcPt.fY; - - if (fDstToIndexClass == kFixedStepInX_MatrixClass) { - const auto step = fDstToIndex.fixedStepInX(SkIntToScalar(y)); - dx = step.fX; - dy = step.fY; - } else { - SkASSERT(fDstToIndexClass == kLinear_MatrixClass); - dx = fDstToIndex.getScaleX(); - dy = fDstToIndex.getSkewY(); - } - - TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy); - (*shadeProc)(&rec, dstC, cache, toggle, count); - } else { // perspective case - SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf; - SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf; - for (; count > 0; --count) { - SkPoint srcPt; - dstProc(fDstToIndex, dstX, dstY, &srcPt); - TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0); - (*shadeProc)(&rec, dstC, cache, toggle, 1); - - dstX += SK_Scalar1; - toggle = next_dither_toggle(toggle); - dstC += 1; - } - } -} - -// Returns the original non-sorted version of the gradient -SkShader::GradientType SkTwoPointConicalGradient::asAGradient( - GradientInfo* info) const { - if (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; -} - -sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) { - DescriptorScope desc; - if (!desc.unflatten(buffer)) { - return nullptr; - } - SkPoint c1 = buffer.readPoint(); - SkPoint c2 = buffer.readPoint(); - SkScalar r1 = buffer.readScalar(); - SkScalar r2 = buffer.readScalar(); - - if (buffer.readBool()) { // flipped - SkTSwap(c1, c2); - SkTSwap(r1, r2); - - SkColor4f* colors = desc.mutableColors(); - SkScalar* pos = desc.mutablePos(); - const int last = desc.fCount - 1; - const int half = desc.fCount >> 1; - for (int i = 0; i < half; ++i) { - SkTSwap(colors[i], colors[last - i]); - if (pos) { - SkScalar tmp = pos[i]; - pos[i] = SK_Scalar1 - pos[last - i]; - pos[last - i] = SK_Scalar1 - tmp; - } - } - if (pos) { - if (desc.fCount & 1) { - pos[half] = SK_Scalar1 - pos[half]; - } - } - } - - return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors, - std::move(desc.fColorSpace), desc.fPos, - desc.fCount, desc.fTileMode, desc.fGradFlags, - desc.fLocalMatrix); -} - -void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writePoint(fCenter1); - buffer.writePoint(fCenter2); - buffer.writeScalar(fRadius1); - buffer.writeScalar(fRadius2); - buffer.writeBool(fFlippedGrad); -} - -#if SK_SUPPORT_GPU - -#include "SkGr.h" -#include "SkTwoPointConicalGradient_gpu.h" - -sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor( - const AsFPArgs& args) const { - SkASSERT(args.fContext); - SkASSERT(fPtsToUnit.isIdentity()); - sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), - args.fDstColorSpace); - sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make( - GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode, - std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); -} - -#endif - -sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { - SkSTArray<8, SkColor> origColorsStorage(fColorCount); - SkSTArray<8, SkScalar> origPosStorage(fColorCount); - SkSTArray<8, SkColor> xformedColorsStorage(fColorCount); - SkColor* origColors = origColorsStorage.begin(); - SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr; - SkColor* xformedColors = xformedColorsStorage.begin(); - - // Flip if necessary - SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1; - SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2; - SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1; - SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2; - for (int i = 0; i < fColorCount; i++) { - origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i]; - if (origPos) { - origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i]; - } - } - - xformer->apply(xformedColors, origColors, fColorCount); - return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors, - origPos, fColorCount, fTileMode, fGradFlags, - &this->getLocalMatrix()); -} - - -#ifndef SK_IGNORE_TO_STRING -void SkTwoPointConicalGradient::toString(SkString* str) const { - str->append("SkTwoPointConicalGradient: ("); - - str->append("center1: ("); - str->appendScalar(fCenter1.fX); - str->append(", "); - str->appendScalar(fCenter1.fY); - str->append(") radius1: "); - str->appendScalar(fRadius1); - str->append(" "); - - str->append("center2: ("); - str->appendScalar(fCenter2.fX); - str->append(", "); - str->appendScalar(fCenter2.fY); - str->append(") radius2: "); - str->appendScalar(fRadius2); - str->append(" "); - - this->INHERITED::toString(str); - - str->append(")"); -} -#endif diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.h b/src/shaders/gradients/SkTwoPointConicalGradient.h deleted file mode 100644 index b32f52c1e0..0000000000 --- a/src/shaders/gradients/SkTwoPointConicalGradient.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTwoPointConicalGradient_DEFINED -#define SkTwoPointConicalGradient_DEFINED - -#include "SkColorSpaceXformer.h" -#include "SkGradientShaderPriv.h" - -// TODO(dominikg): Worth making it truly immutable (i.e. set values in constructor)? -// Should only be initialized once via init(). Immutable afterwards. -struct TwoPtRadial { - enum { - // This value is outside the range SK_FixedMin to SK_FixedMax. - kDontDrawT = 0x80000000 - }; - - float fCenterX, fCenterY; - float fDCenterX, fDCenterY; - float fRadius; - float fDRadius; - float fA; - float fRadius2; - float fRDR; - bool fFlipped; - - void init(const SkPoint& center0, SkScalar rad0, - const SkPoint& center1, SkScalar rad1, - bool flipped); - - static bool DontDrawT(SkFixed t) { - return kDontDrawT == (uint32_t)t; - } -}; - - -class SkTwoPointConicalGradient : public SkGradientShaderBase { - TwoPtRadial fRec; -public: - SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius, - const SkPoint& end, SkScalar endRadius, - bool flippedGrad, const Descriptor&); - - class TwoPointConicalGradientContext : public SkGradientShaderBase::GradientShaderBaseContext { - public: - TwoPointConicalGradientContext(const SkTwoPointConicalGradient&, const ContextRec&); - ~TwoPointConicalGradientContext() override {} - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - - private: - typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED; - }; - - SkShader::GradientType asAGradient(GradientInfo* info) const override; -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; -#endif - bool isOpaque() const override; - - SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); } - SkScalar getStartRadius() const { return fRadius1; } - SkScalar getDiffRadius() const { return fRadius2 - fRadius1; } - 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) - -protected: - SkTwoPointConicalGradient(SkReadBuffer& buffer); - void flatten(SkWriteBuffer& buffer) const override; - Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; - sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; - -private: - SkPoint fCenter1; - SkPoint fCenter2; - SkScalar fRadius1; - SkScalar fRadius2; - bool fFlippedGrad; - - friend class SkGradientShader; - typedef SkGradientShaderBase INHERITED; -}; - -#endif diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp deleted file mode 100644 index 8402199362..0000000000 --- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp +++ /dev/null @@ -1,1341 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkTwoPointConicalGradient.h" - -#if SK_SUPPORT_GPU -#include "GrCoordTransform.h" -#include "GrPaint.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#include "SkTwoPointConicalGradient_gpu.h" - -// For brevity -typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; - -static const SkScalar kErrorTol = 0.00001f; -static const SkScalar kEdgeErrorTol = 5.f * kErrorTol; - -/** - * We have three general cases for 2pt conical gradients. First we always assume that - * the start radius <= end radius. Our first case (kInside_) is when the start circle - * is completely enclosed by the end circle. The second case (kOutside_) is the case - * when the start circle is either completely outside the end circle or the circles - * overlap. The final case (kEdge_) is when the start circle is inside the end one, - * but the two are just barely touching at 1 point along their edges. - */ -enum ConicalType { - kInside_ConicalType, - kOutside_ConicalType, - kEdge_ConicalType, -}; - -////////////////////////////////////////////////////////////////////////////// - -static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader, - SkMatrix* invLMatrix) { - // Inverse of the current local matrix is passed in then, - // translate to center1, rotate so center2 is on x axis. - const SkPoint& center1 = shader.getStartCenter(); - const SkPoint& center2 = shader.getEndCenter(); - - invLMatrix->postTranslate(-center1.fX, -center1.fY); - - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - if (0 != diffLen) { - SkScalar invDiffLen = SkScalarInvert(diffLen); - SkMatrix rot; - rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX); - invLMatrix->postConcat(rot); - } -} - -class Edge2PtConicalEffect : public GrGradientEffect { -public: - class GLSLEdge2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { - return sk_sp<GrFragmentProcessor>(new Edge2PtConicalEffect(args)); - } - - ~Edge2PtConicalEffect() override {} - - const char* name() const override { - return "Two-Point Conical Gradient Edge Touching"; - } - - // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. - SkScalar center() const { return fCenterX1; } - SkScalar diffRadius() const { return fDiffRadius; } - SkScalar radius() const { return fRadius0; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fCenterX1 == s.fCenterX1 && - this->fRadius0 == s.fRadius0 && - this->fDiffRadius == s.fDiffRadius); - } - - Edge2PtConicalEffect(const CreateArgs& args) - : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) { - const SkTwoPointConicalGradient& shader = - *static_cast<const SkTwoPointConicalGradient*>(args.fShader); - fCenterX1 = shader.getCenterX1(); - fRadius0 = shader.getStartRadius(); - fDiffRadius = shader.getDiffRadius(); - this->initClassID<Edge2PtConicalEffect>(); - // We should only be calling this shader if we are degenerate case with touching circles - // When deciding if we are in edge case, we scaled by the end radius for cases when the - // start radius was close to zero, otherwise we scaled by the start radius. In addition - // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we - // need the sqrt value below - SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) < - (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol : - fRadius0 * sqrt(kEdgeErrorTol))); - - // We pass the linear part of the quadratic as a varying. - // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) - fBTransform = this->getCoordTransform(); - SkMatrix& bMatrix = *fBTransform.accessMatrix(); - SkScalar r0dr = fRadius0 * fDiffRadius; - bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] + - r0dr * bMatrix[SkMatrix::kMPersp0]); - bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] + - r0dr * bMatrix[SkMatrix::kMPersp1]); - bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] + - r0dr * bMatrix[SkMatrix::kMPersp2]); - this->addCoordTransform(&fBTransform); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - // @{ - // Cache of values - these can change arbitrarily, EXCEPT - // we shouldn't change between degenerate and non-degenerate?! - - GrCoordTransform fBTransform; - SkScalar fCenterX1; - SkScalar fRadius0; - SkScalar fDiffRadius; - - // @} - - typedef GrGradientEffect INHERITED; -}; - -class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor { -public: - GLSLEdge2PtConicalProcessor(const GrProcessor&); - ~GLSLEdge2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedRadius; - SkScalar fCachedDiffRadius; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const { - return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = d->fRandom->nextUScalar1(); - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // If the circles are identical the factory will give us an empty shader. - // This will happen if we pick identical centers - } while (center1 == center2); - - // Below makes sure that circle one is contained within circle two - // and both circles are touching on an edge - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - radius2 = radius1 + diffLen; - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedRadius(-SK_ScalarMax) - , fCachedDiffRadius(-SK_ScalarMax) {} - -void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) { - const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - - SkString cName("c"); - SkString tName("t"); - SkString p0; // start radius - SkString p1; // start radius squared - SkString p2; // difference in radii (r1 - r0) - - - p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - - // We interpolate the linear component in coords[1]. - SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType()); - const char* coords2D; - SkString bVar; - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - if (kVec3f_GrSLType == args.fTransformedCoords[0].getType()) { - fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n", - args.fTransformedCoords[0].c_str(), - args.fTransformedCoords[0].c_str(), - args.fTransformedCoords[1].c_str(), - args.fTransformedCoords[1].c_str()); - coords2D = "interpolants.xy"; - bVar = "interpolants.z"; - } else { - coords2D = args.fTransformedCoords[0].c_str(); - bVar.printf("%s.x", args.fTransformedCoords[1].c_str()); - } - - // output will default to transparent black (we simply won't write anything - // else to it if invalid, instead of discarding or returning prematurely) - fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor); - - // c = (x^2)+(y^2) - params[1] - fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", - cName.c_str(), coords2D, coords2D, p1.c_str()); - - // linear case: t = -c/b - fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), - cName.c_str(), bVar.c_str()); - - // if r(t) > 0, then t will be the x coordinate - fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), - p2.c_str(), p0.c_str()); - fragBuilder->codeAppend("\t"); - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); - fragBuilder->codeAppend("\t}\n"); -} - -void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>(); - SkScalar radius0 = data.radius(); - SkScalar diffRadius = data.diffRadius(); - - if (fCachedRadius != radius0 || - fCachedDiffRadius != diffRadius) { - - pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius); - fCachedRadius = radius0; - fCachedDiffRadius = diffRadius; - } -} - -void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); -} - -////////////////////////////////////////////////////////////////////////////// -// Focal Conical Gradients -////////////////////////////////////////////////////////////////////////////// - -static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader, - SkMatrix* invLMatrix, SkScalar* focalX) { - // Inverse of the current local matrix is passed in then, - // translate, scale, and rotate such that endCircle is unit circle on x-axis, - // and focal point is at the origin. - ConicalType conicalType; - const SkPoint& focal = shader.getStartCenter(); - const SkPoint& centerEnd = shader.getEndCenter(); - SkScalar radius = shader.getEndRadius(); - SkScalar invRadius = 1.f / radius; - - SkMatrix matrix; - - matrix.setTranslate(-centerEnd.fX, -centerEnd.fY); - matrix.postScale(invRadius, invRadius); - - SkPoint focalTrans; - matrix.mapPoints(&focalTrans, &focal, 1); - *focalX = focalTrans.length(); - - if (0.f != *focalX) { - SkScalar invFocalX = SkScalarInvert(*focalX); - SkMatrix rot; - rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX); - matrix.postConcat(rot); - } - - matrix.postTranslate(-(*focalX), 0.f); - - // If the focal point is touching the edge of the circle it will - // cause a degenerate case that must be handled separately - // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the - // stability trade off versus the linear approx used in the Edge Shader - if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) { - return kEdge_ConicalType; - } - - // Scale factor 1 / (1 - focalX * focalX) - SkScalar oneMinusF2 = 1.f - *focalX * *focalX; - SkScalar s = SkScalarInvert(oneMinusF2); - - - if (s >= 0.f) { - conicalType = kInside_ConicalType; - matrix.postScale(s, s * SkScalarSqrt(oneMinusF2)); - } else { - conicalType = kOutside_ConicalType; - matrix.postScale(s, s); - } - - invLMatrix->postConcat(matrix); - - return conicalType; -} - -////////////////////////////////////////////////////////////////////////////// - -class FocalOutside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLFocalOutside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) { - return sk_sp<GrFragmentProcessor>( - new FocalOutside2PtConicalEffect(args, focalX)); - } - - ~FocalOutside2PtConicalEffect() override {} - - const char* name() const override { - return "Two-Point Conical Gradient Focal Outside"; - } - - bool isFlipped() const { return fIsFlipped; } - SkScalar focal() const { return fFocalX; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fFocalX == s.fFocalX && - this->fIsFlipped == s.fIsFlipped); - } - - static bool IsFlipped(const CreateArgs& args) { - // eww. - return static_cast<const SkTwoPointConicalGradient*>(args.fShader)->isFlippedGrad(); - } - - FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX) - : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) - , fFocalX(focalX) - , fIsFlipped(IsFlipped(args)) { - this->initClassID<FocalOutside2PtConicalEffect>(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - SkScalar fFocalX; - bool fIsFlipped; - - typedef GrGradientEffect INHERITED; -}; - -class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLFocalOutside2PtConicalProcessor(const GrProcessor&); - ~GLSLFocalOutside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - bool fIsFlipped; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedFocal; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const { - return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = 0.f; - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // Need to make sure the centers are not the same or else focal point will be inside - } while (center1 == center2); - - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - // Below makes sure that the focal point is not contained within circle two - radius2 = d->fRandom->nextRangeF(0.f, diffLen); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor - ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedFocal(SK_ScalarMax) { - const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>(); - fIsFlipped = data.isFlipped(); -} - -void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) { - const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - SkString p0; // focalX - SkString p1; // 1 - focalX * focalX - - p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2) - - // output will default to transparent black (we simply won't write anything - // else to it if invalid, instead of discarding or returning prematurely) - fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor); - - fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str()); - - // Must check to see if we flipped the circle order (to make sure start radius < end radius) - // If so we must also flip sign on sqrt - if (!fIsFlipped) { - fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(), - coords2D, p0.c_str()); - } else { - fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(), - coords2D, p0.c_str()); - } - - fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str()); - fragBuilder->codeAppend("\t\t"); - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); - fragBuilder->codeAppend("\t}\n"); -} - -void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>(); - SkASSERT(data.isFlipped() == fIsFlipped); - SkScalar focal = data.focal(); - - if (fCachedFocal != focal) { - SkScalar oneMinus2F = 1.f - focal * focal; - - pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F)); - fCachedFocal = focal; - } -} - -void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - uint32_t* key = b->add32n(2); - key[0] = GenBaseGradientKey(processor); - key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped(); -} - -////////////////////////////////////////////////////////////////////////////// - -class FocalInside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLFocalInside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) { - return sk_sp<GrFragmentProcessor>( - new FocalInside2PtConicalEffect(args, focalX)); - } - - ~FocalInside2PtConicalEffect() override {} - - const char* name() const override { - return "Two-Point Conical Gradient Focal Inside"; - } - - SkScalar focal() const { return fFocalX; } - - typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor; - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fFocalX == s.fFocalX); - } - - FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX) - : INHERITED(args, args.fShader->colorsAreOpaque()), fFocalX(focalX) { - this->initClassID<FocalInside2PtConicalEffect>(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - SkScalar fFocalX; - - typedef GrGradientEffect INHERITED; -}; - -class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLFocalInside2PtConicalProcessor(const GrProcessor&); - ~GLSLFocalInside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fFocalUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedFocal; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const { - return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = 0.f; - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // Below makes sure radius2 is larger enouch such that the focal point - // is inside the end circle - SkScalar increase = d->fRandom->nextUScalar1(); - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - radius2 = diffLen + increase; - // If the circles are identical the factory will give us an empty shader. - } while (radius1 == radius2 && center1 == center2); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor - ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedFocal(SK_ScalarMax) {} - -void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) { - const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kFloat_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - - // this is the distance along x-axis from the end center to focal point in - // transformed coordinates - GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // t = p.x * focalX + length(p) - fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(), - coords2D, focal.c_str(), coords2D); - - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>(); - SkScalar focal = data.focal(); - - if (fCachedFocal != focal) { - pdman.set1f(fFocalUni, SkScalarToFloat(focal)); - fCachedFocal = focal; - } -} - -void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); -} - -////////////////////////////////////////////////////////////////////////////// -// Circle Conical Gradients -////////////////////////////////////////////////////////////////////////////// - -struct CircleConicalInfo { - SkPoint fCenterEnd; - SkScalar fA; - SkScalar fB; - SkScalar fC; -}; - -// Returns focal distance along x-axis in transformed coords -static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader, - SkMatrix* invLMatrix, CircleConicalInfo* info) { - // Inverse of the current local matrix is passed in then, - // translate and scale such that start circle is on the origin and has radius 1 - const SkPoint& centerStart = shader.getStartCenter(); - const SkPoint& centerEnd = shader.getEndCenter(); - SkScalar radiusStart = shader.getStartRadius(); - SkScalar radiusEnd = shader.getEndRadius(); - - SkMatrix matrix; - - matrix.setTranslate(-centerStart.fX, -centerStart.fY); - - SkScalar invStartRad = 1.f / radiusStart; - matrix.postScale(invStartRad, invStartRad); - - radiusEnd /= radiusStart; - - SkPoint centerEndTrans; - matrix.mapPoints(¢erEndTrans, ¢erEnd, 1); - - SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY - - radiusEnd * radiusEnd + 2 * radiusEnd - 1; - - // Check to see if start circle is inside end circle with edges touching. - // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting - // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing - // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is - // still accurate. - if (SkScalarAbs(A) < kEdgeErrorTol) { - return kEdge_ConicalType; - } - - SkScalar C = 1.f / A; - SkScalar B = (radiusEnd - 1.f) * C; - - matrix.postScale(C, C); - - invLMatrix->postConcat(matrix); - - info->fCenterEnd = centerEndTrans; - info->fA = A; - info->fB = B; - info->fC = C; - - // if A ends up being negative, the start circle is contained completely inside the end cirlce - if (A < 0.f) { - return kInside_ConicalType; - } - return kOutside_ConicalType; -} - -class CircleInside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLCircleInside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) { - return sk_sp<GrFragmentProcessor>( - new CircleInside2PtConicalEffect(args, info)); - } - - ~CircleInside2PtConicalEffect() override {} - - const char* name() const override { return "Two-Point Conical Gradient Inside"; } - - SkScalar centerX() const { return fInfo.fCenterEnd.fX; } - SkScalar centerY() const { return fInfo.fCenterEnd.fY; } - SkScalar A() const { return fInfo.fA; } - SkScalar B() const { return fInfo.fB; } - SkScalar C() const { return fInfo.fC; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && - this->fInfo.fA == s.fInfo.fA && - this->fInfo.fB == s.fInfo.fB && - this->fInfo.fC == s.fInfo.fC); - } - - CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info) - : INHERITED(args, args.fShader->colorsAreOpaque()), fInfo(info) { - this->initClassID<CircleInside2PtConicalEffect>(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - const CircleConicalInfo fInfo; - - typedef GrGradientEffect INHERITED; -}; - -class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLCircleInside2PtConicalProcessor(const GrProcessor&); - ~GLSLCircleInside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fCenterUni; - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedCenterX; - SkScalar fCachedCenterY; - SkScalar fCachedA; - SkScalar fCachedB; - SkScalar fCachedC; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const { - return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0 - SkPoint center2; - SkScalar radius2; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // Below makes sure that circle one is contained within circle two - SkScalar increase = d->fRandom->nextUScalar1(); - SkPoint diff = center2 - center1; - SkScalar diffLen = diff.length(); - radius2 = radius1 + diffLen + increase; - // If the circles are identical the factory will give us an empty shader. - } while (radius1 == radius2 && center1 == center2); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor - ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedCenterX(SK_ScalarMax) - , fCachedCenterY(SK_ScalarMax) - , fCachedA(SK_ScalarMax) - , fCachedB(SK_ScalarMax) - , fCachedC(SK_ScalarMax) {} - -void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) { - const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSCenter"); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec3f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - - GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni); - // params.x = A - // params.y = B - // params.z = C - GrShaderVar params = uniformHandler->getUniformVariable(fParamUni); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // p = coords2D - // e = center end - // r = radius end - // A = dot(e, e) - r^2 + 2 * r - 1 - // B = (r -1) / A - // C = 1 / A - // d = dot(e, p) + B - // t = d +/- sqrt(d^2 - A * dot(p, p) + C) - fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), - params.c_str()); - fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n", - tName.c_str(), params.c_str(), params.c_str()); - - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); -} - -void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>(); - SkScalar centerX = data.centerX(); - SkScalar centerY = data.centerY(); - SkScalar A = data.A(); - SkScalar B = data.B(); - SkScalar C = data.C(); - - if (fCachedCenterX != centerX || fCachedCenterY != centerY || - fCachedA != A || fCachedB != B || fCachedC != C) { - - pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); - pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C)); - - fCachedCenterX = centerX; - fCachedCenterY = centerY; - fCachedA = A; - fCachedB = B; - fCachedC = C; - } -} - -void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - b->add32(GenBaseGradientKey(processor)); -} - -////////////////////////////////////////////////////////////////////////////// - -class CircleOutside2PtConicalEffect : public GrGradientEffect { -public: - class GLSLCircleOutside2PtConicalProcessor; - - static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) { - return sk_sp<GrFragmentProcessor>( - new CircleOutside2PtConicalEffect(args, info)); - } - - ~CircleOutside2PtConicalEffect() override {} - - const char* name() const override { return "Two-Point Conical Gradient Outside"; } - - SkScalar centerX() const { return fInfo.fCenterEnd.fX; } - SkScalar centerY() const { return fInfo.fCenterEnd.fY; } - SkScalar A() const { return fInfo.fA; } - SkScalar B() const { return fInfo.fB; } - SkScalar C() const { return fInfo.fC; } - SkScalar tLimit() const { return fTLimit; } - bool isFlipped() const { return fIsFlipped; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - - bool onIsEqual(const GrFragmentProcessor& sBase) const override { - const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>(); - return (INHERITED::onIsEqual(sBase) && - this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && - this->fInfo.fA == s.fInfo.fA && - this->fInfo.fB == s.fInfo.fB && - this->fInfo.fC == s.fInfo.fC && - this->fTLimit == s.fTLimit && - this->fIsFlipped == s.fIsFlipped); - } - - CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info) - : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) - , fInfo(info) { - this->initClassID<CircleOutside2PtConicalEffect>(); - const SkTwoPointConicalGradient& shader = - *static_cast<const SkTwoPointConicalGradient*>(args.fShader); - if (shader.getStartRadius() != shader.getEndRadius()) { - fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius()); - } else { - fTLimit = SK_ScalarMin; - } - - fIsFlipped = shader.isFlippedGrad(); - } - - GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - - const CircleConicalInfo fInfo; - SkScalar fTLimit; - bool fIsFlipped; - - typedef GrGradientEffect INHERITED; -}; - -class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor - : public GrGradientEffect::GLSLProcessor { -public: - GLSLCircleOutside2PtConicalProcessor(const GrProcessor&); - ~GLSLCircleOutside2PtConicalProcessor() override {} - - virtual void emitCode(EmitArgs&) override; - - static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b); - -protected: - void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; - - UniformHandle fCenterUni; - UniformHandle fParamUni; - - const char* fVSVaryingName; - const char* fFSVaryingName; - - bool fIsFlipped; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedCenterX; - SkScalar fCachedCenterY; - SkScalar fCachedA; - SkScalar fCachedB; - SkScalar fCachedC; - SkScalar fCachedTLimit; - - // @} - -private: - typedef GrGradientEffect::GLSLProcessor INHERITED; - -}; - -void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const { - return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this); -} - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect); - -/* - * All Two point conical gradient test create functions may occasionally create edge case shaders - */ -#if GR_TEST_UTILS -sk_sp<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) { - SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; - SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0 - SkPoint center2; - SkScalar radius2; - SkScalar diffLen; - do { - center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()); - // If the circles share a center than we can't be in the outside case - } while (center1 == center2); - SkPoint diff = center2 - center1; - diffLen = diff.length(); - // Below makes sure that circle one is not contained within circle two - // and have radius2 >= radius to match sorting on cpu side - radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen); - - RandomGradientParams params(d->fRandom); - auto shader = params.fUseColors4f ? - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors4f, params.fColorSpace, params.fStops, - params.fColorCount, params.fTileMode) : - SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, - params.fColors, params.fStops, - params.fColorCount, params.fTileMode); - GrTest::TestAsFPArgs asFPArgs(d); - sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); - GrAlwaysAssert(fp); - return fp; -} -#endif - -CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor - ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor) - : fVSVaryingName(nullptr) - , fFSVaryingName(nullptr) - , fCachedCenterX(SK_ScalarMax) - , fCachedCenterY(SK_ScalarMax) - , fCachedA(SK_ScalarMax) - , fCachedB(SK_ScalarMax) - , fCachedC(SK_ScalarMax) - , fCachedTLimit(SK_ScalarMax) { - const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>(); - fIsFlipped = data.isFlipped(); - } - -void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) { - const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>(); - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - this->emitUniforms(uniformHandler, ge); - fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec2f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSCenter"); - fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, - kVec4f_GrSLType, kDefault_GrSLPrecision, - "Conical2FSParams"); - SkString tName("t"); - - GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni); - // params.x = A - // params.y = B - // params.z = C - GrShaderVar params = uniformHandler->getUniformVariable(fParamUni); - - // if we have a vec3 from being in perspective, convert it to a vec2 first - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); - const char* coords2D = coords2DString.c_str(); - - // output will default to transparent black (we simply won't write anything - // else to it if invalid, instead of discarding or returning prematurely) - fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor); - - // p = coords2D - // e = center end - // r = radius end - // A = dot(e, e) - r^2 + 2 * r - 1 - // B = (r -1) / A - // C = 1 / A - // d = dot(e, p) + B - // t = d +/- sqrt(d^2 - A * dot(p, p) + C) - - fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); - fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), - params.c_str()); - fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), - params.c_str()); - - // Must check to see if we flipped the circle order (to make sure start radius < end radius) - // If so we must also flip sign on sqrt - if (!fIsFlipped) { - fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str()); - } else { - fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str()); - } - - fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", - tName.c_str(), params.c_str()); - fragBuilder->codeAppend("\t\t"); - this->emitColor(fragBuilder, - uniformHandler, - args.fShaderCaps, - ge, - tName.c_str(), - args.fOutputColor, - args.fInputColor, - args.fTexSamplers); - fragBuilder->codeAppend("\t}\n"); -} - -void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData( - const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) { - INHERITED::onSetData(pdman, processor); - const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>(); - SkASSERT(data.isFlipped() == fIsFlipped); - SkScalar centerX = data.centerX(); - SkScalar centerY = data.centerY(); - SkScalar A = data.A(); - SkScalar B = data.B(); - SkScalar C = data.C(); - SkScalar tLimit = data.tLimit(); - - if (fCachedCenterX != centerX || fCachedCenterY != centerY || - fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) { - - pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); - pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C), - SkScalarToFloat(tLimit)); - - fCachedCenterX = centerX; - fCachedCenterY = centerY; - fCachedA = A; - fCachedB = B; - fCachedC = C; - fCachedTLimit = tLimit; - } -} - -void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey( - const GrProcessor& processor, - const GrShaderCaps&, GrProcessorKeyBuilder* b) { - uint32_t* key = b->add32n(2); - key[0] = GenBaseGradientKey(processor); - key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped(); -} - -////////////////////////////////////////////////////////////////////////////// - -sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make( - const GrGradientEffect::CreateArgs& args) { - const SkTwoPointConicalGradient& shader = - *static_cast<const SkTwoPointConicalGradient*>(args.fShader); - - SkMatrix matrix; - if (!shader.getLocalMatrix().invert(&matrix)) { - return nullptr; - } - if (args.fMatrix) { - SkMatrix inv; - if (!args.fMatrix->invert(&inv)) { - return nullptr; - } - matrix.postConcat(inv); - } - - GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode, - std::move(args.fColorSpaceXform), args.fGammaCorrect); - - if (shader.getStartRadius() < kErrorTol) { - SkScalar focalX; - ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX); - if (type == kInside_ConicalType) { - return FocalInside2PtConicalEffect::Make(newArgs, focalX); - } else if(type == kEdge_ConicalType) { - set_matrix_edge_conical(shader, &matrix); - return Edge2PtConicalEffect::Make(newArgs); - } else { - return FocalOutside2PtConicalEffect::Make(newArgs, focalX); - } - } - - CircleConicalInfo info; - ConicalType type = set_matrix_circle_conical(shader, &matrix, &info); - - if (type == kInside_ConicalType) { - return CircleInside2PtConicalEffect::Make(newArgs, info); - } else if (type == kEdge_ConicalType) { - set_matrix_edge_conical(shader, &matrix); - return Edge2PtConicalEffect::Make(newArgs); - } else { - return CircleOutside2PtConicalEffect::Make(newArgs, info); - } -} - -#endif diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.h b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.h deleted file mode 100644 index 46edb1f7d1..0000000000 --- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTwoPointConicalGradient_gpu_DEFINED -#define SkTwoPointConicalGradient_gpu_DEFINED - -#include "SkGradientShaderPriv.h" - -class GrProcessor; -class SkTwoPointConicalGradient; - -namespace Gr2PtConicalGradientEffect { - /** - * Creates an effect that produces a two point conical gradient based on the - * shader passed in. - */ - sk_sp<GrFragmentProcessor> Make(const GrGradientEffect::CreateArgs& args); -}; - -#endif |