Index: src/core/SkLightingShader.cpp |
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp |
index 02f14b34ddc9215c72070aeedc769a8f36418cd2..92f41ad8c8ac77c44e835d986571fe7be3935374 100644 |
--- a/src/core/SkLightingShader.cpp |
+++ b/src/core/SkLightingShader.cpp |
@@ -21,15 +21,11 @@ |
/* |
SkLightingShader TODOs: |
- support other than clamp mode |
- allow 'diffuse' & 'normal' to be of different dimensions? |
support different light types |
support multiple lights |
- enforce normal map is 4 channel |
- use SkImages instead if SkBitmaps |
+ fix non-opaque diffuse textures |
To Test: |
- non-opaque diffuse textures |
A8 diffuse textures |
down & upsampled draws |
*/ |
@@ -81,6 +77,7 @@ public: |
private: |
SkShader::Context* fDiffuseContext; |
SkNormalSource::Provider* fNormalProvider; |
+ SkColor fPaintColor; |
uint32_t fFlags; |
void* fHeapAllocated; |
@@ -121,6 +118,9 @@ private: |
#include "SkGr.h" |
#include "SkGrPriv.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) { |
@@ -180,9 +180,13 @@ public: |
lightDirUniName); |
// diffuse light |
fragBuilder->codeAppendf("vec3 result = %s*diffuseColor.rgb*NdotL;", lightColorUniName); |
- // ambient light |
- fragBuilder->codeAppendf("result += %s;", ambientColorUniName); |
- fragBuilder->codeAppendf("%s = vec4(result.rgb, diffuseColor.a);", args.fOutputColor); |
+ // ambient light (multiplied by input color's alpha because we're working in premul'd |
+ // space) |
+ fragBuilder->codeAppendf("result += diffuseColor.a * %s;", 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 GrGLSLCaps&, |
@@ -270,18 +274,25 @@ sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor( |
return nullptr; |
} |
- sk_sp<GrFragmentProcessor> fpPipeline[] = { |
+ if (fDiffuseShader) { |
+ sk_sp<GrFragmentProcessor> fpPipeline[] = { |
fDiffuseShader->asFragmentProcessor(context, viewM, localMatrix, filterQuality, |
gammaTreatment), |
sk_make_sp<LightingFP>(std::move(normalFP), fLights) |
- }; |
- if(!fpPipeline[0]) { |
- return nullptr; |
- } |
- |
- sk_sp<GrFragmentProcessor> inner(GrFragmentProcessor::RunInSeries(fpPipeline, 2)); |
+ }; |
+ if(!fpPipeline[0]) { |
+ return nullptr; |
+ } |
- return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); |
+ 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 |
@@ -289,7 +300,7 @@ sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor( |
//////////////////////////////////////////////////////////////////////////// |
bool SkLightingShaderImpl::isOpaque() const { |
- return fDiffuseShader->isOpaque(); |
+ return (fDiffuseShader ? fDiffuseShader->isOpaque() : false); |
} |
SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( |
@@ -308,13 +319,16 @@ SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( |
flags |= kOpaqueAlpha_Flag; |
} |
+ fPaintColor = rec.fPaint->getColor(); |
fFlags = flags; |
} |
SkLightingShaderImpl::LightingShaderContext::~LightingShaderContext() { |
// The dependencies have been created outside of the context on memory that was allocated by |
// the onCreateContext() method. Call the destructors and free the memory. |
- fDiffuseContext->~Context(); |
+ if (fDiffuseContext) { |
+ fDiffuseContext->~Context(); |
+ } |
fNormalProvider->~Provider(); |
sk_free(fHeapAllocated); |
@@ -352,15 +366,21 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, |
SkPMColor diffuse[BUFFER_MAX]; |
SkPoint3 normals[BUFFER_MAX]; |
+ SkColor diffColor = fPaintColor; |
+ |
do { |
int n = SkTMin(count, BUFFER_MAX); |
- fDiffuseContext->shadeSpan(x, y, diffuse, n); |
fNormalProvider->fillScanLine(x, y, normals, n); |
- for (int i = 0; i < n; ++i) { |
+ if (fDiffuseContext) { |
+ fDiffuseContext->shadeSpan(x, y, diffuse, n); |
+ } |
- SkColor diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); |
+ for (int i = 0; i < n; ++i) { |
+ if (fDiffuseContext) { |
+ diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); |
+ } |
SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f); |
// This is all done in linear unpremul color space (each component 0..255.0f though) |
@@ -381,6 +401,7 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, |
} |
} |
+ // convert() premultiplies the accumulate color with alpha |
result[i] = convert(accum, SkColorGetA(diffColor)); |
} |
@@ -430,7 +451,12 @@ sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { |
sk_sp<SkLights> lights(builder.finish()); |
sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>()); |
- sk_sp<SkShader> diffuseShader(buf.readFlattenable<SkShader>()); |
+ |
+ bool hasDiffuse = buf.readBool(); |
+ sk_sp<SkShader> diffuseShader = nullptr; |
+ if (hasDiffuse) { |
+ diffuseShader = buf.readFlattenable<SkShader>(); |
+ } |
return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), |
std::move(lights)); |
@@ -453,7 +479,10 @@ void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { |
} |
buf.writeFlattenable(fNormalSource.get()); |
- buf.writeFlattenable(fDiffuseShader.get()); |
+ buf.writeBool(fDiffuseShader); |
+ if (fDiffuseShader) { |
+ buf.writeFlattenable(fDiffuseShader.get()); |
+ } |
} |
size_t SkLightingShaderImpl::onContextSize(const ContextRec& rec) const { |
@@ -462,18 +491,23 @@ size_t SkLightingShaderImpl::onContextSize(const ContextRec& rec) const { |
SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, |
void* storage) const { |
- size_t heapRequired = fDiffuseShader->contextSize(rec) + |
+ size_t heapRequired = (fDiffuseShader ? fDiffuseShader->contextSize(rec) : 0) + |
fNormalSource->providerSize(rec); |
void* heapAllocated = sk_malloc_throw(heapRequired); |
void* diffuseContextStorage = heapAllocated; |
- SkShader::Context* diffuseContext = fDiffuseShader->createContext(rec, diffuseContextStorage); |
- if (!diffuseContext) { |
- sk_free(heapAllocated); |
- return nullptr; |
+ void* normalProviderStorage = (char*) diffuseContextStorage + |
+ (fDiffuseShader ? fDiffuseShader->contextSize(rec) : 0); |
+ |
+ SkShader::Context *diffuseContext = nullptr; |
+ if (fDiffuseShader) { |
+ diffuseContext = fDiffuseShader->createContext(rec, diffuseContextStorage); |
+ if (!diffuseContext) { |
+ sk_free(heapAllocated); |
+ return nullptr; |
+ } |
} |
- void* normalProviderStorage = (char*)heapAllocated + fDiffuseShader->contextSize(rec); |
SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, |
normalProviderStorage); |
if (!normalProvider) { |
@@ -491,10 +525,8 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, |
sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader, |
sk_sp<SkNormalSource> normalSource, |
sk_sp<SkLights> lights) { |
- if (!diffuseShader || !normalSource) { |
- // TODO: Use paint's color in absence of a diffuseShader |
- // TODO: Use a default implementation of normalSource instead |
- return nullptr; |
+ if (!normalSource) { |
+ normalSource = SkNormalSource::MakeFlat(); |
} |
return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), |