'<(skia_src_path)/core/SkNormalFlatSource.h',
'<(skia_src_path)/core/SkNormalSource.cpp',
'<(skia_src_path)/core/SkNormalSource.h',
+ '<(skia_src_path)/core/SkNormalSourcePriv.h',
'<(skia_src_path)/core/SkNx.h',
'<(skia_src_path)/core/SkOpts.cpp',
'<(skia_src_path)/core/SkOpts.h',
'include_dirs': [
'../include/effects',
'../include/client/android',
+ '../include/gpu',
'../include/images',
'../include/ports',
'../include/private',
'../include/utils',
'../include/utils/win',
'../src/core',
+ '../src/gpu',
'../src/image',
'../src/lazy',
'../src/ports',
GrFragmentProcessor()
: INHERITED()
+ , fUsesDistanceVectorField(false)
, fUsesLocalCoords(false)
, fNumTexturesExclChildren(0)
, fNumBuffersExclChildren(0)
/** Do any of the coordtransforms for this processor require local coords? */
bool usesLocalCoords() const { return fUsesLocalCoords; }
+ /** Does this FP need a vector to the nearest edge? */
+ bool usesDistanceVectorField() const { return fUsesDistanceVectorField; }
+
/** Returns true if this and other processor conservatively draw identically. It can only return
true when the two processor are of the same subclass (i.e. they return the same object from
from getFactory()).
*/
virtual void onComputeInvariantOutput(GrInvariantOutput* inout) const = 0;
+ /* Sub-classes should set this to true in their constructors if they need access to a distance
+ * vector field to the nearest edge
+ */
+ bool fUsesDistanceVectorField;
+
private:
void notifyRefCntIsZero() const final;
bool getAllowSRGBInputs() const { return fAllowSRGBInputs; }
/**
+ * Does one of the fragment processors need a field of distance vectors to the nearest edge?
+ */
+ bool usesDistanceVectorField() const { return fUsesDistanceVectorField; }
+
+ /**
* Should rendering be gamma-correct, end-to-end. Causes sRGB render targets to behave
* as such (with linear blending), and sRGB inputs to be filtered and decoded correctly.
*/
*/
void addColorFragmentProcessor(sk_sp<GrFragmentProcessor> fp) {
SkASSERT(fp);
+ fUsesDistanceVectorField |= fp->usesDistanceVectorField();
fColorFragmentProcessors.push_back(std::move(fp));
}
*/
void addCoverageFragmentProcessor(sk_sp<GrFragmentProcessor> fp) {
SkASSERT(fp);
+ fUsesDistanceVectorField |= fp->usesDistanceVectorField();
fCoverageFragmentProcessors.push_back(std::move(fp));
}
fAntiAlias = paint.fAntiAlias;
fDisableOutputConversionToSRGB = paint.fDisableOutputConversionToSRGB;
fAllowSRGBInputs = paint.fAllowSRGBInputs;
+ fUsesDistanceVectorField = paint.fUsesDistanceVectorField;
fColor = paint.fColor;
fColorFragmentProcessors = paint.fColorFragmentProcessors;
bool fAntiAlias;
bool fDisableOutputConversionToSRGB;
bool fAllowSRGBInputs;
+ bool fUsesDistanceVectorField;
GrColor4f fColor;
};
#include "SkNormalBevelSource.h"
#include "SkNormalSource.h"
+#include "SkNormalSourcePriv.h"
#include "SkPoint3.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
, fWidth(width)
, fHeight(height) {
this->initClassID<NormalBevelFP>();
+
+ fUsesDistanceVectorField = true;
}
- class GLSLNormalBevelFP : public GrGLSLFragmentProcessor {
+ class GLSLNormalBevelFP : public GLSLNormalFP {
public:
GLSLNormalBevelFP() {
fPrevWidth = SkFloatToScalar(0.0f);
fPrevHeight = SkFloatToScalar(0.0f);
}
- void emitCode(EmitArgs& args) override {
- GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+ void onEmitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
const char* widthUniName = nullptr;
fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
kDefault_GrSLPrecision, "Height", &heightUniName);
- fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
+ fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 1.0, 0.0);", args.fOutputColor);
}
static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
}
protected:
- void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
+ void setNormalData(const GrGLSLProgramDataManager& pdman,
+ const GrProcessor& proc) override {
const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
if (fPrevWidth != normalBevelFP.fWidth) {
#include "SkNormalFlatSource.h"
#include "SkNormalSource.h"
+#include "SkNormalSourcePriv.h"
#include "SkPoint3.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
this->initClassID<NormalFlatFP>();
}
- class GLSLNormalFlatFP : public GrGLSLFragmentProcessor {
+ class GLSLNormalFlatFP : public GLSLNormalFP {
public:
GLSLNormalFlatFP() {}
- void emitCode(EmitArgs& args) override {
- GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+ void onEmitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
}
}
protected:
- void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
+ void setNormalData(const GrGLSLProgramDataManager& pdman,
+ const GrProcessor& proc) override {}
};
void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
#include "SkLightingShader.h"
#include "SkMatrix.h"
#include "SkNormalSource.h"
+#include "SkNormalSourcePriv.h"
#include "SkPM4f.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
this->initClassID<NormalMapFP>();
}
- class GLSLNormalMapFP : public GrGLSLFragmentProcessor {
+ class GLSLNormalMapFP : public GLSLNormalFP {
public:
GLSLNormalMapFP()
: fColumnMajorInvCTM22{0.0f} {}
- void emitCode(EmitArgs& args) override {
- GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+ void onEmitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
// add uniform
}
protected:
- void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
+ void setNormalData(const GrGLSLProgramDataManager& pdman,
+ const GrProcessor& proc) override {
const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
const SkMatrix& invCTM = normalMapFP.invCTM();
/** 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 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.
--- /dev/null
+/*
+ * 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 SkNormalSourcePriv_DEFINED
+#define SkNormalSourcePriv_DEFINED
+
+#if SK_SUPPORT_GPU
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+/* GLSLFragmentProcessors for NormalSourceImpls must sub-class this class and override onEmitCode,
+ * and setNormalData calls, as well as all other calls FPs normally override, except for the 2
+ * defined in this superclass.
+ * This class exists to intercept emitCode calls and emit <0, 0, 1> if the FP requires a distance
+ * vector but the GP doesn't provide it. onSetData calls need to be intercepted too because
+ * uniform handlers will be invalid in subclasses where onEmitCode isn't called.
+ * We don't need to adjust the key here since the use of a given GP (through its class ID already in
+ * the key), will determine what code gets emitted here.
+ */
+class GLSLNormalFP : public GrGLSLFragmentProcessor {
+public:
+ GLSLNormalFP()
+ : fDidIntercept(false) {}
+
+ void emitCode(EmitArgs& args) final override {
+ if (args.fFp.usesDistanceVectorField() && !args.fGpImplementsDistanceVector) {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ fragBuilder->codeAppendf("// GLSLNormalFP intercepted emitCode call, GP does not "
+ "implement required distance vector feature\n");
+ fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
+
+ fDidIntercept = true;
+ } else {
+ this->onEmitCode(args);
+ }
+ }
+
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) final override {
+ if (!fDidIntercept) {
+ this->setNormalData(pdman, proc);
+ }
+ }
+
+protected:
+ virtual void onEmitCode(EmitArgs& args) = 0;
+ virtual void setNormalData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) = 0;
+
+private:
+ bool fDidIntercept;
+};
+#endif
+
+#endif
if (child->usesLocalCoords()) {
fUsesLocalCoords = true;
}
+ if (child->usesDistanceVectorField()) {
+ fUsesDistanceVectorField = true;
+ }
int index = fChildProcessors.count();
fChildProcessors.push_back(child.release());
if (builder.getAllowSRGBInputs()) {
pipeline->fFlags |= kAllowSRGBInputs_Flag;
}
+ if (builder.getUsesDistanceVectorField()) {
+ pipeline->fFlags |= kUsesDistanceVectorField_Flag;
+ }
if (args.fHasStencilClip) {
pipeline->fFlags |= kHasStencilClip_Flag;
}
bool getAllowSRGBInputs() const {
return SkToBool(fFlags & kAllowSRGBInputs_Flag);
}
+ bool usesDistanceVectorField() const {
+ return SkToBool(fFlags & kUsesDistanceVectorField_Flag);
+ }
bool hasStencilClip() const {
return SkToBool(fFlags & kHasStencilClip_Flag);
}
kSnapVertices_Flag = 0x2,
kDisableOutputConversionToSRGB_Flag = 0x4,
kAllowSRGBInputs_Flag = 0x8,
- kHasStencilClip_Flag = 0x10
+ kUsesDistanceVectorField_Flag = 0x10,
+ kHasStencilClip_Flag = 0x20,
};
typedef GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> RenderTarget;
paint.getDisableOutputConversionToSRGB());
this->setState(GrPipelineBuilder::kAllowSRGBInputs_Flag,
paint.getAllowSRGBInputs());
+ this->setState(GrPipelineBuilder::kUsesDistanceVectorField_Flag,
+ paint.usesDistanceVectorField());
}
//////////////////////////////////////////////////////////////////////////////s
*/
kAllowSRGBInputs_Flag = 0x08,
- kLast_Flag = kAllowSRGBInputs_Flag,
+ /**
+ * Signals that one or more FPs need access to the distance vector field to the nearest
+ * edge
+ */
+ kUsesDistanceVectorField_Flag = 0x10,
+
+ kLast_Flag = kUsesDistanceVectorField_Flag,
};
bool isHWAntialias() const { return SkToBool(fFlags & kHWAntialias_Flag); }
return SkToBool(fFlags & kDisableOutputConversionToSRGB_Flag); }
bool getAllowSRGBInputs() const {
return SkToBool(fFlags & kAllowSRGBInputs_Flag); }
+ bool getUsesDistanceVectorField() const {
+ return SkToBool(fFlags & kUsesDistanceVectorField_Flag); }
/**
* Enable render state settings.
return 0.0;
}
+ /* Sub-class should override and return true if this primitive processor implements the distance
+ * vector field, a field of vectors to the nearest point in the edge of the shape. */
+ virtual bool implementsDistanceVector() const { return false; }
+
protected:
GrPrimitiveProcessor() : fVertexStride(0) {}
inputColor,
childCoords,
childTexSamplers,
- childBufferSamplers);
+ childBufferSamplers,
+ args.fGpImplementsDistanceVector);
this->childProcessor(childIndex)->emitCode(childArgs);
fragBuilder->codeAppend("}\n");
const char* inputColor,
const GrGLSLTransformedCoordsArray& coords,
const SamplerHandle* texSamplers,
- const SamplerHandle* bufferSamplers)
+ const SamplerHandle* bufferSamplers,
+ bool gpImplementsDistanceVector)
: fFragBuilder(fragBuilder)
, fUniformHandler(uniformHandler)
, fGLSLCaps(caps)
, fInputColor(inputColor)
, fCoords(coords)
, fTexSamplers(texSamplers)
- , fBufferSamplers(bufferSamplers) {}
+ , fBufferSamplers(bufferSamplers)
+ , fGpImplementsDistanceVector(gpImplementsDistanceVector){}
GrGLSLFPFragmentBuilder* fFragBuilder;
GrGLSLUniformHandler* fUniformHandler;
const GrGLSLCaps* fGLSLCaps;
const GrGLSLTransformedCoordsArray& fCoords;
const SamplerHandle* fTexSamplers;
const SamplerHandle* fBufferSamplers;
+ bool fGpImplementsDistanceVector;
};
virtual void emitCode(EmitArgs&) = 0;
}
}
+const char* GrGLSLFragmentShaderBuilder::distanceVectorName() const {
+ return "fsDistanceVector";
+}
+
void GrGLSLFragmentShaderBuilder::appendOffsetToSample(const char* sampleIdx, Coordinates coords) {
SkASSERT(fProgramBuilder->header().fSamplePatternKey);
SkDEBUGCODE(fUsedProcessorFeatures |= GrProcessor::kSampleLocations_RequiredFeature);
*/
virtual void maskSampleCoverage(const char* mask, bool invert = false) = 0;
+ /** Returns a variable name that represents a vector to the nearest edge of the shape, in source
+ space coordinates. */
+ virtual const char* distanceVectorName() const = 0;
+
/**
* Fragment procs with child procs should call these functions before/after calling emitCode
* on a child proc.
virtual SkString ensureFSCoords2D(const GrGLSLTransformedCoordsArray& coords,
int index) override;
const char* fragmentPosition() override;
+ const char* distanceVectorName() const override;
// GrGLSLFPFragmentBuilder interface.
void appendOffsetToSample(const char* sampleIdx, Coordinates) override;
bool fHasSecondaryOutput;
uint8_t fUsedSampleOffsetArrays;
bool fHasInitializedSampleMask;
+ SkString fDistanceVectorOutput;
#ifdef SK_DEBUG
// some state to verify shaders and effects are consistent, this is reset between effects by
const GrPrimitiveProcessor& gp,
const char* outputColor,
const char* outputCoverage,
+ const char* distanceVectorName,
const SamplerHandle* texSamplers,
const SamplerHandle* bufferSamplers,
const TransformsIn& transformsIn,
, fGP(gp)
, fOutputColor(outputColor)
, fOutputCoverage(outputCoverage)
+ , fDistanceVectorName(distanceVectorName)
, fTexSamplers(texSamplers)
, fBufferSamplers(bufferSamplers)
, fTransformsIn(transformsIn)
const GrPrimitiveProcessor& fGP;
const char* fOutputColor;
const char* fOutputCoverage;
+ const char* fDistanceVectorName;
const SamplerHandle* fTexSamplers;
const SamplerHandle* fBufferSamplers;
const TransformsIn& fTransformsIn;
*/
virtual void emitCode(EmitArgs&) = 0;
-
/** A GrGLSLPrimitiveProcessor instance can be reused with any GrGLSLPrimitiveProcessor that
produces the same stage key; this function reads data from a GrGLSLPrimitiveProcessor and
uploads any uniform variables required by the shaders created in emitCode(). The
this->nameExpression(outputColor, "outputColor");
this->nameExpression(outputCoverage, "outputCoverage");
+ const char* distanceVectorName = nullptr;
+ if (this->fPipeline.usesDistanceVectorField() && proc.implementsDistanceVector()) {
+ distanceVectorName = fFS.distanceVectorName();
+ fFS.codeAppend( "// Un-normalized vector to the closed geometric edge (in source space)\n");
+ fFS.codeAppendf("vec2 %s;", distanceVectorName);
+ }
+
// Enclose custom code in a block to avoid namespace conflicts
SkString openBrace;
openBrace.printf("{ // Stage %d, %s\n", fStageIndex, proc.name());
proc,
outputColor->c_str(),
outputCoverage->c_str(),
+ distanceVectorName,
texSamplers.begin(),
bufferSamplers.begin(),
fCoordTransforms,
input.isOnes() ? nullptr : input.c_str(),
fOutCoords[index],
texSamplers.begin(),
- bufferSamplers.begin());
+ bufferSamplers.begin(),
+ this->primitiveProcessor().implementsDistanceVector());
+
fragProc->emitCode(args);
// We have to check that effects and the code they emit are consistent, ie if an effect