Reimplement drawBitmapRectToRect to correctly handle fraction srcRect.

The prev impl relied on drawBitmap "deducing" the destination rect by applying
the computed matrix to the bitmap's bounds. This cannot be done if the srcRect
is fractional, and therefore not representable w/ a bitmap.

The new impl computes the same matrix, but calls down to the device via drawRect
+ a bitmap_shader. This allows us to specfiy the dstRect explicitly.

The possible down-side is that we now rely on the device subclass to efficiently
handle draRect+shader, instead of calling its drawBitmap entry-point.

To give the device the chance to handle this differently, I now call through to
a new device virtual: drawBitmapRect. The default impl is to create the shader
and call drawRect, but a subclass can intercept that.

For now, the GPU override of drawBitmapRect is mimicing the old behavior (by
rounding the srcRect to an iRect). This preserves its ability to call drawBitmap
which handles very-large textures, but shows some gittering/imprecision, due to
the rounding. ... this is the same GPU behavior we have before this CL.
Review URL: https://codereview.appspot.com/6542065

git-svn-id: http://skia.googlecode.com/svn/trunk@5663 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2012-09-25 15:37:50 +00:00
parent 8640d5024d
commit 33535f3c48
6 changed files with 309 additions and 74 deletions

View File

@ -249,6 +249,15 @@ protected:
const SkMatrix& matrix, const SkPaint& paint);
virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint);
/**
* The default impl. will create a bitmap-shader from the bitmap,
* and call drawRect with it.
*/
virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
const SkRect* srcOrNull, const SkRect& dst,
const SkPaint& paint);
/**
* Does not handle text decoration.
* Decorations (underline and stike-thru) will be handled by SkCanvas.

View File

@ -79,6 +79,9 @@ public:
virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
const SkIRect* srcRectOrNull,
const SkMatrix&, const SkPaint&) SK_OVERRIDE;
virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
const SkRect* srcOrNull, const SkRect& dst,
const SkPaint& paint) SK_OVERRIDE;
virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint);
virtual void drawText(const SkDraw&, const void* text, size_t len,

View File

@ -30,37 +30,78 @@
class GrContext;
#endif
#define INT_SIZE 64
#define SCALAR_SIZE SkIntToScalar(INT_SIZE)
static void make_bitmap(SkBitmap* bitmap, GrContext* ctx) {
SkCanvas canvas;
#if SK_SUPPORT_GPU
if (ctx) {
SkDevice* dev = new SkGpuDevice(ctx, SkBitmap::kARGB_8888_Config, 64, 64);
canvas.setDevice(dev)->unref();
*bitmap = dev->accessBitmap(false);
} else
#endif
{
bitmap->setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
bitmap->allocPixels();
canvas.setBitmapDevice(*bitmap);
}
static void make_bitmap(SkBitmap* bitmap) {
bitmap->setConfig(SkBitmap::kARGB_8888_Config, INT_SIZE, INT_SIZE);
bitmap->allocPixels();
SkCanvas canvas(*bitmap);
canvas.drawColor(SK_ColorRED);
SkPaint paint;
paint.setAntiAlias(true);
const SkPoint pts[] = { { 0, 0 }, { 64, 64 } };
const SkPoint pts[] = { { 0, 0 }, { SCALAR_SIZE, SCALAR_SIZE } };
const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
SkShader::kClamp_TileMode))->unref();
canvas.drawCircle(32, 32, 32, paint);
canvas.drawCircle(SCALAR_SIZE/2, SCALAR_SIZE/2, SCALAR_SIZE/2, paint);
}
static SkPoint unit_vec(int degrees) {
SkScalar rad = SkDegreesToRadians(SkIntToScalar(degrees));
SkScalar s, c;
s = SkScalarSinCos(rad, &c);
return SkPoint::Make(c, s);
}
static void bounce(SkScalar* value, SkScalar* delta, SkScalar min, SkScalar max) {
*value += *delta;
if (*value < min) {
*value = min;
*delta = - *delta;
} else if (*value > max) {
*value = max;
*delta = - *delta;
}
}
static void bounce_pt(SkPoint* pt, SkVector* vec, const SkRect& limit) {
bounce(&pt->fX, &vec->fX, limit.fLeft, limit.fRight);
bounce(&pt->fY, &vec->fY, limit.fTop, limit.fBottom);
}
class BitmapRectView : public SampleView {
SkPoint fSrcPts[2];
SkPoint fSrcVec[2];
SkRect fSrcLimit;
SkRect fDstR[2];
void bounce() {
bounce_pt(&fSrcPts[0], &fSrcVec[0], fSrcLimit);
bounce_pt(&fSrcPts[1], &fSrcVec[1], fSrcLimit);
}
public:
BitmapRectView() {
this->setBGColor(SK_ColorGRAY);
fSrcPts[0].set(0, 0);
fSrcPts[1].set(SCALAR_SIZE, SCALAR_SIZE);
fSrcVec[0] = unit_vec(30);
fSrcVec[1] = unit_vec(107);
fSrcLimit.set(-SCALAR_SIZE/4, -SCALAR_SIZE/4,
SCALAR_SIZE*5/4, SCALAR_SIZE*5/4);
fDstR[0] = SkRect::MakeXYWH(SkIntToScalar(10), SkIntToScalar(100),
SkIntToScalar(250), SkIntToScalar(300));
fDstR[1] = fDstR[0];
fDstR[1].offset(fDstR[0].width() * 5/4, 0);
fSrcPts[0].set(32, 32);
fSrcPts[1].set(90, 90);
}
protected:
@ -74,37 +115,31 @@ protected:
}
virtual void onDrawContent(SkCanvas* canvas) {
GrContext* ctx = SampleCode::GetGr();
const SkIRect src[] = {
{ 0, 0, 32, 32 },
{ 0, 0, 80, 80 },
{ 32, 32, 96, 96 },
{ -32, -32, 32, 32, }
};
SkRect srcR;
srcR.set(fSrcPts[0], fSrcPts[1]);
srcR = SkRect::MakeXYWH(fSrcPts[0].fX, fSrcPts[0].fY, 32, 32);
srcR.offset(-srcR.width()/2, -srcR.height()/2);
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(ctx ? SK_ColorGREEN : SK_ColorYELLOW);
paint.setColor(SK_ColorYELLOW);
SkBitmap bitmap;
make_bitmap(&bitmap, ctx);
make_bitmap(&bitmap);
SkRect dstR = { 0, 200, 128, 380 };
canvas->translate(20, 20);
canvas->translate(16, 40);
for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
SkRect srcR;
srcR.set(src[i]);
canvas->drawBitmap(bitmap, 0, 0, &paint);
canvas->drawRect(srcR, paint);
canvas->drawBitmap(bitmap, 0, 0, &paint);
canvas->drawBitmapRect(bitmap, &src[i], dstR, &paint);
canvas->drawRect(dstR, paint);
canvas->drawRect(srcR, paint);
canvas->translate(160, 0);
for (int i = 0; i < 2; ++i) {
paint.setFilterBitmap(1 == i);
canvas->drawBitmapRectToRect(bitmap, &srcR, fDstR[i], &paint);
canvas->drawRect(fDstR[i], paint);
}
this->bounce();
this->inval(NULL);
}
private:
@ -113,6 +148,101 @@ private:
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new BitmapRectView; }
static SkViewRegister reg(MyFactory);
static void make_big_bitmap(SkBitmap* bm) {
static const char gText[] =
"We the people, in order to form a more perfect union, establish justice,"
" ensure domestic tranquility, provide for the common defense, promote the"
" general welfare and ensure the blessings of liberty to ourselves and our"
" posterity, do ordain and establish this constitution for the United"
" States of America.";
const int BIG_H = 120;
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(SkIntToScalar(BIG_H));
const int BIG_W = SkScalarRoundToInt(paint.measureText(gText, strlen(gText)));
bm->setConfig(SkBitmap::kARGB_8888_Config, BIG_W, BIG_H);
bm->allocPixels();
bm->eraseColor(SK_ColorWHITE);
SkCanvas canvas(*bm);
canvas.drawText(gText, strlen(gText), 0, paint.getTextSize()*4/5, paint);
}
class BitmapRectView2 : public SampleView {
SkBitmap fBitmap;
SkRect fSrcR;
SkRect fLimitR;
SkScalar fDX;
SkRect fDstR[2];
void bounceMe() {
SkScalar width = fSrcR.width();
bounce(&fSrcR.fLeft, &fDX, fLimitR.fLeft, fLimitR.fRight - width);
fSrcR.fRight = fSrcR.fLeft + width;
}
public:
BitmapRectView2() {
make_big_bitmap(&fBitmap);
this->setBGColor(SK_ColorGRAY);
fSrcR.fLeft = 0;
fSrcR.fTop = 0;
fSrcR.fRight = SkIntToScalar(fBitmap.height()) * 3;
fSrcR.fBottom = SkIntToScalar(fBitmap.height());
fLimitR.set(0, 0,
SkIntToScalar(fBitmap.width()),
SkIntToScalar(fBitmap.height()));
fDX = SK_Scalar1;
fDstR[0] = SkRect::MakeXYWH(SkIntToScalar(20), SkIntToScalar(20),
SkIntToScalar(600), SkIntToScalar(200));
fDstR[1] = fDstR[0];
fDstR[1].offset(0, fDstR[0].height() * 5/4);
}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "BigBitmapRect");
return true;
}
return this->INHERITED::onQuery(evt);
}
virtual void onDrawContent(SkCanvas* canvas) {
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorYELLOW);
for (int i = 0; i < 2; ++i) {
paint.setFilterBitmap(1 == i);
canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR[i], &paint);
canvas->drawRect(fDstR[i], paint);
}
this->bounceMe();
this->inval(NULL);
}
private:
typedef SkView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* F0() { return new BitmapRectView; }
static SkView* F1() { return new BitmapRectView2; }
static SkViewRegister gR0(F0);
static SkViewRegister gR1(F1);

View File

@ -1593,41 +1593,18 @@ void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
}
}
SkMatrix matrix;
// Compute matrix from the two rectangles
{
SkRect tmpSrc;
if (src) {
tmpSrc = *src;
// if the extract process clipped off the top or left of the
// original, we adjust for that here to get the position right.
if (tmpSrc.fLeft > 0) {
tmpSrc.fRight -= tmpSrc.fLeft;
tmpSrc.fLeft = 0;
}
if (tmpSrc.fTop > 0) {
tmpSrc.fBottom -= tmpSrc.fTop;
tmpSrc.fTop = 0;
}
} else {
tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
}
matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
SkLazyPaint lazy;
if (NULL == paint) {
paint = lazy.init();
}
// ensure that src is "valid" before we pass it to our internal routines
// and to SkDevice. i.e. sure it is contained inside the original bitmap.
SkIRect isrcStorage;
SkIRect* isrcPtr = NULL;
if (src) {
src->roundOut(&isrcStorage);
if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
return;
}
isrcPtr = &isrcStorage;
LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
while (iter.next()) {
iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
}
this->internalDrawBitmap(bitmap, isrcPtr, matrix, paint);
LOOPER_END
}
void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,

View File

@ -11,6 +11,7 @@
#include "SkMetaData.h"
#include "SkRasterClip.h"
#include "SkRect.h"
#include "SkShader.h"
SK_DEFINE_INST_COUNT(SkDevice)
@ -348,6 +349,80 @@ void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
draw.drawBitmap(*bitmapPtr, matrix, paint);
}
void SkDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
const SkRect* src, const SkRect& dst,
const SkPaint& paint) {
SkMatrix matrix;
SkRect bitmapBounds, tmpSrc, tmpDst;
SkBitmap tmpBitmap;
bitmapBounds.set(0, 0,
SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
// Compute matrix from the two rectangles
if (src) {
tmpSrc = *src;
} else {
tmpSrc = bitmapBounds;
}
matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
const SkRect* dstPtr = &dst;
const SkBitmap* bitmapPtr = &bitmap;
// clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
// needed (if the src was clipped). No check needed if src==null.
if (src) {
if (!bitmapBounds.contains(*src)) {
if (!tmpSrc.intersect(bitmapBounds)) {
return; // nothing to draw
}
// recompute dst, based on the smaller tmpSrc
matrix.mapRect(&tmpDst, tmpSrc);
dstPtr = &tmpDst;
}
// since we may need to clamp to the borders of the src rect within
// the bitmap, we extract a subset.
SkIRect srcIR;
tmpSrc.roundOut(&srcIR);
if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
return;
}
bitmapPtr = &tmpBitmap;
// Since we did an extract, we need to adjust the matrix accordingly
SkScalar dx = 0, dy = 0;
if (srcIR.fLeft > 0) {
dx = SkIntToScalar(srcIR.fLeft);
}
if (srcIR.fTop > 0) {
dy = SkIntToScalar(srcIR.fTop);
}
if (dx || dy) {
matrix.preTranslate(dx, dy);
}
}
// construct a shader, so we can call drawRect with the dst
SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
if (NULL == s) {
return;
}
s->setLocalMatrix(matrix);
SkPaint paintWithShader(paint);
paintWithShader.setStyle(SkPaint::kFill_Style);
paintWithShader.setShader(s)->unref();
// Call ourself, in case the subclass wanted to share this setup code
// but handle the drawRect code themselves.
this->drawRect(draw, *dstPtr, paintWithShader);
}
void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint) {
draw.drawSprite(bitmap, x, y, paint);

View File

@ -1585,6 +1585,47 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
GR_Scalar1 * h / texture->height()));
}
void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
const SkRect* src, const SkRect& dst,
const SkPaint& paint) {
SkMatrix matrix;
// Compute matrix from the two rectangles
{
SkRect tmpSrc;
if (src) {
tmpSrc = *src;
// if the extract process clipped off the top or left of the
// original, we adjust for that here to get the position right.
if (tmpSrc.fLeft > 0) {
tmpSrc.fRight -= tmpSrc.fLeft;
tmpSrc.fLeft = 0;
}
if (tmpSrc.fTop > 0) {
tmpSrc.fBottom -= tmpSrc.fTop;
tmpSrc.fTop = 0;
}
} else {
tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
}
matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
}
// ensure that src is "valid" before we pass it to our internal routines
// and to SkDevice. i.e. sure it is contained inside the original bitmap.
SkIRect isrcStorage;
SkIRect* isrcPtr = NULL;
if (src) {
src->roundOut(&isrcStorage);
if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
return;
}
isrcPtr = &isrcStorage;
}
this->drawBitmap(draw, bitmap, isrcPtr, matrix, paint);
}
void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device,
int x, int y, const SkPaint& paint) {
// clear of the source device must occur before CHECK_SHOULD_DRAW