I've been doing some work with Android's Canvas
, specifically trying to determine how its getClipBounds
results are determined. I understand Canvas
internally keeps a transform Matrix
which is updated as I call translate
, scale
, etc. but trying to replicate that Matrix
's results has baffled me.
@Override
public void onDraw(Canvas canvas) {
Rect clipBounds;
RectF viewport;
canvas.save();
canvas.concat(translationMatrix);
//viewportMatrix.preConcat(canvas.getMatrix());
viewportMatrix.set(canvas.getMatrix());
clipBounds = canvas.getClipBounds();
viewport = GetViewport();
Log.d("clipBounds", clipBounds.toString() + " (" + clipBounds.width() + ", " + clipBounds.height() + ")");
Log.d("viewport", viewport.toString() + " (" + viewport.width() + ", " + viewport.height() + ")");
//drawing is done here
canvas.restore();
}
//viewport code modeled after http://stackoverflow.com/a/17142856/2245528
private RectF GetViewport() {
RectF viewport = new RectF();
viewportMatrix.mapRect(viewport, originalViewport);
return viewport;
}
private void Translate(float x, float y) {
translationMatrix.postTranslate(x, y);
invalidate();
}
private void Scale(float scaleFactor, PointF focusPoint) {
if (focusPoint == null) {
translationMatrix.postScale(scaleFactor, scaleFactor);
}
//keep the focus point in focus if possible
else {
translationMatrix.postScale(scaleFactor, scaleFactor, focusPoint.x, focusPoint.y);
}
invalidate();
}
private final Matrix translationMatrix = new Matrix();
private final RectF originalViewport = new RectF();
private final Matrix viewportMatrix = new Matrix();
originalViewport
is set to 0, 0, canvas.getWidth(), canvas.getHeight()
. Translate
and Scale
are called from gesture event handlers, which are working correctly.
The part that confuses me is viewportMatrix
. It doesn't seem to matter whether I do
viewportMatrix.set(canvas.getMatrix());
or
viewportMatrix.preConcat(canvas.getMatrix());
or even a one-time call to
viewportMatrix.set(canvas.getMatrix());
at the beginning followed by side-by-side Translate
/Scale
calls to the two matrixes. I've even tried completely ignoring the Canvas
's built-in Matrix
and rewriting GetViewport
as
//viewport code modeled after http://stackoverflow.com/a/17142856/2245528
private RectF GetViewport() {
RectF viewport = new RectF();
translationMatrix.mapRect(viewport, originalViewport);
return viewport;
}
I can never seem to match getClipBounds()
, and the discrepancies are fairly serious:
with viewportMatrix.set(canvas.getMatrix)
:
clipBounds: Rect(-97, -97 - 602, 452) (699, 549)
viewport: RectF(97.04178, 97.06036, 797.04175, 647.06036) (700.0, 550.0)
with viewportMatrix.preConcat(canvas.getMatrix)
:
clipBounds: Rect(-97, -96 - 602, 453) (699, 549)
viewport: RectF(2708.9663, 2722.2754, 3408.9663, 3272.2754) (700.0, 550.0)
with translationMatrix.mapRect
:
clipBounds: Rect(-96, -96 - 603, 453) (699, 549)
viewport: RectF(96.73213, 96.85794, 796.7321, 646.8579) (700.0, 550.0)
with a one-shot call to viewportMatrix.preConcat(canvas.getMatrix())
followed by side-by-side Translate
/Scale
calls:
clipBounds: Rect(-96, -97 - 603, 452) (699, 549)
viewport: RectF(96.57738, 97.78168, 796.5774, 647.7817) (700.0, 550.0)
with a one-shot call to viewportMatrix.set(canvas.getMatrix())
followed by side-by-side Translate
/Scale
calls:
clipBounds: Rect(-96, -96 - 603, 453) (699, 549)
viewport: RectF(96.40051, 96.88153, 796.4005, 646.88153) (700.0, 550.0)
I can't even check the Canvas
source, as all the Matrix
code vanishes into private native calls whose code isn't shown.
Why are my GetViewport
calls so grossly off, and what's going on behind-the-scenes with getClipBounds
?