blob: c9f1fb02c3a17d49c841ee69dcf38a6f29d75541 [file] [log] [blame]
/*
* 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 "SkLumaColorFilter.h"
#include "SkColorPriv.h"
#include "SkString.h"
#include "SkUnPreMultiply.h"
#if SK_SUPPORT_GPU
#include "gl/GrGLEffect.h"
#include "GrContext.h"
#include "GrTBackendEffectFactory.h"
#endif
void SkLumaColorFilter::filterSpan(const SkPMColor src[], int count,
SkPMColor dst[]) const {
const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
for (int i = 0; i < count; ++i) {
SkPMColor c = src[i];
unsigned r = SkGetPackedR32(c);
unsigned g = SkGetPackedG32(c);
unsigned b = SkGetPackedB32(c);
unsigned a = SkGetPackedA32(c);
// No need to do anything for white (luminance == 1.0)
if (a != r || a != g || a != b) {
/*
* To avoid un-premultiplying multiple components, we can start
* with the luminance computed in PM space:
*
* Lum = i * (r / a) + j * (g / a) + k * (b / a)
* Lum = (i * r + j * g + k * b) / a
* Lum = Lum'(PM) / a
*
* Then the filter function is:
*
* C' = [ Lum * a, Lum * r, Lum * g, Lum * b ]
*
* which is equivalent to:
*
* C' = [ Lum'(PM), Lum * r, Lum * g, Lum * b ]
*/
unsigned pm_lum = SkComputeLuminance(r, g, b);
unsigned lum = SkUnPreMultiply::ApplyScale(table[a], pm_lum);
c = SkPackARGB32(pm_lum,
SkMulDiv255Round(r, lum),
SkMulDiv255Round(g, lum),
SkMulDiv255Round(b, lum));
}
dst[i] = c;
}
}
SkColorFilter* SkLumaColorFilter::Create() {
return SkNEW(SkLumaColorFilter);
}
SkLumaColorFilter::SkLumaColorFilter()
: INHERITED() {
}
SkLumaColorFilter::SkLumaColorFilter(SkFlattenableReadBuffer& buffer)
: INHERITED(buffer) {
}
void SkLumaColorFilter::flatten(SkFlattenableWriteBuffer&) const {
}
#ifdef SK_DEVELOPER
void SkLumaColorFilter::toString(SkString* str) const {
str->append("SkLumaColorFilter ");
}
#endif
#if SK_SUPPORT_GPU
class LumaColorFilterEffect : public GrEffect {
public:
static GrEffectRef* Create() {
AutoEffectUnref effect(SkNEW(LumaColorFilterEffect));
return CreateEffectRef(effect);
}
static const char* Name() { return "Luminance-to-Alpha"; }
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
return GrTBackendEffectFactory<LumaColorFilterEffect>::getInstance();
}
virtual void getConstantColorComponents(GrColor* color,
uint32_t* validFlags) const SK_OVERRIDE {
*validFlags = 0;
}
class GLEffect : public GrGLEffect {
public:
GLEffect(const GrBackendEffectFactory& factory,
const GrDrawEffect&)
: INHERITED(factory) {
}
static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) {
// this class always generates the same code.
return 0;
}
virtual void emitCode(GrGLShaderBuilder* builder,
const GrDrawEffect&,
EffectKey,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray&) SK_OVERRIDE {
if (NULL == inputColor) {
inputColor = GrGLSLOnesVecf(4);
}
// The max() is to guard against 0 / 0 during unpremul when the incoming color is
// transparent black.
builder->fsCodeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
builder->fsCodeAppendf("\tfloat luma = dot(vec3(%f, %f, %f), %s.rgb);\n",
SK_ITU_BT709_LUM_COEFF_R,
SK_ITU_BT709_LUM_COEFF_G,
SK_ITU_BT709_LUM_COEFF_B,
inputColor);
builder->fsCodeAppendf("\t%s = vec4(%s.rgb * luma / nonZeroAlpha, luma);\n",
outputColor, inputColor);
}
private:
typedef GrGLEffect INHERITED;
};
private:
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE {
return true;
}
};
GrEffectRef* SkLumaColorFilter::asNewEffect(GrContext*) const {
return LumaColorFilterEffect::Create();
}
#endif