aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2009-08-27 19:28:37 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2009-08-27 19:28:37 +0000
commit3c9b2a4a0e4f57db23640e85959ee78b86634628 (patch)
treeed5f297f3dbc1d4d6703846c11c2a4f0c5174b9d
parent2a4d1ff189ef8df14ec40499125b01f406cb442b (diff)
expand SkShader's flag kConstInY to 16 and 32 variants, allowing a shader
(like gradients) to support predithering. If they do, then they would suppress kConstInY16, since they no longer are const. The blitters now check for each flag separately, so we don't have to give up const-in-Y in the 32bit case, since in that mode we don't care about dithering. git-svn-id: http://skia.googlecode.com/svn/trunk@339 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkShader.h33
-rw-r--r--samplecode/SampleDitherBitmap.cpp29
-rw-r--r--src/core/SkBitmapProcShader.cpp5
-rw-r--r--src/core/SkBlitter_RGB16.cpp6
-rw-r--r--src/core/SkShader.cpp2
-rw-r--r--src/effects/SkGradientShader.cpp125
6 files changed, 82 insertions, 118 deletions
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 8fac8cbb08..f3d4856b3a 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -62,10 +62,10 @@ public:
enum Flags {
//!< set if all of the colors will be opaque
- kOpaqueAlpha_Flag = 0x01,
+ kOpaqueAlpha_Flag = 0x01,
//! set if this shader's shadeSpan16() method can be called
- kHasSpan16_Flag = 0x02,
+ kHasSpan16_Flag = 0x02,
/** Set this bit if the shader's native data type is instrinsically 16
bit, meaning that calling the 32bit shadeSpan() entry point will
@@ -76,9 +76,17 @@ public:
/** set (after setContext) if the spans only vary in X (const in Y).
e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient
- that varies from left-to-right
+ that varies from left-to-right. This flag specifies this for
+ shadeSpan().
*/
- kConstInY_Flag = 0x08
+ kConstInY32_Flag = 0x08,
+
+ /** same as kConstInY32_Flag, but is set if this is true for shadeSpan16
+ which may not always be the case, since shadeSpan16 may be
+ predithered, which would mean it was not const in Y, even though
+ the 32bit shadeSpan() would be const.
+ */
+ kConstInY16_Flag = 0x10
};
/** Called sometimes before drawing with this shader.
@@ -99,29 +107,28 @@ public:
parameters, or false if not. If false is returned, nothing
will be drawn.
*/
- virtual bool setContext( const SkBitmap& device,
- const SkPaint& paint,
- const SkMatrix& matrix);
+ virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
+ const SkMatrix& matrix);
/** Called for each span of the object being drawn. Your subclass
should set the appropriate colors (with premultiplied alpha) that
correspond to the specified device coordinates.
*/
- virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0;
- /** Called only for 16bit devices when getFlags() returns kOpaqueAlphaFlag | kHasSpan16_Flag
+ virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0;
+ /** Called only for 16bit devices when getFlags() returns
+ kOpaqueAlphaFlag | kHasSpan16_Flag
*/
- virtual void shadeSpan16(int x, int y, uint16_t[], int count);
+ virtual void shadeSpan16(int x, int y, uint16_t[], int count);
/** Similar to shadeSpan, but only returns the alpha-channel for a span.
The default implementation calls shadeSpan() and then extracts the alpha
values from the returned colors.
*/
- virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
+ virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
/** Helper function that returns true if this shader's shadeSpan16() method can
be called.
*/
- bool canCallShadeSpan16()
- {
+ bool canCallShadeSpan16() {
return SkShader::CanCallShadeSpan16(this->getFlags());
}
diff --git a/samplecode/SampleDitherBitmap.cpp b/samplecode/SampleDitherBitmap.cpp
index e859cdaf44..1000fdfaa0 100644
--- a/samplecode/SampleDitherBitmap.cpp
+++ b/samplecode/SampleDitherBitmap.cpp
@@ -1,9 +1,35 @@
#include "SampleCode.h"
#include "SkColorPriv.h"
+#include "SkGradientShader.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkUtils.h"
+static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+ canvas->drawRect(r, p);
+
+ SkPaint frame(p);
+ frame.setShader(NULL);
+ frame.setStyle(SkPaint::kStroke_Style);
+ canvas->drawRect(r, frame);
+}
+
+static void draw_gradient(SkCanvas* canvas) {
+ SkRect r = { 0, 0, SkIntToScalar(256), SkIntToScalar(32) };
+ SkPoint pts[] = { r.fLeft, r.fTop, r.fRight, r.fTop };
+ SkColor colors[] = { 0xFF000000, 0xFFFF0000 };
+ SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode);
+
+ SkPaint p;
+ p.setShader(s)->unref();
+ draw_rect(canvas, r, p);
+
+ canvas->translate(0, SkIntToScalar(40));
+ p.setDither(true);
+ draw_rect(canvas, r, p);
+}
+
static void test_pathregion() {
SkPath path;
SkRegion region;
@@ -103,6 +129,9 @@ protected:
draw2(canvas, fBM8);
canvas->translate(0, SkIntToScalar(fBM8.height() *3));
draw2(canvas, fBM32);
+
+ canvas->translate(0, SkIntToScalar(fBM8.height() *3));
+ draw_gradient(canvas);
}
private:
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index cb4d129d5b..a8e21b4f5f 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -132,7 +132,10 @@ bool SkBitmapProcShader::setContext(const SkBitmap& device,
// if we're only 1-pixel heigh, and we don't rotate, then we can claim this
if (1 == fState.fBitmap->height() &&
only_scale_and_translate(this->getTotalInverse())) {
- flags |= kConstInY_Flag;
+ flags |= kConstInY32_Flag;
+ if (flags & kHasSpan16_Flag) {
+ flags |= kConstInY16_Flag;
+ }
}
fFlags = flags;
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
index c41efef0a5..ac6b5d0bbf 100644
--- a/src/core/SkBlitter_RGB16.cpp
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -528,7 +528,7 @@ void SkRGB16_Shader16_Blitter::blitRect(int x, int y, int width, int height) {
int alpha = shader->getSpan16Alpha();
if (0xFF == alpha) {
- if (fShaderFlags & SkShader::kConstInY_Flag) {
+ if (fShaderFlags & SkShader::kConstInY16_Flag) {
// have the shader blit directly into the device the first time
shader->shadeSpan16(x, y, dst, width);
// and now just memcpy that line on the subsequent lines
@@ -549,7 +549,7 @@ void SkRGB16_Shader16_Blitter::blitRect(int x, int y, int width, int height) {
} else {
int scale = SkAlpha255To256(alpha);
uint16_t* span16 = (uint16_t*)fBuffer;
- if (fShaderFlags & SkShader::kConstInY_Flag) {
+ if (fShaderFlags & SkShader::kConstInY16_Flag) {
shader->shadeSpan16(x, y, span16, width);
do {
SkBlendRGB16(span16, dst, scale, width);
@@ -668,7 +668,7 @@ void SkRGB16_Shader_Blitter::blitRect(int x, int y, int width, int height) {
uint16_t* dst = fDevice.getAddr16(x, y);
size_t dstRB = fDevice.rowBytes();
- if (fShaderFlags & SkShader::kConstInY_Flag) {
+ if (fShaderFlags & SkShader::kConstInY32_Flag) {
shader->shadeSpan(x, y, buffer, width);
do {
proc(dst, buffer, width, 0xFF, x, y);
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index 5567914530..5290f2ff0a 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -263,7 +263,7 @@ bool SkColorShader::setContext(const SkBitmap& device, const SkPaint& paint,
}
fPMColor = SkPackARGB32(a, r, g, b);
- fFlags = kHasSpan16_Flag | kConstInY_Flag;
+ fFlags = kHasSpan16_Flag | kConstInY32_Flag;
if (SkGetPackedA32(fPMColor) == 255) {
fFlags |= kOpaqueAlpha_Flag;
}
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
index 635e0e579f..860e902dd2 100644
--- a/src/effects/SkGradientShader.cpp
+++ b/src/effects/SkGradientShader.cpp
@@ -20,18 +20,6 @@
#include "SkUnitMapper.h"
#include "SkUtils.h"
-/*
- ToDo
-
- - not sure we still need the full Rec struct, now that we're using a cache
- - detect const-alpha (but not opaque) in getFlags()
-*/
-
-/* dither seems to look better, but not stuningly yet, and it slows us down a little
- so its not on by default yet.
-*/
-#define TEST_GRADIENT_DITHER
-
///////////////////////////////////////////////////////////////////////////
typedef SkFixed (*TileProc)(SkFixed);
@@ -503,12 +491,9 @@ static inline U16CPU dot6to16(unsigned x) {
const uint16_t* Gradient_Shader::getCache16() {
if (fCache16 == NULL) {
- if (fCache16Storage == NULL) // set the storage and our working ptr
-#ifdef TEST_GRADIENT_DITHER
+ if (fCache16Storage == NULL) { // set the storage and our working ptr
fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
-#else
- fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
-#endif
+ }
fCache16 = fCache16Storage;
if (fColorCount == 2) {
build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
@@ -527,20 +512,14 @@ const uint16_t* Gradient_Shader::getCache16() {
}
if (fMapper) {
-#ifdef TEST_GRADIENT_DITHER
fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
-#else
- fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
-#endif
uint16_t* linear = fCache16; // just computed linear data
uint16_t* mapped = fCache16Storage; // storage for mapped data
SkUnitMapper* map = fMapper;
for (int i = 0; i < 64; i++) {
int index = map->mapUnit16(dot6to16(i)) >> 10;
mapped[i] = linear[index];
-#ifdef TEST_GRADIENT_DITHER
mapped[i + 64] = linear[index + 64];
-#endif
}
sk_free(fCache16);
fCache16 = fCache16Storage;
@@ -655,7 +634,13 @@ bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
if ((fDstToIndex.getType() & ~mask) == 0) {
- fFlags |= SkShader::kConstInY_Flag;
+ fFlags |= SkShader::kConstInY32_Flag;
+ if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
+ // only claim this if we do have a 16bit mode (i.e. none of our
+ // colors have alpha), and if we are not dithering (which obviously
+ // is not const in Y).
+ fFlags |= SkShader::kConstInY16_Flag;
+ }
}
return true;
}
@@ -779,11 +764,9 @@ bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
return true;
}
-#ifdef TEST_GRADIENT_DITHER
-static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
-{
- if (reinterpret_cast<uintptr_t>(dst) & 2)
- {
+static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
+ int count) {
+ if (reinterpret_cast<uintptr_t>(dst) & 2) {
*dst++ = value;
count -= 1;
SkTSwap(value, other);
@@ -791,10 +774,10 @@ static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int
sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
- if (count & 1)
+ if (count & 1) {
dst[count - 1] = value;
+ }
}
-#endif
void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
{
@@ -804,9 +787,7 @@ void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
SkMatrix::MapXYProc dstProc = fDstToIndexProc;
TileProc proc = fTileProc;
const uint16_t* cache = this->getCache16();
-#ifdef TEST_GRADIENT_DITHER
int toggle = ((x ^ y) & 1) << kCache16Bits;
-#endif
if (fDstToIndexClass != kPerspective_MatrixClass) {
dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
@@ -827,34 +808,22 @@ void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
// we're a vertical gradient, so no change in a span
unsigned fi = proc(fx) >> 10;
SkASSERT(fi <= 63);
-#ifdef TEST_GRADIENT_DITHER
dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
-#else
- sk_memset16(dstC, cache[fi], count);
-#endif
} else if (proc == clamp_tileproc) {
do {
unsigned fi = SkClampMax(fx >> 10, 63);
SkASSERT(fi <= 63);
fx += dx;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + fi];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[fi];
-#endif
} while (--count != 0);
} else if (proc == mirror_tileproc) {
do {
unsigned fi = mirror_6bits(fx >> 10);
SkASSERT(fi <= 0x3F);
fx += dx;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + fi];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[fi];
-#endif
} while (--count != 0);
} else {
SkASSERT(proc == repeat_tileproc);
@@ -862,12 +831,8 @@ void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
unsigned fi = repeat_6bits(fx >> 10);
SkASSERT(fi <= 0x3F);
fx += dx;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + fi];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[fi];
-#endif
} while (--count != 0);
}
} else {
@@ -879,12 +844,8 @@ void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
SkASSERT(fi <= 0xFFFF);
int index = fi >> (16 - kCache16Bits);
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + index];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[index];
-#endif
dstX += SK_Scalar1;
} while (--count != 0);
@@ -1033,40 +994,33 @@ public:
} while (--count != 0);
}
}
- virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
- {
+
+ virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) {
SkASSERT(count > 0);
SkPoint srcPt;
SkMatrix::MapXYProc dstProc = fDstToIndexProc;
TileProc proc = fTileProc;
const uint16_t* cache = this->getCache16();
-#ifdef TEST_GRADIENT_DITHER
int toggle = ((x ^ y) & 1) << kCache16Bits;
-#endif
- if (fDstToIndexClass != kPerspective_MatrixClass)
- {
+ if (fDstToIndexClass != kPerspective_MatrixClass) {
dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
- if (fDstToIndexClass == kFixedStepInX_MatrixClass)
- {
+ if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
SkFixed storage[2];
(void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
dx = storage[0];
dy = storage[1];
- }
- else
- {
+ } else {
SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
dx = SkScalarToFixed(fDstToIndex.getScaleX());
dy = SkScalarToFixed(fDstToIndex.getSkewY());
}
- if (proc == clamp_tileproc)
- {
+ if (proc == clamp_tileproc) {
const uint8_t* sqrt_table = gSqrt8Table;
/* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
@@ -1079,8 +1033,7 @@ public:
dx >>= 1;
fy >>= 1;
dy >>= 1;
- if (dy == 0) // might perform this check for the other modes, but the win will be a smaller % of the total
- {
+ if (dy == 0) { // might perform this check for the other modes, but the win will be a smaller % of the total
fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
fy *= fy;
do {
@@ -1088,16 +1041,10 @@ public:
unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
fx += dx;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
-#endif
} while (--count != 0);
- }
- else
- {
+ } else {
do {
unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
@@ -1105,33 +1052,21 @@ public:
fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
fx += dx;
fy += dy;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
-#endif
} while (--count != 0);
}
- }
- else if (proc == mirror_tileproc)
- {
+ } else if (proc == mirror_tileproc) {
do {
SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
unsigned fi = mirror_tileproc(dist);
SkASSERT(fi <= 0xFFFF);
fx += dx;
fy += dy;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[fi >> (16 - kCache16Bits)];
-#endif
} while (--count != 0);
- }
- else
- {
+ } else {
SkASSERT(proc == repeat_tileproc);
do {
SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
@@ -1139,17 +1074,11 @@ public:
SkASSERT(fi <= 0xFFFF);
fx += dx;
fy += dy;
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[fi >> (16 - kCache16Bits)];
-#endif
} while (--count != 0);
}
- }
- else // perspective case
- {
+ } else { // perspective case
SkScalar dstX = SkIntToScalar(x);
SkScalar dstY = SkIntToScalar(y);
do {
@@ -1158,12 +1087,8 @@ public:
SkASSERT(fi <= 0xFFFF);
int index = fi >> (16 - kCache16Bits);
-#ifdef TEST_GRADIENT_DITHER
*dstC++ = cache[toggle + index];
toggle ^= (1 << kCache16Bits);
-#else
- *dstC++ = cache[index];
-#endif
dstX += SK_Scalar1;
} while (--count != 0);