GPU device preserves pixel values across read/write/read of unpremul pixel values

Review URL: http://codereview.appspot.com/5695047/



git-svn-id: http://skia.googlecode.com/svn/trunk@3237 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2012-02-23 15:39:54 +00:00
parent 78525202b8
commit a91e923874
11 changed files with 402 additions and 45 deletions

View File

@ -52,6 +52,7 @@
'../tests/PathTest.cpp',
'../tests/PDFPrimitivesTest.cpp',
'../tests/PointTest.cpp',
'../tests/PremulAlphaRoundTripTest.cpp',
'../tests/QuickRejectTest.cpp',
'../tests/Reader32Test.cpp',
'../tests/ReadPixelsTest.cpp',

View File

@ -255,3 +255,27 @@ void SkConvertConfig8888Pixels(uint32_t* dstPixels,
break;
}
}
uint32_t SkPackConfig8888(SkCanvas::Config8888 config,
uint32_t a,
uint32_t r,
uint32_t g,
uint32_t b) {
switch (config) {
case SkCanvas::kNative_Premul_Config8888:
case SkCanvas::kNative_Unpremul_Config8888:
return pack_config8888<SK_NATIVE_A_IDX,
SK_NATIVE_R_IDX,
SK_NATIVE_G_IDX,
SK_NATIVE_B_IDX>(a, r, g, b);
case SkCanvas::kBGRA_Premul_Config8888:
case SkCanvas::kBGRA_Unpremul_Config8888:
return pack_config8888<3, 2, 1, 0>(a, r, g, b);
case SkCanvas::kRGBA_Premul_Config8888:
case SkCanvas::kRGBA_Unpremul_Config8888:
return pack_config8888<3, 0, 1, 2>(a, r, g, b);
default:
SkDEBUGFAIL("Unexpected config8888");
return 0;
}
}

View File

@ -22,6 +22,15 @@ void SkConvertConfig8888Pixels(uint32_t* dstPixels,
int width,
int height);
/**
* Packs a, r, g, b, values into byte order specified by config.
*/
uint32_t SkPackConfig8888(SkCanvas::Config8888 config,
uint32_t a,
uint32_t r,
uint32_t g,
uint32_t b);
namespace {
/**

View File

@ -1787,6 +1787,35 @@ bool GrContext::internalReadTexturePixels(GrTexture* texture,
}
}
#include "SkConfig8888.h"
namespace {
/**
* Converts a GrPixelConfig to a SkCanvas::Config8888. Only byte-per-channel
* formats are representable as Config8888 and so the function returns false
* if the GrPixelConfig has no equivalent Config8888.
*/
bool grconfig_to_config8888(GrPixelConfig config,
SkCanvas::Config8888* config8888) {
switch (config) {
case kRGBA_8888_PM_GrPixelConfig:
*config8888 = SkCanvas::kRGBA_Premul_Config8888;
return true;
case kRGBA_8888_UPM_GrPixelConfig:
*config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
return true;
case kBGRA_8888_PM_GrPixelConfig:
*config8888 = SkCanvas::kBGRA_Premul_Config8888;
return true;
case kBGRA_8888_UPM_GrPixelConfig:
*config8888 = SkCanvas::kBGRA_Unpremul_Config8888;
return true;
default:
return false;
}
}
}
bool GrContext::internalReadRenderTargetPixels(GrRenderTarget* target,
int left, int top,
int width, int height,
@ -1803,19 +1832,34 @@ bool GrContext::internalReadRenderTargetPixels(GrRenderTarget* target,
return false;
}
}
// PM <-> UPM conversion requires a draw. Currently we only support drawing
// into a UPM target, not reading from a UPM texture. Thus, UPM->PM is not
// not supported at this time.
if (GrPixelConfigIsUnpremultiplied(target->config()) &&
!GrPixelConfigIsUnpremultiplied(config)) {
return false;
}
if (!(kDontFlush_PixelOpsFlag & flags)) {
this->flush();
}
if (!GrPixelConfigIsUnpremultiplied(target->config()) &&
GrPixelConfigIsUnpremultiplied(config) &&
!fGpu->canPreserveReadWriteUnpremulPixels()) {
SkCanvas::Config8888 srcConfig8888, dstConfig8888;
if (!grconfig_to_config8888(target->config(), &srcConfig8888) ||
!grconfig_to_config8888(config, &dstConfig8888)) {
return false;
}
// do read back using target's own config
this->internalReadRenderTargetPixels(target,
left, top,
width, height,
target->config(),
buffer, rowBytes,
kDontFlush_PixelOpsFlag);
// sw convert the pixels to unpremul config
uint32_t* pixels = reinterpret_cast<uint32_t*>(buffer);
SkConvertConfig8888Pixels(pixels, rowBytes, dstConfig8888,
pixels, rowBytes, srcConfig8888,
width, height);
return true;
}
GrTexture* src = target->asTexture();
bool swapRAndB = NULL != src &&
fGpu->preferredReadPixelsConfig(config) ==
@ -1968,6 +2012,28 @@ void GrContext::internalWriteRenderTargetPixels(GrRenderTarget* target,
return;
}
#endif
if (!GrPixelConfigIsUnpremultiplied(target->config()) &&
GrPixelConfigIsUnpremultiplied(config) &&
!fGpu->canPreserveReadWriteUnpremulPixels()) {
SkCanvas::Config8888 srcConfig8888, dstConfig8888;
if (!grconfig_to_config8888(config, &srcConfig8888) ||
!grconfig_to_config8888(target->config(), &dstConfig8888)) {
return;
}
// allocate a tmp buffer and sw convert the pixels to premul
SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(width * height);
const uint32_t* src = reinterpret_cast<const uint32_t*>(buffer);
SkConvertConfig8888Pixels(tmpPixels.get(), 4 * width, dstConfig8888,
src, rowBytes, srcConfig8888,
width, height);
// upload the already premul pixels
this->internalWriteRenderTargetPixels(target,
left, top,
width, height,
target->config(),
tmpPixels, 4 * width, flags);
return;
}
bool swapRAndB = fGpu->preferredReadPixelsConfig(config) ==
GrPixelConfigSwapRAndB(config);

View File

@ -183,6 +183,13 @@ public:
*/
void forceRenderTargetFlush();
/**
* If this returns true then a sequence that reads unpremultiplied pixels
* from a surface, writes back the same values, and reads them again will
* give the same pixel values back in both reads.
*/
virtual bool canPreserveReadWriteUnpremulPixels() = 0;
/**
* readPixels with some configs may be slow. Given a desired config this
* function returns a fast-path config. The returned config must have the

View File

@ -953,13 +953,22 @@ bool GrGLProgram::genProgram(const GrGLContextInfo& gl,
inCoverage.c_str(),
&segments.fFSCode);
}
if (ProgramDesc::kNo_OutputPM == fProgramDesc.fOutputPM) {
segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(%s.rgb / %s.a, %s.a);\n",
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str());
if (ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig ==
fProgramDesc.fOutputConfig) {
segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0)/255.0, %s.a);\n",
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str());
} else if (ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig ==
fProgramDesc.fOutputConfig) {
segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0)/255.0, %s.a);\n",
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str(),
colorOutput.getName().c_str());
}
}
@ -1809,13 +1818,16 @@ void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
};
static const uint32_t kMulByAlphaMask =
(StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag);
const char* swizzle = "";
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));
GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
swizzle = ".aaaa";
}
@ -1843,30 +1855,37 @@ void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
switch (desc.fFetchMode) {
case StageDesc::k2x2_FetchMode:
GrAssert(!(desc.fInConfigFlags &
StageDesc::kMulRGBByAlpha_InConfigFlag));
GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
gen2x2FS(stageNum, segments, locations, &sampleCoords,
samplerName, texelSizeName, swizzle, fsOutColor,
texFunc, modulate, complexCoord, coordDims);
break;
case StageDesc::kConvolution_FetchMode:
GrAssert(!(desc.fInConfigFlags &
StageDesc::kMulRGBByAlpha_InConfigFlag));
GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
genConvolutionFS(stageNum, desc, segments,
samplerName, kernel, swizzle, imageIncrementName, fsOutColor,
sampleCoords, texFunc, modulate);
break;
default:
if (desc.fInConfigFlags & StageDesc::kMulRGBByAlpha_InConfigFlag) {
if (desc.fInConfigFlags & kMulByAlphaMask) {
// only one of the mul by alpha flags should be set
GrAssert(GrIsPow2(kMulByAlphaMask & desc.fInConfigFlags));
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());
if (desc.fInConfigFlags &
StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag) {
segments->fFSCode.appendf("\t%s = vec4(ceil(%s.rgb*%s.a*255.0)/255.0,%s.a)%s;\n",
fsOutColor, fsOutColor, fsOutColor,
fsOutColor, modulate.c_str());
} else {
segments->fFSCode.appendf("\t%s = vec4(floor(%s.rgb*%s.a*255.0)/255.0,%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(),

View File

@ -86,13 +86,17 @@ public:
memset(this, 0, sizeof(ProgramDesc));
}
enum OutputPM {
enum OutputConfig {
// PM-color OR color with no alpha channel
kYes_OutputPM,
// nonPM-color with alpha channel
kNo_OutputPM,
kPremultiplied_OutputConfig,
// nonPM-color with alpha channel. Round components up after
// dividing by alpha. Assumes output is 8 bits for r, g, and b
kUnpremultiplied_RoundUp_OutputConfig,
// nonPM-color with alpha channel. Round components down after
// dividing by alpha. Assumes output is 8 bits for r, g, and b
kUnpremultiplied_RoundDown_OutputConfig,
kOutputPMCnt
kOutputConfigCnt
};
struct StageDesc {
@ -114,7 +118,7 @@ public:
described are performed after reading a texel.
*/
enum InConfigFlags {
kNone_InConfigFlag = 0x0,
kNone_InConfigFlag = 0x0,
/**
Swap the R and B channels. This is incompatible with
@ -122,21 +126,27 @@ public:
the shader using GL_ARB_texture_swizzle if possible rather
than setting this flag.
*/
kSwapRAndB_InConfigFlag = 0x1,
kSwapRAndB_InConfigFlag = 0x1,
/**
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
kSwapRAndB and kMulRGBByAlpha*. It is prefereable to perform
the smear outside the shader using GL_ARB_texture_swizzle if
possible rather than setting this flag.
*/
kSmearAlpha_InConfigFlag = 0x2,
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.
It is assumed the src texture has 8bit color components. After
reading the texture one version rounds up to the next multiple
of 1/255.0 and the other rounds down. At most one of these
flags may be set.
*/
kMulRGBByAlpha_InConfigFlag = 0x4,
kMulRGBByAlpha_RoundUp_InConfigFlag = 0x4,
kMulRGBByAlpha_RoundDown_InConfigFlag = 0x8,
kDummyInConfigFlag,
kInConfigBitMask = (kDummyInConfigFlag-1) |
@ -211,7 +221,7 @@ public:
uint8_t fColorInput; // casts to enum ColorInput
uint8_t fCoverageInput; // casts to enum CoverageInput
uint8_t fOutputPM; // cases to enum OutputPM
uint8_t fOutputConfig; // casts to enum OutputConfig
uint8_t fDualSrcOutput; // casts to enum DualSrcOutput
int8_t fFirstCoverageStage;
SkBool8 fEmitsPointSize;

View File

@ -207,6 +207,7 @@ GrGpuGL::GrGpuGL(const GrGLContextInfo& ctxInfo) : fGLContextInfo(ctxInfo) {
this->initCaps();
fLastSuccessfulStencilFmtIdx = 0;
fCanPreserveUnpremulRoundtrip = kUnknown_CanPreserveUnpremulRoundtrip;
}
GrGpuGL::~GrGpuGL() {
@ -292,6 +293,85 @@ void GrGpuGL::initCaps() {
fCaps.fFSAASupport = GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType();
}
bool GrGpuGL::canPreserveReadWriteUnpremulPixels() {
if (kUnknown_CanPreserveUnpremulRoundtrip ==
fCanPreserveUnpremulRoundtrip) {
SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
uint32_t* srcData = data.get();
uint32_t* firstRead = data.get() + 256 * 256;
uint32_t* secondRead = data.get() + 2 * 256 * 256;
for (int y = 0; y < 256; ++y) {
for (int x = 0; x < 256; ++x) {
uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
color[3] = y;
color[2] = x;
color[1] = x;
color[0] = x;
}
}
// We have broader support for read/write pixels on render targets
// than on textures.
GrTextureDesc dstDesc;
dstDesc.fFlags = kRenderTarget_GrTextureFlagBit |
kNoStencil_GrTextureFlagBit;
dstDesc.fWidth = 256;
dstDesc.fHeight = 256;
dstDesc.fConfig = kRGBA_8888_GrPixelConfig;
dstDesc.fSampleCnt = 0;
SkAutoTUnref<GrTexture> dstTex(this->createTexture(dstDesc, NULL, 0));
if (!dstTex.get()) {
return false;
}
GrRenderTarget* rt = dstTex.get()->asRenderTarget();
GrAssert(NULL != rt);
bool failed = true;
static const UnpremulConversion gMethods[] = {
kUpOnWrite_DownOnRead_UnpremulConversion,
kDownOnWrite_UpOnRead_UnpremulConversion,
};
// pretend that we can do the roundtrip to avoid recursive calls to
// this function
fCanPreserveUnpremulRoundtrip = kYes_CanPreserveUnpremulRoundtrip;
for (size_t i = 0; i < GR_ARRAY_COUNT(gMethods) && failed; ++i) {
fUnpremulConversion = gMethods[i];
rt->writePixels(0, 0,
256, 256,
kRGBA_8888_UPM_GrPixelConfig, srcData, 0);
rt->readPixels(0, 0,
256, 256,
kRGBA_8888_UPM_GrPixelConfig, firstRead, 0);
rt->writePixels(0, 0,
256, 256,
kRGBA_8888_UPM_GrPixelConfig, firstRead, 0);
rt->readPixels(0, 0,
256, 256,
kRGBA_8888_UPM_GrPixelConfig, secondRead, 0);
failed = false;
for (int j = 0; j < 256 * 256; ++j) {
if (firstRead[j] != secondRead[j]) {
failed = true;
break;
}
}
}
fCanPreserveUnpremulRoundtrip = failed ?
kNo_CanPreserveUnpremulRoundtrip :
kYes_CanPreserveUnpremulRoundtrip;
}
if (kYes_CanPreserveUnpremulRoundtrip == fCanPreserveUnpremulRoundtrip) {
return true;
} else {
return false;
}
}
GrPixelConfig GrGpuGL::preferredReadPixelsConfig(GrPixelConfig config) const {
if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
return GrPixelConfigSwapRAndB(config);

View File

@ -46,6 +46,8 @@ public:
size_t rowBytes) const SK_OVERRIDE;
virtual bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE;
virtual bool canPreserveReadWriteUnpremulPixels() SK_OVERRIDE;
protected:
GrGpuGL(const GrGLContextInfo& ctxInfo);
@ -62,6 +64,11 @@ protected:
bool fSmoothLineEnabled;
} fHWAAState;
enum UnpremulConversion {
kUpOnWrite_DownOnRead_UnpremulConversion,
kDownOnWrite_UpOnRead_UnpremulConversion
} fUnpremulConversion;
GrDrawState fHWDrawState;
bool fHWStencilClip;
@ -246,6 +253,11 @@ private:
// from our loop that tries stencil formats and calls check fb status.
int fLastSuccessfulStencilFmtIdx;
enum CanPreserveUnpremulRoundtrip {
kUnknown_CanPreserveUnpremulRoundtrip,
kNo_CanPreserveUnpremulRoundtrip,
kYes_CanPreserveUnpremulRoundtrip,
} fCanPreserveUnpremulRoundtrip;
bool fPrintedCaps;

View File

@ -173,8 +173,9 @@ bool GrGpuGLShaders::programUnitTest() {
static const int IN_CONFIG_FLAGS[] = {
StageDesc::kNone_InConfigFlag,
StageDesc::kSwapRAndB_InConfigFlag,
StageDesc::kSwapRAndB_InConfigFlag | StageDesc::kMulRGBByAlpha_InConfigFlag,
StageDesc::kMulRGBByAlpha_InConfigFlag,
StageDesc::kSwapRAndB_InConfigFlag |
StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag,
StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag,
StageDesc::kSmearAlpha_InConfigFlag,
};
GrGLProgram program;
@ -210,7 +211,7 @@ bool GrGpuGLShaders::programUnitTest() {
pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport &&
random_bool(&random);
#endif
pdesc.fOutputPM = random_int(&random, ProgramDesc::kOutputPMCnt);
pdesc.fOutputConfig = random_int(&random, ProgramDesc::kOutputConfigCnt);
bool edgeAA = random_bool(&random);
if (edgeAA) {
@ -264,17 +265,20 @@ bool GrGpuGLShaders::programUnitTest() {
stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
}
stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
static const uint32_t kMulByAlphaMask =
StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
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;
stage.fInConfigFlags &= ~kMulByAlphaMask;
break;
case StageDesc::k2x2_FetchMode:
stage.fKernelWidth = 0;
stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
stage.fInConfigFlags &= ~kMulByAlphaMask;
break;
}
}
@ -1102,7 +1106,17 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
}
}
if (GrPixelConfigIsUnpremultiplied(texture->config())) {
stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_InConfigFlag;
// The shader generator assumes that color channels are bytes
// when rounding.
GrAssert(4 == GrBytesPerPixel(texture->config()));
if (kUpOnWrite_DownOnRead_UnpremulConversion ==
fUnpremulConversion) {
stage.fInConfigFlags |=
StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
} else {
stage.fInConfigFlags |=
StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag;
}
}
if (sampler.getFilter() == GrSamplerState::kConvolution_Filter) {
@ -1120,9 +1134,18 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
}
if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) {
desc.fOutputPM = ProgramDesc::kNo_OutputPM;
// The shader generator assumes that color channels are bytes
// when rounding.
GrAssert(4 == GrBytesPerPixel(drawState.getRenderTarget()->config()));
if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) {
desc.fOutputConfig =
ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig;
} else {
desc.fOutputConfig =
ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig;
}
} else {
desc.fOutputPM = ProgramDesc::kYes_OutputPM;
desc.fOutputConfig = ProgramDesc::kPremultiplied_OutputConfig;
}
desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;

View File

@ -0,0 +1,106 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
#include "SkCanvas.h"
#include "SkConfig8888.h"
#include "SkGpuDevice.h"
namespace {
void fillCanvas(SkCanvas* canvas, SkCanvas::Config8888 unpremulConfig) {
SkBitmap bmp;
bmp.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
bmp.allocPixels();
SkAutoLockPixels alp(bmp);
uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels());
for (int a = 0; a < 256; ++a) {
for (int r = 0; r < 256; ++r) {
pixels[a * 256 + r] = SkPackConfig8888(unpremulConfig, a, r, 0, 0);
}
}
canvas->writePixels(bmp, 0, 0, unpremulConfig);
}
static const SkCanvas::Config8888 gUnpremulConfigs[] = {
SkCanvas::kNative_Unpremul_Config8888,
/**
* There is a bug in Ganesh (http://code.google.com/p/skia/issues/detail?id=438)
* that causes the readback of pixels from BGRA canvas to an RGBA bitmap to
* fail. This should be removed as soon as the issue above is resolved.
*/
#if !defined(SK_BUILD_FOR_ANDROID)
SkCanvas::kBGRA_Unpremul_Config8888,
#endif
SkCanvas::kRGBA_Unpremul_Config8888,
};
void PremulAlphaRoundTripTest(skiatest::Reporter* reporter,
GrContext* context) {
SkCanvas canvas;
for (int dtype = 0; dtype < 2; ++dtype) {
if (0 == dtype) {
canvas.setDevice(new SkDevice(SkBitmap::kARGB_8888_Config,
256,
256,
false))->unref();
} else {
#if SK_SCALAR_IS_FIXED
// GPU device known not to work in the fixed pt build.
continue;
#endif
canvas.setDevice(new SkGpuDevice(context,
SkBitmap::kARGB_8888_Config,
256,
256))->unref();
}
SkBitmap readBmp1;
readBmp1.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
readBmp1.allocPixels();
SkBitmap readBmp2;
readBmp2.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
readBmp2.allocPixels();
for (size_t upmaIdx = 0;
upmaIdx < SK_ARRAY_COUNT(gUnpremulConfigs);
++upmaIdx) {
fillCanvas(&canvas, gUnpremulConfigs[upmaIdx]);
{
SkAutoLockPixels alp1(readBmp1);
SkAutoLockPixels alp2(readBmp2);
sk_bzero(readBmp1.getPixels(), readBmp1.getSafeSize());
sk_bzero(readBmp2.getPixels(), readBmp2.getSafeSize());
}
canvas.readPixels(&readBmp1, 0, 0, gUnpremulConfigs[upmaIdx]);
canvas.writePixels(readBmp1, 0, 0, gUnpremulConfigs[upmaIdx]);
canvas.readPixels(&readBmp2, 0, 0, gUnpremulConfigs[upmaIdx]);
SkAutoLockPixels alp1(readBmp1);
SkAutoLockPixels alp2(readBmp2);
uint32_t* pixels1 =
reinterpret_cast<uint32_t*>(readBmp1.getPixels());
uint32_t* pixels2 =
reinterpret_cast<uint32_t*>(readBmp2.getPixels());
for (int y = 0; y < 256; ++y) {
for (int x = 0; x < 256; ++x) {
int i = y * 256 + x;
REPORTER_ASSERT(reporter, pixels1[i] == pixels2[i]);
}
}
}
}
}
}
#include "TestClassDef.h"
DEFINE_GPUTESTCLASS("PremulAlphaRoundTripTest", PremulAlphaRoundTripTestClass, PremulAlphaRoundTripTest)