#if SK_SUPPORT_GPU
-#include "GrProcessor.h"
+#include "GrFragmentProcessor.h"
#include "GrTBackendProcessorFactory.h"
+#include "SkGr.h"
+#include "effects/GrTextureStripAtlas.h"
#include "gl/GrGLProcessor.h"
#include "gl/builders/GrGLProgramBuilder.h"
-#include "SkGr.h"
+
class GLColorTableEffect;
class ColorTableEffect : public GrFragmentProcessor {
public:
- static GrFragmentProcessor* Create(GrTexture* texture, unsigned flags) {
- return SkNEW_ARGS(ColorTableEffect, (texture, flags));
- }
+ static GrFragmentProcessor* Create(GrContext* context, SkBitmap bitmap, unsigned flags);
virtual ~ColorTableEffect();
static const char* Name() { return "ColorTable"; }
virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
- virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
-
typedef GLColorTableEffect GLProcessor;
+ const GrTextureStripAtlas* atlas() const { return fAtlas; }
+ int atlasRow() const { return fRow; }
+
private:
- virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
+ virtual bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
- explicit ColorTableEffect(GrTexture* texture, unsigned flags);
+ virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE;
+
+ ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row, unsigned flags);
GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
- GrTextureAccess fTextureAccess;
- unsigned fFlags; // currently not used in shader code, just to assist
- // getConstantColorComponents().
+ GrTextureAccess fTextureAccess;
+
+ // currently not used in shader code, just to assist onComputeInvariantOutput().
+ unsigned fFlags;
+
+ GrTextureStripAtlas* fAtlas;
+ int fRow;
typedef GrFragmentProcessor INHERITED;
};
public:
GLColorTableEffect(const GrBackendProcessorFactory&, const GrProcessor&);
- virtual void emitCode(GrGLProgramBuilder*,
+ virtual void emitCode(GrGLFPBuilder*,
const GrFragmentProcessor&,
const GrProcessorKey&,
const char* outputColor,
const TransformedCoordsArray&,
const TextureSamplerArray&) SK_OVERRIDE;
- virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {}
+ virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b) {}
private:
-
+ UniformHandle fRGBAYValuesUni;
typedef GrGLFragmentProcessor INHERITED;
};
: INHERITED(factory) {
}
-void GLColorTableEffect::emitCode(GrGLProgramBuilder* builder,
+void GLColorTableEffect::setData(const GrGLProgramDataManager& pdm, const GrProcessor& proc) {
+ // The textures are organized in a strip where the rows are ordered a, r, g, b.
+ float rgbaYValues[4];
+ const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
+ if (cte.atlas()) {
+ SkScalar yDelta = cte.atlas()->getNormalizedTexelHeight();
+ rgbaYValues[3] = cte.atlas()->getYOffset(cte.atlasRow()) + SK_ScalarHalf * yDelta;
+ rgbaYValues[0] = rgbaYValues[3] + yDelta;
+ rgbaYValues[1] = rgbaYValues[0] + yDelta;
+ rgbaYValues[2] = rgbaYValues[1] + yDelta;
+ } else {
+ rgbaYValues[3] = 0.125;
+ rgbaYValues[0] = 0.375;
+ rgbaYValues[1] = 0.625;
+ rgbaYValues[2] = 0.875;
+ }
+ pdm.set4fv(fRGBAYValuesUni, 1, rgbaYValues);
+}
+
+void GLColorTableEffect::emitCode(GrGLFPBuilder* builder,
const GrFragmentProcessor&,
const GrProcessorKey&,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray& samplers) {
-
+ const char* yoffsets;
+ fRGBAYValuesUni = builder->addUniform(GrGLFPBuilder::kFragment_Visibility,
+ kVec4f_GrSLType, "yoffsets", &yoffsets);
static const float kColorScaleFactor = 255.0f / 256.0f;
static const float kColorOffsetFactor = 1.0f / 512.0f;
- GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+ GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
if (NULL == inputColor) {
// the input color is solid white (all ones).
static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor;
kColorOffsetFactor, kColorOffsetFactor);
}
+ SkString coord;
+
fsBuilder->codeAppendf("\t\t%s.a = ", outputColor);
- fsBuilder->appendTextureLookup(samplers[0], "vec2(coord.a, 0.125)");
+ coord.printf("vec2(coord.a, %s.a)", yoffsets);
+ fsBuilder->appendTextureLookup(samplers[0], coord.c_str());
fsBuilder->codeAppend(";\n");
fsBuilder->codeAppendf("\t\t%s.r = ", outputColor);
- fsBuilder->appendTextureLookup(samplers[0], "vec2(coord.r, 0.375)");
+ coord.printf("vec2(coord.r, %s.r)", yoffsets);
+ fsBuilder->appendTextureLookup(samplers[0], coord.c_str());
fsBuilder->codeAppend(";\n");
fsBuilder->codeAppendf("\t\t%s.g = ", outputColor);
- fsBuilder->appendTextureLookup(samplers[0], "vec2(coord.g, 0.625)");
+ coord.printf("vec2(coord.g, %s.g)", yoffsets);
+ fsBuilder->appendTextureLookup(samplers[0], coord.c_str());
fsBuilder->codeAppend(";\n");
fsBuilder->codeAppendf("\t\t%s.b = ", outputColor);
- fsBuilder->appendTextureLookup(samplers[0], "vec2(coord.b, 0.875)");
+ coord.printf("vec2(coord.b, %s.b)", yoffsets);
+ fsBuilder->appendTextureLookup(samplers[0], coord.c_str());
fsBuilder->codeAppend(";\n");
fsBuilder->codeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
}
///////////////////////////////////////////////////////////////////////////////
+GrFragmentProcessor* ColorTableEffect::Create(GrContext* context, SkBitmap bitmap, unsigned flags) {
+
+ GrTextureStripAtlas::Desc desc;
+ desc.fWidth = bitmap.width();
+ desc.fHeight = 128;
+ desc.fRowHeight = bitmap.height();
+ desc.fContext = context;
+ desc.fConfig = SkImageInfo2GrPixelConfig(bitmap.info());
+ GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(desc);
+ int row = atlas->lockRow(bitmap);
+ SkAutoTUnref<GrTexture> texture;
+ if (-1 == row) {
+ atlas = NULL;
+ // Passing params=NULL because this effect does no tiling or filtering.
+ texture.reset(GrRefCachedBitmapTexture(context, bitmap, NULL));
+ } else {
+ texture.reset(SkRef(atlas->getTexture()));
+ }
-ColorTableEffect::ColorTableEffect(GrTexture* texture, unsigned flags)
+ return SkNEW_ARGS(ColorTableEffect, (texture, atlas, row, flags));
+}
+
+ColorTableEffect::ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row,
+ unsigned flags)
: fTextureAccess(texture, "a")
- , fFlags(flags) {
+ , fFlags(flags)
+ , fAtlas(atlas)
+ , fRow(row) {
+
this->addTextureAccess(&fTextureAccess);
}
ColorTableEffect::~ColorTableEffect() {
+ if (fAtlas) {
+ fAtlas->unlockRow(fRow);
+ }
}
const GrBackendFragmentProcessorFactory& ColorTableEffect::getFactory() const {
return GrTBackendFragmentProcessorFactory<ColorTableEffect>::getInstance();
}
-bool ColorTableEffect::onIsEqual(const GrProcessor& sBase) const {
- return this->texture(0) == sBase.texture(0);
+bool ColorTableEffect::onIsEqual(const GrFragmentProcessor& other) const {
+ // For non-atlased instances, the texture (compared by base class) is sufficient to
+ // differentiate different tables. For atlased instances we ensure they are using the
+ // same row.
+ const ColorTableEffect& that = other.cast<ColorTableEffect>();
+ SkASSERT(SkToBool(fAtlas) == SkToBool(that.fAtlas));
+ // Ok to always do this comparison since both would be -1 if non-atlased.
+ return fRow == that.fRow;
}
-void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+void ColorTableEffect::onComputeInvariantOutput(InvariantOutput* inout) const {
// If we kept the table in the effect then we could actually run known inputs through the
// table.
+ uint8_t invalidateFlags = 0;
if (fFlags & SkTable_ColorFilter::kR_Flag) {
- *validFlags &= ~kR_GrColorComponentFlag;
+ invalidateFlags |= kR_GrColorComponentFlag;
}
if (fFlags & SkTable_ColorFilter::kG_Flag) {
- *validFlags &= ~kG_GrColorComponentFlag;
+ invalidateFlags |= kG_GrColorComponentFlag;
}
if (fFlags & SkTable_ColorFilter::kB_Flag) {
- *validFlags &= ~kB_GrColorComponentFlag;
+ invalidateFlags |= kB_GrColorComponentFlag;
}
if (fFlags & SkTable_ColorFilter::kA_Flag) {
- *validFlags &= ~kA_GrColorComponentFlag;
+ invalidateFlags |= kA_GrColorComponentFlag;
}
+ inout->invalidateComponents(invalidateFlags, InvariantOutput::kWill_ReadInput);
}
-
///////////////////////////////////////////////////////////////////////////////
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect);
GrContext* context,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
- static unsigned kAllFlags = SkTable_ColorFilter::kR_Flag | SkTable_ColorFilter::kG_Flag |
- SkTable_ColorFilter::kB_Flag | SkTable_ColorFilter::kA_Flag;
- return ColorTableEffect::Create(textures[GrProcessorUnitTest::kAlphaTextureIdx], kAllFlags);
+ int flags = 0;
+ uint8_t luts[256][4];
+ do {
+ for (int i = 0; i < 4; ++i) {
+ flags |= random->nextBool() ? (1 << i): 0;
+ }
+ } while (!flags);
+ for (int i = 0; i < 4; ++i) {
+ if (flags & (1 << i)) {
+ for (int j = 0; j < 256; ++j) {
+ luts[j][i] = SkToU8(random->nextBits(8));
+ }
+ }
+ }
+ SkAutoTUnref<SkColorFilter> filter(SkTableColorFilter::CreateARGB(
+ (flags & (1 << 0)) ? luts[0] : NULL,
+ (flags & (1 << 1)) ? luts[1] : NULL,
+ (flags & (1 << 2)) ? luts[2] : NULL,
+ (flags & (1 << 3)) ? luts[3] : NULL
+ ));
+ return filter->asFragmentProcessor(context);
}
GrFragmentProcessor* SkTable_ColorFilter::asFragmentProcessor(GrContext* context) const {
SkBitmap bitmap;
- GrFragmentProcessor* fp = NULL;
this->asComponentTable(&bitmap);
- // passing NULL because this effect does no tiling or filtering.
- GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, bitmap, NULL);
- if (texture) {
- fp = ColorTableEffect::Create(texture, fFlags);
-
- // Unlock immediately, this is not great, but we don't have a way of
- // knowing when else to unlock it currently. TODO: Remove this when
- // unref becomes the unlock replacement for all types of textures.
- GrUnlockAndUnrefCachedBitmapTexture(texture);
- }
- return fp;
+
+ return ColorTableEffect::Create(context, bitmap, fFlags);
}
#endif // SK_SUPPORT_GPU