aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
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 /src
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
Diffstat (limited to 'src')
-rw-r--r--src/core/SkCanvas.cpp43
-rw-r--r--src/core/SkDevice.cpp75
-rw-r--r--src/gpu/SkGpuDevice.cpp41
3 files changed, 126 insertions, 33 deletions
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