aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-25 15:37:50 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-25 15:37:50 +0000
commit33535f3c48bf723c46f334a93d4a06d782dad30e (patch)
treed5dfb1ec904b1cf48fda20bdd194ffa66c170201
parent8640d5024d57da5508bdf7585849e3b1f1cb365b (diff)
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
-rw-r--r--include/core/SkDevice.h9
-rw-r--r--include/gpu/SkGpuDevice.h3
-rw-r--r--samplecode/SampleBitmapRect.cpp208
-rw-r--r--src/core/SkCanvas.cpp43
-rw-r--r--src/core/SkDevice.cpp75
-rw-r--r--src/gpu/SkGpuDevice.cpp41
6 files changed, 307 insertions, 72 deletions
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index da7842b566..851a167982 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -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.
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 04c4e0fec6..45ae05a8ae 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -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,
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
index ba7efbf16e..18208fb1a3 100644
--- a/samplecode/SampleBitmapRect.cpp
+++ b/samplecode/SampleBitmapRect.cpp
@@ -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,45 +115,134 @@ 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);
+
+ canvas->translate(20, 20);
+
+ canvas->drawBitmap(bitmap, 0, 0, &paint);
+ canvas->drawRect(srcR, paint);
+
+ 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:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+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);
- SkRect dstR = { 0, 200, 128, 380 };
+ this->setBGColor(SK_ColorGRAY);
- canvas->translate(16, 40);
- for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
- SkRect srcR;
- srcR.set(src[i]);
+ fSrcR.fLeft = 0;
+ fSrcR.fTop = 0;
+ fSrcR.fRight = SkIntToScalar(fBitmap.height()) * 3;
+ fSrcR.fBottom = SkIntToScalar(fBitmap.height());
- canvas->drawBitmap(bitmap, 0, 0, &paint);
- canvas->drawBitmapRect(bitmap, &src[i], dstR, &paint);
+ fLimitR.set(0, 0,
+ SkIntToScalar(fBitmap.width()),
+ SkIntToScalar(fBitmap.height()));
- canvas->drawRect(dstR, paint);
- canvas->drawRect(srcR, paint);
+ fDX = SK_Scalar1;
- canvas->translate(160, 0);
+ 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* MyFactory() { return new BitmapRectView; }
-static SkViewRegister reg(MyFactory);
+static SkView* F0() { return new BitmapRectView; }
+static SkView* F1() { return new BitmapRectView2; }
+static SkViewRegister gR0(F0);
+static SkViewRegister gR1(F1);
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 9ef9c25e91..1bb3a456e9 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -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,
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 10d89d591f..fb126dbb34 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -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);
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 2149d11a24..bcda7d8001 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -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