NickT
NickT

Reputation: 23873

Bitmap rotating about the wrong pivot

I'm adding a compass overlay to my mapping application. I've decided to create a fairly complicated compass rose and have that drawn to a bitmap in the overlay's constructor just once, rather than on every draw. In the overlay's draw I just rotate the bitmap according to the magnetic sensor's value with a Matrix.

In short, it is incorrect when the rotation is anything other than 90, 180 or 270 degrees - it appears not to rotate about the centre.

I've created a minimal sample which reproduces the problem which is shown below together with screen shots of the 0, 45 and 90 degree rotations that I see. The overlay shape is much simpler than the real version and some values have been hard coded to cut down the post size, but this uses the same principles as the real app.

public class BasicMapOverlayActivity extends MapActivity {

    private MapController mMapCtrlr;
    private MapView mMapVw;
    private int mStartLat = 53500000;
    private int mStartLon = -3000000; 
    private float mBearing = 0.0f;
    private static final int COMPASS_OVL_SIZE = 100;
    private Bitmap mCompassRoseBmap;
    private Canvas mCompassRoseCanvas;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mMapVw = (MapView) findViewById(R.id.map);
        mMapCtrlr = mMapVw.getController();
        mMapCtrlr.setZoom(14);
        mMapVw.setSatellite(true);
        mMapVw.setBuiltInZoomControls(true);
        GeoPoint startGpt = new GeoPoint(mStartLat, mStartLon);
        mMapCtrlr.setCenter(startGpt);
        mCompassRoseCanvas = new Canvas();
        mCompassRoseBmap = Bitmap.createBitmap(COMPASS_OVL_SIZE, COMPASS_OVL_SIZE, Bitmap.Config.ARGB_8888);
        mCompassRoseCanvas.setBitmap(mCompassRoseBmap);
        List<Overlay> listOfOverlays = mMapVw.getOverlays();
        CompassOverlay compassOverlay = new CompassOverlay(mCompassRoseCanvas);
        listOfOverlays.add(compassOverlay);
    }
    public void myClickHandler(View target) {

        switch (target.getId()) {
            case R.id.TurnZeroButton:
                mBearing = 0;
                break;
            case R.id.TurnThirtyButton:
                mBearing = 30;
                break;
            case R.id.Turn45Button:
                mBearing = 45;
                break;
            case R.id.TurnNinetyButton:
                mBearing = 90;
                break;
            case R.id.Turn180Button:
                mBearing = 180;
                break;
        }
        EditText et = (EditText) findViewById(R.id.editText1);
        NumberFormat formatter = new DecimalFormat("##0");
        et.setText(formatter.format(mBearing));
        mMapVw.invalidate();
    }
    @Override
    protected boolean isRouteDisplayed() {return false;}

    public class CompassOverlay extends com.google.android.maps.Overlay {

        private Paint overlayPaint;
        private RectF oRec;

        public CompassOverlay(Canvas canvas) {
            super();
            createRose(canvas, COMPASS_OVL_SIZE);
        }

        public void createRose(Canvas canvas, int overlaySize) {

            float scale = (float) overlaySize;
            canvas.scale(scale, scale);
            overlayPaint = new Paint();
            overlayPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            overlayPaint.setColor(Color.YELLOW);
            overlayPaint.setAntiAlias(true);
            oRec = new RectF();
            oRec.set(0.0f, 0.0f, 1.0f, 1.0f);
            // draw rectangle edges and diagonals
            canvas.drawLine(oRec.left, oRec.top, oRec.right, oRec.bottom, overlayPaint);
            canvas.drawLine(oRec.left, oRec.bottom, oRec.right, oRec.top, overlayPaint);
            canvas.drawLine(oRec.left, oRec.top, oRec.right, oRec.top, overlayPaint);
            canvas.drawLine(oRec.right, oRec.top, oRec.right, oRec.bottom, overlayPaint);
            canvas.drawLine(oRec.right, oRec.bottom, oRec.left, oRec.bottom, overlayPaint);
            canvas.drawLine(oRec.left, oRec.bottom, oRec.left, oRec.top, overlayPaint);
            // draw red vertical line as a direction indicator
            overlayPaint.setColor(Color.RED);
            canvas.drawLine(0.5f, oRec.top, 0.5f, oRec.bottom/2, overlayPaint);// vertical line
            overlayPaint.setColor(Color.YELLOW);
        }
        @Override
        public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {

            Bitmap rBitmap = rotateCompassBitmap(mBearing);
            canvas.drawBitmap(rBitmap, 160, 70, null);
            rBitmap.recycle();
            return false;
        }

        private Bitmap rotateCompassBitmap(float rotationDegrees) {
            Matrix matrix = new Matrix();
            matrix.postRotate(rotationDegrees);
            Bitmap rotatedBitmap = Bitmap.createBitmap(mCompassRoseBmap, 0, 0,
                     mCompassRoseBmap.getWidth(), mCompassRoseBmap.getHeight(), matrix, true); 
            return  rotatedBitmap;
        }
    }
}

The layout is just a simple map, with some buttons which apply a rotation by means of a clickListener. The rotation applied is shown in an EditText.

Zero rotation Zero rotation

45 degrees 45 degrees

90 degrees 90 degrees

Any help would be much appreciated, as I'm the first to admit that I'm not the world's best when it comes to graphics.

Upvotes: 0

Views: 1224

Answers (1)

Dan S
Dan S

Reputation: 9189

Use postRotate with a point to rotate about.

Upvotes: 1

Related Questions