Do writepixels alpha-premul using gpu
Review URL: http://codereview.appspot.com/5373064/
git-svn-id: http://skia.googlecode.com/svn/trunk@2668 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index e5908de..eb92f09 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -268,10 +268,13 @@
/**
* Pixel configurations.
- * Unpremultiplied configs are intended for conversion out from skia. They are
- * not supported as input (e.g. drawBitmap or a bitmap shader). When used a
- * render target config only draws that use blend coeffs 1,0 (AKA src-mode)
- * will succeed.
+ *
+ * Unpremultiplied configs are intended for converting pixel data in and out
+ * from skia. Surfaces with these configs have limited support. As an input
+ * (GrPaint texture) the corresponding GrSamplerState must have its filter set
+ * to kNearest_Filter. Otherwise, the draw will fail. When the render target
+ * has an unpremultiplied config draws must use blend coeffs 1,0 (AKA src-mode).
+ * Other coeffs will cause the draw to fail.
*/
enum GrPixelConfig {
kUnknown_GrPixelConfig,
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 03a68dd..3d9195c 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -810,8 +810,15 @@
}
}
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ // We don't support using unpremultiplied textures with filters (other
+ // than nearest). Alpha-premulling is not distributive WRT to filtering.
+ // We'd have to filter each texel before filtering. We could do this for
+ // our custom filters but we would also have to disable bilerp and do
+ // a custom bilerp in the shader. Until Skia itself supports unpremul
+ // configs there is no pressure to implement this.
if (this->isStageEnabled(s) &&
- GrPixelConfigIsUnpremultiplied(fCurrDrawState.fTextures[s]->config())) {
+ GrPixelConfigIsUnpremultiplied(fCurrDrawState.fTextures[s]->config()) &&
+ GrSamplerState::kNearest_Filter != fCurrDrawState.fSamplerStates[s].getFilter()) {
return false;
}
}
diff --git a/src/gpu/GrGLProgram.cpp b/src/gpu/GrGLProgram.cpp
index 9fe195c..770d910 100644
--- a/src/gpu/GrGLProgram.cpp
+++ b/src/gpu/GrGLProgram.cpp
@@ -1564,7 +1564,9 @@
ShaderCodeSegments* segments,
StageUniLocations* locations) const {
- GrAssert(stageNum >= 0 && stageNum <= 9);
+ GrAssert(stageNum >= 0 && stageNum <= GrDrawState::kNumStages);
+ GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) ==
+ desc.fInConfigFlags);
// First decide how many coords are needed to access the texture
// Right now it's always 2 but we could start using 1D textures for
@@ -1710,19 +1712,14 @@
};
const char* swizzle = "";
- switch (desc.fSwizzle) {
- case StageDesc::kAlphaSmear_Swizzle:
- swizzle = ".aaaa";
- break;
- case StageDesc::kSwapRAndB_Swizzle:
- swizzle = ".bgra";
- break;
- case StageDesc::kNone_Swizzle:
- swizzle = "";
- break;
- default:
- GrCrash("Swizzle descriptor didn't match any expected value");
- }
+ if (desc.fInConfigFlags & StageDesc::kSwapRAndB_InConfigFlag) {
+ GrAssert(!(desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag));
+ swizzle = ".bgra";
+ } else if (desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag) {
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kMulRGBByAlpha_InConfigFlag));
+ swizzle = ".aaaa";
+ }
GrStringBuilder modulate;
if (NULL != fsInColor) {
@@ -1747,20 +1744,36 @@
switch (desc.fFetchMode) {
case StageDesc::k2x2_FetchMode:
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kMulRGBByAlpha_InConfigFlag));
gen2x2FS(stageNum, segments, locations, &sampleCoords,
samplerName, texelSizeName, swizzle, fsOutColor,
texFunc, modulate, complexCoord, coordDims);
break;
case StageDesc::kConvolution_FetchMode:
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kMulRGBByAlpha_InConfigFlag));
genConvolutionFS(stageNum, desc, segments,
samplerName, kernelName, swizzle, imageIncrementName, fsOutColor,
sampleCoords, texFunc, modulate);
break;
default:
- segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n",
- fsOutColor, texFunc.c_str(),
- samplerName, sampleCoords.c_str(),
- swizzle, modulate.c_str());
+ if (desc.fInConfigFlags & StageDesc::kMulRGBByAlpha_InConfigFlag) {
+ GrAssert(!(desc.fInConfigFlags &
+ StageDesc::kSmearAlpha_InConfigFlag));
+ segments->fFSCode.appendf("\t%s = %s(%s, %s)%s;\n",
+ fsOutColor, texFunc.c_str(),
+ samplerName, sampleCoords.c_str(),
+ swizzle);
+ segments->fFSCode.appendf("\t%s = vec4(%s.rgb*%s.a,%s.a)%s;\n",
+ fsOutColor, fsOutColor, fsOutColor,
+ fsOutColor, modulate.c_str());
+ } else {
+ segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n",
+ fsOutColor, texFunc.c_str(),
+ samplerName, sampleCoords.c_str(),
+ swizzle, modulate.c_str());
+ }
}
}
diff --git a/src/gpu/GrGLProgram.h b/src/gpu/GrGLProgram.h
index e0f8fbc..12f175f 100644
--- a/src/gpu/GrGLProgram.h
+++ b/src/gpu/GrGLProgram.h
@@ -126,25 +126,37 @@
kFetchModeCnt,
};
/**
- Describes how to swizzle the texture's components. If swizzling
- can be applied outside of the shader (GL_ARB_texture_swizzle) that
- is preferrable to using this enum. Changing the enum value used
- causes another program to be generated.
+ Flags set based on a src texture's pixel config. The operations
+ described are performed after reading a texel.
*/
- enum Swizzle {
+ enum InConfigFlags {
+ kNone_InConfigFlag = 0x0,
+
/**
- No swizzling applied to the inputs
+ Swap the R and B channels. This is incompatible with
+ kSmearAlpha. It is prefereable to perform the swizzle outside
+ the shader using GL_ARB_texture_swizzle if possible rather
+ than setting this flag.
*/
- kNone_Swizzle,
+ kSwapRAndB_InConfigFlag = 0x1,
+
/**
- Swap the R and B channels
- */
- kSwapRAndB_Swizzle,
- /**
- Smear alpha across all four channels.
+ Smear alpha across all four channels. This is incompatible with
+ kSwapRAndB and kPremul. It is prefereable to perform the
+ smear outside the shader using GL_ARB_texture_swizzle if
+ possible rather than setting this flag.
*/
- kAlphaSmear_Swizzle,
- kSwizzleCnt
+ kSmearAlpha_InConfigFlag = 0x2,
+
+ /**
+ Multiply r,g,b by a after texture reads. This flag incompatible
+ with kSmearAlpha and may only be used with FetchMode kSingle.
+ */
+ kMulRGBByAlpha_InConfigFlag = 0x4,
+
+ kDummyInConfigFlag,
+ kInConfigBitMask = (kDummyInConfigFlag-1) |
+ (kDummyInConfigFlag-2)
};
enum CoordMapping {
kIdentity_CoordMapping,
@@ -158,11 +170,14 @@
};
uint8_t fOptFlags;
- uint8_t fSwizzle; // casts to enum Swizzle
+ uint8_t fInConfigFlags; // bitfield of InConfigFlags values
uint8_t fFetchMode; // casts to enum FetchMode
uint8_t fCoordMapping; // casts to enum CoordMapping
uint8_t fKernelWidth;
+ GR_STATIC_ASSERT((InConfigFlags)(uint8_t)kInConfigBitMask ==
+ kInConfigBitMask);
+
inline bool isEnabled() const {
return SkToBool(fOptFlags & kIsEnabled_OptFlagBit);
}
diff --git a/src/gpu/GrGpuGL.cpp b/src/gpu/GrGpuGL.cpp
index dc6ace7..774b8d9 100644
--- a/src/gpu/GrGpuGL.cpp
+++ b/src/gpu/GrGpuGL.cpp
@@ -1419,26 +1419,35 @@
// parent class should never let us get here with no RT
GrAssert(NULL != fCurrDrawState.fRenderTarget);
- GrIRect r;
+ GrIRect clippedRect;
if (NULL != rect) {
// flushScissor expects rect to be clipped to the target.
- r = *rect;
+ clippedRect = *rect;
GrIRect rtRect = SkIRect::MakeWH(fCurrDrawState.fRenderTarget->width(),
fCurrDrawState.fRenderTarget->height());
- if (r.intersect(rtRect)) {
- rect = &r;
+ if (clippedRect.intersect(rtRect)) {
+ rect = &clippedRect;
} else {
return;
}
}
this->flushRenderTarget(rect);
this->flushScissor(rect);
- GL_CALL(ColorMask(GR_GL_TRUE,GR_GL_TRUE,GR_GL_TRUE,GR_GL_TRUE));
+
+ GrGLfloat r, g, b, a;
+ static const GrGLfloat scale255 = 1.f / 255.f;
+ a = GrColorUnpackA(color) * scale255;
+ GrGLfloat scaleRGB = scale255;
+ if (GrPixelConfigIsUnpremultiplied(fCurrDrawState.fRenderTarget->config())) {
+ scaleRGB *= a;
+ }
+ r = GrColorUnpackR(color) * scaleRGB;
+ g = GrColorUnpackG(color) * scaleRGB;
+ b = GrColorUnpackB(color) * scaleRGB;
+
+ GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
fHWDrawState.fFlagBits &= ~kNoColorWrites_StateBit;
- GL_CALL(ClearColor(GrColorUnpackR(color)/255.f,
- GrColorUnpackG(color)/255.f,
- GrColorUnpackB(color)/255.f,
- GrColorUnpackA(color)/255.f));
+ GL_CALL(ClearColor(r, g, b, a));
GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
}
diff --git a/src/gpu/GrGpuGLShaders.cpp b/src/gpu/GrGpuGLShaders.cpp
index 67b1c37..76b02ac 100644
--- a/src/gpu/GrGpuGLShaders.cpp
+++ b/src/gpu/GrGpuGLShaders.cpp
@@ -168,9 +168,19 @@
}
}
-template <typename T>
-T random_val(GrRandom* r, T count) {
- return (T)(int)(r->nextF() * count);
+// GrRandoms nextU() values have patterns in the low bits
+// So using nextU() % array_count might never take some values.
+int random_int(GrRandom* r, int count) {
+ return (int)(r->nextF() * count);
+}
+
+// min is inclusive, max is exclusive
+int random_int(GrRandom* r, int min, int max) {
+ return (int)(r->nextF() * (max-min)) + min;
+}
+
+bool random_bool(GrRandom* r) {
+ return r->nextF() > .5f;
}
}
@@ -184,13 +194,18 @@
StageDesc::kNoPerspective_OptFlagBit,
StageDesc::kIdentity_CoordMapping
};
+ static const int IN_CONFIG_FLAGS[] = {
+ StageDesc::kNone_InConfigFlag,
+ StageDesc::kSwapRAndB_InConfigFlag,
+ StageDesc::kSwapRAndB_InConfigFlag | StageDesc::kMulRGBByAlpha_InConfigFlag,
+ StageDesc::kMulRGBByAlpha_InConfigFlag,
+ StageDesc::kSmearAlpha_InConfigFlag,
+ };
GrGLProgram program;
ProgramDesc& pdesc = program.fProgramDesc;
static const int NUM_TESTS = 512;
- // GrRandoms nextU() values have patterns in the low bits
- // So using nextU() % array_count might never take some values.
GrRandom random;
for (int t = 0; t < NUM_TESTS; ++t) {
@@ -204,33 +219,29 @@
pdesc.fVertexLayout = 0;
pdesc.fEmitsPointSize = random.nextF() > .5f;
- pdesc.fColorInput = static_cast<int>(random.nextF() *
- ProgramDesc::kColorInputCnt);
+ pdesc.fColorInput = random_int(&random, ProgramDesc::kColorInputCnt);
- int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt));
- pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx;
+ pdesc.fColorFilterXfermode = random_int(&random, SkXfermode::kCoeffModesCnt);
- idx = (int)(random.nextF() * (GrDrawState::kNumStages + 1));
- pdesc.fFirstCoverageStage = idx;
+ pdesc.fFirstCoverageStage = random_int(&random, GrDrawState::kNumStages);
- pdesc.fVertexLayout |= (random.nextF() > .5f) ?
+ pdesc.fVertexLayout |= random_bool(&random) ?
GrDrawTarget::kCoverage_VertexLayoutBit :
0;
#if GR_GL_EXPERIMENTAL_GS
pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport &&
- random.nextF() > .5f;
+ random_bool(&random);
#endif
- pdesc.fOutputPM = static_cast<int>(random.nextF() *
- ProgramDesc::kOutputPMCnt);
+ pdesc.fOutputPM = random_int(&random, ProgramDesc::kOutputPMCnt);
- bool edgeAA = random.nextF() > .5f;
+ bool edgeAA = random_bool(&random);
if (edgeAA) {
- bool vertexEdgeAA = random.nextF() > .5f;
+ bool vertexEdgeAA = random_bool(&random);
if (vertexEdgeAA) {
pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
if (this->getCaps().fShaderDerivativeSupport) {
- pdesc.fVertexEdgeType = random.nextF() > 0.5f ?
+ pdesc.fVertexEdgeType = random_bool(&random) ?
GrDrawState::kHairQuad_EdgeType :
GrDrawState::kHairLine_EdgeType;
} else {
@@ -238,49 +249,57 @@
}
pdesc.fEdgeAANumEdges = 0;
} else {
- pdesc.fEdgeAANumEdges = static_cast<int>(1 + random.nextF() *
- this->getMaxEdges());
- pdesc.fEdgeAAConcave = random.nextF() > .5f;
+ pdesc.fEdgeAANumEdges = random_int(&random, 1, this->getMaxEdges());
+ pdesc.fEdgeAAConcave = random_bool(&random);
}
} else {
pdesc.fEdgeAANumEdges = 0;
}
if (this->getCaps().fDualSourceBlendingSupport) {
- pdesc.fDualSrcOutput =
- (ProgramDesc::DualSrcOutput)
- (int)(random.nextF() * ProgramDesc::kDualSrcOutputCnt);
+ pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
} else {
pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
}
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
// enable the stage?
- if (random.nextF() > .5f) {
+ if (random_bool(&random)) {
// use separate tex coords?
- if (random.nextF() > .5f) {
- int t = (int)(random.nextF() * GrDrawState::kMaxTexCoords);
+ if (random_bool(&random)) {
+ int t = random_int(&random, GrDrawState::kMaxTexCoords);
pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
} else {
pdesc.fVertexLayout |= StagePosAsTexCoordVertexLayoutBit(s);
}
}
// use text-formatted verts?
- if (random.nextF() > .5f) {
+ if (random_bool(&random)) {
pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
}
- idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
StageDesc& stage = pdesc.fStages[s];
- stage.fOptFlags = STAGE_OPTS[idx];
- stage.fSwizzle = random_val(&random, StageDesc::kSwizzleCnt);
- stage.fCoordMapping = random_val(&random, StageDesc::kCoordMappingCnt);
- stage.fFetchMode = random_val(&random, StageDesc::kFetchModeCnt);
+ stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
+ stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))];
+ stage.fCoordMapping = random_int(&random, StageDesc::kCoordMappingCnt);
+ stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
// convolution shaders don't work with persp tex matrix
if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) {
stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
}
stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
- stage.fKernelWidth = static_cast<int8_t>(4 * random.nextF() + 2);
+ switch (stage.fFetchMode) {
+ case StageDesc::kSingle_FetchMode:
+ stage.fKernelWidth = 0;
+ break;
+ case StageDesc::kConvolution_FetchMode:
+ stage.fKernelWidth = random_int(&random, 2, 8);
+ stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
+ break;
+ case StageDesc::k2x2_FetchMode:
+ stage.fKernelWidth = 0;
+ stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
+ break;
+ }
}
CachedData cachedData;
if (!program.genProgram(this->glInterface(),
@@ -1001,19 +1020,19 @@
stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit;
}
+ stage.fInConfigFlags = 0;
if (!this->glCaps().fTextureSwizzleSupport) {
if (GrPixelConfigIsAlphaOnly(texture->config())) {
// if we don't have texture swizzle support then
// the shader must do an alpha smear after reading
// the texture
- stage.fSwizzle = StageDesc::kAlphaSmear_Swizzle;
+ stage.fInConfigFlags |= StageDesc::kSmearAlpha_InConfigFlag;
} else if (sampler.swapsRAndB()) {
- stage.fSwizzle = StageDesc::kSwapRAndB_Swizzle;
- } else {
- stage.fSwizzle = StageDesc::kNone_Swizzle;
+ stage.fInConfigFlags |= StageDesc::kSwapRAndB_InConfigFlag;
}
- } else {
- stage.fSwizzle = StageDesc::kNone_Swizzle;
+ }
+ if (GrPixelConfigIsUnpremultiplied(texture->config())) {
+ stage.fInConfigFlags = StageDesc::kMulRGBByAlpha_InConfigFlag;
}
if (sampler.getFilter() == GrSamplerState::kConvolution_Filter) {
@@ -1022,11 +1041,11 @@
stage.fKernelWidth = 0;
}
} else {
- stage.fOptFlags = 0;
- stage.fCoordMapping = (StageDesc::CoordMapping) 0;
- stage.fSwizzle = (StageDesc::Swizzle) 0;
- stage.fFetchMode = (StageDesc::FetchMode) 0;
- stage.fKernelWidth = 0;
+ stage.fOptFlags = 0;
+ stage.fCoordMapping = (StageDesc::CoordMapping) 0;
+ stage.fInConfigFlags = 0;
+ stage.fFetchMode = (StageDesc::FetchMode) 0;
+ stage.fKernelWidth = 0;
}
}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 7230516..91184b7 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -299,9 +299,6 @@
bitmap.rowBytes());
}
-// This can be removed when temporary code in writePixels is removed
-#include "SkConfig8888.h"
-
void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
SkCanvas::Config8888 config8888) {
SkAutoLockPixels alp(bitmap);
@@ -317,30 +314,13 @@
bitmap.isOpaque());
}
- // Temporary until we add support for drawing from an unpremul config in
- // GrContext
- const SkBitmap* src = &bitmap;
- SkBitmap tmp;
- if (GrPixelConfigIsUnpremultiplied(config)) {
- config = kSkia8888_PM_GrPixelConfig;
- tmp.setConfig(SkBitmap::kARGB_8888_Config,
- bitmap.width(), bitmap.height());
- if (!tmp.allocPixels()) {
- return;
- }
- SkAutoLockPixels alp(bitmap);
- uint32_t* pixels = reinterpret_cast<uint32_t*>(bitmap.getPixels());
- SkCopyConfig8888ToBitmap(tmp, pixels, bitmap.rowBytes(), config8888);
- src = &tmp;
- }
-
fContext->setRenderTarget(fRenderTarget);
// we aren't setting the clip or matrix, so mark as dirty
// we don't need to set them for this call and don't have them anyway
fNeedPrepareRenderTarget = true;
- fContext->writePixels(x, y, src->width(), src->height(),
- config, src->getPixels(), src->rowBytes());
+ fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
+ config, bitmap.getPixels(), bitmap.rowBytes());
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index c82b0b0..00e08f0 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -245,7 +245,6 @@
bool mul;
SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul);
bool check;
- // do we need fuzzy test?
REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul));
if (!check) {
success = false;