How does Canvas define clip boundaries?

I am working with Android Canvas , trying to determine how its getClipBounds results are getClipBounds . I understand that Canvas internally saves the Matrix transform, which is updated as I call translate , scale , etc., but trying to replicate the Matrix results that 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 set to 0, 0, canvas.getWidth(), canvas.getHeight() . translate and scale are called from gesture event handlers that work correctly.

Part of me is confused by viewportMatrix . It seems to me that I do not

 viewportMatrix.set(canvas.getMatrix()); 

or

 viewportMatrix.preConcat(canvas.getMatrix()); 

or even a one-time call

 viewportMatrix.set(canvas.getMatrix()); 

at the beginning, followed by side by side translate / scale calls for the two matrices. I even tried completely ignoring the built-in Matrix Canvas 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 compare with getClipBounds() , and the discrepancies are pretty 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 single call to viewportMatrix.preConcat(canvas.getMatrix()) , followed by translate / scale side by side:

clipBounds: Rect (-96, -97 - 603, 452) (699, 549)
viewport: RectF (96.57738, 97.78168, 796.5774, 647.7817) (700.0, 550.0)

with a single call to viewportMatrix.set(canvas.getMatrix()) , followed by translate / scale side by side:

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 source of Canvas , because all Matrix code disappears in private calls of its own, the code of which is not displayed.

Why are my GetViewport calls so disconnected and what happens behind the scenes with getClipBounds ?

+7
java android android-canvas
source share
1 answer

I read this answer on GameDev SE , which uses matrix inversion for the exchange between screen and world coordinate systems:

To go from the screen to the world just use Vector2.Transform. This is commonly used to obtain the location of the mouse in the world for collecting objects.

 Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix)); 

To go from the world to the screen, just do the opposite.

 Vector2.Transform(mouseLocation, Camera.TransformMatrix); 

Inspired by this approach, I tried to invert the transformation matrix as follows:

 private RectF GetViewport() { RectF viewport = new RectF(); Matrix viewportMatrix = new Matrix(); translationMatrix.invert(viewportMatrix); viewportMatrix.mapRect(viewport, originalViewport); return viewport; } 

This viewport correctly matches the results returned by getClipBounds .

This manual tutorial explains what I noticed from the results of the transform calls, but did not apply to my matrix:

In a computer program, the camera does not move at all and in fact, the world simply moves in the opposite direction and orientation of how you want the camera to move in reality.

To get it right, we have to think about two different things:

  • Camera Transformation Matrix : A transformation that puts the camera in the correct position and orientation in world space (this is the transformation that you would apply to the 3D model of the camera if you wanted to represent it in the scene).
  • View Matrix . This matrix transforms vertices from the space of the world into the space of representations. This matrix is ​​the inverse camera transformation matrix described above.
+1
source share

All Articles