diff options
-rw-r--r-- | gm/lightingshaderbevel.cpp | 269 | ||||
-rw-r--r-- | gyp/core.gypi | 6 | ||||
-rw-r--r-- | src/core/SkNormalBevelSource.cpp | 165 | ||||
-rw-r--r-- | src/core/SkNormalBevelSource.h | 57 | ||||
-rw-r--r-- | src/core/SkNormalFlatSource.cpp | 107 | ||||
-rw-r--r-- | src/core/SkNormalFlatSource.h | 48 | ||||
-rw-r--r-- | src/core/SkNormalMapSource.cpp | 265 | ||||
-rw-r--r-- | src/core/SkNormalMapSource.h | 62 | ||||
-rw-r--r-- | src/core/SkNormalSource.cpp | 452 | ||||
-rw-r--r-- | src/core/SkNormalSource.h | 54 | ||||
-rw-r--r-- | tests/SerializationTest.cpp | 11 |
11 files changed, 1051 insertions, 445 deletions
diff --git a/gm/lightingshaderbevel.cpp b/gm/lightingshaderbevel.cpp new file mode 100644 index 0000000000..d119a27cbd --- /dev/null +++ b/gm/lightingshaderbevel.cpp @@ -0,0 +1,269 @@ +/* + * 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 "gm.h" + +#include "SkBitmapProcShader.h" +#include "SkLightingShader.h" +#include "SkNormalSource.h" +#include "SkPath.h" +#include "SkPoint3.h" +#include "SkShader.h" + + +namespace skiagm { + +// This GM exercises lighting shaders when used with bevel SkNormalSource objects. +class LightingShaderBevelGM : public GM { +public: + LightingShaderBevelGM() { + this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); + } + +protected: + SkString onShortName() override { + return SkString("lightingshaderbevel"); + } + + SkISize onISize() override { + return SkISize::Make(SkScalarCeilToInt(GRID_NUM_COLUMNS * GRID_CELL_WIDTH), + SkScalarCeilToInt(GRID_NUM_ROWS * GRID_CELL_WIDTH)); + } + + void onOnceBeforeDraw() override { + SkLights::Builder builder; + const SkVector3 kLightFromUpperRight = SkVector3::Make(0.788f, 0.394f, 0.473f); + + builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), + kLightFromUpperRight)); + builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.2f, 0.2f))); + fLights = builder.finish(); + + // fRect is assumed to be square throughout this file + fRect = SkRect::MakeIWH(kTexSize, kTexSize); + SkMatrix matrix; + SkRect bitmapBounds = SkRect::MakeIWH(kTexSize, kTexSize); + matrix.setRectToRect(bitmapBounds, fRect, SkMatrix::kFill_ScaleToFit); + + SkBitmap diffuseMap = sk_tool_utils::create_checkerboard_bitmap( + kTexSize, kTexSize, + sk_tool_utils::color_to_565(0x0), + sk_tool_utils::color_to_565(0xFF804020), + 8); + fDiffuse = SkMakeBitmapShader(diffuseMap, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, &matrix, nullptr); + + fConvexPath.moveTo(fRect.width() / 2.0f, 0.0f); + fConvexPath.lineTo(0.0f, fRect.height()); + fConvexPath.lineTo(fRect.width(), fRect.height()); + fConvexPath.close(); + + // Creating concave path + { + SkScalar x = 0.0f; + SkScalar y = fRect.height() / 2.0f; + + const int NUM_SPIKES = 8; + + const SkScalar x0 = x; + const SkScalar dx = fRect.width() / (NUM_SPIKES * 2); + const SkScalar dy = SK_Scalar1 * 10; + + + fConcavePath.moveTo(x, y + dy); + for (int i = 0; i < NUM_SPIKES; i++) { + x += dx; + fConcavePath.lineTo(x, y - dy); + x += dx; + fConcavePath.lineTo(x, y + dy); + } + fConcavePath.lineTo(x, y + (2 * dy)); + fConcavePath.lineTo(x0, y + (2 * dy)); + fConcavePath.close(); + } + } + + // Scales shape around origin, rotates shape around origin, then translates shape to origin + void positionCTM(SkCanvas *canvas, SkScalar scaleX, SkScalar scaleY, SkScalar rotate) const { + canvas->translate(kTexSize/2.0f, kTexSize/2.0f); + canvas->scale(scaleX, scaleY); + canvas->rotate(rotate); + canvas->translate(-kTexSize/2.0f, -kTexSize/2.0f); + } + + enum Shape { + kCircle_Shape, + kRect_Shape, + kRRect_Shape, + kConvexPath_Shape, + kConcavePath_Shape, + + kLast_Shape = kConcavePath_Shape + }; + void drawShape(enum Shape shape, SkCanvas* canvas, SkScalar scaleX, SkScalar scaleY, + SkScalar rotate, SkNormalSource::BevelType bevelType, SkScalar bevelHeight) { + canvas->save(); + + this->positionCTM(canvas, scaleX, scaleY, rotate); + + SkPaint paint; + + SkScalar bevelWidth = 10.0f; + sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeBevel(bevelType, bevelWidth, + bevelHeight); + + paint.setShader(SkLightingShader::Make(fDiffuse, std::move(normalSource), fLights)); + paint.setAntiAlias(true); + switch(shape) { + case kCircle_Shape: + canvas->drawCircle(fRect.centerX(), fRect.centerY(), fRect.width()/2.0f, paint); + break; + case kRect_Shape: + canvas->drawRect(fRect, paint); + break; + case kRRect_Shape: + canvas->drawRoundRect(fRect, 5.0f, 5.0f, paint); + break; + case kConvexPath_Shape: + canvas->drawPath(fConvexPath, paint); + break; + case kConcavePath_Shape: + canvas->drawPath(fConcavePath, paint); + break; + default: + SkDEBUGFAIL("Invalid shape enum for drawShape"); + } + + canvas->restore(); + } + + void onDraw(SkCanvas* canvas) override { + SkPaint labelPaint; + labelPaint.setTypeface(sk_tool_utils::create_portable_typeface("sans-serif", + SkFontStyle())); + labelPaint.setAntiAlias(true); + labelPaint.setTextSize(LABEL_SIZE); + + int gridNum = 0; + + // Running through all possible parameter combinations + for (auto bevelType : {SkNormalSource::BevelType::kLinear, + SkNormalSource::BevelType::kRoundedIn, + SkNormalSource::BevelType::kRoundedOut}) { + for (SkScalar bevelHeight: {-7.0f, 7.0f}) { + for (int shapeInt = 0; shapeInt < NUM_SHAPES; shapeInt++) { + Shape shape = (Shape)shapeInt; + + // Determining position + SkScalar xPos = (gridNum / GRID_NUM_ROWS) * GRID_CELL_WIDTH; + SkScalar yPos = (gridNum % GRID_NUM_ROWS) * GRID_CELL_WIDTH; + + canvas->save(); + + canvas->translate(xPos, yPos); + this->drawShape(shape, canvas, 1.0f, 1.0f, 0.f, bevelType, bevelHeight); + // Drawing labels + canvas->translate(0.0f, SkIntToScalar(kTexSize)); + { + canvas->translate(0.0f, LABEL_SIZE); + SkString label; + label.append("bevelType: "); + switch (bevelType) { + case SkNormalSource::BevelType::kLinear: + label.append("linear"); + break; + case SkNormalSource::BevelType::kRoundedIn: + label.append("roundedIn"); + break; + case SkNormalSource::BevelType::kRoundedOut: + label.append("roundedOut"); + break; + } + canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint); + } + { + canvas->translate(0.0f, LABEL_SIZE); + SkString label; + label.appendf("bevelHeight: %.1f", bevelHeight); + canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint); + } + + canvas->restore(); + + gridNum++; + } + } + } + + // Testing rotation + for (int shapeInt = 0; shapeInt < NUM_SHAPES; shapeInt++) { + Shape shape = (Shape)shapeInt; + + // Determining position + SkScalar xPos = (gridNum / GRID_NUM_ROWS) * GRID_CELL_WIDTH; + SkScalar yPos = (gridNum % GRID_NUM_ROWS) * GRID_CELL_WIDTH; + + canvas->save(); + + canvas->translate(xPos, yPos); + this->drawShape(shape, canvas, SK_ScalarRoot2Over2, SK_ScalarRoot2Over2, 45.0f, + SkNormalSource::BevelType::kLinear, 7.0f); + + // Drawing labels + canvas->translate(0.0f, SkIntToScalar(kTexSize)); + { + canvas->translate(0.0f, LABEL_SIZE); + SkString label; + label.appendf("bevelType: linear"); + canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint); + } + { + canvas->translate(0.0f, LABEL_SIZE); + SkString label; + label.appendf("bevelHeight: %.1f", 7.0f); + canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint); + } + { + canvas->translate(0.0f, LABEL_SIZE); + SkString label; + label.appendf("rotated"); + canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint); + } + + canvas->restore(); + + gridNum++; + } + + // Making sure NUM_COMBINATIONS_PER_SHAPE is set correctly + SkASSERT(gridNum == (NUM_COMBINATIONS_PER_SHAPE*NUM_SHAPES)); + } + +private: + static constexpr int kTexSize = 96; + static constexpr int NUM_SHAPES = kLast_Shape + 1; + static constexpr int NUM_COMBINATIONS_PER_SHAPE = 7; + static constexpr int GRID_NUM_ROWS = NUM_SHAPES; + static constexpr int GRID_NUM_COLUMNS = NUM_COMBINATIONS_PER_SHAPE; + static constexpr SkScalar LABEL_SIZE = 10.0f; + static constexpr int NUM_LABELS_PER_CELL = 3; + static constexpr SkScalar GRID_CELL_WIDTH = kTexSize + 10.0f + NUM_LABELS_PER_CELL * LABEL_SIZE; + + sk_sp<SkShader> fDiffuse; + + SkRect fRect; + SkPath fConvexPath; + SkPath fConcavePath; + sk_sp<SkLights> fLights; + + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM(return new LightingShaderBevelGM;) +} diff --git a/gyp/core.gypi b/gyp/core.gypi index 736e47d115..6306a75df9 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -189,6 +189,12 @@ '<(skia_src_path)/core/SkNextID.h', '<(skia_src_path)/core/SkNinePatchIter.cpp', '<(skia_src_path)/core/SkNinePatchIter.h', + '<(skia_src_path)/core/SkNormalBevelSource.cpp', + '<(skia_src_path)/core/SkNormalBevelSource.h', + '<(skia_src_path)/core/SkNormalMapSource.cpp', + '<(skia_src_path)/core/SkNormalMapSource.h', + '<(skia_src_path)/core/SkNormalFlatSource.cpp', + '<(skia_src_path)/core/SkNormalFlatSource.h', '<(skia_src_path)/core/SkNormalSource.cpp', '<(skia_src_path)/core/SkNormalSource.h', '<(skia_src_path)/core/SkNx.h', diff --git a/src/core/SkNormalBevelSource.cpp b/src/core/SkNormalBevelSource.cpp new file mode 100644 index 0000000000..feccb0cce8 --- /dev/null +++ b/src/core/SkNormalBevelSource.cpp @@ -0,0 +1,165 @@ +/* + * 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 "SkNormalBevelSource.h" + +#include "SkNormalSource.h" +#include "SkPoint3.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +#if SK_SUPPORT_GPU +#include "GrInvariantOutput.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "SkGr.h" + +class NormalBevelFP : public GrFragmentProcessor { +public: + NormalBevelFP(SkNormalSource::BevelType type, SkScalar width, SkScalar height) + : fType(type) + , fWidth(width) + , fHeight(height) { + this->initClassID<NormalBevelFP>(); + } + + class GLSLNormalBevelFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalBevelFP() { + fPrevWidth = SkFloatToScalar(0.0f); + fPrevHeight = SkFloatToScalar(0.0f); + } + + void emitCode(EmitArgs& args) override { + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + + const char* widthUniName = nullptr; + fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, + kDefault_GrSLPrecision, "Width", &widthUniName); + + const char* heightUniName = nullptr; + fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, + kDefault_GrSLPrecision, "Height", &heightUniName); + + fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor); + } + + static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + const NormalBevelFP& fp = proc.cast<NormalBevelFP>(); + b->add32(static_cast<int>(fp.fType)); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { + const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>(); + + if (fPrevWidth != normalBevelFP.fWidth) { + pdman.set1f(fWidthUni, normalBevelFP.fWidth); + fPrevWidth = normalBevelFP.fWidth; + } + if (fPrevHeight != normalBevelFP.fHeight) { + pdman.set1f(fHeightUni, normalBevelFP.fHeight); + fPrevHeight = normalBevelFP.fHeight; + } + } + + private: + SkScalar fPrevWidth; + GrGLSLProgramDataManager::UniformHandle fWidthUni; + + SkScalar fPrevHeight; + GrGLSLProgramDataManager::UniformHandle fHeightUni; + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLNormalBevelFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "NormalBevelFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); + } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalBevelFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>(); + return fType == normalBevelFP.fType && + fWidth == normalBevelFP.fWidth && + fHeight == normalBevelFP.fHeight; + } + + SkNormalSource::BevelType fType; + SkScalar fWidth; + SkScalar fHeight; +}; + +sk_sp<GrFragmentProcessor> SkNormalBevelSourceImpl::asFragmentProcessor( + const SkShader::AsFPArgs&) const { + + return sk_make_sp<NormalBevelFP>(fType, fWidth, fHeight); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +SkNormalBevelSourceImpl::Provider::Provider() {} + +SkNormalBevelSourceImpl::Provider::~Provider() {} + +SkNormalSource::Provider* SkNormalBevelSourceImpl::asProvider(const SkShader::ContextRec &rec, + void *storage) const { + return new (storage) Provider(); +} + +size_t SkNormalBevelSourceImpl::providerSize(const SkShader::ContextRec&) const { + return sizeof(Provider); +} + +void SkNormalBevelSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + for (int i = 0; i < count; i++) { + output[i] = {0.0f, 0.0f, 1.0f}; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkNormalBevelSourceImpl::CreateProc(SkReadBuffer& buf) { + + auto type = static_cast<SkNormalSource::BevelType>(buf.readInt()); + SkScalar width = buf.readScalar(); + SkScalar height = buf.readScalar(); + + return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height); +} + +void SkNormalBevelSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + buf.writeInt(static_cast<int>(fType)); + buf.writeScalar(fWidth); + buf.writeScalar(fHeight); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeBevel(BevelType type, SkScalar width, SkScalar height) { + /* TODO make sure this checks are tolerant enough to account for loss of conversion when GPUs + use 16-bit float types. We don't want to assume stuff is non-zero on the GPU and be wrong.*/ + SkASSERT(width > 0.0f && !SkScalarNearlyZero(width)); + if (SkScalarNearlyZero(height)) { + return SkNormalSource::MakeFlat(); + } + + return sk_make_sp<SkNormalBevelSourceImpl>(type, width, height); +} diff --git a/src/core/SkNormalBevelSource.h b/src/core/SkNormalBevelSource.h new file mode 100644 index 0000000000..d133738bce --- /dev/null +++ b/src/core/SkNormalBevelSource.h @@ -0,0 +1,57 @@ +/* + * 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 SkNormalBevelSource_DEFINED +#define SkNormalBevelSource_DEFINED + +#include "SkNormalSource.h" + +class SK_API SkNormalBevelSourceImpl : public SkNormalSource { +public: + SkNormalBevelSourceImpl(BevelType type, SkScalar width, SkScalar height) + : fType(type) + , fWidth(width) + , fHeight(height) {} + +#if SK_SUPPORT_GPU + sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override; +#endif + + SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec, + void* storage) const override; + size_t providerSize(const SkShader::ContextRec& rec) const override; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalBevelSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(); + + virtual ~Provider(); + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + + private: + typedef SkNormalSource::Provider INHERITED; + + }; + + SkNormalSource::BevelType fType; + SkScalar fWidth; + SkScalar fHeight; + + friend class SkNormalSource; + + typedef SkNormalSource INHERITED; +}; + + +#endif diff --git a/src/core/SkNormalFlatSource.cpp b/src/core/SkNormalFlatSource.cpp new file mode 100644 index 0000000000..fcb1a4f3ad --- /dev/null +++ b/src/core/SkNormalFlatSource.cpp @@ -0,0 +1,107 @@ +/* + * 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 "SkNormalFlatSource.h" + +#include "SkNormalSource.h" +#include "SkPoint3.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +#if SK_SUPPORT_GPU +#include "GrInvariantOutput.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" + +class NormalFlatFP : public GrFragmentProcessor { +public: + NormalFlatFP() { + this->initClassID<NormalFlatFP>(); + } + + class GLSLNormalFlatFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalFlatFP() {} + + void emitCode(EmitArgs& args) override { + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + + fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor); + } + + static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + b->add32(0x0); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {} + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLNormalFlatFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "NormalFlatFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); + } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalFlatFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + return true; + } +}; + +sk_sp<GrFragmentProcessor> SkNormalFlatSourceImpl::asFragmentProcessor( + const SkShader::AsFPArgs&) const { + + return sk_make_sp<NormalFlatFP>(); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +SkNormalFlatSourceImpl::Provider::Provider() {} + +SkNormalFlatSourceImpl::Provider::~Provider() {} + +SkNormalSource::Provider* SkNormalFlatSourceImpl::asProvider(const SkShader::ContextRec &rec, + void *storage) const { + return new (storage) Provider(); +} + +size_t SkNormalFlatSourceImpl::providerSize(const SkShader::ContextRec&) const { + return sizeof(Provider); +} + +void SkNormalFlatSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + for (int i = 0; i < count; i++) { + output[i] = {0.0f, 0.0f, 1.0f}; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkNormalFlatSourceImpl::CreateProc(SkReadBuffer& buf) { + return sk_make_sp<SkNormalFlatSourceImpl>(); +} + +void SkNormalFlatSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFlat() { + return sk_make_sp<SkNormalFlatSourceImpl>(); +} diff --git a/src/core/SkNormalFlatSource.h b/src/core/SkNormalFlatSource.h new file mode 100644 index 0000000000..e1295596b9 --- /dev/null +++ b/src/core/SkNormalFlatSource.h @@ -0,0 +1,48 @@ +/* + * 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 SkNormalFlatSource_DEFINED +#define SkNormalFlatSource_DEFINED + +#include "SkNormalSource.h" + +class SK_API SkNormalFlatSourceImpl : public SkNormalSource { +public: + SkNormalFlatSourceImpl(){} + +#if SK_SUPPORT_GPU + sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override; +#endif + + SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec, + void* storage) const override; + size_t providerSize(const SkShader::ContextRec& rec) const override; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalFlatSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(); + + virtual ~Provider(); + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + + private: + typedef SkNormalSource::Provider INHERITED; + }; + + friend class SkNormalSource; + + typedef SkNormalSource INHERITED; +}; + +#endif diff --git a/src/core/SkNormalMapSource.cpp b/src/core/SkNormalMapSource.cpp new file mode 100644 index 0000000000..2ecf1d310d --- /dev/null +++ b/src/core/SkNormalMapSource.cpp @@ -0,0 +1,265 @@ +/* + * 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 "SkNormalMapSource.h" + +#include "SkLightingShader.h" +#include "SkMatrix.h" +#include "SkNormalSource.h" +#include "SkPM4f.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +#if SK_SUPPORT_GPU +#include "GrCoordTransform.h" +#include "GrInvariantOutput.h" +#include "GrTextureParams.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "SkGr.h" + +class NormalMapFP : public GrFragmentProcessor { +public: + NormalMapFP(sk_sp<GrFragmentProcessor> mapFP, const SkMatrix& invCTM) + : fInvCTM(invCTM) { + this->registerChildProcessor(mapFP); + + this->initClassID<NormalMapFP>(); + } + + class GLSLNormalMapFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalMapFP() + : fColumnMajorInvCTM22{0.0f} {} + + void emitCode(EmitArgs& args) override { + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + + // add uniform + const char* xformUniName = nullptr; + fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat22f_GrSLType, + kDefault_GrSLPrecision, "Xform", &xformUniName); + + SkString dstNormalColorName("dstNormalColor"); + this->emitChild(0, nullptr, &dstNormalColorName, args); + fragBuilder->codeAppendf("vec3 normal = normalize(%s.rgb - vec3(0.5));", + dstNormalColorName.c_str()); + + // If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0 + fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {"); + fragBuilder->codeAppendf(" %s = normalize(vec4(0.0, 0.0, normal.z, 0.0));", + args.fOutputColor); + // Else, Normalizing the transformed X and Y, while keeping constant both Z and the + // vector's angle in the XY plane. This maintains the "slope" for the surface while + // appropriately rotating the normal regardless of any anisotropic scaling that occurs. + // Here, we call 'scaling factor' the number that must divide the transformed X and Y so + // that the normal's length remains equal to 1. + fragBuilder->codeAppend( "} else {"); + fragBuilder->codeAppendf(" vec2 transformed = %s * normal.xy;", + xformUniName); + fragBuilder->codeAppend( " float scalingFactorSquared = " + "( (transformed.x * transformed.x) " + "+ (transformed.y * transformed.y) )" + "/(1.0 - (normal.z * normal.z));"); + fragBuilder->codeAppendf(" %s = vec4(transformed*inversesqrt(scalingFactorSquared)," + "normal.z, 0.0);", + args.fOutputColor); + fragBuilder->codeAppend( "}"); + } + + static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + b->add32(0x0); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + + const SkMatrix& invCTM = normalMapFP.invCTM(); + fColumnMajorInvCTM22[0] = invCTM.get(SkMatrix::kMScaleX); + fColumnMajorInvCTM22[1] = invCTM.get(SkMatrix::kMSkewY); + fColumnMajorInvCTM22[2] = invCTM.get(SkMatrix::kMSkewX); + fColumnMajorInvCTM22[3] = invCTM.get(SkMatrix::kMScaleY); + pdman.setMatrix2f(fXformUni, fColumnMajorInvCTM22); + } + + private: + float fColumnMajorInvCTM22[4]; + GrGLSLProgramDataManager::UniformHandle fXformUni; + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLNormalMapFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "NormalMapFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); + } + + const SkMatrix& invCTM() const { return fInvCTM; } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + return fInvCTM == normalMapFP.fInvCTM; + } + + SkMatrix fInvCTM; +}; + +sk_sp<GrFragmentProcessor> SkNormalMapSourceImpl::asFragmentProcessor( + const SkShader::AsFPArgs& args) const { + sk_sp<GrFragmentProcessor> mapFP = fMapShader->asFragmentProcessor(args); + if (!mapFP) { + return nullptr; + } + + return sk_make_sp<NormalMapFP>(std::move(mapFP), fInvCTM); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +SkNormalMapSourceImpl::Provider::Provider(const SkNormalMapSourceImpl& source, + SkShader::Context* mapContext, + SkPaint* overridePaint) + : fSource(source) + , fMapContext(mapContext) + , fOverridePaint(overridePaint) {} + +SkNormalMapSourceImpl::Provider::~Provider() { + fMapContext->~Context(); + fOverridePaint->~SkPaint(); +} + +SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider( + const SkShader::ContextRec &rec, void *storage) const { + SkMatrix normTotalInv; + if (!this->computeNormTotalInverse(rec, &normTotalInv)) { + return nullptr; + } + + // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd + void* paintStorage = (char*)storage + sizeof(Provider); + SkPaint* overridePaint = new (paintStorage) SkPaint(*(rec.fPaint)); + overridePaint->setAlpha(0xFF); + SkShader::ContextRec overrideRec(*overridePaint, *(rec.fMatrix), rec.fLocalMatrix, + rec.fPreferredDstType); + + void* mapContextStorage = (char*) paintStorage + sizeof(SkPaint); + SkShader::Context* context = fMapShader->createContext(overrideRec, mapContextStorage); + if (!context) { + return nullptr; + } + + return new (storage) Provider(*this, context, overridePaint); +} + +size_t SkNormalMapSourceImpl::providerSize(const SkShader::ContextRec& rec) const { + return sizeof(Provider) + sizeof(SkPaint) + fMapShader->contextSize(rec); +} + +bool SkNormalMapSourceImpl::computeNormTotalInverse(const SkShader::ContextRec& rec, + SkMatrix* normTotalInverse) const { + SkMatrix total; + total.setConcat(*rec.fMatrix, fMapShader->getLocalMatrix()); + + const SkMatrix* m = &total; + if (rec.fLocalMatrix) { + total.setConcat(*m, *rec.fLocalMatrix); + m = &total; + } + return m->invert(normTotalInverse); +} + +#define BUFFER_MAX 16 +void SkNormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + SkPMColor tmpNormalColors[BUFFER_MAX]; + + do { + int n = SkTMin(count, BUFFER_MAX); + + fMapContext->shadeSpan(x, y, tmpNormalColors, n); + + for (int i = 0; i < n; i++) { + SkPoint3 tempNorm; + + tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, + SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, + SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); + + tempNorm.normalize(); + + + if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { + SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); + + // Normalizing the transformed X and Y, while keeping constant both Z and the + // vector's angle in the XY plane. This maintains the "slope" for the surface while + // appropriately rotating the normal for any anisotropic scaling that occurs. + // Here, we call scaling factor the number that must divide the transformed X and Y + // so that the normal's length remains equal to 1. + SkScalar scalingFactorSquared = + (SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY)) + / (1.0f - SkScalarSquare(tempNorm.fZ)); + SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared)); + + output[i].fX = transformed.fX * invScalingFactor; + output[i].fY = transformed.fY * invScalingFactor; + output[i].fZ = tempNorm.fZ; + } else { + output[i] = {0.0f, 0.0f, tempNorm.fZ}; + output[i].normalize(); + } + + SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f)); + } + + output += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkNormalMapSourceImpl::CreateProc(SkReadBuffer& buf) { + + sk_sp<SkShader> mapShader = buf.readFlattenable<SkShader>(); + + SkMatrix invCTM; + buf.readMatrix(&invCTM); + + return sk_make_sp<SkNormalMapSourceImpl>(std::move(mapShader), invCTM); +} + +void SkNormalMapSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + buf.writeFlattenable(fMapShader.get()); + buf.writeMatrix(fInvCTM); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm) { + SkMatrix invCTM; + + if (!ctm.invert(&invCTM) || !map) { + return nullptr; + } + + return sk_make_sp<SkNormalMapSourceImpl>(std::move(map), invCTM); +} diff --git a/src/core/SkNormalMapSource.h b/src/core/SkNormalMapSource.h new file mode 100644 index 0000000000..5908369fc7 --- /dev/null +++ b/src/core/SkNormalMapSource.h @@ -0,0 +1,62 @@ +/* + * 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 SkNormalMapSource_DEFINED +#define SkNormalMapSource_DEFINED + +#include "SkNormalSource.h" + +class SkNormalMapSourceImpl : public SkNormalSource { +public: + SkNormalMapSourceImpl(sk_sp<SkShader> mapShader, const SkMatrix& invCTM) + : fMapShader(std::move(mapShader)) + , fInvCTM(invCTM) {} + +#if SK_SUPPORT_GPU + sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override; +#endif + + SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec, + void* storage) const override; + size_t providerSize(const SkShader::ContextRec& rec) const override; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalMapSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + + bool computeNormTotalInverse(const SkShader::ContextRec& rec, SkMatrix* normTotalInverse) const; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(const SkNormalMapSourceImpl& source, SkShader::Context* mapContext, + SkPaint* overridePaint); + + virtual ~Provider() override; + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + + private: + const SkNormalMapSourceImpl& fSource; + SkShader::Context* fMapContext; + + SkPaint* fOverridePaint; + + typedef SkNormalSource::Provider INHERITED; + }; + + sk_sp<SkShader> fMapShader; + SkMatrix fInvCTM; // Inverse of the canvas total matrix, used for rotating normals. + + friend class SkNormalSource; + + typedef SkNormalSource INHERITED; +}; + +#endif + diff --git a/src/core/SkNormalSource.cpp b/src/core/SkNormalSource.cpp index 38cf0bf0af..2bea7baf6f 100644 --- a/src/core/SkNormalSource.cpp +++ b/src/core/SkNormalSource.cpp @@ -5,458 +5,20 @@ * found in the LICENSE file. */ -#include "SkError.h" -#include "SkErrorInternals.h" -#include "SkLightingShader.h" -#include "SkMatrix.h" +#include "SkNormalBevelSource.h" +#include "SkNormalFlatSource.h" +#include "SkNormalMapSource.h" #include "SkNormalSource.h" -#include "SkPM4f.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" -// Genretating vtable +// Generating vtable SkNormalSource::~SkNormalSource() {} -/////////////////////////////////////////////////////////////////////////////// - -class NormalMapSourceImpl : public SkNormalSource { -public: - NormalMapSourceImpl(sk_sp<SkShader> mapShader, const SkMatrix& invCTM) - : fMapShader(std::move(mapShader)) - , fInvCTM(invCTM) {} - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override; -#endif - - SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec, - void* storage) const override; - - size_t providerSize(const SkShader::ContextRec& rec) const override; - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(NormalMapSourceImpl) - -protected: - void flatten(SkWriteBuffer& buf) const override; - - bool computeNormTotalInverse(const SkShader::ContextRec& rec, SkMatrix* normTotalInverse) const; - -private: - class Provider : public SkNormalSource::Provider { - public: - Provider(const NormalMapSourceImpl& source, SkShader::Context* mapContext, - SkPaint* overridePaint); - - virtual ~Provider() override; - - void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; - - private: - const NormalMapSourceImpl& fSource; - SkShader::Context* fMapContext; - - SkPaint* fOverridePaint; - - typedef SkNormalSource::Provider INHERITED; - }; - - sk_sp<SkShader> fMapShader; - SkMatrix fInvCTM; // Inverse of the canvas total matrix, used for rotating normals. - - friend class SkNormalSource; - - typedef SkNormalSource INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -#include "GrCoordTransform.h" -#include "GrInvariantOutput.h" -#include "GrTextureParams.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "SkGr.h" - -class NormalMapFP : public GrFragmentProcessor { -public: - NormalMapFP(sk_sp<GrFragmentProcessor> mapFP, const SkMatrix& invCTM) - : fInvCTM(invCTM) { - this->registerChildProcessor(mapFP); - - this->initClassID<NormalMapFP>(); - } - - class GLSLNormalMapFP : public GrGLSLFragmentProcessor { - public: - GLSLNormalMapFP() - : fColumnMajorInvCTM22{0.0f} {} - - void emitCode(EmitArgs& args) override { - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - - // add uniform - const char* xformUniName = nullptr; - fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat22f_GrSLType, - kDefault_GrSLPrecision, "Xform", &xformUniName); - - SkString dstNormalColorName("dstNormalColor"); - this->emitChild(0, nullptr, &dstNormalColorName, args); - fragBuilder->codeAppendf("vec3 normal = normalize(%s.rgb - vec3(0.5));", - dstNormalColorName.c_str()); - - // If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0 - fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {"); - fragBuilder->codeAppendf(" %s = normalize(vec4(0.0, 0.0, normal.z, 0.0));", - args.fOutputColor); - // Else, Normalizing the transformed X and Y, while keeping constant both Z and the - // vector's angle in the XY plane. This maintains the "slope" for the surface while - // appropriately rotating the normal regardless of any anisotropic scaling that occurs. - // Here, we call 'scaling factor' the number that must divide the transformed X and Y so - // that the normal's length remains equal to 1. - fragBuilder->codeAppend( "} else {"); - fragBuilder->codeAppendf(" vec2 transformed = %s * normal.xy;", - xformUniName); - fragBuilder->codeAppend( " float scalingFactorSquared = " - "( (transformed.x * transformed.x) " - "+ (transformed.y * transformed.y) )" - "/(1.0 - (normal.z * normal.z));"); - fragBuilder->codeAppendf(" %s = vec4(transformed*inversesqrt(scalingFactorSquared)," - "normal.z, 0.0);", - args.fOutputColor); - fragBuilder->codeAppend( "}"); - } - - static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, - GrProcessorKeyBuilder* b) { - b->add32(0x0); - } - - protected: - void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { - const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); - - const SkMatrix& invCTM = normalMapFP.invCTM(); - fColumnMajorInvCTM22[0] = invCTM.get(SkMatrix::kMScaleX); - fColumnMajorInvCTM22[1] = invCTM.get(SkMatrix::kMSkewY); - fColumnMajorInvCTM22[2] = invCTM.get(SkMatrix::kMSkewX); - fColumnMajorInvCTM22[3] = invCTM.get(SkMatrix::kMScaleY); - pdman.setMatrix2f(fXformUni, fColumnMajorInvCTM22); - } - - private: - float fColumnMajorInvCTM22[4]; - GrGLSLProgramDataManager::UniformHandle fXformUni; - }; - - void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { - GLSLNormalMapFP::GenKey(*this, caps, b); - } - - const char* name() const override { return "NormalMapFP"; } - - void onComputeInvariantOutput(GrInvariantOutput* inout) const override { - inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); - } - - const SkMatrix& invCTM() const { return fInvCTM; } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; } - - bool onIsEqual(const GrFragmentProcessor& proc) const override { - const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); - return fInvCTM == normalMapFP.fInvCTM; - } - - SkMatrix fInvCTM; -}; - -sk_sp<GrFragmentProcessor> NormalMapSourceImpl::asFragmentProcessor( - const SkShader::AsFPArgs& args) const { - sk_sp<GrFragmentProcessor> mapFP = fMapShader->asFragmentProcessor(args); - if (!mapFP) { - return nullptr; - } - - return sk_make_sp<NormalMapFP>(std::move(mapFP), fInvCTM); -} - -#endif // SK_SUPPORT_GPU - -//////////////////////////////////////////////////////////////////////////// - -NormalMapSourceImpl::Provider::Provider(const NormalMapSourceImpl& source, - SkShader::Context* mapContext, - SkPaint* overridePaint) - : fSource(source) - , fMapContext(mapContext) - , fOverridePaint(overridePaint) {} - -NormalMapSourceImpl::Provider::~Provider() { - fMapContext->~Context(); - fOverridePaint->~SkPaint(); -} - -SkNormalSource::Provider* NormalMapSourceImpl::asProvider( - const SkShader::ContextRec &rec, void *storage) const { - SkMatrix normTotalInv; - if (!this->computeNormTotalInverse(rec, &normTotalInv)) { - return nullptr; - } - - // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd - void* paintStorage = (char*)storage + sizeof(Provider); - SkPaint* overridePaint = new (paintStorage) SkPaint(*(rec.fPaint)); - overridePaint->setAlpha(0xFF); - SkShader::ContextRec overrideRec(*overridePaint, *(rec.fMatrix), rec.fLocalMatrix, - rec.fPreferredDstType); - - void* mapContextStorage = (char*) paintStorage + sizeof(SkPaint); - SkShader::Context* context = fMapShader->createContext(overrideRec, mapContextStorage); - if (!context) { - return nullptr; - } - - return new (storage) Provider(*this, context, overridePaint); -} - -size_t NormalMapSourceImpl::providerSize(const SkShader::ContextRec& rec) const { - return sizeof(Provider) + sizeof(SkPaint) + fMapShader->contextSize(rec); -} - -bool NormalMapSourceImpl::computeNormTotalInverse(const SkShader::ContextRec& rec, - SkMatrix* normTotalInverse) const { - SkMatrix total; - total.setConcat(*rec.fMatrix, fMapShader->getLocalMatrix()); - - const SkMatrix* m = &total; - if (rec.fLocalMatrix) { - total.setConcat(*m, *rec.fLocalMatrix); - m = &total; - } - return m->invert(normTotalInverse); -} - -#define BUFFER_MAX 16 -void NormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], - int count) const { - SkPMColor tmpNormalColors[BUFFER_MAX]; - - do { - int n = SkTMin(count, BUFFER_MAX); - - fMapContext->shadeSpan(x, y, tmpNormalColors, n); - - for (int i = 0; i < n; i++) { - SkPoint3 tempNorm; - - tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, - SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, - SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); - - tempNorm.normalize(); - - - if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { - SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); - - // Normalizing the transformed X and Y, while keeping constant both Z and the - // vector's angle in the XY plane. This maintains the "slope" for the surface while - // appropriately rotating the normal for any anisotropic scaling that occurs. - // Here, we call scaling factor the number that must divide the transformed X and Y - // so that the normal's length remains equal to 1. - SkScalar scalingFactorSquared = - (SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY)) - / (1.0f - SkScalarSquare(tempNorm.fZ)); - SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared)); - - output[i].fX = transformed.fX * invScalingFactor; - output[i].fY = transformed.fY * invScalingFactor; - output[i].fZ = tempNorm.fZ; - } else { - output[i] = {0.0f, 0.0f, tempNorm.fZ}; - output[i].normalize(); - } - - SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f)); - } - - output += n; - x += n; - count -= n; - } while (count > 0); -} - -//////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkFlattenable> NormalMapSourceImpl::CreateProc(SkReadBuffer& buf) { - - sk_sp<SkShader> mapShader = buf.readFlattenable<SkShader>(); - - SkMatrix invCTM; - buf.readMatrix(&invCTM); - - return sk_make_sp<NormalMapSourceImpl>(std::move(mapShader), invCTM); -} - -void NormalMapSourceImpl::flatten(SkWriteBuffer& buf) const { - this->INHERITED::flatten(buf); - - buf.writeFlattenable(fMapShader.get()); - buf.writeMatrix(fInvCTM); -} - -//////////////////////////////////////////////////////////////////////////// - -sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm) { - SkMatrix invCTM; - - if (!ctm.invert(&invCTM) || !map) { - return nullptr; - } - - return sk_make_sp<NormalMapSourceImpl>(std::move(map), invCTM); -} - -/////////////////////////////////////////////////////////////////////////////// - -class SK_API NormalFlatSourceImpl : public SkNormalSource { -public: - NormalFlatSourceImpl(){} - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(const SkShader::AsFPArgs&) const override; -#endif - - SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec, - void* storage) const override; - size_t providerSize(const SkShader::ContextRec& rec) const override; - - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(NormalFlatSourceImpl) - -protected: - void flatten(SkWriteBuffer& buf) const override; - -private: - class Provider : public SkNormalSource::Provider { - public: - Provider(); - - virtual ~Provider(); - - void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; - - private: - typedef SkNormalSource::Provider INHERITED; - }; - - friend class SkNormalSource; - - typedef SkNormalSource INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////// - -#if SK_SUPPORT_GPU - -class NormalFlatFP : public GrFragmentProcessor { -public: - NormalFlatFP() { - this->initClassID<NormalFlatFP>(); - } - - class GLSLNormalFlatFP : public GrGLSLFragmentProcessor { - public: - GLSLNormalFlatFP() {} - - void emitCode(EmitArgs& args) override { - GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; - - fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor); - } - - static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, - GrProcessorKeyBuilder* b) { - b->add32(0x0); - } - - protected: - void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {} - }; - - void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { - GLSLNormalFlatFP::GenKey(*this, caps, b); - } - - const char* name() const override { return "NormalFlatFP"; } - - void onComputeInvariantOutput(GrInvariantOutput* inout) const override { - inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); - } - -private: - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalFlatFP; } - - bool onIsEqual(const GrFragmentProcessor& proc) const override { - return true; - } -}; - -sk_sp<GrFragmentProcessor> NormalFlatSourceImpl::asFragmentProcessor( - const SkShader::AsFPArgs&) const { - - return sk_make_sp<NormalFlatFP>(); -} - -#endif // SK_SUPPORT_GPU - -//////////////////////////////////////////////////////////////////////////// - -NormalFlatSourceImpl::Provider::Provider() {} - -NormalFlatSourceImpl::Provider::~Provider() {} - -SkNormalSource::Provider* NormalFlatSourceImpl::asProvider(const SkShader::ContextRec &rec, - void *storage) const { - return new (storage) Provider(); -} - -size_t NormalFlatSourceImpl::providerSize(const SkShader::ContextRec&) const { - return sizeof(Provider); -} - -void NormalFlatSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], - int count) const { - for (int i = 0; i < count; i++) { - output[i] = {0.0f, 0.0f, 1.0f}; - } -} - -//////////////////////////////////////////////////////////////////////////////// - -sk_sp<SkFlattenable> NormalFlatSourceImpl::CreateProc(SkReadBuffer& buf) { - return sk_make_sp<NormalFlatSourceImpl>(); -} - -void NormalFlatSourceImpl::flatten(SkWriteBuffer& buf) const { - this->INHERITED::flatten(buf); -} - -//////////////////////////////////////////////////////////////////////////// - -sk_sp<SkNormalSource> SkNormalSource::MakeFlat() { - return sk_make_sp<NormalFlatSourceImpl>(); -} - -//////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalMapSourceImpl) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalFlatSourceImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalMapSourceImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalFlatSourceImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalBevelSourceImpl) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END //////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkNormalSource.h b/src/core/SkNormalSource.h index f8db496103..8cbb3e4fae 100644 --- a/src/core/SkNormalSource.h +++ b/src/core/SkNormalSource.h @@ -9,6 +9,14 @@ #define SkNormalSource_DEFINED #include "SkFlattenable.h" +#include "SkShader.h" + +class SkMatrix; +struct SkPoint3; + +#if SK_SUPPORT_GPU +class GrFragmentProcessor; +#endif /** Abstract class that generates or reads in normals for use by SkLightingShader. */ @@ -64,6 +72,52 @@ public: */ static sk_sp<SkNormalSource> MakeFlat(); + /** This enum specifies the shape of the bevel. All bevels output <0, 0, 1> as the surface + * normal for any point more than 'width' away from any edge. + * + * Mathematical details: + * For the purpose of describing the shape of the bevel, we define 'w' to be the given width of + * the bevel, and 'h' to be the given height. We will assume the shape is rotated such that the + * point being shaded as well as the closest point in the shape's edge to that point are in the + * x-axis, and the shape is translated so that the aforementioned point in the edge is at + * coordinates (w, 0, 0) and the end of the bevel is at (0, 0, h). + * + */ + enum class BevelType { + /* This bevel simulates a surface that is slanted from the shape's edges inwards, linearly. + * + * Mathematical details: + * This bevel follows a straight line from (w, 0, 0) to (0, 0, h). + */ + kLinear, + /* This bevel simulates a surface that rounds off at the shape's edges, smoothly becoming + * perpendicular to the x-y plane. + * + * Mathematical details: + * This bevel follows the only quadratic bezier curve whose start point is at (w, 0, 0), + * control point is at (w, 0, h), and end point is at (0, 0, h). + */ + kRoundedOut, + /* This bevel simulates a surface that sharply becomes perpendicular to the x-y plane when + * at 'width' units from the nearest edge, and then rounds off towards the shape's + * edge, smoothly becoming parallel to the x-y plane. + * + * Mathematical details: + * This bevel follows the only quadratic bezier curve whose start point is at (w, 0, 0), + * control point is at (0, 0, 0), and end point is at (0, 0, h). + */ + kRoundedIn + }; + /** Returns a normal source that generates a bevel for the given shape. UNIMPLEMENTED: Will + return straight-up normals only. + + @param type the type of bevel to add + @param width the width of the bevel, in source space. Must be positive. + @param height the height of the plateau, in source space. Can be positive, negative, + or zero. A negative height means the simulated bevels slope downwards. + */ + static sk_sp<SkNormalSource> MakeBevel(BevelType, SkScalar width, SkScalar height); + SK_DEFINE_FLATTENABLE_TYPE(SkNormalSource) SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() }; diff --git a/tests/SerializationTest.cpp b/tests/SerializationTest.cpp index f806c4a5cc..a6be1889a5 100644 --- a/tests/SerializationTest.cpp +++ b/tests/SerializationTest.cpp @@ -608,6 +608,17 @@ DEF_TEST(Serialization, reporter) { fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); } + + // Test NormalBevelSource serialization + { + sk_sp<SkNormalSource> bevelSource = SkNormalSource::MakeBevel( + SkNormalSource::BevelType::kLinear, 2.0f, 5.0f); + + SkAutoTUnref<SkNormalSource>(TestFlattenableSerialization(bevelSource.get(), true, + reporter)); + // TODO test equality? + + } } /////////////////////////////////////////////////////////////////////////////////////////////////// |