Add flag to gradients to interpolate colors in premul space. Experimental API to encapsulate the shared parameters for all gradients into a struct.

BUG=
R=bsalomon@google.com

Review URL: https://codereview.chromium.org/15893002

git-svn-id: http://skia.googlecode.com/svn/trunk@9273 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2013-05-24 14:58:44 +00:00
parent 8b79028d27
commit 3d3a860d0b
8 changed files with 252 additions and 85 deletions

82
gm/alphagradients.cpp Normal file
View File

@ -0,0 +1,82 @@
/*
* 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 "gm.h"
#include "SkCanvas.h"
#include "SkGradientShader.h"
class AlphaGradientsGM : public skiagm::GM {
public:
AlphaGradientsGM() {}
protected:
virtual SkString onShortName() SK_OVERRIDE {
return SkString("alphagradients");
}
virtual SkISize onISize() SK_OVERRIDE {
return SkISize::Make(640, 480);
}
static void draw_grad(SkCanvas* canvas, const SkRect& r,
SkColor c0, SkColor c1, bool doPreMul) {
SkColor colors[] = { c0, c1 };
SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fBottom } };
SkPaint paint;
uint32_t flags = doPreMul ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0;
SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
SkShader::kClamp_TileMode,
NULL, flags);
paint.setShader(s)->unref();
canvas->drawRect(r, paint);
paint.setShader(NULL);
paint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(r, paint);
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
static const struct {
SkColor fColor0;
SkColor fColor1;
} gRec[] = {
{ 0xFFFFFFFF, 0x00000000 },
{ 0xFFFFFFFF, 0x00FF0000 },
{ 0xFFFFFFFF, 0x00FFFF00 },
{ 0xFFFFFFFF, 0x00FFFFFF },
{ 0xFFFF0000, 0x00000000 },
{ 0xFFFF0000, 0x00FF0000 },
{ 0xFFFF0000, 0x00FFFF00 },
{ 0xFFFF0000, 0x00FFFFFF },
{ 0xFF0000FF, 0x00000000 },
{ 0xFF0000FF, 0x00FF0000 },
{ 0xFF0000FF, 0x00FFFF00 },
{ 0xFF0000FF, 0x00FFFFFF },
};
SkRect r = SkRect::MakeWH(300, 30);
canvas->translate(10, 10);
for (int doPreMul = 0; doPreMul <= 1; ++doPreMul) {
canvas->save();
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
draw_grad(canvas, r, gRec[i].fColor0, gRec[i].fColor1, SkToBool(doPreMul));
canvas->translate(0, r.height() + 8);
}
canvas->restore();
canvas->translate(r.width() + 10, 0);
}
}
virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
private:
typedef skiagm::GM INHERITED;
};
DEF_GM( return SkNEW(AlphaGradientsGM); )

View File

@ -3,6 +3,7 @@
'sources': [
'../gm/aaclip.cpp',
'../gm/aarectmodes.cpp',
'../gm/alphagradients.cpp',
'../gm/arithmode.cpp',
'../gm/bicubicfilter.cpp',
'../gm/bigmatrix.cpp',

View File

@ -312,6 +312,7 @@ public:
SkPoint fPoint[2]; //!< Type specific, see above.
SkScalar fRadius[2]; //!< Type specific, see above.
TileMode fTileMode; //!< The tile mode used.
uint32_t fGradientFlags; //!< see SkGradientShader::Flags
};
virtual GradientType asAGradient(GradientInfo* info) const;

View File

@ -1,4 +1,3 @@
/*
* Copyright 2006 The Android Open Source Project
*
@ -6,7 +5,6 @@
* found in the LICENSE file.
*/
#ifndef SkGradientShader_DEFINED
#define SkGradientShader_DEFINED
@ -21,6 +19,15 @@ class SkUnitMapper;
*/
class SK_API SkGradientShader {
public:
enum Flags {
/** By default gradients will interpolate their colors in unpremul space
* and then premultiply each of the results. By setting this flag, the
* gradients will premultiply their colors first, and then interpolate
* between them.
*/
kInterpolateColorsInPremul_Flag = 1 << 0,
};
/** Returns a shader that generates a linear gradient between the two
specified points.
<p />
@ -38,10 +45,11 @@ public:
@param mode The tiling mode
@param mapper May be NULL. Callback to modify the spread of the colors.
*/
static SkShader* CreateLinear( const SkPoint pts[2],
const SkColor colors[], const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL);
static SkShader* CreateLinear(const SkPoint pts[2],
const SkColor colors[], const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL,
uint32_t flags = 0);
/** Returns a shader that generates a radial gradient given the center and radius.
<p />
@ -60,10 +68,11 @@ public:
@param mode The tiling mode
@param mapper May be NULL. Callback to modify the spread of the colors.
*/
static SkShader* CreateRadial( const SkPoint& center, SkScalar radius,
const SkColor colors[], const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL);
static SkShader* CreateRadial(const SkPoint& center, SkScalar radius,
const SkColor colors[], const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL,
uint32_t flags = 0);
/** Returns a shader that generates a radial gradient given the start position, start radius, end position and end radius.
<p />
@ -92,7 +101,8 @@ public:
const SkColor colors[],
const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL);
SkUnitMapper* mapper = NULL,
uint32_t flags = 0);
/**
* Returns a shader that generates a conical gradient given two circles, or
@ -101,13 +111,14 @@ public:
* http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
*/
static SkShader* CreateTwoPointConical(const SkPoint& start,
SkScalar startRadius,
const SkPoint& end,
SkScalar endRadius,
const SkColor colors[],
const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL);
SkScalar startRadius,
const SkPoint& end,
SkScalar endRadius,
const SkColor colors[],
const SkScalar pos[], int count,
SkShader::TileMode mode,
SkUnitMapper* mapper = NULL,
uint32_t flags = 0);
/** Returns a shader that generates a sweep gradient given a center.
<p />
@ -127,7 +138,8 @@ public:
*/
static SkShader* CreateSweep(SkScalar cx, SkScalar cy,
const SkColor colors[], const SkScalar pos[],
int count, SkUnitMapper* mapper = NULL);
int count, SkUnitMapper* mapper = NULL,
uint32_t flags = 0);
SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
};

View File

@ -19,6 +19,7 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc) {
fMapper = desc.fMapper;
SkSafeRef(fMapper);
fGradFlags = SkToU8(desc.fFlags);
SkASSERT((unsigned)desc.fTileMode < SkShader::kTileModeCount);
SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
@ -128,8 +129,21 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc) {
this->initCommon();
}
SkGradientShaderBase::SkGradientShaderBase(SkFlattenableReadBuffer& buffer) :
INHERITED(buffer) {
static uint32_t pack_mode_flags(SkShader::TileMode mode, uint32_t flags) {
SkASSERT(0 == (flags >> 28));
SkASSERT(0 == ((uint32_t)mode >> 4));
return (flags << 4) | mode;
}
static SkShader::TileMode unpack_mode(uint32_t packed) {
return (SkShader::TileMode)(packed & 0xF);
}
static uint32_t unpack_flags(uint32_t packed) {
return packed >> 4;
}
SkGradientShaderBase::SkGradientShaderBase(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
fCacheAlpha = 256;
fMapper = buffer.readFlattenableT<SkUnitMapper>();
@ -147,7 +161,11 @@ SkGradientShaderBase::SkGradientShaderBase(SkFlattenableReadBuffer& buffer) :
}
buffer.readColorArray(fOrigColors);
fTileMode = (TileMode)buffer.readUInt();
{
uint32_t packed = buffer.readUInt();
fGradFlags = SkToU8(unpack_flags(packed));
fTileMode = unpack_mode(packed);
}
fTileProc = gTileProcs[fTileMode];
fRecs = (Rec*)(fOrigColors + colorCount);
if (colorCount > 2) {
@ -186,7 +204,7 @@ void SkGradientShaderBase::flatten(SkFlattenableWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeFlattenable(fMapper);
buffer.writeColorArray(fOrigColors, fColorCount);
buffer.writeUInt(fTileMode);
buffer.writeUInt(pack_mode_flags(fTileMode, fGradFlags));
if (fColorCount > 2) {
Rec* recs = fRecs;
for (int i = 1; i < fColorCount; i++) {
@ -288,39 +306,58 @@ void SkGradientShaderBase::Build16bitCache(uint16_t cache[], SkColor c0, SkColor
} while (--count != 0);
}
/*
* r,g,b used to be SkFixed, but on gcc (4.2.1 mac and 4.6.3 goobuntu) in
* release builds, we saw a compiler error where the 0xFF parameter in
* SkPackARGB32() was being totally ignored whenever it was called with
* a non-zero add (e.g. 0x8000).
*
* We found two work-arounds:
* 1. change r,g,b to unsigned (or just one of them)
* 2. change SkPackARGB32 to + its (a << SK_A32_SHIFT) value instead
* of using |
*
* We chose #1 just because it was more localized.
* See http://code.google.com/p/skia/issues/detail?id=1113
*
* The type SkUFixed encapsulate this need for unsigned, but logically Fixed.
*/
typedef uint32_t SkUFixed;
void SkGradientShaderBase::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
int count, U8CPU paintAlpha) {
int count, U8CPU paintAlpha, uint32_t gradFlags) {
SkASSERT(count > 1);
// need to apply paintAlpha to our two endpoints
SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
SkFixed da;
{
int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
da = SkIntToFixed(tmp - a) / (count - 1);
uint32_t a0 = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
uint32_t a1 = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
const bool interpInPremul = SkToBool(gradFlags &
SkGradientShader::kInterpolateColorsInPremul_Flag);
uint32_t r0 = SkColorGetR(c0);
uint32_t g0 = SkColorGetG(c0);
uint32_t b0 = SkColorGetB(c0);
uint32_t r1 = SkColorGetR(c1);
uint32_t g1 = SkColorGetG(c1);
uint32_t b1 = SkColorGetB(c1);
if (interpInPremul) {
r0 = SkMulDiv255Round(r0, a0);
g0 = SkMulDiv255Round(g0, a0);
b0 = SkMulDiv255Round(b0, a0);
r1 = SkMulDiv255Round(r1, a1);
g1 = SkMulDiv255Round(g1, a1);
b1 = SkMulDiv255Round(b1, a1);
}
/*
* r,g,b used to be SkFixed, but on gcc (4.2.1 mac and 4.6.3 goobuntu) in
* release builds, we saw a compiler error where the 0xFF parameter in
* SkPackARGB32() was being totally ignored whenever it was called with
* a non-zero add (e.g. 0x8000).
*
* We found two work-arounds:
* 1. change r,g,b to unsigned (or just one of them)
* 2. change SkPackARGB32 to + its (a << SK_A32_SHIFT) value instead
* of using |
*
* We chose #1 just because it was more localized.
* See http://code.google.com/p/skia/issues/detail?id=1113
*/
uint32_t r = SkColorGetR(c0);
uint32_t g = SkColorGetG(c0);
uint32_t b = SkColorGetB(c0);
SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
SkFixed da = SkIntToFixed(a1 - a0) / (count - 1);
SkFixed dr = SkIntToFixed(r1 - r0) / (count - 1);
SkFixed dg = SkIntToFixed(g1 - g0) / (count - 1);
SkFixed db = SkIntToFixed(b1 - b0) / (count - 1);
/* We pre-add 1/8 to avoid having to add this to our [0] value each time
in the loop. Without this, the bias for each would be
@ -328,9 +365,10 @@ void SkGradientShaderBase::Build32bitCache(SkPMColor cache[], SkColor c0, SkColo
With this trick, we can add 0 for the first (no-op) and just adjust the
others.
*/
r = SkIntToFixed(r) + 0x2000;
g = SkIntToFixed(g) + 0x2000;
b = SkIntToFixed(b) + 0x2000;
SkUFixed a = SkIntToFixed(a0) + 0x2000;
SkUFixed r = SkIntToFixed(r0) + 0x2000;
SkUFixed g = SkIntToFixed(g0) + 0x2000;
SkUFixed b = SkIntToFixed(b0) + 0x2000;
/*
* Our dither-cell (spatially) is
@ -343,7 +381,7 @@ void SkGradientShaderBase::Build32bitCache(SkPMColor cache[], SkColor c0, SkColo
* [3] -> [ 5/8 ... 7/8 ) values near 3/4
*/
if (0xFF == a && 0 == da) {
if (0xFF == a0 && 0 == da) {
do {
cache[kCache32Count*0] = SkPackARGB32(0xFF, (r + 0 ) >> 16,
(g + 0 ) >> 16,
@ -362,8 +400,31 @@ void SkGradientShaderBase::Build32bitCache(SkPMColor cache[], SkColor c0, SkColo
g += dg;
b += db;
} while (--count != 0);
} else {
a = SkIntToFixed(a) + 0x2000;
} else if (interpInPremul) {
do {
cache[kCache32Count*0] = SkPackARGB32((a + 0 ) >> 16,
(r + 0 ) >> 16,
(g + 0 ) >> 16,
(b + 0 ) >> 16);
cache[kCache32Count*1] = SkPackARGB32((a + 0x8000) >> 16,
(r + 0x8000) >> 16,
(g + 0x8000) >> 16,
(b + 0x8000) >> 16);
cache[kCache32Count*2] = SkPackARGB32((a + 0xC000) >> 16,
(r + 0xC000) >> 16,
(g + 0xC000) >> 16,
(b + 0xC000) >> 16);
cache[kCache32Count*3] = SkPackARGB32((a + 0x4000) >> 16,
(r + 0x4000) >> 16,
(g + 0x4000) >> 16,
(b + 0x4000) >> 16);
cache += 1;
a += da;
r += dr;
g += dg;
b += db;
} while (--count != 0);
} else { // interpolate in unpreml space
do {
cache[kCache32Count*0] = SkPremultiplyARGBInline((a + 0 ) >> 16,
(r + 0 ) >> 16,
@ -463,7 +524,7 @@ const SkPMColor* SkGradientShaderBase::getCache32() const {
fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
if (fColorCount == 2) {
Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
kCache32Count, fCacheAlpha);
kCache32Count, fCacheAlpha, fGradFlags);
} else {
Rec* rec = fRecs;
int prevIndex = 0;
@ -473,8 +534,8 @@ const SkPMColor* SkGradientShaderBase::getCache32() const {
if (nextIndex > prevIndex)
Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
fOrigColors[i],
nextIndex - prevIndex + 1, fCacheAlpha);
fOrigColors[i], nextIndex - prevIndex + 1,
fCacheAlpha, fGradFlags);
prevIndex = nextIndex;
}
}
@ -522,8 +583,8 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const {
return;
}
// build our key: [numColors + colors[] + {positions[]} ]
int count = 1 + fColorCount;
// build our key: [numColors + colors[] + {positions[]} + flags ]
int count = 1 + fColorCount + 1;
if (fColorCount > 2) {
count += fColorCount - 1; // fRecs[].fPos
}
@ -539,6 +600,7 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const {
*buffer++ = fRecs[i].fPos;
}
}
*buffer++ = fGradFlags;
SkASSERT(buffer - storage.get() == count);
///////////////////////////////////
@ -583,6 +645,7 @@ void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
}
info->fColorCount = fColorCount;
info->fTileMode = fTileMode;
info->fGradientFlags = fGradFlags;
}
}
@ -643,26 +706,28 @@ static void desc_init(SkGradientShaderBase::Descriptor* desc,
const SkColor colors[],
const SkScalar pos[], int colorCount,
SkShader::TileMode mode,
SkUnitMapper* mapper) {
SkUnitMapper* mapper, uint32_t flags) {
desc->fColors = colors;
desc->fPos = pos;
desc->fCount = colorCount;
desc->fTileMode = mode;
desc->fMapper = mapper;
desc->fFlags = flags;
}
SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
const SkColor colors[],
const SkScalar pos[], int colorCount,
SkShader::TileMode mode,
SkUnitMapper* mapper) {
SkUnitMapper* mapper,
uint32_t flags) {
if (NULL == pts || NULL == colors || colorCount < 1) {
return NULL;
}
EXPAND_1_COLOR(colorCount);
SkGradientShaderBase::Descriptor desc;
desc_init(&desc, colors, pos, colorCount, mode, mapper);
desc_init(&desc, colors, pos, colorCount, mode, mapper, flags);
return SkNEW_ARGS(SkLinearGradient, (pts, desc));
}
@ -670,14 +735,15 @@ SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
const SkColor colors[],
const SkScalar pos[], int colorCount,
SkShader::TileMode mode,
SkUnitMapper* mapper) {
SkUnitMapper* mapper,
uint32_t flags) {
if (radius <= 0 || NULL == colors || colorCount < 1) {
return NULL;
}
EXPAND_1_COLOR(colorCount);
SkGradientShaderBase::Descriptor desc;
desc_init(&desc, colors, pos, colorCount, mode, mapper);
desc_init(&desc, colors, pos, colorCount, mode, mapper, flags);
return SkNEW_ARGS(SkRadialGradient, (center, radius, desc));
}
@ -689,27 +755,29 @@ SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
const SkScalar pos[],
int colorCount,
SkShader::TileMode mode,
SkUnitMapper* mapper) {
SkUnitMapper* mapper,
uint32_t flags) {
if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
return NULL;
}
EXPAND_1_COLOR(colorCount);
SkGradientShaderBase::Descriptor desc;
desc_init(&desc, colors, pos, colorCount, mode, mapper);
desc_init(&desc, colors, pos, colorCount, mode, mapper, flags);
return SkNEW_ARGS(SkTwoPointRadialGradient,
(start, startRadius, end, endRadius, desc));
}
SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
SkScalar startRadius,
const SkPoint& end,
SkScalar endRadius,
const SkColor colors[],
const SkScalar pos[],
int colorCount,
SkShader::TileMode mode,
SkUnitMapper* mapper) {
SkScalar startRadius,
const SkPoint& end,
SkScalar endRadius,
const SkColor colors[],
const SkScalar pos[],
int colorCount,
SkShader::TileMode mode,
SkUnitMapper* mapper,
uint32_t flags) {
if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
return NULL;
}
@ -719,7 +787,7 @@ SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
EXPAND_1_COLOR(colorCount);
SkGradientShaderBase::Descriptor desc;
desc_init(&desc, colors, pos, colorCount, mode, mapper);
desc_init(&desc, colors, pos, colorCount, mode, mapper, flags);
return SkNEW_ARGS(SkTwoPointConicalGradient,
(start, startRadius, end, endRadius, desc));
}
@ -727,14 +795,15 @@ SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
const SkColor colors[],
const SkScalar pos[],
int colorCount, SkUnitMapper* mapper) {
int colorCount, SkUnitMapper* mapper,
uint32_t flags) {
if (NULL == colors || colorCount < 1) {
return NULL;
}
EXPAND_1_COLOR(colorCount);
SkGradientShaderBase::Descriptor desc;
desc_init(&desc, colors, pos, colorCount, SkShader::kClamp_TileMode, mapper);
desc_init(&desc, colors, pos, colorCount, SkShader::kClamp_TileMode, mapper, flags);
return SkNEW_ARGS(SkSweepGradient, (cx, cy, desc));
}

View File

@ -93,6 +93,7 @@ public:
int fCount;
SkShader::TileMode fTileMode;
SkUnitMapper* fMapper;
uint32_t fFlags;
};
public:
@ -141,6 +142,7 @@ protected:
int fColorCount;
uint8_t fDstToIndexClass;
uint8_t fFlags;
uint8_t fGradFlags;
struct Rec {
SkFixed fPos; // 0...1
@ -172,7 +174,7 @@ private:
static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
U8CPU alpha);
U8CPU alpha, uint32_t gradFlags);
void setCacheAlpha(U8CPU alpha) const;
void initCommon();

View File

@ -10,8 +10,8 @@
SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy,
const Descriptor& desc)
: SkGradientShaderBase(desc),
fCenter(SkPoint::Make(cx, cy))
: SkGradientShaderBase(desc)
, fCenter(SkPoint::Make(cx, cy))
{
fPtsToUnit.setTranslate(-cx, -cy);

View File

@ -175,9 +175,9 @@ void SkTwoPointConicalGradient::init() {
/////////////////////////////////////////////////////////////////////
SkTwoPointConicalGradient::SkTwoPointConicalGradient(
const SkPoint& start, SkScalar startRadius,
const SkPoint& end, SkScalar endRadius,
const Descriptor& desc)
const SkPoint& start, SkScalar startRadius,
const SkPoint& end, SkScalar endRadius,
const Descriptor& desc)
: SkGradientShaderBase(desc),
fCenter1(start),
fCenter2(end),